There's Cross-compile with Zig but that gets to be more of a hassle as you use more C libraries that aren't distributed as opam packages, and currently OCaml isn't able to benefit from Zig's ability to also cross-compile across CPU architectures and OSes. If the only benefit is more-portable binaries within Linux targets of the same architecture, then we get that benefit with more flexibility and less hassle from building with Alpine Linux.
The most popular way to do that is with Docker. Docker has very many serious and practical advantages when you get going with it, but in the early stages it's quite a pain. Thus,
Distrobox
Get it from https://distrobox.it/ or Note that distrobox is not hermetically sealed from the host OS, so 'opam switch' will actually show your existing switches. This introduces some possibility for error but also enormously simplifies casual use.
With the alpine switch you can create static executables with By default, for ASLR, Alpine Linux builds position-independent executables that still require a .so loader. You could hypothetically ship the loader with the binary, but I couldn't find a way to do that (like More a more motivating example, lets get a portable binary with openssl:
For the bin/dune file:
and bin/main.ml:
We can get a static executable that prints some cert information with openssl:
dnf install distrobox, and then:
$ distrobox create --name alpine --image alpine:latestImage alpine:latest not found.
Do you want to pull the image now? [Y/n]:
Resolved "alpine" as an alias (/etc/containers/registries.conf.d/000-shortnames.conf)
Trying to pull docker.io/library/alpine:latest...
Getting image source signatures
Copying blob 014e56e61396 done |
Copying config 7acffee03f done |
Writing manifest to image destination
7acffee03fe864cd6b88219a1028855d6c912e7cf6fac633aa4307529fd0cc08
Creating 'alpine' using image alpine:latest [ OK ]
Distrobox 'alpine' successfully created.
To enter, run:
distrobox enter alpine
$ distrobox enter alpine
Starting container... [ OK ]
Installing basic packages... [ OK ]
Setting up devpts mounts... [ OK ]
Setting up read-only mounts... [ OK ]
Setting up read-write mounts... [ OK ]
Setting up host's sockets integration... [ OK ]
Integrating host's themes, icons, fonts... [ OK ]
Setting up distrobox profile... [ OK ]
Setting up sudo... [ OK ]
Setting up user's group list... [ OK ]
Setting up existing user... [ OK ]
Ensuring user's access... [ OK ]
Container Setup Complete!
📦$ sudo apk add opam
(1/5) Installing bubblewrap (0.11.0-r2)
(2/5) Installing bubblewrap-doc (0.11.0-r2)
(3/5) Installing bubblewrap-bash-completion (0.11.0-r2)
(4/5) Installing opam (2.5.0-r0)
(5/5) Installing opam-doc (2.5.0-r0)
Executing busybox-1.37.0-r29.trigger
OK: 504 MiB in 379 packages
📦$ sudo apk add build-base m4 perl bash ncurses-dev zlib-dev gmp-dev libffi-dev python3 curl tar
📦$ opam switch create alpine 5.4.0
-ccopt static, but you may be unpleasantly surprised when you run them:
$ ./linux.musl.static
Segmentation fault (core dumped)
-Wl,--dynamic-linker=./ld-musl-x86_64.so.1) that didn't still segfault. You can drop the protection with -no-pie
📦$ ocamlopt.opt o19.ml -ccopt -static -ccopt -no-pie -o linux.musl.static
$ ./linux.musl.static
22
$ ldd linux.musl.static
not a dynamic executable
📦$ sudo apk add gmp-static openssl-libs-static
📦$ opam install dune ssl
📦$ dune init proj checkcert
(executable
(public_name checkcert)
(name main)
(ocamlopt_flags -ccopt -static -ccopt -no-pie)
(libraries o31 ssl unix x509))
let () =
Ssl.init ();
let addr =
Unix.getaddrinfo Sys.argv.(1) "443" [AI_SOCKTYPE SOCK_STREAM]
in
let sockaddr = (List.hd addr).ai_addr in
let ssl = Ssl.open_connection Ssl.TLSv1_3 sockaddr in
let cert = Ssl.get_certificate ssl in
let cipher = Ssl.get_cipher ssl in
Printf.printf "SSL connection ok.\n";
Printf.printf "Certificate issuer: %s\nsubject: %s\n" (Ssl.get_issuer cert)
(Ssl.get_subject cert);
Printf.printf "Cipher: %s (%s)\n%s"
(Ssl.get_cipher_name cipher)
(Ssl.get_cipher_version cipher)
(Ssl.get_cipher_description cipher);
Ssl.shutdown ssl;
print_newline ()
📦$ dune exec checkcert ocaml.org
SSL connection ok.
Certificate issuer: /C=US/O=Let's Encrypt/CN=E8
subject: /CN=v3.ocaml.org
Cipher: TLS_AES_256_GCM_SHA384 (TLSv1.3)
TLS_AES_256_GCM_SHA384 TLSv1.3 Kx=any Au=any Enc=AESGCM(256) Mac=AEAD
$ ldd ./_build/default/bin/main.exe
not a dynamic executable
$ ./_build/default/bin/main.exe dune.build
SSL connection ok.
Certificate issuer: /C=GB/ST=Greater Manchester/L=Salford/O=Sectigo Limited/CN=Sectigo RSA Domain Validation Secure Server CA
subject: /CN=*.github.io
Cipher: TLS_AES_128_GCM_SHA256 (TLSv1.3)
TLS_AES_128_GCM_SHA256 TLSv1.3 Kx=any Au=any Enc=AESGCM(128) Mac=AEAD