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 = falseand 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).