Installation
This document covers installing prebuilt Koja binaries and building the compiler from source on macOS and Linux. For a complete overview of the language, see the language reference.
Prebuilt binaries
Releases from v0.12.1 onward ship prebuilt binaries for macOS arm64 (Apple Silicon) and Linux x86_64 (glibc), and releases from v0.14.1 onward add Linux arm64 (glibc). Each tarball contains the koja compiler and the koja-lsp language server. If you’re on one of these platforms and don’t plan to modify the compiler, this is the recommended path — no Rust or LLVM toolchain required.
Via asdf (recommended)
asdf plugin add koja https://github.com/koja-lang/asdf-koja.git
asdf install koja latest
asdf set --home koja latest
koja --version
Manual download
Grab the tarball and .sha256 sidecar for your platform from the releases page, then:
curl -fLO https://github.com/koja-lang/koja/releases/download/v0.15.1/koja-v0.15.1-darwin-arm64.tar.gz
curl -fLO https://github.com/koja-lang/koja/releases/download/v0.15.1/koja-v0.15.1-darwin-arm64.tar.gz.sha256
shasum -a 256 -c koja-v0.15.1-darwin-arm64.tar.gz.sha256
tar -xzf koja-v0.15.1-darwin-arm64.tar.gz
mkdir -p ~/.local/bin
cp koja-v0.15.1-darwin-arm64/{koja,koja-lsp} ~/.local/bin/
(Use sha256sum -c on Linux, and substitute linux-x86_64 or linux-arm64 for darwin-arm64.)
Make sure ~/.local/bin is on your PATH, then run koja --version.
Building from source
On other platforms, or to work on the compiler itself, build from source as described in the sections below.
Requirements
- Rust 1.85 or newer (1.94.1+ recommended — some transitive crates push the practical minimum upward; if you see MSRV errors, run
rustup update stableorrustup override set 1.94.1inside the repo) - LLVM 18 (specifically 18.1.x —
llvm-syswill not accept other versions) - A C toolchain (
cc/clang)
The compiler links LLVM 18 statically through llvm-sys, so the LLVM development libraries must be installed and discoverable at build time.
Toolchain compatibility
Whatever installed your Rust toolchain also chose your libstdc++, and llvm-sys will static-link LLVM against that same libstdc++. Mixing ecosystems for those two pieces produces a hard-to-diagnose SIGSEGV deep inside the LLVM X86 backend the first time codegen runs. Stick to one of the coherent combinations:
| Rust source | LLVM source | Status |
|---|---|---|
| Homebrew (macOS or Linuxbrew) | Homebrew (brew install llvm@18) |
Supported |
| rustup | apt (llvm-18-dev) |
Supported, simplest on Debian/Ubuntu |
| rustup | Homebrew | Supported |
| Homebrew | apt | Not supported |
If you previously installed Rust via Homebrew and want to switch to apt LLVM, brew uninstall rust and reinstall via rustup first.
macOS (Homebrew)
This is the primary supported configuration.
brew install llvm@18
git clone https://github.com/koja-lang/koja && cd koja
export LLVM_SYS_181_PREFIX="$(brew --prefix llvm@18)"
export LIBRARY_PATH="$(brew --prefix)/lib:$LIBRARY_PATH"
cargo build -p koja-runtime-posix
cargo build -p koja-runtime-posix --release
cargo build --release -p koja-driver
cp target/release/koja ~/.local/bin/koja
codesign --force -s - ~/.local/bin/koja
The two-step koja-runtime-posix build is intentional: the driver’s build.rs searches for libkoja_runtime.a in both the debug and release build directories. codesign is required on macOS so the kernel doesn’t kill the binary on launch.
Linux (Debian / Ubuntu — rustup + apt)
The simplest path on Linux. Rust comes from rustup (self-contained, doesn’t link the system libstdc++), and LLVM comes from apt.
Install rustup if you don’t already have it:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup default stable
Install LLVM 18 and every dev dependency llvm-sys will need to link:
sudo apt install -y \
build-essential cmake pkg-config gdb \
llvm-18-dev libpolly-18-dev clang-18 libclang-18-dev \
zlib1g-dev libzstd-dev libtinfo-dev libxml2-dev libffi-dev libz3-dev
Pin llvm-sys to the apt-installed LLVM and build:
git clone https://github.com/koja-lang/koja && cd koja
export LLVM_SYS_181_PREFIX="$(llvm-config-18 --prefix)"
cargo build -p koja-runtime-posix
cargo build -p koja-runtime-posix --release
cargo build --release -p koja-driver
cp target/release/koja ~/.local/bin/koja
Linux (Linuxbrew)
Use this path if your Rust toolchain also comes from Homebrew. The build flow mirrors macOS — brew --prefix resolves to /home/linuxbrew/.linuxbrew instead of /opt/homebrew.
brew install llvm@18
git clone https://github.com/koja-lang/koja && cd koja
export LLVM_SYS_181_PREFIX="$(brew --prefix llvm@18)"
export LIBRARY_PATH="$(brew --prefix)/lib:$LIBRARY_PATH"
cargo build -p koja-runtime-posix
cargo build -p koja-runtime-posix --release
cargo build --release -p koja-driver
cp target/release/koja ~/.local/bin/koja
Verifying the install
koja run examples/hello.kojs
You should see hello, world! printed to stdout.
Troubleshooting
cannot find -lPolly
Debian splits Polly out of the main LLVM package:
sudo apt install -y libpolly-18-dev
cargo clean -p llvm-sys
cannot find -lz / -lzstd / -ltinfo / -lxml2 / -lffi / -lz3
LLVM 18 was built against these system libraries, and the linker re-resolves them when you link the compiler. Install whichever development package is missing:
| Linker arg | Debian package |
|---|---|
-lz |
zlib1g-dev |
-lzstd |
libzstd-dev |
-ltinfo |
libtinfo-dev |
-lxml2 |
libxml2-dev |
-lffi |
libffi-dev |
-lz3 |
libz3-dev |
The full apt install line above pre-installs all of these.
command not found: clang (during boring-sys build)
boring-sys uses bindgen, which loads libclang.so to parse C headers. Install both:
sudo apt install -y clang-18 libclang-18-dev
For tooling consistency, match the LLVM version (-18) rather than installing the unversioned clang metapackage.
SIGSEGV inside llvm::X86ReadAdvanceTable when running koja run
You almost certainly have Homebrew Rust + apt LLVM (the unsupported combination from the toolchain compatibility table). Symptoms are a bare Segmentation fault with no Rust output, killing the host driver before it produces the user binary. koja check and koja parse succeed because they don’t reach LLVM.
Confirm the diagnosis:
which rustc # /home/linuxbrew/.linuxbrew/bin/rustc → bad
ldd ./target/release/koja | grep -iE 'brew|llvm' # mixed paths → bad
Fix by collapsing to a single ecosystem. The cleanest is rustup + apt LLVM:
brew uninstall rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
cargo clean
cargo build --release
Alternatively, if you want to stay on Linuxbrew, install Homebrew LLVM and remove the apt one:
brew install llvm@18
sudo apt remove --purge llvm-18-dev
unset LLVM_SYS_181_PREFIX
cargo clean
cargo build --release
Stale llvm-sys artifacts after switching LLVM versions
llvm-sys’s build script does not rerun when you change LLVM_SYS_181_PREFIX, so a cargo build after apt install llvm-18-dev reuses the previously-compiled artifacts and ignores the new install. Force a rebuild:
cargo clean -p llvm-sys
# or, if that doesn't take:
cargo clean
Type errors involving *const i8 / *mut i8
Linux aarch64 (e.g. a Debian VM on Apple Silicon) defines c_char as u8, not i8. The repo’s runtime crate uses c_char everywhere; if you see this in your own modifications, prefer *const c_char over *const i8 in any FFI cast.
dsymutil warnings on Linux
Harmless — the macOS-only dsymutil invocation is gated out at compile time on Linux. If you see a stray reference, you’re on stale source; pull and rebuild.