123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432(** An OCaml embedding of rebuilders described in "Build systems à la carte:
Theory and practice".
This embedding is not necessary for MlFront thunks; we can implement thunks
without using a generalized framework! However, this embedding may aid
modifications to thunk implementations.
Confer
{{:https://www.cambridge.org/core/services/aop-cambridge-core/content/view/097CE52C750E69BD16B78C318754C7A4/S0956796820000088a.pdf/build-systems-a-la-carte-theory-and-practice.pdf}5
Rebuilders}.
{v
Andrey Mokhov, Neil Mitchell, and Simon Peyton Jones. 2018. Build Systems à la Carte. Proc. ACM Program. Lang. 2, ICFP, Article 79 (September 2018)
doi:10.1017/S0956796820000088
v} *)(** The type of synchronous rebuilder build system components. *)moduletypeREBUILDER=sigtypevc(** The type of applicative/monad-constrained build task value. *)typeir(** The type of persistent build information carried by a state monad.
The state can be combined with persistent build information from a
{!SCHEDULER} to make the full persistent build information. *)typek(** The type of build task key. *)typev(** The type of build task value. *)typeo(** The type of build task origin. *)typetracked(** The type of what is being tracked for a build task value. *)typelifted_vctypetask=k*((o->k->vc)->vc)typerebuilt_vc=ir->v*ir*trackedtyperebuilt_task=k*((o->k->rebuilt_vc)->rebuilt_vc)typet=ir->o->v->task->rebuilt_task(** The type of rebuilders which take an information recaller [ir], an origin
[o], a value [v], and a task [task] to produce a rebuilt task. *)endletrebuilder_async_cls=BuildConstraints.StateMessage.new_class_exn"MlFront_Thunk.BuildRebuilders.CtRebuilderAsync"(** The type of asynchronous rebuilder build system components. *)moduletypeREBUILDER_ASYNC=sigtypevc(** The type of applicative/monad-constrained build task value. *)typeir(** The type of persistent build information carried by a state monad.
The state can be combined with persistent build information from a
{!SCHEDULER} to make the full persistent build information. *)typek(** The type of build task key. *)typev(** The type of build task value. *)typeo(** The type of build task origin. *)typestatetypetracked(** The type of what is being tracked for a build task value. *)type'apromisetypejournaltypejournal_entry_idtypejournal_entry_valuetypetask=k*((o->k->vc)->vc)type('k,'v)key_dependent_hashtyperebuilt_vc=(state*journal->(v*BuildConstraints.StateMessage.tlist*(journal_entry_id*journal_entry_value)UniqueInsertionList.t)promise)promisetyperebuilt_task=k*((o->k->rebuilt_vc)->rebuilt_vc)typet=ir->o->v->task->rebuilt_task(** The type of rebuilders which take an information recaller [ir], an origin
[o], a value [v], and a task [task] to produce a rebuilt task. *)typeBuildConstraints.StateMessage.obj+=|PostBuildValueAndDependencyHashesof(k*(k,v)key_dependent_hashoption)list*vend(** Functions common to async and sync constructive trace rebuilders. *)moduleCtRebuilderUtils(K:BuildSystems.ABSTRACT_TYPE)(V:BuildSystems.ABSTRACT_TYPE)(CT:BuildSystems.CLOUD_PERSISTENT_HASHwithtypek=K.tandtypev=V.t)=struct(** [is_elem k v list_of_v] is [true] if and only if the key [k] dependent
value [v] is a member of the value list [list_of_v].
Membership is considered using the hash of the value. This is the source
of the {!BuildSystems.CLOUD_PERSISTENT_HASH} technical requirement that
the hash {b must be a secure hash} so that hash equality means value
equality. *)letis_elem(k:K.t)(v:V.t)(vl:V.tlist)=matchCT.maybe_cloud_persistent_hashkvwith|None->false|Someh->List.exists(funv'->matchCT.maybe_cloud_persistent_hashkv'with|None->false|Someh'->CT.strong_hash_equalhh')vlend(** Constructive trace rebuilder.
[CT] is the functor parameter for the constructive trace store. Its hash
function {b must be a "secure hash" like SHA-256} in that two values can be
considered equal if their (secure) hashes are equal.
{b Do not use conventional hashes} like OCaml's {!Stdlib.Hashtbl.hash} or a
MD5 hash which do not imply that hash equality is the same as value
equality.*)moduleCtRebuilder(IR:BuildSystems.ABSTRACT_TYPE)(K:BuildSystems.ABSTRACT_TYPE)(V:BuildSystems.ABSTRACT_TYPE)(O:BuildSystems.ORIGIN_WITH_DEPTHwithtypek=K.t)(C:BuildConstraints.MONAD_STATE_WRITERwithtypestate=IR.tandtypeoutput=(K.t*V.t)list)(CT:BuildTraces.CONSTRUCTIVE_TRACE_STORE_SYNCwithtypek=K.tandtypev=V.tandtypeir=IR.tandmoduleC=C)(Tasks:BuildSystems.TASKSwithtypek=K.tandtypevc=V.tC.tandtypelifted_vc=V.tC.tandtypeo=O.t):sigincludeREBUILDERwithtypek=K.tandtypeir=IR.tandtypev=V.tandtypevc=V.tC.tandtypeo=O.tandtypetracked=(K.t*V.t)listvalrebuilder:ir->o->v->k*((o->k->vC.t)->vC.t)->k*((o->k->rebuilt_vc)->rebuilt_vc)end=structopenCtRebuilderUtils(K)(V)(CT)typek=K.ttypeir=IR.ttypev=V.ttypevc=V.tC.ttypelifted_vc=vctypeo=O.ttypetracked=(K.t*V.t)listtypetask=k*((o->k->vc)->vc)typerebuilt_vc=ir->v*ir*trackedtyperebuilt_task=k*((o->k->rebuilt_vc)->rebuilt_vc)typet=ir->o->v->task->rebuilt_taskmoduleTracked=BuildSystems.TrackOnStateWriter(K)(V)(O)(C)letrebuilder:t=fun(ir:ir)(origin:O.t)(v:V.t)((k,_f_compute)astask:Tasks.task)->(* make rebuilder *)(k,fun(fetch:o->k->rebuilt_vc):rebuilt_vc->letopenBuildConstraints.MonadLetSyntax(C)inlet*cached_values=lettracked_fetch_hashdep_k:vC.t=fetchorigindep_kinCT.construct_ctk(fundep_k->C.map(fundep_v->CT.maybe_cloud_persistent_hashdep_kdep_v)(tracked_fetch_hashdep_k))irinmatchcached_valueswith|_whenis_elemkvcached_values->C.purev|cached_value::_->C.purecached_value|[]->letnew_value,deps=Tracked.trackir(moduleTasks)origintaskfetchinletir_modification=CT.record_ctkv(List.map(fun(dep_k,dep_v)->(dep_k,CT.maybe_cloud_persistent_hashdep_kdep_v))deps)inlet*()=C.modifyir_modificationinC.returnnew_value)end(** Can listen and react to rebuilder events. *)moduletypeREBUILDER_EVENTS=sigtypektypevtypeuctypeovalon_dependency_discovered:depth:int->key:k->depends_upon_key:k->uc(** [on_dependency_discovered ~depth ~key ~depends_upon_key] is the event that
the build task [key] has discovered a dependency on the build task
[depends_upon_key].
The [depth] is how deep in the dependency chain the discovery is. A depth
of [0] means that [key] is being built directly by the user (or top-level
build system).
Returns a unit continuation. *)valbefore_rebuild_key:candidate_values:vlist->origin:o->k->uc(** [before_rebuild_key ~candidate_values ~origin k] is the event that the key
[k] is out-of-date and about to be built so it is up-to-date.
The [candidate_values] are values that were considered for [k] from the
constructive trace store, but none matched the values available in the
value store.
[origin] is the origin of the build task.
Returns a unit continuation. *)end(** Asynchronous constructive trace rebuilder.
[CT] is the functor paramter for the constructive trace store. Its hash
function {b must be a "secure hash" like SHA-256} in that two values can be
considered equal if their (secure) hashes are equal.
{b Do not use conventional hashes} like OCaml's {!Stdlib.Hashtbl.hash} or a
MD5 hash which do not imply that hash equality is the same as value
equality.*)moduleCtRebuilderAsync(IR:BuildSystems.ABSTRACT_TYPE)(K:sigtypetvalshow:t->string[@@warning"-unused-value-declaration"]end)(O:BuildSystems.ORIGIN_WITH_DEPTHwithtypek=K.t)(V:sigtypetvalwith_origin:t->O.t->tvalshow:t->string[@@warning"-unused-value-declaration"]end)(P:BuildConstraints.PROMISE_IMPL)(C:BuildConstraints.MONAD_STATE_WRITER_ASYNCwithtypestate=IR.tandtype'apromise='aP.promise)(RE:REBUILDER_EVENTSwithtypek=K.tandtypev=V.tandtypeuc=unitC.tandtypeo=O.t)(CT:BuildTraces.CONSTRUCTIVE_TRACE_STORE_ASYNCwithtypek=K.tandtypev=V.tandtypeir=IR.tandmoduleC=C)(Tasks:BuildSystems.TASKSwithtypek=K.tandtypevc=V.tC.tandtypelifted_vc=V.tC.tandtypeo=O.t):sigincludeREBUILDER_ASYNCwithtypek=K.tandtypeir=IR.tandtypev=V.tandtypevc=V.tC.tandtypeo=O.tandtypestate=C.stateandtypetracked=(K.t*(K.t,V.t)CT.key_dependent_hash)list*V.tandtype'apromise='aP.promiseandtypejournal=C.journalandtypejournal_entry_id=C.journal_entry_idandtypejournal_entry_value=C.journal_entry_valuevalrebuilder:ir->o->v->k*((o->k->vC.t)->vC.t)->k*((o->k->rebuilt_vc)->rebuilt_vc)end=structopenCtRebuilderUtils(K)(V)(CT)typeBuildConstraints.StateMessage.obj+=|PostBuildValueAndDependencyHashesof(K.t*(K.t,V.t)CT.key_dependent_hashoption)list*V.ttypek=K.ttypeir=IR.ttypev=V.ttypevc=V.tC.ttypeo=O.ttypestate=C.statetypetracked=(K.t*(K.t,V.t)CT.key_dependent_hash)list*V.ttype'apromise='aP.promisetypejournal=C.journaltypejournal_entry_id=C.journal_entry_idtypejournal_entry_value=C.journal_entry_valuetypetask=k*((o->k->vc)->vc)type('k,'v)key_dependent_hash=(k,v)CT.key_dependent_hashtyperebuilt_vc=(C.state*C.journal->(v*BuildConstraints.StateMessage.tlist*(journal_entry_id*journal_entry_value)UniqueInsertionList.t)promise)promisetyperebuilt_task=k*((o->k->rebuilt_vc)->rebuilt_vc)typet=ir->o->v->task->rebuilt_taskmoduleM=structtype('key,'value)t=BuildConstraints.StateMessage.tletdependency_discovered~depth~key~depends_upon_key=RE.on_dependency_discovered~depth~key~depends_upon_keyletmessage_of_build_result~deps~result=letdep_hashes=List.map(fun(k,v)->(k,CT.maybe_cloud_persistent_hashkv))depsinBuildConstraints.StateMessage.createrebuilder_async_cls(PostBuildValueAndDependencyHashes(dep_hashes,result))endmoduleTracked=BuildSystems.TrackOnStateWriterAsync(K)(V)(O)(P)(C)(M)letrebuilder:t=fun(ir:ir)(o:O.t)(v:V.t)((k,f_compute):Tasks.task)->(k,fun(fetch:o->k->rebuilt_vc):rebuilt_vc->letopenBuildConstraints.MonadLetSyntax(C)in(* Printf.eprintf "rebuilder k=%s child_o=%s before construct_ct\n"
(K.show k) (O.show child_o); *)let*cached_values=lettracked_fetch_hash(dep_k:k):vC.t=fetchodep_kinCT.construct_ctk(fundep_k->C.map(fundep_v->CT.maybe_cloud_persistent_hashdep_kdep_v)(tracked_fetch_hashdep_k))irin(* Printf.eprintf
"rebuilder k=%s child_o=%s cached_values(construct_ct)=%d \
maybe_cloud_persistent_hashes:%s\n"
(K.show k) (O.show child_o)
(List.length cached_values)
(String.concat "|"
(List.map
(fun v ->
match CT.maybe_cloud_persistent_hash k v with
| None -> "none"
| Some h -> CT.strong_hash_show h)
cached_values)); *)let(let*+)=P.bind_promiseinmatchcached_valueswith|_whenis_elemkvcached_values->C.purev|cached_value::_->C.purecached_value|[]->let*()=(* Printf.eprintf
"rebuilder before_rebuild_key |1 k=%s | origin=%s\n" (K.show k)
(O.show o); *)RE.before_rebuild_key~candidate_values:cached_values~origin:okinlet*+msg=Tracked.trackir(moduleTasks)o(k,f_compute)(funo'k_dep->fetcho'k_dep)inletdeps,new_value=matchBuildConstraints.StateMessage.downcastmsgrebuilder_async_clswith|Some(PostBuildValueAndDependencyHashes(dep_hashes,result))->(dep_hashes,result)|_->Printf.ksprintffailwith"Expected PostBuildValueAndDependencyHashes, not %s"(BuildConstraints.StateMessage.showmsg)in(* rewrite value to include the now accurate origin *)letnew_value=V.with_originnew_valueoin(* Printf.eprintf "rebuilder k=%s after track new_value=%s\n"
(K.show k) (V.show new_value); *)(* Printf.eprintf "post_record_ct k=%s\n" (K.show k); *)let*()=CT.post_record_ctknew_valuedepsirinC.returnnew_value)end