How to build a mando-cli release binary for Linux x86_64 / WSL locally from an Apple Silicon (ARM64) Mac. This is the manual, one-off path — for the automated CI pipeline see mando-cli-github-build-mirror.

For Agents

  • Binary: mandoproject: mando-cli v0.4.0 — repo: /Volumes/bandi/coding/poc/mando-cli
  • Target: x86_64-unknown-linux-musl (fully static, CI artifact name mando-linux-x86_64).
  • Two non-obvious gotchas below must both be handled or the build fails. Skip to Working Command for the verified one-liner.
  • Use this note when a tester needs an ad-hoc Linux/WSL binary and CI is not an option. Otherwise prefer CI / install scripts.

When to Use This vs CI

PathUse when
CI build (mando-cli-github-build-mirror)Normal releases — GitHub Actions Build Linux x86_64 job on native x86_64 Linux runners. Ships mando-linux-x86_64.
This noteAd-hoc local build, e.g. a tester needs a binary now and a release/tag is not warranted.

CI runners are native x86_64 Linux, which is exactly why CI never hits the gotcha below. Reproducing it locally on Apple Silicon requires the --platform linux/amd64 workaround.

Target Choice — x86_64-unknown-linux-musl

Use the musl target, not glibc:

  • Produces a fully static (static-pie) binary — no libc version dependency.
  • Runs on any WSL distro (Ubuntu, Alpine, Debian, …) regardless of its glibc version.
  • Matches the CI artifact mando-linux-x86_64.

Gotcha 1 — Docker pulls the ARM64 image on Apple Silicon

The natural first attempt is to build inside the CI base image rust:1.88-bookworm via Docker. On Apple Silicon, Docker pulls the arm64 image variant by default.

This breaks musl cross-compilation because of a transitive C dependency:

  • mando-clireqwest (rustls TLS) → ring 0.17.
  • ring is not pure Rust — it contains C crypto sources (curve25519.c, etc.) and its build.rs uses the cc crate.
  • Inside an arm64 container, cc invokes musl-gcc with -m64 to produce x86_64 objects, but the arm64 musl-gcc rejects that flag:
cc1: error: unrecognized command-line option '-m64'

The error blames the C compiler, not Docker

The failure surfaces deep in ring’s build script as a cc1 error. It looks like a toolchain bug. The real cause is the container architecture mismatch — an arm64 musl-gcc cannot emit x86_64 code via -m64.

Fix — force an x86_64 container

Pass --platform linux/amd64 to docker run:

  • The container is then genuinely x86_64 (OrbStack runs it via Rosetta emulation).
  • musl-gcc inside is x86_64-native, so the musl build is same-arch — no -m64 cross-compile.
  • This mirrors the CI environment (native x86_64), which is why CI never hits this.

Gotcha 2 — Path dependencies leak in during resolution

mando-cli’s Cargo.toml has an optional query feature with path dependencies pointing into a sibling git worktree:

../mando/.worktrees/BE-1595/mandarrow-client
../mando/.worktrees/BE-1595/mando-core

Cargo reads path-dep manifests even for disabled features

query is off by default and its code is not compiled, but Cargo still reads those Cargo.toml manifests during dependency resolution. If the container cannot see those paths, resolution fails before any compilation starts.

Fix — mount the poc/ parent directory

Mount the parent poc/ directory, not just mando-cli/:

-v /Volumes/bandi/coding/poc:/work

so the container can resolve ../mando/.worktrees/.... Build with the query feature OFF (the default — do not pass --features query).

Working Command

Verified build command (run from the host; ~2m27s with crates cached in the mando-cli-cargo Docker volume):

docker run --rm --platform linux/amd64 \
  -v /Volumes/bandi/coding/poc:/work \
  -v mando-cli-cargo:/cargo \
  -w /work/mando-cli \
  -e CARGO_HOME=/cargo -e CARGO_TARGET_DIR=/build-target \
  rust:1.88-bookworm \
  bash -euxc 'apt-get update -qq && apt-get install -y -qq musl-tools pkg-config libssl-dev && rustup target add x86_64-unknown-linux-musl && cargo build --release --target x86_64-unknown-linux-musl'
ElementWhy
--platform linux/amd64Gotcha 1 — forces a native-x86_64 container so musl-gcc is same-arch.
-v /Volumes/bandi/coding/poc:/workGotcha 2 — exposes the ../mando/.worktrees/... path deps for resolution.
-v mando-cli-cargo:/cargo + CARGO_HOME=/cargoPersistent crate cache across runs (registry + git checkouts).
CARGO_TARGET_DIR=/build-targetKeeps build artifacts off the bind mount (faster, avoids polluting the host target/).
musl-toolsProvides musl-gcc, required by the musl target + ring’s cc build.
pkg-config libssl-devBuild-time deps satisfied for the toolchain image.

Extract the binary

The binary lands inside the container at:

/build-target/x86_64-unknown-linux-musl/release/mando

Copy it out to dist/mando-linux-x86_64. One way — append a copy step to the in-container script writing into /work (the bind mount), e.g. cp /build-target/x86_64-unknown-linux-musl/release/mando /work/mando-cli/dist/mando-linux-x86_64.

Verification Performed

The produced binary was verified before handing to the tester:

CheckResult
fileELF 64-bit LSB pie executable, x86-64, static-pie linked
ldd (in ubuntu:24.04)statically linked
mando --version (in ubuntu:24.04)mando cli 0.4.0 — exit 0
mando --help (in ubuntu:24.04)full CLI output — exit 0
Run in alpine (musl base)OK
Size~13 MB, unstripped
sha256bf5a99b98257a83fe6065ae37bd7227be4cdc9eb484401c30d98456877f3b76c

ubuntu:24.04 was used as the verification environment because it is representative of what WSL runs.

Runtime Requirement on WSL

WSL needs Docker Compose v2

The mando binary shells out to docker compose at runtime. The WSL environment must have Docker installed with docker compose v2.20+ available — the static binary itself does not bundle it. See mando-cli-docker-lifecycle for the runtime model.

Alternative — cargo-zigbuild (not used here)

cargo-zigbuild uses Zig as the cross C-compiler and linker. It also produces a working x86_64-unknown-linux-musl binary and runs natively on the ARM host with no emulation (faster than a Rosetta-emulated container). Not used for this build because it requires installing zig + cargo-zigbuild on the host; the Docker path needs no extra host tooling and mirrors CI more closely.