build: phase 2 wave A — services build as packages

init, fat, display, display-demo, device-manager, input, logger, and
the two discovery fillers (acpi, fdt — each exporting an artifact named
"discovery"; the root -Ddiscovery picks which ships) convert to binary
packages on the pci-bus template. init's serial heartbeat flag rides
the dependency options (the root forwards its -Dserial). fat's and
display's unit tests move into their packages and the root aggregate
delegates to them. Boot-image file list unchanged.
This commit is contained in:
Daniel Samson
2026-07-30 04:02:49 +01:00
parent 902e4a0a9e
commit 3b23b11b0e
56 changed files with 1064 additions and 60 deletions
+24
View File
@@ -0,0 +1,24 @@
//! The acpi discovery service as a binary package (docs/build-packages-plan.md,
//! phase 2). The artifact is named "discovery": one swappable process per
//! firmware fills the ramdisk's neutral `discovery` slot (docs/discovery.md),
//! so the device manager never learns which firmware it is on. The root
//! build's -Ddiscovery picks this package or `fdt`.
const std = @import("std");
const build_support = @import("build-support");
pub fn build(b: *std.Build) void {
const exe = build_support.userBinary(b, .{
.name = "discovery",
.root_source_file = b.path("acpi.zig"),
.domains = build_support.domains(b),
});
const protocol = b.dependency("protocol", .{});
// The shared AML interpreter (the same parser the kernel runs), the
// device-manager report channel, and the power protocol (the SCI/power
// button path, docs/power.md).
build_support.programModule(exe).addImport("aml", b.dependency("device", .{}).module("aml"));
build_support.programModule(exe).addImport("device-manager-protocol", protocol.module("device-manager-protocol"));
build_support.programModule(exe).addImport("power-protocol", protocol.module("power-protocol"));
b.installArtifact(exe);
}
+15
View File
@@ -0,0 +1,15 @@
.{
.name = .acpi,
.version = "0.0.0",
.fingerprint = 0xf31e9a0903256b64, // Changing this has security and trust implications.
.minimum_zig_version = "0.16.0",
.dependencies = .{
.@"build-support" = .{ .path = "../../../build-support" },
.kernel = .{ .path = "../../../library/kernel" },
.device = .{ .path = "../../../library/device" },
.client = .{ .path = "../../../library/client" },
.@"xkeyboard-config" = .{ .path = "../../../library/xkeyboard-config" },
.protocol = .{ .path = "../../../library/protocol" },
},
.paths = .{""},
}
+21
View File
@@ -0,0 +1,21 @@
//! The device-manager service as a binary package (docs/build-packages-plan.md,
//! phase 2): this file names only what is specific to this binary — the shared
//! recipe and the default-import wiring live in build-support. The root build
//! consumes the artifact(s) for the boot image.
const std = @import("std");
const build_support = @import("build-support");
pub fn build(b: *std.Build) void {
const protocol = b.dependency("protocol", .{});
const device = b.dependency("device", .{});
const exe = build_support.userBinary(b, .{
.name = "device-manager",
.root_source_file = b.path("device-manager.zig"),
.domains = build_support.domains(b),
});
build_support.programModule(exe).addImport("device-manager-protocol", protocol.module("device-manager-protocol"));
build_support.programModule(exe).addImport("device-registry", device.module("device-registry"));
b.installArtifact(exe);
}
@@ -0,0 +1,15 @@
.{
.name = .device_manager,
.version = "0.0.0",
.fingerprint = 0x7092fc24905cd147, // Changing this has security and trust implications.
.minimum_zig_version = "0.16.0",
.dependencies = .{
.@"build-support" = .{ .path = "../../../build-support" },
.kernel = .{ .path = "../../../library/kernel" },
.device = .{ .path = "../../../library/device" },
.client = .{ .path = "../../../library/client" },
.@"xkeyboard-config" = .{ .path = "../../../library/xkeyboard-config" },
.protocol = .{ .path = "../../../library/protocol" },
},
.paths = .{""},
}
+16
View File
@@ -0,0 +1,16 @@
//! The display-demo service as a binary package (docs/build-packages-plan.md,
//! phase 2): this file names only what is specific to this binary — the shared
//! recipe and the default-import wiring live in build-support. The root build
//! consumes the artifact(s) for the boot image.
const std = @import("std");
const build_support = @import("build-support");
pub fn build(b: *std.Build) void {
const exe = build_support.userBinary(b, .{
.name = "display-demo",
.root_source_file = b.path("display-demo.zig"),
.domains = build_support.domains(b),
});
b.installArtifact(exe);
}
@@ -0,0 +1,14 @@
.{
.name = .display_demo,
.version = "0.0.0",
.fingerprint = 0x5d2832e1e2880143, // Changing this has security and trust implications.
.minimum_zig_version = "0.16.0",
.dependencies = .{
.@"build-support" = .{ .path = "../../../build-support" },
.kernel = .{ .path = "../../../library/kernel" },
.device = .{ .path = "../../../library/device" },
.client = .{ .path = "../../../library/client" },
.@"xkeyboard-config" = .{ .path = "../../../library/xkeyboard-config" },
},
.paths = .{""},
}
+35
View File
@@ -0,0 +1,35 @@
//! The display service as a binary package (docs/build-packages-plan.md,
//! phase 2): this file names only what is specific to this binary — the shared
//! recipe and the default-import wiring live in build-support. The root build
//! consumes the artifact(s) for the boot image.
const std = @import("std");
const build_support = @import("build-support");
pub fn build(b: *std.Build) void {
const protocol = b.dependency("protocol", .{});
const exe = build_support.userBinary(b, .{
.name = "display",
.root_source_file = b.path("display.zig"),
.domains = build_support.domains(b),
.threaded = true, // real atomics/TLS (docs/threading.md)
});
build_support.programModule(exe).addImport("display-protocol", protocol.module("display-protocol"));
build_support.programModule(exe).addImport("scanout-protocol", protocol.module("scanout-protocol"));
b.installArtifact(exe);
// Standalone `zig build test`; the root aggregate depends on this step.
const test_step = b.step("test", "Run the display unit tests");
for ([_][]const u8{
"compositor.zig", // Rect math + fill/composite/blit-tile
}) |test_root| {
const unit_tests = b.addTest(.{
.root_module = b.createModule(.{
.root_source_file = b.path(test_root),
.target = b.resolveTargetQuery(.{}),
}),
});
test_step.dependOn(&b.addRunArtifact(unit_tests).step);
}
}
+15
View File
@@ -0,0 +1,15 @@
.{
.name = .display,
.version = "0.0.0",
.fingerprint = 0xcd172a34b127a19, // Changing this has security and trust implications.
.minimum_zig_version = "0.16.0",
.dependencies = .{
.@"build-support" = .{ .path = "../../../build-support" },
.kernel = .{ .path = "../../../library/kernel" },
.device = .{ .path = "../../../library/device" },
.client = .{ .path = "../../../library/client" },
.@"xkeyboard-config" = .{ .path = "../../../library/xkeyboard-config" },
.protocol = .{ .path = "../../../library/protocol" },
},
.paths = .{""},
}
+34
View File
@@ -0,0 +1,34 @@
//! The fat service as a binary package (docs/build-packages-plan.md,
//! phase 2): this file names only what is specific to this binary — the shared
//! recipe and the default-import wiring live in build-support. The root build
//! consumes the artifact(s) for the boot image.
const std = @import("std");
const build_support = @import("build-support");
pub fn build(b: *std.Build) void {
const protocol = b.dependency("protocol", .{});
const exe = build_support.userBinary(b, .{
.name = "fat",
.root_source_file = b.path("fat.zig"),
.domains = build_support.domains(b),
});
build_support.programModule(exe).addImport("vfs-protocol", protocol.module("vfs-protocol"));
b.installArtifact(exe);
// Standalone `zig build test`; the root aggregate depends on this step.
const test_step = b.step("test", "Run the fat unit tests");
for ([_][]const u8{
"on-disk.zig", // FAT on-disk struct sizes + type detection
"engine.zig", // FAT read/write over a RAM-backed image
}) |test_root| {
const unit_tests = b.addTest(.{
.root_module = b.createModule(.{
.root_source_file = b.path(test_root),
.target = b.resolveTargetQuery(.{}),
}),
});
test_step.dependOn(&b.addRunArtifact(unit_tests).step);
}
}
+15
View File
@@ -0,0 +1,15 @@
.{
.name = .fat,
.version = "0.0.0",
.fingerprint = 0x98958143ecfe1636, // Changing this has security and trust implications.
.minimum_zig_version = "0.16.0",
.dependencies = .{
.@"build-support" = .{ .path = "../../../build-support" },
.kernel = .{ .path = "../../../library/kernel" },
.device = .{ .path = "../../../library/device" },
.client = .{ .path = "../../../library/client" },
.@"xkeyboard-config" = .{ .path = "../../../library/xkeyboard-config" },
.protocol = .{ .path = "../../../library/protocol" },
},
.paths = .{""},
}
+17
View File
@@ -0,0 +1,17 @@
//! The fdt discovery service as a binary package (docs/build-packages-plan.md,
//! phase 2). The artifact is named "discovery" like acpi's: the Raspberry Pis
//! hand over a flattened device tree instead of ACPI, and the aarch64 target
//! flips the root build's -Ddiscovery default when it lands (docs/arm.md).
//! A placeholder until the ARM bring-up.
const std = @import("std");
const build_support = @import("build-support");
pub fn build(b: *std.Build) void {
const exe = build_support.userBinary(b, .{
.name = "discovery",
.root_source_file = b.path("fdt.zig"),
.domains = build_support.domains(b),
});
b.installArtifact(exe);
}
+14
View File
@@ -0,0 +1,14 @@
.{
.name = .fdt,
.version = "0.0.0",
.fingerprint = 0xe5e27506c7fc2eea, // Changing this has security and trust implications.
.minimum_zig_version = "0.16.0",
.dependencies = .{
.@"build-support" = .{ .path = "../../../build-support" },
.kernel = .{ .path = "../../../library/kernel" },
.device = .{ .path = "../../../library/device" },
.client = .{ .path = "../../../library/client" },
.@"xkeyboard-config" = .{ .path = "../../../library/xkeyboard-config" },
},
.paths = .{""},
}
+31
View File
@@ -0,0 +1,31 @@
//! init (PID 1) as a binary package (docs/build-packages-plan.md, phase 2):
//! this file names only what is specific to init — the shared recipe and the
//! default-import wiring live in build-support. The root build consumes the
//! artifact for the boot image and forwards its -Dserial here.
const std = @import("std");
const build_support = @import("build-support");
pub fn build(b: *std.Build) void {
const exe = build_support.userBinary(b, .{
.name = "init",
.root_source_file = b.path("init.zig"),
.domains = build_support.domains(b),
});
// init speaks the power protocol (orderly shutdown, docs/power.md) and
// parses its boot service list from /etc/init.csv with the shared csv
// helpers. Which services it starts is data, not a compile-time option:
// the root build selects which init.csv variant is bundled (-Ddiagnose).
build_support.programModule(exe).addImport("power-protocol", b.dependency("protocol", .{}).module("power-protocol"));
build_support.programModule(exe).addImport("csv", b.dependency("csv", .{}).module("csv"));
// init reads the same `serial` flag the kernel does: its liveness heartbeat
// is a serial/test-build diagnostic (the QEMU harness's init tests assert
// on it, and -Dserial images emit it), so a flashable image runs a purely
// event-driven PID 1 that wakes only for real work. The root build forwards
// its top-level -Dserial as this dependency option.
const serial = b.option(bool, "serial", "Compile the serial liveness heartbeat in (forwarded from the root -Dserial)") orelse false;
const init_options = b.addOptions();
init_options.addOption(bool, "serial", serial);
build_support.programModule(exe).addImport("build_options", init_options.createModule());
b.installArtifact(exe);
}
+16
View File
@@ -0,0 +1,16 @@
.{
.name = .init,
.version = "0.0.0",
.fingerprint = 0xc674e474eeeced43, // Changing this has security and trust implications.
.minimum_zig_version = "0.16.0",
.dependencies = .{
.@"build-support" = .{ .path = "../../../build-support" },
.kernel = .{ .path = "../../../library/kernel" },
.device = .{ .path = "../../../library/device" },
.client = .{ .path = "../../../library/client" },
.@"xkeyboard-config" = .{ .path = "../../../library/xkeyboard-config" },
.protocol = .{ .path = "../../../library/protocol" },
.csv = .{ .path = "../../../library/csv" },
},
.paths = .{""},
}
+19
View File
@@ -0,0 +1,19 @@
//! The input service as a binary package (docs/build-packages-plan.md,
//! phase 2): this file names only what is specific to this binary — the shared
//! recipe and the default-import wiring live in build-support. The root build
//! consumes the artifact(s) for the boot image.
const std = @import("std");
const build_support = @import("build-support");
pub fn build(b: *std.Build) void {
const protocol = b.dependency("protocol", .{});
const exe = build_support.userBinary(b, .{
.name = "input",
.root_source_file = b.path("input.zig"),
.domains = build_support.domains(b),
});
build_support.programModule(exe).addImport("input-protocol", protocol.module("input-protocol"));
b.installArtifact(exe);
}
+15
View File
@@ -0,0 +1,15 @@
.{
.name = .input,
.version = "0.0.0",
.fingerprint = 0xd82832d7113ed94e, // Changing this has security and trust implications.
.minimum_zig_version = "0.16.0",
.dependencies = .{
.@"build-support" = .{ .path = "../../../build-support" },
.kernel = .{ .path = "../../../library/kernel" },
.device = .{ .path = "../../../library/device" },
.client = .{ .path = "../../../library/client" },
.@"xkeyboard-config" = .{ .path = "../../../library/xkeyboard-config" },
.protocol = .{ .path = "../../../library/protocol" },
},
.paths = .{""},
}
+16
View File
@@ -0,0 +1,16 @@
//! The logger service as a binary package (docs/build-packages-plan.md,
//! phase 2): this file names only what is specific to this binary — the shared
//! recipe and the default-import wiring live in build-support. The root build
//! consumes the artifact(s) for the boot image.
const std = @import("std");
const build_support = @import("build-support");
pub fn build(b: *std.Build) void {
const exe = build_support.userBinary(b, .{
.name = "logger",
.root_source_file = b.path("logger.zig"),
.domains = build_support.domains(b),
});
b.installArtifact(exe);
}
+14
View File
@@ -0,0 +1,14 @@
.{
.name = .logger,
.version = "0.0.0",
.fingerprint = 0x987e13f37b0eaea2, // Changing this has security and trust implications.
.minimum_zig_version = "0.16.0",
.dependencies = .{
.@"build-support" = .{ .path = "../../../build-support" },
.kernel = .{ .path = "../../../library/kernel" },
.device = .{ .path = "../../../library/device" },
.client = .{ .path = "../../../library/client" },
.@"xkeyboard-config" = .{ .path = "../../../library/xkeyboard-config" },
},
.paths = .{""},
}