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
open Action.Syntax
open Astring
type package = Package.t
type info = Info.t
type 'a value = 'a Key.value
type 'a code = { pos : (string * int * int * int) option; code : string }
let code_opt ?pos fmt = Fmt.kstr (fun code -> { pos; code }) fmt
let code ~pos fmt = Fmt.kstr (fun code -> { pos = Some pos; code }) fmt
type ('a, 'impl) t = {
id : 'a Typeid.t;
module_name : string;
module_type : 'a Type.t;
keys : Key.t list;
runtime_args : Runtime_arg.t list;
packages : package list value;
local_libs : string list;
install : info -> Install.t value;
connect : info -> string -> string list -> 'a code;
dune : info -> Dune.stanza list;
configure : info -> unit Action.t;
files : (info -> Fpath.t list) option;
extra_deps : 'impl list;
}
let pp : type a b. b Fmt.t -> (a, b) t Fmt.t =
fun pp_impl ppf t ->
let open Fmt.Dump in
let fields =
[
field "id" (fun t -> t.id) Typeid.pp;
field "module_name" (fun t -> t.module_name) string;
field "module_type" (fun t -> t.module_type) Type.pp;
field "keys" (fun t -> t.keys) (list Key.pp);
field "install" (fun _ -> "<dyn>") Fmt.string;
field "packages" (fun _ -> "<dyn>") Fmt.string;
field "extra_deps" (fun t -> t.extra_deps) (list pp_impl);
]
in
record fields ppf t
let equal x y = Typeid.equal x.id y.id
let witness x y = Typeid.witness x.id y.id
let hash x = Typeid.id x.id
let default_connect _ _ l = code_opt "return (%s)" (String.concat ~sep:", " l)
let niet _ = Action.ok ()
let nil _ = []
let merge empty union a b =
match (a, b) with
| None, None -> Key.pure empty
| Some a, None -> Key.pure a
| None, Some b -> b
| Some a, Some b -> Key.(pure union $ pure a $ b)
let merge_packages = merge [] List.append
let merge_install = merge Install.empty Install.union
let v ?packages ?packages_v ?(local_libs = []) ?install ?install_v ?(keys = [])
?(runtime_args = []) ?( = []) ?(connect = default_connect)
?(dune = nil) ?(configure = niet) ?files module_name module_type =
let id = Typeid.gen () in
let packages = merge_packages packages packages_v in
let install i =
let aux = function None -> None | Some f -> Some (f i) in
merge_install (aux install) (aux install_v)
in
{
module_type;
id;
module_name;
keys;
runtime_args;
connect;
packages;
local_libs;
install;
dune;
configure;
files;
extra_deps;
}
let id t = Typeid.id t.id
let module_name t = t.module_name
let module_type t = t.module_type
let packages t = t.packages
let local_libs t = t.local_libs
let install t = t.install
let connect t = t.connect
let configure t = t.configure
let files t i =
let gen = Action.generated_files (t.configure i) in
match t.files with
| None -> gen
| Some files -> Fpath.Set.(union gen (of_list (files i)))
let dune t = t.dune
let keys t = t.keys
let runtime_args t = t.runtime_args
let t = t.extra_deps
let start ?pos impl_name args =
code_opt ?pos "@[(%s.start@ %a@ : unit io)@]" impl_name
Fmt.(list ~sep:sp string)
args
let uniq t = Fpath.Set.(elements (of_list t))
let exec_hook i = function None -> Action.ok () | Some h -> h i
let extend ?packages ?packages_v ?dune ?pre_configure ?post_configure ?files t =
let files =
match (files, t.files) with
| None, None -> None
| Some f, None | None, Some f -> Some f
| Some x, Some y -> Some (fun i -> uniq (x i @ y i))
in
let packages =
Key.(pure List.append $ merge_packages packages packages_v $ t.packages)
in
let exec pre f post i =
let* () = exec_hook i pre in
let* () = f i in
exec_hook i post
in
let configure = exec pre_configure t.configure post_configure in
let dune =
Option.map (fun dune i -> t.dune i @ dune i) dune
|> Option.value ~default:t.dune
in
{ t with packages; files; configure; dune }
let nice_name d =
module_name d
|> String.cuts ~sep:"."
|> String.concat ~sep:"_"
|> String.Ascii.lowercase
|> Misc.Name.ocamlify
type ('a, 'i) device = ('a, 'i) t
module Graph = struct
type t =
| D : { dev : ('a, _) device; args : t list; deps : t list; id : int } -> t
type dtree = t
module IdTbl = Hashtbl.Make (struct
type t = dtree
let hash (D t) = t.id
let equal (D t1) (D t2) = Int.equal t1.id t2.id
end)
let fold f t z =
let tbl = IdTbl.create 50 in
let state = ref z in
let rec aux v =
if IdTbl.mem tbl v then ()
else
let (D { args; deps; _ }) = v in
IdTbl.add tbl v ();
List.iter aux deps;
List.iter aux args;
state := f v !state
in
aux t;
!state
let impl_name (D { dev; args = _; deps = _; id }) =
match Type.is_functor (module_type dev) with
| false -> module_name dev
| true ->
let prefix = Astring.String.Ascii.capitalize (nice_name dev) in
Fmt.str "%s__%d" prefix id
let var_name (D { dev; args = _; deps = _; id }) =
let prefix = nice_name dev in
Fmt.str "%s__%i" prefix id
end