From 4a9a7ad411b9a71409bcb20be18028db66a0f92e Mon Sep 17 00:00:00 2001 From: Rudi Grinberg Date: Fri, 28 Aug 2026 17:35:21 +0100 Subject: [PATCH] fix(ctypes): track implicit header dependencies Signed-off-by: Rudi Grinberg --- doc/changes/fixed/16243.md | 2 ++ src/dune_rules/ctypes/ctypes_rules.ml | 29 ++++++++++--------- .../test-cases/ctypes/exe-vendored.t/run.t | 8 +++-- 3 files changed, 22 insertions(+), 17 deletions(-) create mode 100644 doc/changes/fixed/16243.md diff --git a/doc/changes/fixed/16243.md b/doc/changes/fixed/16243.md new file mode 100644 index 00000000000..445baf7eb83 --- /dev/null +++ b/doc/changes/fixed/16243.md @@ -0,0 +1,2 @@ +- Rebuild generated Ctypes helpers when headers from Ctypes' implicit include + directories change (#16243, @rgrinberg) diff --git a/src/dune_rules/ctypes/ctypes_rules.ml b/src/dune_rules/ctypes/ctypes_rules.ml index f5f9d3be774..cc814813c04 100644 --- a/src/dune_rules/ctypes/ctypes_rules.ml +++ b/src/dune_rules/ctypes/ctypes_rules.ml @@ -207,23 +207,24 @@ let build_c_program in let include_args = let open Action_builder.O in - let+ include_dirs = - let* ocaml = Action_builder.of_memo ocaml in - let+ ctypes_include_dirs = - (* XXX: need glob dependency *) - let open Action_builder.O in - let+ lib = - let ctypes = Lib_name.of_string "ctypes" in - Lib.DB.resolve (Scope.libs scope) (Loc.none, ctypes) |> Resolve.Memo.read - in - Lib_flags.L.include_paths [ lib ] (Ocaml Native) ocaml.lib_config - |> Lib_flags.L.include_only + let* ocaml = Action_builder.of_memo ocaml in + let+ ctypes_include_dirs = + let+ lib = + let ctypes = Lib_name.of_string "ctypes" in + Lib.DB.resolve (Scope.libs scope) (Loc.none, ctypes) |> Resolve.Memo.read in - let ocaml_where = ocaml.lib_config.stdlib_dir in - ocaml_where :: ctypes_include_dirs + Lib_flags.L.include_paths [ lib ] (Ocaml Native) ocaml.lib_config + |> Lib_flags.L.include_only + in + let include_arg dir = Command.Args.S [ A "-I"; Path dir ] in + let ctypes_include_arg dir = + let headers = File_selector.of_glob ~dir (Glob.of_string "*.h") in + Command.Args.S + [ include_arg dir; Hidden_deps (Dep.Set.singleton (Dep.file_selector headers)) ] in Command.Args.S - (List.map include_dirs ~f:(fun dir -> Command.Args.S [ A "-I"; Path dir ])) + (include_arg ocaml.lib_config.stdlib_dir + :: List.map ctypes_include_dirs ~f:ctypes_include_arg) in let extra_deps = let source_file_deps = diff --git a/test/blackbox-tests/test-cases/ctypes/exe-vendored.t/run.t b/test/blackbox-tests/test-cases/ctypes/exe-vendored.t/run.t index 68ae6da8077..9ccdf6f50b9 100644 --- a/test/blackbox-tests/test-cases/ctypes/exe-vendored.t/run.t +++ b/test/blackbox-tests/test-cases/ctypes/exe-vendored.t/run.t @@ -36,7 +36,7 @@ With Ctypes 0.3, the final executable link runs outside a sandbox. > ][0]' {"sandbox":false,"example_flags":1} -The Ctypes headers found through its implicit include directory are not rule +The Ctypes headers found through its implicit include directory are rule dependencies. $ dune rules --format=json \ @@ -46,6 +46,7 @@ dependencies. > | select(.deps) > | ruleDepFilePaths > | select(endswith("ctypes_cstubs_internals.h")) + > | {kind: "file", path: basename} > ] > + > [ .[] @@ -53,5 +54,6 @@ dependencies. > | ruleDepGlobEntries > | select((.dir | endswith("ctypes")) > and (.predicate | tostring | contains(".h"))) - > ]) | length > 0' - false + > | {kind: "glob", dir: (.dir | basename), predicate} + > ]) | unique' + [{"kind":"glob","dir":"ctypes","predicate":"*.h"}]