In part 2 I introduced an example goaljobs script that can rebuild a set of packages in Fedora in the right order.
It’s time to take a closer look at targets — the promise that you make that some condition will be true by the time a goal has run.
In the Fedora rebuild script the goal targets looked like this:
let goal rebuilt pkg = target (koji_build_state (fedora_verrel pkg branch) == `Complete); ...
koji_build_state is a regular function. It’s implemented using the
koji buildinfo command line tool for querying the Koji build system. (The koji command line tool is annoyingly hard to automate, but as we’ve got a complete programming language available — not just bash — the implementation of
koji_build_state is tedious and long, but doable).
Querying Koji takes a few seconds and we don’t want to do it every time we check a goal. Goaljobs offers a feature called “The Memory” which lets you memoize functions. “The Memory” is just a fancy name for a key/value store which is kept in
~/.goaljobs-memory and persists across goaljobs sessions:
let koji_build_state verrel = let key = sprintf "koji_build_complete_%s" verrel in if memory_exists key then `Complete else ( (* tedious code to query koji *) if state == `Complete then memory_set key "1"; state )
With strategic use of memoization, evaluating goaljobs goals can be very fast and doesn’t change the fundamental contract of targets.
Finally in this part: a note on how targets are implemented.
A target is a boolean expression which is evaluated once near the beginning of the goal. If it evaluates to true at the beginning of the goal then the rest of the goal can be skipped because the goal has already been achieved / doesn’t need to be repeated.
And since targets are just general expressions, they can be anything at all, from accessing a remote server (as here) to checking the existence of a local file (like make). As long as something can be tested quickly, or can be tested slowly and memoized, it’s suitable to be a target.