WorkMap
Context
Database
Exec
Logger
Prep
Work
WorkKey
of core::cmp::Eq for WorkKey
of to_bytes::IterBytes for WorkKey
for WorkKey
for Database
for Logger
for Context
for Prep
for Work<T>
digest
digest_file
unwrap
WorkMap
type WorkMap = LinearMap<WorkKey, ~str>
Context
struct Context {
db: @Database,
logger: @Logger,
cfg: @json::Object,
freshness: LinearMap<~str, @pure fn(&str, &str) -> bool>,
}
Database
struct Database {
a: (),
}
Exec
struct Exec {
discovered_inputs: WorkMap,
discovered_outputs: WorkMap,
}
Logger
struct Logger {
a: (),
}
Prep
struct Prep {
ctxt: @Context,
fn_name: ~str,
declared_inputs: WorkMap,
declared_outputs: WorkMap,
}
Work
struct Work <T: Owned>{
prep: @mut Prep,
res: Option<Either<T, PortOne<(Exec, T)>>>,
}
WorkKey
struct WorkKey {
kind: ~str,
name: ~str,
}
This is a loose clone of the fbuild build system, made a touch more generic (not wired to special cases on files) and much less metaprogram-y due to rust's comparative weakness there, relative to python.
It's based around imperative bulids that happen to have some function calls cached. That is, it's just a mechanism for describing cached functions. This makes it much simpler and smaller than a "build system" that produces an IR and evaluates it. The evaluation order is normal function calls. Some of them just return really quickly.
A cached function consumes and produces a set of works. A work has a name, a kind (that determines how the value is to be checked for freshness) and a value. Works must also be (de)serializable. Some examples of works:
kind name value |
---|
cfg os linux |
file foo.c |
url foo.com |
Works are conceptually single units, but we store them most of the time in maps of the form (type,name) => value. These are WorkMaps.
A cached function divides the works it's interested up into inputs and outputs, and subdivides those into declared (input and output) works and discovered (input and output) works.
A declared input or output is one that is given to the workcache before any work actually happens, in the "prep" phase. Even when a function's work-doing part (the "exec" phase) never gets called, it has declared inputs and outputs, which can be checked for freshness (and potentially used to determine that the function can be skipped).
The workcache checks all works for freshness, but uses the set of discovered outputs from the previous exec (which it will re-discover and re-record each time the exec phase runs).
Therefore the discovered works cached in the db might be a mis-approximation of the current discoverable works, but this is ok for the following reason: we assume that if an artifact A changed from depending on B,C,D to depending on B,C,D,E, then A itself changed (as part of the change-in-dependencies), so we will be ok.
Each function has a single discriminated output work called its result. This is only different from other works in that it is returned, by value, from a call to the cacheable function; the other output works are used in passing to invalidate dependencies elsewhere in the cache, but do not otherwise escape from a function invocation. Most functions only have one output work anyways.
A database (the central store of a workcache) stores a mappings:
(fn_name,{declared_input}) => ({declared_output},{discovered_input}, {discovered_output},result)
core::cmp::Eq
for WorkKey
eq
fn eq(__other: &WorkKey) -> bool
ne
fn ne(__other: &WorkKey) -> bool
to_bytes::IterBytes
for WorkKey
iter_bytes
fn iter_bytes(lsb0: bool, f: to_bytes::Cb)
WorkKey
new
fn new(kind: &str, name: &str) -> WorkKey
Database
prepare
fn prepare(_fn_name: &str, _declared_inputs: &const WorkMap,
_declared_outputs: &const WorkMap) ->
Option<(WorkMap, WorkMap, ~str)>
cache
fn cache(_fn_name: &str, _declared_inputs: &WorkMap,
_declared_outputs: &WorkMap, _discovered_inputs: &WorkMap,
_discovered_outputs: &WorkMap, _result: &str)
Logger
info
fn info(i: &str)
Context
new
fn new(db: @Database, lg: @Logger, cfg: @json::Object) -> Context
prep
fn prep<T: Owned Encodable<json::Encoder> Decodable<json::Decoder>>(fn_name:
&str,
blk:
&fn
(@mut Prep)
->
Work<T>)
-> Work<T>
Prep
declare_input
fn declare_input(kind: &str, name: &str, val: &str)
declare_output
fn declare_output(kind: &str, name: &str, val: &str)
is_fresh
fn is_fresh(cat: &str, kind: &str, name: &str, val: &str) -> bool
all_fresh
fn all_fresh(cat: &str, map: WorkMap) -> bool
exec
fn exec<T: Owned Encodable<json::Encoder> Decodable<json::Decoder>>(blk:
~fn
(&Exec)
->
T)
-> Work<T>
Work<T>
new
fn new(p: @mut Prep, e: Either<T, PortOne<(Exec, T)>>) -> Work<T>
digest
fn digest<T: Encodable<json::Encoder> Decodable<json::Decoder>>(t: &T) -> ~str
digest_file
fn digest_file(path: &Path) -> ~str
unwrap
fn unwrap<T: Owned Encodable<json::Encoder> Decodable<json::Decoder>>(w:
Work<T>)
-> T