I want to include a module in an OCaml program where the module is not
directly called by the main (or any other) module, but rather does something during
initialization. How do I do that with dune
? It seems that dune
only includes modules that are explicitly invoked, even when I list
other modules in a modules
stanza.
dune
not doing what I want:Mod1.ml
:
let mod1 () : unit =
Printf.printf "in mod1\n"
let () =
mod1 ()
Mod2.ml
:
let mod2 () : unit =
Printf.printf "in mod2\n"
let () =
mod2 ()
dune
:
(executable
(name Mod1)
(modules
Mod1
Mod2
)
)
dune-project
(lang dune 3.3)
Compiling and running:
$ dune exec ./Mod1.exe
in mod1
My goal is to also see in mod2
above output.
ocamlc
invocation:This is what I would do if not using dune
:
$ ocamlc -o both.exe Mod1.ml Mod2.ml
$ ./both.exe
in mod1
in mod2
If I change Mod1.ml
to:
let mod1 () : unit =
Printf.printf "in mod1\n"
let () =
mod1 ();
Mod2.mod2 () (* added *)
Then both modules get included:
$ dune exec ./Mod1.exe
in mod2
in mod1
in mod2
But my goal is include Mod2
in the executable without explicitly
invoking it from Mod1
.
empty_module_interface_if_absent
does not helpIn the documentation for the
executable
stanza, it says:
(empty_module_interface_if_absent)
causes the generation of empty interfaces for every module that does not have an interface file already. Useful when modules are used solely for their side-effects. This field is available since the 3.0 version of the Dune language.
However, this does not help. With dune
file:
(executable
(name Mod1)
(empty_module_interface_if_absent) ; added
(modules
Mod1
Mod2
)
)
and reverting to the original Mod1.ml
, the behavior is the same:
$ dune exec ./Mod1.exe
in mod1
$ ocamlc --version
4.14.0
$ dune --version
3.3.1
$ opam --version
2.1.2
$ uname -a
Linux mint201vm 5.4.0-58-generic #64-Ubuntu SMP Wed Dec 9 08:16:25 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
You can pack your modules in a library and then link this library to your main executable using the linkall
option. E.g., using your concrete example,
(executable
(name mod1)
(modules mod1)
(libraries libmod2)
(flags -linkall))
(library
(name libmod2)
(modules mod2))
But it is more convenient to pack all modules into a library and move the executable into a separate folder. You can create an empty file for the executable main module or do some initialization in it, e.g.,
$ tree
.
├── dune
├── dune-project
├── mod1.ml
├── mod2.ml
└── run
├── dune
└── main.ml
$ cat dune
(library
(name impl))
$ cat run/dune
(executable
(name main)
(libraries impl)
(flags -linkall))
$ dune exec ./run/main.exe
in mod2
in mod1
Either way, note that by default dune packs modules in a library to prevent name-clashing with other libraries (since OCaml has a flat namespace for compilation units). Therefore to access a module Mod2
in the library Libmod2
you have to say Libmod2.Mod2
(if you're outside of the library). If you want to disable this packing, use (wrapped false)
in the library
stanza.