Test / Production module injection

Hi !
I’m working on a project where most of the libraries have two implementations: one for production code, and one for test code (usually simple stubs). We are building using Ocaml 4.05.0 with the following pattern:

  • build injector.cmi from injector.mli which is simply val is_stub_impl: bool
  • build all the libraries with only injector.mli in their dependencies + suppress warning 58 (no implementation found). In each of the libraries there is a ML file which looks like Include (val (if Injector.is_stub_impl then (module MyStubMod: S) else (module MyImplMod: S)))
  • build prod_injector.ml which is let is_stub_impl = false and test_injector.ml which is let is_stub_impl = true
  • when building the final executable, use either prod_injector or test_injector (never both).

With 4.05 it works so far, but I recently tried to update to 4.06 and this pattern no longer works. I’m getting some inconsistent assumptions on interfaces, like if all the modules where build against a injector.mli and only prod_injector.ml/test_injector.ml where build against another version.

Before showing a full fledged example (I’ll have to port our buck scripts to something readable :D), is there a now working way to achieve this goal ? I now dune’s virtual libraries are a good candidate, but we can’ t use dune at the moment as we integrate in a much bigger projects (thus the buck constraint).


It sounds like this might be more robust if built using virtual modules in Dune. That provides first-class support for the separate compilation of implementations and interfaces, and should work consistently across 4.05 and 4.06.

I agree, but that’s not a solution we can use at the moment. I’ll check with buck’s dev if we can implement the virtual modules in there too, that could be neat !

(I only noticed your last paragraph saying you are using buck after posting – apologies!)

You could try to run ocamlobjinfo over the cmi files generated for both versions, to see if a dependency has been introduced somewhere. The implementations have to have exactly the same cmi, otherwise the linking trick doesn’t work. Dune sandboxes the builds pretty carefully to avoid other files leaking in (e.g. with -I and being on the same path).

Since OCaml 4.06.0, -keep-locs is the default (see this PR Compiler drivers: make -keep-locs the default by dbuenzli · Pull Request #1219 · ocaml/ocaml · GitHub), you may need to pass -no-keep-locs in your build system.


Oh I wasn’t aware of that. I’ll have a look thank you !

@hannes It seems that you have spotted the issue ! Thank you very much ! Looks like the build is going on (with “normal” errors of porting to 4.05 -> 4.06).
I’ll confirm that this solves my issue when everything works once again


Yep, that solved the problem. Thanks again !

1 Like

That’s great! Some time ago I encountered the same issue, and needed the same fix :wink:

1 Like