3. Gather and the cook step
Real builds have a shape: take input files, transform each, produce outputs, don't redo the transform if nothing changed. gather and cook describe exactly…
Real builds have a shape: take input files, transform each, produce outputs,
don’t redo the transform if nothing changed. gather and cook describe
exactly that.
gather declares the input file set. Patterns are quoted globs; a
recipe may have at most one gather line. Combine includes and excludes
(exclude is ! immediately followed by a quoted pattern):
recipe lib gather "src/*.c" !"src/scratch.c" cook "build/libmath.a" { ar rcs $<out> $<in> }The cook step produces declared outputs from a command. Its shape depends
on the output pattern:
-
One-to-one: the output pattern contains an input accessor, so cook iterates one unit per input (runnable in parallel):
recipe compilegather "src/*.c"cook "build/$<in.stem>.o" { gcc -c $<in> -o $<out> } -
Many-to-one: no input accessor in the output, so one unit consumes every input (
$<in>expands to all of them, space-separated):recipe archivegather "build/*.o"cook "build/libmath.a" { ar rcs $<out> $<in> }
Chaining steps into a pipeline. A recipe can hold several cook steps, and
each step’s $<in> is the previous step’s output. That’s how you express a
pipeline in one recipe, with every step cached independently:
recipe assets gather "images/*.png" cook "build/$<in.stem>.webp" { cwebp -q 80 $<in> -o $<out> } # one WebP per image cook "build/manifest.txt" { printf '%s\n' $<in> > $<out> } # $<in> = those WebPsEdit one image and cook re-encodes exactly that WebP, then rebuilds the manifest that consumes it; nothing else runs.
Placeholders
Inside output patterns and { ... } bodies, $<...> placeholders expand. They
were chosen so they never collide with shell syntax: $VAR, ${VAR}, brace
expansion, awk '{print $1}' all pass through untouched; only $<...> is
substituted. A typo’d placeholder is a load-time error, never a silent empty
string.
| Placeholder | Meaning |
|---|---|
$<in> | the current input (one item in a one-to-one step; all of them in many-to-one) |
$<out> | the current output |
$<out_1>, $<out_2> | the Nth declared output, for multi-output steps |
$<in.stem> | a path accessor on the input: stem, name, ext, or dir |
$<other> | another recipe’s outputs, space-joined; also records the dependency edge (see Connecting recipes) |
The four path accessors on src/sub/foo.tar.gz: stem → foo.tar, name →
foo.tar.gz, ext → .gz, dir → src/sub.
Multiple outputs from one command are declared with multiple patterns and referenced by index:
recipe wasm gather "src/lib.rs" cook "out/app.js" "out/app.wasm" { wasm-pack build cp pkg/app.js $<out_1> cp pkg/app.wasm $<out_2> }