How to link C lib statically with OCaml app

Here is a simple example with libmagic.

Let's write a simple application. Here is a file test.ml:

let magic_cookie = Magic.make []
let () = print_endline (Magic.file magic_cookie Sys.argv.(1))

And if we build this app in usual way, we got a working binary on this system:

systemA:~$ ocamlfind ocamlopt test.ml -o test -package magic -linkpkg
systemA:~$ ./test test
ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped

But on other system we can got an error:

systemB:~$ ./test test
./test: error while loading shared libraries: libmagic.so.1: cannot open shared object file: No such file or directory

To link libmagic statically, we have to disable autolinking with -noautolink and provide necessary flags to gcc manually with -cclib '-Wl,-Bstatic -lmagic_stubs -lmagic -Wl,-Bdynamic -lz'. Read OCaml manual about these options.

systemA:~$ ocamlfind ocamlopt test.ml -o test -package magic -linkpkg -noautolink -cclib '-Wl,-Bstatic -lmagic_stubs -lmagic -Wl,-Bdynamic -lz'
systemB:~$ ./test test
ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped

You can see what compiler is really doing by adding -verbose option:

systemA:~$ ocamlfind ocamlopt test.ml -o test -package magic -linkpkg -noautolink -cclib '-Wl,-Bstatic -lmagic_stubs -lmagic -Wl,-Bdynamic -lz' -verbose
Effective set of compiler predicates: pkg_magic,native
+ ocamlopt -o test -verbose -noautolink -I /usr/lib/ocaml/magic /usr/lib/ocaml/magic/magic.cmxa test.ml -cclib -Wl,-Bstatic -lmagic_stubs -lmagic -Wl,-Bdynamic -lz
+ as -o 'test.o' '/tmp/camlasm5b7b13.s'
+ as -o '/tmp/camlstartupe71be7.o' '/tmp/camlstartupaa15d5.s'
+ gcc -o 'test'   '-L/usr/lib/ocaml/magic' '-L/usr/lib/ocaml' '/tmp/camlstartupe71be7.o' '/usr/lib/ocaml/std_exit.o' 'test.o' '/usr/lib/ocaml/magic/magic.a' '/usr/lib/ocaml/stdlib.a' '-Wl,-Bstatic' '-lmagic_stubs' '-lmagic' '-Wl,-Bdynamic' '-lz' '/usr/lib/ocaml/libasmrun.a' -lm  -ldl 

Yet another way — no -noautolink but -static and -dynamic instead of -Wl,-Bstatic and -Wl,-Bdynamic. But this way it links libc and other stuff. For example I got 959K binary instead of 284K. I don't know yet why.

systemA:~$ $ ocamlfind ocamlopt test.ml -o test -package magic -linkpkg -cclib '-static -lmagic_stubs -lmagic -dynamic -lz' -verbose
Effective set of compiler predicates: pkg_magic,autolink,native
+ ocamlopt -o test -verbose -I /usr/lib/ocaml/magic /usr/lib/ocaml/magic/magic.cmxa test.ml -cclib -static -lmagic_stubs -lmagic -dynamic -lz
+ as -o 'test.o' '/tmp/camlasm29ca7e.s'
+ as -o '/tmp/camlstartupf55661.o' '/tmp/camlstartupd90aac.s'
+ gcc -o 'test'   '-L/usr/lib/ocaml/magic' '-L/usr/lib/ocaml' '/tmp/camlstartupf55661.o' '/usr/lib/ocaml/std_exit.o' 'test.o' '/usr/lib/ocaml/magic/magic.a' '/usr/lib/ocaml/stdlib.a' '-lmagic_stubs' '-lmagic' '-static' '-lmagic_stubs' '-lmagic' '-dynamic' '-lz' '/usr/lib/ocaml/libasmrun.a' -lm  -ldl 
/usr/lib/ocaml/libasmrun.a(unix.o): In function `caml_dlopen':
(.text+0x232): warning: Using 'dlopen' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking

Does anybody know a better way to link C lib statically with OCaml? Let me know.

See also