build: the root's ship list becomes a declarative table
Each shipped binary was registered twice in the root build (a dependency/artifact line plus a bundled row repeating its name and path). The uniform rows — dependency name = artifact name = boot-path leaf — collapse into production_ship, one line per binary via the service()/driver()/driverArtifact() helpers; only the genuinely non-uniform entries stay spelled out (init's -Dserial, the -Ddiscovery pick, the /etc data files). Selecting what goes into a build is now selecting table rows, and an unselected package's build file is never loaded. The boot manifest is set-identical; its order shifts (discovery and the /etc entries move) — every lookup is by name, and the boot-order QEMU smoke passes.
This commit is contained in:
@@ -96,6 +96,47 @@ fn addKernel(
|
|||||||
return exe;
|
return exe;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// One row of the production ship table: which package, which of its
|
||||||
|
/// artifacts, and the FHS boot path. For most binaries all three share one
|
||||||
|
/// name; the helpers below make a row from just that name.
|
||||||
|
const ShipRow = struct { path: []const u8, package: []const u8, artifact: []const u8 };
|
||||||
|
|
||||||
|
fn service(comptime name: []const u8) ShipRow {
|
||||||
|
return .{ .path = "system/services/" ++ name, .package = name, .artifact = name };
|
||||||
|
}
|
||||||
|
fn driver(comptime name: []const u8) ShipRow {
|
||||||
|
return .{ .path = "system/drivers/" ++ name, .package = name, .artifact = name };
|
||||||
|
}
|
||||||
|
/// An extra artifact of a multi-binary driver package (ps2-bus, usb-hid),
|
||||||
|
/// bundled at its own flattened /system/drivers path.
|
||||||
|
fn driverArtifact(comptime package: []const u8, comptime artifact: []const u8) ShipRow {
|
||||||
|
return .{ .path = "system/drivers/" ++ artifact, .package = package, .artifact = artifact };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The production ship table — what a plain `zig build` image contains,
|
||||||
|
/// beyond the specials the build fn adds around it (init, discovery, the
|
||||||
|
/// /etc data files; the /test fixtures join only under -Dtest-case).
|
||||||
|
/// Selecting what goes into a build = selecting rows: a package in no row is
|
||||||
|
/// not just unshipped, its build file is never even loaded
|
||||||
|
/// (docs/build-packages-plan.md).
|
||||||
|
const production_ship = [_]ShipRow{
|
||||||
|
service("fat"),
|
||||||
|
service("display"),
|
||||||
|
service("display-demo"),
|
||||||
|
service("device-manager"),
|
||||||
|
service("input"),
|
||||||
|
service("logger"),
|
||||||
|
driver("pci-bus"),
|
||||||
|
driver("ps2-bus"),
|
||||||
|
driverArtifact("ps2-bus", "ps2-keyboard"),
|
||||||
|
driverArtifact("ps2-bus", "ps2-mouse"),
|
||||||
|
driver("usb-xhci-bus"),
|
||||||
|
driverArtifact("usb-hid", "usb-hid-keyboard"),
|
||||||
|
driverArtifact("usb-hid", "usb-hid-mouse"),
|
||||||
|
driver("usb-storage"),
|
||||||
|
driver("virtio-gpu"),
|
||||||
|
};
|
||||||
|
|
||||||
pub fn build(b: *std.Build) void {
|
pub fn build(b: *std.Build) void {
|
||||||
ensureZigVersion();
|
ensureZigVersion();
|
||||||
|
|
||||||
@@ -211,48 +252,27 @@ pub fn build(b: *std.Build) void {
|
|||||||
const exe = addKernel(b, kernel_target, optimize, kernel_modules, test_case, serial);
|
const exe = addKernel(b, kernel_target, optimize, kernel_modules, test_case, serial);
|
||||||
const exe_serial = addKernel(b, kernel_target, optimize, kernel_modules, test_case, true);
|
const exe_serial = addKernel(b, kernel_target, optimize, kernel_modules, test_case, true);
|
||||||
|
|
||||||
// --- the user-space binaries, every one of them a package ---
|
// --- what ships: the boot tree ---
|
||||||
// Binary packages (docs/build-packages-plan.md, phase 2): each binary
|
// Every user binary and its FHS home on the boot volume. There is no packed
|
||||||
// builds itself against the domain packages via build-support's shared
|
// ramdisk artifact any more: make-fat-image.py lays each binary out at its
|
||||||
// recipe, started in ring 3 by the kernel's user-ELF loader like always;
|
// path on the image, and the EFI loader walks /system and /test at boot and
|
||||||
// the root build just takes artifacts for the boot image. init receives
|
// builds the in-RAM initial_ramdisk table from the trees — the volume's file
|
||||||
// the root's -Dserial as a dependency option (its liveness heartbeat is a
|
// structure is the single source of truth. Entry names (and hence argv[0] and
|
||||||
// serial/test-build diagnostic the QEMU harness asserts on; a flashable
|
// task names) are these paths with a leading slash.
|
||||||
// image leaves it out).
|
//
|
||||||
const init_exe = b.dependency("init", .{ .serial = serial }).artifact("init");
|
// The uniform rows live in `production_ship` (the table above `build`);
|
||||||
|
// spelled out here are only the genuinely non-uniform entries: init
|
||||||
// --- the rest of the boot tree: /system services and drivers ---
|
// (receives the root's -Dserial as a dependency option — its liveness
|
||||||
// Each is built by the same user-binary recipe and laid out at its FHS path on
|
// heartbeat is a serial/test-build diagnostic the QEMU harness asserts
|
||||||
// the boot volume (see `bundled` below). The EFI loader walks the tree at boot
|
// on; a flashable image leaves it out), discovery (the -Ddiscovery pick),
|
||||||
// and hands the kernel an in-RAM initial_ramdisk of it (system/initial-ramdisk.zig).
|
// and the /etc data files. Each binary builds itself against the domain
|
||||||
// (The /test fixtures are lazy dependencies, resolved further down only
|
// packages via build-support's shared recipe; the root just takes
|
||||||
// for a -Dtest-case build.)
|
// artifacts (docs/build-packages-plan.md).
|
||||||
// The drivers, each directory its own package: the PS/2 bus family (bus +
|
var bundled_list: std.ArrayListUnmanaged(images.BundledBinary) = .empty;
|
||||||
// keyboard + mouse from one package), the xHCI bus driver, the USB HID
|
bundled_list.append(b.allocator, .{
|
||||||
// class drivers, and USB mass storage. Their unit tests ride along.
|
.path = "system/services/init",
|
||||||
const ps2_bus_package = b.dependency("ps2-bus", .{});
|
.binary = b.dependency("init", .{ .serial = serial }).artifact("init").getEmittedBin(),
|
||||||
const ps2_bus_exe = ps2_bus_package.artifact("ps2-bus");
|
}) catch @panic("OOM");
|
||||||
const ps2_keyboard_exe = ps2_bus_package.artifact("ps2-keyboard");
|
|
||||||
const ps2_mouse_exe = ps2_bus_package.artifact("ps2-mouse");
|
|
||||||
const usb_xhci_bus_exe = b.dependency("usb-xhci-bus", .{}).artifact("usb-xhci-bus");
|
|
||||||
const usb_hid_package = b.dependency("usb-hid", .{});
|
|
||||||
const usb_hid_keyboard_exe = usb_hid_package.artifact("usb-hid-keyboard");
|
|
||||||
const usb_hid_mouse_exe = usb_hid_package.artifact("usb-hid-mouse");
|
|
||||||
const usb_storage_package = b.dependency("usb-storage", .{});
|
|
||||||
const usb_storage_exe = usb_storage_package.artifact("usb-storage");
|
|
||||||
// The FAT filesystem server and the display stack, each its own package
|
|
||||||
// (fat's and display's unit tests ride along in their packages).
|
|
||||||
const fat_package = b.dependency("fat", .{});
|
|
||||||
const fat_exe = fat_package.artifact("fat");
|
|
||||||
const display_package = b.dependency("display", .{});
|
|
||||||
const display_exe = display_package.artifact("display");
|
|
||||||
const display_demo_exe = b.dependency("display-demo", .{}).artifact("display-demo");
|
|
||||||
const virtio_gpu_package = b.dependency("virtio-gpu", .{});
|
|
||||||
const virtio_gpu_exe = virtio_gpu_package.artifact("virtio-gpu");
|
|
||||||
// The first binary package (docs/build-packages-plan.md, phase 2): pci-bus
|
|
||||||
// builds itself against the domain packages; the root build just takes the
|
|
||||||
// artifact for the boot image.
|
|
||||||
const pci_bus_exe = b.dependency("pci-bus", .{}).artifact("pci-bus");
|
|
||||||
// The discovery service: one swappable process per firmware
|
// The discovery service: one swappable process per firmware
|
||||||
// (docs/discovery.md), bundled under the neutral ramdisk name
|
// (docs/discovery.md), bundled under the neutral ramdisk name
|
||||||
// "discovery" so the device manager never learns which firmware it is on.
|
// "discovery" so the device manager never learns which firmware it is on.
|
||||||
@@ -269,59 +289,33 @@ pub fn build(b: *std.Build) void {
|
|||||||
.acpi => (b.lazyDependency("acpi", .{}) orelse @panic("system/services/acpi is missing")).artifact("discovery"),
|
.acpi => (b.lazyDependency("acpi", .{}) orelse @panic("system/services/acpi is missing")).artifact("discovery"),
|
||||||
.fdt => (b.lazyDependency("fdt", .{}) orelse @panic("system/services/fdt is missing")).artifact("discovery"),
|
.fdt => (b.lazyDependency("fdt", .{}) orelse @panic("system/services/fdt is missing")).artifact("discovery"),
|
||||||
};
|
};
|
||||||
const device_manager_exe = b.dependency("device-manager", .{}).artifact("device-manager");
|
bundled_list.append(b.allocator, .{
|
||||||
// The input service and its exercisers: the fan-out server, a hardware-free synthetic
|
.path = "system/services/discovery",
|
||||||
// source, and a subscriber that doubles as the `input` test's oracle. See docs/input.md.
|
.binary = discovery_exe.getEmittedBin(),
|
||||||
const input_exe = b.dependency("input", .{}).artifact("input");
|
}) catch @panic("OOM");
|
||||||
const logger_exe = b.dependency("logger", .{}).artifact("logger");
|
// The ship table: every uniform row, one line each.
|
||||||
|
for (production_ship) |row| {
|
||||||
// Every user binary and its FHS home on the boot volume. There is no packed
|
bundled_list.append(b.allocator, .{
|
||||||
// ramdisk artifact any more: make-fat-image.py lays each binary out at this
|
.path = row.path,
|
||||||
// path on the image, and the EFI loader walks /system and /test at boot and
|
.binary = b.dependency(row.package, .{}).artifact(row.artifact).getEmittedBin(),
|
||||||
// builds the in-RAM initial_ramdisk table from the trees — the volume's file
|
}) catch @panic("OOM");
|
||||||
// structure is the single source of truth. Entry names (and hence argv[0] and
|
}
|
||||||
// task names) are these paths with a leading slash. Test fixtures mirror their
|
// Data files, not binaries: packing them under /etc makes the kernel
|
||||||
// repo home: test/system/services/<name> in the source tree IS the boot path.
|
// auto-mount /etc as a read-only initrd tree (system/kernel/vfs.zig
|
||||||
// init's boot service list is data (/etc/init.csv). -Ddiagnose selects the
|
// setInitialRamdisk) — the device manager reads its registry and init its
|
||||||
// variant that omits the display stack (so the kernel's boot transcript stays
|
// service list with no filesystem service running. -Ddiagnose selects the
|
||||||
// on screen); both are bundled at the same /etc/init.csv path.
|
// init.csv variant that omits the display stack (so the kernel's boot
|
||||||
|
// transcript stays on screen); both bundle at the same /etc/init.csv path.
|
||||||
const init_csv_source = if (diagnose) "etc/init-diagnose.csv" else "etc/init.csv";
|
const init_csv_source = if (diagnose) "etc/init-diagnose.csv" else "etc/init.csv";
|
||||||
const production_bundled = [_]images.BundledBinary{
|
bundled_list.append(b.allocator, .{ .path = "etc/devices.csv", .binary = b.path("etc/devices.csv") }) catch @panic("OOM");
|
||||||
.{ .path = "system/services/init", .binary = init_exe.getEmittedBin() },
|
bundled_list.append(b.allocator, .{ .path = "etc/init.csv", .binary = b.path(init_csv_source) }) catch @panic("OOM");
|
||||||
.{ .path = "system/services/fat", .binary = fat_exe.getEmittedBin() },
|
|
||||||
.{ .path = "system/services/display", .binary = display_exe.getEmittedBin() },
|
|
||||||
.{ .path = "system/services/display-demo", .binary = display_demo_exe.getEmittedBin() },
|
|
||||||
.{ .path = "system/services/device-manager", .binary = device_manager_exe.getEmittedBin() },
|
|
||||||
.{ .path = "system/services/input", .binary = input_exe.getEmittedBin() },
|
|
||||||
.{ .path = "system/services/discovery", .binary = discovery_exe.getEmittedBin() },
|
|
||||||
.{ .path = "system/services/logger", .binary = logger_exe.getEmittedBin() },
|
|
||||||
// A data file, not a binary: the device registry the manager reads at boot.
|
|
||||||
// Packing it under /etc makes the kernel auto-mount /etc as a read-only
|
|
||||||
// initrd tree (system/kernel/vfs.zig setInitialRamdisk), so the manager can
|
|
||||||
// fs.open("/etc/devices.csv") with no filesystem service running.
|
|
||||||
.{ .path = "etc/devices.csv", .binary = b.path("etc/devices.csv") },
|
|
||||||
// init's service list, likewise read from the kernel-served initrd /etc.
|
|
||||||
.{ .path = "etc/init.csv", .binary = b.path(init_csv_source) },
|
|
||||||
.{ .path = "system/drivers/ps2-bus", .binary = ps2_bus_exe.getEmittedBin() },
|
|
||||||
.{ .path = "system/drivers/ps2-keyboard", .binary = ps2_keyboard_exe.getEmittedBin() },
|
|
||||||
.{ .path = "system/drivers/ps2-mouse", .binary = ps2_mouse_exe.getEmittedBin() },
|
|
||||||
.{ .path = "system/drivers/usb-xhci-bus", .binary = usb_xhci_bus_exe.getEmittedBin() },
|
|
||||||
.{ .path = "system/drivers/usb-hid-keyboard", .binary = usb_hid_keyboard_exe.getEmittedBin() },
|
|
||||||
.{ .path = "system/drivers/usb-hid-mouse", .binary = usb_hid_mouse_exe.getEmittedBin() },
|
|
||||||
.{ .path = "system/drivers/usb-storage", .binary = usb_storage_exe.getEmittedBin() },
|
|
||||||
.{ .path = "system/drivers/virtio-gpu", .binary = virtio_gpu_exe.getEmittedBin() },
|
|
||||||
.{ .path = "system/drivers/pci-bus", .binary = pci_bus_exe.getEmittedBin() },
|
|
||||||
};
|
|
||||||
// A no-option build assumes neither -Dtest-case nor -Ddiagnose: it ships the
|
// A no-option build assumes neither -Dtest-case nor -Ddiagnose: it ships the
|
||||||
// production set only. The userspace test fixtures under /test join in only
|
// production set only. The userspace test fixtures under /test join in only
|
||||||
// for a test build — which the QEMU harness signals by passing
|
// for a test build — which the QEMU harness signals by passing
|
||||||
// -Dtest-case=<name> for every scenario, exactly when they must be on the
|
// -Dtest-case=<name> for every scenario, exactly when they must be on the
|
||||||
// boot volume. They are LAZY dependencies: a plain build neither compiles
|
// boot volume. They are LAZY dependencies too. Fixture packages are
|
||||||
// them nor loads their build files (docs/build-packages-plan.md). Fixture
|
// uniform — the dependency name, the artifact name, and the boot path's
|
||||||
// packages are uniform — the dependency name, the artifact name, and the
|
// leaf all match the directory — so a name is a whole entry.
|
||||||
// boot path's leaf all match the directory — so a name is a whole entry.
|
|
||||||
var bundled_list: std.ArrayListUnmanaged(images.BundledBinary) = .empty;
|
|
||||||
bundled_list.appendSlice(b.allocator, &production_bundled) catch @panic("OOM");
|
|
||||||
if (test_case != null) for ([_][]const u8{
|
if (test_case != null) for ([_][]const u8{
|
||||||
"vfs-test", // the user-space VFS round-trip client
|
"vfs-test", // the user-space VFS round-trip client
|
||||||
"fat-test",
|
"fat-test",
|
||||||
@@ -419,12 +413,12 @@ pub fn build(b: *std.Build) void {
|
|||||||
protocol_library,
|
protocol_library,
|
||||||
csv_library,
|
csv_library,
|
||||||
xkeyboard_config_library,
|
xkeyboard_config_library,
|
||||||
fat_package,
|
b.dependency("fat", .{}),
|
||||||
display_package,
|
b.dependency("display", .{}),
|
||||||
ps2_bus_package,
|
b.dependency("ps2-bus", .{}),
|
||||||
usb_hid_package,
|
b.dependency("usb-hid", .{}),
|
||||||
usb_storage_package,
|
b.dependency("usb-storage", .{}),
|
||||||
virtio_gpu_package,
|
b.dependency("virtio-gpu", .{}),
|
||||||
}) |package| {
|
}) |package| {
|
||||||
test_step.dependOn(&package.builder.top_level_steps.get("test").?.step);
|
test_step.dependOn(&package.builder.top_level_steps.get("test").?.step);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user