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:
- 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)
- 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