Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Install a Native Compiler

Install a release with one command, or build and install from a source checkout. The release route needs curl, tar, shasum or sha256sum, and a C compiler named cc:

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

A source build needs a C compiler, ar, GNU Make, Python 3, and /bin/bash; see building the compiler. The install command builds the compiler and installs its matching runtime, headers, compile-time Lisp SDK, unit interfaces written by the installed compiler, runnable examples, an empty package directory, and license. With no PREFIX, make install runs make build-install: it copies the development compiler to bin/x2c-<branch>, links bin/x2c to that copy, and installs the shipped commands under libexec/x2c/.

Upgrading from 0.14.0

Version 0.15.0 reads package native arguments from <name>.native.rsp instead of <name>.link. The installer keeps existing packages, so rebuild source packages and replace old native bundles with bundles built for 0.15.0.

The SQLite package calls its prepared-statement type Prepared instead of Statement; change imports and typed variables, and use Var.prepared in place of Var.statement. Automatic differentiation moved from the runtime to the optional autodiff package. Build or install that package and use import "autodiff" for its runtime types and macros.

The removed List.pool_* operations now belong to Pool. The removed List.reduce, Array.reduce, and Iter.reduce operations have no matching method; use Iter.foldl when an explicit seed fits. Use Var.truth in place of Var.truthy.

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/              language component sources (installed prefix);
                         all compiler sources (checkout)

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. Installing these support directories directly into a shared tree such as /usr is not supported. Native executables and archives run only on the platform and architecture that built them.

An installed prefix records cc and ar in lib/x2c/toolchain; Environment gives the full tool selection order.

Windows

Use WSL2. Inside an Ubuntu 24.04 or newer distribution the Linux release installs with the same command. The installer needs curl and a cc (clang 15 or newer, or gcc); make is needed only for a source checkout or package builds:

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, because builds under /mnt/c are slower. 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 and copies its shipped commands into libexec/x2c/. 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 directory with the executable. The build removes generated objects; pass --build-dir to keep them. Source mapping gives the location rules.

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

To compile C generated by x2c translate by hand, see external builds. To build a package from a source archive, see packages.