cookA build system with a Lua heart

Build artifacts just like grandma used to make.

Cargo, CMake, pnpm, codegen, and a scripts/ directory nobody will admit to writing, each with its own idea of what's up to date. Cook is one kitchen for the whole crew: one dependency graph, one content-addressed cache. You describe artifacts in a Cookfile; cook runs exactly the work that changed.

Cap · one comment in a shared package

~1s Turborepo: ~47s

  • Turborepo rebuilt 7 of 11 tasks for a comment that compiled away.
  • Cook rebuilt the package, saw the same artifact, and stopped the cascade.
  • Every number reproduced by bench/compare.sh. Run it yourself.
See the benchmark →
From declaration to executionIllustrative simulation · 18 seconds

A Cookfile lowers to Lua and registers its units. Registration becomes one graph. Every ready unit checks the cache first; only the misses reach the workers.

examples/02-pipelineCookfile
recipe report
gather "chapters/*.txt"
# fan-out: one count per chapter
cook "build/counts/$<in.stem>.count" {
wc -w < $<in> | tr -d ' ' > $<out>
}
# many-to-one: every count in
cook "build/total.txt" {
cat $<in> | paste -sd+ - | bc > $<out>
}
# one step, two outputs
cook "build/report.txt" "build/report.csv" {
...
}
~/examples/02-pipelineWatch the cache work
cook report
Cooked report/build/counts/one.count in 1ms
Cooked report/build/counts/three.count in 1ms
Cooked report/build/counts/two.count in 1ms
Cooked report/build/total.txt in 1ms
Cooked report/build/report.txt in 2ms
Finished in 11ms (5 nodes, 0 cached)
cook report
Cached report (5 nodes)
Finished in 1ms (5 nodes, all cached)
sed -i 's/moon/sun and stars/' chapters/two.txt
cook report
Cached report/build/counts/one.count
Cached report/build/counts/three.count
Rebuilding report/build/counts/two.count — input changed: chapters/two.txt
Cooked report/build/counts/two.count in 1ms
Rebuilding report/build/total.txt — input changed: build/counts/two.count
Cooked report/build/total.txt in 1ms
Rebuilding report/build/report.txt — input changed: build/total.txt
Cooked report/build/report.txt in 2ms
Finished in 18ms (5 nodes, 2 cached)

One kitchen for the whole crew

Cargo stays Cargo, pnpm stays pnpm. Their builds, your codegen and your scripts join one graph and share one cache.

Outputs shape the work

Put $<in.stem> in a target and the step fans out, one cached unit per input, across every core. A static target gathers. You never write the loop or the join.

Data shapes the build

Fan out over a JSON manifest with a probe. Add a record and exactly one new unit builds; delete one and cook sweeps the orphan.

You own the key

Every unit is keyed on what you declared and nothing else. Want the compiler in the key? seal compiler. Keys travel across machines by default.

No mystery misses

cook why prints every determinant behind every key, hit or miss. cook cache verify re-runs cached work and catches any byte of divergence.

The store is a directory

Point your team and CI at one path and they reuse each other's artifacts. No server to run.

cook-dhewm3 · 427 nodes

Doom 3, compiler to demo map

One graph builds the engine, the game plugins and the map tools, then uses the dmap it just built to compile a playable map. Clone, cook, play.

CookfileLua when shell isn’t enough
recipe upper
gather "src/*.txt"
cook "build/$<in.stem>.txt" >{
local text = fs.read(input)
fs.write(output, text:upper())
}
Why not ___?

Turborepo?

Change a shared package and Turbo rebuilds everything downstream. Cook checks whether the artifact actually changed, and stops.

Why not ___?

CMake?

Great inside a native build; everything else becomes custom commands around it. In cook, C, pnpm and codegen are all ordinary units.

Why not ___?

Bazel?

Bazel asks you to rewrite your build in its terms. Cook wraps the tools you already run, with a cache key you own and can audit.

Why it exists

Cook was born on a project that used make and just side by side. make owned the artifacts and the incremental builds; just owned the commands people actually wanted to run. Both were excellent at their jobs, and every new piece of work came with the same question: Makefile or justfile?

Cook is the one answer. just’s recipe ergonomics, make’s artifact graph, content-addressed reuse across the whole repository, and Lua underneath everything. Every Cookfile lowers to Lua before it runs, and cook emit-lua shows you exactly what yours becomes.

~Heard enough?
curl -fsSL https://getcook.sh | sh
cook init
Created Cookfile
Created .gitignore
cook
Cooked build/build/hello.txt in 0ms
Finished in 11ms (1 nodes, 0 cached)
Source

LioraLabs/cook

One Rust binary with Lua 5.4 and LuaRocks bundled. Apache-2.0.

Ten runnable projects

Cook by example

From hello-cook to cache trust, each with a watch-the-cache-work moment.

Liora Labs

All software

ppu.toys, Git Mosaic, Canopy, and what's next.