From 58251d061a8b6bf71d1789f11fed215473be09c3 Mon Sep 17 00:00:00 2001 From: Robin Bate Boerop Date: Sat, 2 May 2026 08:00:13 -0700 Subject: [PATCH 1/4] test: regression guard for cross-lib reads of pre-pp source MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The neighbour [cross-lib-action-preprocess.t] (regression guard for #14383, https://github.com/ocaml/dune/pull/14383) covers the same scenario — a consumer of a library that uses [(preprocess (action ...))] — but with a pass-through preprocessor and plain-OCaml sources. Any future code path that resolves the consumer's cross-library dependencies by running ocamldep on the dep's pre-[Module.pped] source would still pass that test, because ocamldep is happy with plain OCaml. This test plugs the gap: [pp.exe] strips [#]-prefixed lines, and [dep/foo.ml] starts with a [#if]/[#endif] block that is syntactically invalid OCaml. Ocamldep on the post-pp output is fine; ocamldep on the pre-pp input is a syntax error. So if a future change runs ocamldep against the [Module.t] from [Dir_contents.modules_of_local_lib] (which is the raw, pre-[Module.pped] form, with [Module.source] pointing at the unprocessed [.ml] and [Module.pp_flags = None]), the consumer's build will fail with that syntax error and this test will catch it. [dep] is multi-module so a hypothetical short-circuit for single-module stanzas can't mask the regression. Signed-off-by: Robin Bate Boerop --- .../cross-lib-walk-pre-pp-source.t | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 test/blackbox-tests/test-cases/per-module-lib-deps/cross-lib-walk-pre-pp-source.t diff --git a/test/blackbox-tests/test-cases/per-module-lib-deps/cross-lib-walk-pre-pp-source.t b/test/blackbox-tests/test-cases/per-module-lib-deps/cross-lib-walk-pre-pp-source.t new file mode 100644 index 00000000000..f7366b0c0d6 --- /dev/null +++ b/test/blackbox-tests/test-cases/per-module-lib-deps/cross-lib-walk-pre-pp-source.t @@ -0,0 +1,86 @@ +A consumer of a library that uses [(preprocess (action ...))] must +still build when the dep's pre-preprocessing source is not valid +OCaml on its own. Concretely: if any code path runs ocamldep on the +raw, pre-[Module.pped] form of the dep's [.ml] (the [Module.t] from +[Dir_contents.modules_of_local_lib], whose [Module.source] points +at the unprocessed [.ml] and whose [Module.pp_flags] is [None]), +ocamldep rejects the source with a syntax error and the build +fails. + +The neighbour test [cross-lib-action-preprocess.t] covers the same +scenario with a pass-through preprocessor and plain-OCaml sources, +so a code path that runs ocamldep on the pre-pp form goes +undetected. This test plugs that gap: [pp.exe] strips [#]-prefixed +lines, and [dep/foo.ml] starts with a [#if]/[#endif] block. +Ocamldep on the post-pp output is fine; ocamldep on the pre-pp +input is a syntax error. + + $ cat > dune-project < (lang dune 3.0) + > EOF + +A trivial preprocessor that drops every line starting with [#]. +This mimics cppo's behaviour without needing an external tool: the +[.ml] holds [#if]/[#endif]-style markers; the preprocessor's output +is plain OCaml. + + $ mkdir pp + $ cat > pp/dune < (executable (name pp)) + > EOF + $ cat > pp/pp.ml <<'EOF' + > let () = + > let ic = open_in_bin Sys.argv.(1) in + > try + > while true do + > let line = input_line ic in + > if String.length line = 0 || line.[0] <> '#' + > then print_endline line + > done + > with End_of_file -> () + > EOF + +[dep] is an unwrapped, multi-module library whose modules are +preprocessed. [foo.ml] has a [#]-prefixed directive that ocamldep +cannot parse but the preprocessor strips. [bar.ml] is the second +module: keeping the lib multi-module ensures any future per-module +code path that short-circuits single-module stanzas doesn't mask +the regression this test guards against. + + $ mkdir dep + $ cat > dep/dune < (library + > (name dep) + > (wrapped false) + > (preprocess (action (run %{exe:../pp/pp.exe} %{input-file})))) + > EOF + $ cat > dep/foo.ml <<'EOF' + > #if FAKE_DIRECTIVE + > let v = 1 + > #endif + > EOF + $ cat > dep/bar.ml < let helper = 42 + > EOF + +[consumer] references [Foo] from [dep]. Any cross-library +dependency-tracking code path that resolves [Foo]'s deps against +the dep's pre-pp source would fail here: + + $ mkdir consumer + $ cat > consumer/dune < (library (name consumer) (wrapped false) (libraries dep)) + > EOF + $ cat > consumer/c.ml < let _ = Foo.v + > EOF + $ cat > consumer/d.ml < let _ = () + > EOF + +The build must succeed. If a future change reintroduces a cross- +library code path that runs ocamldep on the pre-[Module.pped] form +of a foreign-lib module without re-applying that lib's preprocessor, +this test will fail with a syntax error from [dep/foo.ml]. + + $ dune build @check From c331433001ac1690067cdb218de40370b318ff49 Mon Sep 17 00:00:00 2001 From: Robin Bate Boerop Date: Tue, 5 May 2026 14:29:54 -0700 Subject: [PATCH 2/4] test: bump cross-lib-walk-pre-pp-source.t to (lang dune 3.24) Per @Alizter's review on #14400. New tests landing now should pin to the in-development next-release version (3.24, registered by `feat: %{pkg:foo:lib:file} pforms` in 9260640092). 3.0 was a habit; the test only uses [(executable ...)] / [(library ... (preprocess (action ...)))] / [(libraries ...)], all available since 3.0, so the bump is for consistency only. Signed-off-by: Robin Bate Boerop --- .../per-module-lib-deps/cross-lib-walk-pre-pp-source.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/blackbox-tests/test-cases/per-module-lib-deps/cross-lib-walk-pre-pp-source.t b/test/blackbox-tests/test-cases/per-module-lib-deps/cross-lib-walk-pre-pp-source.t index f7366b0c0d6..d02db60ee3c 100644 --- a/test/blackbox-tests/test-cases/per-module-lib-deps/cross-lib-walk-pre-pp-source.t +++ b/test/blackbox-tests/test-cases/per-module-lib-deps/cross-lib-walk-pre-pp-source.t @@ -16,7 +16,7 @@ Ocamldep on the post-pp output is fine; ocamldep on the pre-pp input is a syntax error. $ cat > dune-project < (lang dune 3.0) + > (lang dune 3.24) > EOF A trivial preprocessor that drops every line starting with [#]. From 3491acdcd68da7fbd969fba7bc62dbce579bbab3 Mon Sep 17 00:00:00 2001 From: Robin Bate Boerop Date: Tue, 5 May 2026 14:40:28 -0700 Subject: [PATCH 3/4] test: also assert the registered cross-lib dep shape MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per @Alizter's review on #14400 (in complementary form, not as a replacement for the build-success check): pin the registered dep shape on the consumer's [c.cmi] rule. The dep is registered as a [*.cmi] glob over the dep's objdir. A future regression that switched to a per-file dep on the wrong basename (e.g. [dep/.dep.objs/byte/foo.pp.cmi] instead of [foo.cmi]) would surface here as a different recorded dep — distinct from the build-success failure mode that catches an internal ocamldep invocation on pre-pp source. Signed-off-by: Robin Bate Boerop --- .../cross-lib-walk-pre-pp-source.t | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/blackbox-tests/test-cases/per-module-lib-deps/cross-lib-walk-pre-pp-source.t b/test/blackbox-tests/test-cases/per-module-lib-deps/cross-lib-walk-pre-pp-source.t index d02db60ee3c..dca340cdc3c 100644 --- a/test/blackbox-tests/test-cases/per-module-lib-deps/cross-lib-walk-pre-pp-source.t +++ b/test/blackbox-tests/test-cases/per-module-lib-deps/cross-lib-walk-pre-pp-source.t @@ -84,3 +84,14 @@ of a foreign-lib module without re-applying that lib's preprocessor, this test will fail with a syntax error from [dep/foo.ml]. $ dune build @check + +Confirm that [consumer/c.cmi] depends on [dep]'s objdir for cmis, +and not on any path that names the pre-pp source. The dep is +registered as a [*.cmi] glob over the dep's objdir; a future +regression that switched to a per-file dep on the wrong basename +(e.g. [dep/.dep.objs/byte/foo.pp.cmi] instead of [foo.cmi]) would +surface here as a different recorded dep: + + $ dune rules --root . --format=json --deps _build/default/consumer/.consumer.objs/byte/c.cmi | + > jq -r 'include "dune"; .[] | depsGlobPredicates' + *.cmi From 939b6f2919ff3f6fb1e423083fa330396634afdd Mon Sep 17 00:00:00 2001 From: Robin Bate Boerop Date: Tue, 5 May 2026 15:25:37 -0700 Subject: [PATCH 4/4] test: use make_dune_project helper in cross-lib-walk-pre-pp-source.t Signed-off-by: Robin Bate Boerop --- .../per-module-lib-deps/cross-lib-walk-pre-pp-source.t | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/blackbox-tests/test-cases/per-module-lib-deps/cross-lib-walk-pre-pp-source.t b/test/blackbox-tests/test-cases/per-module-lib-deps/cross-lib-walk-pre-pp-source.t index dca340cdc3c..2bbb9c26970 100644 --- a/test/blackbox-tests/test-cases/per-module-lib-deps/cross-lib-walk-pre-pp-source.t +++ b/test/blackbox-tests/test-cases/per-module-lib-deps/cross-lib-walk-pre-pp-source.t @@ -15,9 +15,7 @@ lines, and [dep/foo.ml] starts with a [#if]/[#endif] block. Ocamldep on the post-pp output is fine; ocamldep on the pre-pp input is a syntax error. - $ cat > dune-project < (lang dune 3.24) - > EOF + $ make_dune_project 3.24 A trivial preprocessor that drops every line starting with [#]. This mimics cppo's behaviour without needing an external tool: the