Build from source when you are changing the compiler, need an unreleased API, or cannot use a prebuilt archive. If your goal is to write Dodo programs, start with installation; a source build is optional.
Choose a build path
| Goal | Start with | LLVM needed? |
|---|---|---|
| Work on parsing, checking, formatting, or editor analysis | Frontend development | No. |
Build a local dodo CLI with code generation |
The prerequisites and ordinary Cargo build below | Yes, LLVM 23 development files. |
| Produce a Windows release archive | Windows release archive | Yes, plus static support libraries. |
| Produce a Linux binary with bundled LLVM support libraries | Self-contained Linux release | Yes, on the build host. |
| Minimize executable size | Size-focused build | Yes; size depends on the LLVM libraries too. |
Run repository commands from its root unless a section says otherwise. The Rust
toolchain is pinned in rust-toolchain.toml; the compiler’s bundled library is
compiled into the binary, so rebuild Dodo after changing stdlib/ before testing
those changes with the CLI.
Get the source
git clone https://github.com/Jotrorox/dodo.git
cd dodo
git checkout v0.1.4
The tag selects compiler release 0.1.4. For compiler development, use main
instead. Run the commands on this page from the repository root.
Install build prerequisites
To work on the frontend, install Rust 1.98.1 and its platform linker, then follow frontend development without LLVM.
Building the full Dodo compiler from source requires Rust 1.98.1 (pinned in
rust-toolchain.toml), LLVM 23 development files and a C toolchain.
llvm-sys provides the LLVM C API
bindings; the language server implements its JSON encoding and decoding,
JSON-RPC messages, LSP parameter validation, and file
URI conversion internally using Rust’s standard library. Cargo.lock
fixes the dependencies. Ordinary Cargo builds prefer static LLVM and fall back
to a shared LLVM library if static linking is unavailable. A compiler linked to
shared LLVM needs that library installed at runtime.
On Fedora with LLVM 23 packages:
sudo dnf install llvm-devel llvm-static clang gcc gcc-c++ zlib-ng-compat-devel libzstd-devel libxml2-devel libffi-devel
export LLVM_SYS_231_PREFIX=/usr/lib64/llvm23
cargo build --locked --release
cargo install --locked --path .
On Ubuntu 24.04, install LLVM 23 from the official LLVM package repository and a C compiler:
# After configuring the signed LLVM 23 apt repository:
sudo apt-get install llvm-23-dev libpolly-23-dev build-essential zlib1g-dev libzstd-dev libxml2-dev libffi-dev
export LLVM_SYS_231_PREFIX=/usr/lib/llvm-23
cargo build --locked --release
cargo install --locked --path .
The Ubuntu packages are suitable for ordinary Cargo builds. CI uses the
official prebuilt LLVM archive for releases,
so the compiler does not depend on Z3. If LLVM is installed elsewhere, set
LLVM_SYS_231_PREFIX to its installation prefix containing bin/llvm-config.
To prefer shared LLVM explicitly, use:
cargo build --locked --release --features llvm-sys/prefer-dynamic
Use --features llvm-sys/force-static to require static LLVM. The release
packaging recipes select this feature explicitly. These linking features are
mutually exclusive; select only one per build.
Ordinary Cargo builds can also link LLVM’s smaller support libraries (such as zlib, zstd, and the C++ library) dynamically. Use the self-contained Linux recipe to bundle those as well.
Size-focused compiler build
Use the release-small profile to minimize the Dodo compiler executable with
stable compiler options:
cargo build --locked --profile release-small --bin dodo
# Binary: target/release-small/dodo
cargo install --locked --path . --profile release-small
This profile uses size optimization (opt-level = "z"), full link-time
optimization, one codegen unit, and symbol stripping. Compiler panics abort the
process instead of unwinding. Builds may take longer and the compiler may run
more slowly. All LLVM targets remain included; prebuilt LLVM archives limit how
much Rust compiler options can reduce the final size. Actual size depends on the
platform and toolchain.
For the self-contained Linux recipe below, run
bash scripts/build-release.sh release-small; its binary is written to
target/x86_64-unknown-linux-gnu/release-small/dodo.
Self-contained Linux release
On an x86-64 Ubuntu 24.04 build machine, install the release toolchain below, then run:
export LLVM_SYS_231_PREFIX="$PWD/target/llvm-linux"
bash scripts/build-release.sh
install -Dm755 target/x86_64-unknown-linux-gnu/release/dodo "$HOME/.local/bin/dodo"
The resulting binary contains LLVM, the C++ standard library, zlib, zstd, and
any needed libffi code. Its only permitted shared dependencies are the standard
Linux C runtime (libc.so.6), math library (libm.so.6), unwind library
(libgcc_s.so.1), and ELF loader. The tested runtime baseline is x86-64 Linux
with glibc 2.39 or newer (Ubuntu 24.04). LLVM and its support packages are only
needed on the build machine. Keeping every LLVM target increases the binary
size compared with the old shared-LLVM build.
The script requires Python 3 and readelf (binutils) for verification and honors
LLVM_SYS_231_PREFIX, CC, and CARGO_TARGET_DIR. Other native x86-64 GNU/Linux
build hosts need the same development libraries, including libz.a,
libzstd.a, libstdc++.a, and libffi.a. Their runtime glibc requirement depends
on the build host. Other platforms can use the ordinary Cargo build above;
the reduced support-library dependency list is only enforced for this Linux
release recipe.
The release build checks its ELF dependencies and fails if an unexpected shared library remains. CI also copies it into a clean Ubuntu container, checks code and emits native and WebAssembly objects before installing any compiler tools, then builds and runs a program with only the C toolchain added.
Windows release archive
The Windows release targets x86_64-pc-windows-msvc. It embeds LLVM and links
the compiler’s C and C++ runtimes statically. Building it requires the Visual
Studio C++ Build Tools with a Windows SDK, Rust 1.98.1, and the official
clang+llvm-23.1.1-x86_64-pc-windows-msvc.tar.xz development archive from
LLVM 23.1.1.
Use the development archive containing llvm-config.exe and static libraries.
The CI workflow pins and verifies its download.
From PowerShell, after extracting LLVM to C:\llvm-23:
$env:LLVM_SYS_231_PREFIX = "C:\llvm-23"
$env:PATH = "$env:LLVM_SYS_231_PREFIX\bin;$env:PATH"
$env:CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_RUSTFLAGS = "-C target-feature=+crt-static"
rustup target add x86_64-pc-windows-msvc
rustup component add rust-docs
python scripts/build-windows-llvm-support.py
cargo build --locked --features llvm-sys/force-static --release --bin dodo --target x86_64-pc-windows-msvc
python scripts/test_windows_native.py --linker clang
python scripts/package-release.py --target x86_64-pc-windows-msvc
python scripts/test_windows_release.py build/release-assets/dodo-0.1.4-x86_64-pc-windows-msvc.zip --linker clang
The support script requires CMake and Visual Studio 2022. It builds the libxml2,
zlib, and Zstandard libraries omitted from LLVM’s archive, using LLVM’s pinned
versions and static C runtimes, and installs them and their notices into
LLVM_SYS_231_PREFIX. On Windows it also builds a small llvm-config-23.exe
adapter with Rust. The adapter delegates to the official llvm-config.exe and
replaces build-machine paths in --system-libs with names of libraries installed
under the LLVM prefix, so llvm-sys can link them after extraction or relocation.
For cross builds, its --cmake-toolchain option accepts
an MSVC CMake toolchain such as the one generated by cargo-xwin.
The packaging script writes the Windows ZIP to build/release-assets/ alongside
any Linux tarball. Both archives include the compiler, installation
instructions, and dependency notices. It does not produce a SHA256SUMS file.
The default python3 scripts/package-release.py still packages the Ubuntu
Linux release.
The Windows archive test extracts to a temporary directory, checks the version
and embedded standard library, emits native and WebAssembly objects, and builds
and runs a program at -O 0 and -O 3. On Linux, pass --runner /path/to/wine
to both packaging and testing to use a cross-built Windows compiler. Set
WINEPREFIX to an isolated prefix, supply a Windows Clang executable with
--linker, and use repeated --link-arg=ARG options for the MSVC SDK and runtime
library search paths required by that toolchain.
The native Windows CI job also runs scripts/test_windows_native.py before
packaging. It builds and executes filesystem, process, console-handle, TCP/UDP,
thread, and synchronization fixtures at -O0 and -O3 with the normal compiler
linking path. Tests cover Unicode paths, sharing restrictions, rename/delete
with open handles, child pipe EOF and inheritance, IPv4/IPv6 loopback, socket
inheritability, condition waits, cancellation, and repeated handle cleanup.
Each execution uses a separate temporary working directory; failed commands
report their output and exit code, and timeouts terminate the process tree.
Use --compiler PATH to select a compiler and repeat
--fixture tests/os/fs_windows_checks.dodo to select individual fixtures.
The runner requires native Windows, Clang, and the Visual Studio C++/Windows SDK
libraries. The separate Wine suite remains available for cross-platform checks.
Fully static Linux compiler
Install static archives for the C and math runtimes, the C++ standard library,
zlib, zstd, libffi, and any additional libraries listed by
llvm-config --link-static --system-libs.
CI uses the official LLVM 23.1.1 Linux x86-64 archive, which has Z3 disabled. On Ubuntu 24.04, install its prerequisites and the same toolchain:
sudo apt-get install build-essential zlib1g-dev libzstd-dev libxml2-dev libffi-dev python3 curl xz-utils
export LLVM_SYS_231_PREFIX="$PWD/target/llvm-linux"
python3 scripts/install-linux-llvm.py
export PATH="$LLVM_SYS_231_PREFIX/bin:$PATH"
The installer verifies pinned SHA-256 checksums and extracts LLVM’s static
libraries, headers, required tools, and license. The download is about 1.9 GB;
CI caches the extracted installation. To reuse a downloaded archive, pass
--archive /path/to/LLVM-23.1.1-Linux-X64.tar.xz. No LLVM or Z3 source build is
needed.
Ubuntu’s llvm-23-dev package enables Z3 and cannot use this fully static recipe
without an additional static Z3 library. Use the official archive above or an
LLVM build configured with
-DLLVM_ENABLE_Z3_SOLVER=OFF.
This is an LLVM build option, not a Cargo setting. The release script checks all
libraries reported by llvm-config; it does not ignore missing dependencies.
On Fedora, additionally install glibc-static, libstdc++-static,
zlib-ng-compat-static, libzstd-static, and libxml2-static if LLVM uses
libxml2. If your distribution does not ship libffi.a, build a static libffi.
Archives in custom locations can be made available through LIBRARY_PATH.
On a native x86-64 GNU/Linux build host, use:
# Keep LLVM_SYS_231_PREFIX set to the selected LLVM installation.
bash scripts/build-release.sh release-small-static
# Binary: target/x86_64-unknown-linux-gnu/release-small-static/dodo
release-small-static inherits all release-small size settings. The script
adds static C-runtime linking and static relocation, producing a non-PIE
executable, and links LLVM’s support libraries statically. It rejects a binary
with any shared-library dependency or ELF interpreter. Use the script: selecting
the Cargo profile alone does not supply the required Linux linker flags.
All LLVM targets remain included. This profile minimizes size through compiler
and linker options; the size still depends on the prebuilt LLVM archives and
platform libraries. It includes glibc, so it can be larger than release-small
despite having no shared-library dependencies. Checking code and emitting
compiler artifacts need no external tools; linking and running Dodo programs
still needs a C toolchain. Only native x86-64 GNU/Linux builds are supported by
this recipe.
Development
Frontend development without LLVM
The parser, semantic checker, constant evaluation, package loader, formatter, editor analysis, owned TOML parser, project resolver, and CLI option parser can be built and tested without LLVM development files:
cargo test --locked --no-default-features --all-targets
cargo clippy --locked --no-default-features --all-targets -- -D warnings
# Run only parser or semantic checker unit tests:
cargo test --locked --no-default-features --lib parser::tests
cargo test --locked --no-default-features --lib sema::tests
# Focus on project manifests and CLI parsing:
cargo test --locked --no-default-features --test toml --test project --test cli_options
The default llvm Cargo feature enables llvm-sys, native code generation, the
LSP server (which validates targets through LLVM), and the dodo binary. With
--no-default-features, Cargo skips the binary and integration tests that require
it or LLVM; frontend cases in mixed test files still run. Package loading uses
the compiler’s Rust target to select host standard library adapters, and callers
can use package::load_for_target to select another target explicitly.
CI runs these checks in a separate job without installing LLVM. Library users
can select the same frontend with default-features = false on their dodo
dependency; its Rust crate name remains dodoc.
Full compiler checks
With LLVM 23 and the native prerequisites installed:
cargo fmt --all --check
cargo clippy --locked --all-targets -- -D warnings
cargo test --locked --all-targets
cargo run --locked -- test
cargo run --locked -- test -O 3
cargo build --locked --release
python3 scripts/render_spec.py
python3 scripts/render_spec.py --check
python3 scripts/test_check_linkage.py
python3 scripts/test_build_release.py
bash scripts/build-release.sh # x86-64 GNU/Linux release dependency check
cargo test --locked --test project_cli checks manifest discovery, argument and
profile overrides, output preservation, initialization, hosted tests, and real
linker/program working directories. LSP tests also cover manifest platform
selection and configuration reloads.
CI runs these checks, including native Dodo tests and executable documentation.
Tests include rejected programs, specification examples,
native execution at -O0 and -O3, destructor ordering, error propagation,
cross-target object emission, local imports, and expected runtime traps. They
exercise the actual compiler and generated binaries rather than matching LLVM
text alone. On systems that save core dumps, ulimit -c 0 before running the
trap tests avoids creating crash artifacts.
cargo test --locked --test lsp exercises the real compiler’s LSP lifecycle,
framing, buffer updates, imported diagnostics, directory packages, Unicode
positions, completion, navigation, rename, signatures, formatting, target selection,
and recovery from invalid source and notifications. Independent JSON-RPC fixtures
also check request IDs, malformed envelopes, parameter types, and atomic buffer
updates; unit tests cover bounded framing and file/untitled URI validation. Use the
LSP benchmark to measure responsiveness separately from
correctness tests.
The pipeline is organized into lexer,
parser, package,
prepare, sema,
consteval, and codegen, with a reusable
library, an LSP server, and a small CLI driver.
For documentation changes and website checks, see Edit these docs.
Debugger smoke tests in tests/debugging.rs run batch GDB sessions and validate
DWARF with llvm-dwarfdump-23. Install both tools and run:
DODO_REQUIRE_DEBUGGER_TESTS=1 cargo test --locked --test debugging
CI requires these tools. Local runs skip the relevant smoke tests if a tool is
missing, unless DODO_REQUIRE_DEBUGGER_TESTS is set. Runtime-reporting and panic
hook tests always run.