Skip to content

punix build

Type-check + realise every package module declared in FILE. Batch-resilient: a per-package failure is reported, the rest still build.

Synopsis

punix build FILE [--dry-run] [--store-root R] [--scenario S]

Flow

FILE → parse → check → close build-closure (E7)
                       for each package module:
                          canonical derivation → hash → store path
                          if path exists & valid: cache hit
                          else: realise (sandbox, prepare_source, run recipe)
                                writes <store_path>/built marker last

Output

→ building 3 package(s) into /Users/you/.punix/store
  per-package build logs: /Users/you/.punix/punix-build-logs/<module>.log
[1/3] Caddy
[2/3] Forgejo
[3/3] Postgres
  [1] realise Caddy
  [2] cache hit Forgejo
  [3] realise Postgres
ok  Caddy   /Users/you/.punix/store/a1b2…-caddy-2.9.1
ok  Forgejo /Users/you/.punix/store/c3d4…-forgejo-9.0.3
FAIL    Postgres    build failed (rc=2) — last stderr: ld: cannot find -lreadline (log: ./punix-build-logs/Postgres.log)
3 packages: 2 ok, 1 failed

Per-package lines:

  • ok\tMODULE\tPATH — successful realise (or cache hit).
  • FAIL\tMODULE\tDETAIL — failure; DETAIL ends with (log: PATH) pointing at the full stderr/stdout dump.

The format is tab-separated so you can pipe punix build into other tools (awk, cut, jq via a quick transform).

Exit codes

  • 0 — every package succeeded.
  • 1 — whole-program defect: located [E#] before any realise ran (parse/type/build-closure error). No packages were built.
  • 3 — at least one package realise failed. Other packages may have succeeded; see the per-line output.

Flags

  • --dry-run — use RealiseDryRun: compute every store path, don't build. Prints dry-run\tMODULE\tPATH. Useful for scripts that need store paths without paying the build cost; also the underpinning of punix store path.
  • --store-root PATH — store location. Default ~/.punix/store.
  • --scenario NAME — evaluate under the named scenario (see Modules).
  • --only MODULE[,MODULE2,...] — limit the build to a list of modules and their transitive deps.
  • --bootstrap MODE — bootstrap trust policy: fast / seeded / source / bootstrappable. Default: $PUNIX_BOOTSTRAP_MODE or seeded. Violations abort the run before any recipe builds — see install --bootstrap or run punix help bootstrap.

Build closure pre-pass — [E7]

Before any realise runs, the build-closure pass walks the package graph and verifies host-side prerequisites:

  • Every deps = […] reference resolves to a module that itself has a recipe ([E7] if it doesn't).
  • The closure is acyclic ([E7] if a cycle is detected).

This catches the largest class of "I forgot to declare X" errors before any build runs — the operator gets one located error instead of a partial-build with one module's stderr to read.

Recipes and identity

recipe = "std.autotools" in PCL maps to a recipe under src/punix/realise/recipes_lib/std/autotools.py. The recipe's identity (name + content-hash of its .py + sibling files) is part of the canonical derivation — edit the recipe ⇒ every package using it gets a new store path ⇒ rebuild. The conformance suite has a dedicated test for this against a recipe-hook edit.

Recipes for the shipped registry.

Examples

# Build a single PCL file
punix build hello.pcl

# Build everything under pkgs/, prod scenario
punix build pkgs/ --scenario Prod

# Compute store paths without building (cheap; powers IDE integration)
punix build hello.pcl --dry-run

# Different store root for hermetic testing
punix build hello.pcl --store-root /tmp/test-store