A cross compiler is a compiler that runs on some host machine, for instance one running Linux on a 64-bit ARM processor, and generates code for a different target machine, for instance one running Windows with a 64-bit x86 processor.
Building OCaml cross compilers used to be quite tricky and hackish but many incremental changes to the build system over the last years have improved radically the situation. So much so that, with the most recent changes (1, 2) in the development branch of the compiler, it should be possible to build many cross compilers without extra changes
This is all well and good, you might say, but you would rather play with the brand new 5.3 instead of a development branch! So I’ve backported the necessary changes to 5.3.
How to build and use a OCaml 5.3 cross compiler
To make it easy to test, I’ve written an example OPAM file that can be customised to suit your goal. It takes the example of building a cross compiler to 64-bit x86 Windows MinGW, in particular because that always reveals unexpected issues
-
Start by creating a 5.3.0 OPAM switch if you haven’t already.
-
Choose the target you want to create a cross compiler for and find its target triplet. The GCC C cross compilers and the GNU cross binutils use target triplets as prefixes for the commands, so an easy way to find your triplet is to install the tools. So:
-
Install the C cross compiler and toolchain for your target. Many Linux distributions package some cross compilers. For instance, the CI tests for cross compilers installs on Ubuntu:
- the
gcc-mingw-w64-x86-64
package (which depends on the matching binutils) to cross compile to 64-bit x86 Windows MinGW; in that case, that target machine is identified by thex86_64-w64-mingw32
triplet, so it callsconfigure
with the argument--target=x86_64-w64-mingw32
, - the
gcc-aarch64-linux-gnu
package to cross compile to 64-bit arm Linux; in that case, the target machine is identified by theaarch64-linux-gnu
triplet.
- the
-
Create a new OPAM package interactively for instance by choosing the name of the package (
ocaml-xyz
or evenocaml-cross-xyz
are good choices I’d say; my example usesocaml-cross-windows
, for instance) and run:opam pin add --edit ocaml-cross-xyz -
This will open an editor so that you can fill in the instructions on how to build your cross compiler. Use my example to get you started. In particular you’ll want to configure the
--target
parameter with the triplet for your target (that could be the only change!). If your toolchain and C compiler use that triplet as a prefix for all the commands,configure
will find them automatically. Otherwise you’ll need to explicitly set them, by adding arguments such asCC=...
toconfigure
. The CI tests for cross compilers contains such an example to cross compile to Android whereCC
,AR
,PARTIALLD
,RANLIB
andSTRIP
are explicitly set… In other words, I suggest to experiment first with an example with automatic configuration!
You should now have a cross compiler! Let’s use it on a simple sanity check test.ml
:
(* Is the proper (target) OS identified? *)
let _ =
Printf.printf "Version: %s\nOS: %s\nUnix: %b\nWin: %b\nCygwin: %b\n"
Sys.ocaml_version Sys.os_type Sys.unix Sys.win32 Sys.cygwin
(* Do the compiler libs work? *)
(* The interface for [Arch] is not the same across processor architectures, the
following assumes your target is 64-bit x86 *)
let _ =
Printf.printf "allow_unaligned_access = %b\n" Arch.allow_unaligned_access;
Printf.printf "win64 = %b\n" Arch.win64
The package ocaml-cross-xyz
will install an ocamlfind
toolchain called xyz
. So we can compile test.ml
thus:
ocamlfind -toolchain xyz opt -package compiler-libs.optcomp -linkpkg test.ml -verbose
where -verbose
let you check what is actually being run. If your target is Windows MinGW (so cross compiling from some unix), you probably need an extra step before this compilation can go through: the tool flexlink.exe
which is used to link the final Windows binary has been built as part of the package but ocamlopt
will expect to find a command flexlink
(note in particular the absence of .exe
) so I suggest to ln -s
the flexlink
binary somewhere in your PATH
. For instance, it could be:
ln -s "$(opam show --list-files ocaml-cross-xyz | grep flexlink.opt.exe)" ~/bin/flexlink
and then you will be able to run the ocamlfind -toolchain ...
command to compile your program.
Gotchas and details
-
Beware that having a
flexlink
command inPATH
breaks OCaml (5.3 and before)’sconfigure
if you’re not on Windows; this will be fixed in 5.4. -
The example OPAM package contains SHA256 sums for
.patch
files generated on the fly from the corresponding commits but they might change without notice (to add an extra digit to the SHA1 they contain, for instance). If you notice that, ping me so that I can update the SHA256 sums in the gist. -
The example OPAM package pulls the official OCaml 5.3.0 archive along with two patches:
- the first one is a large commit that squashes all the commits that I backported from upstream,
- the second one is a small commit that adds the generation of the
ocamlfind
toolchain configuration.
You can find the detailed backport on my
5.3+ocross
branch and its comparison with the official release. The squashed commit lives on its own branch.