authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-02-18 17:30:54-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-05-25 18:54:34-07:00
log6b7ce1fa22b301ac06d3bfa0a8938546966f684c
tree88d5a9c33855fa67da2c0f1ab2ab1f70ab724fd2
parentb3d162d6bfe82d84d55edd58016347a420732fe2

massage Step code into compiling


10 files changed, 630 insertions(+), 453 deletions(-)

lib/compiler/Maker.zig+64-53
......@@ -419,7 +419,7 @@ pub fn main(init: process.Init.Minimal) !void {
419419 var top_level_steps: std.StringArrayHashMapUnmanaged(Configuration.Step.Index) = .empty;
420420 for (configuration.steps, 0..) |*conf_step, step_index_usize| {
421421 const step_index: Configuration.Step.Index = @enumFromInt(step_index_usize);
422 const flags: Configuration.Step.Flags = @bitCast(configuration.extra[conf_step.extra_index]);
422 const flags = conf_step.flags(&configuration);
423423 if (flags.tag == .top_level) {
424424 const name = step_index.ptr(&configuration).name.slice(&configuration);
425425 try top_level_steps.put(arena, name, step_index);
......@@ -538,7 +538,7 @@ pub fn main(init: process.Init.Minimal) !void {
538538 var w: Watch = w: {
539539 if (!watch) break :w undefined;
540540 if (!Watch.have_impl) fatal("--watch not yet implemented for {t}", .{builtin.os.tag});
541 break :w try .init(graph.cache.cwd, &scanned_config.configuration, maker.steps);
541 break :w try .init(&maker);
542542 };
543543
544544 const now = Io.Clock.Timestamp.now(io, .awake);
......@@ -546,14 +546,10 @@ pub fn main(init: process.Init.Minimal) !void {
546546 maker.web_server = if (webui_listen) |listen_address| ws: {
547547 if (builtin.single_threaded) unreachable; // `fatal` above
548548 break :ws .init(.{
549 .gpa = gpa,
550 .graph = &graph,
551 .all_steps = maker.step_stack.keys(),
549 .maker = &maker,
552550 .root_prog_node = main_progress_node,
553 .watch = watch,
554551 .listen_address = listen_address,
555552 .base_timestamp = now,
556 .configuration = &scanned_config.configuration,
557553 });
558554 } else null;
559555
......@@ -564,7 +560,9 @@ pub fn main(init: process.Init.Minimal) !void {
564560 rebuild: while (true) : (if (maker.error_style.clearOnUpdate()) {
565561 const stderr = try io.lockStderr(&stdio_buffer_allocation, graph.stderr_mode);
566562 defer io.unlockStderr();
567 try stderr.file_writer.interface.writeAll("\x1B[2J\x1B[3J\x1B[H");
563 stderr.file_writer.interface.writeAll("\x1B[2J\x1B[3J\x1B[H") catch |err| switch (err) {
564 error.WriteFailed => return stderr.file_writer.err.?,
565 };
568566 }) {
569567 if (maker.web_server) |*ws| ws.startBuild();
570568
......@@ -608,15 +606,15 @@ pub fn main(init: process.Init.Minimal) !void {
608606 // recursive dependants.
609607 var caption_buf: [std.Progress.Node.max_name_len]u8 = undefined;
610608 const caption = std.fmt.bufPrint(&caption_buf, "watching {d} directories, {d} processes", .{
611 w.dir_count, countSubProcesses(maker.steps, maker.step_stack.keys()),
609 w.dir_count, countSubProcesses(&maker),
612610 }) catch &caption_buf;
613611 var debouncing_node = main_progress_node.start(caption, 0);
614612 var in_debounce = false;
615 while (true) switch (try w.wait(gpa, io, if (in_debounce) .{ .ms = debounce_interval_ms } else .none)) {
613 while (true) switch (try w.wait(if (in_debounce) .{ .ms = debounce_interval_ms } else .none)) {
616614 .timeout => {
617615 assert(in_debounce);
618616 debouncing_node.end();
619 markFailedStepsDirty(gpa, maker.steps, maker.step_stack.keys());
617 markFailedStepsDirty(&maker);
620618 continue :rebuild;
621619 },
622620 .dirty => if (!in_debounce) {
......@@ -629,18 +627,20 @@ pub fn main(init: process.Init.Minimal) !void {
629627 }
630628}
631629
632fn markFailedStepsDirty(gpa: Allocator, make_steps: []Step, all_steps: []const Configuration.Step.Index) void {
630fn markFailedStepsDirty(maker: *Maker) void {
631 const all_steps = maker.step_stack.keys();
632
633633 for (all_steps) |step_index| {
634 const step = &make_steps[@intFromEnum(step_index)];
634 const step = maker.stepByIndex(step_index);
635635 switch (step.state) {
636 .dependency_failure, .failure, .skipped => _ = step.invalidateResult(gpa),
636 .dependency_failure, .failure, .skipped => _ = maker.invalidateResult(step),
637637 else => continue,
638638 }
639639 }
640640 // Now that all dirty steps have been found, the remaining steps that
641641 // succeeded from last run shall be marked "cached".
642642 for (all_steps) |step_index| {
643 const step = &make_steps[@intFromEnum(step_index)];
643 const step = maker.stepByIndex(step_index);
644644 switch (step.state) {
645645 .success => step.result_cached = true,
646646 else => continue,
......@@ -648,10 +648,11 @@ fn markFailedStepsDirty(gpa: Allocator, make_steps: []Step, all_steps: []const C
648648 }
649649}
650650
651fn countSubProcesses(make_steps: []Step, all_steps: []const Configuration.Step.Index) usize {
651fn countSubProcesses(maker: *Maker) usize {
652 const all_steps = maker.step_stack.keys();
652653 var count: usize = 0;
653654 for (all_steps) |step_index| {
654 const s = &make_steps[@intFromEnum(step_index)];
655 const s = maker.stepByIndex(step_index);
655656 count += @intFromBool(s.getZigProcess() != null);
656657 }
657658 return count;
......@@ -664,7 +665,7 @@ const InstallPaths = struct {
664665 include: Path,
665666};
666667
667fn stepByIndex(maker: *const Maker, i: Configuration.Step.Index) *Step {
668pub fn stepByIndex(maker: *const Maker, i: Configuration.Step.Index) *Step {
668669 return &maker.steps[@intFromEnum(i)];
669670}
670671
......@@ -676,7 +677,10 @@ fn prepare(maker: *Maker, step_names: []const []const u8) !void {
676677 const step_stack = &maker.step_stack;
677678 const c = &maker.scanned_config.configuration;
678679
679 @memset(maker.steps, .{});
680 for (maker.steps, 0..) |*step, step_index_usize| {
681 const step_index: Configuration.Step.Index = @enumFromInt(step_index_usize);
682 step.* = .{ .extended = .init(step_index.ptr(c).flags(c).tag) };
683 }
680684
681685 if (step_names.len == 0) {
682686 try step_stack.put(gpa, c.default_step, {});
......@@ -699,7 +703,7 @@ fn prepare(maker: *Maker, step_names: []const []const u8) !void {
699703 rand.shuffle(Configuration.Step.Index, starting_steps);
700704
701705 for (starting_steps) |s| {
702 try constructGraphAndCheckForDependencyLoop(gpa, c, maker.steps, s, &maker.step_stack, rand);
706 try constructGraphAndCheckForDependencyLoop(maker, s, &maker.step_stack, rand);
703707 }
704708
705709 {
......@@ -847,13 +851,8 @@ fn makeStepNames(
847851 }
848852
849853 assert(mode == .limit);
850 var f = Fuzz.init(
851 gpa,
852 io,
853 step_stack.keys(),
854 parent_prog_node,
855 mode,
856 ) catch |err| fatal("failed to start fuzzer: {t}", .{err});
854 var f = Fuzz.init(maker, step_stack.keys(), parent_prog_node, mode) catch |err|
855 fatal("failed to start fuzzer: {t}", .{err});
857856 defer f.deinit();
858857
859858 f.start();
......@@ -1048,13 +1047,7 @@ fn makeStep(
10481047
10491048 .success, .skipped => {},
10501049 }
1051 } else if (make_step.make(.{
1052 .progress_node = step_prog_node,
1053 .watch = maker.watch,
1054 .web_server = if (maker.web_server) |*ws| ws else null,
1055 .unit_test_timeout_ns = maker.unit_test_timeout_ns,
1056 .gpa = gpa,
1057 })) state: {
1050 } else if (Step.make(step_index, maker, step_prog_node)) state: {
10581051 break :state .success;
10591052 } else |err| switch (err) {
10601053 error.MakeFailed => .failure,
......@@ -1091,7 +1084,7 @@ fn makeStep(
10911084 {
10921085 const stderr = try io.lockStderr(&stdio_buffer_allocation, graph.stderr_mode);
10931086 defer io.unlockStderr();
1094 printErrorMessages(gpa, c, maker.steps, step_index, .{}, stderr.terminal(), maker.error_style, maker.multiline_errors) catch |err| switch (err) {
1087 printErrorMessages(maker, step_index, .{}, stderr.terminal(), maker.error_style, maker.multiline_errors) catch |err| switch (err) {
10951088 error.Canceled => |e| return e,
10961089 error.WriteFailed => switch (stderr.file_writer.err.?) {
10971090 error.Canceled => |e| return e,
......@@ -1136,7 +1129,7 @@ fn makeStep(
11361129}
11371130
11381131fn printTreeStep(
1139 maker: *const Maker,
1132 maker: *Maker,
11401133 step_index: Configuration.Step.Index,
11411134 stderr: Io.Terminal,
11421135 parent_node: *PrintNode,
......@@ -1211,7 +1204,7 @@ fn printTreeStep(
12111204 }
12121205}
12131206
1214fn printStepStatus(maker: *const Maker, step_index: Configuration.Step.Index, stderr: Io.Terminal) !void {
1207fn printStepStatus(maker: *Maker, step_index: Configuration.Step.Index, stderr: Io.Terminal) !void {
12151208 const s = maker.stepByIndex(step_index);
12161209 const writer = stderr.writer;
12171210 switch (s.state) {
......@@ -1293,20 +1286,20 @@ fn printStepStatus(maker: *const Maker, step_index: Configuration.Step.Index, st
12931286 try stderr.setColor(.reset);
12941287 },
12951288 .failure => {
1296 try printStepFailure(maker.steps, step_index, stderr, false);
1289 try printStepFailure(maker, step_index, stderr, false);
12971290 try stderr.setColor(.reset);
12981291 },
12991292 }
13001293}
13011294
13021295fn printStepFailure(
1303 make_steps: []Step,
1296 maker: *Maker,
13041297 step_index: Configuration.Step.Index,
13051298 stderr: Io.Terminal,
13061299 dim: bool,
13071300) !void {
13081301 const w = stderr.writer;
1309 const s = &make_steps[@intFromEnum(step_index)];
1302 const s = maker.stepByIndex(step_index);
13101303 if (s.result_error_bundle.errorMessageCount() > 0) {
13111304 try stderr.setColor(.red);
13121305 try w.print(" {d} errors\n", .{
......@@ -1428,14 +1421,14 @@ fn printChildNodePrefix(stderr: Io.Terminal) !void {
14281421/// when it finishes executing in `makeStep`, it spawns next steps to run in
14291422/// random order
14301423fn constructGraphAndCheckForDependencyLoop(
1431 gpa: Allocator,
1432 c: *const Configuration,
1433 steps: []Step,
1424 maker: *Maker,
14341425 step_index: Configuration.Step.Index,
14351426 step_stack: *std.AutoArrayHashMapUnmanaged(Configuration.Step.Index, void),
14361427 rand: std.Random,
14371428) error{ DependencyLoopDetected, OutOfMemory }!void {
1438 const make_step: *Step = &steps[@intFromEnum(step_index)];
1429 const c = &maker.scanned_config.configuration;
1430 const gpa = maker.gpa;
1431 const make_step = maker.stepByIndex(step_index);
14391432 switch (make_step.state) {
14401433 .precheck_started => {
14411434 log.err("dependency loop detected: {s}", .{step_index.ptr(c).name.slice(c)});
......@@ -1456,10 +1449,10 @@ fn constructGraphAndCheckForDependencyLoop(
14561449 rand.shuffle(Configuration.Step.Index, deps);
14571450
14581451 for (deps) |dep| {
1459 const dep_step: *Step = &steps[@intFromEnum(dep)];
1452 const dep_step = maker.stepByIndex(dep);
14601453 try step_stack.put(gpa, dep, {});
14611454 try dep_step.dependants.append(gpa, step_index);
1462 constructGraphAndCheckForDependencyLoop(gpa, c, steps, dep, step_stack, rand) catch |err| switch (err) {
1455 constructGraphAndCheckForDependencyLoop(maker, dep, step_stack, rand) catch |err| switch (err) {
14631456 error.DependencyLoopDetected => {
14641457 log.info("needed by: {s}", .{step_index.ptr(c).name.slice(c)});
14651458 return err;
......@@ -1482,16 +1475,34 @@ fn constructGraphAndCheckForDependencyLoop(
14821475 }
14831476}
14841477
1478/// When file watching, prepares the step for being re-evaluated. Returns
1479/// `true` if the step was newly invalidated, `false` if it was already
1480/// invalidated.
1481pub fn invalidateResult(maker: *Maker, step: *Step) bool {
1482 if (step.state == .precheck_done) return false;
1483 const gpa = maker.gpa;
1484 assert(step.pending_deps == 0);
1485 step.state = .precheck_done;
1486 step.reset(gpa);
1487 for (step.dependants.items) |dependant_index| {
1488 const dependant = maker.stepByIndex(dependant_index);
1489 _ = invalidateResult(maker, dependant);
1490 dependant.pending_deps += 1;
1491 }
1492 return true;
1493}
1494
14851495pub fn printErrorMessages(
1486 gpa: Allocator,
1487 c: *const Configuration,
1488 make_steps: []Step,
1496 maker: *Maker,
14891497 failing_step_index: Configuration.Step.Index,
14901498 options: std.zig.ErrorBundle.RenderOptions,
14911499 stderr: Io.Terminal,
14921500 error_style: ErrorStyle,
14931501 multiline_errors: MultilineErrors,
14941502) !void {
1503 const c = &maker.scanned_config.configuration;
1504 const gpa = maker.gpa;
1505 log.err("TODO also report if result_oom flag is set", .{});
14951506 const writer = stderr.writer;
14961507 if (error_style.verboseContext()) {
14971508 // Provide context for where these error messages are coming from by
......@@ -1500,7 +1511,7 @@ pub fn printErrorMessages(
15001511 defer step_stack.deinit(gpa);
15011512 try step_stack.append(gpa, failing_step_index);
15021513 while (true) {
1503 const last_step = &make_steps[@intFromEnum(step_stack.items[step_stack.items.len - 1])];
1514 const last_step = maker.stepByIndex(step_stack.items[step_stack.items.len - 1]);
15041515 if (last_step.dependants.items.len == 0) break;
15051516 try step_stack.append(gpa, last_step.dependants.items[0]);
15061517 }
......@@ -1517,7 +1528,7 @@ pub fn printErrorMessages(
15171528 try writer.writeAll(step_index.ptr(c).name.slice(c));
15181529
15191530 if (step_index == failing_step_index) {
1520 try printStepFailure(make_steps, step_index, stderr, true);
1531 try printStepFailure(maker, step_index, stderr, true);
15211532 } else {
15221533 try writer.writeAll("\n");
15231534 }
......@@ -1527,11 +1538,11 @@ pub fn printErrorMessages(
15271538 // Just print the failing step itself.
15281539 try stderr.setColor(.dim);
15291540 try writer.writeAll(failing_step_index.ptr(c).name.slice(c));
1530 try printStepFailure(make_steps, failing_step_index, stderr, true);
1541 try printStepFailure(maker, failing_step_index, stderr, true);
15311542 try stderr.setColor(.reset);
15321543 }
15331544
1534 const failing_step = &make_steps[@intFromEnum(failing_step_index)];
1545 const failing_step = maker.stepByIndex(failing_step_index);
15351546
15361547 if (failing_step.result_stderr.len > 0) {
15371548 try writer.writeAll(failing_step.result_stderr);
lib/compiler/Maker/Fuzz.zig+39-19
......@@ -15,8 +15,7 @@ const log = std.log;
1515const Maker = @import("../Maker.zig");
1616const WebServer = @import("WebServer.zig");
1717
18gpa: Allocator,
19io: Io,
18maker: *Maker,
2019mode: Mode,
2120
2221/// Allocated into `gpa`.
......@@ -76,12 +75,15 @@ const CoverageMap = struct {
7675};
7776
7877pub fn init(
79 gpa: Allocator,
80 io: Io,
78 maker: *Maker,
8179 all_steps: []const Configuration.Step.Index,
8280 root_prog_node: std.Progress.Node,
8381 mode: Mode,
8482) error{ OutOfMemory, Canceled }!Fuzz {
83 const graph = maker.graph;
84 const gpa = graph.cache.gpa;
85 const io = graph.io;
86
8587 const run_steps: []const Configuration.Step.Index = steps: {
8688 var steps: std.ArrayList(Configuration.Step.Index) = .empty;
8789 defer steps.deinit(gpa);
......@@ -115,8 +117,7 @@ pub fn init(
115117 }
116118
117119 return .{
118 .gpa = gpa,
119 .io = io,
120 .maker = maker,
120121 .mode = mode,
121122 .run_steps = run_steps,
122123 .group = .init,
......@@ -131,7 +132,10 @@ pub fn init(
131132}
132133
133134pub fn start(fuzz: *Fuzz) void {
134 const io = fuzz.io;
135 const maker = fuzz.maker;
136 const graph = maker.graph;
137 const io = graph.io;
138
135139 fuzz.prog_node = fuzz.root_prog_node.start("Fuzzing", 0);
136140
137141 if (fuzz.mode == .forever) {
......@@ -149,10 +153,14 @@ pub fn start(fuzz: *Fuzz) void {
149153}
150154
151155pub fn deinit(fuzz: *Fuzz) void {
152 const io = fuzz.io;
156 const maker = fuzz.maker;
157 const graph = maker.graph;
158 const io = graph.io;
159 const gpa = maker.gpa;
160
153161 fuzz.group.cancel(io);
154162 fuzz.prog_node.end();
155 fuzz.gpa.free(fuzz.run_steps);
163 gpa.free(fuzz.run_steps);
156164}
157165
158166fn rebuildTestsWorkerRun(run: Configuration.Step.Index, gpa: Allocator, parent_prog_node: std.Progress.Node) void {
......@@ -215,19 +223,20 @@ fn fuzzWorkerRun(fuzz: *Fuzz, run: Configuration.Step.Index) void {
215223pub fn serveSourcesTar(fuzz: *Fuzz, req: *std.http.Server.Request) !void {
216224 if (true) @panic("TODO");
217225 assert(fuzz.mode == .forever);
226 const gpa = fuzz.maker.gpa;
218227
219 var arena_state: std.heap.ArenaAllocator = .init(fuzz.gpa);
228 var arena_state: std.heap.ArenaAllocator = .init(gpa);
220229 defer arena_state.deinit();
221230 const arena = arena_state.allocator();
222231
223232 const DedupTable = std.ArrayHashMapUnmanaged(Build.Cache.Path, void, Build.Cache.Path.TableAdapter, false);
224233 var dedup_table: DedupTable = .empty;
225 defer dedup_table.deinit(fuzz.gpa);
234 defer dedup_table.deinit(gpa);
226235
227236 for (fuzz.run_steps) |run_step| {
228237 const compile_inputs = run_step.producer.?.step.inputs.table;
229238 for (compile_inputs.keys(), compile_inputs.values()) |dir_path, *file_list| {
230 try dedup_table.ensureUnusedCapacity(fuzz.gpa, file_list.items.len);
239 try dedup_table.ensureUnusedCapacity(gpa, file_list.items.len);
231240 for (file_list.items) |sub_path| {
232241 if (!std.mem.endsWith(u8, sub_path, ".zig")) continue;
233242 const joined_path = try dir_path.join(arena, sub_path);
......@@ -266,7 +275,9 @@ pub fn sendUpdate(
266275 socket: *std.http.Server.WebSocket,
267276 prev: *Previous,
268277) !void {
269 const io = fuzz.io;
278 const maker = fuzz.maker;
279 const graph = maker.graph;
280 const io = graph.io;
270281
271282 try fuzz.coverage_mutex.lock(io);
272283 defer fuzz.coverage_mutex.unlock(io);
......@@ -337,7 +348,9 @@ fn coverageRun(fuzz: *Fuzz) void {
337348}
338349
339350fn coverageRunCancelable(fuzz: *Fuzz) Io.Cancelable!void {
340 const io = fuzz.io;
351 const maker = fuzz.maker;
352 const graph = maker.graph;
353 const io = graph.io;
341354
342355 try fuzz.queue_mutex.lock(io);
343356 defer fuzz.queue_mutex.unlock(io);
......@@ -363,8 +376,10 @@ fn prepareTables(fuzz: *Fuzz, run_step_index: Configuration.Step.Index, coverage
363376 if (true) @panic("TODO");
364377 assert(fuzz.mode == .forever);
365378 const ws = fuzz.mode.forever.ws;
366 const gpa = fuzz.gpa;
367 const io = fuzz.io;
379 const maker = fuzz.maker;
380 const graph = maker.graph;
381 const io = graph.io;
382 const gpa = maker.gpa;
368383
369384 try fuzz.coverage_mutex.lock(io);
370385 defer fuzz.coverage_mutex.unlock(io);
......@@ -470,7 +485,10 @@ fn prepareTables(fuzz: *Fuzz, run_step_index: Configuration.Step.Index, coverage
470485}
471486
472487fn addEntryPoint(fuzz: *Fuzz, coverage_id: u64, addr: u64) error{ AlreadyReported, OutOfMemory, Canceled }!void {
473 const io = fuzz.io;
488 const maker = fuzz.maker;
489 const graph = maker.graph;
490 const io = graph.io;
491 const gpa = maker.gpa;
474492
475493 try fuzz.coverage_mutex.lock(io);
476494 defer fuzz.coverage_mutex.unlock(io);
......@@ -516,13 +534,15 @@ fn addEntryPoint(fuzz: *Fuzz, coverage_id: u64, addr: u64) error{ AlreadyReporte
516534 });
517535 }
518536 }
519 try coverage_map.entry_points.append(fuzz.gpa, @intCast(index));
537 try coverage_map.entry_points.append(gpa, @intCast(index));
520538}
521539
522540pub fn waitAndPrintReport(fuzz: *Fuzz) Io.Cancelable!void {
523541 if (true) @panic("TODO");
524542 assert(fuzz.mode == .limit);
525 const io = fuzz.io;
543 const maker = fuzz.maker;
544 const graph = maker.graph;
545 const io = graph.io;
526546
527547 try fuzz.group.await(io);
528548 fuzz.group = .init;
lib/compiler/Maker/Step.zig+124-49
......@@ -1,4 +1,5 @@
1//! The state that maker needs in order to process a step.
1//! The *mutable* state that `Maker` needs in order to process one node from
2//! the build graph.
23const Step = @This();
34
45const builtin = @import("builtin");
......@@ -14,14 +15,20 @@ const Configuration = std.Build.Configuration;
1415const assert = std.debug.assert;
1516
1617const WebServer = @import("WebServer.zig");
18const Maker = @import("../Maker.zig");
1719
18pub const Compile = void; // @import("Step/Compile.zig");
19pub const Run = void; // @import("Step/Run.zig");
20const Compile = @import("Step/Compile.zig");
21const Run = @import("Step/Run.zig");
2022
2123/// Avoid false sharing.
2224_: void align(std.atomic.cache_line) = {},
2325
26/// Extra data for specific types of steps.
27extended: Extended,
28
29/// This field is atomically accessed multi-threaded.
2430state: State = .precheck_unstarted,
31
2532dependants: std.ArrayList(Configuration.Step.Index) = .empty,
2633/// Collects the set of files that retrigger this step to run.
2734///
......@@ -38,6 +45,8 @@ result_error_msgs: std.ArrayList([]const u8) = .empty,
3845result_error_bundle: std.zig.ErrorBundle = .empty,
3946result_stderr: []const u8 = "",
4047result_cached: bool = false,
48/// Indicates error information is missing due to allocation failure.
49result_oom: bool = false,
4150result_duration_ns: ?u64 = null,
4251/// 0 means unavailable or not reported.
4352result_peak_rss: usize = 0,
......@@ -46,6 +55,70 @@ result_peak_rss: usize = 0,
4655result_failed_command: ?[]const u8 = null,
4756test_results: TestResults = .{},
4857
58comptime {
59 // Common cache line size is 128. This check prevents accidentally crossing
60 // an additional cache line. In the future it might be nice to try to fit
61 // this struct in 128 bytes or less.
62 assert(@sizeOf(@This()) <= 128 * 3);
63}
64
65pub const Extended = union(enum) {
66 check_file: Todo,
67 check_object: Todo,
68 compile: Compile,
69 config_header: Todo,
70 fail: Todo,
71 fmt: Todo,
72 install_artifact: Todo,
73 install_dir: Todo,
74 install_file: Todo,
75 objcopy: Todo,
76 options: Todo,
77 remove_dir: Todo,
78 run: Run,
79 top_level: Todo,
80 translate_c: Todo,
81 update_source_files: Todo,
82 write_file: Todo,
83
84 pub fn init(tag: Configuration.Step.Tag) Extended {
85 return switch (tag) {
86 .check_file => .{ .check_file = .{} },
87 .check_object => .{ .check_object = .{} },
88 .compile => .{ .compile = .{} },
89 .config_header => .{ .config_header = .{} },
90 .fail => .{ .fail = .{} },
91 .fmt => .{ .fmt = .{} },
92 .install_artifact => .{ .install_artifact = .{} },
93 .install_dir => .{ .install_dir = .{} },
94 .install_file => .{ .install_file = .{} },
95 .objcopy => .{ .objcopy = .{} },
96 .options => .{ .options = .{} },
97 .remove_dir => .{ .remove_dir = .{} },
98 .run => .{ .run = .{} },
99 .top_level => .{ .top_level = .{} },
100 .translate_c => .{ .translate_c = .{} },
101 .update_source_files => .{ .update_source_files = .{} },
102 .write_file => .{ .write_file = .{} },
103 };
104 }
105
106 pub const Todo = struct {
107 pub fn make(
108 todo: *Todo,
109 step_index: Configuration.Step.Index,
110 maker: *Maker,
111 progress_node: std.Progress.Node,
112 ) Step.ExtendedMakeError!void {
113 _ = todo;
114 _ = step_index;
115 _ = maker;
116 _ = progress_node;
117 @panic("TODO implement another step type");
118 }
119 };
120};
121
49122pub const State = enum {
50123 precheck_unstarted,
51124 precheck_started,
......@@ -128,43 +201,51 @@ pub const TestResults = struct {
128201 }
129202};
130203
131pub const MakeOptions = struct {
132 progress_node: std.Progress.Node,
133 watch: bool,
134 web_server: ?*WebServer,
135 /// If set, this is a timeout to enforce on all individual unit tests, in nanoseconds.
136 unit_test_timeout_ns: ?u64,
137 /// Not to be confused with `Build.allocator`, which is an alias of `Build.graph.arena`.
138 gpa: Allocator,
204pub const MakeError = error{
205 /// Indicates the error is already reported.
206 MakeFailed,
207 MakeSkipped,
139208};
140209
141pub const MakeFn = *const fn (step: *Step, options: MakeOptions) anyerror!void;
210pub const ExtendedMakeError = MakeError || Allocator.Error;
142211
143/// If the Step's `make` function reports `error.MakeFailed`, it indicates they
144/// have already reported the error. Otherwise, we add a simple error report
145/// here.
146pub fn make(s: *Step, options: MakeOptions) error{ MakeFailed, MakeSkipped }!void {
147 if (true) @panic("TODO Step.make");
148 const arena = s.owner.allocator;
149 const graph = s.owner.graph;
212pub fn make(
213 step_index: Configuration.Step.Index,
214 maker: *Maker,
215 progress_node: std.Progress.Node,
216) MakeError!void {
217 const graph = maker.graph;
218 const process_arena = graph.arena; // TODO don't leak into the process arena
150219 const io = graph.io;
220 const c = &maker.scanned_config.configuration;
221 const conf_step = step_index.ptr(c);
222 const s = maker.stepByIndex(step_index);
151223
152224 var start_ts: ?Io.Timestamp = t: {
153225 if (!graph.time_report) break :t null;
154 if (s.id == .compile) break :t null;
155 if (s.id == .run and s.cast(Run).?.stdio == .zig_test) break :t null;
226 const flags = conf_step.flags(c);
227 switch (flags.tag) {
228 .compile => break :t null,
229 .run => {
230 const run_flags: Configuration.Step.Run.Flags = @bitCast(flags);
231 if (run_flags.stdio == .zig_test) break :t null;
232 },
233 else => {},
234 }
156235 break :t Io.Clock.awake.now(io);
157236 };
158 const make_result = s.makeFn(s, options);
237 const make_result = switch (s.extended) {
238 inline else => |*extended| extended.make(step_index, maker, progress_node),
239 };
159240 if (start_ts) |*ts| {
160241 const duration = ts.untilNow(io, .awake);
161 options.web_server.?.updateTimeReportGeneric(s, duration);
242 maker.web_server.?.updateTimeReportGeneric(step_index, duration);
162243 }
163244
164245 make_result catch |err| switch (err) {
165246 error.MakeFailed, error.MakeSkipped => |e| return e,
166 else => {
167 s.result_error_msgs.append(arena, @errorName(err)) catch @panic("OOM");
247 error.OutOfMemory => {
248 s.result_oom = true;
168249 return error.MakeFailed;
169250 },
170251 };
......@@ -173,30 +254,19 @@ pub fn make(s: *Step, options: MakeOptions) error{ MakeFailed, MakeSkipped }!voi
173254 return error.MakeFailed;
174255 }
175256
176 if (s.max_rss != 0 and s.result_peak_rss > s.max_rss) {
177 const msg = std.fmt.allocPrint(arena, "memory usage peaked at {0B:.2} ({0d} bytes), exceeding the declared upper bound of {1B:.2} ({1d} bytes)", .{
178 s.result_peak_rss, s.max_rss,
179 }) catch @panic("OOM");
180 s.result_error_msgs.append(arena, msg) catch @panic("OOM");
257 const max_rss = conf_step.max_rss.toBytes();
258 if (max_rss != 0 and s.result_peak_rss > max_rss) {
259 if (std.fmt.allocPrint(
260 process_arena,
261 "memory usage peaked at {0B:.2} ({0d} bytes), exceeding the declared upper bound of {1B:.2} ({1d} bytes)",
262 .{ s.result_peak_rss, max_rss },
263 )) |msg| {
264 s.oomWrap(s.result_error_msgs.append(process_arena, msg));
265 } else |_| s.result_oom = true;
181266 }
182267}
183268
184/// Implementation detail of file watching. Prepares the step for being re-evaluated.
185/// Returns `true` if the step was newly invalidated, `false` if it was already invalidated.
186pub fn invalidateResult(step: *Step, gpa: Allocator) bool {
187 if (true) @panic("TODO Step.invalidateResult");
188 if (step.state == .precheck_done) return false;
189 assert(step.pending_deps == 0);
190 step.state = .precheck_done;
191 step.reset(gpa);
192 for (step.dependants.items) |dependant| {
193 _ = dependant.invalidateResult(gpa);
194 dependant.pending_deps += 1;
195 }
196 return true;
197}
198
199/// Implementation detail of file watching and forced rebuilds. Prepares the step for being re-evaluated.
269/// Prepares the step for being re-evaluated.
200270pub fn reset(step: *Step, gpa: Allocator) void {
201271 assert(step.state == .precheck_done);
202272
......@@ -547,9 +617,8 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool, web_server: ?*WebSer
547617}
548618
549619pub fn getZigProcess(s: *Step) ?*ZigProcess {
550 if (true) @panic("TODO getZigProcess");
551 return switch (s.id) {
552 .compile => s.cast(Compile).?.zig_process,
620 return switch (s.extended) {
621 .compile => |*compile| compile.zig_process,
553622 else => null,
554623 };
555624}
......@@ -838,3 +907,9 @@ pub fn allocPrintCmd(
838907 }
839908 return aw.toOwnedSlice();
840909}
910
911fn oomWrap(s: *Step, result: error{OutOfMemory}!void) void {
912 result catch {
913 s.result_oom = true;
914 };
915}
lib/compiler/Maker/Step/Compile.zig+123-42
......@@ -1,19 +1,40 @@
1const Compile = @This();
2
3const std = @import("std");
4const Allocator = std.mem.Allocator;
5const Configuration = std.Build.Configuration;
6const Dir = std.Io.Dir;
7const Path = std.Build.Cache.Path;
8const Module = std.Build.Configuration.Module;
9const Io = std.Io;
10const Sha256 = std.crypto.hash.sha2.Sha256;
11const assert = std.debug.assert;
12const mem = std.mem;
13
14const Step = @import("../Step.zig");
15const Maker = @import("../../Maker.zig");
16
117/// Populated during the make phase when there is a long-lived compiler process.
218/// Managed by the build runner, not user build script.
3zig_process: ?*Step.ZigProcess,
4
5fn make(step: *Step, options: Step.MakeOptions) !void {
6 const b = step.owner;
7 const compile: *Compile = @fieldParentPtr("step", step);
8
9 const zig_args = try getZigArgs(compile, false);
19zig_process: ?*Step.ZigProcess = null,
20
21pub fn make(
22 compile: *Compile,
23 step_index: Configuration.Step.Index,
24 maker: *Maker,
25 progress_node: std.Progress.Node,
26) Step.ExtendedMakeError!void {
27 if (true) @panic("TODO implement compile.make()");
28 const graph = maker.graph;
29 const step = maker.stepByIndex(step_index);
30 const zig_args = try getZigArgs(compile, maker, false);
31 const process_arena = graph.arena; // TODO don't leak into the process_arena
1032
1133 const maybe_output_dir = step.evalZigProcess(
1234 zig_args,
13 options.progress_node,
14 (b.graph.incremental == true) and (options.watch or options.web_server != null),
15 options.web_server,
16 options.gpa,
35 progress_node,
36 (graph.incremental == true) and (maker.watch or maker.web_server != null),
37 maker,
1738 ) catch |err| switch (err) {
1839 error.NeedCompileErrorCheck => {
1940 assert(compile.expect_errors != null);
......@@ -26,7 +47,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
2647 // Update generated files
2748 if (maybe_output_dir) |output_dir| {
2849 if (compile.emit_directory) |lp| {
29 lp.path = b.fmt("{f}", .{output_dir});
50 lp.path = try std.fmt.allocPrint(process_arena, "{f}", .{output_dir});
3051 }
3152
3253 // zig fmt: off
......@@ -49,22 +70,23 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
4970 {
5071 try doAtomicSymLinks(
5172 step,
52 compile.getEmittedBin().getPath2(b, step),
73 compile.getEmittedBin().getPath2(step.owner, step),
5374 compile.major_only_filename.?,
5475 compile.name_only_filename.?,
5576 );
5677 }
5778}
5879
59fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
80fn getZigArgs(compile: *Compile, maker: *Maker, fuzz: bool) ![][]const u8 {
6081 const step = &compile.step;
6182 const b = step.owner;
62 const arena = b.allocator;
83 const graph = maker.graph;
84 const arena = graph.arena; // TODO don't leak into the process arena
6385
6486 var zig_args = std.array_list.Managed([]const u8).init(arena);
6587 defer zig_args.deinit();
6688
67 try zig_args.append(b.graph.zig_exe);
89 try zig_args.append(graph.zig_exe);
6890
6991 const cmd = switch (compile.kind) {
7092 .lib => "build-lib",
......@@ -78,7 +100,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
78100 if (b.reference_trace) |some| {
79101 try zig_args.append(try std.fmt.allocPrint(arena, "-freference-trace={d}", .{some}));
80102 }
81 try addFlag(&zig_args, "allow-so-scripts", compile.allow_so_scripts orelse b.graph.allow_so_scripts);
103 try addFlag(&zig_args, "allow-so-scripts", compile.allow_so_scripts orelse graph.allow_so_scripts);
82104
83105 try addFlag(&zig_args, "llvm", compile.use_llvm);
84106 try addFlag(&zig_args, "lld", compile.use_lld);
......@@ -118,7 +140,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
118140 // module, along with any arguments that need to be passed to the
119141 // compiler for each module individually.
120142 var seen_system_libs: std.StringHashMapUnmanaged([]const []const u8) = .empty;
121 var frameworks: std.StringArrayHashMapUnmanaged(Module.LinkFrameworkOptions) = .empty;
143 var frameworks: std.StringArrayHashMapUnmanaged(Module.FrameworkFlags) = .empty;
122144
123145 var prev_has_cflags = false;
124146 var prev_has_rcflags = false;
......@@ -130,7 +152,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
130152
131153 // Fully recursive iteration including dynamic libraries to detect
132154 // libc and libc++ linkage.
133 for (compile.getCompileDependencies(true)) |some_compile| {
155 for (getCompileDependencies(true)) |some_compile| {
134156 for (some_compile.root_module.getGraph().modules) |mod| {
135157 if (mod.link_libc == true) compile.is_linking_libc = true;
136158 if (mod.link_libcpp == true) compile.is_linking_libcpp = true;
......@@ -141,7 +163,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
141163
142164 // For this loop, don't chase dynamic libraries because their link
143165 // objects are already linked.
144 for (compile.getCompileDependencies(false)) |dep_compile| {
166 for (getCompileDependencies(false)) |dep_compile| {
145167 for (dep_compile.root_module.getGraph().modules) |mod| {
146168 // While walking transitive dependencies, if a given link object is
147169 // already included in a library, it should not redundantly be
......@@ -207,7 +229,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
207229 switch (system_lib.use_pkg_config) {
208230 .no => try zig_args.append(b.fmt("{s}{s}", .{ prefix, system_lib.name })),
209231 .yes, .force => {
210 if (compile.runPkgConfig(system_lib.name)) |result| {
232 if (compile.runPkgConfig(maker, system_lib.name)) |result| {
211233 try zig_args.appendSlice(result.cflags);
212234 try zig_args.appendSlice(result.libs);
213235 try seen_system_libs.put(arena, system_lib.name, result.cflags);
......@@ -227,7 +249,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
227249 }));
228250 },
229251 .force => {
230 panic("pkg-config failed for library {s}", .{system_lib.name});
252 return step.fail("pkg-config failed for library {s}", .{system_lib.name});
231253 },
232254 .no => unreachable,
233255 },
......@@ -272,7 +294,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
272294 if (other.linkage == .dynamic and
273295 compile.rootModuleTarget().os.tag != .windows)
274296 {
275 if (fs.path.dirname(full_path_lib)) |dirname| {
297 if (Dir.path.dirname(full_path_lib)) |dirname| {
276298 try zig_args.append("-rpath");
277299 try zig_args.append(dirname);
278300 }
......@@ -479,7 +501,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
479501 if (b.verbose_link or compile.verbose_link) try zig_args.append("--verbose-link");
480502 if (b.verbose_cc or compile.verbose_cc) try zig_args.append("--verbose-cc");
481503 if (b.verbose_llvm_cpu_features) try zig_args.append("--verbose-llvm-cpu-features");
482 if (b.graph.time_report) try zig_args.append("--time-report");
504 if (graph.time_report) try zig_args.append("--time-report");
483505
484506 if (compile.generated_asm != null) try zig_args.append("-femit-asm");
485507 if (compile.generated_bin == null) try zig_args.append("-fno-emit-bin");
......@@ -555,9 +577,9 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
555577 try zig_args.append(b.cache_root.path orelse ".");
556578
557579 try zig_args.append("--global-cache-dir");
558 try zig_args.append(b.graph.global_cache_root.path orelse ".");
580 try zig_args.append(graph.global_cache_root.path orelse ".");
559581
560 if (b.graph.debug_compiler_runtime_libs) |mode|
582 if (graph.debug_compiler_runtime_libs) |mode|
561583 try zig_args.append(b.fmt("--debug-rt={t}", .{mode}));
562584
563585 try zig_args.append("--name");
......@@ -681,7 +703,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
681703
682704 // -I and -L arguments that appear after the last --mod argument apply to all modules.
683705 const cwd: Io.Dir = .cwd();
684 const io = b.graph.io;
706 const io = graph.io;
685707
686708 for (b.search_prefixes.items) |search_prefix| {
687709 var prefix_dir = cwd.openDir(io, search_prefix, .{}) catch |err| {
......@@ -734,8 +756,8 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
734756
735757 const opt_zig_lib_dir = if (compile.zig_lib_dir) |dir|
736758 dir.getPath2(b, step)
737 else if (b.graph.zig_lib_directory.path) |_|
738 b.fmt("{f}", .{b.graph.zig_lib_directory})
759 else if (graph.zig_lib_directory.path) |_|
760 b.fmt("{f}", .{graph.zig_lib_directory})
739761 else
740762 null;
741763
......@@ -769,7 +791,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
769791 "--error-limit", b.fmt("{d}", .{err_limit}),
770792 });
771793
772 try addFlag(&zig_args, "incremental", b.graph.incremental);
794 try addFlag(&zig_args, "incremental", graph.incremental);
773795
774796 try zig_args.append("--listen=-");
775797
......@@ -814,7 +836,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
814836 var args_hex_hash: [Sha256.digest_length * 2]u8 = undefined;
815837 _ = try std.fmt.bufPrint(&args_hex_hash, "{x}", .{&args_hash});
816838
817 const args_file = "args" ++ fs.path.sep_str ++ args_hex_hash;
839 const args_file = "args" ++ Dir.path.sep_str ++ args_hex_hash;
818840 if (b.cache_root.handle.access(io, args_file, .{})) |_| {
819841 // The args file is already present from a previous run.
820842 } else |err| switch (err) {
......@@ -859,7 +881,9 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
859881 return try zig_args.toOwnedSlice();
860882}
861883
862pub fn rebuildInFuzzMode(c: *Compile, gpa: Allocator, progress_node: std.Progress.Node) !Path {
884pub fn rebuildInFuzzMode(c: *Compile, maker: *Maker, progress_node: std.Progress.Node) !Path {
885 const gpa = maker.graph.gpa;
886
863887 c.step.result_error_msgs.clearRetainingCapacity();
864888 c.step.result_stderr = "";
865889
......@@ -871,21 +895,23 @@ pub fn rebuildInFuzzMode(c: *Compile, gpa: Allocator, progress_node: std.Progres
871895 c.step.result_failed_command = null;
872896 }
873897
874 const zig_args = try getZigArgs(c, true);
898 const zig_args = try getZigArgs(c, maker, true);
875899 const maybe_output_bin_path = try c.step.evalZigProcess(zig_args, progress_node, false, null, gpa);
876900 return maybe_output_bin_path.?;
877901}
878902
879903pub fn doAtomicSymLinks(
880904 step: *Step,
905 maker: *Maker,
881906 output_path: []const u8,
882907 filename_major_only: []const u8,
883908 filename_name_only: []const u8,
884909) !void {
885910 const b = step.owner;
886 const io = b.graph.io;
887 const out_dir = fs.path.dirname(output_path) orelse ".";
888 const out_basename = fs.path.basename(output_path);
911 const graph = maker.graph;
912 const io = graph.io;
913 const out_dir = Dir.path.dirname(output_path) orelse ".";
914 const out_basename = Dir.path.basename(output_path);
889915 // sym link for libfoo.so.1 to libfoo.so.1.2.3
890916 const major_only_path = b.pathJoin(&.{ out_dir, filename_major_only });
891917 const cwd: Io.Dir = .cwd();
......@@ -903,10 +929,24 @@ pub fn doAtomicSymLinks(
903929 };
904930}
905931
906fn execPkgConfigList(b: *std.Build, out_code: *u8) (PkgConfigError || RunError)![]const PkgConfigPkg {
907 const pkg_config_exe = b.graph.environ_map.get("PKG_CONFIG") orelse "pkg-config";
908 const stdout = try b.runAllowFail(&[_][]const u8{ pkg_config_exe, "--list-all" }, out_code, .ignore);
909 var list = std.array_list.Managed(PkgConfigPkg).init(b.allocator);
932pub const PkgConfigError = error{
933 PkgConfigCrashed,
934 PkgConfigFailed,
935 PkgConfigNotInstalled,
936 PkgConfigInvalidOutput,
937};
938
939pub const PkgConfigPkg = struct {
940 name: []const u8,
941 desc: []const u8,
942};
943
944fn execPkgConfigList(maker: *Maker, out_code: *u8) (PkgConfigError || Maker.RunError)![]const PkgConfigPkg {
945 const graph = maker.graph;
946 const process_arena = graph.arena; // TODO don't leak into process arena
947 const pkg_config_exe = graph.environ_map.get("PKG_CONFIG") orelse "pkg-config";
948 const stdout = try maker.runAllowFail(&[_][]const u8{ pkg_config_exe, "--list-all" }, out_code, .ignore);
949 var list = std.array_list.Managed(PkgConfigPkg).init(process_arena);
910950 errdefer list.deinit();
911951 var line_it = mem.tokenizeAny(u8, stdout, "\r\n");
912952 while (line_it.next()) |line| {
......@@ -960,7 +1000,8 @@ const PkgConfigResult = struct {
9601000
9611001/// Run pkg-config for the given library name and parse the output, returning the arguments
9621002/// that should be passed to zig to link the given library.
963fn runPkgConfig(compile: *Compile, lib_name: []const u8) !PkgConfigResult {
1003fn runPkgConfig(compile: *Compile, maker: *Maker, lib_name: []const u8) !PkgConfigResult {
1004 const graph = maker.graph;
9641005 const wl_rpath_prefix = "-Wl,-rpath,";
9651006
9661007 const b = compile.step.owner;
......@@ -1013,7 +1054,7 @@ fn runPkgConfig(compile: *Compile, lib_name: []const u8) !PkgConfigResult {
10131054 };
10141055
10151056 var code: u8 = undefined;
1016 const pkg_config_exe = b.graph.environ_map.get("PKG_CONFIG") orelse "pkg-config";
1057 const pkg_config_exe = graph.environ_map.get("PKG_CONFIG") orelse "pkg-config";
10171058 const stdout = if (b.runAllowFail(&[_][]const u8{
10181059 pkg_config_exe,
10191060 pkg_name,
......@@ -1198,3 +1239,43 @@ fn moduleNeedsCliArg(mod: *const Module) bool {
11981239 } else false;
11991240}
12001241
1242const CliNamedModules = struct {
1243 modules: std.AutoArrayHashMapUnmanaged(*Module, void),
1244 names: std.StringArrayHashMapUnmanaged(void),
1245
1246 /// Traverse the whole dependency graph and give every module a unique
1247 /// name, ideally one named after what it's called somewhere in the graph.
1248 /// It will help here to have both a mapping from module to name and a set
1249 /// of all the currently-used names.
1250 fn init(arena: Allocator, root_module: *Module) Allocator.Error!CliNamedModules {
1251 var compile: CliNamedModules = .{
1252 .modules = .{},
1253 .names = .{},
1254 };
1255 const graph = root_module.getGraph();
1256 {
1257 assert(graph.modules[0] == root_module);
1258 try compile.modules.put(arena, root_module, {});
1259 try compile.names.put(arena, "root", {});
1260 }
1261 for (graph.modules[1..], graph.names[1..]) |mod, orig_name| {
1262 var name = orig_name;
1263 var n: usize = 0;
1264 while (true) {
1265 const gop = try compile.names.getOrPut(arena, name);
1266 if (!gop.found_existing) {
1267 try compile.modules.putNoClobber(arena, mod, {});
1268 break;
1269 }
1270 name = try std.fmt.allocPrint(arena, "{s}{d}", .{ orig_name, n });
1271 n += 1;
1272 }
1273 }
1274 return compile;
1275 }
1276};
1277
1278fn getCompileDependencies(chase_dynamic: bool) void {
1279 _ = chase_dynamic;
1280 @panic("TODO");
1281}
lib/compiler/Maker/Step/Run.zig+126-117
......@@ -3,38 +3,46 @@ const Run = @This();
33const builtin = @import("builtin");
44
55const std = @import("std");
6const Io = std.Io;
6const Cache = std.Build.Cache;
7const Configuration = std.Build.Configuration;
78const Dir = std.Io.Dir;
8const mem = std.mem;
9const process = std.process;
109const EnvMap = std.process.Environ.Map;
11const assert = std.debug.assert;
12const Cache = std.Build.Cache;
10const Io = std.Io;
1311const Path = std.Build.Cache.Path;
12const assert = std.debug.assert;
13const mem = std.mem;
14const process = std.process;
1415
1516const Step = @import("../Step.zig");
17const Maker = @import("../../Maker.zig");
1618
1719/// If this is a Zig unit test binary, this tracks the names of the unit
1820/// tests that are also fuzz tests. Indexes cannot be used as they may
1921/// change between reruns.
20fuzz_tests: std.ArrayList([]const u8),
22fuzz_tests: std.ArrayList([]const u8) = .empty,
2123cached_test_metadata: ?CachedTestMetadata = null,
2224
2325/// Populated during the fuzz phase if this run step corresponds to a unit test
2426/// executable that contains fuzz tests.
25rebuilt_executable: ?Path,
27rebuilt_executable: ?Path = null,
2628
27fn make(step: *Step, options: Step.MakeOptions) !void {
28 const b = step.owner;
29 const io = b.graph.io;
30 const arena = b.allocator;
31 const run: *Run = @fieldParentPtr("step", step);
29pub fn make(
30 run: *Run,
31 step_index: Configuration.Step.Index,
32 maker: *Maker,
33 progress_node: std.Progress.Node,
34) Step.ExtendedMakeError!void {
35 if (true) @panic("TODO implement run.make()");
36 const graph = maker.graph;
37 const step = maker.stepByIndex(step_index);
38 const io = graph.io;
39 const arena = graph.arena; // TODO don't leak into the process arena
3240 const has_side_effects = run.hasSideEffects();
3341
3442 var argv_list = std.array_list.Managed([]const u8).init(arena);
3543 var output_placeholders = std.array_list.Managed(IndexedOutput).init(arena);
3644
37 var man = b.graph.cache.obtain();
45 var man = graph.cache.obtain();
3846 defer man.deinit();
3947
4048 if (run.environ_map) |environ_map| {
......@@ -54,19 +62,19 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
5462 man.hash.addBytes(bytes);
5563 },
5664 .lazy_path => |file| {
57 const file_path = file.lazy_path.getPath3(b, step);
58 try argv_list.append(b.fmt("{s}{s}", .{ file.prefix, run.convertPathArg(file_path) }));
65 const file_path = file.lazy_path.getPath3(graph, step);
66 try argv_list.append(graph.fmt("{s}{s}", .{ file.prefix, run.convertPathArg(maker, file_path) }));
5967 man.hash.addBytes(file.prefix);
6068 _ = try man.addFilePath(file_path, null);
6169 },
6270 .decorated_directory => |dd| {
63 const file_path = dd.lazy_path.getPath3(b, step);
64 const resolved_arg = b.fmt("{s}{s}{s}", .{ dd.prefix, run.convertPathArg(file_path), dd.suffix });
71 const file_path = dd.lazy_path.getPath3(graph, step);
72 const resolved_arg = graph.fmt("{s}{s}{s}", .{ dd.prefix, run.convertPathArg(maker, file_path), dd.suffix });
6573 try argv_list.append(resolved_arg);
6674 man.hash.addBytes(resolved_arg);
6775 },
6876 .file_content => |file_plp| {
69 const file_path = file_plp.lazy_path.getPath3(b, step);
77 const file_path = file_plp.lazy_path.getPath3(graph, step);
7078
7179 var result: std.Io.Writer.Allocating = .init(arena);
7280 errdefer result.deinit();
......@@ -99,13 +107,13 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
99107
100108 if (artifact.rootModuleTarget().os.tag == .windows) {
101109 // On Windows we don't have rpaths so we have to add .dll search paths to PATH
102 run.addPathForDynLibs(artifact);
110 addPathForDynLibs(artifact);
103111 }
104112 const file_path = artifact.installed_path orelse artifact.generated_bin.?.path.?;
105113
106 try argv_list.append(b.fmt("{s}{s}", .{
114 try argv_list.append(graph.fmt("{s}{s}", .{
107115 pa.prefix,
108 run.convertPathArg(.{ .root_dir = .cwd(), .sub_path = file_path }),
116 run.convertPathArg(maker, .{ .root_dir = .cwd(), .sub_path = file_path }),
109117 }));
110118
111119 _ = try man.addFile(file_path, null);
......@@ -131,7 +139,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
131139 man.hash.addBytes(bytes);
132140 },
133141 .lazy_path => |lazy_path| {
134 const file_path = lazy_path.getPath2(b, step);
142 const file_path = lazy_path.getPath2(graph, step);
135143 _ = try man.addFile(file_path, null);
136144 },
137145 .none => {},
......@@ -147,14 +155,15 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
147155 man.hash.add(captured.trim_whitespace);
148156 }
149157
150 hashStdIo(&man.hash, run.stdio);
158 std.log.err("TODO hashStdIo", .{});
159 //hashStdIo(&man.hash, run.stdio);
151160
152161 for (run.file_inputs.items) |lazy_path| {
153 _ = try man.addFile(lazy_path.getPath2(b, step), null);
162 _ = try man.addFile(lazy_path.getPath2(graph, step), null);
154163 }
155164
156165 if (run.cwd) |cwd| {
157 const cwd_path = cwd.getPath3(b, step);
166 const cwd_path = cwd.getPath3(graph, step);
158167 _ = man.hash.addBytes(try cwd_path.toString(arena));
159168 }
160169
......@@ -165,9 +174,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
165174 try populateGeneratedPaths(
166175 arena,
167176 output_placeholders.items,
168 run.captured_stdout,
169 run.captured_stderr,
170 b.cache_root,
177 graph.cache_root,
171178 &digest,
172179 );
173180
......@@ -185,36 +192,34 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
185192 try populateGeneratedPaths(
186193 arena,
187194 output_placeholders.items,
188 run.captured_stdout,
189 run.captured_stderr,
190 b.cache_root,
195 graph.cache_root,
191196 &digest,
192197 );
193198
194199 const output_dir_path = "o" ++ Dir.path.sep_str ++ &digest;
195200 for (output_placeholders.items) |placeholder| {
196 const output_sub_path = b.pathJoin(&.{ output_dir_path, placeholder.output.basename });
201 const output_sub_path = graph.pathJoin(&.{ output_dir_path, placeholder.output.basename });
197202 const output_sub_dir_path = switch (placeholder.tag) {
198203 .output_file => Dir.path.dirname(output_sub_path).?,
199204 .output_directory => output_sub_path,
200205 else => unreachable,
201206 };
202 b.cache_root.handle.createDirPath(io, output_sub_dir_path) catch |err| {
203 return step.fail("unable to make path '{f}{s}': {s}", .{
204 b.cache_root, output_sub_dir_path, @errorName(err),
207 graph.cache_root.handle.createDirPath(io, output_sub_dir_path) catch |err| {
208 return step.fail("unable to make path '{f}{s}': {t}", .{
209 graph.cache_root, output_sub_dir_path, err,
205210 });
206211 };
207 const arg_output_path = run.convertPathArg(.{
212 const arg_output_path = run.convertPathArg(maker, .{
208213 .root_dir = .cwd(),
209214 .sub_path = placeholder.output.generated_file.getPath(),
210215 });
211216 argv_list.items[placeholder.index] = if (placeholder.output.prefix.len == 0)
212217 arg_output_path
213218 else
214 b.fmt("{s}{s}", .{ placeholder.output.prefix, arg_output_path });
219 graph.fmt("{s}{s}", .{ placeholder.output.prefix, arg_output_path });
215220 }
216221
217 try runCommand(run, argv_list.items, has_side_effects, output_dir_path, options, null);
222 try runCommand(run, maker, progress_node, argv_list.items, has_side_effects, output_dir_path, null);
218223 if (!has_side_effects) try step.writeManifestAndWatch(&man);
219224 return;
220225 };
......@@ -226,32 +231,32 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
226231
227232 for (output_placeholders.items) |placeholder| {
228233 const output_components = .{ tmp_dir_path, placeholder.output.basename };
229 const output_sub_path = b.pathJoin(&output_components);
234 const output_sub_path = graph.pathJoin(&output_components);
230235 const output_sub_dir_path = switch (placeholder.tag) {
231236 .output_file => Dir.path.dirname(output_sub_path).?,
232237 .output_directory => output_sub_path,
233238 else => unreachable,
234239 };
235 b.cache_root.handle.createDirPath(io, output_sub_dir_path) catch |err| {
236 return step.fail("unable to make path '{f}{s}': {s}", .{
237 b.cache_root, output_sub_dir_path, @errorName(err),
240 graph.cache_root.handle.createDirPath(io, output_sub_dir_path) catch |err| {
241 return step.fail("unable to make path '{f}{s}': {t}", .{
242 graph.cache_root, output_sub_dir_path, err,
238243 });
239244 };
240 const raw_output_path: Cache.Path = .{
241 .root_dir = b.cache_root,
242 .sub_path = b.pathJoin(&output_components),
245 const raw_output_path: Path = .{
246 .root_dir = graph.cache_root,
247 .sub_path = graph.pathJoin(&output_components),
243248 };
244 placeholder.output.generated_file.path = raw_output_path.toString(b.graph.arena) catch @panic("OOM");
245 argv_list.items[placeholder.index] = b.fmt("{s}{s}", .{
249 placeholder.output.generated_file.path = raw_output_path.toString(arena) catch @panic("OOM");
250 argv_list.items[placeholder.index] = graph.fmt("{s}{s}", .{
246251 placeholder.output.prefix,
247 run.convertPathArg(raw_output_path),
252 run.convertPathArg(maker, raw_output_path),
248253 });
249254 }
250255
251 try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, options, null);
256 try runCommand(run, maker, progress_node, argv_list.items, has_side_effects, tmp_dir_path, null);
252257
253258 const dep_file_dir = Dir.cwd();
254 const dep_file_basename = dep_output_file.generated_file.getPath2(b, step);
259 const dep_file_basename = dep_output_file.generated_file.getPath2(graph, step);
255260 if (has_side_effects)
256261 try man.addDepFile(dep_file_dir, dep_file_basename)
257262 else
......@@ -269,21 +274,21 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
269274 if (any_output) {
270275 const o_sub_path = "o" ++ Dir.path.sep_str ++ &digest;
271276
272 b.cache_root.handle.rename(tmp_dir_path, b.cache_root.handle, o_sub_path, io) catch |err| switch (err) {
277 graph.cache_root.handle.rename(tmp_dir_path, graph.cache_root.handle, o_sub_path, io) catch |err| switch (err) {
273278 Dir.RenameError.DirNotEmpty => {
274 b.cache_root.handle.deleteTree(io, o_sub_path) catch |del_err| {
279 graph.cache_root.handle.deleteTree(io, o_sub_path) catch |del_err| {
275280 return step.fail("unable to remove dir '{f}'{s}: {t}", .{
276 b.cache_root, tmp_dir_path, del_err,
281 graph.cache_root, tmp_dir_path, del_err,
277282 });
278283 };
279 b.cache_root.handle.rename(tmp_dir_path, b.cache_root.handle, o_sub_path, io) catch |retry_err| {
284 graph.cache_root.handle.rename(tmp_dir_path, graph.cache_root.handle, o_sub_path, io) catch |retry_err| {
280285 return step.fail("unable to rename dir '{f}{s}' to '{f}{s}': {t}", .{
281 b.cache_root, tmp_dir_path, b.cache_root, o_sub_path, retry_err,
286 graph.cache_root, tmp_dir_path, graph.cache_root, o_sub_path, retry_err,
282287 });
283288 };
284289 },
285290 else => return step.fail("unable to rename dir '{f}{s}' to '{f}{s}': {t}", .{
286 b.cache_root, tmp_dir_path, b.cache_root, o_sub_path, err,
291 graph.cache_root, tmp_dir_path, graph.cache_root, o_sub_path, err,
287292 }),
288293 };
289294 }
......@@ -293,9 +298,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
293298 try populateGeneratedPaths(
294299 arena,
295300 output_placeholders.items,
296 run.captured_stdout,
297 run.captured_stderr,
298 b.cache_root,
301 graph.cache_root,
299302 &digest,
300303 );
301304}
......@@ -347,7 +350,6 @@ fn waitZigTest(
347350 // start and it acknowledging the test starting, we terminate the child and raise an error. This
348351 // *should* never happen, but could in theory be caused by some very unlucky IB in a test.
349352 const response_timeout: Io.Clock.Duration = t: {
350 if (fuzz_context != null) break :t null; // don't timeout fuzz tests
351353 const ns = @max(options.unit_test_timeout_ns orelse 0, 60 * std.time.ns_per_s);
352354 break :t .{ .clock = .awake, .raw = .fromNanoseconds(ns) };
353355 };
......@@ -773,8 +775,8 @@ const FuzzTestRunner = struct {
773775 try f.pending_broadcasts.ensureUnusedCapacity(gpa, size);
774776 f.pending_broadcasts.appendSliceAssumeCapacity(body);
775777 f.pending_broadcasts.appendSliceAssumeCapacity(@ptrCast(&footer));
776 }
777 },
778 }
779 },
778780 else => {}, // ignore other messages
779781 }
780782
......@@ -863,7 +865,7 @@ const FuzzTestRunner = struct {
863865 if (f.coverage_id == null) return;
864866
865867 // Search for the input file corresponding to the instance
866 const InputHeader = Build.abi.fuzz.MmapInputHeader;
868 const InputHeader = std.Build.abi.fuzz.MmapInputHeader;
867869 var in_r_buf: [@sizeOf(InputHeader)]u8 = undefined;
868870 var in_r: Io.File.Reader = undefined;
869871 var in_f: Io.File = undefined;
......@@ -1299,11 +1301,11 @@ fn sendRunFuzzTestMessage(
12991301 }
13001302}
13011303
1302fn evalGeneric(run: *Run, spawn_options: process.SpawnOptions) !EvalGenericResult {
1303 const b = run.step.owner;
1304 const io = b.graph.io;
1305 const arena = b.allocator;
1306 const gpa = b.allocator;
1304fn evalGeneric(run: *Run, maker: *Maker, spawn_options: process.SpawnOptions) !EvalGenericResult {
1305 const graph = maker.graph;
1306 const io = graph.io;
1307 const arena = graph.allocator; // TODO don't leak into the process arena
1308 const gpa = maker.gpa;
13071309
13081310 var child = try process.spawn(io, spawn_options);
13091311 defer child.kill(io);
......@@ -1317,7 +1319,7 @@ fn evalGeneric(run: *Run, spawn_options: process.SpawnOptions) !EvalGenericResul
13171319 child.stdin = null;
13181320 },
13191321 .lazy_path => |lazy_path| {
1320 const path = lazy_path.getPath3(b, &run.step);
1322 const path = lazy_path.getPath3(graph, &run.step);
13211323 const file = path.root_dir.handle.openFile(io, path.subPathOrDot(), .{}) catch |err| {
13221324 return run.step.fail("unable to open stdin file: {t}", .{err});
13231325 };
......@@ -1417,18 +1419,22 @@ fn evalGeneric(run: *Run, spawn_options: process.SpawnOptions) !EvalGenericResul
14171419
14181420const IndexedOutput = struct {
14191421 index: usize,
1420 tag: @typeInfo(Arg).@"union".tag_type.?,
1422 tag: Configuration.Step.Run.Arg.Tag,
14211423 output: *Output,
14221424};
14231425
1426const Output = void; // TODO
1427
14241428pub fn rerunInFuzzMode(
14251429 run: *Run,
14261430 fuzz: *std.Build.Fuzz,
14271431 prog_node: std.Progress.Node,
14281432) !void {
1433 const maker = fuzz.maker;
1434 const graph = maker.graph;
14291435 const step = &run.step;
14301436 const b = step.owner;
1431 const io = b.graph.io;
1437 const io = graph.io;
14321438 const arena = b.allocator;
14331439 var argv_list: std.ArrayList([]const u8) = .empty;
14341440 for (run.argv.items) |arg| {
......@@ -1438,11 +1444,11 @@ pub fn rerunInFuzzMode(
14381444 },
14391445 .lazy_path => |file| {
14401446 const file_path = file.lazy_path.getPath3(b, step);
1441 try argv_list.append(arena, b.fmt("{s}{s}", .{ file.prefix, run.convertPathArg(file_path) }));
1447 try argv_list.append(arena, b.fmt("{s}{s}", .{ file.prefix, run.convertPathArg(maker, file_path) }));
14421448 },
14431449 .decorated_directory => |dd| {
14441450 const file_path = dd.lazy_path.getPath3(b, step);
1445 try argv_list.append(arena, b.fmt("{s}{s}{s}", .{ dd.prefix, run.convertPathArg(file_path), dd.suffix }));
1451 try argv_list.append(arena, b.fmt("{s}{s}{s}", .{ dd.prefix, run.convertPathArg(maker, file_path), dd.suffix }));
14461452 },
14471453 .file_content => |file_plp| {
14481454 const file_path = file_plp.lazy_path.getPath3(b, step);
......@@ -1471,7 +1477,7 @@ pub fn rerunInFuzzMode(
14711477 };
14721478 try argv_list.append(arena, b.fmt("{s}{s}", .{
14731479 pa.prefix,
1474 run.convertPathArg(.{ .root_dir = .cwd(), .sub_path = file_path }),
1480 run.convertPathArg(maker, .{ .root_dir = .cwd(), .sub_path = file_path }),
14751481 }));
14761482 },
14771483 .output_file, .output_directory => unreachable,
......@@ -1487,17 +1493,13 @@ pub fn rerunInFuzzMode(
14871493 var rand_int: u64 = undefined;
14881494 io.random(@ptrCast(&rand_int));
14891495 const tmp_dir_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int);
1490 try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, .{
1491 .progress_node = prog_node,
1492 .watch = undefined, // not used by `runCommand`
1493 .web_server = null, // only needed for time reports
1494 .unit_test_timeout_ns = null, // don't time out fuzz tests for now
1495 .gpa = fuzz.gpa,
1496 }, .{
1496 try runCommand(run, maker, prog_node, argv_list.items, has_side_effects, tmp_dir_path, .{
14971497 .fuzz = fuzz,
14981498 });
14991499}
15001500
1501const CapturedStdIo = void; // TODO get it from Configuration
1502
15011503fn populateGeneratedPaths(
15021504 arena: std.mem.Allocator,
15031505 output_placeholders: []const IndexedOutput,
......@@ -1545,17 +1547,19 @@ const FuzzContext = struct {
15451547
15461548fn runCommand(
15471549 run: *Run,
1550 maker: *Maker,
1551 progress_node: std.Progress.Node,
15481552 argv: []const []const u8,
15491553 has_side_effects: bool,
15501554 output_dir_path: []const u8,
1551 options: Step.MakeOptions,
15521555 fuzz_context: ?FuzzContext,
15531556) !void {
1557 const graph = maker.graph;
1558 const arena = graph.arena; // TODO don't leak into process arena
1559 const gpa = maker.gpa;
15541560 const step = &run.step;
15551561 const b = step.owner;
1556 const arena = b.allocator;
1557 const gpa = options.gpa;
1558 const io = b.graph.io;
1562 const io = graph.io;
15591563
15601564 const cwd: process.Child.Cwd = if (run.cwd) |lazy_cwd| .{ .path = lazy_cwd.getPath2(b, step) } else .inherit;
15611565
......@@ -1571,12 +1575,12 @@ fn runCommand(
15711575 defer interp_argv.deinit();
15721576
15731577 var environ_map: EnvMap = env: {
1574 const orig = run.environ_map orelse &b.graph.environ_map;
1578 const orig = run.environ_map orelse &graph.environ_map;
15751579 break :env try orig.clone(gpa);
15761580 };
15771581 defer environ_map.deinit();
15781582
1579 const opt_generic_result = spawnChildAndCollect(run, argv, &environ_map, has_side_effects, options, fuzz_context) catch |err| term: {
1583 const opt_generic_result = spawnChildAndCollect(run, maker, progress_node, argv, &environ_map, has_side_effects, fuzz_context) catch |err| term: {
15801584 // InvalidExe: cpu arch mismatch
15811585 // FileNotFound: can happen with a wrong dynamic linker path
15821586 if (err == error.InvalidExe or err == error.FileNotFound) interpret: {
......@@ -1597,7 +1601,7 @@ fn runCommand(
15971601 const need_cross_libc = exe.is_linking_libc and
15981602 (root_target.isGnuLibC() or (root_target.isMuslLibC() and exe.linkage == .dynamic));
15991603 const other_target = exe.root_module.resolved_target.?.result;
1600 switch (std.zig.system.getExternalExecutor(io, &b.graph.host.result, &other_target, .{
1604 switch (std.zig.system.getExternalExecutor(io, &graph.host.result, &other_target, .{
16011605 .qemu_fixes_dl = need_cross_libc and b.libc_runtimes_dir != null,
16021606 .link_libc = exe.is_linking_libc,
16031607 })) {
......@@ -1669,7 +1673,7 @@ fn runCommand(
16691673 .bad_dl => |foreign_dl| {
16701674 if (allow_skip) return error.MakeSkipped;
16711675
1672 const host_dl = b.graph.host.result.dynamic_linker.get() orelse "(none)";
1676 const host_dl = graph.host.result.dynamic_linker.get() orelse "(none)";
16731677
16741678 return step.fail(
16751679 \\the host system is unable to execute binaries from the target
......@@ -1681,7 +1685,7 @@ fn runCommand(
16811685 .bad_os_or_cpu => {
16821686 if (allow_skip) return error.MakeSkipped;
16831687
1684 const host_name = try b.graph.host.result.zigTriple(b.allocator);
1688 const host_name = try graph.host.result.zigTriple(b.allocator);
16851689 const foreign_name = try root_target.zigTriple(b.allocator);
16861690
16871691 return step.fail("the host system ({s}) is unable to execute binaries from the target ({s})", .{
......@@ -1692,14 +1696,14 @@ fn runCommand(
16921696
16931697 if (root_target.os.tag == .windows) {
16941698 // On Windows we don't have rpaths so we have to add .dll search paths to PATH
1695 run.addPathForDynLibs(exe);
1699 addPathForDynLibs(exe);
16961700 }
16971701
16981702 gpa.free(step.result_failed_command.?);
16991703 step.result_failed_command = null;
17001704 try Step.handleVerbose2(step.owner, cwd, run.environ_map, interp_argv.items);
17011705
1702 break :term spawnChildAndCollect(run, interp_argv.items, &environ_map, has_side_effects, options, fuzz_context) catch |e| {
1706 break :term spawnChildAndCollect(run, maker, progress_node, interp_argv.items, &environ_map, has_side_effects, fuzz_context) catch |e| {
17031707 if (!run.failing_to_execute_foreign_is_an_error) return error.MakeSkipped;
17041708 if (e == error.MakeFailed) return error.MakeFailed; // error already reported
17051709 return step.fail("unable to spawn interpreter {s}: {t}", .{ interp_argv.items[0], e });
......@@ -1851,14 +1855,16 @@ const EvalGenericResult = struct {
18511855
18521856fn spawnChildAndCollect(
18531857 run: *Run,
1858 maker: *Maker,
1859 progress_node: std.Progress.Node,
18541860 argv: []const []const u8,
18551861 environ_map: *EnvMap,
18561862 has_side_effects: bool,
1857 options: Step.MakeOptions,
18581863 fuzz_context: ?FuzzContext,
18591864) !?EvalGenericResult {
18601865 const b = run.step.owner;
1861 const graph = b.graph;
1866 const graph = maker.graph;
1867 const gpa = maker.gpa;
18621868 const io = graph.io;
18631869
18641870 if (fuzz_context != null) {
......@@ -1870,7 +1876,7 @@ fn spawnChildAndCollect(
18701876
18711877 // If an error occurs, it's caused by this command:
18721878 assert(run.step.result_failed_command == null);
1873 run.step.result_failed_command = try Step.allocPrintCmd(options.gpa, child_cwd, .{
1879 run.step.result_failed_command = try Step.allocPrintCmd(gpa, child_cwd, .{
18741880 .child = environ_map,
18751881 .parent = &graph.environ_map,
18761882 }, argv);
......@@ -1905,7 +1911,7 @@ fn spawnChildAndCollect(
19051911
19061912 if (run.stdio == .zig_test) {
19071913 const started: Io.Clock.Timestamp = .now(io, .awake);
1908 const result = evalZigTest(run, spawn_options, options, fuzz_context) catch |err| switch (err) {
1914 const result = evalZigTest(run, maker, progress_node, spawn_options, fuzz_context) catch |err| switch (err) {
19091915 error.Canceled => |e| return e,
19101916 else => |e| e,
19111917 };
......@@ -1915,7 +1921,7 @@ fn spawnChildAndCollect(
19151921 } else {
19161922 const inherit = spawn_options.stdout == .inherit or spawn_options.stderr == .inherit;
19171923 if (!run.disable_zig_progress and !inherit) {
1918 spawn_options.progress_node = options.progress_node;
1924 spawn_options.progress_node = progress_node;
19191925 }
19201926 const terminal_mode: Io.Terminal.Mode = if (inherit) m: {
19211927 const stderr = try io.lockStderr(&.{}, graph.stderr_mode);
......@@ -1925,7 +1931,7 @@ fn spawnChildAndCollect(
19251931 try setColorEnvironmentVariables(run, environ_map, terminal_mode);
19261932
19271933 const started: Io.Clock.Timestamp = .now(io, .awake);
1928 const result = evalGeneric(run, spawn_options) catch |err| switch (err) {
1934 const result = evalGeneric(run, maker, spawn_options) catch |err| switch (err) {
19291935 error.Canceled => |e| return e,
19301936 else => |e| e,
19311937 };
......@@ -1934,11 +1940,11 @@ fn spawnChildAndCollect(
19341940 }
19351941}
19361942
1937fn hashStdIo(hh: *Cache.HashHelper, stdio: StdIo) void {
1943fn hashStdIo(hh: *Cache.HashHelper, stdio: void) void {
19381944 switch (stdio) {
19391945 .infer_from_args, .inherit, .zig_test => {},
19401946 .check => |checks| for (checks.items) |check| {
1941 hh.add(@as(std.meta.Tag(StdIo.Check), check));
1947 hh.add(@as(std.meta.Tag(@This().StdIo.Check), check));
19421948 switch (check) {
19431949 .expect_stderr_exact,
19441950 .expect_stderr_match,
......@@ -2010,7 +2016,7 @@ fn setColorEnvironmentVariables(run: *Run, environ_map: *EnvMap, terminal_mode:
20102016 }
20112017}
20122018
2013fn checksContainStdout(checks: []const StdIo.Check) bool {
2019fn checksContainStdout(checks: []const @This().StdIo.Check) bool {
20142020 for (checks) |check| switch (check) {
20152021 .expect_stderr_exact,
20162022 .expect_stderr_match,
......@@ -2024,7 +2030,7 @@ fn checksContainStdout(checks: []const StdIo.Check) bool {
20242030 return false;
20252031}
20262032
2027fn checksContainStderr(checks: []const StdIo.Check) bool {
2033fn checksContainStderr(checks: []const @This().StdIo.Check) bool {
20282034 for (checks) |check| switch (check) {
20292035 .expect_stdout_exact,
20302036 .expect_stdout_match,
......@@ -2063,9 +2069,9 @@ fn hasAnyOutputArgs(run: Run) bool {
20632069///
20642070/// Whenever a path is included in the argv of a child, it should be put through this function first
20652071/// to make sure the child doesn't see paths relative to a cwd other than its own.
2066fn convertPathArg(run: *Run, path: Build.Cache.Path) []const u8 {
2072fn convertPathArg(run: *Run, maker: *Maker, path: Path) []const u8 {
20672073 const b = run.step.owner;
2068 const graph = b.graph;
2074 const graph = maker.graph;
20692075 const arena = graph.arena;
20702076
20712077 const path_str = path.toString(arena) catch @panic("OOM");
......@@ -2091,40 +2097,43 @@ fn convertPathArg(run: *Run, path: Build.Cache.Path) []const u8 {
20912097 return Dir.path.join(arena, &.{ ".", child_cwd_rel }) catch @panic("OOM");
20922098}
20932099
2094fn addPathForDynLibs(run: *Run, artifact: *Step.Compile) void {
2095 const b = run.step.owner;
2096 const compiles = artifact.getCompileDependencies(true);
2097 for (compiles) |compile| {
2100fn addPathForDynLibs(artifact: *Step.Compile) void {
2101 if (true) @panic("TODO");
2102 for (artifact.getCompileDependencies(true)) |compile| {
20982103 if (compile.root_module.resolved_target.?.result.os.tag == .windows and
20992104 compile.isDynamicLibrary())
21002105 {
2101 addPathDir(run, Dir.path.dirname(compile.getEmittedBin().getPath2(b, &run.step)).?);
2106 @panic("TODO");
2107 //addPathDir(run, Dir.path.dirname(compile.getEmittedBin().getPath2(b, &run.step)).?);
21022108 }
21032109 }
21042110}
21052111
21062112fn failForeign(
21072113 run: *Run,
2114 maker: *Maker,
2115 step_index: Configuration.Step.Index,
21082116 suggested_flag: []const u8,
21092117 argv0: []const u8,
21102118 exe: *Step.Compile,
2111) error{ MakeFailed, MakeSkipped, OutOfMemory } {
2119) Step.ExtendedMakeError {
2120 const step = maker.stepByIndex(step_index);
21122121 switch (run.stdio) {
21132122 .check, .zig_test => {
2114 if (run.skip_foreign_checks)
2115 return error.MakeSkipped;
2123 if (run.skip_foreign_checks) return error.MakeSkipped;
21162124
2117 const b = run.step.owner;
2118 const host_name = try b.graph.host.result.zigTriple(b.allocator);
2119 const foreign_name = try exe.rootModuleTarget().zigTriple(b.allocator);
2125 const graph = maker.graph;
2126 const process_arena = graph.arena; // TODO don't leak into process arena
2127 const host_name = try graph.host.result.zigTriple(process_arena);
2128 const foreign_name = try exe.rootModuleTarget().zigTriple(process_arena);
21202129
2121 return run.step.fail(
2130 return step.fail(
21222131 \\unable to spawn foreign binary '{s}' ({s}) on host system ({s})
21232132 \\ consider using {s} or enabling skip_foreign_checks in the Run step
21242133 , .{ argv0, foreign_name, host_name, suggested_flag });
21252134 },
21262135 else => {
2127 return run.step.fail("unable to spawn foreign binary '{s}'", .{argv0});
2136 return step.fail("unable to spawn foreign binary '{s}'", .{argv0});
21282137 },
21292138 }
21302139}
lib/compiler/Maker/Watch.zig+45-38
......@@ -10,6 +10,7 @@ const Configuration = std.Build.Configuration;
1010
1111const FsEvents = @import("Watch/FsEvents.zig");
1212const Step = @import("Step.zig");
13const Maker = @import("../Maker.zig");
1314
1415os: Os,
1516/// The number to show as the number of directories being watched.
......@@ -18,8 +19,7 @@ dir_count: usize,
1819// They are `undefined` on implementations which do not utilize then.
1920dir_table: DirTable,
2021generation: Generation,
21configuration: *const Configuration,
22make_steps: []Step,
22maker: *Maker,
2323
2424pub const have_impl = Os != void;
2525
......@@ -105,8 +105,7 @@ const Os = switch (builtin.os.tag) {
105105 };
106106 };
107107
108 fn init(cwd_path: []const u8, configuration: *const Configuration, make_steps: []Step) !Watch {
109 _ = cwd_path;
108 fn init(maker: *Maker) !Watch {
110109 return .{
111110 .dir_table = .{},
112111 .dir_count = 0,
......@@ -118,8 +117,7 @@ const Os = switch (builtin.os.tag) {
118117 else => {},
119118 },
120119 .generation = 0,
121 .make_steps = make_steps,
122 .configuration = configuration,
120 .maker = maker,
123121 };
124122 }
125123
......@@ -136,7 +134,8 @@ const Os = switch (builtin.os.tag) {
136134 return stack_lfh.clone(gpa);
137135 }
138136
139 fn markDirtySteps(w: *Watch, gpa: Allocator, fan_fd: posix.fd_t) !bool {
137 fn markDirtySteps(w: *Watch, fan_fd: posix.fd_t) !bool {
138 const maker = w.maker;
140139 const fanotify = std.os.linux.fanotify;
141140 const M = fanotify.event_metadata;
142141 var events_buf: [256 + 4096]u8 = undefined;
......@@ -155,7 +154,7 @@ const Os = switch (builtin.os.tag) {
155154 if (meta[0].mask.Q_OVERFLOW) {
156155 any_dirty = true;
157156 std.log.warn("file system watch queue overflowed; falling back to fstat", .{});
158 markAllFilesDirty(w, gpa);
157 markAllFilesDirty(w);
159158 return true;
160159 }
161160 const fid: *align(1) fanotify.event_info_fid = @ptrCast(meta + 1);
......@@ -167,9 +166,9 @@ const Os = switch (builtin.os.tag) {
167166 const lfh: FileHandle = .{ .handle = file_handle };
168167 if (w.os.handle_table.getPtr(lfh)) |value| {
169168 if (value.reaction_set.getPtr(".")) |glob_set|
170 any_dirty = markStepSetDirty(gpa, w.make_steps, glob_set, any_dirty);
169 any_dirty = markStepSetDirty(maker, glob_set, any_dirty);
171170 if (value.reaction_set.getPtr(file_name)) |step_set|
172 any_dirty = markStepSetDirty(gpa, w.make_steps, step_set, any_dirty);
171 any_dirty = markStepSetDirty(maker, step_set, any_dirty);
173172 }
174173 },
175174 else => |t| std.log.warn("unexpected fanotify event '{t}'", .{t}),
......@@ -179,9 +178,11 @@ const Os = switch (builtin.os.tag) {
179178 }
180179
181180 fn update(w: *Watch, gpa: Allocator, steps: []const Configuration.Step.Index) !void {
181 const maker = w.maker;
182
182183 // Add missing marks and note persisted ones.
183184 for (steps) |step_index| {
184 const step = &w.make_steps[@intFromEnum(step_index)];
185 const step = maker.stepByIndex(step_index);
185186 for (step.inputs.table.keys(), step.inputs.table.values()) |path, *files| {
186187 const reaction_set = rs: {
187188 const gop = try w.dir_table.getOrPut(gpa, path);
......@@ -298,13 +299,12 @@ const Os = switch (builtin.os.tag) {
298299 w.dir_count = w.dir_table.count();
299300 }
300301
301 fn wait(w: *Watch, gpa: Allocator, io: Io, timeout: Timeout) !WaitResult {
302 _ = io;
302 fn wait(w: *Watch, timeout: Timeout) !WaitResult {
303303 const events_len = try std.posix.poll(w.os.poll_fds.values(), timeout.to_i32_ms());
304304 if (events_len == 0)
305305 return .timeout;
306306 for (w.os.poll_fds.values()) |poll_fd| {
307 if (poll_fd.revents & std.posix.POLL.IN == std.posix.POLL.IN and try markDirtySteps(w, gpa, poll_fd.fd))
307 if (poll_fd.revents & std.posix.POLL.IN == std.posix.POLL.IN and try markDirtySteps(w, poll_fd.fd))
308308 return .dirty;
309309 }
310310 return .clean;
......@@ -515,12 +515,14 @@ const Os = switch (builtin.os.tag) {
515515 return file_id;
516516 }
517517
518 fn markDirtySteps(w: *Watch, gpa: Allocator, dir: *Directory) !bool {
518 fn markDirtySteps(w: *Watch, dir: *Directory) !bool {
519 const maker = w.maker;
520
519521 var any_dirty = false;
520522 const bytes_returned = dir.iosb.Information;
521523 if (bytes_returned == 0) {
522524 std.log.warn("file system watch queue overflowed; falling back to fstat", .{});
523 markAllFilesDirty(w, gpa);
525 markAllFilesDirty(w);
524526 try dir.startListening(w);
525527 return true;
526528 }
......@@ -530,9 +532,9 @@ const Os = switch (builtin.os.tag) {
530532 const notify: *windows.FILE.NOTIFY.INFORMATION = @ptrCast(@alignCast(&dir.buffer[offset]));
531533 const file_name = file_name_buf[0..std.unicode.wtf16LeToWtf8(&file_name_buf, notify.fileName())];
532534 if (dir.reaction_set.getPtr(".")) |glob_set|
533 any_dirty = markStepSetDirty(gpa, glob_set, any_dirty);
535 any_dirty = markStepSetDirty(maker, glob_set, any_dirty);
534536 if (dir.reaction_set.getPtr(file_name)) |step_set|
535 any_dirty = markStepSetDirty(gpa, step_set, any_dirty);
537 any_dirty = markStepSetDirty(maker, step_set, any_dirty);
536538 if (notify.NextEntryOffset == 0)
537539 break;
538540
......@@ -619,14 +621,17 @@ const Os = switch (builtin.os.tag) {
619621 w.dir_count = w.dir_table.count();
620622 }
621623
622 fn wait(w: *Watch, gpa: Allocator, io: Io, timeout: Timeout) !WaitResult {
624 fn wait(w: *Watch, timeout: Timeout) !WaitResult {
625 const maker = w.maker;
626 const io = maker.graph.io;
627
623628 for (0..2) |attempt| {
624629 while (w.os.ready_dirs.popFirst()) |ready_node| {
625630 const dir: *Directory = @fieldParentPtr("ready_node", ready_node);
626631 assert(dir.state == .ready);
627632 dir.state = .idle;
628633 switch (dir.iosb.u.Status) {
629 .SUCCESS => return if (try markDirtySteps(w, gpa, dir)) .dirty else .clean,
634 .SUCCESS => return if (try markDirtySteps(w, dir)) .dirty else .clean,
630635 .PENDING => unreachable,
631636 .CANCELLED => {},
632637 else => |status| return windows.unexpectedStatus(status),
......@@ -810,25 +815,25 @@ const Os = switch (builtin.os.tag) {
810815 w.dir_count = w.dir_table.count();
811816 }
812817
813 fn wait(w: *Watch, gpa: Allocator, io: Io, timeout: Timeout) !WaitResult {
814 _ = io;
818 fn wait(w: *Watch, timeout: Timeout) !WaitResult {
819 const maker = w.maker;
815820 var timespec_buffer: posix.timespec = undefined;
816821 var event_buffer: [100]posix.Kevent = undefined;
817822 var n = try Io.Kqueue.kevent(w.os.kq_fd, &.{}, &event_buffer, timeout.toTimespec(&timespec_buffer));
818823 if (n == 0) return .timeout;
819824 const reaction_sets = w.os.handles.items(.rs);
820 var any_dirty = markDirtySteps(gpa, reaction_sets, event_buffer[0..n], false);
825 var any_dirty = markDirtySteps(maker, reaction_sets, event_buffer[0..n], false);
821826 timespec_buffer = .{ .sec = 0, .nsec = 0 };
822827 while (n == event_buffer.len) {
823828 n = try Io.Kqueue.kevent(w.os.kq_fd, &.{}, &event_buffer, &timespec_buffer);
824829 if (n == 0) break;
825 any_dirty = markDirtySteps(gpa, reaction_sets, event_buffer[0..n], any_dirty);
830 any_dirty = markDirtySteps(maker, reaction_sets, event_buffer[0..n], any_dirty);
826831 }
827832 return if (any_dirty) .dirty else .clean;
828833 }
829834
830835 fn markDirtySteps(
831 gpa: Allocator,
836 maker: *Maker,
832837 reaction_sets: []ReactionSet,
833838 events: []const std.c.Kevent,
834839 start_any_dirty: bool,
......@@ -840,13 +845,13 @@ const Os = switch (builtin.os.tag) {
840845 // If we knew the basename of the changed file, here we would
841846 // mark only the step set dirty, and possibly the glob set:
842847 //if (reaction_set.getPtr(".")) |glob_set|
843 // any_dirty = markStepSetDirty(gpa, glob_set, any_dirty);
848 // any_dirty = markStepSetDirty(maker, glob_set, any_dirty);
844849 //if (reaction_set.getPtr(file_name)) |step_set|
845 // any_dirty = markStepSetDirty(gpa, step_set, any_dirty);
850 // any_dirty = markStepSetDirty(maker, step_set, any_dirty);
846851 // However we don't know the file name so just mark all the
847852 // sets dirty for this directory.
848853 for (reaction_set.values()) |*step_set| {
849 any_dirty = markStepSetDirty(gpa, step_set, any_dirty);
854 any_dirty = markStepSetDirty(maker, step_set, any_dirty);
850855 }
851856 }
852857 return any_dirty;
......@@ -878,8 +883,8 @@ const Os = switch (builtin.os.tag) {
878883 else => void,
879884};
880885
881pub fn init(cwd_path: []const u8, configuration: *const Configuration, make_steps: []Step) !Watch {
882 return Os.init(cwd_path, configuration, make_steps);
886pub fn init(maker: *Maker) !Watch {
887 return Os.init(maker);
883888}
884889
885890pub const Match = struct {
......@@ -904,7 +909,9 @@ pub const Match = struct {
904909 };
905910};
906911
907fn markAllFilesDirty(w: *Watch, gpa: Allocator) void {
912fn markAllFilesDirty(w: *Watch) void {
913 const maker = w.maker;
914
908915 for (switch (builtin.os.tag) {
909916 .windows => w.os.handle_table.keys(),
910917 else => w.os.handle_table.values(),
......@@ -915,18 +922,18 @@ fn markAllFilesDirty(w: *Watch, gpa: Allocator) void {
915922 };
916923 for (reaction_set.values()) |step_set| {
917924 for (step_set.keys()) |step_index| {
918 const step = &w.make_steps[@intFromEnum(step_index)];
919 _ = step.invalidateResult(gpa);
925 const step = maker.stepByIndex(step_index);
926 _ = maker.invalidateResult(step);
920927 }
921928 }
922929 }
923930}
924931
925fn markStepSetDirty(gpa: Allocator, make_steps: []Step, step_set: *StepSet, any_dirty: bool) bool {
932fn markStepSetDirty(maker: *Maker, step_set: *StepSet, any_dirty: bool) bool {
926933 var this_any_dirty = false;
927934 for (step_set.keys()) |step_index| {
928 const step = &make_steps[@intFromEnum(step_index)];
929 if (step.invalidateResult(gpa)) this_any_dirty = true;
935 const step = maker.stepByIndex(step_index);
936 if (maker.invalidateResult(step)) this_any_dirty = true;
930937 }
931938 return any_dirty or this_any_dirty;
932939}
......@@ -971,6 +978,6 @@ pub const WaitResult = enum {
971978 clean,
972979};
973980
974pub fn wait(w: *Watch, gpa: Allocator, io: Io, timeout: Timeout) !WaitResult {
975 return Os.wait(w, gpa, io, timeout);
981pub fn wait(w: *Watch, timeout: Timeout) !WaitResult {
982 return Os.wait(w, timeout);
976983}
lib/compiler/Maker/WebServer.zig+94-71
......@@ -14,16 +14,14 @@ const log = std.log.scoped(.web_server);
1414const mem = std.mem;
1515const net = std.Io.net;
1616
17const Maker = @import("../Maker.zig");
1718const Fuzz = @import("Fuzz.zig");
1819const Graph = @import("Graph.zig");
1920const Step = @import("Step.zig");
2021
21gpa: Allocator,
22graph: *const Graph,
23all_steps: []const Configuration.Step.Index,
22maker: *Maker,
2423listen_address: net.IpAddress,
2524root_prog_node: std.Progress.Node,
26watch: bool,
2725
2826tcp_server: ?net.Server,
2927serve_task: ?Io.Future(Io.Cancelable!void),
......@@ -65,19 +63,16 @@ pub const base_clock: Io.Clock = .awake;
6563
6664/// Thread-safe. Triggers updates to be sent to connected WebSocket clients; see `update_id`.
6765pub fn notifyUpdate(ws: *WebServer) void {
66 const io = ws.maker.graph.io;
6867 _ = ws.update_id.rmw(.Add, 1, .release);
69 ws.graph.io.futexWake(u32, &ws.update_id.raw, 16);
68 io.futexWake(u32, &ws.update_id.raw, 16);
7069}
7170
7271pub const Options = struct {
73 gpa: Allocator,
74 graph: *const Graph,
75 all_steps: []const Configuration.Step.Index,
72 maker: *Maker,
7673 root_prog_node: std.Progress.Node,
77 watch: bool,
7874 listen_address: net.IpAddress,
7975 base_timestamp: Io.Clock.Timestamp,
80 configuration: *const Configuration,
8176};
8277pub fn init(opts: Options) WebServer {
8378 // The upcoming `Io` interface should allow us to use `Io.async` and `Io.concurrent`
......@@ -85,10 +80,13 @@ pub fn init(opts: Options) WebServer {
8580 comptime assert(!builtin.single_threaded);
8681 assert(opts.base_timestamp.clock == base_clock);
8782
88 const all_steps = opts.all_steps;
89 const c = opts.configuration;
83 const maker = opts.maker;
84 const all_steps = maker.step_stack.keys();
85 const c = &maker.scanned_config.configuration;
86 const gpa = maker.gpa;
87 const graph = maker.graph;
9088
91 const step_names_trailing = opts.gpa.alloc(u8, len: {
89 const step_names_trailing = gpa.alloc(u8, len: {
9290 var name_bytes: usize = 0;
9391 for (all_steps) |step_index| name_bytes += step_index.ptr(c).name.slice(c).len;
9492 break :len name_bytes + all_steps.len * 4;
......@@ -105,25 +103,22 @@ pub fn init(opts: Options) WebServer {
105103 assert(idx == step_names_trailing.len);
106104 }
107105
108 const step_status_bits = opts.gpa.alloc(
106 const step_status_bits = gpa.alloc(
109107 u8,
110108 std.math.divCeil(usize, all_steps.len, 4) catch unreachable,
111109 ) catch @panic("out of memory");
112110 @memset(step_status_bits, 0);
113111
114 const time_reports_len: usize = if (opts.graph.time_report) all_steps.len else 0;
115 const time_report_msgs = opts.gpa.alloc([]u8, time_reports_len) catch @panic("out of memory");
116 const time_report_update_times = opts.gpa.alloc(i64, time_reports_len) catch @panic("out of memory");
112 const time_reports_len: usize = if (graph.time_report) all_steps.len else 0;
113 const time_report_msgs = gpa.alloc([]u8, time_reports_len) catch @panic("out of memory");
114 const time_report_update_times = gpa.alloc(i64, time_reports_len) catch @panic("out of memory");
117115 @memset(time_report_msgs, &.{});
118116 @memset(time_report_update_times, std.math.minInt(i64));
119117
120118 return .{
121 .gpa = opts.gpa,
122 .graph = opts.graph,
123 .all_steps = all_steps,
119 .maker = maker,
124120 .listen_address = opts.listen_address,
125121 .root_prog_node = opts.root_prog_node,
126 .watch = opts.watch,
127122
128123 .tcp_server = null,
129124 .serve_task = null,
......@@ -148,8 +143,9 @@ pub fn init(opts: Options) WebServer {
148143 };
149144}
150145pub fn deinit(ws: *WebServer) void {
151 const gpa = ws.gpa;
152 const io = ws.graph.io;
146 const maker = ws.maker;
147 const gpa = maker.gpa;
148 const io = maker.graph.io;
153149
154150 gpa.free(ws.step_names_trailing);
155151 gpa.free(ws.step_status_bits);
......@@ -170,7 +166,8 @@ pub fn deinit(ws: *WebServer) void {
170166pub fn start(ws: *WebServer) error{AlreadyReported}!void {
171167 assert(ws.tcp_server == null);
172168 assert(ws.serve_task == null);
173 const io = ws.graph.io;
169 const maker = ws.maker;
170 const io = maker.graph.io;
174171
175172 ws.tcp_server = ws.listen_address.listen(io, .{ .reuse_address = true }) catch |err| {
176173 log.err("failed to listen to port {d}: {t}", .{ ws.listen_address.getPort(), err });
......@@ -189,9 +186,12 @@ pub fn start(ws: *WebServer) error{AlreadyReported}!void {
189186 }
190187}
191188fn serve(ws: *WebServer) Io.Cancelable!void {
192 const io = ws.graph.io;
189 const maker = ws.maker;
190 const io = maker.graph.io;
191
193192 var group: Io.Group = .init;
194193 defer group.cancel(io);
194
195195 while (true) {
196196 var stream = ws.tcp_server.?.accept(io) catch |err| switch (err) {
197197 error.Canceled => |e| return e,
......@@ -223,8 +223,10 @@ pub fn updateStepStatus(
223223 step_index: Configuration.Step.Index,
224224 new_status: abi.StepUpdate.Status,
225225) void {
226 const maker = ws.maker;
227 const all_steps = maker.step_stack.keys();
226228 // TODO don't do linear search, especially in a hot loop like this
227 const step_idx: u32 = for (ws.all_steps, 0..) |s, i| {
229 const step_idx: u32 = for (all_steps, 0..) |s, i| {
228230 if (s == step_index) break @intCast(i);
229231 } else unreachable;
230232 const ptr = &ws.step_status_bits[step_idx / 4];
......@@ -238,13 +240,16 @@ pub fn updateStepStatus(
238240pub fn finishBuild(ws: *WebServer, opts: struct {
239241 fuzz: bool,
240242}) void {
243 const maker = ws.maker;
244 const all_steps = maker.step_stack.keys();
245
241246 if (opts.fuzz) {
242247 switch (builtin.os.tag) {
243248 // Current implementation depends on two things that need to be ported to Windows:
244249 // * Memory-mapping to share data between the fuzzer and build runner.
245250 // * COFF/PE support added to `std.debug.Info` (it needs a batching API for resolving
246251 // many addresses to source locations).
247 .windows => std.process.fatal("--fuzz not yet implemented for {s}", .{@tagName(builtin.os.tag)}),
252 .windows => std.process.fatal("--fuzz not yet implemented for {t}", .{builtin.os.tag}),
248253 else => {},
249254 }
250255 if (@bitSizeOf(usize) != 64) {
......@@ -260,28 +265,26 @@ pub fn finishBuild(ws: *WebServer, opts: struct {
260265 ws.build_status.store(.fuzz_init, .monotonic);
261266 ws.notifyUpdate();
262267
263 ws.fuzz = Fuzz.init(
264 ws.gpa,
265 ws.graph.io,
266 ws.all_steps,
267 ws.root_prog_node,
268 .{ .forever = .{ .ws = ws } },
269 ) catch |err| std.process.fatal("failed to start fuzzer: {s}", .{@errorName(err)});
268 ws.fuzz = Fuzz.init(maker, all_steps, ws.root_prog_node, .{ .forever = .{ .ws = ws } }) catch |err|
269 std.process.fatal("failed to start fuzzer: {t}", .{err});
270270 ws.fuzz.?.start();
271271 }
272272
273 ws.build_status.store(if (ws.watch) .watching else .idle, .monotonic);
273 ws.build_status.store(if (maker.watch) .watching else .idle, .monotonic);
274274 ws.notifyUpdate();
275275}
276276
277pub fn now(s: *const WebServer) i64 {
278 const io = s.graph.io;
277pub fn now(ws: *const WebServer) i64 {
278 const maker = ws.maker;
279 const io = maker.graph.io;
279280 const ts = base_clock.now(io);
280 return @intCast(s.base_timestamp.durationTo(ts).toNanoseconds());
281 return @intCast(ws.base_timestamp.durationTo(ts).toNanoseconds());
281282}
282283
283284fn accept(ws: *WebServer, stream: net.Stream) void {
284 const io = ws.graph.io;
285 const maker = ws.maker;
286 const io = maker.graph.io;
287
285288 defer {
286289 // `net.Stream.close` wants to helpfully overwrite `stream` with
287290 // `undefined`, but it cannot do so since it is an immutable parameter.
......@@ -326,12 +329,16 @@ fn accept(ws: *WebServer, stream: net.Stream) void {
326329}
327330
328331fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn {
329 const io = ws.graph.io;
332 const maker = ws.maker;
333 const gpa = maker.gpa;
334 const graph = maker.graph;
335 const io = graph.io;
336 const all_steps = maker.step_stack.keys();
330337
331338 var prev_build_status = ws.build_status.load(.monotonic);
332339
333 const prev_step_status_bits = try ws.gpa.alloc(u8, ws.step_status_bits.len);
334 defer ws.gpa.free(prev_step_status_bits);
340 const prev_step_status_bits = try gpa.alloc(u8, ws.step_status_bits.len);
341 defer gpa.free(prev_step_status_bits);
335342 for (prev_step_status_bits, ws.step_status_bits) |*copy, *shared| {
336343 copy.* = @atomicLoad(u8, shared, .monotonic);
337344 }
......@@ -343,10 +350,10 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn {
343350 const hello_header: abi.Hello = .{
344351 .status = prev_build_status,
345352 .flags = .{
346 .time_report = ws.graph.time_report,
353 .time_report = graph.time_report,
347354 },
348355 .timestamp = ws.now(),
349 .steps_len = @intCast(ws.all_steps.len),
356 .steps_len = @intCast(all_steps.len),
350357 };
351358 var bufs: [3][]const u8 = .{ @ptrCast(&hello_header), ws.step_names_trailing, prev_step_status_bits };
352359 try sock.writeMessageVec(&bufs, .binary);
......@@ -369,8 +376,8 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn {
369376 if (update_time <= prev_time) continue;
370377 // We want to send `msg`, but shouldn't block `ws.time_report_mutex` while we do, so
371378 // that we don't hold up the build system on the client accepting this packet.
372 const owned_msg = try ws.gpa.dupe(u8, msg);
373 defer ws.gpa.free(owned_msg);
379 const owned_msg = try gpa.dupe(u8, msg);
380 defer gpa.free(owned_msg);
374381 // Temporarily unlock, then re-lock after the message is sent.
375382 ws.time_report_mutex.unlock(io);
376383 defer ws.time_report_mutex.lockUncancelable(io);
......@@ -427,7 +434,8 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn {
427434 }
428435}
429436fn recvWebSocketMessages(ws: *WebServer, sock: *http.Server.WebSocket) void {
430 const io = ws.graph.io;
437 const maker = ws.maker;
438 const io = maker.graph.io;
431439
432440 while (true) {
433441 const msg = sock.readSmallMessage() catch return;
......@@ -485,8 +493,11 @@ fn serveLibFile(
485493 sub_path: []const u8,
486494 content_type: []const u8,
487495) !void {
496 const maker = ws.maker;
497 const graph = maker.graph;
498
488499 return serveFile(ws, request, .{
489 .root_dir = ws.graph.zig_lib_directory,
500 .root_dir = graph.zig_lib_directory,
490501 .sub_path = sub_path,
491502 }, content_type);
492503}
......@@ -495,7 +506,9 @@ fn serveClientWasm(
495506 req: *http.Server.Request,
496507 optimize_mode: std.builtin.OptimizeMode,
497508) !void {
498 var arena_state: std.heap.ArenaAllocator = .init(ws.gpa);
509 const gpa = ws.maker.gpa;
510
511 var arena_state: std.heap.ArenaAllocator = .init(gpa);
499512 defer arena_state.deinit();
500513 const arena = arena_state.allocator();
501514
......@@ -510,8 +523,10 @@ pub fn serveFile(
510523 path: Cache.Path,
511524 content_type: []const u8,
512525) !void {
513 const gpa = ws.gpa;
514 const io = ws.graph.io;
526 const maker = ws.maker;
527 const gpa = ws.maker.gpa;
528 const io = maker.graph.io;
529
515530 // The desired API is actually sendfile, which will require enhancing http.Server.
516531 // We load the file with every request so that the user can make changes to the file
517532 // and refresh the HTML page without restarting this server.
......@@ -528,7 +543,8 @@ pub fn serveFile(
528543 });
529544}
530545pub fn serveTarFile(ws: *WebServer, request: *http.Server.Request, paths: []const Cache.Path) !void {
531 const graph = ws.graph;
546 const maker = ws.maker;
547 const graph = maker.graph;
532548 const io = graph.io;
533549
534550 var send_buffer: [0x4000]u8 = undefined;
......@@ -576,8 +592,9 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim
576592 const arch_os_abi = "wasm32-freestanding";
577593 const cpu_features = "baseline+atomics+bulk_memory+multivalue+mutable_globals+nontrapping_fptoint+reference_types+sign_ext";
578594
579 const gpa = ws.gpa;
580 const graph = ws.graph;
595 const maker = ws.maker;
596 const gpa = maker.gpa;
597 const graph = maker.graph;
581598 const io = graph.io;
582599
583600 const main_src_path: Cache.Path = .{
......@@ -697,7 +714,7 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim
697714 if (code != 0) {
698715 log.err(
699716 "the following command exited with error code {d}:\n{s}",
700 .{ code, try Step.allocPrintCmd(arena, .inherit, null, argv.items) },
717 .{ code, try std.zig.allocPrintCmd(arena, .inherit, null, argv.items) },
701718 );
702719 return error.WasmCompilationFailed;
703720 }
......@@ -705,21 +722,21 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim
705722 .signal => |sig| {
706723 log.err(
707724 "the following command terminated with signal {t}:\n{s}",
708 .{ sig, try Step.allocPrintCmd(arena, .inherit, null, argv.items) },
725 .{ sig, try std.zig.allocPrintCmd(arena, .inherit, null, argv.items) },
709726 );
710727 return error.WasmCompilationFailed;
711728 },
712729 .stopped => |sig| {
713730 log.err(
714731 "the following command stopped unexpectedly with signal {t}:\n{s}",
715 .{ sig, try Build.Step.allocPrintCmd(arena, .inherit, null, argv.items) },
732 .{ sig, try std.zig.allocPrintCmd(arena, .inherit, null, argv.items) },
716733 );
717734 return error.WasmCompilationFailed;
718735 },
719736 .unknown => {
720737 log.err(
721738 "the following command terminated unexpectedly:\n{s}",
722 .{try Step.allocPrintCmd(arena, .inherit, null, argv.items)},
739 .{try std.zig.allocPrintCmd(arena, .inherit, null, argv.items)},
723740 );
724741 return error.WasmCompilationFailed;
725742 },
......@@ -729,14 +746,14 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim
729746 try result_error_bundle.renderToStderr(io, .{}, .auto);
730747 log.err("the following command failed with {d} compilation errors:\n{s}", .{
731748 result_error_bundle.errorMessageCount(),
732 try Step.allocPrintCmd(arena, .inherit, null, argv.items),
749 try std.zig.allocPrintCmd(arena, .inherit, null, argv.items),
733750 });
734751 return error.WasmCompilationFailed;
735752 }
736753
737754 const base_path = result orelse {
738755 log.err("child process failed to report result\n{s}", .{
739 try Step.allocPrintCmd(arena, .inherit, null, argv.items),
756 try std.zig.allocPrintCmd(arena, .inherit, null, argv.items),
740757 });
741758 return error.WasmCompilationFailed;
742759 };
......@@ -773,11 +790,13 @@ pub fn updateTimeReportCompile(ws: *WebServer, opts: struct {
773790 /// The trailing data of `abi.time_report.CompileResult`, except the step name.
774791 trailing: []const u8,
775792}) void {
776 const gpa = ws.gpa;
777 const io = ws.graph.io;
793 const maker = ws.maker;
794 const gpa = maker.gpa;
795 const io = maker.graph.io;
796 const all_steps = maker.step_stack.keys();
778797
779798 // TODO don't do linear search
780 const step_idx: u32 = for (ws.all_steps, 0..) |s, i| {
799 const step_idx: u32 = for (all_steps, 0..) |s, i| {
781800 if (s == opts.compile_step) break @intCast(i);
782801 } else unreachable;
783802
......@@ -815,11 +834,13 @@ pub fn updateTimeReportCompile(ws: *WebServer, opts: struct {
815834}
816835
817836pub fn updateTimeReportGeneric(ws: *WebServer, step_index: Configuration.Step.Index, duration: Io.Duration) void {
818 const gpa = ws.gpa;
819 const io = ws.graph.io;
837 const maker = ws.maker;
838 const gpa = maker.gpa;
839 const io = maker.graph.io;
840 const all_steps = maker.step_stack.keys();
820841
821842 // TODO don't do linear search
822 const step_idx: u32 = for (ws.all_steps, 0..) |s, i| {
843 const step_idx: u32 = for (all_steps, 0..) |s, i| {
823844 if (s == step_index) break @intCast(i);
824845 } else unreachable;
825846
......@@ -852,11 +873,13 @@ pub fn updateTimeReportRunTest(
852873 tests: *const Step.Run.CachedTestMetadata,
853874 ns_per_test: []const u64,
854875) void {
855 const gpa = ws.gpa;
856 const io = ws.graph.io;
876 const maker = ws.maker;
877 const gpa = maker.gpa;
878 const io = maker.graph.io;
879 const all_steps = maker.step_stack.keys();
857880
858881 // TODO don't do linear search
859 const step_idx: u32 = for (ws.all_steps, 0..) |s, i| {
882 const step_idx: u32 = for (all_steps, 0..) |s, i| {
860883 if (s == run_step_index) break @intCast(i);
861884 } else unreachable;
862885
......@@ -910,7 +933,7 @@ const RunnerRequest = union(enum) {
910933 rebuild,
911934};
912935pub fn getRunnerRequest(ws: *WebServer) ?RunnerRequest {
913 const io = ws.graph.io;
936 const io = ws.maker.graph.io;
914937 ws.runner_request_mutex.lock(io) catch return;
915938 defer ws.runner_request_mutex.unlock(io);
916939 if (ws.runner_request) |req| {
......@@ -921,7 +944,7 @@ pub fn getRunnerRequest(ws: *WebServer) ?RunnerRequest {
921944 return null;
922945}
923946pub fn wait(ws: *WebServer) Io.Cancelable!RunnerRequest {
924 const io = ws.graph.io;
947 const io = ws.maker.graph.io;
925948 try ws.runner_request_mutex.lock(io);
926949 defer ws.runner_request_mutex.unlock(io);
927950 while (true) {
lib/std/Build.zig-13
......@@ -45,7 +45,6 @@ install_prefix: []const u8,
4545/// Path to the directory containing build.zig.
4646build_root: Cache.Directory,
4747cache_root: Cache.Directory,
48pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null,
4948debug_log_scopes: []const []const u8 = &.{},
5049debug_compile_errors: bool = false,
5150debug_incremental: bool = false,
......@@ -176,18 +175,6 @@ pub const RunError = error{
176175 ExecNotSupported,
177176} || std.process.SpawnError;
178177
179pub const PkgConfigError = error{
180 PkgConfigCrashed,
181 PkgConfigFailed,
182 PkgConfigNotInstalled,
183 PkgConfigInvalidOutput,
184};
185
186pub const PkgConfigPkg = struct {
187 name: []const u8,
188 desc: []const u8,
189};
190
191178const UserInputOptionsMap = StringHashMap(UserInputOption);
192179const AvailableOptionsMap = StringHashMap(AvailableOption);
193180
lib/std/Build/Step/Compile.zig-40
......@@ -8,13 +8,9 @@ const fs = std.fs;
88const assert = std.debug.assert;
99const panic = std.debug.panic;
1010const StringHashMap = std.StringHashMap;
11const Sha256 = std.crypto.hash.sha2.Sha256;
1211const Allocator = std.mem.Allocator;
1312const Step = std.Build.Step;
1413const LazyPath = std.Build.LazyPath;
15const PkgConfigPkg = std.Build.PkgConfigPkg;
16const PkgConfigError = std.Build.PkgConfigError;
17const RunError = std.Build.RunError;
1814const Module = std.Build.Module;
1915const InstallDir = std.Build.InstallDir;
2016const GeneratedFile = std.Build.GeneratedFile;
......@@ -777,42 +773,6 @@ pub fn setExecCmd(compile: *Compile, args: []const ?[]const u8) void {
777773 compile.exec_cmd_args = duped_args;
778774}
779775
780const CliNamedModules = struct {
781 modules: std.AutoArrayHashMapUnmanaged(*Module, void),
782 names: std.StringArrayHashMapUnmanaged(void),
783
784 /// Traverse the whole dependency graph and give every module a unique
785 /// name, ideally one named after what it's called somewhere in the graph.
786 /// It will help here to have both a mapping from module to name and a set
787 /// of all the currently-used names.
788 fn init(arena: Allocator, root_module: *Module) Allocator.Error!CliNamedModules {
789 var compile: CliNamedModules = .{
790 .modules = .{},
791 .names = .{},
792 };
793 const graph = root_module.getGraph();
794 {
795 assert(graph.modules[0] == root_module);
796 try compile.modules.put(arena, root_module, {});
797 try compile.names.put(arena, "root", {});
798 }
799 for (graph.modules[1..], graph.names[1..]) |mod, orig_name| {
800 var name = orig_name;
801 var n: usize = 0;
802 while (true) {
803 const gop = try compile.names.getOrPut(arena, name);
804 if (!gop.found_existing) {
805 try compile.modules.putNoClobber(arena, mod, {});
806 break;
807 }
808 name = try std.fmt.allocPrint(arena, "{s}{d}", .{ orig_name, n });
809 n += 1;
810 }
811 }
812 return compile;
813 }
814};
815
816776fn getGeneratedFilePath(compile: *Compile, comptime tag_name: []const u8, asking_step: ?*Step) ![]const u8 {
817777 const step = &compile.step;
818778 const b = step.owner;
lib/std/zig/Configuration.zig+15-11
......@@ -436,23 +436,23 @@ pub const Step = extern struct {
436436 };
437437
438438 pub const Tag = enum(u5) {
439 top_level,
439 check_file,
440 check_object,
440441 compile,
442 config_header,
443 fail,
444 fmt,
441445 install_artifact,
442 install_file,
443446 install_dir,
447 install_file,
448 objcopy,
449 options,
444450 remove_dir,
445 fail,
446 fmt,
451 run,
452 top_level,
447453 translate_c,
448 write_file,
449454 update_source_files,
450 run,
451 check_file,
452 check_object,
453 config_header,
454 objcopy,
455 options,
455 write_file,
456456 };
457457
458458 pub const TopLevel = struct {
......@@ -808,6 +808,10 @@ pub const Step = extern struct {
808808 _: u23 = 0,
809809 };
810810 };
811
812 pub fn flags(s: *const Step, c: *const Configuration) Flags {
813 return @bitCast(c.extra[s.extra_index]);
814 }
811815};
812816
813817pub const MaxRss = enum(u32) {