docs: catch the build docs up with the package split

Review findings: the plan doc claimed no implementation existed, had the
domain dependency order wrong (kernel depends on protocol; device on
kernel + protocol + csv), never placed the three shared contracts, and
named a nonexistent new-driver-checklist.md. Its status now records the
implemented phases (and the deliberate pci-bus-first pilot), the target
shape carries the contract placements and the path-dependency-only
constraint on the kernel package's out-of-root abi export, and the
execution notes describe the post-pilot build for whichever session
runs the remaining waves. README's repo layout gains build-support/
and the packages-note; driver-model, threading, system-requirements,
and the two display plan docs stop citing root build.zig for recipe
facts that now live in build-support.
This commit is contained in:
Daniel Samson
2026-07-26 23:12:14 +01:00
parent 15575960bd
commit 902e4a0a9e
8 changed files with 101 additions and 56 deletions
+57 -33
View File
@@ -1,10 +1,17 @@
# Plan: packages — hierarchical builds for libraries and binaries
**Status: proposed, awaiting sign-off. No implementation yet.**
**Status: in progress.** Implemented on branch `claude/build-packages-plan-174144`:
phase 0 (`build-support`), phase 1 (all six library domains as packages, root as
the pilot consumer), and the first phase-2 binary package — `pci-bus`, a
deliberate deviation from wave A's two-small-services opener, because a driver
with per-binary extras (device-manager-protocol, pci-class) exercises the
template harder than a plain service. Every phase landed green (unit tests, the
QEMU suite at parity with main, boot-image file list unchanged). Remaining:
waves B–D of phase 2, then phase 3.
## Why
`build.zig` is ~1,250 lines and grows by three hand-written stanzas per binary;
`build.zig` was ~1,250 lines, growing by three hand-written stanzas per binary;
at a driver per device family that does not scale. More fundamentally: in one
monolithic build every binary compiles against library *source*, so a library
interface break is silently absorbed by whoever edits everything in one commit —
@@ -21,18 +28,27 @@ know where anything lives.
## Target shape
```
build-support/ package: the danos build API (userBinary(), targets, default imports)
library/kernel/ package "kernel": modules ipc, service, memory, process, logging, time, ...
library/device/ package "device": modules driver, pci, usb-abi, model, ... (depends on kernel)
build-support/ package: the danos build API (userBinary(), defaultImports(), targets)
library/kernel/ package "kernel": modules abi, ipc, service, memory, process, logging, time, ... (depends on protocol)
library/device/ package "device": modules driver, pci, usb-abi, model, ... (depends on kernel, protocol, csv)
library/protocol/ package "protocol": the wire protocols
library/client/ package "client" (depends on kernel, protocol)
library/csv/ package "csv"
library/xkeyboard-config/ package "xkeyboard-config"
system/services/<name>/ one package per binary: ~10-line build.zig + zon
system/services/<name>/ one package per binary: ~15-line build.zig + zon
system/drivers/<name>/ one package per binary
build.zig (root) orchestrator: dependency() per binary, image assembly, QEMU, test steps
```
The three shared contracts: `boot-handoff` stays a root module (only the
loader↔kernel pair speaks it); `abi` is exported by the kernel package from
`../../system/abi.zig` (the source stays with the kernel; userspace's one view
of it lives in the package, so every consumer names the same module instance);
`device-abi` is exported by device. Reaching outside the package root means the
kernel package is valid only as an in-repo path dependency — it could never be
fetched by hash — which is fine: path dependencies are the only way any of
these packages is consumed.
Rules:
- **Dependencies are declared at domain level** (a binary's zon names `kernel`,
@@ -72,41 +88,47 @@ the freestanding target setup, and the default-import wiring into the
`build-support` package. Root build consumes it; nothing else moves. This is
the cross-cutting-change home, so it lands first.
**Phase 1 — library domains become packages.** In dependency order: `kernel`
(no deps) → `csv`, `protocol` → `device`, `client` → `xkeyboard-config`. Each
gets build.zig + zon + a standalone test step. The root build swaps its
`createModule` calls for `b.dependency("<domain>").module("<name>")`. **No
binary moves in this phase** — the root build is the pilot consumer, which
proves the packages without touching 30 binaries.
**Phase 1 — library domains become packages.** In dependency order: `protocol`
and `csv` (the roots) → `kernel` (depends on protocol: file-system speaks
vfs-protocol) → `device`, `client`; `xkeyboard-config` stands alone. Each gets
build.zig + zon + a standalone test step (client's is empty until its modules
grow host tests — kept for uniformity, since the root aggregate depends on
every domain's test step). The root build swaps its `createModule` calls for
`b.dependency("<domain>").module("<name>")`. **No binary moves in this phase**
— the root build is the pilot consumer, which proves the packages without
touching 30 binaries.
**Phase 2 — binaries become packages, in waves.** Wave A: two small services
(e.g. logger, display-demo) to shake out the template. Wave B: remaining
services. Wave C: drivers. Wave D: test fixtures. Root build shrinks to
orchestration per wave.
**Phase 2 — binaries become packages, in waves.** The template was shaken out
by the pci-bus pilot (see Status). Wave A: services. Wave B: the remaining
drivers. Wave C: test fixtures. Root build shrinks to orchestration per wave.
**Phase 3 — root cleanup.** Split what remains of the root build into
`build/images.zig`, `build/qemu.zig`, imported by a short root `build.zig`.
**Afterwards** (outside this plan): the intel-uhd-graphics-750 driver is
(re)created as a greenfield package — the new-driver checklist's step 2 gets
rewritten against the package template at that point.
(re)created as a greenfield package — the "Adding a driver" checklist's build
step (docs/device-driver-development/devices-csv.md, step 1) gets rewritten
against the package template at that point.
## Execution notes (for whichever session runs this)
## Execution notes (for whichever session runs the remaining waves)
Anchors in today's root `build.zig` (~1,250 lines):
Anchors in the root `build.zig` as it stands after the pilot:
- `addUserBinary` / `addThreadedUserBinary` / `addUserBinaryImpl` and the
`default_imports` plumbing start around line 63 — this is what phase 0
extracts into `build-support`. `addUserBinaryImpl` also wires the `start`
root shim from `default_imports`; that trick must survive the move.
- `programModule(<exe>).addImport(...)` calls (search `programModule`) are the
per-binary extra imports — the data for each binary's future build.zig.
- The shared recipe lives in `build-support/build.zig`: `userBinary` (domains
form, what binary packages call), `userBinaryFromImports` (the underlying
recipe root's stanzas still use), and `defaultImports` — the ONE list of
default modules; root and the packages both draw from it. The `start` root
shim and `user.ld` are named through the kernel package (Dependency.path).
- `programModule(<exe>).addImport(...)` calls in root (search `programModule`)
are the per-binary extra imports — the data for each binary's future
build.zig. `system/drivers/pci-bus/build.zig` is the template to copy.
- The boot-tree array (search `"etc/init.csv"` or `.getEmittedBin()`) is the
image file list — the authoritative before/after comparison target.
- Protocol/module definitions (search `createModule`) map module names to
`library/...` source paths — the data for each domain package's exports.
- The QEMU size-check test hardcodes source paths (search
`virtio-gpu-protocol.zig` near line 1149) — moves with phase 2 wave C.
image file list — the authoritative before/after comparison target. A
converted binary's stanza becomes
`b.dependency("<name>", .{}).artifact("<name>")` plus a zon entry.
- The QEMU size-check tests hardcode source paths (search
`virtio-gpu-protocol.zig` in the root test list) — they move with their
binaries' waves.
Verification per phase:
@@ -128,5 +150,7 @@ docs/coding-standards.md (kebab-case names, no abbreviations), and the
upgrade lands separately, never mid-phase.
- The QEMU size-check tests hardcode source paths (e.g. virtio-gpu protocol
struct sizes in the root build) — phase 2 wave C must carry those along.
- Doc updates ride each phase: drivers.md, new-driver-checklist.md, and
docs/README.md reference build steps that will change shape.
- Doc updates ride each phase: docs/README.md (repo layout + source map),
docs/device-driver-development/devices-csv.md ("Adding a driver", step 1),
and the docs that cite the build recipe (driver-model.md, threading.md,
system-requirements.md) reference build shapes that keep changing.