1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
|
//! Build fragment for 9proc: the 9P debug/introspection library (module
//! `9proc`), its freestanding check, the `9proc-demo` server and the
//! test suites under 9proc/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 `9proc/` 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 `9proc` prefix.
//!
//! Steps: 9proc, 9proc-test, 9proc-check-freestanding,
//! 9proc-debug-test, 9proc-debug-itest, 9proc-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 `9proc` module, exported with `b.addModule` so dependents of
/// cloud9 can `.module("9proc")`. 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 `9proc-demo`); null when the target is
/// not Linux. 9ns's integration tests use it as their server.
demo: ?*std.Build.Step.Compile,
/// `9proc-test`: library (and demo) unit tests.
test_step: *std.Build.Step,
/// `9proc-check-freestanding`: the core compiled for riscv32-freestanding-none.
check_step: *std.Build.Step,
/// `9proc-debug-test`: linux/debug.zig unit tests; null when not Linux.
debug_test_step: ?*std.Build.Step,
/// `9proc-adv`: the hostile-client suites are attached by `add`, the
/// Linux-layer suite (which needs a 9ns mount) by `addNsTests`.
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 `9proc` for packages that depend on cloud9.
const lib_mod = b.addModule("9proc", .{
.root_source_file = b.path("9proc/src/root.zig"),
.target = target,
.optimize = optimize,
.imports = &.{.{ .name = "cloud9", .module = ctx.cloud9 }},
});
const test_step = b.step("9proc-test", "Run the 9proc 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 9proc/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 = "9proc-freestanding",
.root_module = b.createModule(.{
.root_source_file = b.path("9proc/src/freestanding_check.zig"),
.target = fs_target,
.optimize = optimize,
.imports = &.{.{ .name = "cloud9", .module = fs_cloud9 }},
}),
});
const check_step = b.step("9proc-check-freestanding", "Compile the 9proc core for riscv32-freestanding-none");
check_step.dependOn(&fs_check.step);
// `9proc-adv` exists on every target so the step list is stable; its
// suites are attached below (Linux only) and by addNsTests.
const adv_step = b.step("9proc-adv", "Run 9proc's adversarial suites (hostile clients, Linux layer; several minutes)");
if (!is_linux) {
adv_step.dependOn(&b.addFail("9proc-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("9proc/demo/main.zig"),
.target = target,
.optimize = optimize,
.imports = &.{
.{ .name = "cloud9", .module = ctx.cloud9 },
.{ .name = "build_options", .module = build_options.createModule() },
.{ .name = "9proc", .module = lib_mod },
},
});
const demo = b.addExecutable(.{ .name = "9proc-demo", .root_module = demo_mod });
const install_demo = b.addInstallArtifact(demo, .{});
b.getInstallStep().dependOn(&install_demo.step);
b.step("9proc", "Build and install only the 9proc-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("9proc/src/linux/debug.zig"),
.target = target,
.optimize = optimize,
});
const debug_test_step = b.step("9proc-debug-test", "Run the 9proc/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_9proc_hostile", "adv_core_hostile" }) |suite| {
const run = b.addSystemCommand(&.{"bash"});
run.addFileArg(b.path("9proc/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 9ns 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 9ns fragment; `ns` is null when
/// 9ns is disabled, in which case the steps exist but fail with a notice.
/// Returns the `9proc-debug-itest` step (null when the target is not Linux).
pub fn addNsTests(b: *std.Build, arts: Artifacts, ns: ?*std.Build.Step.Compile) ?*std.Build.Step {
const demo = arts.demo orelse return null; // not Linux: nothing to drive
const debug_itest = b.step("9proc-debug-itest", "Run 9proc/test/debug.sh (threads, stacks, breakpoints, panic through 9ns)");
const exe = ns orelse {
const fail = b.addFail("9proc-debug-itest and the Linux-layer adversarial suite need 9ns (build with -D9ns=true)");
debug_itest.dependOn(&fail.step);
arts.adv_step.dependOn(&fail.step);
return debug_itest;
};
const dbg = b.addSystemCommand(&.{"bash"});
dbg.addFileArg(b.path("9proc/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("9proc/test/adv_linux_probe.sh"));
adv_linux.addArtifactArg(exe);
adv_linux.addArtifactArg(demo);
arts.adv_step.dependOn(&adv_linux.step);
return debug_itest;
}
|