//! Build fragment for 9ns: mount a 9P2000 tree into a fresh mount //! namespace via FUSE and run a program in it (Linux only, no libc). 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 //! `9ns/` 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 `9ns` prefix. //! //! Steps: 9ns, 9ns-test, 9ns-itest, 9ns-adv. const std = @import("std"); /// What the root passes in. The root owns target/optimize resolution and the /// cloud9 module; the integration suites also need a 9P server to mount, /// which is the 9proc demo built by the sibling fragment. pub const Context = struct { target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, cloud9: *std.Build.Module, /// The `9proc-demo` server; null when 9proc is disabled, in /// which case the end-to-end steps exist but fail with a notice. proc_demo: ?*std.Build.Step.Compile, }; pub const Artifacts = struct { /// The 9ns executable (also installed by the plain `zig build`). exe: *std.Build.Step.Compile, /// `9ns-test`, `9ns-itest`, `9ns-adv`. test_step: *std.Build.Step, itest_step: *std.Build.Step, adv_step: *std.Build.Step, }; pub fn add(b: *std.Build, ctx: Context) Artifacts { const target = ctx.target; const optimize = ctx.optimize; std.debug.assert(target.result.os.tag == .linux); // the root only enables 9ns on Linux const ns_mod = b.createModule(.{ .root_source_file = b.path("9ns/src/main.zig"), .target = target, .optimize = optimize, .imports = &.{.{ .name = "cloud9", .module = ctx.cloud9 }}, }); const ns = b.addExecutable(.{ .name = "9ns", .root_module = ns_mod }); const install = b.addInstallArtifact(ns, .{}); b.getInstallStep().dependOn(&install.step); b.step("9ns", "Build and install only the 9ns binary").dependOn(&install.step); // Unit tests: protocol structs, session, bridge, namespace helpers (main.zig // reaches every module). const test_step = b.step("9ns-test", "Run 9ns's unit tests"); test_step.dependOn(&b.addRunArtifact(b.addTest(.{ .root_module = ns_mod })).step); // End-to-end suites: real namespaces, real FUSE, a real 9P server. // integration.sh: the single-connection transports; mntgen.sh: the // posted-registry multi-server mode (registry servers + lazy dial). const itest = b.step("9ns-itest", "Run 9ns/test/integration.sh and 9ns/test/mntgen.sh (needs unprivileged user namespaces and /dev/fuse)"); const adv = b.step("9ns-adv", "Run 9ns/test/adversarial.sh (hostile servers, namespaces, stress; several minutes)"); const demo = ctx.proc_demo orelse { const fail = b.addFail("9ns-itest and 9ns-adv need the 9proc-demo server (build with -D9proc=true)"); itest.dependOn(&fail.step); adv.dependOn(&fail.step); return .{ .exe = ns, .test_step = test_step, .itest_step = itest, .adv_step = adv }; }; inline for (.{ .{ itest, "integration" }, .{ itest, "mntgen" }, .{ adv, "adversarial" } }) |pair| { const run = b.addSystemCommand(&.{"bash"}); run.addFileArg(b.path("9ns/test/" ++ pair[1] ++ ".sh")); run.addArtifactArg(ns); run.addArtifactArg(demo); pair[0].dependOn(&run.step); } return .{ .exe = ns, .test_step = test_step, .itest_step = itest, .adv_step = adv }; }