-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathbuild.zig
More file actions
187 lines (168 loc) · 8.84 KB
/
Copy pathbuild.zig
File metadata and controls
187 lines (168 loc) · 8.84 KB
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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
const std = @import("std");
// magnet - generic, zero-allocation UDP game-networking stack.
// Layered module graph: edges below ARE the allowed dependency direction, so the
// downward-only layering (`proto` never imports `runtime`, `config` never imports `proto`, …)
// is enforced by the *compiler* - a layer module physically cannot import what it doesn't list.
// config ← trace core (leaf) wire (leaf)
// magnet (root.zig) ← core, wire, config, trace (proto/replication/runtime files live here)
const Graph = struct {
magnet: *std.Build.Module,
/// Run each module's tests.
runs: [8]*std.Build.Step,
/// Compile-only counterparts of `runs`. `check` uses these so a cross-compile
/// type-check covers every layer: the root module's tests do not pull in the other
/// modules' `test` blocks, so checking `magnet` alone silently skips `runtime/`
/// (where the platform-specific backends live and cross-target breakage hides).
compiles: [8]*std.Build.Step,
};
fn buildGraph(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, register: bool) Graph {
const mk = struct {
b: *std.Build,
target: std.Build.ResolvedTarget,
optimize: std.builtin.OptimizeMode,
fn m(self: @This(), path: []const u8) *std.Build.Module {
return self.b.createModule(.{ .root_source_file = self.b.path(path), .target = self.target, .optimize = self.optimize });
}
}{ .b = b, .target = target, .optimize = optimize };
// leaves
const trace_mod = mk.m("src/trace.zig");
const config_mod = mk.m("src/config.zig");
config_mod.addImport("trace", trace_mod);
const core_mod = mk.m("src/core/core.zig");
const wire_mod = mk.m("src/wire/wire.zig");
// L1–L3 protocol core
const proto_mod = mk.m("src/proto/proto.zig");
proto_mod.addImport("core", core_mod);
proto_mod.addImport("wire", wire_mod);
proto_mod.addImport("config", config_mod);
proto_mod.addImport("trace", trace_mod);
// L5 replication
const repl_mod = mk.m("src/replication/replication.zig");
repl_mod.addImport("core", core_mod);
repl_mod.addImport("wire", wire_mod);
// L0 runtime (top)
const rt_mod = mk.m("src/runtime/runtime.zig");
rt_mod.addImport("core", core_mod);
rt_mod.addImport("config", config_mod);
rt_mod.addImport("proto", proto_mod);
const magnet = if (register)
b.addModule("magnet", .{ .root_source_file = b.path("src/root.zig"), .target = target, .optimize = optimize })
else
mk.m("src/root.zig");
magnet.addImport("core", core_mod);
magnet.addImport("wire", wire_mod);
magnet.addImport("config", config_mod);
magnet.addImport("trace", trace_mod);
magnet.addImport("proto", proto_mod);
magnet.addImport("replication", repl_mod);
magnet.addImport("runtime", rt_mod);
var runs: [8]*std.Build.Step = undefined;
var compiles: [8]*std.Build.Step = undefined;
const mods = [_]*std.Build.Module{ core_mod, wire_mod, config_mod, trace_mod, proto_mod, repl_mod, rt_mod, magnet };
for (mods, 0..) |m, i| {
const t = b.addTest(.{ .root_module = m });
compiles[i] = &t.step;
runs[i] = &b.addRunArtifact(t).step;
}
return .{ .magnet = magnet, .runs = runs, .compiles = compiles };
}
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
// ---- mechanical rule enforcement (style / scope / imports) - now a backup to the
// module graph, which already makes upward imports impossible to compile. ----
const enforce_run = b.addSystemCommand(&.{ "sh", "-c", enforce_script });
enforce_run.has_side_effects = true;
const enforce_step = b.step("enforce", "Mechanically enforce layering + style rules");
enforce_step.dependOn(&enforce_run.step);
const api_audit_run = b.addSystemCommand(&.{ "sh", "tools/audit-api.sh" });
api_audit_run.has_side_effects = true;
const api_audit_step = b.step("api-audit", "Check the canonical public surface for removed/dynamic names");
api_audit_step.dependOn(&api_audit_run.step);
const g = buildGraph(b, target, optimize, true);
const test_step = b.step("test", "Run all unit + integration tests");
for (g.runs) |r| {
r.dependOn(&enforce_run.step);
test_step.dependOn(r);
}
// Type-check every layer, not just the root: `-Dtarget=x86_64-linux zig build check`
// is the only thing that compiles `runtime/io_linux.zig` from a non-Linux host.
const check_step = b.step("check", "Type-check the library (every module, honors -Dtarget)");
for (g.compiles) |c| check_step.dependOn(c);
// ---- API documentation (autodoc HTML from the `///` doc comments) ----
const docs_obj = b.addObject(.{ .name = "magnet", .root_module = g.magnet });
const docs_step = b.step("docs", "Generate API docs → zig-out/docs/ (open index.html)");
docs_step.dependOn(&b.addInstallDirectory(.{
.source_dir = docs_obj.getEmittedDocs(),
.install_dir = .prefix,
.install_subdir = "docs",
}).step);
// ---- example recipes ----
const examples = [_][]const u8{
"echo", "channels", "typed_messages", "serialize",
"congestion", "big_transfer", "encrypted", "connect_tokens",
"cert_identity", "migration", "udp_server", "sharded_server",
"discovery", "observability", "replicate", "fps",
"interpolation", "mmo_interest", "client_authority", "entity_refs",
"lockstep_rts", "p2p_rollback", "nat_punch", "file_stream",
"connectivity", "security_features",
};
const examples_step = b.step("examples", "Build all example recipes");
inline for (examples) |name| {
const exe_mod = b.createModule(.{ .root_source_file = b.path("examples/" ++ name ++ ".zig"), .target = target, .optimize = optimize });
exe_mod.addImport("magnet", g.magnet);
const exe = b.addExecutable(.{ .name = name, .root_module = exe_mod });
examples_step.dependOn(&b.addInstallArtifact(exe, .{}).step);
const run_step = b.step("run-" ++ name, "Run the " ++ name ++ " example");
run_step.dependOn(&b.addRunArtifact(exe).step);
}
// ---- perf: run the benchmark recipes in ReleaseFast ----
const bench_step = b.step("bench", "Run the perf benchmarks (ReleaseFast)");
inline for (.{ "bench_throughput", "bench_spatial" }) |name| {
const bm = b.createModule(.{ .root_source_file = b.path("examples/" ++ name ++ ".zig"), .target = target, .optimize = .ReleaseFast });
bm.addImport("magnet", buildGraph(b, target, .ReleaseFast, false).magnet);
const exe = b.addExecutable(.{ .name = name ++ "-bench", .root_module = bm });
bench_step.dependOn(&b.addRunArtifact(exe).step);
}
// ---- CI: the full suite across the optimize matrix ----
const matrix_step = b.step("test-matrix", "Run all tests across Debug/ReleaseSafe/ReleaseFast/ReleaseSmall");
for (g.runs) |r| matrix_step.dependOn(r); // Debug
inline for (.{ .ReleaseSafe, .ReleaseFast, .ReleaseSmall }) |mode| {
const mg = buildGraph(b, target, mode, false);
for (mg.runs) |r| {
r.dependOn(&enforce_run.step);
matrix_step.dependOn(r);
}
}
const ci_step = b.step("ci", "enforce + test-matrix + examples (the release gate)");
ci_step.dependOn(matrix_step);
ci_step.dependOn(examples_step);
ci_step.dependOn(api_audit_step);
const hardening_step = b.step("hardening", "Deterministic impairments, fuzz/soak, invariants, matrix, and optimize modes");
hardening_step.dependOn(matrix_step);
hardening_step.dependOn(api_audit_step);
const security_step = b.step("security-review", "Reproducible in-repository security review evidence package");
security_step.dependOn(hardening_step);
security_step.dependOn(examples_step);
}
// Layer import rules. (The module graph already enforces the cross-layer direction; this
// also bans `usingnamespace` and catches stray relative imports that skip a module.)
const enforce_script =
\\viol=0
\\check() {
\\ d="$1"; shift
\\ for pat in "$@"; do
\\ m=$(grep -rn "@import(\"[^\"]*$pat" "$d" 2>/dev/null)
\\ if [ -n "$m" ]; then echo "IMPORT VIOLATION: $d may not import '$pat'"; echo "$m"; viol=1; fi
\\ done
\\}
\\check src/core wire/ proto/ runtime/ replication/
\\check src/wire proto/ runtime/ replication/
\\check src/proto runtime/ replication/
\\check src/replication runtime/
\\if grep -rn "usingnamespace" src 2>/dev/null | grep -q .; then
\\ echo "STYLE VIOLATION: usingnamespace is banned"; grep -rn "usingnamespace" src; viol=1
\\fi
\\if [ "$viol" != "0" ]; then echo "enforce: FAILED"; exit 1; fi
\\echo "enforce: layering + style OK"
;