RFD0017 - OCaml Cross-Compilation Snapshot
- Feature Name:
ocaml_cross_compilation_snapshot - Start Date:
2026-03-23 - Status:
implemented
Summary
Section titled “Summary”This RFD documents the current cross-compilation state carried by Riot’s
vendored OCaml fork under vendor/ocaml.
The current state is centered on one large fork commit,
ef81d5fd5 feat(cross): add relocatable cross-compilation tooling, plus the
supporting documents in:
vendor/ocaml/LINEAR.mdvendor/ocaml/CROSS_COMPILE_GUIDE.mdvendor/ocaml/CROSS_MSVC.mdvendor/ocaml/RELOCATABLE.mdvendor/ocaml/SUMMARY.md
At a high level, the fork currently provides:
- relocatable OCaml compiler installs
- scripted native and cross builds under
vendor/ocaml/cross/ - Linux glibc and musl target support
- MinGW Windows target scripts
- an exploratory MSVC design document rather than an implemented MSVC pipeline
At a high level, Riot/Riot currently provides:
- workspace-level
toolchain.targetsconfiguration inocaml-toolchain.toml riot build --target ...target selection and single-target execution- host and target-specific toolchain installation under
~/.riot/toolchains/ - build-context propagation of cross-compilation target metadata into the planner and executor
- partial consumption of cross target metadata, with stronger support for target-specific OCaml toolchains than for fully configured C toolchain plumbing
This RFD is a snapshot, not a proposal. It exists to make the current shape of the fork explicit before Riot integrates it more deeply into its own bootstrap, toolchain, and CI flows.
Motivation
Section titled “Motivation”Riot now vendors its OCaml fork under vendor/ocaml, and that fork carries a
substantial cross-compilation system that is larger than a normal “patch a few
compiler files” story.
Right now the relevant information is split across:
- fork-local implementation scripts in
vendor/ocaml/cross/ - multiple design and summary documents in the fork root
- one large fork commit that mixes code, scripts, packaging, and CI
- Riot-side bootstrap and toolchain code that is beginning to point at the vendored compiler tree
That makes it hard to answer questions like:
- what is actually implemented today
- which target combinations are scripted today
- whether Windows support is real or exploratory
- where relocatability comes from
- how much of this is in the compiler fork versus Riot’s own workflows
This RFD exists to capture the current state before more integration work lands.
Guide-level explanation
Section titled “Guide-level explanation”The current cross-compilation story has two layers:
- a vendored OCaml fork in
vendor/ocaml - Riot-side tooling that can eventually consume compiler installs produced by that fork
The fork is where almost all of the implemented behavior lives today.
Contributor mental model
Section titled “Contributor mental model”The current fork should be understood as:
- an OCaml source tree with a relocatable install model
- a
cross/directory full of build and packaging scripts - a matrix of native and cross target configuration files
- optional CI definitions that were moved out of active
.github/workflowsin the fork
It should not be understood as:
- a fully integrated Riot build flow yet
- a fully general target-agnostic cross-compilation framework
- an implemented MSVC pipeline
What “relocatable” means here
Section titled “What “relocatable” means here”The fork is designed to produce compiler installs that can be moved anywhere on disk as long as they preserve the usual layout:
bin/lib/ocaml/The important property is:
- the compiler and runtime should resolve
../lib/ocamlrelative to the executable location instead of relying only on a fixed absolute install path
That is what makes tarball distribution practical for Riot’s toolchain story.
What the current scripts do
Section titled “What the current scripts do”The main user-facing scripts live under vendor/ocaml/cross/:
build-native.shbuild-cross.shbuild.shpackage.shdownload-toolchains.shtest-musl.shtest-relocatable.sh
The target definitions live in vendor/ocaml/cross/targets/.
Today the scripted targets include:
- native macOS ARM64
- native Linux x86_64 glibc
- native Linux ARM64 glibc
- macOS ARM64 to Linux x86_64 glibc
- macOS ARM64 to Linux ARM64 glibc
- macOS ARM64 to Linux x86_64 musl
- macOS ARM64 to Linux ARM64 musl
- Linux x86_64 glibc to Linux ARM64 glibc
- Linux ARM64 glibc to Linux x86_64 glibc
- Linux x86_64 glibc to Linux x86_64 musl
- Linux ARM64 glibc to Linux ARM64 musl
- macOS ARM64 to Windows x86_64 MinGW
- Linux x86_64 glibc to Windows x86_64 MinGW
That means the current implemented Windows story is MinGW-based.
CROSS_MSVC.md is an exploration of how an MSVC-targeting path could work, but
it is not the same as saying Riot currently ships MSVC cross-compilers.
Where Riot itself is today
Section titled “Where Riot itself is today”The Riot repository is beginning to grow a self-contained compiler story around this vendored fork, but the fork is still the authoritative place for the cross-compilation implementation itself.
In current terms:
vendor/ocamlis the compiler source and scripted build surface- Riot bootstrap/toolchain code can prefer that vendored source for host toolchains
- Riot does not yet have a fully settled end-to-end workflow that exercises all of the fork’s target combinations from this repository alone
That distinction matters: the compiler fork already contains a lot of behavior, but Riot has not yet fully absorbed it into its own steady-state workflows.
What Riot itself can do today
Section titled “What Riot itself can do today”On the Riot side, the current cross-compilation entrypoint is riot build
with target selection.
The current user-facing pieces are:
ocaml-toolchain.tomlcan declaretoolchain.targets = [...]riot build --target <pattern>can resolve a configured targetriot build --all-targetsexists at the CLI surface, but the build command still rejects multiple matched targets and asks the user to pick oneriot toolchaincan inspect and install configured toolchains
The important current behavior is:
- host builds default to the current host triple
- a single explicit target triple can be selected for a build invocation
- the local session/server stack passes that target through to the build context
- the planner uses that build context for target-specific profile overrides and sysroot-aware C compile/link flags
The important current limitation is:
- Riot currently supports one build target per build invocation, even though the configuration format and some CLI surface area already anticipate multiple configured targets
Reference-level explanation
Section titled “Reference-level explanation”1. The cross-compilation implementation currently lives in one fork commit
Section titled “1. The cross-compilation implementation currently lives in one fork commit”The current cross-compilation system in the vendored compiler is primarily the result of one fork commit:
vendor/ocaml@ef81d5fd5
That commit introduces:
- cross build scripts
- packaging and test helpers
- target definitions
- relocatability documentation
- Windows exploration documentation
- vendored FlexDLL support under
cross/flexdll - disabled fork-local GitHub workflow definitions under
.github/workflows-disabled
So the current implementation should be treated as a fork-local subsystem, not as a small patch series sprinkled through upstream OCaml.
2. Relocatability is implemented by source changes, not only scripts
Section titled “2. Relocatability is implemented by source changes, not only scripts”The fork documentation is slightly inconsistent about how small the core compiler change is.
cross/README.md emphasizes a one-line Makefile change, but the current source
tree and the other fork documents show a broader implementation that currently
touches at least these files:
Makefileutils/config.generated.ml.inutils/config.common.ml.inruntime/startup_byt.cconfigure.ac
The durable current story is:
- the install layout is made relocatable by compiler/runtime path-resolution changes in the OCaml source tree
- the
cross/scripts are built on top of that relocatable behavior
2.1 Runtime and compiler path resolution
Section titled “2.1 Runtime and compiler path resolution”The current source confirms:
utils/config.common.ml.inusesSys.executable_nameruntime/startup_byt.cusescaml_executable_name()- both compiler/runtime config paths now work with relative stdlib locations
So the current fork should be described as “relocatable compiler/runtime path resolution is implemented in source”, not merely “packaging scripts arrange portable directories”.
3. Current scripted target surface
Section titled “3. Current scripted target surface”The target definitions currently present under vendor/ocaml/cross/targets/
are:
aarch64-apple-darwin.shx86_64-unknown-linux-gnu.shaarch64-unknown-linux-gnu.shaarch64-apple-darwin-x-aarch64-unknown-linux-gnu.shaarch64-apple-darwin-x-x86_64-unknown-linux-gnu.shaarch64-apple-darwin-x-aarch64-unknown-linux-musl.shaarch64-apple-darwin-x-x86_64-unknown-linux-musl.shx86_64-unknown-linux-gnu-x-aarch64-unknown-linux-gnu.shaarch64-unknown-linux-gnu-x-x86_64-unknown-linux-gnu.shx86_64-unknown-linux-gnu-x-x86_64-unknown-linux-musl.shaarch64-unknown-linux-gnu-x-aarch64-unknown-linux-musl.shaarch64-apple-darwin-x-x86_64-w64-mingw32.shx86_64-unknown-linux-gnu-x-x86_64-w64-mingw32.sh
That means:
- Linux glibc is implemented
- Linux musl is implemented
- MinGW Windows targets are scripted
- MSVC is not represented by target scripts here
4. MSVC is exploratory, not implemented
Section titled “4. MSVC is exploratory, not implemented”vendor/ocaml/CROSS_MSVC.md is a design exploration.
It describes:
- why MSVC would be desirable
- why MinGW is easier today
- why Windows SDK and
cl.exemake Unix-hosted MSVC cross-compilation hard - potential approaches involving Clang/LLVM, Wine, or SDK extraction
But the current fork snapshot does not include:
- active MSVC target scripts in
cross/targets/ - an implemented MSVC build pipeline
- validated MSVC compiler artifacts
So the correct snapshot wording is:
- MinGW Windows targeting is partially scripted
- MSVC cross-compilation remains a design investigation
5. Riot has a real cross-compilation surface, but it is still partial
Section titled “5. Riot has a real cross-compilation surface, but it is still partial”The Riot repository already contains a real target-aware toolchain and build surface.
The current pieces are:
packages/riot-model/src/toolchain_config.mlpackages/riot-toolchain/src/riot_toolchain.mlpackages/riot-toolchain/src/cross_compiling_toolchain.mlpackages/riot-cli/src/build.mlpackages/riot-build/src/build_server.mlpackages/riot-model/src/target.mlpackages/riot-model/src/build_ctx.mlpackages/riot-planner/src/action_graph.ml
5.1 Workspace configuration
Section titled “5.1 Workspace configuration”ocaml-toolchain.toml can currently declare:
toolchain.versiontoolchain.sourcetoolchain.targets
The targets list is the current Riot-side source of truth for which target
triples the workspace intends to support.
If targets is empty, Riot currently behaves as host-only by default.
5.2 CLI target selection
Section titled “5.2 CLI target selection”riot build currently exposes:
-x,--target--all-targets
But the current command implementation only executes one target per invocation. If target resolution matches more than one configured target, the CLI prints the matched list and exits instead of building them all.
So the durable snapshot is:
- target selection is implemented
- multi-target execution is not implemented yet
5.3 Toolchain acquisition
Section titled “5.3 Toolchain acquisition”Riot_toolchain.init_for_target currently behaves like this:
- if
target = host, it prefers a native host toolchain - if
./vendor/ocaml/compilerexists, Riot can symlink that as the local host toolchain - otherwise Riot downloads a prebuilt host toolchain tarball from
cdn.ocaml.ai - if
target != host, Riot downloads a prebuilt cross toolchain tarball fromcdn.ocaml.ai
This means Riot currently distinguishes two very different stories:
- vendored compiler source is currently part of the native host bootstrap path
- cross-target toolchains are currently treated as prebuilt downloadable
artifacts, not as compiler installs built on demand from
vendor/ocaml
That is an important current boundary.
Riot now also carries a local helper script,
scripts/toolchain/ocaml.sh, for manually building,
packaging, and uploading a selected subset of those prebuilt tarballs to the
configured CDN bucket while the automated publishing workflow remains disabled.
5.4 Build context propagation
Section titled “5.4 Build context propagation”When riot build selects a non-host target:
packages/riot-build/src/build_server.mlparses the target triple- it constructs
Riot_model.Target.Cross - it asks
CrossCompilingToolchain.detectto discover:sysrootbin_dirbin_prefix
- it stores that information in the build context
- it initializes a target-specific OCaml toolchain for that build
So Riot does have a real target-aware build context, not merely a target label passed to logging.
5.5 What the planner and executor consume today
Section titled “5.5 What the planner and executor consume today”The current planner/executor stack uses target information in a narrower way than the model suggests.
Today the clearest implemented uses are:
- selecting the OCaml toolchain binaries for the target
- looking up target-specific
profileoverrides by target platform name - adding
--sysroot=...to C compilation actions when a sysroot was detected - adding
--sysroot=...and target-derived linker flags to executable linking
The current weaker area is:
- Riot records
bin_dirandbin_prefixfor cross C toolchains, but the action graph/executor path is still much more clearly wired aroundsysrootand profile/linker flags than around a fully explicit prefixed C toolchain command path
So the current Riot snapshot should be described as:
- target-aware OCaml toolchain selection: implemented
- target-aware build context propagation: implemented
- sysroot-aware C compile/link flag injection: implemented
- full end-to-end prefixed cross C toolchain plumbing: not yet clearly complete
6. Riot’s current cross-compilation story is a hybrid
Section titled “6. Riot’s current cross-compilation story is a hybrid”Putting the fork and Riot together, the current state is:
- the vendored compiler fork owns the heavy cross-compilation implementation
- Riot can already select targets and request target-specific OCaml toolchains
- Riot can already thread target metadata into build planning
- Riot still relies on prebuilt cross toolchain artifacts for actual target toolchain installation
- Riot does not yet build cross toolchains from
vendor/ocamlas part of the normalriot buildstory
That means Riot’s current cross-compilation story should be understood as a hybrid:
- source-driven for the vendored native compiler/bootstrap path
- artifact-driven for current target toolchain consumption
- not yet a fully unified “Riot builds all host and cross compilers from source on demand” system
7. Fork-local CI exists, but is intentionally disabled in the fork
Section titled “7. Fork-local CI exists, but is intentionally disabled in the fork”The cross-compilation commit moves several workflow files out of the active
.github/workflows directory into .github/workflows-disabled/.
That means the current fork carries workflow definitions and matrix ideas, but does not currently rely on the fork repository itself to execute them.
This is important for Riot because it implies:
- Riot can study and reuse the intended CI matrix
- Riot should not assume the fork is already self-testing every target in its own GitHub Actions configuration
8. Current Riot integration boundary
Section titled “8. Current Riot integration boundary”At the moment, Riot should treat the vendored compiler fork as:
- the source of truth for compiler-side cross-compilation scripts and patches
- not yet the final source of truth for Riot’s end-to-end workflow contracts
The integration questions that remain outside this snapshot include:
- how Riot bootstrap should prefer vendored source over downloaded tarballs
- how Riot CI should build, cache, and publish cross compilers
- whether Riot keeps using prebuilt toolchain tarballs, local vendored builds, or both
- how MinGW support should appear in Riot user-facing commands
- whether MSVC becomes a real supported target or remains documentation only
Drawbacks
Section titled “Drawbacks”The current state has several rough edges:
- the implementation is concentrated in a large fork commit rather than a smaller patch stack
- the fork docs are internally inconsistent in a few places about how minimal the relocatability change really is
- Linux and Windows cross-target support live together in the fork, but Riot has not yet turned that into one coherent local workflow here
- MSVC documentation exists next to MinGW implementation, which can easily overstate the actual supported surface if read too quickly
Rationale and alternatives
Section titled “Rationale and alternatives”This snapshot does not choose a future integration strategy. It only records the current state faithfully.
The main alternative would be to skip documenting the fork until Riot’s own bootstrap and CI integration are complete. That would make the eventual design look cleaner, but it would hide the substantial cross-compilation work that already exists and make follow-up decisions harder to reason about.
Prior art
Section titled “Prior art”The primary prior art for this snapshot is the vendored OCaml fork itself:
vendor/ocaml/LINEAR.mdvendor/ocaml/CROSS_COMPILE_GUIDE.mdvendor/ocaml/CROSS_MSVC.mdvendor/ocaml/RELOCATABLE.mdvendor/ocaml/SUMMARY.mdvendor/ocaml/cross/README.mdvendor/ocaml@ef81d5fd5
Within Riot, the closest related document is:
RFD0009-riot-toolchain-system-snapshot.md
That RFD explains how Riot chooses and runs toolchains today, while this RFD captures the current compiler-fork side of the cross-compilation story.
Unresolved questions
Section titled “Unresolved questions”- Should Riot treat
vendor/ocamlas the primary source build path for toolchains, or only as a development/debugging path behind prebuilt tarballs? - Should Riot absorb the fork’s disabled workflow matrix into its own CI, or keep the compiler build/publish pipeline elsewhere?
- Should MinGW targets become first-class Riot targets, or are they only intermediate experiments on the path to another Windows story?
- Does Riot want to carry an actual MSVC implementation, or only keep the exploration document around for future reference?