Login

Cross-compile with Zig

Doing this with minimal dependencies is easy, by telling ocamlopt.opt to use Zig and then telling Zig to pick a target, but your first attempt can easily run into two hazards:

  1. ocamlopt.opt doesn't like "zig cc" as a compiler, or rather, it will try to invoke an executable named "zig cc" including the space. IMO, this is correct and reflects careful spawning of subprocesses - but, OCaml is only partially correct here, so it's a little more confusing.
    $ ocamlopt.opt -cc 'zig cc' -ccopt -static o19.ml
    (* output from zig starting up and doing work *)
    ld.lld: error: unable to find library -lm
    ld.lld: error: unable to find library -lc
    File "caml_startup", line 1:
    Error: Error during linking (exit code 1)
  2. if ocamlopt.opt itself isn't compiled with musl, then linking errors are very likely, even if you're not targeting musl:
    $ TARGET=x86_64-linux-gnu ocamlopt.opt -cc $PWD/zigcc.sh o19.ml
    ld.lld: error: undefined symbol: __isoc23_strtol
    >>> referenced by floats.c:308 (runtime/floats.c:308)
    >>>               floats.n.o:(caml_float_of_string) in archive

So, step 1 is to get a musl compiler. opam and OCaml 5.4 make this easy:

$ opam switch create 5.4.0+options ocaml-option-static

Then, create a shell wrapper for zig:

#! /bin/bash
# zigcc.sh
exec zig cc --target=$TARGET "$@"

Then, use it with your musl ocaml:

$ TARGET=x86_64-linux-gnu ocamlopt.opt -cc $PWD/zigcc.sh o19.ml -o linux.gnu

$ TARGET=x86_64-linux-musl ocamlopt.opt -cc $PWD/zigcc.sh o19.ml -ccopt -static -o linux.musl

$ TARGET=x86_64-windows ocamlopt.opt -cc $PWD/zigcc.sh o19.ml -o windows.exe
lld-link: error: /tmp/camlstartupbd13cd.o: unknown file type
lld-link: error: /home/user/.opam/static/lib/ocaml/std_exit.o: unknown file type─ [1200/1200] Linking
lld-link: error: o19.o: unknown file type
lld-link: error: unknown file type: main.n.o
File "caml_startup", line 1:
Error: Error during linking (exit code 1)

$ TARGET=riscv64-linux-musl ocamlopt.opt -cc $PWD/zigcc.sh -ccopt -static o19.ml -o linux.riscv
ld.lld: error: /tmp/camlstartup755e47.o is incompatible with elf64lriscv
ld.lld: error: /home/user/.opam/static/lib/ocaml/std_exit.o is incompatible with elf64lriscv
(* much output *)

$ TARGET=aarch64-macos ocamlopt.opt -cc $PWD/zigcc.sh 
o19.ml -o linux.riscv                     
error: unknown cpu architecture: 437649920
    note: while parsing /home/user/.opam/static/lib/ocaml/stdlib.a()
(* much output *)

Well, it doesn't all work, but reasonably portable binaries are possible within the same CPU architecture:

$ ldd linux.gnu linux.musl
linux.gnu:
        linux-vdso.so.1 (0x00007ffee18d2000)
        libm.so.6 => /lib64/libm.so.6 (0x00007fef4d28c000)
        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fef4d288000)
        libc.so.6 => /lib64/libc.so.6 (0x00007fef4d096000)
        libdl.so.2 => /lib64/libdl.so.2 (0x00007fef4d092000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fef4d3a2000)
linux.musl:
        not a dynamic executable

$ du -sh linux.gnu linux.musl
1.3M    linux.gnu
2.8M    linux.musl