Skip to content

Commit 473cdae

Browse files
committed
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 630aa54 commit 473cdae

16 files changed

Lines changed: 378 additions & 211 deletions

File tree

src/dune_rules/dep_rules.ml

Lines changed: 132 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,6 @@ open Import
22
open Memo.O
33
module Parallel_map = Memo.Make_parallel_map (Module_name.Unique.Map)
44

5-
let transitive_deps_contents modules =
6-
List.map modules ~f:(fun m ->
7-
(* TODO use object names *)
8-
Modules.Sourced_module.to_module m |> Module.name |> Module_name.to_string)
9-
|> String.concat ~sep:"\n"
10-
;;
11-
125
let ooi_deps
136
~vimpl
147
~sctx
@@ -17,11 +10,10 @@ let ooi_deps
1710
~dune_version
1811
~vlib_obj_map
1912
~(ml_kind : Ml_kind.t)
20-
~for_
2113
(sourced_module : Modules.Sourced_module.t)
2214
=
2315
let m = Modules.Sourced_module.to_module sourced_module in
24-
let* read =
16+
let+ read =
2517
let unit =
2618
let cm_kind =
2719
match ml_kind with
@@ -42,7 +34,6 @@ let ooi_deps
4234
| [ x ] -> x
4335
| [] | _ :: _ -> assert false)
4436
in
45-
let add_rule = Super_context.add_rule sctx ~dir in
4637
let read =
4738
Action_builder.memoize
4839
"ocamlobjinfo"
@@ -54,14 +45,6 @@ let ooi_deps
5445
then None
5546
else Module_name.Unique.Map.find vlib_obj_map dep))
5647
in
57-
let+ () =
58-
add_rule
59-
(let target =
60-
Obj_dir.Module.dep obj_dir ~for_ (Transitive (m, ml_kind)) |> Option.value_exn
61-
in
62-
Action_builder.map read ~f:transitive_deps_contents
63-
|> Action_builder.write_file_dyn target)
64-
in
6548
read
6649
;;
6750

@@ -72,77 +55,115 @@ let wrapped_compat_deps modules m =
7255
| None -> [ inner ]
7356
;;
7457

75-
let deps_of_module ~modules ~sandbox ~sctx ~dir ~obj_dir ~ml_kind ~for_ m =
76-
match Module.kind m with
77-
| Wrapped_compat ->
78-
wrapped_compat_deps modules m |> Action_builder.return |> Memo.return
79-
| _ ->
80-
let+ deps = Ocamldep.deps_of ~sandbox ~modules ~sctx ~dir ~obj_dir ~ml_kind ~for_ m in
81-
(match Modules.With_vlib.alias_for modules m with
82-
| [] -> deps
83-
| aliases ->
84-
let open Action_builder.O in
85-
let+ deps = deps in
86-
aliases @ deps)
87-
;;
58+
type imported_vlib_deps =
59+
{ deps_of :
60+
Modules.Sourced_module.t
61+
-> ml_kind:Ml_kind.t
62+
-> Module.t list Action_builder.t Memo.t
63+
}
8864

89-
let deps_of_vlib_module ~obj_dir ~vimpl ~dir ~sctx ~ml_kind ~for_ sourced_module =
90-
match
91-
let vlib = Vimpl.vlib vimpl in
92-
Lib.Local.of_lib vlib
93-
with
65+
let make_imported_vlib_deps ~obj_dir ~vimpl ~dir ~sctx ~sandbox =
66+
let vlib = Vimpl.vlib vimpl in
67+
match Lib.Local.of_lib vlib with
9468
| None ->
95-
let+ deps =
96-
let vlib_obj_map = Vimpl.vlib_obj_map vimpl in
97-
let dune_version =
98-
let impl = Vimpl.impl vimpl in
99-
Dune_project.dune_version impl.project
69+
let vlib_obj_map = Vimpl.vlib_obj_map vimpl in
70+
let dune_version =
71+
let impl = Vimpl.impl vimpl in
72+
Dune_project.dune_version impl.project
73+
in
74+
let deps_of sourced_module ~ml_kind =
75+
let+ deps =
76+
ooi_deps
77+
~vimpl
78+
~sctx
79+
~dir
80+
~obj_dir
81+
~dune_version
82+
~vlib_obj_map
83+
~ml_kind
84+
sourced_module
10085
in
101-
ooi_deps
102-
~vimpl
103-
~sctx
104-
~dir
105-
~obj_dir
106-
~dune_version
107-
~vlib_obj_map
108-
~ml_kind
109-
~for_
110-
sourced_module
86+
Action_builder.map deps ~f:(fun deps ->
87+
List.map deps ~f:Modules.Sourced_module.to_module)
11188
in
112-
Action_builder.map deps ~f:(List.map ~f:Modules.Sourced_module.to_module)
89+
{ deps_of }
11390
| Some lib ->
11491
let vlib_obj_dir =
11592
let info = Lib.Local.info lib in
11693
Lib_info.obj_dir info
11794
in
118-
let m = Modules.Sourced_module.to_module sourced_module in
119-
let+ () =
120-
let src =
121-
Obj_dir.Module.dep vlib_obj_dir ~for_ (Transitive (m, ml_kind))
122-
|> Option.value_exn
123-
|> Path.build
124-
in
125-
let dst =
126-
Obj_dir.Module.dep obj_dir ~for_ (Transitive (m, ml_kind)) |> Option.value_exn
127-
in
128-
Super_context.add_rule sctx ~dir (Action_builder.symlink ~src ~dst)
129-
in
13095
let modules = Vimpl.vlib_modules vimpl |> Modules.With_vlib.modules in
131-
Ocamldep.read_deps_of ~obj_dir:vlib_obj_dir ~modules ~ml_kind ~for_ m
96+
let ocamldep =
97+
Ocamldep.create
98+
~deps_of_imported_vlib:(fun _ ~ml_kind:_ -> None)
99+
~sandbox
100+
~modules
101+
~sctx
102+
~dir:(Obj_dir.dir vlib_obj_dir)
103+
~obj_dir:vlib_obj_dir
104+
()
105+
in
106+
let deps_of sourced_module ~ml_kind =
107+
let m = Modules.Sourced_module.to_module sourced_module in
108+
Ocamldep.deps_of ocamldep ~ml_kind m |> Memo.return
109+
in
110+
{ deps_of }
111+
;;
112+
113+
let deps_of_imported_vlib imported_vlib_deps m ~ml_kind =
114+
let open Action_builder.O in
115+
let* deps =
116+
Action_builder.of_memo
117+
(imported_vlib_deps.deps_of (Modules.Sourced_module.Imported_from_vlib m) ~ml_kind)
118+
in
119+
deps
120+
;;
121+
122+
let make_ocamldep ~obj_dir ~modules ~sandbox ~impl ~dir ~sctx =
123+
let imported_vlib_deps =
124+
match (Modules.With_vlib.split_by_lib modules).vlib with
125+
| [] -> None
126+
| _ :: _ ->
127+
Some
128+
(make_imported_vlib_deps
129+
~obj_dir
130+
~vimpl:(Virtual_rules.vimpl_exn impl)
131+
~dir
132+
~sctx
133+
~sandbox)
134+
in
135+
let deps_of_imported_vlib =
136+
match imported_vlib_deps with
137+
| None -> fun _ ~ml_kind:_ -> None
138+
| Some deps -> fun m ~ml_kind -> Some (deps_of_imported_vlib deps m ~ml_kind)
139+
in
140+
( Ocamldep.create ~deps_of_imported_vlib ~sandbox ~modules ~sctx ~dir ~obj_dir ()
141+
, imported_vlib_deps )
142+
;;
143+
144+
let deps_of_module ~modules ~ocamldep ~ml_kind m =
145+
match Module.kind m with
146+
| Wrapped_compat ->
147+
wrapped_compat_deps modules m |> Action_builder.return |> Memo.return
148+
| _ ->
149+
let deps = Ocamldep.deps_of ocamldep ~ml_kind m in
150+
Memo.return
151+
(match Modules.With_vlib.alias_for modules m with
152+
| [] -> deps
153+
| aliases ->
154+
let open Action_builder.O in
155+
let+ deps = deps in
156+
aliases @ deps)
132157
;;
133158

134159
(** Tests whether a set of modules is a singleton *)
135160
let has_single_file modules = Option.is_some @@ Modules.With_vlib.as_singleton modules
136161

137162
let rec deps_of
138-
~obj_dir
139163
~modules
140-
~sandbox
141-
~impl
142-
~dir
143-
~sctx
164+
~ocamldep
165+
~imported_vlib_deps
144166
~ml_kind
145-
~for_
146167
(m : Modules.Sourced_module.t)
147168
=
148169
let is_alias_or_root =
@@ -157,31 +178,44 @@ let rec deps_of
157178
then Memo.return (Action_builder.return [])
158179
else (
159180
let skip_if_source_absent f sourced_module =
160-
let m = Modules.Sourced_module.to_module m in
161-
if Module.has m ~ml_kind
181+
let module_ = Modules.Sourced_module.to_module sourced_module in
182+
if Module.has module_ ~ml_kind
162183
then f sourced_module
163184
else Memo.return (Action_builder.return [])
164185
in
165186
match m with
166187
| Imported_from_vlib _ ->
167-
let vimpl = Virtual_rules.vimpl_exn impl in
168-
skip_if_source_absent
169-
(deps_of_vlib_module ~obj_dir ~vimpl ~dir ~sctx ~ml_kind ~for_)
170-
m
171-
| Normal m ->
188+
(match imported_vlib_deps with
189+
| None -> Code_error.raise "imported vlib module without vlib deps" []
190+
| Some imported_vlib_deps ->
191+
skip_if_source_absent
192+
(fun sourced_module -> imported_vlib_deps.deps_of sourced_module ~ml_kind)
193+
m)
194+
| Normal _ ->
172195
skip_if_source_absent
173-
(deps_of_module ~modules ~sandbox ~sctx ~dir ~obj_dir ~ml_kind ~for_)
196+
(fun sourced_module ->
197+
Modules.Sourced_module.to_module sourced_module
198+
|> deps_of_module ~modules ~ocamldep ~ml_kind)
174199
m
175200
| Impl_of_virtual_module impl_or_vlib ->
176-
deps_of ~obj_dir ~modules ~sandbox ~impl ~dir ~sctx ~ml_kind ~for_
201+
deps_of ~modules ~ocamldep ~imported_vlib_deps ~ml_kind
177202
@@
178203
let m = Ml_kind.Dict.get impl_or_vlib ml_kind in
179204
(match ml_kind with
180205
| Intf -> Imported_from_vlib m
181206
| Impl -> Normal m))
182207
;;
183208

184-
let read_transitive_deps_of_module ~modules ~obj_dir ~ml_kind ~for_ unit =
209+
let read_transitive_deps_of_module
210+
~sandbox
211+
~sctx
212+
~modules
213+
~obj_dir
214+
~impl
215+
~dir
216+
~ml_kind
217+
unit
218+
=
185219
match Module.kind unit with
186220
| Root | Alias _ -> Action_builder.return []
187221
| Wrapped_compat -> wrapped_compat_deps modules unit |> Action_builder.return
@@ -190,7 +224,10 @@ let read_transitive_deps_of_module ~modules ~obj_dir ~ml_kind ~for_ unit =
190224
then Action_builder.return []
191225
else
192226
let open Action_builder.O in
193-
let+ deps = Ocamldep.read_deps_of ~obj_dir ~modules ~ml_kind ~for_ unit in
227+
let ocamldep, _imported_vlib_deps =
228+
make_ocamldep ~obj_dir ~modules ~sandbox ~impl ~dir ~sctx
229+
in
230+
let+ deps = Ocamldep.deps_of ocamldep ~ml_kind unit in
194231
(match Modules.With_vlib.alias_for modules unit with
195232
| [] -> deps
196233
| aliases -> aliases @ deps)
@@ -206,9 +243,10 @@ let read_immediate_deps_of ~sandbox ~sctx ~obj_dir ~modules ~ml_kind m =
206243
else Ocamldep.read_immediate_deps_of ~sandbox ~sctx ~obj_dir ~modules ~ml_kind m
207244
;;
208245

209-
let read_deps_of ~obj_dir ~modules ~ml_kind ~for_ m =
246+
let read_deps_of ~sandbox ~sctx ~obj_dir ~modules ~impl ~dir ~ml_kind m =
210247
if Module.has m ~ml_kind
211-
then read_transitive_deps_of_module ~modules ~obj_dir ~ml_kind ~for_ m
248+
then
249+
read_transitive_deps_of_module ~sandbox ~sctx ~modules ~obj_dir ~impl ~dir ~ml_kind m
212250
else Action_builder.return []
213251
;;
214252

@@ -218,20 +256,26 @@ let dict_of_func_concurrently f =
218256
Ml_kind.Dict.make ~impl ~intf
219257
;;
220258

221-
let for_module ~obj_dir ~modules ~sandbox ~impl ~dir ~sctx ~for_ module_ =
259+
let for_module ~obj_dir ~modules ~sandbox ~impl ~dir ~sctx ~for_:_ module_ =
260+
let ocamldep, imported_vlib_deps =
261+
make_ocamldep ~obj_dir ~modules ~sandbox ~impl ~dir ~sctx
262+
in
222263
dict_of_func_concurrently
223-
(deps_of ~obj_dir ~modules ~sandbox ~impl ~dir ~sctx ~for_ (Normal module_))
264+
(deps_of ~modules ~ocamldep ~imported_vlib_deps (Normal module_))
224265
;;
225266

226-
let rules ~obj_dir ~modules ~sandbox ~impl ~sctx ~dir ~for_ =
267+
let rules ~obj_dir ~modules ~sandbox ~impl ~sctx ~dir ~for_:_ =
227268
match Modules.With_vlib.as_singleton modules with
228269
| Some m -> Memo.return (Dep_graph.Ml_kind.dummy m)
229270
| None ->
271+
let ocamldep, imported_vlib_deps =
272+
make_ocamldep ~obj_dir ~modules ~sandbox ~impl ~dir ~sctx
273+
in
230274
dict_of_func_concurrently (fun ~ml_kind ->
231275
let+ per_module =
232276
Modules.With_vlib.obj_map modules
233277
|> Parallel_map.parallel_map ~f:(fun _obj_name m ->
234-
deps_of ~obj_dir ~modules ~sandbox ~impl ~sctx ~dir ~ml_kind ~for_ m)
278+
deps_of ~modules ~ocamldep ~imported_vlib_deps ~ml_kind m)
235279
in
236280
Dep_graph.make ~dir ~per_module)
237281
|> Memo.map ~f:(Dep_graph.Ml_kind.for_module_compilation ~modules)

src/dune_rules/dep_rules.mli

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,12 @@ 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
40+
-> impl:Virtual_rules.t
41+
-> dir:Path.Build.t
3842
-> ml_kind:Ml_kind.t
39-
-> for_:Compilation_mode.t
4043
-> Module.t
4144
-> Module.t list Action_builder.t

src/dune_rules/melange/melange_rules.ml

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,15 @@ 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+
~ml_kind:Intf
257+
module_
250258
in
251259
(* Cross-module optimization follows implementation artifacts, but the
252260
initial reachability also comes from the emitted module's interface
@@ -256,10 +264,10 @@ let make_same_lib_emission_deps =
256264
|> Action_builder.map ~f:(deps_of_xopt_closure ~obj_dir)
257265
| false ->
258266
(* 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. *)
267+
Compilation dependencies collapse transitive edges through interfaces
268+
when a dependency has an [.mli], which is insufficient for JS
269+
emission. Follow the implementation dependency graph directly
270+
instead. *)
263271
Dep_graph.top_closed_implementations dep_graph [ module_ ]
264272
|> Action_builder.map ~f:(deps_of_impl_closure ~obj_dir)
265273
;;

0 commit comments

Comments
 (0)