Calling OCaml from C in dune
2025-02-02
category: tech
These are some notes on putting together a
dune
file for creating a C executable which
calls OCaml code.
The OCaml manual describes how to call OCaml code from C
under the section “Advanced example with callbacks” in the
chapter “Interfacing C with OCaml”, using the following
invocations to ocamlopt
:
$ ocamlopt -output-obj -o modcaml.o mod.ml
$ ocamlopt -c modwrap.c
$ cp `ocamlopt -where`/libasmrun.a mod.a && chmod +w mod.a
$ ar r mod.a modcaml.o modwrap.o
$ cc -o prog -I `ocamlopt -where` main.c mod.a -lm
Reading the section
in the OCaml manual about ocamlopt
command line
options carefully, it looks like we can use
-output-complete-obj
to simplify above
commands:
$ ocamlopt -output-complete-obj -o modcaml.o mod.ml
$ ocamlopt -c modwrap.c
$ cc -o prog_native2 -I `ocamlopt -where` main.c modcaml.o modwrap.o -lm
Translating to dune
We first want to recreate the modcaml.o
file. Grepping in the dune codebase for
-output-complete-obj
, we find this
code which matches on
Executables.Link_mode.t
.
The dune docs mention how to specify linking modes for executables, so we’ll try that.
(executables
(names mod)
(modes object))
After some trial and error, I’ve figured out that the
target corresponding to this rule is
mod.exe.o
:
$ dune build --verbose mod.exe.o
...
Actual targets:
- _build/default/mod.exe.o
Running[1]: (cd _build/default && /.../.opam/5.2.1/bin/ocamlc.opt -w @1..3@5..28@31..39@43@46..47@49..57@61..62@67@69-40 -strict-sequence -strict-formats -short-paths -keep-locs -g -bin-annot -bin-annot-occurrences -I .mod.eobjs/byte -no-alias-deps -opaque -o .mod.eobjs/byte/dune__exe__Mod.cmi -c -intf mod.mli)
Running[2]: (cd _build/default && /.../.opam/5.2.1/bin/ocamlopt.opt -w @1..3@5..28@31..39@43@46..47@49..57@61..62@67@69-40 -strict-sequence -strict-formats -short-paths -keep-locs -g -I .mod.eobjs/byte -I .mod.eobjs/native -intf-suffix .ml -no-alias-deps -opaque -o .mod.eobjs/native/dune__exe__Mod.cmx -c -impl mod.ml)
Running[3]: (cd _build/default && /.../.opam/5.2.1/bin/ocamlopt.opt -w @1..3@5..28@31..39@43@46..47@49..57@61..62@67@69-40 -strict-sequence -strict-formats -short-paths -keep-locs -g -o mod.exe.o -output-complete-obj .mod.eobjs/native/dune__exe__Mod.cmx)
I’m not exactly sure if there are better ways to do this, but we manually define rules to run the remaining steps:
(rule
(targets modwrap.o)
(deps modwrap.c)
(action
(run ocamlopt %{deps})))
(rule
(targets main.exe)
(deps main.c mod.exe.o modwrap.o)
(action
(run %{cc} -o %{targets} -I %{ocaml_where} %{deps} -lm)))
And voila!
$ dune exec ./main.exe
fib(10) = Result is: 89
The full code is available in the calling-ocaml-from-c repository.
index | atom/rss | generated off rev 14870e6