Skip to main content

Build native binaries

mochi build --target=c-aot compiles a Mochi source file to a statically linked native binary via an intermediate ISO C23 source and a bundled cross-compiler. No C toolchain installation is required; the first build invocation downloads a pinned zig cc binary and caches it under ~/.cache/mochi/.

Quick start

# Compile hello.mochi to ./hello
mochi build --target=c-aot --out=hello hello.mochi
./hello

The binary has no shared library dependencies beyond the system libc (or none at all when you add --portable).

Flags

FlagDefaultDescription
--target=c-aotrequiredSelect the native AOT pipeline
--out PATHrequiredOutput binary path
--triple TRIPLEhostTarget triple (cross-compile)
--profile PROFILEreleaserelease or debug
--portableoffStatically link with musl; no shared-library deps
--emit=coffKeep the generated .c source next to the binary
--cc PATHautoOverride the C compiler ($CC env also accepted)
--apexoffBuild an Actually Portable Executable via cosmocc

Cross-compilation

Pass any tier-1 triple with --triple. The driver routes through zig cc automatically and downloads zig on first use.

# Build for a Linux x86_64 server from any host
mochi build --target=c-aot --triple=x86_64-linux-musl --out=app-linux app.mochi

# Build a WASM module runnable under wasmtime
mochi build --target=c-aot --triple=wasm32-wasi --out=app.wasm app.mochi
wasmtime run app.wasm

Tier-1 triples

TripleDescription
x86_64-linux-gnuLinux x86_64, glibc
x86_64-linux-muslLinux x86_64, musl (fully static)
aarch64-linux-gnuLinux arm64, glibc
aarch64-linux-muslLinux arm64, musl
aarch64-macos-nonemacOS Apple Silicon
x86_64-macos-nonemacOS Intel
x86_64-windows-gnuWindows x86_64, MinGW ABI
x86_64-windows-msvcWindows x86_64, MSVC ABI
wasm32-wasiWebAssembly (wasmtime / wasmer)

When --triple is omitted, the driver compiles for the host triple using the host C compiler.

Build profiles

# Debug build: adds -g and ASan + UBSan instrumentation
mochi build --target=c-aot --profile=debug --out=app-debug app.mochi

# Release build (default): no debug info, no sanitisers
mochi build --target=c-aot --out=app app.mochi

The debug profile adds -g -fsanitize=address,undefined -fno-sanitize-recover=all. Debug builds abort on the first memory or undefined-behaviour error, making it easy to catch bugs before shipping the release binary.

Portable (static) binaries

On Linux, --portable links against musl via zig cc so the binary has zero shared-library dependencies:

mochi build --target=c-aot --portable --out=app-static app.mochi
file app-static
# app-static: ELF 64-bit LSB executable, statically linked

On macOS, static binaries require a musl cross-compile (use --triple). The --portable flag is silently ignored for wasm32-wasi targets (WASM is always fully self-contained).

Actually Portable Executables

--apex builds a single binary via cosmocc that runs natively on Linux, macOS, Windows, FreeBSD, NetBSD, and OpenBSD without any toolchain install on the target machine:

# Requires cosmocc on PATH or MOCHI_COSMOCC_PATH set
mochi build --target=c-aot --apex --out=app app.mochi
./app # runs on macOS
scp app remote:~ && ssh remote ./app # runs on Linux

Install cosmocc from cosmo.zip or set MOCHI_COSMOCC_PATH to point to an existing install. Unlike --triple (which requires zig cc), --apex needs no cross-compiler and no sysroot downloads; cosmocc carries everything internally.

Inspecting the generated C source

mochi build --target=c-aot --emit=c --out=app app.mochi
# Produces: app (binary) and app.c (generated source)
cat app.c

Use --emit=c to audit what the compiler produces, to vendor the output for a custom build system, or to share with a C tool that consumes ISO C23.

Reproducible builds

Two builds of the same source on the same host triple and with the same profile produce bit-identical binaries. The driver:

  • Strips absolute temp-dir paths from DWARF sections (-ffile-prefix-map).
  • Suppresses the random UUID in Mach-O binaries on macOS (-Wl,-no_uuid).
  • Uses a content-addressed binary cache under ~/.cache/mochi/ keyed on source hash + profile + triple + runtime fingerprint.

Caching

The driver caches compiled binaries under ~/.cache/mochi/. On a cache hit the output binary is restored in milliseconds:

$ time mochi build --target=c-aot --out=app app.mochi
cached app
mochi build 0.01s

The cache is keyed on the source file content, the profile, the target triple, and a fingerprint of the libmochi runtime. Changing any of these produces a fresh build.

Compiler override

Set $CC or --cc to use a specific compiler:

# Use a specific clang
mochi build --target=c-aot --cc=clang-18 --out=app app.mochi

# Use ccache for faster repeated builds
mochi build --target=c-aot --cc="ccache cc" --out=app app.mochi

When --triple is set and $CC is unset, the driver uses the bundled zig cc automatically.

C-direct FFI

Mochi programs can call C functions defined in a neighbour .c file:

// hello.mochi
extern fun c_add(a: int, b: int): int
print(c_add(3, 4))
// hello.c (same directory as hello.mochi)
#include <stdint.h>
int64_t c_add(int64_t a, int64_t b) { return a + b; }
mochi build --target=c-aot --out=hello hello.mochi
./hello
# 7

The driver detects hello.c alongside hello.mochi and compiles both in the same cc invocation. The extern fun declaration generates the matching C extern prototype automatically.

See also

  • Quickstart — run programs interactively with mochi run
  • Get started — build a complete CLI app
  • MEP-45 — technical spec for the AOT pipeline