Install a Native Compiler
Install a release with one command, or build and install from a source
checkout. The release route needs curl, tar, and a C compiler:
curl -fsSL https://x2c-lang.dev/install.sh | sh
export PATH="$HOME/.local/x2c/bin:$PATH"
X2C_PREFIX chooses another prefix and --version <n> pins a release;
the current version comes from https://x2c-lang.dev/x2c-version.txt.
Running the script again upgrades the prefix and keeps installed packages.
tools/check-install-script.sh exercises the same script against a
release layout built from the current tree.
From a source checkout:
make install PREFIX="$HOME/.local/x2c"
export PATH="$HOME/.local/x2c/bin:$PATH"
x2c build hello.x --output hello
./hello
Use the project’s supported GNU Make. The install command builds the compiler
and installs its matching runtime, headers, compile-time Lisp SDK, unit
interfaces, runnable examples, an empty package directory, and license. It
does not require Cosmopolitan or an APE build. With no PREFIX, make install keeps its development behavior: it installs into the checkout’s
bin/ under the current branch name.
The x2c home
An installed prefix and a source checkout share one layout, the x2c home:
<home>/bin/x2c the compiler
<home>/include/x2c/ runtime sources and generated headers
<home>/lib/ the runtime archive and unit interfaces
<home>/etc/ compile-time Lisp and macro sources
<home>/packages/ installed packages, one directory each
<home>/examples/ runnable examples (installed prefix)
<home>/src/ compiler sources (checkout only)
The compiler finds its home by walking up from its own executable, then from
the current directory, to the nearest directory holding include/ and
etc/compiler-sdk.xlisp. X2C_HOME sets the home explicitly and takes
precedence.
x2c env prints the resolved home, layout, package roots, and host tools;
x2c env home prints one value. Every import searches <home>/packages
after the explicit --package-dir and manifest directories, so a package
placed there needs no build flags.
PREFIX must be absolute and dedicated to x2c. Move or rename the complete
prefix to relocate it. Keep bin, include, lib, etc, packages, and
licenses together. Paths may contain spaces. Quote the compiler path when
invoking it:
"/path with spaces/x2c/bin/x2c" build hello.x --output hello
An upgrade stages a complete payload, replaces files owned by the previous
installation, and removes obsolete owned files using its inventory. It leaves
unrelated files alone and refuses to overwrite an unrelated file with the same
name. Publication replaces individual files; do not run an installation while
programs are compiling against it. Keep .x2c-install-manifest with the prefix
so upgrades retain ownership and build identity information.
For a packaging staging directory, use DESTDIR with the intended prefix:
make install PREFIX=/opt/x2c DESTDIR="$PWD/staging"
This writes staging/opt/x2c. Move that complete directory to its destination;
the staging path is not embedded in the compiler’s runtime configuration.
make dist PREFIX=/opt/x2c stages the same installation under dist/ and
writes dist/x2c-<version>-<platform>.tar.gz with a .sha256 beside it.
make uninstall PREFIX="$HOME/.local/x2c" removes every file the inventory
owns and any directory that becomes empty. Packages installed under
<prefix>/packages are not owned by the compiler inventory: an upgrade keeps
them, and an uninstall leaves the prefix in place when they remain.
System-wide merged layouts such as installing these support directories
directly into /usr are outside this dedicated-prefix contract. Native
executables and archives remain specific to their host platform and
architecture; relocating them does not make them cross-platform binaries.
The installed tool defaults are cc and ar from PATH. Explicit --cc and
--ar options take precedence, followed by X2C_CC/X2C_AR, then CC/AR,
then the defaults in lib/x2c/toolchain. Build identity is recorded separately
in the installation inventory, not as a path to the producer’s compiler tools.
Windows
Use WSL2. Inside an Ubuntu 24.04 or newer distribution the Linux release
installs with the same command, and the compiler needs only clang or
gcc, make, and curl from the distribution’s packages:
sudo apt-get install -y clang make curl
curl -fsSL https://x2c-lang.dev/install.sh | sh
Keep the prefix and your projects on the Linux filesystem, under your WSL
home rather than under /mnt/c, so builds run at native speed. Use clang
15 or newer, or gcc: Ubuntu 22.04 ships clang 14, which rejects two static
initializers in the runtime.
A source checkout also builds under MSYS2 with its gcc, given three
settings: check the repository out with LF line endings
(git config core.autocrlf false), enable native symlinks
(export MSYS=winsymlinks:native, which needs Developer Mode or an
administrator shell), and select the compiler with CC=gcc X2C_CC=gcc.
make build-safe then produces a working compiler, and the check
workflow’s windows_spike input tests both routes. Native Windows without a
POSIX layer is not supported, and under MSYS2 only a source build is
supported.
Developer workflow
The dev branch is the everyday integration branch; main is reserved for
releases. Start new development checkouts and branches from dev. A source
checkout is the development install. After make build-safe, the
checkout is a complete home with its own packages/ directory, and bin/x2c
is the bootstrap compiler. Run make build-install before putting bin/ on
PATH; it links bin/x2c to a copy of the current branch’s compiler. A
compiler in the checkout’s bin/ links builds/0/libx2c.a, the runtime built
with the headers in include/x2c. Select the checkout for one shell with
PATH, or for one command with X2C_HOME:
export PATH="$HOME/src/x2c/bin:$PATH"
X2C_HOME="$HOME/src/x2c" x2c env
Keep the global prefix on a release. Install it from a tagged main, never
from a working branch, so a bug seen through it reproduces for every user.
Validate a staging candidate in a fresh scratch prefix and use its explicit
package index, then remove that prefix afterwards. The staging site supplies
the candidate installer and commands; bare x2c install still uses the
production package index. Compiler version alone does not distinguish two
candidates of the same version. Compiler development itself runs through
make inside the checkout and does not depend on which compiler PATH selects.
Releases publish the compiler for macOS and Linux on arm64 and x86_64. The
installer’s default is the newest release; --version <n> installs an
earlier one, and every release’s downloads stay available.
Build, debug, and use packages
Start a project with x2c new:
x2c new hello
cd hello
x2c run -q
Direct source and x2c.toml project builds use the same installed compiler.
For source debugging, opt into native debug information and original source
locations:
x2c build -g --source-map hello.x --output hello
lldb ./hello
On macOS this also produces hello.dSYM. Move that companion directory
with the executable when debugging elsewhere. Generated objects may be removed
after the build; --build-dir remains available when you want to retain them.
See source mapping for the source-location
contract and platform debug-artifact behavior. Source mapping changes native
__FILE__ and __LINE__ to refer to the original x2c source.
Compile C against the x2c runtime
A C program can call x2c code through the generated header. Point -I at the
include root and write the runtime header as <x2c/x2c.h>, so every system
header still resolves to the C library:
#include <string.h> /* the C library */
#include <x2c/x2c.h> /* the x2c runtime */
cc app.c -I "$HOME/.local/x2c/include" \
"$HOME/.local/x2c/lib/libx2c.a" -pthread -lm -o app
C generated by x2c translate keeps its own quoted includes. To compile it
by hand, pass -iquote "$HOME/.local/x2c/include/x2c" instead. x2c build
passes that option itself.
For a package source distribution, unpack the source archive, select the installed compiler, and use a dependency cache outside its prefix:
export X2C_DEPS_DIR="$HOME/.cache/x2c-dependencies"
make -C packages/yyjson prepare build X2C="$HOME/.local/x2c/bin/x2c"
x2c build --package-dir packages \
--c-include-dir packages/yyjson/deps/include app.x --output app
Source package builds retain their existing producer-path restrictions; an installed compiler path containing spaces is supported. Native dependency preparation can download sources. Ordinary consumer builds do not install a compiler or fetch missing native dependencies automatically.