-
Notifications
You must be signed in to change notification settings - Fork 487
Expand file tree
/
Copy pathdescribe_external_lib_deps.ml
More file actions
251 lines (229 loc) · 7.18 KB
/
Copy pathdescribe_external_lib_deps.ml
File metadata and controls
251 lines (229 loc) · 7.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
open Import
module Lib_dep = Dune_lang.Lib_dep
module Kind = struct
type t =
| Required
| Optional
let to_dyn : t -> Dyn.t = function
| Required -> String "required"
| Optional -> String "optional"
;;
end
type lib_dep =
{ name : Lib_name.t
; kind : Kind.t
}
let lib_dep_to_dyn t =
let open Dyn in
List [ String (Lib_name.to_string t.name); Kind.to_dyn t.kind ]
;;
module Item = struct
module Kind = struct
type t =
| Executables
| Library
| Tests
let to_string = function
| Executables -> "executables"
| Library -> "library"
| Tests -> "tests"
;;
end
type t =
{ kind : Kind.t
; dir : Path.Source.t
; external_deps : lib_dep list
; internal_deps : lib_dep list
; names : string list
; package : Package.t option
; extensions : Filename.Extension.t list
}
let to_dyn { kind; dir; external_deps; internal_deps; names; package; extensions } =
let open Dyn in
let record =
record
[ "names", (list string) names
; "extensions", (list Filename.Extension.to_dyn) extensions
; "package", option Package.Name.to_dyn (Option.map ~f:Package.name package)
; "source_dir", String (Path.Source.to_string dir)
; "external_deps", list lib_dep_to_dyn external_deps
; "internal_deps", list lib_dep_to_dyn internal_deps
]
in
Variant (Kind.to_string kind, [ record ])
;;
end
type dep =
| Local of lib_dep
| External of lib_dep
let is_external db name =
let open Memo.O in
let+ lib = Dune_rules.Lib.DB.find_even_when_hidden db name in
match lib with
| None -> true
| Some t ->
(match Dune_rules.Lib_info.status (Dune_rules.Lib.info t) with
| Installed_private | Public _ | Private _ -> false
| Installed -> true)
;;
let resolve_lib db name kind =
let open Memo.O in
let+ is_external = is_external db name in
if is_external then External { name; kind } else Local { name; kind }
;;
let resolve_lib_pps db preprocess =
let open Memo.O in
Dune_rules.Instrumentation.with_instrumentation
preprocess
~instrumentation_backend:(Dune_rules.Lib.DB.instrumentation_backend db)
|> Resolve.Memo.read_memo
>>| Dune_lang.Preprocess.Per_module.pps
>>= Memo.parallel_map ~f:(fun (_, name) -> resolve_lib db name Kind.Required)
;;
let resolve_lib_deps db lib_deps =
let open Memo.O in
Memo.parallel_map lib_deps ~f:(fun (lib : Lib_dep.t) ->
match lib with
| Direct (_, name) | Re_export (_, name) | Instantiate { lib = name; _ } ->
let+ v = resolve_lib db name Kind.Required in
[ v ]
| Select select ->
select.choices
|> Memo.parallel_map ~f:(fun (choice : Lib_dep.Select.Choice.t) ->
Lib_name.Set.to_string_list choice.required
@ Lib_name.Set.to_string_list choice.forbidden
|> Memo.parallel_map ~f:(fun name ->
let name = Lib_name.of_string name in
resolve_lib db name Kind.Optional))
>>| List.concat)
>>| List.concat
;;
let resolve_libs db dir libraries preprocess names package kind extensions =
let open Memo.O in
let open Item in
let* lib_deps = resolve_lib_deps db libraries in
let+ lib_pps = resolve_lib_pps db preprocess in
let deps = lib_deps @ lib_pps in
let internal_deps, external_deps =
deps
|> List.partition_map ~f:(function
| Local lib -> Either.Left lib
| External lib -> Either.Right lib)
in
{ external_deps; internal_deps; kind; names; package; dir; extensions }
;;
let exes_extensions (lib_config : Dune_rules.Lib_config.t) modes =
Dune_rules.Executables.Link_mode.Map.to_list modes
|> List.map ~f:(fun (m, loc) ->
Dune_rules.Executables.Link_mode.extension
m
~loc
~ext_obj:lib_config.ext_obj
~ext_dll:lib_config.ext_dll)
;;
let libs db (context : Context.t) =
let open Memo.O in
let* dune_files = Context.name context |> Dune_rules.Dune_load.dune_files in
Memo.parallel_map dune_files ~f:(fun (dune_file : Dune_rules.Dune_file.t) ->
Dune_file.stanzas dune_file
>>= Memo.parallel_map ~f:(fun stanza ->
let dir = Dune_file.dir dune_file in
match Stanza.repr stanza with
| Dune_rules.Executables.T exes ->
let* ocaml = Context.ocaml context in
resolve_libs
db
dir
exes.buildable.libraries
exes.buildable.preprocess.config
(Nonempty_list.to_list_map exes.names ~f:snd)
exes.package
Item.Kind.Executables
(exes_extensions ocaml.lib_config exes.modes)
>>| List.singleton
| Dune_rules.Library.T lib ->
resolve_libs
db
dir
lib.buildable.libraries
lib.buildable.preprocess.config
[ Dune_rules.Library.best_name lib |> Lib_name.to_string ]
(Dune_rules.Library.package lib)
Item.Kind.Library
[]
>>| List.singleton
| Dune_rules.Tests.T tests ->
let* ocaml = Context.ocaml context in
resolve_libs
db
dir
tests.exes.buildable.libraries
tests.exes.buildable.preprocess.config
(Nonempty_list.to_list_map tests.exes.names ~f:snd)
(if Option.is_none tests.package then tests.exes.package else tests.package)
Item.Kind.Tests
(exes_extensions ocaml.lib_config tests.exes.modes)
>>| List.singleton
| _ -> Memo.return [])
>>| List.concat)
>>| List.concat
;;
let filter_empty ~include_empty items =
if include_empty
then items
else
List.filter
~f:(fun (x : Item.t) ->
not (List.is_empty x.external_deps && List.is_empty x.internal_deps))
items
;;
let external_resolved_libs ~include_empty (context : Context.t) =
let open Memo.O in
let* scope = Dune_rules.Scope.DB.find_by_dir (Context.build_dir context) in
let db = Dune_rules.Scope.libs scope in
libs db context >>| filter_empty ~include_empty
;;
let to_dyn context_name external_resolved_libs =
let open Dyn in
Tuple [ String context_name; list Item.to_dyn external_resolved_libs ]
;;
let arg_include_empty =
let open Arg in
value
& flag
& info
[ "include-empty" ]
~doc:
(Some "Whether to include items that have no internal or external dependencies")
;;
let term =
let+ builder = Common.Builder.term
and+ context_name = Common.context_arg ~doc:(Some "Build context to use.")
and+ _ = Describe_lang_compat.arg
and+ format = Describe_format.arg
and+ include_empty = arg_include_empty in
let common, config = Common.init builder in
Scheduler_setup.go_with_rpc_server ~common ~config
@@ fun () ->
Build.build_memo_exn
@@ fun () ->
let open Memo.O in
let* setup = Util.setup () in
let super_context = Dune_rules.Main.find_scontext_exn setup ~name:context_name in
let context_name =
Super_context.context super_context
|> Context.name
|> Dune_engine.Context_name.to_string
in
external_resolved_libs ~include_empty (Super_context.context super_context)
>>| to_dyn context_name
>>| Describe_format.print_dyn format
;;
let command =
let doc =
"Print out external libraries needed to build the project. It's an approximated set \
of libraries."
in
let info = Cmd.info ~doc "external-lib-deps" in
Cmd.v info term
;;