B0_memo
Build memoizer
TODO explain better how this all works and try to simplify. If the path given to Tool.t
is not made of a single path segment it is not search in the environmet and it is the duty of the client to ensure it gets ready at some point. Either by a direct call to ready_file
or by another file write.
module Tool : sig ... end
Command line tools.
module Env : sig ... end
Memo environment lookups.
type tool_lookup =
t ->
B0_std.Cmd.tool ->
(B0_std.Fpath.t, string) Stdlib.result B0_std.Fut.t
The type for tool lookups. Given a command line tool specification returns a file path to the tool executable or an error message mentioning the tool if it cannot be found.
val tool_lookup_of_os_env :
?sep:string ->
?var:string ->
B0_std.Os.Env.t ->
tool_lookup
env_tool_lookup ~sep ~var env
is a tool lookup that gets the value of the var
variable in env
treats it as a sep
separated search path and uses the result to lookup with B0_std.Os.Cmd.get
with the memo's win_exe
. var
defaults to PATH
and sep
to B0_std.Fpath.search_path_sep
.
val tool : t -> Tool.t -> B0_std.Cmd.t -> cmd
tool m t
is tool t
memoized. Use the resulting function to spawn the tool with the given arguments.
val tool_opt : t -> Tool.t -> (B0_std.Cmd.t -> cmd) option B0_std.Fut.t
tool_opt m t
is like tool
, except None
is returned if the tool cannot be found. y
val spawn :
t ->
?stamp:string ->
?reads:B0_std.Fpath.t list ->
?writes:B0_std.Fpath.t list ->
?env:B0_std.Os.Env.t ->
?cwd:B0_std.Fpath.t ->
?stdin:B0_std.Fpath.t ->
?stdout:B0_zero.Op.Spawn.stdo ->
?stderr:B0_zero.Op.Spawn.stdo ->
?success_exits:B0_zero.Op.Spawn.success_exits ->
?post_exec:(B0_zero.Op.t -> unit) ->
?k:(B0_zero.Op.t -> int -> unit) ->
cmd ->
unit
spawn m ~reads ~writes ~env ~cwd ~stdin ~stdout ~stderr
~success_exits cmd
spawns cmd
once reads
files are ready and makes files writes
ready if the spawn succeeds and the file exists. The rest of the arguments are:
stdin
reads input from the given file. If unspecified reads from the standard input of the program running the build. Warning. The file is not automatically added to reads
, this allows for example to use B0_std.Fpath.null
.stdout
and stderr
, the redirections for the standard outputs of the command, see B0_zero.Op.Spawn.stdo
. Path to files are created if needed. Warning. File redirections are not automatically added to writes
; this allows for example to use B0_std.Fpath.null
.success_exits
the exit codes that determine if the build operation is successful (defaults to 0
, use []
to always succeed)env
, environment variables added to the build environment. This overrides environment variables read by the tool in the build environment except for forced one. It also allows to specify enovironment that may not be mentioned by the running tool's environment specification.cwd
the current working directory. Default is the memo's cwd
. In general it's better to avoid using relative file paths and tweaking the cwd
. Construct make your paths absolute and invocations independent from the cwd
.post_exec
, if specified is called with the build operation after it has been executed or revived. If it was executed this is called before the operation gets recorded. It can be used to define the reads
and writes
of the operation if they are difficult to find out before hand. Do not access m
in that function.k
, if specified a function invoked once the spawn has succesfully executed with the operation and the exit code.stamp
is used for caching if two spawns diff only in their stamp they will cache to different keys. This can be used to memoize tool whose outputs may not entirely depend on the environment, the cli stamp and the the content of read files.Note. If the tool spawn acts on a sort of "main" file (e.g. a source file) it should be specified as the first element of reads
, this is interpreted specially by certain build tracer.
val spawn' :
t ->
?stamp:string ->
?reads:B0_std.Fpath.t list ->
writes_root:B0_std.Fpath.t ->
?writes:(B0_zero.Op.t -> B0_std.Fpath.t list) ->
?env:B0_std.Os.Env.t ->
?cwd:B0_std.Fpath.t ->
?stdin:B0_std.Fpath.t ->
?stdout:B0_zero.Op.Spawn.stdo ->
?stderr:B0_zero.Op.Spawn.stdo ->
?success_exits:B0_zero.Op.Spawn.success_exits ->
?k:(B0_zero.Op.t -> int -> unit) ->
cmd ->
unit
spawn'
is like spawn
except the actual file paths written by the spawn need not be determined before the spawn. Only the root directory of writes need to be specified via writes_root
. After the spawn executes the writes can be determined via the writes
function, the returned paths must be absolute and be prefixed by writes_root
(defaults to recursively list all the files rooted in writes_root
).
val run_proc : t -> (unit -> unit B0_std.Fut.t) -> unit
run_proc m proc
calls proc ()
and handles any fail
ure. This also catches non-asynchronous uncaught exceptions and turns them into `Fail
notification operations.
val fail : t -> ('a, Stdlib.Format.formatter, unit, 'b) Stdlib.format4 -> 'a
fail m fmt ...
fails the procedure via a notify
operation.
val fail_if_error : t -> ('a, string) Stdlib.result -> 'a
fail_if_error m r
is v
if r
is Ok v
and fail m "%s" e
if r
is Error _
.
val ready_file : t -> B0_std.Fpath.t -> unit
read_file m p
declares path p
to be ready, that is exists and is up-to-date in b
. This is typically used with source files and files external to the build like installed libraries.
val ready_files : t -> B0_std.Fpath.t list -> unit
ready_files m ps
is List.iter (ready_files m) ps
.
val read : t -> B0_std.Fpath.t -> string B0_std.Fut.t
read m file k
is a future that determines with the contents s
of file file
when it becomes ready in m
.
val write :
t ->
?stamp:string ->
?reads:B0_std.Fpath.t list ->
?mode:int ->
B0_std.Fpath.t ->
(unit -> (string, string) Stdlib.result) ->
unit
write m ~reads file w
writes file
with data w ()
and mode mode
(defaults to 0o644
) when reads
are ready. w
's result must only depend on reads
, mode
and stamp
(defaults to ""
) and should not perform other side effects on the file system.
val copy :
t ->
?mode:int ->
?linenum:int ->
B0_std.Fpath.t ->
dst:B0_std.Fpath.t ->
unit
copy m ~mode ?linenum src ~dst
copies file src
to dst
with mode mode
(defaults to 0o644
) when src
is ready. If linenum
is specified, the following line number directive is prependend in dst
to the contents of src
:
#line $(linenum) "$(src)"
val copy_to_dir :
t ->
?mode:int ->
?linenum:int ->
?src_root:B0_std.Fpath.t ->
B0_std.Fpath.t ->
dir:B0_std.Fpath.t ->
B0_std.Fpath.t
copy_to_dir src dir
is copy src ~dst
with dst
as Fpath.reroot ~src_root ~dst_root:dst src
and src_root
defaulting to Fpath.parent src
. The function returns the destination file.
val mkdir : t -> ?mode:int -> B0_std.Fpath.t -> unit B0_std.Fut.t
mkdir m dir p
is a future that determines with ()
when the directory path p
has been created with mode mode
(defaults to 0o755
). The behaviour with respect to file permission of intermediate path segments matches B0_std.Os.Dir.create
.
val delete : t -> B0_std.Fpath.t -> unit B0_std.Fut.t
delete m p
is a future that determines with ()
when path p
is deleted (trashed in fact) and free to reuse.
val wait_files : t -> B0_std.Fpath.t list -> unit B0_std.Fut.t
wait_files m files
is a future that deterines with ()
when all files
are ready in m
. FIXME Unclear whether we really want this, but somehow it's part of the primitives.
Activity marks are just identifiers used for UI purposes to watermark the activity – notably build operations – occuring in the memo.
val mark : t -> string
mark m
is m
's mark.
XXX This needs a bit of reviewing.
val notify :
?k:(unit -> unit) ->
t ->
notify_kind ->
('a, Stdlib.Format.formatter, unit, unit) Stdlib.format4 ->
'a
notify m kind msg
is a notification msg
of kind kind
. Note that a `Fail
notification will entail an an has_failures
on the memo, see also fail
and fail_if_error
.
val notify_if_error :
t ->
notify_kind ->
use:'a ->
('a, string) Stdlib.result ->
'a
notify_if_error m kind ~use r
is v
if r
is Ok v
. If r
is Error e
, a notification of kind kind
is added to m
and use
is returned. Note that a `Fail
notification will entail an has_failures
on the memo, see also fail
and fail_if_error
.
The type for memoizer feedback.
val make_zero :
?clock:B0_std.Os.Mtime.counter ->
?cpu_clock:B0_std.Os.Cpu.Time.counter ->
feedback:(feedback -> unit) ->
cwd:B0_std.Fpath.t ->
?win_exe:bool ->
?tool_lookup:tool_lookup ->
?env:B0_std.Os.Env.t ->
?forced_env_vars:Tool.env_vars ->
B0_zero.Guard.t ->
B0_zero.Reviver.t ->
B0_zero.Exec.t ->
(t, string) Stdlib.result
val make :
?hash_fun:(module B0_std.Hash.T) ->
?win_exe:bool ->
?tool_lookup:tool_lookup ->
?env:B0_std.Os.Env.t ->
?forced_env_vars:Tool.env_vars ->
?cwd:B0_std.Fpath.t ->
?jobs:int ->
?feedback:([ feedback | B0_zero.Exec.feedback ] -> unit) ->
cache_dir:B0_std.Fpath.t ->
trash_dir:B0_std.Fpath.t ->
unit ->
(t, string) Stdlib.result
make
is a simpler make_zero
hash_fun
defaults to B0_std.Hash.Xxh3_64
.jobs
defaults to B0_std.Os.Cpu.logical_count
.env
defaults to B0_std.Os.Env.current
cwd
defaults to B0_std.Os.Dir.cwd
cache_dir
is the cache directory.trash_dir
is the trash directory.feedback
defaults to a nop.forced_env_vars
, defaults to []
.with_feedback m feedback
is m
with feedback replaced by feedback
.
val delete_trash : block:bool -> t -> (unit, string) Stdlib.result
delete_trash ~block m
is B0_zero.Trash.delete
~block (trash m)
.
val hash_string : t -> string -> B0_std.Hash.t
hash_string m s
is B0_zero.Reviver.hash_string
(reviver m) s
.
val hash_file : t -> B0_std.Fpath.t -> (B0_std.Hash.t, string) Stdlib.result
hash_file m f
is B0_zero.Reviver.hash_file
(reviver m) f
. Note that these file hashes operations are memoized.
val stir : block:bool -> t -> unit
stir ~block m
runs the memoizer a bit. If block
is true
blocks until the memoizer is stuck with no operation to execute.
val status : t -> (unit, B0_zero.Op.aggregate_error) Stdlib.result
status m
looks for aggregate errors in m
in ops m
, see B0_zero.Op.aggregate_error
for details.
Usually called after a blocking stir
to check everything executed as expected. The function itself has no effect more operations can be on m
afterwards. If you are only interested in checking if a failure occured in the memo has_failures
is faster.
val timestamp : t -> B0_std.Mtime.Span.t
timestamp m
gets a clock
time stamp.
val clock : t -> B0_std.Os.Mtime.counter
clock m
is m
's clock.
val cpu_clock : t -> B0_std.Os.Cpu.Time.counter
cpu_clock m
is m
's cpu clock.
val env : t -> B0_std.Os.Env.t
env m
is the environment of m
. The environment read by the tools' declared environment variables.
val exec : t -> B0_zero.Exec.t
exec m
is m
's executors.
val forced_env_vars : t -> Tool.env_vars
forced_env_vars m
are the forced environment variables of m
. These variables are put in the stamped environment of any tool despite what it declares to access.
val guard : t -> B0_zero.Guard.t
guard m
is m
's guard.
val has_failures : t -> bool
has_failures m
is true
iff at least one operation has failed.
val ops : t -> B0_zero.Op.t list
ops m
is the list of operations that were submitted to the memoizer
val reviver : t -> B0_zero.Reviver.t
reviver m
is m
's reviver.
val tool_lookup : t -> tool_lookup
tool_lookup m
is m
's tool lookup function.
val trash : t -> B0_zero.Trash.t
trash m
is m
's trash.
val win_exe : t -> bool
win_exe m
is true
if we spawn windows executables. This affects tool lookups. Defaults to Sys.win32
.