How to use BER MetaOCaml with dune (cleanly)?

I’m trying to use BER MetaOCaml in a dune project. I was reading through several posts and github issues and didn’t manage to find any concrete examples. After trying many things, the following kinda works:

dune

(executable
 (name foo)
 (link_flags dynlink.cmxa compiler-libs/ocamlcommon.cmxa codelib.cmx runnative.cmx)
 (modes native))

bin/foo.ml

let string_of_code c =
  let () = Codelib.print_code Format.str_formatter c in
  Format.flush_str_formatter ()

let () =
  let c = .<1 + 2>. in
  let res = Runnative.run c in
  Printf.printf "Code: %s\nResult: %d\n" (string_of_code c) res

output of dune exec bin/foo.exe:

Code: .<1 + 2>.      

Result: 3

As you can see, there’s the horrible link_flags hack that scares children and adults alike. It also limits the mode to native (you could probably come up with a similar hack for byte mode). I would like to know if there’s a way to cleanly use metaocaml with dune. Thanks.

Relevant:

  1. https://github.com/ocaml/dune/issues/1413.
  2. https://github.com/ocaml/opam-repository/pull/13474
  3. opam switch show is 4.07.1+BER.
2 Likes

Have you tried adding (libraries metaocaml)? This seems to be the outcome of the other discussions.

I did try (and also added dynlink for exe mode). This is the error I get:

$ dune exec test/foo.exe
File "test/dune", line 2, characters 7-10:
2 |  (name foo)
           ^^^
Error: File unavailable:
/home/username/.opam/4.07.1+BER/lib/metaocaml/+codelib.cmx
File "test/dune", line 2, characters 7-10:
2 |  (name foo)
           ^^^
Error: File unavailable:
/home/username/.opam/4.07.1+BER/lib/metaocaml/+runnative.cmx

and even if I move those files and renamed them, I get the following error:

gcc: error: /home/username/.opam/4.07.1+BER/lib/metaocaml/+runnative.o: No such file or directory
gcc: error: /home/username/.opam/4.07.1+BER/lib/metaocaml/+codelib.o: No such file or directory
File "caml_startup", line 1:
Error: Error during linking

I can see those files are coming from the base-metaocaml-ocamlfind mentioned in one of the issues.

Could you try editing /home/username/.opam/4.07.1+BER/lib/metaocaml/META as follows:

  • remove the + in the archives fields
  • add directory="+"

?

This META file seems to be using a rarely used findlib feature that is not implemented by dune.

2 Likes

That worked!

Concretely:

META:

description = "Extra libraries for MetaOCaml"
directory="+"
requires = "compiler-libs.common"
requires(byte) = "compiler-libs.bytecomp compiler-libs.toplevel"
requires(native) = "compiler-libs.optcomp dynlink"
archive(byte) = "metalib.cma berstart.cmo"
archive(native) = "codelib.cmx runnative.cmx"

dune:

(executable
 (name foo)
 (libraries dynlink metaocaml)
 (modes exe))

For posterity, could you describe why it wasn’t working in first place? And also, why is dynlink explicitly required in the libraries if it’s in the requires list in the META?

Thanks!

P.S.: here’s a PR to opam in case this is the general fix: https://github.com/ocaml/opam-repository/pull/16117

+ at the beginning of a directory or file name in META files means “the OCaml standard library directory”. So +metalib.cma means $(ocamlc -where)/metalib.cma. However, dune only implements this expansion for the directory field. I must have missed this part of the doc when I first implemented support for META files in dune and somehow thought it was only supported in the directory field. So technically it is a bug in Dune and we should consider fixing it.

That said, given that:

  • I’m pretty sure very few people rely on this feature, and indeed this is the first time I see it used
  • most META files are now generated by dune
  • META files will eventually disappear

It seems simpler to me to update the META file of metaocaml and consider the fix in Dune a low-priority one.

That’s a mistery. To be clear, it is missing when building the native executable?

Correct. This is the full error if dynlink is removed from libraries:

File "_none_", line 1:
Error: No implementations provided for the following modules:
         Dynlink referenced from /home/username/.opam/4.07.1+BER/lib/ocaml/runnative.cmx

Ah, I remember. We don’t support per-byte/native dependencies in dune. So the requires = ... is always used.

Got it. That explains also the error I get in byte mode then.
Thanks for your help!