diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-19 21:26:05 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-09-19 21:26:05 -0300 |
| commit | b05abcba3ea09ea106ad28364c6e40a3ec31b890 (patch) | |
| tree | 9170fac5e7e5d8bde108de34a182aaa9d6844117 /introspect/build.zig | |
| parent | ae310a207534b33b7321dd2b9f423a73b1969159 (diff) | |
| download | cloud9-b05abcba3ea09ea106ad28364c6e40a3ec31b890.tar.gz cloud9-b05abcba3ea09ea106ad28364c6e40a3ec31b890.zip | |
Add 9player and introspect as programs beside the library
9player/: FUSE mount CLI that mounts a 9P2000 tree into a fresh user+mount
namespace and runs a program in it (no root, no libfuse, no libc).
introspect/: the 9P debug/introspection library (freestanding core, value
renderers, Linux probe with threads/stacks/memory/breakpoints/panics) and
its demo server. Each has its own build fragment; the root build.zig wires
them behind -D9player/-Dintrospect with namespaced steps (9player-itest,
introspect-check-freestanding, programs-test, ...) and exports the
introspect module for dependents. This is the layout for related programs.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Diffstat (limited to 'introspect/build.zig')
| -rw-r--r-- | introspect/build.zig | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/introspect/build.zig b/introspect/build.zig new file mode 100644 index 0000000..6662151 --- /dev/null +++ b/introspect/build.zig @@ -0,0 +1,169 @@ +//! Build fragment for introspect: the 9P debug/introspection library (module +//! `introspect`), its freestanding check, the `introspect` demo server and the +//! test suites under introspect/test. It is `@import`ed by the root build.zig +//! and called with the root builder, so every `b.path(...)` here is relative +//! to the cloud9 root (hence the `introspect/` prefix), every option is +//! defined by the root (no `standardTargetOptions` here) and every step it +//! registers lands in the root's step list under the `introspect` prefix. +//! +//! Steps: introspect, introspect-test, introspect-check-freestanding, +//! introspect-debug-test, introspect-debug-itest, introspect-adv. +const std = @import("std"); + +/// What the root passes in. The root owns target/optimize resolution and the +/// cloud9 module; this fragment derives everything else from them. +pub const Context = struct { + target: std.Build.ResolvedTarget, + optimize: std.builtin.OptimizeMode, + /// The cloud9 library module for `target`. Its `root_source_file` is also + /// used to instantiate cloud9 for the freestanding check target. + cloud9: *std.Build.Module, +}; + +pub const Artifacts = struct { + /// The `introspect` module, exported with `b.addModule` so dependents of + /// cloud9 can `.module("introspect")`. Built for any target; the Linux + /// layer is compiled in only when the target OS is Linux. + module: *std.Build.Module, + /// The demo 9P2000 server (binary `introspect`); null when the target is + /// not Linux. 9player's integration tests use it as their server. + demo: ?*std.Build.Step.Compile, + /// `introspect-test`: library (and demo) unit tests. + test_step: *std.Build.Step, + /// `introspect-check-freestanding`: the core compiled for riscv32-freestanding-none. + check_step: *std.Build.Step, + /// `introspect-debug-test`: linux/debug.zig unit tests; null when not Linux. + debug_test_step: ?*std.Build.Step, + /// `introspect-adv`: the hostile-client suites are attached by `add`, the + /// Linux-layer suite (which needs a 9player mount) by `addPlayerTests`. + adv_step: *std.Build.Step, +}; + +pub fn add(b: *std.Build, ctx: Context) Artifacts { + const target = ctx.target; + const optimize = ctx.optimize; + const is_linux = target.result.os.tag == .linux; + + // Build-time facts embedded into the demo (/build/*): jj change id, UTC + // time, optimize mode, target triple. `jj` is pointed at the build root + // (the cloud9 checkout) explicitly, so the result does not depend on the + // directory `zig build` was invoked from. + const build_options = b.addOptions(); + var code: u8 = 0; + const repo = b.build_root.path orelse "."; + const change_id = b.runAllowFail(&.{ "jj", "-R", repo, "log", "--no-graph", "-r", "@", "-T", "change_id.short()", "--ignore-working-copy" }, &code, .ignore) catch "unknown"; + build_options.addOption([]const u8, "change_id", std.mem.trim(u8, change_id, " \n\r\t")); + const stamp = b.runAllowFail(&.{ "date", "-u", "+%Y-%m-%dT%H:%M:%SZ" }, &code, .ignore) catch "unknown"; + build_options.addOption([]const u8, "build_time", std.mem.trim(u8, stamp, " \n\r\t")); + build_options.addOption([]const u8, "optimize", @tagName(optimize)); + build_options.addOption([]const u8, "target", target.result.zigTriple(b.allocator) catch "unknown"); + + // The library: freestanding core + vars, scratch (allocator), Linux layer. + // Exported under the name `introspect` for packages that depend on cloud9. + const lib_mod = b.addModule("introspect", .{ + .root_source_file = b.path("introspect/src/root.zig"), + .target = target, + .optimize = optimize, + .imports = &.{.{ .name = "cloud9", .module = ctx.cloud9 }}, + }); + + const test_step = b.step("introspect-test", "Run the introspect library's unit tests (and the demo's)"); + test_step.dependOn(&b.addRunArtifact(b.addTest(.{ .root_module = lib_mod })).step); + + // The core must compile without an OS (rule 1 of introspect/docs/LIBRARY.md). + // cloud9 is re-instantiated for that target from the same root source. + const fs_target = b.resolveTargetQuery(.{ .cpu_arch = .riscv32, .os_tag = .freestanding, .abi = .none }); + const fs_cloud9 = b.createModule(.{ + .root_source_file = ctx.cloud9.root_source_file.?, + .target = fs_target, + .optimize = optimize, + }); + const fs_check = b.addObject(.{ + .name = "introspect-freestanding", + .root_module = b.createModule(.{ + .root_source_file = b.path("introspect/src/freestanding_check.zig"), + .target = fs_target, + .optimize = optimize, + .imports = &.{.{ .name = "cloud9", .module = fs_cloud9 }}, + }), + }); + const check_step = b.step("introspect-check-freestanding", "Compile the introspect core for riscv32-freestanding-none"); + check_step.dependOn(&fs_check.step); + + // `introspect-adv` exists on every target so the step list is stable; its + // suites are attached below (Linux only) and by addPlayerTests. + const adv_step = b.step("introspect-adv", "Run introspect's adversarial suites (hostile clients, Linux layer; several minutes)"); + + if (!is_linux) { + adv_step.dependOn(&b.addFail("introspect-adv needs a Linux target (the demo server is Linux-only)").step); + return .{ .module = lib_mod, .demo = null, .test_step = test_step, .check_step = check_step, .debug_test_step = null, .adv_step = adv_step }; + } + + // The demo: a 9P2000 server whose tree is the binary itself (build-time, + // comptime and runtime facts, a worker thread, the Linux debug layer). + const demo_mod = b.createModule(.{ + .root_source_file = b.path("introspect/demo/main.zig"), + .target = target, + .optimize = optimize, + .imports = &.{ + .{ .name = "cloud9", .module = ctx.cloud9 }, + .{ .name = "build_options", .module = build_options.createModule() }, + .{ .name = "introspect", .module = lib_mod }, + }, + }); + const demo = b.addExecutable(.{ .name = "introspect", .root_module = demo_mod }); + const install_demo = b.addInstallArtifact(demo, .{}); + b.getInstallStep().dependOn(&install_demo.step); + b.step("introspect", "Build and install only the introspect demo server").dependOn(&install_demo.step); + test_step.dependOn(&b.addRunArtifact(b.addTest(.{ .root_module = demo_mod })).step); + + // Linux debug facilities (threads, stacks, breakpoints, panic): self-contained unit tests. + const debug_mod = b.createModule(.{ + .root_source_file = b.path("introspect/src/linux/debug.zig"), + .target = target, + .optimize = optimize, + }); + const debug_test_step = b.step("introspect-debug-test", "Run the introspect/src/linux/debug.zig unit tests"); + debug_test_step.dependOn(&b.addRunArtifact(b.addTest(.{ .root_module = debug_mod })).step); + + // Hostile raw-9P clients against the demo (framing, tags, floods; the core's + // /vars tree, snapshots, fid table). `--fast` as in the umbrella script. + inline for (.{ "adv_introspect_hostile", "adv_core_hostile" }) |suite| { + const run = b.addSystemCommand(&.{"bash"}); + run.addFileArg(b.path("introspect/test/" ++ suite ++ ".sh")); + run.addArtifactArg(demo); + run.addArg("--fast"); + adv_step.dependOn(&run.step); + } + + return .{ .module = lib_mod, .demo = demo, .test_step = test_step, .check_step = check_step, .debug_test_step = debug_test_step, .adv_step = adv_step }; +} + +/// The suites that drive the demo through a 9player mount: test/debug.sh +/// (threads, stacks, breakpoints, panic end to end) and +/// test/adv_linux_probe.sh (memory endpoints, signal machinery, poll loop). +/// Called by the root after the 9player fragment; `player` is null when +/// 9player is disabled, in which case the steps exist but fail with a notice. +/// Returns the `introspect-debug-itest` step (null when the target is not Linux). +pub fn addPlayerTests(b: *std.Build, arts: Artifacts, player: ?*std.Build.Step.Compile) ?*std.Build.Step { + const demo = arts.demo orelse return null; // not Linux: nothing to drive + const debug_itest = b.step("introspect-debug-itest", "Run introspect/test/debug.sh (threads, stacks, breakpoints, panic through 9player)"); + const exe = player orelse { + const fail = b.addFail("introspect-debug-itest and the Linux-layer adversarial suite need 9player (build with -D9player=true)"); + debug_itest.dependOn(&fail.step); + arts.adv_step.dependOn(&fail.step); + return debug_itest; + }; + const dbg = b.addSystemCommand(&.{"bash"}); + dbg.addFileArg(b.path("introspect/test/debug.sh")); + dbg.addArtifactArg(exe); + dbg.addArtifactArg(demo); + debug_itest.dependOn(&dbg.step); + + const adv_linux = b.addSystemCommand(&.{"bash"}); + adv_linux.addFileArg(b.path("introspect/test/adv_linux_probe.sh")); + adv_linux.addArtifactArg(exe); + adv_linux.addArtifactArg(demo); + arts.adv_step.dependOn(&adv_linux.step); + return debug_itest; +} |
