build: phase 2 wave C — test fixtures build as packages
All thirteen /test/system/services fixtures convert on the pci-bus template (thread-test threaded; crash-test/device-list/pci-cap-test/ iommu-fault-test carrying their protocol and PCI extras). With the last addUserBinary caller gone, the root build's wrapper functions and its default-imports plumbing are deleted — every user binary now reaches the shared recipe only through its own package. Production boot-image file list unchanged; a -Dtest-case build bundles the fixtures exactly as before.
This commit is contained in:
@@ -51,63 +51,6 @@ fn timestamp(b: *std.Build) []const u8 {
|
||||
});
|
||||
}
|
||||
|
||||
/// One user-space binary via the shared build-support recipe (freestanding,
|
||||
/// ReleaseSmall, `.large` code model, root shim + user link script — see
|
||||
/// build-support/build.zig for the full story). The program's file becomes the
|
||||
/// `program` module; reach it through `programModule` to add per-binary imports.
|
||||
fn addUserBinary(
|
||||
b: *std.Build,
|
||||
target: std.Build.ResolvedTarget,
|
||||
default_imports: []const std.Build.Module.Import,
|
||||
name: []const u8,
|
||||
root: []const u8,
|
||||
) *std.Build.Step.Compile {
|
||||
return addUserBinaryImpl(b, target, default_imports, name, root, false);
|
||||
}
|
||||
|
||||
/// As `addUserBinary`, but built multi-threaded (`single_threaded = false`) so real
|
||||
/// atomics/TLS work — required before a binary may call `Thread.spawn`
|
||||
/// (docs/threading.md). Threads are a deliberate per-binary opt-in.
|
||||
fn addThreadedUserBinary(
|
||||
b: *std.Build,
|
||||
target: std.Build.ResolvedTarget,
|
||||
default_imports: []const std.Build.Module.Import,
|
||||
name: []const u8,
|
||||
root: []const u8,
|
||||
) *std.Build.Step.Compile {
|
||||
return addUserBinaryImpl(b, target, default_imports, name, root, true);
|
||||
}
|
||||
|
||||
fn addUserBinaryImpl(
|
||||
b: *std.Build,
|
||||
target: std.Build.ResolvedTarget,
|
||||
default_imports: []const std.Build.Module.Import,
|
||||
name: []const u8,
|
||||
root: []const u8,
|
||||
threaded: bool,
|
||||
) *std.Build.Step.Compile {
|
||||
// The shim and link script live in the kernel domain package, so name them
|
||||
// through it — the same spelling build-support's userBinary uses.
|
||||
const kernel_library = b.dependency("kernel", .{});
|
||||
return build_support.userBinaryFromImports(b, .{
|
||||
.name = name,
|
||||
.root_source_file = b.path(root),
|
||||
.shim_source_file = kernel_library.path("root.zig"),
|
||||
.linker_script = kernel_library.path("user.ld"),
|
||||
.target = target,
|
||||
.default_imports = default_imports,
|
||||
.threaded = threaded,
|
||||
});
|
||||
}
|
||||
|
||||
/// The `program` module of a binary built by `addUserBinary` — the module rooted
|
||||
/// at the program's own source file. Per-binary imports (protocol modules, bus
|
||||
/// ABIs) go here, not on the root shim: module imports are not transitive, so an
|
||||
/// import added to the root would be invisible to the program's code.
|
||||
fn programModule(exe: *std.Build.Step.Compile) *std.Build.Module {
|
||||
return build_support.programModule(exe);
|
||||
}
|
||||
|
||||
/// The modules the kernel imports, gathered once so both kernel variants (the
|
||||
/// installed one and the serial-enabled one `run-x86-64` boots) are built from
|
||||
/// the same set. `build_options` is *not* here — it carries `serial`/`test_case`,
|
||||
@@ -250,7 +193,6 @@ pub fn build(b: *std.Build) void {
|
||||
});
|
||||
const abi_module = kernel_library.module("abi");
|
||||
const device_abi_module = device_library.module("device-abi");
|
||||
const pci_class_module = device_library.module("pci-class");
|
||||
|
||||
// Kernel tunables (maximum_cpus, stack sizes, tick rate). A dependency-free module of
|
||||
// compile-time constants, imported wherever a knob is read; keeps the trade-offs
|
||||
@@ -292,14 +234,7 @@ pub fn build(b: *std.Build) void {
|
||||
},
|
||||
});
|
||||
|
||||
// The wire protocols some test fixtures name as per-binary extras,
|
||||
// exported by the protocol package.
|
||||
const device_manager_protocol_module = protocol_library.module("device-manager-protocol");
|
||||
|
||||
// The driver-side PCI library some test fixtures name as a per-binary
|
||||
// extra. (The rest of the domain modules reach the root only through
|
||||
// build-support's defaultImports below.)
|
||||
const pci_module = device_library.module("pci");
|
||||
|
||||
// The initial_ramdisk container format, shared by the kernel (unpacks it) and
|
||||
// the EFI loader (packs it in RAM from the boot volume's /system tree). No
|
||||
@@ -346,18 +281,7 @@ pub fn build(b: *std.Build) void {
|
||||
const kernel_install = b.addInstallArtifact(exe, .{ .dest_dir = .{ .override = .{ .custom = "system" } } });
|
||||
b.getInstallStep().dependOn(&kernel_install.step);
|
||||
|
||||
// --- init: the first user-space program (a system service) ---
|
||||
// The default module set every user binary can import directly, drawn from
|
||||
// build-support's single authoritative list so the root stanzas and the
|
||||
// binary packages can never drift apart. Per-binary extras are added with
|
||||
// programModule(exe).addImport.
|
||||
const default_imports = build_support.defaultImports(.{
|
||||
.kernel = kernel_library,
|
||||
.device = device_library,
|
||||
.client = client_library,
|
||||
.xkeyboard_config = xkeyboard_config_library,
|
||||
});
|
||||
|
||||
// --- the user-space binaries, every one of them a package ---
|
||||
// Binary packages (docs/build-packages-plan.md, phase 2): each binary
|
||||
// builds itself against the domain packages via build-support's shared
|
||||
// recipe, started in ring 3 by the kernel's user-ELF loader like always;
|
||||
@@ -371,7 +295,7 @@ pub fn build(b: *std.Build) void {
|
||||
// Each is built by the same user-binary recipe and laid out at its FHS path on
|
||||
// the boot volume (see `bundled` below). The EFI loader walks the tree at boot
|
||||
// and hands the kernel an in-RAM initial_ramdisk of it (system/initial-ramdisk.zig).
|
||||
const vfstest_exe = addUserBinary(b, kernel_target, &default_imports, "vfs-test", "test/system/services/vfs-test/vfs-test.zig");
|
||||
const vfstest_exe = b.dependency("vfs-test", .{}).artifact("vfs-test");
|
||||
// The drivers, each directory its own package: the PS/2 bus family (bus +
|
||||
// keyboard + mouse from one package), the xHCI bus driver, the USB HID
|
||||
// class drivers, and USB mass storage. Their unit tests ride along.
|
||||
@@ -394,29 +318,21 @@ pub fn build(b: *std.Build) void {
|
||||
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");
|
||||
const shared_memory_server_exe = addUserBinary(b, kernel_target, &default_imports, "shared-memory-server", "test/system/services/shared-memory-server/shared-memory-server.zig");
|
||||
const shared_memory_client_exe = addUserBinary(b, kernel_target, &default_imports, "shared-memory-client", "test/system/services/shared-memory-client/shared-memory-client.zig");
|
||||
const fat_test_exe = addUserBinary(b, kernel_target, &default_imports, "fat-test", "test/system/services/fat-test/fat-test.zig");
|
||||
const shared_memory_server_exe = b.dependency("shared-memory-server", .{}).artifact("shared-memory-server");
|
||||
const shared_memory_client_exe = b.dependency("shared-memory-client", .{}).artifact("shared-memory-client");
|
||||
const fat_test_exe = b.dependency("fat-test", .{}).artifact("fat-test");
|
||||
// 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");
|
||||
// A test fixture, not a real driver: hellos to the device manager, then faults —
|
||||
// what the driver-restart scenario drives the crash-loop cap with.
|
||||
const crash_test_exe = addUserBinary(b, kernel_target, &default_imports, "crash-test", "test/system/services/crash-test/crash-test.zig");
|
||||
programModule(crash_test_exe).addImport("device-manager-protocol", device_manager_protocol_module);
|
||||
const device_list_exe = addUserBinary(b, kernel_target, &default_imports, "device-list", "test/system/services/device-list/device-list.zig");
|
||||
programModule(device_list_exe).addImport("device-manager-protocol", device_manager_protocol_module);
|
||||
// A test fixture: claims the pci-caps case's extra unclaimed NIC and exercises the
|
||||
// driver-side PCI library surface (capabilities, MSI, MSI-X, power, FLR) against it.
|
||||
const pci_cap_test_exe = addUserBinary(b, kernel_target, &default_imports, "pci-cap-test", "test/system/services/pci-cap-test/pci-cap-test.zig");
|
||||
programModule(pci_cap_test_exe).addImport("pci", pci_module);
|
||||
programModule(pci_cap_test_exe).addImport("pci-class", pci_class_module);
|
||||
// The IOMMU-enforcement negative test: claims an unclaimed e1000e and fires a rogue
|
||||
// DMA that VT-d must fault. Same PCI building blocks as pci-cap-test.
|
||||
const iommu_fault_test_exe = addUserBinary(b, kernel_target, &default_imports, "iommu-fault-test", "test/system/services/iommu-fault-test/iommu-fault-test.zig");
|
||||
programModule(iommu_fault_test_exe).addImport("pci", pci_module);
|
||||
programModule(iommu_fault_test_exe).addImport("pci-class", pci_class_module);
|
||||
// crash-test is a fixture, not a real driver: it hellos to the device
|
||||
// manager, then faults — what the driver-restart scenario drives the
|
||||
// crash-loop cap with. pci-cap-test and iommu-fault-test exercise the
|
||||
// driver-side PCI library surface and the VT-d rogue-DMA negative proof.
|
||||
const crash_test_exe = b.dependency("crash-test", .{}).artifact("crash-test");
|
||||
const device_list_exe = b.dependency("device-list", .{}).artifact("device-list");
|
||||
const pci_cap_test_exe = b.dependency("pci-cap-test", .{}).artifact("pci-cap-test");
|
||||
const iommu_fault_test_exe = b.dependency("iommu-fault-test", .{}).artifact("iommu-fault-test");
|
||||
// The discovery service: one swappable process per firmware
|
||||
// (docs/discovery.md), bundled under the neutral ramdisk name
|
||||
// "discovery" so the device manager never learns which firmware it is on.
|
||||
@@ -435,14 +351,14 @@ pub fn build(b: *std.Build) void {
|
||||
// The input service and its exercisers: the fan-out server, a hardware-free synthetic
|
||||
// source, and a subscriber that doubles as the `input` test's oracle. See docs/input.md.
|
||||
const input_exe = b.dependency("input", .{}).artifact("input");
|
||||
const input_source_exe = addUserBinary(b, kernel_target, &default_imports, "input-source", "test/system/services/input-source/input-source.zig");
|
||||
const input_test_exe = addUserBinary(b, kernel_target, &default_imports, "input-test", "test/system/services/input-test/input-test.zig");
|
||||
const args_echo_exe = addUserBinary(b, kernel_target, &default_imports, "args-echo", "test/system/services/args-echo/args-echo.zig");
|
||||
const process_test_exe = addUserBinary(b, kernel_target, &default_imports, "process-test", "test/system/services/process-test/process-test.zig");
|
||||
const input_source_exe = b.dependency("input-source", .{}).artifact("input-source");
|
||||
const input_test_exe = b.dependency("input-test", .{}).artifact("input-test");
|
||||
const args_echo_exe = b.dependency("args-echo", .{}).artifact("args-echo");
|
||||
const process_test_exe = b.dependency("process-test", .{}).artifact("process-test");
|
||||
const logger_exe = b.dependency("logger", .{}).artifact("logger");
|
||||
// The first multi-threaded binary: exercises runtime.Thread over the thread ABI
|
||||
// (docs/threading.md). Built threaded so its shared-memory poll is real.
|
||||
const thread_test_exe = addThreadedUserBinary(b, kernel_target, &default_imports, "thread-test", "test/system/services/thread-test/thread-test.zig");
|
||||
// (docs/threading.md). Its package opts into threading (real atomics/TLS).
|
||||
const thread_test_exe = b.dependency("thread-test", .{}).artifact("thread-test");
|
||||
|
||||
// Every user binary and its FHS home on the boot volume. There is no packed
|
||||
// ramdisk artifact any more: make-fat-image.py lays each binary out at this
|
||||
|
||||
@@ -58,6 +58,19 @@
|
||||
.@"usb-hid" = .{ .path = "system/drivers/usb-hid" },
|
||||
.@"usb-storage" = .{ .path = "system/drivers/usb-storage" },
|
||||
.@"virtio-gpu" = .{ .path = "system/drivers/virtio-gpu" },
|
||||
.@"vfs-test" = .{ .path = "test/system/services/vfs-test" },
|
||||
.@"fat-test" = .{ .path = "test/system/services/fat-test" },
|
||||
.@"shared-memory-server" = .{ .path = "test/system/services/shared-memory-server" },
|
||||
.@"shared-memory-client" = .{ .path = "test/system/services/shared-memory-client" },
|
||||
.@"crash-test" = .{ .path = "test/system/services/crash-test" },
|
||||
.@"device-list" = .{ .path = "test/system/services/device-list" },
|
||||
.@"pci-cap-test" = .{ .path = "test/system/services/pci-cap-test" },
|
||||
.@"iommu-fault-test" = .{ .path = "test/system/services/iommu-fault-test" },
|
||||
.@"input-source" = .{ .path = "test/system/services/input-source" },
|
||||
.@"input-test" = .{ .path = "test/system/services/input-test" },
|
||||
.@"args-echo" = .{ .path = "test/system/services/args-echo" },
|
||||
.@"process-test" = .{ .path = "test/system/services/process-test" },
|
||||
.@"thread-test" = .{ .path = "test/system/services/thread-test" },
|
||||
// See `zig fetch --save <url>` for a command-line interface for adding dependencies.
|
||||
//.example = .{
|
||||
// // When updating this field to a new URL, be sure to delete the corresponding
|
||||
|
||||
Reference in New Issue
Block a user