OCaml compiler .cmt/.cmi and Dune cache invalidation on compiler flag changes
0 reputation · 22 Aug 2022, 10:03 UTC
0 reputation · 22 Aug 2022, 10:03 UTC
OCaml compiler emits .cmi and .cmt files that capture type information for later reuse. Dune orchestrates builds and reuses compiled .cmo and metadata artifacts based on source file timestamps and content.
The integration boundary is unclear when compiler options such as -I include paths, -ccopt, or -g change. Dune currently does not treat flag changes as dependencies for these artifacts, which creates uncertainty about cache validity across builds.
The design decision of how to represent compiler options in Dune's dependency graph remains unresolved, with proposals to hash relevant flags into cache keys but no agreed implementation.
What criteria should define a flag-sensitive cache key for .cmt/.cmo artifacts in Dune? Which compiler options must invalidate cached metadata versus compiled objects? How should include path changes be tracked relative to source content?
33775 reputation · 22 Aug 2022, 13:12 UTC
Dune does not currently hash arbitrary compiler flags into the cache key for .cmi, .cmt, .cmo, or .cmx artifacts. It keys these targets on the source file content, the compiler version, and the declared dependencies discovered by ocamldep. As a result, changing flags such as -I, -ppx, -g, or -ccopt does not by itself trigger a rebuild of those metadata or object files. If you are seeing stale artifacts after a flag change, the likely explanation is that the flag is absent from the dependency graph, not that the compiler is reusing an invalid file.
Confirmed: Dune's action for OCaml modules includes the preprocessed source digest, the compiler executable identity, and the .ml.depends file contents. Likely: because flags passed via (flags ...) are not serialized into that action, they do not participate in cache validation. This is a known gap in how build systems represent compiler options as dependencies, and it remains an open design question rather than a documented, stable contract.
A robust key would need to partition flags by the artifact they affect:
.cmi and .cmt): -I/-include-dir, -pp/-ppx, -principal/-no-principal, -rectypes/-no-rectypes, -short-paths, and warning flags that promote errors such as -warn-error. These change module resolution, the parsed AST, or the serialized type information..cmo/.cmx): all of the above plus -g, -O family, -ccopt, -float-const-prop, -unbox-closures, and inlining controls.The key should normalize flag order, expand @response files, and be computed per build context (native, bytecode, cross-compiled).
Hashing the literal -I path strings is insufficient because the same string can resolve to different .cmi files as directories change. A better model treats each include directory as a content-addressed dependency: hash the set of .cmi files actually found along that path during compilation. This introduces a circular-dependency risk if those .cmi files are themselves built with incompatible flags, so the hash must be computed only after the transitive interface dependencies are stable.
dune build --verbose before and after the flag change and compare whether the OCaml action for the target is re-executed.dune rules _build/default/path/to/target.cmi to see the action and dependencies Dune has computed.ocamlobjinfo _build/default/path/to/target.cmi to inspect the interface file; if it is unchanged after a flag that should alter types, the artifact was not rebuilt.Because cache-key behavior evolves across Dune releases, the exact version matters. Which Dune version are you using, and did the flag change also modify the dune file stanza or only an environment variable?
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.