-
Notifications
You must be signed in to change notification settings - Fork 392
/
lib.ml
2060 lines (1808 loc) · 64.2 KB
/
lib.ml
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
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
open Import
open Resolve.Memo.O
(* Errors *)
module Dep_path : sig
module Entry : sig
module Lib : sig
type t =
{ path : Path.t
; name : Lib_name.t
}
val pp : t -> _ Pp.t
end
module Implements_via : sig
type t =
| Variant of Variant.t
| Default_for of Lib.t
end
type t =
{ lib : Lib.t
; implements_via : Implements_via.t option
}
end
type t = Entry.t list
val pp : t -> _ Pp.t
end = struct
module Entry = struct
module Lib = struct
type t =
{ path : Path.t
; name : Lib_name.t
}
let pp { path; name } =
Pp.textf "library %S in %s" (Lib_name.to_string name)
(Path.to_string_maybe_quoted path)
end
module Implements_via = struct
type t =
| Variant of Variant.t
| Default_for of Lib.t
let pp = function
| Variant v -> Pp.textf "via variant %S" (Variant.to_string v)
| Default_for l ->
Pp.seq (Pp.text "via default implementation for ") (Lib.pp l)
end
type t =
{ lib : Lib.t
; implements_via : Implements_via.t option
}
let pp { lib; implements_via } =
match implements_via with
| None -> Lib.pp lib
| Some via ->
Pp.concat ~sep:Pp.space [ Lib.pp lib; Implements_via.pp via ]
end
type t = Entry.t list
let pp t =
Pp.vbox
(Pp.concat ~sep:Pp.cut
(List.map t ~f:(fun x ->
Pp.box ~indent:3
(Pp.seq (Pp.verbatim "-> ")
(Pp.seq (Pp.text "required by ") (Entry.pp x))))))
end
(* The current module never raises. It returns all errors as [Result.Error
(User_error.E _)] values instead. Errors are later inserted into
[Action_builder.t] values so that they are only raised during the actual
build rather than while generating the rules. *)
module Error = struct
(* This sub-module construct the error values generated by functions in this
module.
When a location is not available, for instance because the error is
attached to transitive dependency of a library written by the user in a
[dune] file, a dependency path should be used to explain how dune came to
consider the library that triggered the error. *)
let make ?loc ?hints paragraphs =
Resolve.Memo.fail
(User_error.make ?loc ?hints paragraphs
~annots:
(User_message.Annots.singleton User_message.Annots.needs_stack_trace
()))
let pp_lib info =
let name = Lib_info.name info in
let src_dir = Lib_info.src_dir info in
Pp.textf "%S in %s" (Lib_name.to_string name)
(Path.to_string_maybe_quoted src_dir)
let pp_lib_and_dep_path (info, dp) =
let info = Pp.box (pp_lib info) in
match dp with
| [] -> info
| _ -> Pp.vbox (Pp.concat ~sep:Pp.cut [ info; Dep_path.pp dp ])
let not_found ~loc ~name =
make ~loc [ Pp.textf "Library %S not found." (Lib_name.to_string name) ]
let hidden ~loc ~name ~dir ~reason =
make ~loc
[ Pp.textf "Library %S in %s is hidden (%s)." (Lib_name.to_string name)
(Path.to_string_maybe_quoted dir)
reason
]
(* diml: it is not very clear what a "default implementation cycle" is *)
let default_implementation_cycle cycle =
make
[ Pp.text
"Default implementation cycle detected between the following \
libraries:"
; Pp.chain cycle ~f:(fun info ->
let name = Lib_info.name info in
Pp.textf "%S" (Lib_name.to_string name))
]
let double_implementation impl1 impl2 ~vlib =
make
[ Pp.concat
[ Pp.text "Conflicting implementations for virtual library "
; pp_lib vlib
; Pp.char ':'
]
; Pp.enumerate [ impl1; impl2 ] ~f:pp_lib_and_dep_path
; Pp.text "This cannot work."
]
let no_implementation (info, dp) =
make
(Pp.concat
[ Pp.text "No implementation found for virtual library "
; pp_lib info
; Pp.char '.'
]
::
(match dp with
| [] -> []
| _ -> [ Dep_path.pp dp ]))
let overlap ~in_workspace ~installed =
make
[ Pp.text "Conflict between the following libraries:"
; Pp.enumerate [ (in_workspace, []); installed ] ~f:pp_lib_and_dep_path
]
let no_solution_found_for_select ~loc =
Resolve.fail
(User_error.make ~loc
[ Pp.text "No solution found for this select form." ])
let not_an_implementation_of ~vlib ~impl =
make
[ Pp.textf "%S is not an implementation of %S."
(Lib_name.to_string (Lib_info.name impl))
(Lib_name.to_string (Lib_info.name vlib))
]
let dependency_cycle cycle =
make
[ Pp.text "Dependency cycle detected between the following libraries:"
; Pp.chain cycle ~f:(fun (dir, name) ->
Pp.textf "%S in %s" (Lib_name.to_string name)
(Path.to_string_maybe_quoted dir))
]
let private_deps_not_allowed ~kind ~loc private_dep =
let name = Lib_info.name private_dep in
User_error.E
(User_error.make ~loc
[ Pp.textf
"Library %S is private, it cannot be a dependency of a %s. You \
need to give %S a public name."
(Lib_name.to_string name)
(match kind with
| `Private_package -> "private library attached to a package"
| `Public -> "public library")
(Lib_name.to_string name)
])
let only_ppx_deps_allowed ~loc dep =
let name = Lib_info.name dep in
make ~loc
[ Pp.textf
"Ppx dependency on a non-ppx library %S. If %S is in fact a ppx \
rewriter library, it should have (kind ppx_rewriter) in its dune \
file."
(Lib_name.to_string name) (Lib_name.to_string name)
]
let not_virtual_lib ~loc ~impl ~not_vlib =
let impl = Lib_info.name impl in
let not_vlib = Lib_info.name not_vlib in
make ~loc
[ Pp.textf "Library %S is not virtual. It cannot be implemented by %S."
(Lib_name.to_string not_vlib)
(Lib_name.to_string impl)
]
end
(* Types *)
module Resolved_select = struct
type t =
{ src_fn : string Resolve.t
; dst_fn : string
}
end
type sub_system = ..
module Sub_system0 = struct
module type S = sig
module Info : Sub_system_info.S
type t
type sub_system += T of t
val public_info : (t -> Info.t Resolve.Memo.t) option
end
type 'a s = (module S with type t = 'a)
module Instance = struct
type t = T : 'a s * 'a -> t
end
end
module Id : sig
type t =
{ path : Path.t
; name : Lib_name.t
}
val to_dep_path_lib : t -> Dep_path.Entry.Lib.t
val compare : t -> t -> Ordering.t
include Comparator.OPS with type t := t
val make : path:Path.t -> name:Lib_name.t -> t
include Comparable_intf.S with type key := t
module Top_closure :
Top_closure_intf.S with type key := t and type 'a monad := 'a Resolve.Memo.t
end = struct
module T = struct
type t =
{ path : Path.t
; name : Lib_name.t
}
let compare { path; name } t =
let open Ordering.O in
let= () = Lib_name.compare name t.name in
Path.compare path t.path
let to_dyn { path; name } =
let open Dyn in
record [ ("path", Path.to_dyn path); ("name", Lib_name.to_dyn name) ]
end
include T
let to_dep_path_lib { path; name } = { Dep_path.Entry.Lib.path; name }
include (Comparator.Operators (T) : Comparator.OPS with type t := T.t)
let make ~path ~name = { path; name }
include Comparable.Make (T)
module Top_closure = Top_closure.Make (Set) (Resolve.Memo)
end
module T = struct
type t =
{ info : Lib_info.external_
; name : Lib_name.t
; unique_id : Id.t
; re_exports : t list Resolve.t
; (* [requires] is contains all required libraries, including the ones
mentioned in [re_exports]. *)
requires : t list Resolve.t
; ppx_runtime_deps_host : t list Resolve.t Memo.Lazy.t
; pps : t list Resolve.t
; resolved_selects : Resolved_select.t list Resolve.t
; implements : t Resolve.t option
; lib_config : Lib_config.t
; project : Dune_project.t option
; (* these fields cannot be forced until the library is instantiated *)
default_implementation : t Resolve.t Memo.Lazy.t option
; sub_systems : Sub_system0.Instance.t Memo.Lazy.t Sub_system_name.Map.t
}
let compare (x : t) (y : t) = Id.compare x.unique_id y.unique_id
let to_dyn t =
Dyn.record
[ ("name", Lib_name.to_dyn t.name)
; ("loc", Loc.to_dyn_hum (Lib_info.loc t.info))
]
end
include T
type lib = t
include (Comparator.Operators (T) : Comparator.OPS with type t := t)
module Hidden = struct
type 'lib t =
{ lib : 'lib
; path : Path.t
; reason : string
}
let of_lib lib ~reason =
let path = Lib_info.src_dir lib.info in
{ lib; path; reason }
let to_dyn to_dyn { lib; path; reason } =
let open Dyn in
record
[ ("lib", to_dyn lib)
; ("path", Path.to_dyn path)
; ("reason", string reason)
]
let error { path; reason; lib = _ } ~name ~loc =
Error.hidden ~loc ~name ~dir:path ~reason
let unsatisfied_exist_if pkg =
let info = Dune_package.Lib.info pkg in
let path = Lib_info.src_dir info in
{ lib = info; reason = "unsatisfied 'exist_if'"; path }
end
module Private_deps = struct
type t =
| From_same_project of [ `Public | `Private_package ]
| Allow_all
let equal a b =
match (a, b) with
| Allow_all, Allow_all
| From_same_project `Public, From_same_project `Public
| From_same_project `Private_package, From_same_project `Private_package ->
true
| _ -> false
let check t ~loc ~lib =
match t with
| Allow_all -> Ok lib
| From_same_project kind -> (
match Lib_info.status lib.info with
| Private (_, Some _) -> Ok lib
| Private (_, None) ->
Error (Error.private_deps_not_allowed ~kind ~loc lib.info)
| _ -> Ok lib)
end
module Status = struct
type t =
| Found of lib
| Not_found
| Hidden of lib Hidden.t
| Invalid of exn
let to_dyn t =
let open Dyn in
match t with
| Invalid e -> variant "Invalid" [ Exn.to_dyn e ]
| Not_found -> variant "Not_found" []
| Hidden { lib = _; path; reason } ->
variant "Hidden" [ Path.to_dyn path; string reason ]
| Found t -> variant "Found" [ to_dyn t ]
end
type db =
{ parent : db option
; host : db Memo.Lazy.t option
; resolve : Lib_name.t -> resolve_result Memo.t
; resolve_ppx_runtime_deps :
(Path.t Lib_info.t * Private_deps.t, t list Resolve.t) Memo.Table.t
; all : Lib_name.t list Memo.Lazy.t
; lib_config : Lib_config.t
}
and resolve_result =
| Not_found
| Found of Lib_info.external_
| Hidden of Lib_info.external_ Hidden.t
| Invalid of exn
| (* Redirect (None, lib) looks up lib in the same database *)
Redirect of
db option * (Loc.t * Lib_name.t)
let lib_config (t : lib) = t.lib_config
let name t = t.name
let info t = t.info
let project t = t.project
let implements t = Option.map ~f:Memo.return t.implements
let requires t = Memo.return t.requires
let ppx_runtime_deps t = Memo.Lazy.force t.ppx_runtime_deps_host
let pps t = Memo.return t.pps
let is_local t =
let obj_dir = Lib_info.obj_dir t.info in
Path.is_managed (Obj_dir.byte_dir obj_dir)
let main_module_name t =
let main_module_name = Lib_info.main_module_name t.info in
match main_module_name with
| This mmn -> Resolve.Memo.return mmn
| From _ -> (
let+ vlib = Memo.return (Option.value_exn t.implements) in
let main_module_name = Lib_info.main_module_name vlib.info in
match main_module_name with
| This x -> x
| From _ -> assert false)
let wrapped t =
let wrapped = Lib_info.wrapped t.info in
match wrapped with
| None -> Resolve.Memo.return None
| Some (This wrapped) -> Resolve.Memo.return (Some wrapped)
| Some (From _) -> (
let+ vlib = Memo.return (Option.value_exn t.implements) in
let wrapped = Lib_info.wrapped vlib.info in
match wrapped with
| Some (From _) (* can't inherit this value in virtual libs *) | None ->
assert false (* will always be specified in dune package *)
| Some (This x) -> Some x)
(* We can't write a structural equality because of all the lazy fields *)
let equal = ( == )
let hash = Poly.hash
include Comparable.Make (T)
module L = struct
let top_closure l ~key ~deps =
Id.Top_closure.top_closure l ~key:(fun t -> (key t).unique_id) ~deps
end
(* Sub-systems *)
module Sub_system = struct
type t = sub_system = ..
module type S = sig
module Info : Sub_system_info.S
type t
type sub_system += T of t
val instantiate :
resolve:(Loc.t * Lib_name.t -> lib Resolve.Memo.t)
-> get:(loc:Loc.t -> lib -> t option Memo.t)
-> lib
-> Info.t
-> t Memo.t
val public_info : (t -> Info.t Resolve.Memo.t) option
end
module type S' = sig
include S
val for_instance : t Sub_system0.s
val get : lib -> t option Memo.t
end
(* This mutable table is safe under the assumption that subsystems are
registered at the top level, which is currently true. *)
let all = Table.create (module Sub_system_name) 16
module Register (M : S) = struct
let get lib =
let open Memo.O in
match Sub_system_name.Map.find lib.sub_systems M.Info.name with
| None -> Memo.return None
| Some sub -> (
let+ (Sub_system0.Instance.T ((module X), t)) = Memo.Lazy.force sub in
match X.T t with
| M.T t -> Some t
| _ -> assert false)
let () =
let module M = struct
include M
let for_instance = (module M : Sub_system0.S with type t = t)
let get = get
end in
Table.set all M.Info.name (module M : S')
end
let instantiate name info lib ~resolve =
let open Memo.O in
let impl = Table.find_exn all name in
let (module M : S') = impl in
match info with
| M.Info.T info ->
let get ~loc lib' =
if lib = lib' then
User_error.raise ~loc
[ Pp.textf "Library %S depends on itself"
(Lib_name.to_string lib.name)
]
else M.get lib'
in
let+ inst = M.instantiate ~resolve ~get lib info in
Sub_system0.Instance.T (M.for_instance, inst)
| _ -> assert false
let public_info =
let open Memo.O in
(* TODO this should continue using [Resolve]. Not doing so
will prevent generating the [dune-package] rule if the sub system is
missing *)
let module M = Memo.Make_map_traversals (Sub_system_name.Map) in
fun lib ->
M.parallel_map lib.sub_systems ~f:(fun _name inst ->
let* (Sub_system0.Instance.T ((module M), t)) =
Memo.Lazy.force inst
in
match M.public_info with
| None -> Memo.return None
| Some f ->
let+ info = Resolve.Memo.read_memo (f t) in
Some (M.Info.T info))
>>| Sub_system_name.Map.filter_opt
end
(* Library name resolution and transitive closure *)
(* Dependency stack used while resolving the dependencies of a library that was
just returned by the [resolve] callback *)
module Dep_stack : sig
type t
val to_required_by : t -> Dep_path.Entry.t list
val empty : t
module Implements_via : sig
type t = Default_for of Id.t
end
val push :
t -> implements_via:Implements_via.t option -> Id.t -> t Resolve.Memo.t
end = struct
module Implements_via = struct
type t = Default_for of Id.t
let to_dep_path_implements_via = function
| Default_for id ->
Dep_path.Entry.Implements_via.Default_for (Id.to_dep_path_lib id)
end
type t =
{ stack : Id.t list
; implements_via : Implements_via.t Id.Map.t
; seen : Id.Set.t
}
let empty = { stack = []; seen = Id.Set.empty; implements_via = Id.Map.empty }
let to_required_by t =
List.map t.stack ~f:(fun ({ Id.path; name; _ } as id) ->
let implements_via =
let open Option.O in
let+ via = Id.Map.find t.implements_via id in
Implements_via.to_dep_path_implements_via via
in
{ Dep_path.Entry.lib = { path; name }; implements_via })
let dependency_cycle t (last : Id.t) =
assert (Id.Set.mem t.seen last);
let rec build_loop acc stack =
match stack with
| [] -> assert false
| (x : Id.t) :: stack ->
let acc = (x.path, x.name) :: acc in
if Id.equal x last then acc else build_loop acc stack
in
let loop = build_loop [ (last.path, last.name) ] t.stack in
Error.dependency_cycle loop
let push (t : t) ~implements_via (x : Id.t) =
if Id.Set.mem t.seen x then dependency_cycle t x
else
let implements_via =
match implements_via with
| None -> t.implements_via
| Some via -> Id.Map.add_exn t.implements_via x via
in
Resolve.Memo.return
{ stack = x :: t.stack; seen = Id.Set.add t.seen x; implements_via }
end
module Vlib : sig
(** Make sure that for every virtual library in the list there is at most one
corresponding implementation.
Additionally, if linking is [true], ensures that every virtual library as
an implementation and re-arrange the list so that implementations replaces
virtual libraries. *)
val associate :
(t * Dep_stack.t) list -> linking:bool -> t list Resolve.Memo.t
module Unimplemented : sig
(** set of unimplemented libraries*)
type t
val empty : t
val add : t -> lib -> t Resolve.Memo.t
val with_default_implementations : t -> lib list
end
end = struct
module Unimplemented = struct
type t =
{ implemented : Set.t
; unimplemented : Set.t
}
let empty = { implemented = Set.empty; unimplemented = Set.empty }
let add t lib =
let virtual_ = Lib_info.virtual_ lib.info in
match (lib.implements, virtual_) with
| None, None -> Resolve.Memo.return t
| Some _, Some _ -> assert false (* can't be virtual and implement *)
| None, Some _ ->
Resolve.Memo.return
(if Set.mem t.implemented lib then t
else { t with unimplemented = Set.add t.unimplemented lib })
| Some vlib, None ->
let+ vlib = Memo.return vlib in
{ implemented = Set.add t.implemented vlib
; unimplemented = Set.remove t.unimplemented vlib
}
let with_default_implementations t =
Set.fold t.unimplemented ~init:[] ~f:(fun lib acc ->
match lib.default_implementation with
| None -> acc
| Some _ -> lib :: acc)
end
module Table = struct
module Partial = struct
type vlib_status =
| No_impl of Dep_stack.t
| Impl of lib * Dep_stack.t
type t = vlib_status Map.t
let is_empty = Map.is_empty
let make closure : t Resolve.Memo.t =
let rec loop acc = function
| [] -> Resolve.Memo.return acc
| (lib, stack) :: libs -> (
let virtual_ = Lib_info.virtual_ lib.info in
match (lib.implements, virtual_) with
| None, None -> loop acc libs
| Some _, Some _ ->
assert false (* can't be virtual and implement *)
| None, Some _ -> loop (Map.set acc lib (No_impl stack)) libs
| Some vlib, None -> (
let* vlib = Memo.return vlib in
match Map.find acc vlib with
| None ->
(* we've already traversed the virtual library because it must
have occurred earlier in the closure *)
assert false
| Some (No_impl _) ->
loop (Map.set acc vlib (Impl (lib, stack))) libs
| Some (Impl (lib', stack')) ->
let req_by' = Dep_stack.to_required_by stack' in
let req_by = Dep_stack.to_required_by stack in
Error.double_implementation (lib'.info, req_by')
(lib.info, req_by) ~vlib:vlib.info))
in
loop Map.empty closure
end
type t = lib Map.t
let make impls : t Resolve.Memo.t =
let rec loop acc = function
| [] -> Resolve.Memo.return acc
| (vlib, Partial.No_impl stack) :: _ ->
let rb = Dep_stack.to_required_by stack in
Error.no_implementation (vlib.info, rb)
| (vlib, Impl (impl, _stack)) :: libs ->
loop (Map.set acc vlib impl) libs
in
loop Map.empty (Map.to_list impls)
end
let second_step_closure =
let module R = struct
module M =
State.Make
(struct
type t = lib list * Id.Set.t
end)
(Resolve.Memo)
module List = Monad.List (M)
include M
end in
let open R.O in
fun ts impls ->
let rec loop t =
let t = Option.value ~default:t (Map.find impls t) in
let* res, visited = R.get in
if Id.Set.mem visited t.unique_id then R.return ()
else
let* () = R.set (res, Id.Set.add visited t.unique_id) in
let* deps = R.lift (Memo.return t.requires) in
let* () = many deps in
R.modify (fun (res, visited) -> (t :: res, visited))
and many deps = R.List.iter deps ~f:loop in
let open Resolve.Memo.O in
let+ (res, _visited), () = R.run (many ts) ([], Id.Set.empty) in
List.rev res
let associate closure ~linking =
let* impls = Table.Partial.make closure in
let closure = List.map closure ~f:fst in
if linking && not (Table.Partial.is_empty impls) then
let* impls = Table.make impls in
second_step_closure closure impls
else Resolve.Memo.return closure
end
let instrumentation_backend instrument_with resolve libname =
if not (List.mem ~equal:Lib_name.equal instrument_with (snd libname)) then
Resolve.Memo.return None
else
let* lib = resolve libname in
match lib |> info |> Lib_info.instrumentation_backend with
| Some _ as ppx -> Resolve.Memo.return ppx
| None ->
Resolve.Memo.fail
(User_error.make ~loc:(fst libname)
[ Pp.textf
"Library %S is not declared to have an instrumentation backend."
(Lib_name.to_string (snd libname))
])
module rec Resolve_names : sig
val find_internal : db -> Lib_name.t -> Status.t Memo.t
val resolve_dep :
db
-> Loc.t * Lib_name.t
-> private_deps:Private_deps.t
-> lib Resolve.Memo.t
val resolve_name : db -> Lib_name.t -> Status.t Memo.t
val available_internal : db -> Lib_name.t -> bool Memo.t
val resolve_simple_deps :
db
-> (Loc.t * Lib_name.t) list
-> private_deps:Private_deps.t
-> t list Resolve.Memo.t
type resolved =
{ requires : lib list Resolve.t
; pps : lib list Resolve.t
; selects : Resolved_select.t list
; re_exports : lib list Resolve.t
}
val resolve_deps_and_add_runtime_deps :
db
-> Lib_dep.t list
-> private_deps:Private_deps.t
-> pps:(Loc.t * Lib_name.t) list
-> dune_version:Dune_lang.Syntax.Version.t option
-> resolved Memo.t
val compile_closure_with_overlap_checks :
db option
-> lib list
-> forbidden_libraries:Loc.t Map.t
-> lib list Resolve.Memo.t
val linking_closure_with_overlap_checks :
db option
-> lib list
-> forbidden_libraries:Loc.t Map.t
-> lib list Resolve.Memo.t
end = struct
open Resolve_names
let projects_by_package =
Memo.lazy_ (fun () ->
let open Memo.O in
let+ { projects; _ } = Dune_load.load () in
List.concat_map projects ~f:(fun project ->
Dune_project.packages project
|> Package.Name.Map.values
|> List.map ~f:(fun (pkg : Package.t) ->
let name = Package.name pkg in
(name, project)))
|> Package.Name.Map.of_list_exn)
let instantiate_impl (db, name, info, hidden) =
let open Memo.O in
let unique_id = Id.make ~name ~path:(Lib_info.src_dir info) in
let private_deps =
match Lib_info.status info with
(* [Allow_all] is used for libraries that are installed because we don't
have to check it again. It has been checked when compiling the
libraries before their installation *)
| Installed_private | Private (_, None) | Installed ->
Private_deps.Allow_all
| Private (_, Some _) -> From_same_project `Private_package
| Public (_, _) -> From_same_project `Public
in
let resolve name = resolve_dep db name ~private_deps in
let* resolved =
let open Resolve.Memo.O in
let* pps =
let instrumentation_backend =
instrumentation_backend db.lib_config.instrument_with resolve
in
Lib_info.preprocess info
|> Preprocess.Per_module.with_instrumentation ~instrumentation_backend
>>| Preprocess.Per_module.pps
in
let dune_version = Lib_info.dune_version info in
Lib_info.requires info
|> resolve_deps_and_add_runtime_deps db ~private_deps ~dune_version ~pps
|> Memo.map ~f:Resolve.return
in
let* implements =
match Lib_info.implements info with
| None -> Memo.return None
| Some ((loc, _) as name) ->
let res =
let open Resolve.Memo.O in
let* vlib = resolve name in
let virtual_ = Lib_info.virtual_ vlib.info in
match virtual_ with
| None -> Error.not_virtual_lib ~loc ~impl:info ~not_vlib:vlib.info
| Some _ -> Resolve.Memo.return vlib
in
Memo.map res ~f:Option.some
in
let* requires =
let requires =
let open Resolve.O in
let* resolved = resolved in
resolved.requires
in
match implements with
| None -> Memo.return requires
| Some vlib ->
let open Resolve.Memo.O in
let* (_ : lib list) =
let* vlib = Memo.return vlib in
let* requires_for_closure_check =
Memo.return
(let open Resolve.O in
let+ requires = requires in
List.filter requires ~f:(fun lib -> not (equal lib vlib)))
in
linking_closure_with_overlap_checks None requires_for_closure_check
~forbidden_libraries:(Map.singleton vlib Loc.none)
in
Memo.return requires
in
let resolve_impl impl_name =
let open Resolve.Memo.O in
let* impl = resolve impl_name in
let* vlib =
match impl.implements with
| Some vlib -> Memo.return vlib
| None -> Error.not_an_implementation_of ~vlib:info ~impl:impl.info
in
if Id.equal vlib.unique_id unique_id then Resolve.Memo.return impl
else Error.not_an_implementation_of ~vlib:info ~impl:impl.info
in
let default_implementation =
Lib_info.default_implementation info
|> Option.map ~f:(fun l ->
Memo.lazy_ (fun () ->
let open Resolve.Memo.O in
let* impl = resolve_impl l in
match Lib_info.package impl.info with
| None -> Resolve.Memo.return impl
| Some p -> (
let loc = fst l in
match Lib_info.package info with
| None ->
(* We don't need to verify that impl is private if this
virtual library is private. Every implementation already
depends on the virtual library, so the check will be
done there. *)
Resolve.Memo.return impl
| Some p' ->
(* It's not good to rely on package names for equality like
this, but we piggy back on the fact that package names
are globally unique *)
if Package.Name.equal p p' then Resolve.Memo.return impl
else
Error.make ~loc
[ Pp.textf
"default implementation belongs to package %s \
while virtual library belongs to package %s. \
This is impossible."
(Package.Name.to_string p)
(Package.Name.to_string p')
])))
in
let* requires =
Memo.return
(let open Resolve.O in
let* requires = requires in
match implements with
| None -> Resolve.return requires
| Some impl ->
let+ impl = impl in
impl :: requires)
in
let map_error x =
let src_dir = Lib_info.src_dir info in
Resolve.push_stack_frame x ~human_readable_description:(fun () ->
Dep_path.Entry.Lib.pp { name; path = src_dir })
in
let ppx_runtime_deps_host =
Memo.lazy_ (fun () ->
Memo.exec db.resolve_ppx_runtime_deps (info, private_deps)
|> Memo.map ~f:map_error)
in
let requires = map_error requires in
let* project =
let status = Lib_info.status info in
match Lib_info.Status.project status with
| Some _ as project -> Memo.return project
| None ->
let+ projects_by_package = Memo.Lazy.force projects_by_package in
let open Option.O in
let* package = Lib_info.package info in
Package.Name.Map.find projects_by_package package
in
let rec t =
lazy
(let open Resolve.O in
let resolved_selects = resolved >>| fun r -> r.selects in
let pps = resolved >>= fun r -> r.pps in
let re_exports = resolved >>= fun r -> r.re_exports in
{ info
; name
; unique_id
; requires
; ppx_runtime_deps_host
; pps
; resolved_selects
; re_exports