//! The "protocol" library domain: the wire protocols — each service's public //! interface, exposed as its own module (docs/driver-model.md). Both sides of //! every conversation depend on the contract by name; neither reaches into the //! other's files. Pure flat wire types: no protocol module imports anything. //! //! One module here is not a protocol but the shape the others are written in, //! and therefore the one module every other one imports: //! //! envelope : the packet prefix + comptime Define (docs/os-development/protocol-namespace.md) //! //! vfs-protocol : the VFS server <-> the file layer (unistd/stdio) //! input-protocol : the input fan-out service <-> sources + subscribers //! block-protocol : a filesystem <-> a block driver (usb-storage) //! usb-transfer-protocol : a USB class driver <-> the xHCI bus driver //! device-manager-protocol : the device manager <-> drivers + discovery //! display-protocol : the compositor's client-facing surface //! scanout-protocol : the compositor -> a native scanout driver (docs/display-v2.md) //! power-protocol : system power's domain-named surface (docs/power.md) const std = @import("std"); pub fn build(b: *std.Build) void { // Not a protocol, hence not `-protocol`: the envelope is what a protocol is // defined *through*, so it is built first and handed to every protocol // below as their one import. const envelope = b.addModule("envelope", .{ .root_source_file = b.path("envelope/envelope.zig") }); for ([_]struct { name: []const u8, root: []const u8 }{ .{ .name = "vfs-protocol", .root = "vfs/vfs-protocol.zig" }, .{ .name = "input-protocol", .root = "input/input-protocol.zig" }, .{ .name = "block-protocol", .root = "block/block-protocol.zig" }, .{ .name = "usb-transfer-protocol", .root = "usb-transfer/usb-transfer-protocol.zig" }, .{ .name = "device-manager-protocol", .root = "device-manager/device-manager-protocol.zig" }, .{ .name = "display-protocol", .root = "display/display-protocol.zig" }, .{ .name = "scanout-protocol", .root = "scanout/scanout-protocol.zig" }, .{ .name = "power-protocol", .root = "power/power-protocol.zig" }, }) |protocol| { _ = b.addModule(protocol.name, .{ .root_source_file = b.path(protocol.root), .imports = &.{.{ .name = "envelope", .module = envelope }}, }); } // Standalone `zig build test` for this domain alone; the root build keeps // its aggregate test step. const test_step = b.step("test", "Run the protocol unit tests"); // The envelope tests itself with no import of its own — everything else // imports it, so it is built separately rather than importing itself. const envelope_tests = b.addTest(.{ .root_module = b.createModule(.{ .root_source_file = b.path("envelope/envelope.zig"), // framing round trips, verb numbering, dispatch, the floors .target = b.resolveTargetQuery(.{}), }), }); test_step.dependOn(&b.addRunArtifact(envelope_tests).step); for ([_][]const u8{ "vfs/vfs-protocol.zig", // NodeKind / DirectoryEntry sizes + op values "input/input-protocol.zig", // event numbering + the push-floor budget "display/display-protocol.zig", // pack(): native pixel encoding per format "device-manager/device-manager-protocol.zig", // the dual-use report, exactly on the push floor "power/power-protocol.zig", // the event kind as the packet's verb "usb-transfer/usb-transfer-protocol.zig", // the tail-carried control stage + the trimmed report }) |root| { const protocol_tests = b.addTest(.{ .root_module = b.createModule(.{ .root_source_file = b.path(root), .target = b.resolveTargetQuery(.{}), .imports = &.{.{ .name = "envelope", .module = envelope }}, }), }); test_step.dependOn(&b.addRunArtifact(protocol_tests).step); } }