# 🤖 Toolchains

Installed on docs-t1 (x86-64, Debian 13) under `$HOME`, with no `sudo`,
by `scripts/toolchains.sh`. Put them on `PATH` with
`source scripts/env.sh`. Carloku runs **linux-arm64**, so each language
also needs a way to produce a static (or libc-only) arm64 binary.
`scripts/smoke.sh <lang>` proves that route with a hello-world: it builds
the binary, checks it's aarch64 and static, and runs it under qemu.

Checked 2026-10-23.

| Setup | Versions on docs-t1 | Route to linux-arm64 | Smoke |
|---|---|---|---|
| Go, Go + Rastrillo | Go 1.26.10 | `CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build` | ok, static |
| Rust | rustc 1.99.1, cargo-zigbuild 0.24 | `cargo zigbuild --target aarch64-unknown-linux-musl` (zig is the linker) | ok, static |
| Zig | Zig 0.17.0 | `-target aarch64-linux-musl` | ok, static |
| Swift | Swift 6.2 + Static Linux SDK | `swift build --swift-sdk aarch64-swift-linux-musl` | ok, static |
| OCaml | opam 2.6.1, OCaml 5.3.0 (switch `bakeoff`) | Build **inside the arm64 root**: OCaml 5.3.0, ocamlfind and opam 2.3 for arm64, link with `-ccopt -static` | ok, static |
| Haskell | GHC 9.10.3, cabal 3.16 (ghcup) | Build **inside the arm64 root**: GHC 9.6.6 and cabal 3.10 for arm64, link with `-static -optl-static -optl-pthread` | ok, static |
| Nim | Nim 2.2.12 | `nim c --cpu:arm64 --os:linux --cc:clang` with `--clang.exe` and `--clang.linkerexe` set to `scripts/zigcc-aarch64-musl` (zig cc, musl) and `--passL:-static` | ok, static |
| Spinel | spinel `abf54280` (tep's pin), tep, spinelkit, Ruby 4.0.9 + prism (pixi) | Either build Spinel **inside the arm64 root** and compile with `--cc="cc -static"`, or follow lpay's zig cross-build (`paulca/lpay` `spinel/BUILD-arm64.md`) | ok, static (arm64 root route) |

## The arm64 root

`scripts/arm64.sh <command>` runs a command in a Debian 13 arm64 root
filesystem (`~/.local/share/bakeoff-toolchains/arm64root`) through proot
and qemu-user. It needs no root access and no binfmt. The repo, `$TMPDIR`
and the toolchains directory are visible inside at the same paths.
Everything in it runs emulated, so builds are several times slower than
native. Install extra Debian packages with
`scripts/arm64.sh apt-get install -y <pkg>`.

**The root is shared.** Other builders use it at the same time. If `apt`
or `dpkg` reports a lock, wait a minute and retry. Install what you need,
but never remove or downgrade packages others may rely on, and keep your
build output and caches under your own app directory (for example
`apps/<slug>/.cache/`), not in the root's `/root` or `/tmp`.

To run any arm64 binary on docs-t1:
`qemu-aarch64-static -L ~/.local/share/bakeoff-toolchains/arm64root <binary>`.

## Notes for builders

- **OCaml and Haskell:** the x86 compiler versions differ from the arm64
  root's (OCaml 5.3.0 in both; GHC 9.10.3 on x86 but 9.6.6 in the root).
  Choose library versions that build with the arm64 root's compiler, or
  install a matching one inside the root (ghcup and opam both work
  there, emulated).
- **Static glibc** (OCaml, Haskell, Spinel via the arm64 root) warns about
  `getaddrinfo` and `dlopen`. That's fine for an app that doesn't resolve
  hostnames itself.
- **Mojo was removed from the bakeoff** (Paul's call, 2026-10-24). Mojo
  1.2.0 can't make a single-file binary: every executable needs
  `libKGENCompilerRTShared.so` (plus `libstdc++`, `libMSupportGlobals.so`
  and `libAsyncRTRuntimeGlobals.so`), and no static runtime ships. In 2023
  its authors described fully standalone binaries
  ([news.ycombinator.com/item?id=35797485](https://news.ycombinator.com/item?id=35797485));
  that no longer holds. The finished app is kept on the `app/mojo` branch,
  and the platform Idea is
  [amadan.net/carlos/platform discussion 360](https://amadan.net/carlos/platform/-/discussion/360).
- **Fallback:** if a route above fails for a real app, the builder writes
  `BLOCKED needs-arm64-host`, and the orchestrator arranges a native
  arm64 machine.
