Skip to content

ADR 008: CLI multi-platform distribution via native execution platforms

Author: Joe McGinley Status: Accepted Created: 2026-06-12 Refines: ADR 006 (extends its per-arch model to CLI distribution and resolves its pool-availability question)


Problem

The OCaml ruleset (ADR 004/005/006) will eventually produce a user-distributed CLI (Semgrep-derived), not just cluster images. That changes the target matrix: linux x86_64/arm64, macOS arm64 (must-have within a year), possibly macOS x86_64 and Windows later. ADR 006 fixed the per-arch mechanism for the cluster but left distribution unaddressed, and the obvious gap was uncomfortable: OCaml appeared to force "Bazel for linux plus a separate non-Bazel mac lane", a fragmented release story.

Deep research (June 2026, multi-source with 3-vote adversarial verification of the load-bearing claims) re-examined the strategy space: upstream OCaml cross-compilation, cross tooling, wasm, QEMU emulation, and how peer OCaml CLIs actually ship. This ADR records the verified findings and the decided direction.

Decision

One Bazel graph, multiple native execution platforms, all scheduled through BuildBuddy. The build definition stays singular; only the execution platform varies per target platform.

TargetExecutionNotes
linux x86_64BuildBuddy cloud executors (today's pool)unchanged
linux arm64BuildBuddy cloud arm64 executors (Arch: arm64)cloud pool exists since 2026-01-15, un-gating ADR 006's Phase 7 with zero infra on our side
macOS arm64Self-hosted BuildBuddy darwin executor on genuine Apple hardwareEC2 Mac dedicated host or a rack Mac mini; choice deferred to implementation. Release-lane platform (nightly/tag builds), not per-PR CI
WindowsOut of scope until demand existssemgrep's native MinGW-on-windows-runner pipeline is a proven template for our exact C-stub stack when needed

Supporting decisions:

  • Post-5.4 consolidation option (not a prerequisite): vanilla OCaml 5.4+ can build a linux-x86_64 to linux-arm64 cross compiler (upstream CI-tested). Once our pin moves to 5.4 (semgrep upstream is moving before EoY), linux arm64 builds can consolidate onto the amd64 pool, keeping a small arm64 test shard. This restores the single-pool uniformity the rest of the repo already has (Go cross-compiles via transitions; apko assembles prebuilt apks), and is a contained optimization on top of this decision.
  • Distribution layer: musl-static linux binaries (the dune/unison pattern) rather than a glibc/manylinux matrix; GitHub artifact attestations on release tarballs (the dune pattern).
  • macOS is structurally different from linux arm64, and the plan accepts that. A compile target is CPU + OS + ABI/libc, not an architecture. The aarch64 code generator is shared, but darwin means Mach-O + ld64 + mandatory code signing, and compiling/linking against libSystem and the Apple SDK, which the Xcode EULA ties to Apple hardware. That licensing fact, not a technical gap, is why no clean linux-to-macOS toolchain exists in any language ecosystem and why mac capacity means Apple hardware (which EC2 Mac provides legitimately).

Architecture

mermaid
graph TD
    G[One Bazel graph<br/>same //...:cli targets] --> S[BuildBuddy scheduler]
    S --> A[Cloud pool<br/>linux amd64]
    S --> B[Cloud pool<br/>linux arm64<br/>since 2026-01]
    S --> C[Self-hosted darwin executor<br/>EC2 Mac or rack Mac mini<br/>release lane only]
    A --> R[Release artifacts<br/>musl-static linux, mac arm64<br/>+ attestations]
    B --> R
    C --> R

Verified findings

Load-bearing claims were adversarially verified (3 independent votes each, primary sources):

FindingStatus
BuildBuddy launched autoscaled cloud linux/arm64 RBE executors 2026-01-15; darwin/windows executors remain self-hosted only3/3 confirmed
Upstream OCaml CI cross-builds linux-x86_64 to aarch64-linux-gnu (plus mingw64, Android); no darwin target3/3 confirmed
The simplified cross machinery (PR #13526) ships in 5.4.0+, not vanilla 5.3 (the "5.3 cross compiler" demo used backports)2/3 corrected the 5.3 claim
No publicly available linux-to-macOS toolchain for OCaml 5.x exists (osxcross-based repos frozen at 4.14; the zig attempt died at link errors and was abandoned)confirmed (softened from "none working": one company privately crosses a 5.2 backport to macOS, host unspecified)
QEMU user-mode emulation: 5-20x slowdown on compiler workloads, documented Bazel breakage, no production RBE-under-QEMU precedentconfirmed
wasm_of_ocaml is mature for JS hosts (~2x native; Jane Street production) but the standalone WASI runtime is still an unmerged PR; C stubs are hand-written glue; semgrep retired its JS/wasm Windows path in favor of native3/3 confirmed
Every peer OCaml CLI (semgrep, dune, flow, unison, infer, ocamlformat) ships from native runners per OS/arch; none cross-compile; semgrep's arm64 wheels build on Depot native arm builders (QEMU as enabled fallback only)confirmed (one secondary-source detail corrected)
semgrep ships native Windows since Fall 2025: native windows-2022 runner + MinGW-w64 + opam, keeping PCRE2 and tree-sitter as native DLLsconfirmed

Alternatives Considered

  • linux-to-macOS cross-compilation: no public OCaml 5 toolchain, the one serious attempt (zig cc) abandoned at link errors, and the Apple SDK is EULA-bound to Apple hardware. Dead for the planning horizon.
  • QEMU emulation for foreign arches: 5-20x on compiler-heavy work plus documented Bazel breakage. Even semgrep keeps it only as a fallback.
  • wasm distribution: Node-shaped today (no merged WASI runtime), manual C stub glue, and the closest peer retired this approach. Retained as a future browser/playground option only.
  • A GitHub-Actions-only release lane outside Bazel: loses hermeticity, RBE caching, and the single build definition; this fragmentation concern is what prompted the research.
  • Dropping macOS: fails the stated must-have.

Security

Baseline per docs/security.md. New surface: a self-hosted darwin executor joins the build plane; its BuildBuddy API key should be scoped to that executor role and the host treated as release infrastructure (patched, access-controlled). Release artifacts gain GitHub artifact attestations. Distributing macOS binaries will eventually require Apple Developer ID signing/notarization (Gatekeeper); that pipeline is deliberately out of scope here and tracked as an open question.

Risks

RiskLikelihoodImpactMitigation
EC2 Mac economics (24h minimum dedicated-host billing) make the mac lane expensive if treated as always-on CIMediumLowRelease-lane only; scheduled allocate/build/release automation, or a rack Mac mini (no minimum, one-time cost)
Darwin executor is a pet (macOS updates, Xcode CLT, executor upgrades)HighLowOne host, release-lane only; AMI/snapshot the known-good image
BuildBuddy cloud mac RBE turns out to exist and we built self-hosted unnecessarilyLowLowAsk BuildBuddy before provisioning; architecture is identical either way
5.4 pin bump slips (cross consolidation deferred indefinitely)MediumLowConsolidation is an optimization; the arm64 cloud pool path works on 5.3 today
macOS signing/notarization blocks first mac releaseMediumMediumResolve the Developer ID question before announcing mac support

Open Questions

  1. EC2 Mac dedicated host vs rack Mac mini for the darwin executor (ops/cost preference; swappable later).
  2. Apple Developer ID signing + notarization pipeline for distributed mac binaries.
  3. Whether/when Windows demand justifies replicating semgrep's MinGW pipeline.

References

ResourceRelevance
BuildBuddy: Remote Builds on linux/arm64cloud arm64 executors (2026-01-15)
BuildBuddy RBE platforms docsArch exec_property; darwin self-hosted only
ocaml/ocaml build-cross.ymlupstream-tested cross targets
ocaml/ocaml PR #13526cross-compiler build simplification (5.4.0)
discuss: OCaml 5.3 cross compiler5.3 needs backports
discuss: zig as OCaml cross compilerlinux-to-darwin attempt abandoned
semgrep v1.100.0 release workflowspeer pipeline: native runners per platform
semgrep windows.libsonnetnative Windows MinGW template (our C-stub stack)
Tarides: wasm_of_ocaml optimisations (2026-02)WASI still an open PR
dune binary-distributionmusl-static + attestation distribution pattern
uber/hermetic_cc_toolchainzig cc for the C half of future cross consolidation
docs/decisions/tooling/006-extensible-multiarch-ocaml-toolchains.mdthe per-arch mechanism this extends