Skip to content

test: lock in incremental-rebuild for root_module-aliased deps#14335

Open
robinbb wants to merge 1 commit intoocaml:mainfrom
robinbb:robinbb-root-module-d-file
Open

test: lock in incremental-rebuild for root_module-aliased deps#14335
robinbb wants to merge 1 commit intoocaml:mainfrom
robinbb:robinbb-root-module-d-file

Conversation

@robinbb
Copy link
Copy Markdown
Collaborator

@robinbb robinbb commented Apr 25, 2026

Summary

Add a regression test under test/blackbox-tests/test-cases/root-module/ that asserts the incremental-rebuild property for (root_module …)-aliased dependencies:

  • Editing the dependency's .mli invalidates the consumer.
  • Editing only the dependency's .ml does not invalidate the consumer.

This property holds on main today via dune's existing glob-over-objdir mechanism. The test locks it in as a specification — any future change to dune's inter-library-dependency tracking (notably the per-module filter being developed in #14116) must preserve it.

Test plan

  • dune runtest test/blackbox-tests/test-cases/root-module/

@robinbb robinbb force-pushed the robinbb-root-module-d-file branch from d3072d0 to 7984ef8 Compare April 25, 2026 22:58
@robinbb robinbb changed the title feat: synthesise an immediate-deps file for root_module test: lock in incremental-rebuild for root_module-aliased deps Apr 25, 2026
@robinbb robinbb requested a review from Copilot April 25, 2026 23:03
@robinbb robinbb self-assigned this Apr 25, 2026
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a blackbox regression test to specify incremental rebuild behavior when a consumer uses (root_module ...) to access an aliased dependency, ensuring interface changes trigger rebuilds while implementation-only changes do not.

Changes:

  • Add a new blackbox test case under test/blackbox-tests/test-cases/root-module/ to lock in incremental rebuild behavior for root-module-aliased deps.
  • Validate rebuild/no-rebuild outcomes by querying dune trace output via jq.

Comment thread test/blackbox-tests/test-cases/root-module/incremental-rebuild.t Outdated
Comment thread test/blackbox-tests/test-cases/root-module/incremental-rebuild.t Outdated
robinbb added a commit to robinbb/dune that referenced this pull request Apr 25, 2026
A consumer that uses [Root.LibY.foo] reports only [Root] as a
top-level ocamldep reference; qualified-path components are not
surfaced. The filter therefore needs to look through [Root] to
discover [LibY]. But [Root] had no [.d] file (its [Module.kind]
short-circuits in [Dep_rules.deps_of] via [is_alias_or_root], an
intentional cycle-prevention measure introduced in commit
a5d8945 / change ocaml#12227), so [read_immediate_deps_raw_of]
returned an empty set and the cross-library walk never reached
[LibY]. The consumer's compile rule was missing the dep on
[LibY]'s cmi: the OCaml path got away with it because [-I] flags
still found the cmi at compile time, but Melange's strict-
ordering rules surfaced the gap as a missing-cmi error.

Synthesise a [.d]-format file for [Root] alongside [root.ml] in
[build_root_module], using [Root_module.entries] (the same input
[root.ml] is generated from). The file matches what [ocamldep
-modules] would output, so the general path in
[Ocamldep.read_immediate_deps_raw_of] reads it like any other
module's [.d]. [obj_dir.ml] is updated to return a path for
[(Root, Immediate _)] while continuing to refuse
[(Root, Transitive _)] — the cycle that motivated the original
short-circuit lives in [.all-deps] generation, which we still
suppress.

This keeps [read_dep_m_raw] uniform (no [Module.kind] switch),
shrinks [module_kind_has_readable_ocamldep]'s false branch by
one entry, and locates the [Root] knowledge in
[build_root_module] where [root.ml] is also produced.

The regression test for incremental-rebuild behaviour through
[Root] lives separately at
test/blackbox-tests/test-cases/root-module/incremental-rebuild.t
in the prerequisite PR (ocaml#14335), so the test is on main as a
pure regression guard.

Signed-off-by: Robin Bate Boerop <me@robinbb.com>
@robinbb robinbb force-pushed the robinbb-root-module-d-file branch from 7984ef8 to 5d88955 Compare April 25, 2026 23:24
@robinbb robinbb requested a review from Copilot April 25, 2026 23:25
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.

@robinbb robinbb marked this pull request as ready for review April 26, 2026 00:32
@robinbb robinbb force-pushed the robinbb-root-module-d-file branch from 5d88955 to e511ed8 Compare April 26, 2026 18:53
robinbb added a commit to robinbb/dune that referenced this pull request Apr 26, 2026
A consumer that uses [Root.LibY.foo] reports only [Root] as a
top-level ocamldep reference; qualified-path components are not
surfaced. The filter therefore needs to look through [Root] to
discover [LibY]. But [Root] had no [.d] file (its [Module.kind]
short-circuits in [Dep_rules.deps_of] via [is_alias_or_root], an
intentional cycle-prevention measure introduced in commit
a5d8945 / change ocaml#12227), so [read_immediate_deps_raw_of]
returned an empty set and the cross-library walk never reached
[LibY]. The consumer's compile rule was missing the dep on
[LibY]'s cmi: the OCaml path got away with it because [-I] flags
still found the cmi at compile time, but Melange's strict-
ordering rules surfaced the gap as a missing-cmi error.

Synthesise a [.d]-format file for [Root] alongside [root.ml] in
[build_root_module], using [Root_module.entries] (the same input
[root.ml] is generated from). The file matches what [ocamldep
-modules] would output, so the general path in
[Ocamldep.read_immediate_deps_raw_of] reads it like any other
module's [.d]. [obj_dir.ml] is updated to return a path for
[(Root, Immediate _)] while continuing to refuse
[(Root, Transitive _)] — the cycle that motivated the original
short-circuit lives in [.all-deps] generation, which we still
suppress.

This keeps [read_dep_m_raw] uniform (no [Module.kind] switch),
shrinks [module_kind_has_readable_ocamldep]'s false branch by
one entry, and locates the [Root] knowledge in
[build_root_module] where [root.ml] is also produced.

The regression test for incremental-rebuild behaviour through
[Root] lives separately at
test/blackbox-tests/test-cases/root-module/incremental-rebuild.t
in the prerequisite PR (ocaml#14335), so the test is on main as a
pure regression guard.

Signed-off-by: Robin Bate Boerop <me@robinbb.com>
robinbb added a commit to robinbb/dune that referenced this pull request Apr 27, 2026
A consumer that uses [Root.LibY.foo] reports only [Root] as a
top-level ocamldep reference; qualified-path components are not
surfaced. The filter therefore needs to look through [Root] to
discover [LibY]. But [Root] had no [.d] file (its [Module.kind]
short-circuits in [Dep_rules.deps_of] via [is_alias_or_root], an
intentional cycle-prevention measure introduced in commit
a5d8945 / change ocaml#12227), so [read_immediate_deps_raw_of]
returned an empty set and the cross-library walk never reached
[LibY]. The consumer's compile rule was missing the dep on
[LibY]'s cmi: the OCaml path got away with it because [-I] flags
still found the cmi at compile time, but Melange's strict-
ordering rules surfaced the gap as a missing-cmi error.

Synthesise a [.d]-format file for [Root] alongside [root.ml] in
[build_root_module], using [Root_module.entries] (the same input
[root.ml] is generated from). The file matches what [ocamldep
-modules] would output, so the general path in
[Ocamldep.read_immediate_deps_raw_of] reads it like any other
module's [.d]. [obj_dir.ml] is updated to return a path for
[(Root, Immediate _)] while continuing to refuse
[(Root, Transitive _)] — the cycle that motivated the original
short-circuit lives in [.all-deps] generation, which we still
suppress.

This keeps [read_dep_m_raw] uniform (no [Module.kind] switch),
shrinks [module_kind_has_readable_ocamldep]'s false branch by
one entry, and locates the [Root] knowledge in
[build_root_module] where [root.ml] is also produced.

The regression test for incremental-rebuild behaviour through
[Root] lives separately at
test/blackbox-tests/test-cases/root-module/incremental-rebuild.t
in the prerequisite PR (ocaml#14335), so the test is on main as a
pure regression guard.

Signed-off-by: Robin Bate Boerop <me@robinbb.com>
Add a regression test asserting that a consumer using
[(root_module ...)] to alias a dependency rebuilds when the
dependency's [.mli] changes, and does not rebuild when only the
dependency's [.ml] changes. The property holds today via dune's
existing glob-over-objdir mechanism; the test guards it as a
specification against future inter-library-dependency-tracking
changes (notably the per-module filter being developed in

Signed-off-by: Robin Bate Boerop <me@robinbb.com>
ocaml#14116).
@robinbb robinbb force-pushed the robinbb-root-module-d-file branch from e511ed8 to 241dd79 Compare April 27, 2026 15:41
robinbb added a commit to robinbb/dune that referenced this pull request Apr 27, 2026
A consumer that uses [Root.LibY.foo] reports only [Root] as a
top-level ocamldep reference; qualified-path components are not
surfaced. The filter therefore needs to look through [Root] to
discover [LibY]. But [Root] had no [.d] file (its [Module.kind]
short-circuits in [Dep_rules.deps_of] via [is_alias_or_root], an
intentional cycle-prevention measure introduced in commit
a5d8945 / change ocaml#12227), so [read_immediate_deps_raw_of]
returned an empty set and the cross-library walk never reached
[LibY]. The consumer's compile rule was missing the dep on
[LibY]'s cmi: the OCaml path got away with it because [-I] flags
still found the cmi at compile time, but Melange's strict-
ordering rules surfaced the gap as a missing-cmi error.

Synthesise a [.d]-format file for [Root] alongside [root.ml] in
[build_root_module], using [Root_module.entries] (the same input
[root.ml] is generated from). The file matches what [ocamldep
-modules] would output, so the general path in
[Ocamldep.read_immediate_deps_raw_of] reads it like any other
module's [.d]. [obj_dir.ml] is updated to return a path for
[(Root, Immediate _)] while continuing to refuse
[(Root, Transitive _)] — the cycle that motivated the original
short-circuit lives in [.all-deps] generation, which we still
suppress.

This keeps [read_dep_m_raw] uniform (no [Module.kind] switch),
shrinks [module_kind_has_readable_ocamldep]'s false branch by
one entry, and locates the [Root] knowledge in
[build_root_module] where [root.ml] is also produced.

The regression test for incremental-rebuild behaviour through
[Root] lives separately at
test/blackbox-tests/test-cases/root-module/incremental-rebuild.t
in the prerequisite PR (ocaml#14335), so the test is on main as a
pure regression guard.

Signed-off-by: Robin Bate Boerop <me@robinbb.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants