Core Concepts

Rules & Targets

In Netsuke, a Rule defines how something is built, while a Target defines what is built. Understanding the relationship between these two concepts is key to mastering the build graph.

The Build Graph

Netsuke constructs a Directed Acyclic Graph (DAG) where nodes are targets and edges are dependencies. When you run netsuke build app, the system:

  1. Parses the manifest for the requested target.
  2. Identifies all transitive dependencies.
  3. Constructs the action graph (commands to run).
  4. Executes actions in parallel, topologically sorted.
Static graph first Rules and targets are resolved before execution begins. That is what lets Netsuke hand a concrete plan to Ninja.
app
main.o
utils.o
compile
Dependency Graph Visualization

Defining reusable rules

Core

A Netsuke rule is a reusable command or script template. Targets can reference the rule by name, add their own inputs, and keep duplicated shell out of the manifest.

Netsukefile
rules:
  - name: "compile"
    command: "gcc -c {{ ins }} -o {{ outs }}"
    description: "Compiling {{ outs }}"

  - name: "link"
    command: "gcc {{ ins }} -o {{ outs }}"

targets:
  - name: "app"
    rule: "link"
    sources: ["main.o", "utils.o"]
1

Rule name

Targets refer to a rule by name. Keep names short and literal.

2

Recipe field

A rule carries exactly one recipe: command or script.

3

Target wiring

Targets supply the concrete sources, outputs, and optional per-target variables.

Target fields

Netsuke target references are plain names and paths. The important distinction is between normal dependencies, order-only dependencies, and phony or always-run targets.

Field Example Meaning
sources ["main.o", "utils.o"] Build inputs whose changes should trigger a rebuild.
deps ["build"] Named dependencies that must be built first and do trigger rebuilds.
order_only_deps ["build-dir"] Dependencies that must run first but do not trigger rebuilds when they change.
phony / always true / false Control whether a target is logical only or should run every time.

CLI Tip: Querying Targets

Use netsuke graph or netsuke generate to inspect the plan without running a full build.

$ netsuke graph --output build.dot

Outputs & Determinism

Deterministic Output

Netsuke generates the same static plan from the same manifest and environment. Deterministic manifests make rebuild decisions easier to explain and verify.

  • Consistent across machines
  • Bit-for-bit identical builds
  • Byte-for-byte stable build.ninja output

Validation boundaries

The reviewed docs emphasize validation, deterministic plan generation, and explicit shell escaping. Treat those as the documented safety story.

Read Security Model →

Clean and incremental builds

Netsuke does not execute the build itself. It compiles the manifest into a static plan and hands that plan to Ninja, which decides what actually needs to run. How granular the rules and targets are therefore determines how much work an incremental build can skip.

Clean build

With no outputs on disk, every edge in the plan is out of date, so Ninja runs every command once. This is the upper bound on build work: the whole graph, in dependency order.

  • Each target's recipe executes exactly once
  • Ordering follows sources, deps, and order_only_deps

Incremental build

Ninja walks the same static graph and compares each edge's declared inputs against its outputs. Only the commands whose inputs changed are re-run, along with whatever depends on their results.

  • Unchanged subgraphs are skipped, not re-checked command by command
  • order_only_deps constrain ordering without forcing a rebuild
  • Targets marked always run regardless of input state

Where the cost sits

All the dynamic work happens once, at generation time. Jinja expansion, foreach, when, and glob resolution are all evaluated before build.ninja is written. The plan Ninja receives contains no conditionals, no string manipulation, and no filesystem discovery, so the execution phase spends its time on dependency checking and on running commands.

Generation is deterministic: the same manifest and environment produce a byte-for-byte identical build.ninja. An unchanged manifest therefore yields an unchanged plan, and a build with nothing to do collapses to a dependency check with no recipes executed.