Dynamically link native code into utop?

I spent sometime today to see where ocamlnat stands. I managed to get it work both on linux (via docker) and on macos.

You can try it by using this opam v2 repo (see the README for instructions).

With pure ocaml code it seems to work fine here’s a sample interaction using gg:

bash-4.3$ rlwrap ocamlnat -noinit 
        OCaml version 4.05.0 - native toplevel

# #directory "/home/opam/.opam/4.05.0+ocamlnat/lib/gg";;
# #load "bigarray.cmxs";;
# #load "gg.cmxs";;
# #install_printer Gg.V2.pp;;
# open Gg;;
# V2.(ox + oy);;
- : Gg.v2 = (1 1)

A few notes:

  1. ocamlnat is invoked with -noinit this is to prevent to read your .ocamlinit which likely #use 'topfind' which fails (more on that below).
  2. As a result include directories and dependencies are loaded manually. Note: it’s the cmxs you need to load. You’ll need to make sure the libraries you use properly builds them.
  3. Toplevel support libraries that install printers like gg_top.cmxs fail because they try to use the Toploop module rather than Opttoploop.
  4. I tried with a library with simple C bindings (mtime), it works out of the box on linux but on macos it needs a few adjustements on the cli to find both the static C library (?) and the dynamic one. See the interaction at the end of the message.

1 and 3 are due to the infortunate API design of the byte and native toploop API that splits the module names into Toploop and Opttoploop rather than expose the same names and treat the problem as a library variant. This implies that any consumer of the API has to conditionalize itself which doesn’t scale, this problem is documented in MPR 7589.

For a library with C bindings on macos the following env var had to be adjusted (and there are weird warnings I didn’t investigate exactly where they come from, I suspect a configuration thing that is no longer relevant in ocaml itself):

> DYLD_LIBRARY_PATH=$(opam var stublibs) LIBRARY_PATH=$(opam var mtime:lib)/os rlwrap ocamlnat -noinit
        OCaml version 4.05.0 - native toplevel

# #directory "/Users/dbuenzli/.opam/4.05.0+ocamlnat/lib/mtime";;
# #directory "/Users/dbuenzli/.opam/4.05.0+ocamlnat/lib/mtime/os";;
# #load "mtime.cmxs";;
# #use "mtime_top_init.ml";;
ld: warning: directory not found for option '-L/Users/dbuenzli/.opam/4.05.0+ocamlnat/lib/ocaml/camlp4'
ld: warning: directory not found for option '-L/Users/dbuenzli/.opam/4.05.0+ocamlnat/lib/ocaml/camlp4'
ld: warning: directory not found for option '-L/Users/dbuenzli/.opam/4.05.0+ocamlnat/lib/ocaml/camlp4'
ld: warning: directory not found for option '-L/Users/dbuenzli/.opam/4.05.0+ocamlnat/lib/ocaml/camlp4'
# #load "mtime_clock.cmxs";;
# Mtime_clock.now ();;
ld: warning: directory not found for option '-L/Users/dbuenzli/.opam/4.05.0+ocamlnat/lib/ocaml/camlp4'
- : Mtime.t = 92862271359034ns
6 Likes