Skip to content

Commit c4dd788

Browse files
committed
refactor: Remove explicit OCaml transitive dependency files
Compute transitive module dependency closures directly from anonymous ocamldep and ocamlobjinfo actions instead of materializing .all-deps files. This removes the merge-file rule path for internal dependency setup while preserving module-level cycle diagnostics. As a side-effect, this fixes an ugly "Rule not found" error. Signed-off-by: Rudi Grinberg <me@rgrinberg.com>
1 parent 549dd57 commit c4dd788

17 files changed

Lines changed: 436 additions & 256 deletions

File tree

src/dune_rules/dep_rules.ml

Lines changed: 372 additions & 84 deletions
Large diffs are not rendered by default.

src/dune_rules/dep_rules.mli

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,13 @@ val read_immediate_deps_of
3333
-> Module.t list Action_builder.t
3434

3535
val read_deps_of
36-
: obj_dir:Path.Build.t Obj_dir.t
36+
: sandbox:Sandbox_config.t
37+
-> sctx:Super_context.t
38+
-> obj_dir:Path.Build.t Obj_dir.t
3739
-> modules:Modules.With_vlib.t
38-
-> ml_kind:Ml_kind.t
40+
-> impl:Virtual_rules.t
41+
-> dir:Path.Build.t
3942
-> for_:Compilation_mode.t
43+
-> ml_kind:Ml_kind.t
4044
-> Module.t
4145
-> Module.t list Action_builder.t

src/dune_rules/melange/melange_rules.ml

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,16 @@ let make_same_lib_emission_deps =
246246
>>= function
247247
| true ->
248248
let* intf_deps =
249-
Dep_rules.read_deps_of ~obj_dir ~modules ~ml_kind:Intf module_ ~for_
249+
Dep_rules.read_deps_of
250+
~sandbox
251+
~sctx
252+
~obj_dir
253+
~modules
254+
~impl:Virtual_rules.no_implements
255+
~dir:(Obj_dir.dir obj_dir)
256+
~for_
257+
~ml_kind:Intf
258+
module_
250259
in
251260
(* Cross-module optimization follows implementation artifacts, but the
252261
initial reachability also comes from the emitted module's interface
@@ -256,10 +265,10 @@ let make_same_lib_emission_deps =
256265
|> Action_builder.map ~f:(deps_of_xopt_closure ~obj_dir)
257266
| false ->
258267
(* Emission reads same-library implementation artifacts recursively.
259-
The generic [.impl.all-deps] files collapse transitive edges through
260-
interfaces when a dependency has an [.mli], which is fine for
261-
compilation but insufficient for JS emission. Follow the
262-
implementation dependency graph directly instead. *)
268+
Compilation dependencies collapse transitive edges through interfaces
269+
when a dependency has an [.mli], which is insufficient for JS
270+
emission. Follow the implementation dependency graph directly
271+
instead. *)
263272
Dep_graph.top_closed_implementations dep_graph [ module_ ]
264273
|> Action_builder.map ~f:(deps_of_impl_closure ~obj_dir)
265274
;;

src/dune_rules/ocamldep.ml

Lines changed: 0 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,4 @@
11
open Import
2-
open Memo.O
3-
4-
module Merge_files_into = struct
5-
module Spec = struct
6-
type ('src, 'dst) t =
7-
{ transitive : 'src list
8-
; immediate : Module_name.Unique.t list
9-
; target : 'dst
10-
}
11-
12-
let name = "merge_files_into"
13-
let version = 2
14-
let is_useful_to ~memoize:_ = true
15-
16-
let bimap t path target =
17-
{ t with transitive = List.map t.transitive ~f:path; target = target t.target }
18-
;;
19-
20-
let encode
21-
(type src dst)
22-
({ transitive; immediate; target } : (src, dst) t)
23-
(input : src -> Sexp.t)
24-
(output : dst -> Sexp.t)
25-
: Sexp.t
26-
=
27-
List
28-
[ List (List.map transitive ~f:input)
29-
; List
30-
(List.map ~f:(fun s -> Sexp.Atom (Module_name.Unique.to_string s)) immediate)
31-
; output target
32-
]
33-
;;
34-
35-
let action { transitive; immediate; target } ~ectx:_ ~eenv:_ =
36-
Async.async (fun () ->
37-
List.fold_left
38-
transitive
39-
~init:(Module_name.Unique.Set.of_list immediate)
40-
~f:(fun set source_path ->
41-
Io.lines_of_file source_path
42-
|> Module_name.Unique.Set.of_list_map ~f:Module_name.Unique.of_string
43-
|> Module_name.Unique.Set.union set)
44-
|> Module_name.Unique.Set.to_list_map ~f:Module_name.Unique.to_string
45-
|> Io.write_lines (Path.build target))
46-
;;
47-
end
48-
49-
module Action = Action_ext.Make (Spec)
50-
51-
let action ~transitive ~immediate ~target =
52-
Action.action { transitive; immediate; target }
53-
;;
54-
end
552

563
let parse_module_names ~dir ~(unit : Module.t) ~modules words =
574
List.concat_map words ~f:(fun m ->
@@ -75,14 +22,6 @@ let parse_module_names ~dir ~(unit : Module.t) ~modules words =
7522
])
7623
;;
7724

78-
let parse_compilation_units ~modules =
79-
let obj_map = Modules.With_vlib.obj_map modules in
80-
List.filter_map ~f:(fun m ->
81-
let obj_name = Module_name.Unique.of_string m in
82-
Module_name.Unique.Map.find obj_map obj_name
83-
|> Option.map ~f:Modules.Sourced_module.to_module)
84-
;;
85-
8625
let parse_deps_exn =
8726
let invalid file lines =
8827
User_error.raise
@@ -106,19 +45,6 @@ let parse_deps_exn =
10645
String.extract_blank_separated_words deps)
10746
;;
10847

109-
let transitive_deps =
110-
let transive_dep obj_dir m ~for_ =
111-
(match Module.kind m with
112-
| Root | Alias _ -> None
113-
| _ -> if Module.has m ~ml_kind:Intf then Some Ml_kind.Intf else Some Impl)
114-
|> Option.map ~f:(fun ml_kind ->
115-
Obj_dir.Module.dep obj_dir ~for_ (Transitive (m, ml_kind))
116-
|> Option.value_exn (* we already checked if it's an alias module *)
117-
|> Path.build)
118-
in
119-
fun obj_dir modules ~for_ -> List.filter_map modules ~f:(transive_dep obj_dir ~for_)
120-
;;
121-
12248
let ocamldep_action ~sandbox ~sctx ~dir ~ml_kind unit =
12349
let context = Super_context.context sctx in
12450
let flags, sandbox =
@@ -174,39 +100,3 @@ let read_immediate_deps_of ~sandbox ~sctx ~obj_dir ~modules ~ml_kind unit =
174100
|> Action_builder.of_memo
175101
|> Action_builder.memoize memo_name
176102
;;
177-
178-
let deps_of ~sandbox ~modules ~sctx ~dir ~obj_dir ~ml_kind ~for_ unit =
179-
let dep = Obj_dir.Module.dep obj_dir ~for_ in
180-
let all_deps_file = dep (Transitive (unit, ml_kind)) |> Option.value_exn in
181-
let+ () =
182-
let produce_all_deps =
183-
let open Action_builder.O in
184-
(let+ transitive, immediate =
185-
(let+ immediate_deps =
186-
read_immediate_deps_of ~sandbox ~sctx ~obj_dir ~modules ~ml_kind unit
187-
in
188-
let transitive_deps = transitive_deps obj_dir immediate_deps ~for_ in
189-
let immediate_deps = List.map immediate_deps ~f:Module.obj_name in
190-
(transitive_deps, immediate_deps), transitive_deps)
191-
|> Action_builder.dyn_paths
192-
in
193-
Merge_files_into.action ~transitive ~immediate ~target:all_deps_file)
194-
|> Action_builder.with_file_targets ~file_targets:[ all_deps_file ]
195-
in
196-
Action_builder.With_targets.map ~f:Action.Full.make produce_all_deps
197-
|> Super_context.add_rule sctx ~dir
198-
in
199-
let all_deps_file = Path.build all_deps_file in
200-
Action_builder.lines_of all_deps_file
201-
|> Action_builder.map ~f:(parse_compilation_units ~modules)
202-
|> Action_builder.memoize (Path.to_string all_deps_file)
203-
;;
204-
205-
let read_deps_of ~obj_dir ~modules ~ml_kind ~for_ unit =
206-
let all_deps_file =
207-
Obj_dir.Module.dep obj_dir ~for_ (Transitive (unit, ml_kind)) |> Option.value_exn
208-
in
209-
Action_builder.lines_of (Path.build all_deps_file)
210-
|> Action_builder.map ~f:(parse_compilation_units ~modules)
211-
|> Action_builder.memoize (Path.Build.to_string all_deps_file)
212-
;;

src/dune_rules/ocamldep.mli

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,6 @@
22

33
open Import
44

5-
val deps_of
6-
: sandbox:Sandbox_config.t
7-
-> modules:Modules.With_vlib.t
8-
-> sctx:Super_context.t
9-
-> dir:Path.Build.t
10-
-> obj_dir:Path.Build.t Obj_dir.t
11-
-> ml_kind:Ml_kind.t
12-
-> for_:Compilation_mode.t
13-
-> Module.t
14-
-> Module.t list Action_builder.t Memo.t
15-
16-
val read_deps_of
17-
: obj_dir:Path.Build.t Obj_dir.t
18-
-> modules:Modules.With_vlib.t
19-
-> ml_kind:Ml_kind.t
20-
-> for_:Compilation_mode.t
21-
-> Module.t
22-
-> Module.t list Action_builder.t
23-
245
(** [read_immediate_deps_of ~obj_dir ~modules ~ml_kind unit] returns the
256
immediate dependencies found in the modules of [modules] for the file with
267
kind [ml_kind] of the module [unit]. If there is no such file with kind

test/blackbox-tests/test-cases/cyclic-dep-executable.t

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,11 @@ Reports module dependency cycles inside executables.
2323

2424
$ dune build
2525
Error: Dependency cycle between:
26-
_build/default/.foo.eobjs/dune__exe__Baz.impl.all-deps
27-
-> _build/default/.foo.eobjs/dune__exe__Bar.impl.all-deps
28-
-> _build/default/.foo.eobjs/dune__exe__Baz.impl.all-deps
29-
-> required by _build/default/.foo.eobjs/dune__exe__Foo.impl.all-deps
26+
transitive deps of dune__exe__Baz.impl in _build/default
27+
-> transitive deps of dune__exe__Bar.impl in _build/default
28+
-> transitive deps of dune__exe__Baz.impl in _build/default
29+
-> required by transitive deps of dune__exe__Foo.impl in _build/default
3030
-> required by _build/default/foo.exe
3131
-> required by alias all
3232
-> required by alias default
3333
[1]
34-

test/blackbox-tests/test-cases/include-qualified/build-with-sandbox.t

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,28 @@ Test `(include_subdirs qualified)` with sandboxing
1919

2020
$ DUNE_SANDBOX=symlink dune build
2121

22-
Transitive deps file includes the alias module
22+
No transitive deps file is materialized.
2323

24-
$ cat _build/default/lib/.foo.objs/foo__Bar.impl.all-deps
25-
foo__Sub
26-
foo__Sub__Hello
24+
$ find _build -name "*.all-deps" | sort
25+
26+
Transitive alias dependencies are still available under sandboxing when a
27+
compiled interface exposes a qualified submodule path.
28+
29+
$ cat > lib/a.ml <<EOF
30+
> let x = B.x
31+
> EOF
32+
$ cat > lib/b.mli <<EOF
33+
> val x : Sub.T.t
34+
> EOF
35+
$ cat > lib/b.ml <<EOF
36+
> let x = Sub.T.x
37+
> EOF
38+
$ cat > lib/sub/t.ml <<EOF
39+
> type t = int
40+
> let x = 0
41+
> EOF
42+
43+
$ DUNE_SANDBOX=symlink dune build
2744

2845
$ cat > lib/dune <<EOF
2946
> (include_subdirs qualified)
@@ -34,6 +51,7 @@ Transitive deps file includes the alias module
3451
> EOF
3552
$ cat > lib/sub/sub.ml <<EOF
3653
> module Hello = Hello
54+
> module T = T
3755
> let world = "world"
3856
> EOF
3957
$ cat > lib/sub/hello.ml <<EOF
@@ -42,7 +60,4 @@ Transitive deps file includes the alias module
4260

4361
$ DUNE_SANDBOX=symlink dune build
4462

45-
$ cat _build/default/lib/.foo.objs/foo__Bar.impl.all-deps
46-
foo__Sub
47-
foo__Sub__
48-
foo__Sub__Hello
63+
$ find _build -name "*.all-deps" | sort

test/blackbox-tests/test-cases/include-qualified/invalid-deps/group-interface-sub-module.t

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ We shouldn't allow foo/y/$x.ml to depend on foo/foo.ml
2323
X is the main module of the library and is the only module exposed outside of
2424
the library. Consequently, it should be the one depending on all the other
2525
modules in the library.
26-
-> required by _build/default/.foo.objs/foo__X__Y__Z.impl.all-deps
26+
-> required by transitive deps of foo__X__Y__Z.impl in _build/default
2727
-> required by _build/default/.foo.objs/byte/foo__X__Y__Z.cmo
2828
-> required by _build/default/foo.cma
2929
-> required by alias all

test/blackbox-tests/test-cases/include-qualified/invalid-deps/group-interface.t

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ We shouldn't allow foo/$x.ml to depend on foo/foo.ml
2323
Baz is the main module of the library and is the only module exposed outside
2424
of the library. Consequently, it should be the one depending on all the other
2525
modules in the library.
26-
-> required by _build/default/.foo.objs/foo__Baz__Bar.impl.all-deps
26+
-> required by transitive deps of foo__Baz__Bar.impl in _build/default
2727
-> required by _build/default/.foo.objs/byte/foo__Baz__Bar.cmo
2828
-> required by _build/default/foo.cma
2929
-> required by alias all

test/blackbox-tests/test-cases/include-qualified/ocamldep-regression.t

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ We also forbid submodules from depending on their interface modules:
3939
Baz is the main module of the library and is the only module exposed outside
4040
of the library. Consequently, it should be the one depending on all the other
4141
modules in the library.
42-
-> required by _build/default/.foo.objs/foo__Baz__Bar.impl.all-deps
42+
-> required by transitive deps of foo__Baz__Bar.impl in _build/default
4343
-> required by _build/default/.foo.objs/byte/foo__Baz__Bar.cmo
4444
-> required by _build/default/foo.cma
4545
-> required by alias all
@@ -60,7 +60,7 @@ Or their parent interface modules:
6060
Baz is the main module of the library and is the only module exposed outside
6161
of the library. Consequently, it should be the one depending on all the other
6262
modules in the library.
63-
-> required by _build/default/.foo.objs/foo__Baz__Foo__Z.impl.all-deps
63+
-> required by transitive deps of foo__Baz__Foo__Z.impl in _build/default
6464
-> required by _build/default/.foo.objs/byte/foo__Baz__Foo__Z.cmo
6565
-> required by _build/default/foo.cma
6666
-> required by alias all

0 commit comments

Comments
 (0)