build: exact per-binary imports — the pre-wired default set is gone

Every binary's build.zig now names precisely the modules its source
imports (derived by scanning each artifact's sources, transitively
through same-directory files), and its zon carries only the domains
those come from — kernel stays implicit (the root shim + link script
live there). build-support's userBinary resolves each name through one
module-to-domain table (module_homes); Domains/domains()/defaultImports
and the raw recipe entry point are deleted. An undeclared @import is a
compile error (verified: injecting @import("xkeyboard-config") into
logger fails with 'no module named ... available within module
program'), and e.g. xkeyboard-config now appears in exactly two
manifests — the two keyboard drivers. Availability never bloated the
emitted binaries (Zig compiles only what a program imports); this makes
the declared interfaces honest. Production and -Dtest-case manifests
byte-identical; all build variants and standalone package builds
green.
This commit is contained in:
Daniel Samson
2026-07-30 06:42:31 +01:00
parent c621b649f6
commit 4476208361
60 changed files with 401 additions and 450 deletions
+23 -15
View File
@@ -11,7 +11,9 @@ root split into `build/images.zig` + `build/qemu.zig`; the root `build.zig` is
tests, the QEMU suite at parity with main, boot-image file list unchanged.
The `lazyDependency` payoff (What-this-buys #4) is in too: the /test fixtures
and the unselected discovery package are lazy — a build loads and compiles
only what it ships.
only what it ships. And imports are exact: the pre-wired default set is gone;
every binary names precisely the modules its source imports and carries only
those domains in its manifest (rule 1 below).
## Why
@@ -55,10 +57,15 @@ these packages is consumed.
Rules:
- **Dependencies are declared at domain level** (a binary's zon names `kernel`,
`device`), **imports stay module-level** (`@import("pci")`). `build-support`
pre-wires the core set every binary uses (service, ipc, memory, process,
logging); per-binary build files name only extras.
- **Imports are exact and per binary.** A binary's build.zig names precisely
the modules its source `@import`s — the moral equivalent of a C file's
include list — and its zon names only the domains those modules come from
(plus `build-support` and `kernel`, which is implicit in every binary: the
root shim and user link script live there). Nothing is pre-wired: an
undeclared `@import` is a compile error, and build-support's one
module-to-domain table (`module_homes`) resolves each name. Availability
never meant bloat — Zig only compiles what a program actually imports — but
exactness makes the declared interface honest and machine-checked.
- **Modules export source, not artifacts** — each consumer compiles libraries
with its own flags, so per-binary optimization choices keep working; Zig's
cache deduplicates.
@@ -71,9 +78,10 @@ Rules:
## What this buys
1. Library interfaces become machine-checked: a consumer can only import what a
domain exports, and each domain's zon declares what it needs (claim-before-
touch, applied to source).
1. Library interfaces become machine-checked: a consumer can only import what
it declared — per binary, down to the single module — and each domain's zon
declares what it needs (claim-before-touch, applied to source). A keyboard
driver carries `xkeyboard-config` in its manifest; nothing else does.
2. Each library domain gets a standalone `zig build test` — runtime-library
stability testing in isolation.
3. Adding a binary = adding a directory (source + two small files), not editing
@@ -124,15 +132,15 @@ rewritten against the package template.
## Execution notes (the finished shape)
- The shared recipe lives in `build-support/build.zig`: `userBinary` (what
every binary package calls), `userBinaryFromImports` (the underlying
recipe), and `defaultImports` — the ONE list of default modules. The `start`
root shim and `user.ld` are named through the kernel package
(Dependency.path).
every binary package calls, resolving each named import through the
`module_homes` table) and `programModule` (for per-binary addOptions
modules). The `start` root shim and `user.ld` are named through the kernel
package (Dependency.path).
- Adding a binary = adding a directory with source + a ~15-line build.zig +
zon (copy any existing binary package, e.g.
`system/drivers/pci-bus/build.zig`), then one dependency + one bundled
entry in the root build.zig and one zon line. Per-binary extras go through
`build_support.programModule(exe).addImport(...)` inside the package.
`system/drivers/pci-bus/build.zig`) listing exactly the modules the source
imports and the domains they come from, then one dependency + one bundled
entry in the root build.zig and one zon line.
- The boot-tree array in the root (search `"etc/init.csv"` or
`.getEmittedBin()`) is the image file list — the authoritative comparison
target for any future build change.