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
open Astring
open Action.Syntax
type t = Device.Graph.t
let if_keys x =
Impl.collect
(module Key.Set)
(function If cond -> Key.deps cond | App | Dev _ -> Key.Set.empty)
x
module Keys = struct
type t = Key.Set.t
let union a b = Key.Set.union a b
let empty = Key.Set.empty
end
let keys x =
Impl.collect
(module Keys)
(function
| Dev c -> Key.Set.of_list (Device.keys c)
| If cond -> Key.deps cond
| App -> Keys.empty)
x
module Runtime_args = struct
type t = Runtime_arg.Set.t
let union a b = Runtime_arg.Set.union a b
let empty = Runtime_arg.Set.empty
end
let runtime_args x =
Impl.collect
(module Runtime_args)
(function
| Dev c -> Runtime_arg.Set.of_list (Device.runtime_args c)
| If _ -> Runtime_args.empty
| App -> Runtime_args.empty)
x
module Packages = struct
type t = Package.Set.t Key.value
let union x y = Key.(pure Package.Set.union $ x $ y)
let empty = Key.pure Package.Set.empty
end
let packages t =
let open Impl in
let aux = function
| Dev c ->
let pkgs = Device.packages c in
let runtime_args = Device.runtime_args c in
let =
List.fold_left
(fun acc k ->
let pkgs = Runtime_arg.packages k in
Package.Set.(union acc (of_list pkgs)))
Package.Set.empty runtime_args
in
let aux x = Package.Set.(union (of_list x) extra_pkgs) in
Key.(pure aux $ pkgs)
| If _ | App -> Packages.empty
in
let return x = Package.Set.to_list x in
Key.(pure return $ Impl.collect (module Packages) aux t)
module Installs = struct
type t = Install.t Key.value
let union x y = Key.(pure Install.union $ x $ y)
let empty = Key.pure Install.empty
end
let install i x =
Impl.collect
(module Installs)
(function Dev c -> Device.install c i | If _ | App -> Installs.empty)
x
let files info t =
Impl.collect
(module Fpath.Set)
(function Dev c -> Device.files c info | If _ | App -> Fpath.Set.empty)
t
module Dune = struct
type t = Dune.stanza list
let union = ( @ )
let empty = []
end
let dune info =
Impl.collect (module Dune) @@ function
| Dev c -> Device.dune c info
| If _ | App -> Dune.empty
let module_expression fmt (c, args) =
Fmt.pf fmt "%s%a" (Device.module_name c)
Fmt.(
list ~sep:(any "")
(any "(" ++ of_to_string Device.Graph.impl_name ++ any ")"))
args
let find_all_devices info g i =
let ctx = Info.context info in
let id = Impl.with_left_most_device ctx i { f = Device.id } in
let f x l =
let (Device.Graph.D { dev; _ }) = x in
if Device.id dev = id then x :: l else l
in
Device.Graph.fold f g []
let iter_actions f t =
let f v res =
let* () = res in
f v
in
Device.Graph.fold f t (Action.ok ())
let lines_of_str str =
String.fold_left (fun n -> function '\n' -> n + 1 | _ -> n) 0 str
type main = { dir : Fpath.t; path : Fpath.t; mutable lines : int }
let main info =
let path = Info.main info in
let dir = Fpath.(Info.(parent (config_file info) / project_name info)) in
let+ str = Action.read_file path in
let lines = lines_of_str str in
{ dir; path; lines }
let append_main main msg fmt =
let purpose = Fmt.str "Append to main.ml (%s)" msg in
Fmt.kstr
(fun str ->
main.lines <- main.lines + lines_of_str str + 1;
Action.with_output ~path:main.path ~append:true ~purpose (fun ppf ->
Fmt.pf ppf "%s@." str))
fmt
let pp_pos ppf = function
| None -> ()
| Some (file, line, _, _) -> Fmt.pf ppf "# %d %S@." line file
let reset_pos { dir; path; lines } =
let file = Fpath.(dir // path) |> Fpath.normalize |> Fpath.to_string in
Some (file, lines + 2, 0, 0)
let configure info t =
let f (v : t) =
let* main = main info in
let (D { dev; args; _ }) = v in
let* () = Device.configure dev info in
if args = [] then Action.ok ()
else
let* () = append_main main "reset" "%a" pp_pos (reset_pos main) in
append_main main "configure" "module %s = %a\n" (Device.Graph.impl_name v)
module_expression (dev, args)
in
iter_actions f t
let meta_init fmt (connect_name, result_name) =
Fmt.pf fmt " let _%s = Lazy.force %s in@ " result_name connect_name
let emit_connect fmt (iname, names, runtime_args, connect_code) =
let rnames = List.map (fun x -> "_" ^ x) names in
let knames = List.map (fun k -> "_" ^ Runtime_arg.var_name k) runtime_args in
let bind ppf name = Fmt.pf ppf " _%s >>= fun %s ->\n" name name in
let bind_key ppf k =
Fmt.pf ppf " let _%s = %a in\n" (Runtime_arg.var_name k) Runtime_arg.call k
in
let { Device.pos; code } = connect_code (rnames @ knames) in
Fmt.pf fmt "let %s = lazy (\n%a%a%a%a %s@\n);;" iname
Fmt.(list ~sep:nop meta_init)
(List.combine names rnames)
Fmt.(list ~sep:nop bind)
rnames
Fmt.(list ~sep:nop bind_key)
runtime_args pp_pos pos code
let emit_run main init main_name =
let force ppf name = Fmt.pf ppf "Lazy.force %s >>= fun _ ->\n " name in
append_main main "emit_run"
"let () =\n let t = %aLazy.force %s in\n run t\n;;"
Fmt.(list ~sep:nop force)
init main_name
let connect ?(init = []) info t =
let* main = main info in
let f (v : t) =
let (D { dev; args; deps; _ }) = v in
let var_name = Device.Graph.var_name v in
let impl_name = Device.Graph.impl_name v in
let arg_names = List.map Device.Graph.var_name (args @ deps) in
let runtime_args = Device.runtime_args dev in
let* () =
append_main main "connect" "%a" emit_connect
(var_name, arg_names, runtime_args, Device.connect dev info impl_name)
in
append_main main "reset" "%a" pp_pos (reset_pos main)
in
let* () = iter_actions f t in
let main_name = Device.Graph.var_name t in
let init_names =
List.fold_left
(fun acc i ->
match find_all_devices info t i with
| [] -> assert false
| ds -> List.map Device.Graph.var_name ds @ acc)
[] init
|> List.rev
in
emit_run main init_names main_name