dowel

ADR-0018: The output stage is a backend layer over one neutral build graph

Status: Accepted

Context

Planning ends with an action graph: a list of process launches with their inputs, outputs, and ordering. Everything after that is a separate concern — who runs those processes.

That separation was not expressed anywhere. exec::run matched on an Executor enum with two arms; one arm wrote build.ninja and started ninja, the other walked the actions in process. Both reached into Plan directly, so every fact a runner needed was reachable whether or not it was part of any stated contract, and adding a third way to run a build meant editing the match rather than adding a file.

Two things were wanted, and neither had a place to go.

The second is the one that decides the design. A format that merely describes what a build would do rots, because nothing fails when a fact stops being written into it. A format that a real backend consumes cannot rot, because the build stops working.

Decision

The output stage is a layer. Between the planner and any runner sits one neutral value, BuildGraph, and every backend consumes only that.

Four backends ship:

Name What it does
ninja writes build.ninja and runs ninja. The default where ninja exists
direct runs the steps in process, sequentially, comparing mtimes and reading depfiles
make writes Makefile and runs make
graph writes build-graph.json and stops

graph is the connection point for a backend that is not in this repository. Its output is the serialization of BuildGraph itself, with a format name and an integer version, and it can be parsed back into an equal BuildGraph.

dowel graph --kind=action --format=json prints that same document. There is one JSON description of an action graph, not two, and the one that exists is the one the backends run on.

--executor is renamed to --backend. The old spelling is refused with a message naming the new one rather than silently accepted, because the set of values it takes has changed.

Consequences