Source file BuildEngine.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
(** The reference implementation build engine.
{3 Implementors of build engine}
You will want to first read
{{:https://discuss.ocaml.org/t/ocaml-version-of-the-build-systems-a-la-carte-paper/17042}OCaml
version of the “Build systems à la carte” paper} first. For thunks we use
the term "build engine", but it is interchangeable with the term "build
system" used in the paper.
The reference build engine uses the same components as
["tests/MlFront_Thunk/alacarte_6_4_test.ml"] to aid in understanding. All of
those components are in {!BuildCore}. *)
module LuaJsonSupport =
MlFront_Thunk.ThunkLuaLibJsonDk.Support (MlFront_Thunk.ThunkLuaScript.Value)
module Make (Ctx : BuildContext.S) = struct
module BuildFinder' = BuildFinder.Make (Ctx)
module BuildTaskFactory' = BuildTaskFactory.Make (Ctx)
module BuildTaskScript' = BuildTaskScript.Make (Ctx)
module BuildTaskUnresolved' = BuildTaskUnresolved.Make (Ctx)
module BuildValues' = BuildValues.Make (Ctx)
let log_debug msgf = Ctx.run_isolated_promise @@ Ctx.Log.debug msgf
type parse_result =
| HadWarnings
| AddedValuesJsonFile of {
values_file_sha256 : string;
local_values_file : [ `Validated of Ctx.Io.file_object ];
}
| AddedValuesLuaFile of {
values_file_sha256 : string;
local_values_file : [ `Validated of Ctx.Io.file_object ];
}
let parse_valueslua_file_gracefully ctx ~values_file_sha256
~validated_values_file (module Tasks' : Ctx.THUNK_TASKS)
(values_file : Ctx.Io.file_object) =
if Ctx.debug_task ctx then
log_debug (fun l ->
l "[task] %s parses `%s`"
(Ctx.V.get_valuesluafile_value_id ~values_file_sha256)
(Ctx.Io.file_origin values_file));
BuildTaskFactory'.add_valuesluafile_tasks_gracefully ctx
~values_file:validated_values_file ~values_file_sha256
~add_task:
(BuildTaskUnresolved'.add_task_if_missing
(module Tasks' : Ctx.THUNK_TASKS))
()
let parse_valuesjson_file_gracefully ?deny_deprecated_function_args ctx
~values_file_sha256 ~validated_values_file
(module Tasks' : Ctx.THUNK_TASKS) (values_file : Ctx.Io.file_object) =
if Ctx.debug_task ctx then
log_debug (fun l ->
l "[task] %s parses `%s`"
(Ctx.V.get_valuesjsonfile_value_id ~values_file_sha256)
(Ctx.Io.file_origin values_file));
BuildTaskFactory'.add_valuesjsonfile_tasks_gracefully
?deny_deprecated_function_args ctx ~values_file:validated_values_file
~values_file_sha256
~add_task:
(BuildTaskUnresolved'.add_task_if_missing
(module Tasks' : Ctx.THUNK_TASKS))
()
type state = Ctx.State.t
(** For .mli *)
let create_key_for_object = Ctx.K.create_for_object
(** For .mli *)
let create_key_for_bundle = Ctx.K.create_for_bundle
(** For .mli *)
let create_key_for_asset = Ctx.K.create_for_asset
(** For .mli *)
let create_key_for_scriptmodule = Ctx.K.create_for_scriptmodule
let parse_values_files_gracefully ctx (module Tasks' : Ctx.THUNK_TASKS) =
let ( let* ), return = Ctx.Promise.(bind, return) in
let values_files =
let f typ =
List.map
(BuildFinder'.get_values_files_in_dir_and_sublibraries
~explain:(Ctx.explain ctx) ~typ)
in
f `System (Ctx.sysincludedirs ctx)
@ f `Workspace (Ctx.workspaceincludedirs ctx)
@ f `User (Ctx.userincludedirs ctx)
|> List.flatten
in
let f ({ file = values_file; subtyp } : BuildFinder'.t) =
let* sha256_result =
Ctx.Io.checksum_file ~algo:`SHA256 ~dos2unix:() values_file
in
let values_file_type =
match subtyp with ValuesJson _flags -> `Json | ValuesLua -> `Lua
in
let alert s =
Printf.sprintf "%s: %s" (Ctx.Io.file_origin values_file) s
in
match sha256_result with
| `Error err ->
let* () = Ctx.Log.warn (fun l -> l "%s" (alert err)) in
return HadWarnings
| `Checksum (values_file_sha256, _values_file_sz) -> (
let* (_added : bool) =
BuildValues'.add_values_file_exn
~valuestore:(Ctx.valuestore_maybe_relto_basedir ctx)
~values_file_sha256 ~values_file_type values_file
in
Assumptions.no_trust_for_local_values_file ();
let validated_values_file = `Validated values_file in
let* values_result =
match subtyp with
| ValuesJson flags ->
let deny_deprecated_function_args =
if List.mem `Allow_deprecated_toplevel_moduleid flags then
Some ()
else None
in
parse_valuesjson_file_gracefully ?deny_deprecated_function_args
ctx ~values_file_sha256 ~validated_values_file
(module Tasks')
values_file
| ValuesLua ->
parse_valueslua_file_gracefully ctx ~values_file_sha256
~validated_values_file
(module Tasks')
values_file
in
match values_result with
| Error msg ->
let* () = Ctx.Log.warn (fun l -> l "%s" (alert msg)) in
return HadWarnings
| Ok `Json ->
return
(AddedValuesJsonFile
{
values_file_sha256;
local_values_file = validated_values_file;
})
| Ok `Lua ->
return
(AddedValuesLuaFile
{
values_file_sha256;
local_values_file = validated_values_file;
}))
in
let* file_results = Ctx.Promise.parallel (List.map f values_files) in
let* embedded_results =
let embedded_parse_results =
List.map f (BuildFinder'.get_embedded_values_files ctx)
in
Ctx.Promise.parallel embedded_parse_results
in
let final_result =
List.fold_left
(fun (acc_warnings, acc_keys) warnings_result ->
match (acc_warnings, (warnings_result : parse_result)) with
| `NoWarnings, HadWarnings -> (`HadWarnings, acc_keys)
| `HadWarnings, HadWarnings -> (`HadWarnings, acc_keys)
| _, AddedValuesJsonFile { values_file_sha256; local_values_file } ->
( acc_warnings,
`ValuesJsonFileSHA256 (values_file_sha256, local_values_file)
:: acc_keys )
| _, AddedValuesLuaFile { values_file_sha256; local_values_file } ->
( acc_warnings,
`ValuesLuaFileSHA256 (values_file_sha256, local_values_file)
:: acc_keys ))
(`NoWarnings, [])
(file_results @ embedded_results)
in
Ctx.Promise.return final_result
let troubleshoot_recursion =
let counter = ref 0 in
fun () ->
incr counter;
if !counter = 3 then failwith "stop at 3"
[@@warning "-unused-value-declaration"]
let register_scriptmodules ctx ~kont_state () =
List.map
(function
| ({
scriptmodule_values_canonical_id;
scriptmodule_values_file_sha256;
scriptmodule_id;
scriptmodule_range;
scriptmodule_file;
} :
Ctx.V.scriptmodule) ->
BuildTaskScript'.register_scriptmodule ctx ~kont_state
~scriptmodule_values_canonical_id ~scriptmodule_values_file_sha256
~scriptmodule_id ~scriptmodule_range ~scriptmodule_file ())
(Ctx.State.get_all_scriptmodule_values kont_state)
(** For .mli *)
let load_state_and_tasks_gracefully ?(lua_valuescan_origin = `UserRanBuild)
?(lua_allow_fetch = false) ctx ~traces () =
let module Tasks = (val Ctx.tasks_module) in
let precompiled_traces = Ctx.State.precompile_traces traces in
let build_state = ref None in
let fetch_script_module =
if lua_allow_fetch then
Some
(fun module_id module_semver ->
match !build_state with
| None -> Ok `NotFound
| Some build_state ->
let key =
Ctx.K.create_for_scriptmodule ~debug_reference:None ~module_id
~module_semver ()
in
let cont =
BuildTaskUnresolved'.run_single ctx ~tasks:(module Tasks) key
in
let (_ : state) =
BuildTaskUnresolved'.run_unit_continuation cont build_state
in
Ok `Found)
else None
in
let parse_values_files_promise =
parse_values_files_gracefully ctx (module Tasks)
in
let warnings_result, valuefiles =
Ctx.run_isolated_promise parse_values_files_promise
in
flush_all ();
let store =
Ctx.MutableStore.initialise
(Ctx.StoreInfo.create ~explain:(Ctx.explain ctx)
~warnings_during_parsing:
(match warnings_result with
| `HadWarnings -> true
| `NoWarnings -> false))
in
let kont_state =
Ctx.State.create_with_precompiled_traces_and_lua_options
~valuescan_origin:lua_valuescan_origin ?fetch_script_module store
precompiled_traces
in
build_state := Some kont_state;
Assumptions.accepted_distributions_are_present_in_trace_store ();
Assumptions.distribution_entry_points_only_trace_stores_and_include_dirs ();
Ctx.State.accept_known_distributions kont_state;
let prefetch_keys =
List.map
(function
| `ValuesJsonFileSHA256 (values_file_sha256, local_file) ->
Ctx.State.assign_values_file_location ~values_file_sha256
~local_file kont_state;
`ValuesJsonFileSHA256 values_file_sha256
| `ValuesLuaFileSHA256 (values_file_sha256, local_file) ->
Ctx.State.assign_values_file_location ~values_file_sha256
~local_file kont_state;
`ValuesLuaFileSHA256 values_file_sha256)
valuefiles
in
Assumptions
.scriptmodule_in_tracestore_guarantees_lua_scriptmodule_registered ();
let promises = register_scriptmodules ctx ~kont_state () in
let (_ : unit list) =
Ctx.run_isolated_promise (Ctx.Promise.parallel promises)
in
(kont_state, (module Tasks : Ctx.THUNK_TASKS), prefetch_keys)
(** For .mli *)
let output_get_object ctx ~build_request ~source ~command_output
~archive_member key =
let open Ctx.Syntax in
let* state = get in
let value = Ctx.MutableStore.get_value key (Ctx.State.store state) in
Ctx.output_get_object ctx ~build_request ~source ~command_output
~archive_member key value
(** For .mli *)
let output_install_object ctx ~build_request ~source ~command_output
~archive_member key =
let open Ctx.Syntax in
let* state = get in
let value = Ctx.MutableStore.get_value key (Ctx.State.store state) in
Ctx.output_install_object ctx ~build_request ~source ~command_output
~archive_member key value
(** For .mli *)
let output_post_object ctx ~build_request ~source ~command_output
~archive_member key =
let open Ctx.Syntax in
let* state = get in
let value = Ctx.MutableStore.get_value key (Ctx.State.store state) in
Ctx.output_post_object ctx ~build_request ~source ~command_output
~archive_member key value
(** For .mli *)
let output_get_bundle ctx ~build_request ~source ~command_output key =
let open Ctx.Syntax in
let* state = get in
let value = Ctx.MutableStore.get_value key (Ctx.State.store state) in
Ctx.output_get_bundle ctx ~build_request ~source ~command_output key value
(** For .mli *)
let output_get_asset ctx ~build_request ~source ~command_output
~archive_member key =
let open Ctx.Syntax in
let* state = get in
let value = Ctx.MutableStore.get_value key (Ctx.State.store state) in
Ctx.output_get_asset ctx ~build_request ~source ~command_output
~archive_member key value
(** For .mli *)
let unzip_and_cache_value ctx ~source range key =
let open Ctx.Syntax in
let* state = get in
let value = Ctx.MutableStore.get_value key (Ctx.State.store state) in
Ctx.unzip_and_cache_value ctx ~source range value
let remove_invalid_values ctx (state : state) =
let valuestore = Ctx.valuestore_maybe_relto_basedir ctx in
let buildlogtrace = if Ctx.buildlogtrace ctx then Some () else None in
let cont =
let open Ctx.Syntax in
match Ctx.integrity ctx with
| `None -> return ()
| `Existence ->
Assumptions
.persisted_values_are_checked_for_existence_during_trace_store_load ();
return ()
| `Checksum as integrity ->
let integrity_name = "checksum" in
if buildlogtrace = Some () then
log_debug (fun l ->
l "[buildlog] post load %s check start" integrity_name);
let* state' = get in
let _newgen, traces = Ctx.State.new_generation state' in
let visited = Hashtbl.create (List.length traces) in
let* () =
List.fold_left
(fun acc
({ generation = _; key = k; dependencies = _; result = v } as
trace :
Ctx.State.trace) ->
let* () = acc in
match
(Ctx.V.value_id v, Ctx.V.maybe_cloud_persistent_hash k v)
with
| Some value_id, Some value_sha256 ->
if Hashtbl.mem visited value_id then return ()
else begin
Hashtbl.add visited value_id ();
let* (maybe_available : MlFront_Core.FilePath.t option) =
BuildValues'.make_value_available ?buildlogtrace
~valuestore ~value_id ~value_sha256 ~integrity ()
in
match maybe_available with
| Some _available_fp -> return ()
| None ->
if buildlogtrace = Some () then
log_debug (fun l ->
l "[buildlog] remove trace %s" value_id);
let* state' = get in
Ctx.State.remove_trace state' trace;
return ()
end
| _ -> return ())
(return ()) traces
in
if buildlogtrace = Some () then
log_debug (fun l ->
l "[buildlog] post load %s check finished" integrity_name);
return ()
in
let (), state' = Ctx.run_continuation cont state in
state'
end