← docs2 min read

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…

manual

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 compile
    gather "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 archive
    gather "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 WebPs

Edit 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.

PlaceholderMeaning
$<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>
}