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 {...@@ -419,7 +419,7 @@ pub fn main(init: process.Init.Minimal) !void {
419 var top_level_steps: std.StringArrayHashMapUnmanaged(Configuration.Step.Index) = .empty;419 var top_level_steps: std.StringArrayHashMapUnmanaged(Configuration.Step.Index) = .empty;
420 for (configuration.steps, 0..) |*conf_step, step_index_usize| {420 for (configuration.steps, 0..) |*conf_step, step_index_usize| {
421 const step_index: Configuration.Step.Index = @enumFromInt(step_index_usize);421 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);
423 if (flags.tag == .top_level) {423 if (flags.tag == .top_level) {
424 const name = step_index.ptr(&configuration).name.slice(&configuration);424 const name = step_index.ptr(&configuration).name.slice(&configuration);
425 try top_level_steps.put(arena, name, step_index);425 try top_level_steps.put(arena, name, step_index);
...@@ -538,7 +538,7 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -538,7 +538,7 @@ pub fn main(init: process.Init.Minimal) !void {
538 var w: Watch = w: {538 var w: Watch = w: {
539 if (!watch) break :w undefined;539 if (!watch) break :w undefined;
540 if (!Watch.have_impl) fatal("--watch not yet implemented for {t}", .{builtin.os.tag});540 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);
542 };542 };
543543
544 const now = Io.Clock.Timestamp.now(io, .awake);544 const now = Io.Clock.Timestamp.now(io, .awake);
...@@ -546,14 +546,10 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -546,14 +546,10 @@ pub fn main(init: process.Init.Minimal) !void {
546 maker.web_server = if (webui_listen) |listen_address| ws: {546 maker.web_server = if (webui_listen) |listen_address| ws: {
547 if (builtin.single_threaded) unreachable; // `fatal` above547 if (builtin.single_threaded) unreachable; // `fatal` above
548 break :ws .init(.{548 break :ws .init(.{
549 .gpa = gpa,549 .maker = &maker,
550 .graph = &graph,
551 .all_steps = maker.step_stack.keys(),
552 .root_prog_node = main_progress_node,550 .root_prog_node = main_progress_node,
553 .watch = watch,
554 .listen_address = listen_address,551 .listen_address = listen_address,
555 .base_timestamp = now,552 .base_timestamp = now,
556 .configuration = &scanned_config.configuration,
557 });553 });
558 } else null;554 } else null;
559555
...@@ -564,7 +560,9 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -564,7 +560,9 @@ pub fn main(init: process.Init.Minimal) !void {
564 rebuild: while (true) : (if (maker.error_style.clearOnUpdate()) {560 rebuild: while (true) : (if (maker.error_style.clearOnUpdate()) {
565 const stderr = try io.lockStderr(&stdio_buffer_allocation, graph.stderr_mode);561 const stderr = try io.lockStderr(&stdio_buffer_allocation, graph.stderr_mode);
566 defer io.unlockStderr();562 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 };
568 }) {566 }) {
569 if (maker.web_server) |*ws| ws.startBuild();567 if (maker.web_server) |*ws| ws.startBuild();
570568
...@@ -608,15 +606,15 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -608,15 +606,15 @@ pub fn main(init: process.Init.Minimal) !void {
608 // recursive dependants.606 // recursive dependants.
609 var caption_buf: [std.Progress.Node.max_name_len]u8 = undefined;607 var caption_buf: [std.Progress.Node.max_name_len]u8 = undefined;
610 const caption = std.fmt.bufPrint(&caption_buf, "watching {d} directories, {d} processes", .{608 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),
612 }) catch &caption_buf;610 }) catch &caption_buf;
613 var debouncing_node = main_progress_node.start(caption, 0);611 var debouncing_node = main_progress_node.start(caption, 0);
614 var in_debounce = false;612 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)) {
616 .timeout => {614 .timeout => {
617 assert(in_debounce);615 assert(in_debounce);
618 debouncing_node.end();616 debouncing_node.end();
619 markFailedStepsDirty(gpa, maker.steps, maker.step_stack.keys());617 markFailedStepsDirty(&maker);
620 continue :rebuild;618 continue :rebuild;
621 },619 },
622 .dirty => if (!in_debounce) {620 .dirty => if (!in_debounce) {
...@@ -629,18 +627,20 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -629,18 +627,20 @@ pub fn main(init: process.Init.Minimal) !void {
629 }627 }
630}628}
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
633 for (all_steps) |step_index| {633 for (all_steps) |step_index| {
634 const step = &make_steps[@intFromEnum(step_index)];634 const step = maker.stepByIndex(step_index);
635 switch (step.state) {635 switch (step.state) {
636 .dependency_failure, .failure, .skipped => _ = step.invalidateResult(gpa),636 .dependency_failure, .failure, .skipped => _ = maker.invalidateResult(step),
637 else => continue,637 else => continue,
638 }638 }
639 }639 }
640 // Now that all dirty steps have been found, the remaining steps that640 // Now that all dirty steps have been found, the remaining steps that
641 // succeeded from last run shall be marked "cached".641 // succeeded from last run shall be marked "cached".
642 for (all_steps) |step_index| {642 for (all_steps) |step_index| {
643 const step = &make_steps[@intFromEnum(step_index)];643 const step = maker.stepByIndex(step_index);
644 switch (step.state) {644 switch (step.state) {
645 .success => step.result_cached = true,645 .success => step.result_cached = true,
646 else => continue,646 else => continue,
...@@ -648,10 +648,11 @@ fn markFailedStepsDirty(gpa: Allocator, make_steps: []Step, all_steps: []const C...@@ -648,10 +648,11 @@ fn markFailedStepsDirty(gpa: Allocator, make_steps: []Step, all_steps: []const C
648 }648 }
649}649}
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();
652 var count: usize = 0;653 var count: usize = 0;
653 for (all_steps) |step_index| {654 for (all_steps) |step_index| {
654 const s = &make_steps[@intFromEnum(step_index)];655 const s = maker.stepByIndex(step_index);
655 count += @intFromBool(s.getZigProcess() != null);656 count += @intFromBool(s.getZigProcess() != null);
656 }657 }
657 return count;658 return count;
...@@ -664,7 +665,7 @@ const InstallPaths = struct {...@@ -664,7 +665,7 @@ const InstallPaths = struct {
664 include: Path,665 include: Path,
665};666};
666667
667fn stepByIndex(maker: *const Maker, i: Configuration.Step.Index) *Step {668pub fn stepByIndex(maker: *const Maker, i: Configuration.Step.Index) *Step {
668 return &maker.steps[@intFromEnum(i)];669 return &maker.steps[@intFromEnum(i)];
669}670}
670671
...@@ -676,7 +677,10 @@ fn prepare(maker: *Maker, step_names: []const []const u8) !void {...@@ -676,7 +677,10 @@ fn prepare(maker: *Maker, step_names: []const []const u8) !void {
676 const step_stack = &maker.step_stack;677 const step_stack = &maker.step_stack;
677 const c = &maker.scanned_config.configuration;678 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
681 if (step_names.len == 0) {685 if (step_names.len == 0) {
682 try step_stack.put(gpa, c.default_step, {});686 try step_stack.put(gpa, c.default_step, {});
...@@ -699,7 +703,7 @@ fn prepare(maker: *Maker, step_names: []const []const u8) !void {...@@ -699,7 +703,7 @@ fn prepare(maker: *Maker, step_names: []const []const u8) !void {
699 rand.shuffle(Configuration.Step.Index, starting_steps);703 rand.shuffle(Configuration.Step.Index, starting_steps);
700704
701 for (starting_steps) |s| {705 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);
703 }707 }
704708
705 {709 {
...@@ -847,13 +851,8 @@ fn makeStepNames(...@@ -847,13 +851,8 @@ fn makeStepNames(
847 }851 }
848852
849 assert(mode == .limit);853 assert(mode == .limit);
850 var f = Fuzz.init(854 var f = Fuzz.init(maker, step_stack.keys(), parent_prog_node, mode) catch |err|
851 gpa,855 fatal("failed to start fuzzer: {t}", .{err});
852 io,
853 step_stack.keys(),
854 parent_prog_node,
855 mode,
856 ) catch |err| fatal("failed to start fuzzer: {t}", .{err});
857 defer f.deinit();856 defer f.deinit();
858857
859 f.start();858 f.start();
...@@ -1048,13 +1047,7 @@ fn makeStep(...@@ -1048,13 +1047,7 @@ fn makeStep(
10481047
1049 .success, .skipped => {},1048 .success, .skipped => {},
1050 }1049 }
1051 } else if (make_step.make(.{1050 } else if (Step.make(step_index, maker, step_prog_node)) state: {
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: {
1058 break :state .success;1051 break :state .success;
1059 } else |err| switch (err) {1052 } else |err| switch (err) {
1060 error.MakeFailed => .failure,1053 error.MakeFailed => .failure,
...@@ -1091,7 +1084,7 @@ fn makeStep(...@@ -1091,7 +1084,7 @@ fn makeStep(
1091 {1084 {
1092 const stderr = try io.lockStderr(&stdio_buffer_allocation, graph.stderr_mode);1085 const stderr = try io.lockStderr(&stdio_buffer_allocation, graph.stderr_mode);
1093 defer io.unlockStderr();1086 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) {
1095 error.Canceled => |e| return e,1088 error.Canceled => |e| return e,
1096 error.WriteFailed => switch (stderr.file_writer.err.?) {1089 error.WriteFailed => switch (stderr.file_writer.err.?) {
1097 error.Canceled => |e| return e,1090 error.Canceled => |e| return e,
...@@ -1136,7 +1129,7 @@ fn makeStep(...@@ -1136,7 +1129,7 @@ fn makeStep(
1136}1129}
11371130
1138fn printTreeStep(1131fn printTreeStep(
1139 maker: *const Maker,1132 maker: *Maker,
1140 step_index: Configuration.Step.Index,1133 step_index: Configuration.Step.Index,
1141 stderr: Io.Terminal,1134 stderr: Io.Terminal,
1142 parent_node: *PrintNode,1135 parent_node: *PrintNode,
...@@ -1211,7 +1204,7 @@ fn printTreeStep(...@@ -1211,7 +1204,7 @@ fn printTreeStep(
1211 }1204 }
1212}1205}
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 {
1215 const s = maker.stepByIndex(step_index);1208 const s = maker.stepByIndex(step_index);
1216 const writer = stderr.writer;1209 const writer = stderr.writer;
1217 switch (s.state) {1210 switch (s.state) {
...@@ -1293,20 +1286,20 @@ fn printStepStatus(maker: *const Maker, step_index: Configuration.Step.Index, st...@@ -1293,20 +1286,20 @@ fn printStepStatus(maker: *const Maker, step_index: Configuration.Step.Index, st
1293 try stderr.setColor(.reset);1286 try stderr.setColor(.reset);
1294 },1287 },
1295 .failure => {1288 .failure => {
1296 try printStepFailure(maker.steps, step_index, stderr, false);1289 try printStepFailure(maker, step_index, stderr, false);
1297 try stderr.setColor(.reset);1290 try stderr.setColor(.reset);
1298 },1291 },
1299 }1292 }
1300}1293}
13011294
1302fn printStepFailure(1295fn printStepFailure(
1303 make_steps: []Step,1296 maker: *Maker,
1304 step_index: Configuration.Step.Index,1297 step_index: Configuration.Step.Index,
1305 stderr: Io.Terminal,1298 stderr: Io.Terminal,
1306 dim: bool,1299 dim: bool,
1307) !void {1300) !void {
1308 const w = stderr.writer;1301 const w = stderr.writer;
1309 const s = &make_steps[@intFromEnum(step_index)];1302 const s = maker.stepByIndex(step_index);
1310 if (s.result_error_bundle.errorMessageCount() > 0) {1303 if (s.result_error_bundle.errorMessageCount() > 0) {
1311 try stderr.setColor(.red);1304 try stderr.setColor(.red);
1312 try w.print(" {d} errors\n", .{1305 try w.print(" {d} errors\n", .{
...@@ -1428,14 +1421,14 @@ fn printChildNodePrefix(stderr: Io.Terminal) !void {...@@ -1428,14 +1421,14 @@ fn printChildNodePrefix(stderr: Io.Terminal) !void {
1428/// when it finishes executing in `makeStep`, it spawns next steps to run in1421/// when it finishes executing in `makeStep`, it spawns next steps to run in
1429/// random order1422/// random order
1430fn constructGraphAndCheckForDependencyLoop(1423fn constructGraphAndCheckForDependencyLoop(
1431 gpa: Allocator,1424 maker: *Maker,
1432 c: *const Configuration,
1433 steps: []Step,
1434 step_index: Configuration.Step.Index,1425 step_index: Configuration.Step.Index,
1435 step_stack: *std.AutoArrayHashMapUnmanaged(Configuration.Step.Index, void),1426 step_stack: *std.AutoArrayHashMapUnmanaged(Configuration.Step.Index, void),
1436 rand: std.Random,1427 rand: std.Random,
1437) error{ DependencyLoopDetected, OutOfMemory }!void {1428) 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);
1439 switch (make_step.state) {1432 switch (make_step.state) {
1440 .precheck_started => {1433 .precheck_started => {
1441 log.err("dependency loop detected: {s}", .{step_index.ptr(c).name.slice(c)});1434 log.err("dependency loop detected: {s}", .{step_index.ptr(c).name.slice(c)});
...@@ -1456,10 +1449,10 @@ fn constructGraphAndCheckForDependencyLoop(...@@ -1456,10 +1449,10 @@ fn constructGraphAndCheckForDependencyLoop(
1456 rand.shuffle(Configuration.Step.Index, deps);1449 rand.shuffle(Configuration.Step.Index, deps);
14571450
1458 for (deps) |dep| {1451 for (deps) |dep| {
1459 const dep_step: *Step = &steps[@intFromEnum(dep)];1452 const dep_step = maker.stepByIndex(dep);
1460 try step_stack.put(gpa, dep, {});1453 try step_stack.put(gpa, dep, {});
1461 try dep_step.dependants.append(gpa, step_index);1454 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) {
1463 error.DependencyLoopDetected => {1456 error.DependencyLoopDetected => {
1464 log.info("needed by: {s}", .{step_index.ptr(c).name.slice(c)});1457 log.info("needed by: {s}", .{step_index.ptr(c).name.slice(c)});
1465 return err;1458 return err;
...@@ -1482,16 +1475,34 @@ fn constructGraphAndCheckForDependencyLoop(...@@ -1482,16 +1475,34 @@ fn constructGraphAndCheckForDependencyLoop(
1482 }1475 }
1483}1476}
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
1485pub fn printErrorMessages(1495pub fn printErrorMessages(
1486 gpa: Allocator,1496 maker: *Maker,
1487 c: *const Configuration,
1488 make_steps: []Step,
1489 failing_step_index: Configuration.Step.Index,1497 failing_step_index: Configuration.Step.Index,
1490 options: std.zig.ErrorBundle.RenderOptions,1498 options: std.zig.ErrorBundle.RenderOptions,
1491 stderr: Io.Terminal,1499 stderr: Io.Terminal,
1492 error_style: ErrorStyle,1500 error_style: ErrorStyle,
1493 multiline_errors: MultilineErrors,1501 multiline_errors: MultilineErrors,
1494) !void {1502) !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", .{});
1495 const writer = stderr.writer;1506 const writer = stderr.writer;
1496 if (error_style.verboseContext()) {1507 if (error_style.verboseContext()) {
1497 // Provide context for where these error messages are coming from by1508 // Provide context for where these error messages are coming from by
...@@ -1500,7 +1511,7 @@ pub fn printErrorMessages(...@@ -1500,7 +1511,7 @@ pub fn printErrorMessages(
1500 defer step_stack.deinit(gpa);1511 defer step_stack.deinit(gpa);
1501 try step_stack.append(gpa, failing_step_index);1512 try step_stack.append(gpa, failing_step_index);
1502 while (true) {1513 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]);
1504 if (last_step.dependants.items.len == 0) break;1515 if (last_step.dependants.items.len == 0) break;
1505 try step_stack.append(gpa, last_step.dependants.items[0]);1516 try step_stack.append(gpa, last_step.dependants.items[0]);
1506 }1517 }
...@@ -1517,7 +1528,7 @@ pub fn printErrorMessages(...@@ -1517,7 +1528,7 @@ pub fn printErrorMessages(
1517 try writer.writeAll(step_index.ptr(c).name.slice(c));1528 try writer.writeAll(step_index.ptr(c).name.slice(c));
15181529
1519 if (step_index == failing_step_index) {1530 if (step_index == failing_step_index) {
1520 try printStepFailure(make_steps, step_index, stderr, true);1531 try printStepFailure(maker, step_index, stderr, true);
1521 } else {1532 } else {
1522 try writer.writeAll("\n");1533 try writer.writeAll("\n");
1523 }1534 }
...@@ -1527,11 +1538,11 @@ pub fn printErrorMessages(...@@ -1527,11 +1538,11 @@ pub fn printErrorMessages(
1527 // Just print the failing step itself.1538 // Just print the failing step itself.
1528 try stderr.setColor(.dim);1539 try stderr.setColor(.dim);
1529 try writer.writeAll(failing_step_index.ptr(c).name.slice(c));1540 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);
1531 try stderr.setColor(.reset);1542 try stderr.setColor(.reset);
1532 }1543 }
15331544
1534 const failing_step = &make_steps[@intFromEnum(failing_step_index)];1545 const failing_step = maker.stepByIndex(failing_step_index);
15351546
1536 if (failing_step.result_stderr.len > 0) {1547 if (failing_step.result_stderr.len > 0) {
1537 try writer.writeAll(failing_step.result_stderr);1548 try writer.writeAll(failing_step.result_stderr);
lib/compiler/Maker/Fuzz.zig+39-19
...@@ -15,8 +15,7 @@ const log = std.log;...@@ -15,8 +15,7 @@ const log = std.log;
15const Maker = @import("../Maker.zig");15const Maker = @import("../Maker.zig");
16const WebServer = @import("WebServer.zig");16const WebServer = @import("WebServer.zig");
1717
18gpa: Allocator,18maker: *Maker,
19io: Io,
20mode: Mode,19mode: Mode,
2120
22/// Allocated into `gpa`.21/// Allocated into `gpa`.
...@@ -76,12 +75,15 @@ const CoverageMap = struct {...@@ -76,12 +75,15 @@ const CoverageMap = struct {
76};75};
7776
78pub fn init(77pub fn init(
79 gpa: Allocator,78 maker: *Maker,
80 io: Io,
81 all_steps: []const Configuration.Step.Index,79 all_steps: []const Configuration.Step.Index,
82 root_prog_node: std.Progress.Node,80 root_prog_node: std.Progress.Node,
83 mode: Mode,81 mode: Mode,
84) error{ OutOfMemory, Canceled }!Fuzz {82) error{ OutOfMemory, Canceled }!Fuzz {
83 const graph = maker.graph;
84 const gpa = graph.cache.gpa;
85 const io = graph.io;
86
85 const run_steps: []const Configuration.Step.Index = steps: {87 const run_steps: []const Configuration.Step.Index = steps: {
86 var steps: std.ArrayList(Configuration.Step.Index) = .empty;88 var steps: std.ArrayList(Configuration.Step.Index) = .empty;
87 defer steps.deinit(gpa);89 defer steps.deinit(gpa);
...@@ -115,8 +117,7 @@ pub fn init(...@@ -115,8 +117,7 @@ pub fn init(
115 }117 }
116118
117 return .{119 return .{
118 .gpa = gpa,120 .maker = maker,
119 .io = io,
120 .mode = mode,121 .mode = mode,
121 .run_steps = run_steps,122 .run_steps = run_steps,
122 .group = .init,123 .group = .init,
...@@ -131,7 +132,10 @@ pub fn init(...@@ -131,7 +132,10 @@ pub fn init(
131}132}
132133
133pub fn start(fuzz: *Fuzz) void {134pub 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
135 fuzz.prog_node = fuzz.root_prog_node.start("Fuzzing", 0);139 fuzz.prog_node = fuzz.root_prog_node.start("Fuzzing", 0);
136140
137 if (fuzz.mode == .forever) {141 if (fuzz.mode == .forever) {
...@@ -149,10 +153,14 @@ pub fn start(fuzz: *Fuzz) void {...@@ -149,10 +153,14 @@ pub fn start(fuzz: *Fuzz) void {
149}153}
150154
151pub fn deinit(fuzz: *Fuzz) void {155pub 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
153 fuzz.group.cancel(io);161 fuzz.group.cancel(io);
154 fuzz.prog_node.end();162 fuzz.prog_node.end();
155 fuzz.gpa.free(fuzz.run_steps);163 gpa.free(fuzz.run_steps);
156}164}
157165
158fn rebuildTestsWorkerRun(run: Configuration.Step.Index, gpa: Allocator, parent_prog_node: std.Progress.Node) void {166fn 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 {...@@ -215,19 +223,20 @@ fn fuzzWorkerRun(fuzz: *Fuzz, run: Configuration.Step.Index) void {
215pub fn serveSourcesTar(fuzz: *Fuzz, req: *std.http.Server.Request) !void {223pub fn serveSourcesTar(fuzz: *Fuzz, req: *std.http.Server.Request) !void {
216 if (true) @panic("TODO");224 if (true) @panic("TODO");
217 assert(fuzz.mode == .forever);225 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);
220 defer arena_state.deinit();229 defer arena_state.deinit();
221 const arena = arena_state.allocator();230 const arena = arena_state.allocator();
222231
223 const DedupTable = std.ArrayHashMapUnmanaged(Build.Cache.Path, void, Build.Cache.Path.TableAdapter, false);232 const DedupTable = std.ArrayHashMapUnmanaged(Build.Cache.Path, void, Build.Cache.Path.TableAdapter, false);
224 var dedup_table: DedupTable = .empty;233 var dedup_table: DedupTable = .empty;
225 defer dedup_table.deinit(fuzz.gpa);234 defer dedup_table.deinit(gpa);
226235
227 for (fuzz.run_steps) |run_step| {236 for (fuzz.run_steps) |run_step| {
228 const compile_inputs = run_step.producer.?.step.inputs.table;237 const compile_inputs = run_step.producer.?.step.inputs.table;
229 for (compile_inputs.keys(), compile_inputs.values()) |dir_path, *file_list| {238 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);
231 for (file_list.items) |sub_path| {240 for (file_list.items) |sub_path| {
232 if (!std.mem.endsWith(u8, sub_path, ".zig")) continue;241 if (!std.mem.endsWith(u8, sub_path, ".zig")) continue;
233 const joined_path = try dir_path.join(arena, sub_path);242 const joined_path = try dir_path.join(arena, sub_path);
...@@ -266,7 +275,9 @@ pub fn sendUpdate(...@@ -266,7 +275,9 @@ pub fn sendUpdate(
266 socket: *std.http.Server.WebSocket,275 socket: *std.http.Server.WebSocket,
267 prev: *Previous,276 prev: *Previous,
268) !void {277) !void {
269 const io = fuzz.io;278 const maker = fuzz.maker;
279 const graph = maker.graph;
280 const io = graph.io;
270281
271 try fuzz.coverage_mutex.lock(io);282 try fuzz.coverage_mutex.lock(io);
272 defer fuzz.coverage_mutex.unlock(io);283 defer fuzz.coverage_mutex.unlock(io);
...@@ -337,7 +348,9 @@ fn coverageRun(fuzz: *Fuzz) void {...@@ -337,7 +348,9 @@ fn coverageRun(fuzz: *Fuzz) void {
337}348}
338349
339fn coverageRunCancelable(fuzz: *Fuzz) Io.Cancelable!void {350fn 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
342 try fuzz.queue_mutex.lock(io);355 try fuzz.queue_mutex.lock(io);
343 defer fuzz.queue_mutex.unlock(io);356 defer fuzz.queue_mutex.unlock(io);
...@@ -363,8 +376,10 @@ fn prepareTables(fuzz: *Fuzz, run_step_index: Configuration.Step.Index, coverage...@@ -363,8 +376,10 @@ fn prepareTables(fuzz: *Fuzz, run_step_index: Configuration.Step.Index, coverage
363 if (true) @panic("TODO");376 if (true) @panic("TODO");
364 assert(fuzz.mode == .forever);377 assert(fuzz.mode == .forever);
365 const ws = fuzz.mode.forever.ws;378 const ws = fuzz.mode.forever.ws;
366 const gpa = fuzz.gpa;379 const maker = fuzz.maker;
367 const io = fuzz.io;380 const graph = maker.graph;
381 const io = graph.io;
382 const gpa = maker.gpa;
368383
369 try fuzz.coverage_mutex.lock(io);384 try fuzz.coverage_mutex.lock(io);
370 defer fuzz.coverage_mutex.unlock(io);385 defer fuzz.coverage_mutex.unlock(io);
...@@ -470,7 +485,10 @@ fn prepareTables(fuzz: *Fuzz, run_step_index: Configuration.Step.Index, coverage...@@ -470,7 +485,10 @@ fn prepareTables(fuzz: *Fuzz, run_step_index: Configuration.Step.Index, coverage
470}485}
471486
472fn addEntryPoint(fuzz: *Fuzz, coverage_id: u64, addr: u64) error{ AlreadyReported, OutOfMemory, Canceled }!void {487fn 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
475 try fuzz.coverage_mutex.lock(io);493 try fuzz.coverage_mutex.lock(io);
476 defer fuzz.coverage_mutex.unlock(io);494 defer fuzz.coverage_mutex.unlock(io);
...@@ -516,13 +534,15 @@ fn addEntryPoint(fuzz: *Fuzz, coverage_id: u64, addr: u64) error{ AlreadyReporte...@@ -516,13 +534,15 @@ fn addEntryPoint(fuzz: *Fuzz, coverage_id: u64, addr: u64) error{ AlreadyReporte
516 });534 });
517 }535 }
518 }536 }
519 try coverage_map.entry_points.append(fuzz.gpa, @intCast(index));537 try coverage_map.entry_points.append(gpa, @intCast(index));
520}538}
521539
522pub fn waitAndPrintReport(fuzz: *Fuzz) Io.Cancelable!void {540pub fn waitAndPrintReport(fuzz: *Fuzz) Io.Cancelable!void {
523 if (true) @panic("TODO");541 if (true) @panic("TODO");
524 assert(fuzz.mode == .limit);542 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
527 try fuzz.group.await(io);547 try fuzz.group.await(io);
528 fuzz.group = .init;548 fuzz.group = .init;
lib/compiler/Maker/Step.zig+124-49
...@@ -1,4 +1,5 @@...@@ -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.
2const Step = @This();3const Step = @This();
34
4const builtin = @import("builtin");5const builtin = @import("builtin");
...@@ -14,14 +15,20 @@ const Configuration = std.Build.Configuration;...@@ -14,14 +15,20 @@ const Configuration = std.Build.Configuration;
14const assert = std.debug.assert;15const assert = std.debug.assert;
1516
16const WebServer = @import("WebServer.zig");17const WebServer = @import("WebServer.zig");
18const Maker = @import("../Maker.zig");
1719
18pub const Compile = void; // @import("Step/Compile.zig");20const Compile = @import("Step/Compile.zig");
19pub const Run = void; // @import("Step/Run.zig");21const Run = @import("Step/Run.zig");
2022
21/// Avoid false sharing.23/// Avoid false sharing.
22_: void align(std.atomic.cache_line) = {},24_: 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.
24state: State = .precheck_unstarted,30state: State = .precheck_unstarted,
31
25dependants: std.ArrayList(Configuration.Step.Index) = .empty,32dependants: std.ArrayList(Configuration.Step.Index) = .empty,
26/// Collects the set of files that retrigger this step to run.33/// Collects the set of files that retrigger this step to run.
27///34///
...@@ -38,6 +45,8 @@ result_error_msgs: std.ArrayList([]const u8) = .empty,...@@ -38,6 +45,8 @@ result_error_msgs: std.ArrayList([]const u8) = .empty,
38result_error_bundle: std.zig.ErrorBundle = .empty,45result_error_bundle: std.zig.ErrorBundle = .empty,
39result_stderr: []const u8 = "",46result_stderr: []const u8 = "",
40result_cached: bool = false,47result_cached: bool = false,
48/// Indicates error information is missing due to allocation failure.
49result_oom: bool = false,
41result_duration_ns: ?u64 = null,50result_duration_ns: ?u64 = null,
42/// 0 means unavailable or not reported.51/// 0 means unavailable or not reported.
43result_peak_rss: usize = 0,52result_peak_rss: usize = 0,
...@@ -46,6 +55,70 @@ result_peak_rss: usize = 0,...@@ -46,6 +55,70 @@ result_peak_rss: usize = 0,
46result_failed_command: ?[]const u8 = null,55result_failed_command: ?[]const u8 = null,
47test_results: TestResults = .{},56test_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
49pub const State = enum {122pub const State = enum {
50 precheck_unstarted,123 precheck_unstarted,
51 precheck_started,124 precheck_started,
...@@ -128,43 +201,51 @@ pub const TestResults = struct {...@@ -128,43 +201,51 @@ pub const TestResults = struct {
128 }201 }
129};202};
130203
131pub const MakeOptions = struct {204pub const MakeError = error{
132 progress_node: std.Progress.Node,205 /// Indicates the error is already reported.
133 watch: bool,206 MakeFailed,
134 web_server: ?*WebServer,207 MakeSkipped,
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,
139};208};
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 they212pub fn make(
144/// have already reported the error. Otherwise, we add a simple error report213 step_index: Configuration.Step.Index,
145/// here.214 maker: *Maker,
146pub fn make(s: *Step, options: MakeOptions) error{ MakeFailed, MakeSkipped }!void {215 progress_node: std.Progress.Node,
147 if (true) @panic("TODO Step.make");216) MakeError!void {
148 const arena = s.owner.allocator;217 const graph = maker.graph;
149 const graph = s.owner.graph;218 const process_arena = graph.arena; // TODO don't leak into the process arena
150 const io = graph.io;219 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
152 var start_ts: ?Io.Timestamp = t: {224 var start_ts: ?Io.Timestamp = t: {
153 if (!graph.time_report) break :t null;225 if (!graph.time_report) break :t null;
154 if (s.id == .compile) break :t null;226 const flags = conf_step.flags(c);
155 if (s.id == .run and s.cast(Run).?.stdio == .zig_test) break :t null;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 }
156 break :t Io.Clock.awake.now(io);235 break :t Io.Clock.awake.now(io);
157 };236 };
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 };
159 if (start_ts) |*ts| {240 if (start_ts) |*ts| {
160 const duration = ts.untilNow(io, .awake);241 const duration = ts.untilNow(io, .awake);
161 options.web_server.?.updateTimeReportGeneric(s, duration);242 maker.web_server.?.updateTimeReportGeneric(step_index, duration);
162 }243 }
163244
164 make_result catch |err| switch (err) {245 make_result catch |err| switch (err) {
165 error.MakeFailed, error.MakeSkipped => |e| return e,246 error.MakeFailed, error.MakeSkipped => |e| return e,
166 else => {247 error.OutOfMemory => {
167 s.result_error_msgs.append(arena, @errorName(err)) catch @panic("OOM");248 s.result_oom = true;
168 return error.MakeFailed;249 return error.MakeFailed;
169 },250 },
170 };251 };
...@@ -173,30 +254,19 @@ pub fn make(s: *Step, options: MakeOptions) error{ MakeFailed, MakeSkipped }!voi...@@ -173,30 +254,19 @@ pub fn make(s: *Step, options: MakeOptions) error{ MakeFailed, MakeSkipped }!voi
173 return error.MakeFailed;254 return error.MakeFailed;
174 }255 }
175256
176 if (s.max_rss != 0 and s.result_peak_rss > s.max_rss) {257 const max_rss = conf_step.max_rss.toBytes();
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)", .{258 if (max_rss != 0 and s.result_peak_rss > max_rss) {
178 s.result_peak_rss, s.max_rss,259 if (std.fmt.allocPrint(
179 }) catch @panic("OOM");260 process_arena,
180 s.result_error_msgs.append(arena, msg) catch @panic("OOM");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;
181 }266 }
182}267}
183268
184/// Implementation detail of file watching. Prepares the step for being re-evaluated.269/// 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.
200pub fn reset(step: *Step, gpa: Allocator) void {270pub fn reset(step: *Step, gpa: Allocator) void {
201 assert(step.state == .precheck_done);271 assert(step.state == .precheck_done);
202272
...@@ -547,9 +617,8 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool, web_server: ?*WebSer...@@ -547,9 +617,8 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool, web_server: ?*WebSer
547}617}
548618
549pub fn getZigProcess(s: *Step) ?*ZigProcess {619pub fn getZigProcess(s: *Step) ?*ZigProcess {
550 if (true) @panic("TODO getZigProcess");620 return switch (s.extended) {
551 return switch (s.id) {621 .compile => |*compile| compile.zig_process,
552 .compile => s.cast(Compile).?.zig_process,
553 else => null,622 else => null,
554 };623 };
555}624}
...@@ -838,3 +907,9 @@ pub fn allocPrintCmd(...@@ -838,3 +907,9 @@ pub fn allocPrintCmd(
838 }907 }
839 return aw.toOwnedSlice();908 return aw.toOwnedSlice();
840}909}
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 @@...@@ -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
1/// Populated during the make phase when there is a long-lived compiler process.17/// Populated during the make phase when there is a long-lived compiler process.
2/// Managed by the build runner, not user build script.18/// Managed by the build runner, not user build script.
3zig_process: ?*Step.ZigProcess,19zig_process: ?*Step.ZigProcess = null,
420
5fn make(step: *Step, options: Step.MakeOptions) !void {21pub fn make(
6 const b = step.owner;22 compile: *Compile,
7 const compile: *Compile = @fieldParentPtr("step", step);23 step_index: Configuration.Step.Index,
824 maker: *Maker,
9 const zig_args = try getZigArgs(compile, false);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
11 const maybe_output_dir = step.evalZigProcess(33 const maybe_output_dir = step.evalZigProcess(
12 zig_args,34 zig_args,
13 options.progress_node,35 progress_node,
14 (b.graph.incremental == true) and (options.watch or options.web_server != null),36 (graph.incremental == true) and (maker.watch or maker.web_server != null),
15 options.web_server,37 maker,
16 options.gpa,
17 ) catch |err| switch (err) {38 ) catch |err| switch (err) {
18 error.NeedCompileErrorCheck => {39 error.NeedCompileErrorCheck => {
19 assert(compile.expect_errors != null);40 assert(compile.expect_errors != null);
...@@ -26,7 +47,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -26,7 +47,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
26 // Update generated files47 // Update generated files
27 if (maybe_output_dir) |output_dir| {48 if (maybe_output_dir) |output_dir| {
28 if (compile.emit_directory) |lp| {49 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});
30 }51 }
3152
32 // zig fmt: off53 // zig fmt: off
...@@ -49,22 +70,23 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -49,22 +70,23 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
49 {70 {
50 try doAtomicSymLinks(71 try doAtomicSymLinks(
51 step,72 step,
52 compile.getEmittedBin().getPath2(b, step),73 compile.getEmittedBin().getPath2(step.owner, step),
53 compile.major_only_filename.?,74 compile.major_only_filename.?,
54 compile.name_only_filename.?,75 compile.name_only_filename.?,
55 );76 );
56 }77 }
57}78}
5879
59fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {80fn getZigArgs(compile: *Compile, maker: *Maker, fuzz: bool) ![][]const u8 {
60 const step = &compile.step;81 const step = &compile.step;
61 const b = step.owner;82 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
64 var zig_args = std.array_list.Managed([]const u8).init(arena);86 var zig_args = std.array_list.Managed([]const u8).init(arena);
65 defer zig_args.deinit();87 defer zig_args.deinit();
6688
67 try zig_args.append(b.graph.zig_exe);89 try zig_args.append(graph.zig_exe);
6890
69 const cmd = switch (compile.kind) {91 const cmd = switch (compile.kind) {
70 .lib => "build-lib",92 .lib => "build-lib",
...@@ -78,7 +100,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {...@@ -78,7 +100,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
78 if (b.reference_trace) |some| {100 if (b.reference_trace) |some| {
79 try zig_args.append(try std.fmt.allocPrint(arena, "-freference-trace={d}", .{some}));101 try zig_args.append(try std.fmt.allocPrint(arena, "-freference-trace={d}", .{some}));
80 }102 }
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
83 try addFlag(&zig_args, "llvm", compile.use_llvm);105 try addFlag(&zig_args, "llvm", compile.use_llvm);
84 try addFlag(&zig_args, "lld", compile.use_lld);106 try addFlag(&zig_args, "lld", compile.use_lld);
...@@ -118,7 +140,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {...@@ -118,7 +140,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
118 // module, along with any arguments that need to be passed to the140 // module, along with any arguments that need to be passed to the
119 // compiler for each module individually.141 // compiler for each module individually.
120 var seen_system_libs: std.StringHashMapUnmanaged([]const []const u8) = .empty;142 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
123 var prev_has_cflags = false;145 var prev_has_cflags = false;
124 var prev_has_rcflags = false;146 var prev_has_rcflags = false;
...@@ -130,7 +152,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {...@@ -130,7 +152,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
130152
131 // Fully recursive iteration including dynamic libraries to detect153 // Fully recursive iteration including dynamic libraries to detect
132 // libc and libc++ linkage.154 // libc and libc++ linkage.
133 for (compile.getCompileDependencies(true)) |some_compile| {155 for (getCompileDependencies(true)) |some_compile| {
134 for (some_compile.root_module.getGraph().modules) |mod| {156 for (some_compile.root_module.getGraph().modules) |mod| {
135 if (mod.link_libc == true) compile.is_linking_libc = true;157 if (mod.link_libc == true) compile.is_linking_libc = true;
136 if (mod.link_libcpp == true) compile.is_linking_libcpp = true;158 if (mod.link_libcpp == true) compile.is_linking_libcpp = true;
...@@ -141,7 +163,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {...@@ -141,7 +163,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
141163
142 // For this loop, don't chase dynamic libraries because their link164 // For this loop, don't chase dynamic libraries because their link
143 // objects are already linked.165 // objects are already linked.
144 for (compile.getCompileDependencies(false)) |dep_compile| {166 for (getCompileDependencies(false)) |dep_compile| {
145 for (dep_compile.root_module.getGraph().modules) |mod| {167 for (dep_compile.root_module.getGraph().modules) |mod| {
146 // While walking transitive dependencies, if a given link object is168 // While walking transitive dependencies, if a given link object is
147 // already included in a library, it should not redundantly be169 // already included in a library, it should not redundantly be
...@@ -207,7 +229,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {...@@ -207,7 +229,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
207 switch (system_lib.use_pkg_config) {229 switch (system_lib.use_pkg_config) {
208 .no => try zig_args.append(b.fmt("{s}{s}", .{ prefix, system_lib.name })),230 .no => try zig_args.append(b.fmt("{s}{s}", .{ prefix, system_lib.name })),
209 .yes, .force => {231 .yes, .force => {
210 if (compile.runPkgConfig(system_lib.name)) |result| {232 if (compile.runPkgConfig(maker, system_lib.name)) |result| {
211 try zig_args.appendSlice(result.cflags);233 try zig_args.appendSlice(result.cflags);
212 try zig_args.appendSlice(result.libs);234 try zig_args.appendSlice(result.libs);
213 try seen_system_libs.put(arena, system_lib.name, result.cflags);235 try seen_system_libs.put(arena, system_lib.name, result.cflags);
...@@ -227,7 +249,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {...@@ -227,7 +249,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
227 }));249 }));
228 },250 },
229 .force => {251 .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});
231 },253 },
232 .no => unreachable,254 .no => unreachable,
233 },255 },
...@@ -272,7 +294,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {...@@ -272,7 +294,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
272 if (other.linkage == .dynamic and294 if (other.linkage == .dynamic and
273 compile.rootModuleTarget().os.tag != .windows)295 compile.rootModuleTarget().os.tag != .windows)
274 {296 {
275 if (fs.path.dirname(full_path_lib)) |dirname| {297 if (Dir.path.dirname(full_path_lib)) |dirname| {
276 try zig_args.append("-rpath");298 try zig_args.append("-rpath");
277 try zig_args.append(dirname);299 try zig_args.append(dirname);
278 }300 }
...@@ -479,7 +501,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {...@@ -479,7 +501,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
479 if (b.verbose_link or compile.verbose_link) try zig_args.append("--verbose-link");501 if (b.verbose_link or compile.verbose_link) try zig_args.append("--verbose-link");
480 if (b.verbose_cc or compile.verbose_cc) try zig_args.append("--verbose-cc");502 if (b.verbose_cc or compile.verbose_cc) try zig_args.append("--verbose-cc");
481 if (b.verbose_llvm_cpu_features) try zig_args.append("--verbose-llvm-cpu-features");503 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
484 if (compile.generated_asm != null) try zig_args.append("-femit-asm");506 if (compile.generated_asm != null) try zig_args.append("-femit-asm");
485 if (compile.generated_bin == null) try zig_args.append("-fno-emit-bin");507 if (compile.generated_bin == null) try zig_args.append("-fno-emit-bin");
...@@ -555,9 +577,9 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {...@@ -555,9 +577,9 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
555 try zig_args.append(b.cache_root.path orelse ".");577 try zig_args.append(b.cache_root.path orelse ".");
556578
557 try zig_args.append("--global-cache-dir");579 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|
561 try zig_args.append(b.fmt("--debug-rt={t}", .{mode}));583 try zig_args.append(b.fmt("--debug-rt={t}", .{mode}));
562584
563 try zig_args.append("--name");585 try zig_args.append("--name");
...@@ -681,7 +703,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {...@@ -681,7 +703,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
681703
682 // -I and -L arguments that appear after the last --mod argument apply to all modules.704 // -I and -L arguments that appear after the last --mod argument apply to all modules.
683 const cwd: Io.Dir = .cwd();705 const cwd: Io.Dir = .cwd();
684 const io = b.graph.io;706 const io = graph.io;
685707
686 for (b.search_prefixes.items) |search_prefix| {708 for (b.search_prefixes.items) |search_prefix| {
687 var prefix_dir = cwd.openDir(io, search_prefix, .{}) catch |err| {709 var prefix_dir = cwd.openDir(io, search_prefix, .{}) catch |err| {
...@@ -734,8 +756,8 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {...@@ -734,8 +756,8 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
734756
735 const opt_zig_lib_dir = if (compile.zig_lib_dir) |dir|757 const opt_zig_lib_dir = if (compile.zig_lib_dir) |dir|
736 dir.getPath2(b, step)758 dir.getPath2(b, step)
737 else if (b.graph.zig_lib_directory.path) |_|759 else if (graph.zig_lib_directory.path) |_|
738 b.fmt("{f}", .{b.graph.zig_lib_directory})760 b.fmt("{f}", .{graph.zig_lib_directory})
739 else761 else
740 null;762 null;
741763
...@@ -769,7 +791,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {...@@ -769,7 +791,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
769 "--error-limit", b.fmt("{d}", .{err_limit}),791 "--error-limit", b.fmt("{d}", .{err_limit}),
770 });792 });
771793
772 try addFlag(&zig_args, "incremental", b.graph.incremental);794 try addFlag(&zig_args, "incremental", graph.incremental);
773795
774 try zig_args.append("--listen=-");796 try zig_args.append("--listen=-");
775797
...@@ -814,7 +836,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {...@@ -814,7 +836,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
814 var args_hex_hash: [Sha256.digest_length * 2]u8 = undefined;836 var args_hex_hash: [Sha256.digest_length * 2]u8 = undefined;
815 _ = try std.fmt.bufPrint(&args_hex_hash, "{x}", .{&args_hash});837 _ = 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;
818 if (b.cache_root.handle.access(io, args_file, .{})) |_| {840 if (b.cache_root.handle.access(io, args_file, .{})) |_| {
819 // The args file is already present from a previous run.841 // The args file is already present from a previous run.
820 } else |err| switch (err) {842 } else |err| switch (err) {
...@@ -859,7 +881,9 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {...@@ -859,7 +881,9 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
859 return try zig_args.toOwnedSlice();881 return try zig_args.toOwnedSlice();
860}882}
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
863 c.step.result_error_msgs.clearRetainingCapacity();887 c.step.result_error_msgs.clearRetainingCapacity();
864 c.step.result_stderr = "";888 c.step.result_stderr = "";
865889
...@@ -871,21 +895,23 @@ pub fn rebuildInFuzzMode(c: *Compile, gpa: Allocator, progress_node: std.Progres...@@ -871,21 +895,23 @@ pub fn rebuildInFuzzMode(c: *Compile, gpa: Allocator, progress_node: std.Progres
871 c.step.result_failed_command = null;895 c.step.result_failed_command = null;
872 }896 }
873897
874 const zig_args = try getZigArgs(c, true);898 const zig_args = try getZigArgs(c, maker, true);
875 const maybe_output_bin_path = try c.step.evalZigProcess(zig_args, progress_node, false, null, gpa);899 const maybe_output_bin_path = try c.step.evalZigProcess(zig_args, progress_node, false, null, gpa);
876 return maybe_output_bin_path.?;900 return maybe_output_bin_path.?;
877}901}
878902
879pub fn doAtomicSymLinks(903pub fn doAtomicSymLinks(
880 step: *Step,904 step: *Step,
905 maker: *Maker,
881 output_path: []const u8,906 output_path: []const u8,
882 filename_major_only: []const u8,907 filename_major_only: []const u8,
883 filename_name_only: []const u8,908 filename_name_only: []const u8,
884) !void {909) !void {
885 const b = step.owner;910 const b = step.owner;
886 const io = b.graph.io;911 const graph = maker.graph;
887 const out_dir = fs.path.dirname(output_path) orelse ".";912 const io = graph.io;
888 const out_basename = fs.path.basename(output_path);913 const out_dir = Dir.path.dirname(output_path) orelse ".";
914 const out_basename = Dir.path.basename(output_path);
889 // sym link for libfoo.so.1 to libfoo.so.1.2.3915 // sym link for libfoo.so.1 to libfoo.so.1.2.3
890 const major_only_path = b.pathJoin(&.{ out_dir, filename_major_only });916 const major_only_path = b.pathJoin(&.{ out_dir, filename_major_only });
891 const cwd: Io.Dir = .cwd();917 const cwd: Io.Dir = .cwd();
...@@ -903,10 +929,24 @@ pub fn doAtomicSymLinks(...@@ -903,10 +929,24 @@ pub fn doAtomicSymLinks(
903 };929 };
904}930}
905931
906fn execPkgConfigList(b: *std.Build, out_code: *u8) (PkgConfigError || RunError)![]const PkgConfigPkg {932pub const PkgConfigError = error{
907 const pkg_config_exe = b.graph.environ_map.get("PKG_CONFIG") orelse "pkg-config";933 PkgConfigCrashed,
908 const stdout = try b.runAllowFail(&[_][]const u8{ pkg_config_exe, "--list-all" }, out_code, .ignore);934 PkgConfigFailed,
909 var list = std.array_list.Managed(PkgConfigPkg).init(b.allocator);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);
910 errdefer list.deinit();950 errdefer list.deinit();
911 var line_it = mem.tokenizeAny(u8, stdout, "\r\n");951 var line_it = mem.tokenizeAny(u8, stdout, "\r\n");
912 while (line_it.next()) |line| {952 while (line_it.next()) |line| {
...@@ -960,7 +1000,8 @@ const PkgConfigResult = struct {...@@ -960,7 +1000,8 @@ const PkgConfigResult = struct {
9601000
961/// Run pkg-config for the given library name and parse the output, returning the arguments1001/// Run pkg-config for the given library name and parse the output, returning the arguments
962/// that should be passed to zig to link the given library.1002/// 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;
964 const wl_rpath_prefix = "-Wl,-rpath,";1005 const wl_rpath_prefix = "-Wl,-rpath,";
9651006
966 const b = compile.step.owner;1007 const b = compile.step.owner;
...@@ -1013,7 +1054,7 @@ fn runPkgConfig(compile: *Compile, lib_name: []const u8) !PkgConfigResult {...@@ -1013,7 +1054,7 @@ fn runPkgConfig(compile: *Compile, lib_name: []const u8) !PkgConfigResult {
1013 };1054 };
10141055
1015 var code: u8 = undefined;1056 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";
1017 const stdout = if (b.runAllowFail(&[_][]const u8{1058 const stdout = if (b.runAllowFail(&[_][]const u8{
1018 pkg_config_exe,1059 pkg_config_exe,
1019 pkg_name,1060 pkg_name,
...@@ -1198,3 +1239,43 @@ fn moduleNeedsCliArg(mod: *const Module) bool {...@@ -1198,3 +1239,43 @@ fn moduleNeedsCliArg(mod: *const Module) bool {
1198 } else false;1239 } else false;
1199}1240}
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();...@@ -3,38 +3,46 @@ const Run = @This();
3const builtin = @import("builtin");3const builtin = @import("builtin");
44
5const std = @import("std");5const std = @import("std");
6const Io = std.Io;6const Cache = std.Build.Cache;
7const Configuration = std.Build.Configuration;
7const Dir = std.Io.Dir;8const Dir = std.Io.Dir;
8const mem = std.mem;
9const process = std.process;
10const EnvMap = std.process.Environ.Map;9const EnvMap = std.process.Environ.Map;
11const assert = std.debug.assert;10const Io = std.Io;
12const Cache = std.Build.Cache;
13const Path = std.Build.Cache.Path;11const Path = std.Build.Cache.Path;
12const assert = std.debug.assert;
13const mem = std.mem;
14const process = std.process;
1415
15const Step = @import("../Step.zig");16const Step = @import("../Step.zig");
17const Maker = @import("../../Maker.zig");
1618
17/// If this is a Zig unit test binary, this tracks the names of the unit19/// If this is a Zig unit test binary, this tracks the names of the unit
18/// tests that are also fuzz tests. Indexes cannot be used as they may20/// tests that are also fuzz tests. Indexes cannot be used as they may
19/// change between reruns.21/// change between reruns.
20fuzz_tests: std.ArrayList([]const u8),22fuzz_tests: std.ArrayList([]const u8) = .empty,
21cached_test_metadata: ?CachedTestMetadata = null,23cached_test_metadata: ?CachedTestMetadata = null,
2224
23/// Populated during the fuzz phase if this run step corresponds to a unit test25/// Populated during the fuzz phase if this run step corresponds to a unit test
24/// executable that contains fuzz tests.26/// executable that contains fuzz tests.
25rebuilt_executable: ?Path,27rebuilt_executable: ?Path = null,
2628
27fn make(step: *Step, options: Step.MakeOptions) !void {29pub fn make(
28 const b = step.owner;30 run: *Run,
29 const io = b.graph.io;31 step_index: Configuration.Step.Index,
30 const arena = b.allocator;32 maker: *Maker,
31 const run: *Run = @fieldParentPtr("step", step);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
32 const has_side_effects = run.hasSideEffects();40 const has_side_effects = run.hasSideEffects();
3341
34 var argv_list = std.array_list.Managed([]const u8).init(arena);42 var argv_list = std.array_list.Managed([]const u8).init(arena);
35 var output_placeholders = std.array_list.Managed(IndexedOutput).init(arena);43 var output_placeholders = std.array_list.Managed(IndexedOutput).init(arena);
3644
37 var man = b.graph.cache.obtain();45 var man = graph.cache.obtain();
38 defer man.deinit();46 defer man.deinit();
3947
40 if (run.environ_map) |environ_map| {48 if (run.environ_map) |environ_map| {
...@@ -54,19 +62,19 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -54,19 +62,19 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
54 man.hash.addBytes(bytes);62 man.hash.addBytes(bytes);
55 },63 },
56 .lazy_path => |file| {64 .lazy_path => |file| {
57 const file_path = file.lazy_path.getPath3(b, step);65 const file_path = file.lazy_path.getPath3(graph, step);
58 try argv_list.append(b.fmt("{s}{s}", .{ file.prefix, run.convertPathArg(file_path) }));66 try argv_list.append(graph.fmt("{s}{s}", .{ file.prefix, run.convertPathArg(maker, file_path) }));
59 man.hash.addBytes(file.prefix);67 man.hash.addBytes(file.prefix);
60 _ = try man.addFilePath(file_path, null);68 _ = try man.addFilePath(file_path, null);
61 },69 },
62 .decorated_directory => |dd| {70 .decorated_directory => |dd| {
63 const file_path = dd.lazy_path.getPath3(b, step);71 const file_path = dd.lazy_path.getPath3(graph, step);
64 const resolved_arg = b.fmt("{s}{s}{s}", .{ dd.prefix, run.convertPathArg(file_path), dd.suffix });72 const resolved_arg = graph.fmt("{s}{s}{s}", .{ dd.prefix, run.convertPathArg(maker, file_path), dd.suffix });
65 try argv_list.append(resolved_arg);73 try argv_list.append(resolved_arg);
66 man.hash.addBytes(resolved_arg);74 man.hash.addBytes(resolved_arg);
67 },75 },
68 .file_content => |file_plp| {76 .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
71 var result: std.Io.Writer.Allocating = .init(arena);79 var result: std.Io.Writer.Allocating = .init(arena);
72 errdefer result.deinit();80 errdefer result.deinit();
...@@ -99,13 +107,13 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -99,13 +107,13 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
99107
100 if (artifact.rootModuleTarget().os.tag == .windows) {108 if (artifact.rootModuleTarget().os.tag == .windows) {
101 // On Windows we don't have rpaths so we have to add .dll search paths to PATH109 // On Windows we don't have rpaths so we have to add .dll search paths to PATH
102 run.addPathForDynLibs(artifact);110 addPathForDynLibs(artifact);
103 }111 }
104 const file_path = artifact.installed_path orelse artifact.generated_bin.?.path.?;112 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}", .{
107 pa.prefix,115 pa.prefix,
108 run.convertPathArg(.{ .root_dir = .cwd(), .sub_path = file_path }),116 run.convertPathArg(maker, .{ .root_dir = .cwd(), .sub_path = file_path }),
109 }));117 }));
110118
111 _ = try man.addFile(file_path, null);119 _ = try man.addFile(file_path, null);
...@@ -131,7 +139,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -131,7 +139,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
131 man.hash.addBytes(bytes);139 man.hash.addBytes(bytes);
132 },140 },
133 .lazy_path => |lazy_path| {141 .lazy_path => |lazy_path| {
134 const file_path = lazy_path.getPath2(b, step);142 const file_path = lazy_path.getPath2(graph, step);
135 _ = try man.addFile(file_path, null);143 _ = try man.addFile(file_path, null);
136 },144 },
137 .none => {},145 .none => {},
...@@ -147,14 +155,15 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -147,14 +155,15 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
147 man.hash.add(captured.trim_whitespace);155 man.hash.add(captured.trim_whitespace);
148 }156 }
149157
150 hashStdIo(&man.hash, run.stdio);158 std.log.err("TODO hashStdIo", .{});
159 //hashStdIo(&man.hash, run.stdio);
151160
152 for (run.file_inputs.items) |lazy_path| {161 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);
154 }163 }
155164
156 if (run.cwd) |cwd| {165 if (run.cwd) |cwd| {
157 const cwd_path = cwd.getPath3(b, step);166 const cwd_path = cwd.getPath3(graph, step);
158 _ = man.hash.addBytes(try cwd_path.toString(arena));167 _ = man.hash.addBytes(try cwd_path.toString(arena));
159 }168 }
160169
...@@ -165,9 +174,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -165,9 +174,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
165 try populateGeneratedPaths(174 try populateGeneratedPaths(
166 arena,175 arena,
167 output_placeholders.items,176 output_placeholders.items,
168 run.captured_stdout,177 graph.cache_root,
169 run.captured_stderr,
170 b.cache_root,
171 &digest,178 &digest,
172 );179 );
173180
...@@ -185,36 +192,34 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -185,36 +192,34 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
185 try populateGeneratedPaths(192 try populateGeneratedPaths(
186 arena,193 arena,
187 output_placeholders.items,194 output_placeholders.items,
188 run.captured_stdout,195 graph.cache_root,
189 run.captured_stderr,
190 b.cache_root,
191 &digest,196 &digest,
192 );197 );
193198
194 const output_dir_path = "o" ++ Dir.path.sep_str ++ &digest;199 const output_dir_path = "o" ++ Dir.path.sep_str ++ &digest;
195 for (output_placeholders.items) |placeholder| {200 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 });
197 const output_sub_dir_path = switch (placeholder.tag) {202 const output_sub_dir_path = switch (placeholder.tag) {
198 .output_file => Dir.path.dirname(output_sub_path).?,203 .output_file => Dir.path.dirname(output_sub_path).?,
199 .output_directory => output_sub_path,204 .output_directory => output_sub_path,
200 else => unreachable,205 else => unreachable,
201 };206 };
202 b.cache_root.handle.createDirPath(io, output_sub_dir_path) catch |err| {207 graph.cache_root.handle.createDirPath(io, output_sub_dir_path) catch |err| {
203 return step.fail("unable to make path '{f}{s}': {s}", .{208 return step.fail("unable to make path '{f}{s}': {t}", .{
204 b.cache_root, output_sub_dir_path, @errorName(err),209 graph.cache_root, output_sub_dir_path, err,
205 });210 });
206 };211 };
207 const arg_output_path = run.convertPathArg(.{212 const arg_output_path = run.convertPathArg(maker, .{
208 .root_dir = .cwd(),213 .root_dir = .cwd(),
209 .sub_path = placeholder.output.generated_file.getPath(),214 .sub_path = placeholder.output.generated_file.getPath(),
210 });215 });
211 argv_list.items[placeholder.index] = if (placeholder.output.prefix.len == 0)216 argv_list.items[placeholder.index] = if (placeholder.output.prefix.len == 0)
212 arg_output_path217 arg_output_path
213 else218 else
214 b.fmt("{s}{s}", .{ placeholder.output.prefix, arg_output_path });219 graph.fmt("{s}{s}", .{ placeholder.output.prefix, arg_output_path });
215 }220 }
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);
218 if (!has_side_effects) try step.writeManifestAndWatch(&man);223 if (!has_side_effects) try step.writeManifestAndWatch(&man);
219 return;224 return;
220 };225 };
...@@ -226,32 +231,32 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -226,32 +231,32 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
226231
227 for (output_placeholders.items) |placeholder| {232 for (output_placeholders.items) |placeholder| {
228 const output_components = .{ tmp_dir_path, placeholder.output.basename };233 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);
230 const output_sub_dir_path = switch (placeholder.tag) {235 const output_sub_dir_path = switch (placeholder.tag) {
231 .output_file => Dir.path.dirname(output_sub_path).?,236 .output_file => Dir.path.dirname(output_sub_path).?,
232 .output_directory => output_sub_path,237 .output_directory => output_sub_path,
233 else => unreachable,238 else => unreachable,
234 };239 };
235 b.cache_root.handle.createDirPath(io, output_sub_dir_path) catch |err| {240 graph.cache_root.handle.createDirPath(io, output_sub_dir_path) catch |err| {
236 return step.fail("unable to make path '{f}{s}': {s}", .{241 return step.fail("unable to make path '{f}{s}': {t}", .{
237 b.cache_root, output_sub_dir_path, @errorName(err),242 graph.cache_root, output_sub_dir_path, err,
238 });243 });
239 };244 };
240 const raw_output_path: Cache.Path = .{245 const raw_output_path: Path = .{
241 .root_dir = b.cache_root,246 .root_dir = graph.cache_root,
242 .sub_path = b.pathJoin(&output_components),247 .sub_path = graph.pathJoin(&output_components),
243 };248 };
244 placeholder.output.generated_file.path = raw_output_path.toString(b.graph.arena) catch @panic("OOM");249 placeholder.output.generated_file.path = raw_output_path.toString(arena) catch @panic("OOM");
245 argv_list.items[placeholder.index] = b.fmt("{s}{s}", .{250 argv_list.items[placeholder.index] = graph.fmt("{s}{s}", .{
246 placeholder.output.prefix,251 placeholder.output.prefix,
247 run.convertPathArg(raw_output_path),252 run.convertPathArg(maker, raw_output_path),
248 });253 });
249 }254 }
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
253 const dep_file_dir = Dir.cwd();258 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);
255 if (has_side_effects)260 if (has_side_effects)
256 try man.addDepFile(dep_file_dir, dep_file_basename)261 try man.addDepFile(dep_file_dir, dep_file_basename)
257 else262 else
...@@ -269,21 +274,21 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -269,21 +274,21 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
269 if (any_output) {274 if (any_output) {
270 const o_sub_path = "o" ++ Dir.path.sep_str ++ &digest;275 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) {
273 Dir.RenameError.DirNotEmpty => {278 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| {
275 return step.fail("unable to remove dir '{f}'{s}: {t}", .{280 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,
277 });282 });
278 };283 };
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| {
280 return step.fail("unable to rename dir '{f}{s}' to '{f}{s}': {t}", .{285 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,
282 });287 });
283 };288 };
284 },289 },
285 else => return step.fail("unable to rename dir '{f}{s}' to '{f}{s}': {t}", .{290 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,
287 }),292 }),
288 };293 };
289 }294 }
...@@ -293,9 +298,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -293,9 +298,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
293 try populateGeneratedPaths(298 try populateGeneratedPaths(
294 arena,299 arena,
295 output_placeholders.items,300 output_placeholders.items,
296 run.captured_stdout,301 graph.cache_root,
297 run.captured_stderr,
298 b.cache_root,
299 &digest,302 &digest,
300 );303 );
301}304}
...@@ -347,7 +350,6 @@ fn waitZigTest(...@@ -347,7 +350,6 @@ fn waitZigTest(
347 // start and it acknowledging the test starting, we terminate the child and raise an error. This350 // start and it acknowledging the test starting, we terminate the child and raise an error. This
348 // *should* never happen, but could in theory be caused by some very unlucky IB in a test.351 // *should* never happen, but could in theory be caused by some very unlucky IB in a test.
349 const response_timeout: Io.Clock.Duration = t: {352 const response_timeout: Io.Clock.Duration = t: {
350 if (fuzz_context != null) break :t null; // don't timeout fuzz tests
351 const ns = @max(options.unit_test_timeout_ns orelse 0, 60 * std.time.ns_per_s);353 const ns = @max(options.unit_test_timeout_ns orelse 0, 60 * std.time.ns_per_s);
352 break :t .{ .clock = .awake, .raw = .fromNanoseconds(ns) };354 break :t .{ .clock = .awake, .raw = .fromNanoseconds(ns) };
353 };355 };
...@@ -773,8 +775,8 @@ const FuzzTestRunner = struct {...@@ -773,8 +775,8 @@ const FuzzTestRunner = struct {
773 try f.pending_broadcasts.ensureUnusedCapacity(gpa, size);775 try f.pending_broadcasts.ensureUnusedCapacity(gpa, size);
774 f.pending_broadcasts.appendSliceAssumeCapacity(body);776 f.pending_broadcasts.appendSliceAssumeCapacity(body);
775 f.pending_broadcasts.appendSliceAssumeCapacity(@ptrCast(&footer));777 f.pending_broadcasts.appendSliceAssumeCapacity(@ptrCast(&footer));
776 }778 }
777 },779 },
778 else => {}, // ignore other messages780 else => {}, // ignore other messages
779 }781 }
780782
...@@ -863,7 +865,7 @@ const FuzzTestRunner = struct {...@@ -863,7 +865,7 @@ const FuzzTestRunner = struct {
863 if (f.coverage_id == null) return;865 if (f.coverage_id == null) return;
864866
865 // Search for the input file corresponding to the instance867 // Search for the input file corresponding to the instance
866 const InputHeader = Build.abi.fuzz.MmapInputHeader;868 const InputHeader = std.Build.abi.fuzz.MmapInputHeader;
867 var in_r_buf: [@sizeOf(InputHeader)]u8 = undefined;869 var in_r_buf: [@sizeOf(InputHeader)]u8 = undefined;
868 var in_r: Io.File.Reader = undefined;870 var in_r: Io.File.Reader = undefined;
869 var in_f: Io.File = undefined;871 var in_f: Io.File = undefined;
...@@ -1299,11 +1301,11 @@ fn sendRunFuzzTestMessage(...@@ -1299,11 +1301,11 @@ fn sendRunFuzzTestMessage(
1299 }1301 }
1300}1302}
13011303
1302fn evalGeneric(run: *Run, spawn_options: process.SpawnOptions) !EvalGenericResult {1304fn evalGeneric(run: *Run, maker: *Maker, spawn_options: process.SpawnOptions) !EvalGenericResult {
1303 const b = run.step.owner;1305 const graph = maker.graph;
1304 const io = b.graph.io;1306 const io = graph.io;
1305 const arena = b.allocator;1307 const arena = graph.allocator; // TODO don't leak into the process arena
1306 const gpa = b.allocator;1308 const gpa = maker.gpa;
13071309
1308 var child = try process.spawn(io, spawn_options);1310 var child = try process.spawn(io, spawn_options);
1309 defer child.kill(io);1311 defer child.kill(io);
...@@ -1317,7 +1319,7 @@ fn evalGeneric(run: *Run, spawn_options: process.SpawnOptions) !EvalGenericResul...@@ -1317,7 +1319,7 @@ fn evalGeneric(run: *Run, spawn_options: process.SpawnOptions) !EvalGenericResul
1317 child.stdin = null;1319 child.stdin = null;
1318 },1320 },
1319 .lazy_path => |lazy_path| {1321 .lazy_path => |lazy_path| {
1320 const path = lazy_path.getPath3(b, &run.step);1322 const path = lazy_path.getPath3(graph, &run.step);
1321 const file = path.root_dir.handle.openFile(io, path.subPathOrDot(), .{}) catch |err| {1323 const file = path.root_dir.handle.openFile(io, path.subPathOrDot(), .{}) catch |err| {
1322 return run.step.fail("unable to open stdin file: {t}", .{err});1324 return run.step.fail("unable to open stdin file: {t}", .{err});
1323 };1325 };
...@@ -1417,18 +1419,22 @@ fn evalGeneric(run: *Run, spawn_options: process.SpawnOptions) !EvalGenericResul...@@ -1417,18 +1419,22 @@ fn evalGeneric(run: *Run, spawn_options: process.SpawnOptions) !EvalGenericResul
14171419
1418const IndexedOutput = struct {1420const IndexedOutput = struct {
1419 index: usize,1421 index: usize,
1420 tag: @typeInfo(Arg).@"union".tag_type.?,1422 tag: Configuration.Step.Run.Arg.Tag,
1421 output: *Output,1423 output: *Output,
1422};1424};
14231425
1426const Output = void; // TODO
1427
1424pub fn rerunInFuzzMode(1428pub fn rerunInFuzzMode(
1425 run: *Run,1429 run: *Run,
1426 fuzz: *std.Build.Fuzz,1430 fuzz: *std.Build.Fuzz,
1427 prog_node: std.Progress.Node,1431 prog_node: std.Progress.Node,
1428) !void {1432) !void {
1433 const maker = fuzz.maker;
1434 const graph = maker.graph;
1429 const step = &run.step;1435 const step = &run.step;
1430 const b = step.owner;1436 const b = step.owner;
1431 const io = b.graph.io;1437 const io = graph.io;
1432 const arena = b.allocator;1438 const arena = b.allocator;
1433 var argv_list: std.ArrayList([]const u8) = .empty;1439 var argv_list: std.ArrayList([]const u8) = .empty;
1434 for (run.argv.items) |arg| {1440 for (run.argv.items) |arg| {
...@@ -1438,11 +1444,11 @@ pub fn rerunInFuzzMode(...@@ -1438,11 +1444,11 @@ pub fn rerunInFuzzMode(
1438 },1444 },
1439 .lazy_path => |file| {1445 .lazy_path => |file| {
1440 const file_path = file.lazy_path.getPath3(b, step);1446 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) }));
1442 },1448 },
1443 .decorated_directory => |dd| {1449 .decorated_directory => |dd| {
1444 const file_path = dd.lazy_path.getPath3(b, step);1450 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 }));
1446 },1452 },
1447 .file_content => |file_plp| {1453 .file_content => |file_plp| {
1448 const file_path = file_plp.lazy_path.getPath3(b, step);1454 const file_path = file_plp.lazy_path.getPath3(b, step);
...@@ -1471,7 +1477,7 @@ pub fn rerunInFuzzMode(...@@ -1471,7 +1477,7 @@ pub fn rerunInFuzzMode(
1471 };1477 };
1472 try argv_list.append(arena, b.fmt("{s}{s}", .{1478 try argv_list.append(arena, b.fmt("{s}{s}", .{
1473 pa.prefix,1479 pa.prefix,
1474 run.convertPathArg(.{ .root_dir = .cwd(), .sub_path = file_path }),1480 run.convertPathArg(maker, .{ .root_dir = .cwd(), .sub_path = file_path }),
1475 }));1481 }));
1476 },1482 },
1477 .output_file, .output_directory => unreachable,1483 .output_file, .output_directory => unreachable,
...@@ -1487,17 +1493,13 @@ pub fn rerunInFuzzMode(...@@ -1487,17 +1493,13 @@ pub fn rerunInFuzzMode(
1487 var rand_int: u64 = undefined;1493 var rand_int: u64 = undefined;
1488 io.random(@ptrCast(&rand_int));1494 io.random(@ptrCast(&rand_int));
1489 const tmp_dir_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int);1495 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, .{1496 try runCommand(run, maker, prog_node, 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 }, .{
1497 .fuzz = fuzz,1497 .fuzz = fuzz,
1498 });1498 });
1499}1499}
15001500
1501const CapturedStdIo = void; // TODO get it from Configuration
1502
1501fn populateGeneratedPaths(1503fn populateGeneratedPaths(
1502 arena: std.mem.Allocator,1504 arena: std.mem.Allocator,
1503 output_placeholders: []const IndexedOutput,1505 output_placeholders: []const IndexedOutput,
...@@ -1545,17 +1547,19 @@ const FuzzContext = struct {...@@ -1545,17 +1547,19 @@ const FuzzContext = struct {
15451547
1546fn runCommand(1548fn runCommand(
1547 run: *Run,1549 run: *Run,
1550 maker: *Maker,
1551 progress_node: std.Progress.Node,
1548 argv: []const []const u8,1552 argv: []const []const u8,
1549 has_side_effects: bool,1553 has_side_effects: bool,
1550 output_dir_path: []const u8,1554 output_dir_path: []const u8,
1551 options: Step.MakeOptions,
1552 fuzz_context: ?FuzzContext,1555 fuzz_context: ?FuzzContext,
1553) !void {1556) !void {
1557 const graph = maker.graph;
1558 const arena = graph.arena; // TODO don't leak into process arena
1559 const gpa = maker.gpa;
1554 const step = &run.step;1560 const step = &run.step;
1555 const b = step.owner;1561 const b = step.owner;
1556 const arena = b.allocator;1562 const io = graph.io;
1557 const gpa = options.gpa;
1558 const io = b.graph.io;
15591563
1560 const cwd: process.Child.Cwd = if (run.cwd) |lazy_cwd| .{ .path = lazy_cwd.getPath2(b, step) } else .inherit;1564 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(...@@ -1571,12 +1575,12 @@ fn runCommand(
1571 defer interp_argv.deinit();1575 defer interp_argv.deinit();
15721576
1573 var environ_map: EnvMap = env: {1577 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;
1575 break :env try orig.clone(gpa);1579 break :env try orig.clone(gpa);
1576 };1580 };
1577 defer environ_map.deinit();1581 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: {
1580 // InvalidExe: cpu arch mismatch1584 // InvalidExe: cpu arch mismatch
1581 // FileNotFound: can happen with a wrong dynamic linker path1585 // FileNotFound: can happen with a wrong dynamic linker path
1582 if (err == error.InvalidExe or err == error.FileNotFound) interpret: {1586 if (err == error.InvalidExe or err == error.FileNotFound) interpret: {
...@@ -1597,7 +1601,7 @@ fn runCommand(...@@ -1597,7 +1601,7 @@ fn runCommand(
1597 const need_cross_libc = exe.is_linking_libc and1601 const need_cross_libc = exe.is_linking_libc and
1598 (root_target.isGnuLibC() or (root_target.isMuslLibC() and exe.linkage == .dynamic));1602 (root_target.isGnuLibC() or (root_target.isMuslLibC() and exe.linkage == .dynamic));
1599 const other_target = exe.root_module.resolved_target.?.result;1603 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, .{
1601 .qemu_fixes_dl = need_cross_libc and b.libc_runtimes_dir != null,1605 .qemu_fixes_dl = need_cross_libc and b.libc_runtimes_dir != null,
1602 .link_libc = exe.is_linking_libc,1606 .link_libc = exe.is_linking_libc,
1603 })) {1607 })) {
...@@ -1669,7 +1673,7 @@ fn runCommand(...@@ -1669,7 +1673,7 @@ fn runCommand(
1669 .bad_dl => |foreign_dl| {1673 .bad_dl => |foreign_dl| {
1670 if (allow_skip) return error.MakeSkipped;1674 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
1674 return step.fail(1678 return step.fail(
1675 \\the host system is unable to execute binaries from the target1679 \\the host system is unable to execute binaries from the target
...@@ -1681,7 +1685,7 @@ fn runCommand(...@@ -1681,7 +1685,7 @@ fn runCommand(
1681 .bad_os_or_cpu => {1685 .bad_os_or_cpu => {
1682 if (allow_skip) return error.MakeSkipped;1686 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);
1685 const foreign_name = try root_target.zigTriple(b.allocator);1689 const foreign_name = try root_target.zigTriple(b.allocator);
16861690
1687 return step.fail("the host system ({s}) is unable to execute binaries from the target ({s})", .{1691 return step.fail("the host system ({s}) is unable to execute binaries from the target ({s})", .{
...@@ -1692,14 +1696,14 @@ fn runCommand(...@@ -1692,14 +1696,14 @@ fn runCommand(
16921696
1693 if (root_target.os.tag == .windows) {1697 if (root_target.os.tag == .windows) {
1694 // On Windows we don't have rpaths so we have to add .dll search paths to PATH1698 // On Windows we don't have rpaths so we have to add .dll search paths to PATH
1695 run.addPathForDynLibs(exe);1699 addPathForDynLibs(exe);
1696 }1700 }
16971701
1698 gpa.free(step.result_failed_command.?);1702 gpa.free(step.result_failed_command.?);
1699 step.result_failed_command = null;1703 step.result_failed_command = null;
1700 try Step.handleVerbose2(step.owner, cwd, run.environ_map, interp_argv.items);1704 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| {
1703 if (!run.failing_to_execute_foreign_is_an_error) return error.MakeSkipped;1707 if (!run.failing_to_execute_foreign_is_an_error) return error.MakeSkipped;
1704 if (e == error.MakeFailed) return error.MakeFailed; // error already reported1708 if (e == error.MakeFailed) return error.MakeFailed; // error already reported
1705 return step.fail("unable to spawn interpreter {s}: {t}", .{ interp_argv.items[0], e });1709 return step.fail("unable to spawn interpreter {s}: {t}", .{ interp_argv.items[0], e });
...@@ -1851,14 +1855,16 @@ const EvalGenericResult = struct {...@@ -1851,14 +1855,16 @@ const EvalGenericResult = struct {
18511855
1852fn spawnChildAndCollect(1856fn spawnChildAndCollect(
1853 run: *Run,1857 run: *Run,
1858 maker: *Maker,
1859 progress_node: std.Progress.Node,
1854 argv: []const []const u8,1860 argv: []const []const u8,
1855 environ_map: *EnvMap,1861 environ_map: *EnvMap,
1856 has_side_effects: bool,1862 has_side_effects: bool,
1857 options: Step.MakeOptions,
1858 fuzz_context: ?FuzzContext,1863 fuzz_context: ?FuzzContext,
1859) !?EvalGenericResult {1864) !?EvalGenericResult {
1860 const b = run.step.owner;1865 const b = run.step.owner;
1861 const graph = b.graph;1866 const graph = maker.graph;
1867 const gpa = maker.gpa;
1862 const io = graph.io;1868 const io = graph.io;
18631869
1864 if (fuzz_context != null) {1870 if (fuzz_context != null) {
...@@ -1870,7 +1876,7 @@ fn spawnChildAndCollect(...@@ -1870,7 +1876,7 @@ fn spawnChildAndCollect(
18701876
1871 // If an error occurs, it's caused by this command:1877 // If an error occurs, it's caused by this command:
1872 assert(run.step.result_failed_command == null);1878 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, .{
1874 .child = environ_map,1880 .child = environ_map,
1875 .parent = &graph.environ_map,1881 .parent = &graph.environ_map,
1876 }, argv);1882 }, argv);
...@@ -1905,7 +1911,7 @@ fn spawnChildAndCollect(...@@ -1905,7 +1911,7 @@ fn spawnChildAndCollect(
19051911
1906 if (run.stdio == .zig_test) {1912 if (run.stdio == .zig_test) {
1907 const started: Io.Clock.Timestamp = .now(io, .awake);1913 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) {
1909 error.Canceled => |e| return e,1915 error.Canceled => |e| return e,
1910 else => |e| e,1916 else => |e| e,
1911 };1917 };
...@@ -1915,7 +1921,7 @@ fn spawnChildAndCollect(...@@ -1915,7 +1921,7 @@ fn spawnChildAndCollect(
1915 } else {1921 } else {
1916 const inherit = spawn_options.stdout == .inherit or spawn_options.stderr == .inherit;1922 const inherit = spawn_options.stdout == .inherit or spawn_options.stderr == .inherit;
1917 if (!run.disable_zig_progress and !inherit) {1923 if (!run.disable_zig_progress and !inherit) {
1918 spawn_options.progress_node = options.progress_node;1924 spawn_options.progress_node = progress_node;
1919 }1925 }
1920 const terminal_mode: Io.Terminal.Mode = if (inherit) m: {1926 const terminal_mode: Io.Terminal.Mode = if (inherit) m: {
1921 const stderr = try io.lockStderr(&.{}, graph.stderr_mode);1927 const stderr = try io.lockStderr(&.{}, graph.stderr_mode);
...@@ -1925,7 +1931,7 @@ fn spawnChildAndCollect(...@@ -1925,7 +1931,7 @@ fn spawnChildAndCollect(
1925 try setColorEnvironmentVariables(run, environ_map, terminal_mode);1931 try setColorEnvironmentVariables(run, environ_map, terminal_mode);
19261932
1927 const started: Io.Clock.Timestamp = .now(io, .awake);1933 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) {
1929 error.Canceled => |e| return e,1935 error.Canceled => |e| return e,
1930 else => |e| e,1936 else => |e| e,
1931 };1937 };
...@@ -1934,11 +1940,11 @@ fn spawnChildAndCollect(...@@ -1934,11 +1940,11 @@ fn spawnChildAndCollect(
1934 }1940 }
1935}1941}
19361942
1937fn hashStdIo(hh: *Cache.HashHelper, stdio: StdIo) void {1943fn hashStdIo(hh: *Cache.HashHelper, stdio: void) void {
1938 switch (stdio) {1944 switch (stdio) {
1939 .infer_from_args, .inherit, .zig_test => {},1945 .infer_from_args, .inherit, .zig_test => {},
1940 .check => |checks| for (checks.items) |check| {1946 .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));
1942 switch (check) {1948 switch (check) {
1943 .expect_stderr_exact,1949 .expect_stderr_exact,
1944 .expect_stderr_match,1950 .expect_stderr_match,
...@@ -2010,7 +2016,7 @@ fn setColorEnvironmentVariables(run: *Run, environ_map: *EnvMap, terminal_mode:...@@ -2010,7 +2016,7 @@ fn setColorEnvironmentVariables(run: *Run, environ_map: *EnvMap, terminal_mode:
2010 }2016 }
2011}2017}
20122018
2013fn checksContainStdout(checks: []const StdIo.Check) bool {2019fn checksContainStdout(checks: []const @This().StdIo.Check) bool {
2014 for (checks) |check| switch (check) {2020 for (checks) |check| switch (check) {
2015 .expect_stderr_exact,2021 .expect_stderr_exact,
2016 .expect_stderr_match,2022 .expect_stderr_match,
...@@ -2024,7 +2030,7 @@ fn checksContainStdout(checks: []const StdIo.Check) bool {...@@ -2024,7 +2030,7 @@ fn checksContainStdout(checks: []const StdIo.Check) bool {
2024 return false;2030 return false;
2025}2031}
20262032
2027fn checksContainStderr(checks: []const StdIo.Check) bool {2033fn checksContainStderr(checks: []const @This().StdIo.Check) bool {
2028 for (checks) |check| switch (check) {2034 for (checks) |check| switch (check) {
2029 .expect_stdout_exact,2035 .expect_stdout_exact,
2030 .expect_stdout_match,2036 .expect_stdout_match,
...@@ -2063,9 +2069,9 @@ fn hasAnyOutputArgs(run: Run) bool {...@@ -2063,9 +2069,9 @@ fn hasAnyOutputArgs(run: Run) bool {
2063///2069///
2064/// Whenever a path is included in the argv of a child, it should be put through this function first2070/// Whenever a path is included in the argv of a child, it should be put through this function first
2065/// to make sure the child doesn't see paths relative to a cwd other than its own.2071/// 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 {
2067 const b = run.step.owner;2073 const b = run.step.owner;
2068 const graph = b.graph;2074 const graph = maker.graph;
2069 const arena = graph.arena;2075 const arena = graph.arena;
20702076
2071 const path_str = path.toString(arena) catch @panic("OOM");2077 const path_str = path.toString(arena) catch @panic("OOM");
...@@ -2091,40 +2097,43 @@ fn convertPathArg(run: *Run, path: Build.Cache.Path) []const u8 {...@@ -2091,40 +2097,43 @@ fn convertPathArg(run: *Run, path: Build.Cache.Path) []const u8 {
2091 return Dir.path.join(arena, &.{ ".", child_cwd_rel }) catch @panic("OOM");2097 return Dir.path.join(arena, &.{ ".", child_cwd_rel }) catch @panic("OOM");
2092}2098}
20932099
2094fn addPathForDynLibs(run: *Run, artifact: *Step.Compile) void {2100fn addPathForDynLibs(artifact: *Step.Compile) void {
2095 const b = run.step.owner;2101 if (true) @panic("TODO");
2096 const compiles = artifact.getCompileDependencies(true);2102 for (artifact.getCompileDependencies(true)) |compile| {
2097 for (compiles) |compile| {
2098 if (compile.root_module.resolved_target.?.result.os.tag == .windows and2103 if (compile.root_module.resolved_target.?.result.os.tag == .windows and
2099 compile.isDynamicLibrary())2104 compile.isDynamicLibrary())
2100 {2105 {
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)).?);
2102 }2108 }
2103 }2109 }
2104}2110}
21052111
2106fn failForeign(2112fn failForeign(
2107 run: *Run,2113 run: *Run,
2114 maker: *Maker,
2115 step_index: Configuration.Step.Index,
2108 suggested_flag: []const u8,2116 suggested_flag: []const u8,
2109 argv0: []const u8,2117 argv0: []const u8,
2110 exe: *Step.Compile,2118 exe: *Step.Compile,
2111) error{ MakeFailed, MakeSkipped, OutOfMemory } {2119) Step.ExtendedMakeError {
2120 const step = maker.stepByIndex(step_index);
2112 switch (run.stdio) {2121 switch (run.stdio) {
2113 .check, .zig_test => {2122 .check, .zig_test => {
2114 if (run.skip_foreign_checks)2123 if (run.skip_foreign_checks) return error.MakeSkipped;
2115 return error.MakeSkipped;
21162124
2117 const b = run.step.owner;2125 const graph = maker.graph;
2118 const host_name = try b.graph.host.result.zigTriple(b.allocator);2126 const process_arena = graph.arena; // TODO don't leak into process arena
2119 const foreign_name = try exe.rootModuleTarget().zigTriple(b.allocator);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(
2122 \\unable to spawn foreign binary '{s}' ({s}) on host system ({s})2131 \\unable to spawn foreign binary '{s}' ({s}) on host system ({s})
2123 \\ consider using {s} or enabling skip_foreign_checks in the Run step2132 \\ consider using {s} or enabling skip_foreign_checks in the Run step
2124 , .{ argv0, foreign_name, host_name, suggested_flag });2133 , .{ argv0, foreign_name, host_name, suggested_flag });
2125 },2134 },
2126 else => {2135 else => {
2127 return run.step.fail("unable to spawn foreign binary '{s}'", .{argv0});2136 return step.fail("unable to spawn foreign binary '{s}'", .{argv0});
2128 },2137 },
2129 }2138 }
2130}2139}
lib/compiler/Maker/Watch.zig+45-38
...@@ -10,6 +10,7 @@ const Configuration = std.Build.Configuration;...@@ -10,6 +10,7 @@ const Configuration = std.Build.Configuration;
1010
11const FsEvents = @import("Watch/FsEvents.zig");11const FsEvents = @import("Watch/FsEvents.zig");
12const Step = @import("Step.zig");12const Step = @import("Step.zig");
13const Maker = @import("../Maker.zig");
1314
14os: Os,15os: Os,
15/// The number to show as the number of directories being watched.16/// The number to show as the number of directories being watched.
...@@ -18,8 +19,7 @@ dir_count: usize,...@@ -18,8 +19,7 @@ dir_count: usize,
18// They are `undefined` on implementations which do not utilize then.19// They are `undefined` on implementations which do not utilize then.
19dir_table: DirTable,20dir_table: DirTable,
20generation: Generation,21generation: Generation,
21configuration: *const Configuration,22maker: *Maker,
22make_steps: []Step,
2323
24pub const have_impl = Os != void;24pub const have_impl = Os != void;
2525
...@@ -105,8 +105,7 @@ const Os = switch (builtin.os.tag) {...@@ -105,8 +105,7 @@ const Os = switch (builtin.os.tag) {
105 };105 };
106 };106 };
107107
108 fn init(cwd_path: []const u8, configuration: *const Configuration, make_steps: []Step) !Watch {108 fn init(maker: *Maker) !Watch {
109 _ = cwd_path;
110 return .{109 return .{
111 .dir_table = .{},110 .dir_table = .{},
112 .dir_count = 0,111 .dir_count = 0,
...@@ -118,8 +117,7 @@ const Os = switch (builtin.os.tag) {...@@ -118,8 +117,7 @@ const Os = switch (builtin.os.tag) {
118 else => {},117 else => {},
119 },118 },
120 .generation = 0,119 .generation = 0,
121 .make_steps = make_steps,120 .maker = maker,
122 .configuration = configuration,
123 };121 };
124 }122 }
125123
...@@ -136,7 +134,8 @@ const Os = switch (builtin.os.tag) {...@@ -136,7 +134,8 @@ const Os = switch (builtin.os.tag) {
136 return stack_lfh.clone(gpa);134 return stack_lfh.clone(gpa);
137 }135 }
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;
140 const fanotify = std.os.linux.fanotify;139 const fanotify = std.os.linux.fanotify;
141 const M = fanotify.event_metadata;140 const M = fanotify.event_metadata;
142 var events_buf: [256 + 4096]u8 = undefined;141 var events_buf: [256 + 4096]u8 = undefined;
...@@ -155,7 +154,7 @@ const Os = switch (builtin.os.tag) {...@@ -155,7 +154,7 @@ const Os = switch (builtin.os.tag) {
155 if (meta[0].mask.Q_OVERFLOW) {154 if (meta[0].mask.Q_OVERFLOW) {
156 any_dirty = true;155 any_dirty = true;
157 std.log.warn("file system watch queue overflowed; falling back to fstat", .{});156 std.log.warn("file system watch queue overflowed; falling back to fstat", .{});
158 markAllFilesDirty(w, gpa);157 markAllFilesDirty(w);
159 return true;158 return true;
160 }159 }
161 const fid: *align(1) fanotify.event_info_fid = @ptrCast(meta + 1);160 const fid: *align(1) fanotify.event_info_fid = @ptrCast(meta + 1);
...@@ -167,9 +166,9 @@ const Os = switch (builtin.os.tag) {...@@ -167,9 +166,9 @@ const Os = switch (builtin.os.tag) {
167 const lfh: FileHandle = .{ .handle = file_handle };166 const lfh: FileHandle = .{ .handle = file_handle };
168 if (w.os.handle_table.getPtr(lfh)) |value| {167 if (w.os.handle_table.getPtr(lfh)) |value| {
169 if (value.reaction_set.getPtr(".")) |glob_set|168 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);
171 if (value.reaction_set.getPtr(file_name)) |step_set|170 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);
173 }172 }
174 },173 },
175 else => |t| std.log.warn("unexpected fanotify event '{t}'", .{t}),174 else => |t| std.log.warn("unexpected fanotify event '{t}'", .{t}),
...@@ -179,9 +178,11 @@ const Os = switch (builtin.os.tag) {...@@ -179,9 +178,11 @@ const Os = switch (builtin.os.tag) {
179 }178 }
180179
181 fn update(w: *Watch, gpa: Allocator, steps: []const Configuration.Step.Index) !void {180 fn update(w: *Watch, gpa: Allocator, steps: []const Configuration.Step.Index) !void {
181 const maker = w.maker;
182
182 // Add missing marks and note persisted ones.183 // Add missing marks and note persisted ones.
183 for (steps) |step_index| {184 for (steps) |step_index| {
184 const step = &w.make_steps[@intFromEnum(step_index)];185 const step = maker.stepByIndex(step_index);
185 for (step.inputs.table.keys(), step.inputs.table.values()) |path, *files| {186 for (step.inputs.table.keys(), step.inputs.table.values()) |path, *files| {
186 const reaction_set = rs: {187 const reaction_set = rs: {
187 const gop = try w.dir_table.getOrPut(gpa, path);188 const gop = try w.dir_table.getOrPut(gpa, path);
...@@ -298,13 +299,12 @@ const Os = switch (builtin.os.tag) {...@@ -298,13 +299,12 @@ const Os = switch (builtin.os.tag) {
298 w.dir_count = w.dir_table.count();299 w.dir_count = w.dir_table.count();
299 }300 }
300301
301 fn wait(w: *Watch, gpa: Allocator, io: Io, timeout: Timeout) !WaitResult {302 fn wait(w: *Watch, timeout: Timeout) !WaitResult {
302 _ = io;
303 const events_len = try std.posix.poll(w.os.poll_fds.values(), timeout.to_i32_ms());303 const events_len = try std.posix.poll(w.os.poll_fds.values(), timeout.to_i32_ms());
304 if (events_len == 0)304 if (events_len == 0)
305 return .timeout;305 return .timeout;
306 for (w.os.poll_fds.values()) |poll_fd| {306 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))
308 return .dirty;308 return .dirty;
309 }309 }
310 return .clean;310 return .clean;
...@@ -515,12 +515,14 @@ const Os = switch (builtin.os.tag) {...@@ -515,12 +515,14 @@ const Os = switch (builtin.os.tag) {
515 return file_id;515 return file_id;
516 }516 }
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
519 var any_dirty = false;521 var any_dirty = false;
520 const bytes_returned = dir.iosb.Information;522 const bytes_returned = dir.iosb.Information;
521 if (bytes_returned == 0) {523 if (bytes_returned == 0) {
522 std.log.warn("file system watch queue overflowed; falling back to fstat", .{});524 std.log.warn("file system watch queue overflowed; falling back to fstat", .{});
523 markAllFilesDirty(w, gpa);525 markAllFilesDirty(w);
524 try dir.startListening(w);526 try dir.startListening(w);
525 return true;527 return true;
526 }528 }
...@@ -530,9 +532,9 @@ const Os = switch (builtin.os.tag) {...@@ -530,9 +532,9 @@ const Os = switch (builtin.os.tag) {
530 const notify: *windows.FILE.NOTIFY.INFORMATION = @ptrCast(@alignCast(&dir.buffer[offset]));532 const notify: *windows.FILE.NOTIFY.INFORMATION = @ptrCast(@alignCast(&dir.buffer[offset]));
531 const file_name = file_name_buf[0..std.unicode.wtf16LeToWtf8(&file_name_buf, notify.fileName())];533 const file_name = file_name_buf[0..std.unicode.wtf16LeToWtf8(&file_name_buf, notify.fileName())];
532 if (dir.reaction_set.getPtr(".")) |glob_set|534 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);
534 if (dir.reaction_set.getPtr(file_name)) |step_set|536 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);
536 if (notify.NextEntryOffset == 0)538 if (notify.NextEntryOffset == 0)
537 break;539 break;
538540
...@@ -619,14 +621,17 @@ const Os = switch (builtin.os.tag) {...@@ -619,14 +621,17 @@ const Os = switch (builtin.os.tag) {
619 w.dir_count = w.dir_table.count();621 w.dir_count = w.dir_table.count();
620 }622 }
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
623 for (0..2) |attempt| {628 for (0..2) |attempt| {
624 while (w.os.ready_dirs.popFirst()) |ready_node| {629 while (w.os.ready_dirs.popFirst()) |ready_node| {
625 const dir: *Directory = @fieldParentPtr("ready_node", ready_node);630 const dir: *Directory = @fieldParentPtr("ready_node", ready_node);
626 assert(dir.state == .ready);631 assert(dir.state == .ready);
627 dir.state = .idle;632 dir.state = .idle;
628 switch (dir.iosb.u.Status) {633 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,
630 .PENDING => unreachable,635 .PENDING => unreachable,
631 .CANCELLED => {},636 .CANCELLED => {},
632 else => |status| return windows.unexpectedStatus(status),637 else => |status| return windows.unexpectedStatus(status),
...@@ -810,25 +815,25 @@ const Os = switch (builtin.os.tag) {...@@ -810,25 +815,25 @@ const Os = switch (builtin.os.tag) {
810 w.dir_count = w.dir_table.count();815 w.dir_count = w.dir_table.count();
811 }816 }
812817
813 fn wait(w: *Watch, gpa: Allocator, io: Io, timeout: Timeout) !WaitResult {818 fn wait(w: *Watch, timeout: Timeout) !WaitResult {
814 _ = io;819 const maker = w.maker;
815 var timespec_buffer: posix.timespec = undefined;820 var timespec_buffer: posix.timespec = undefined;
816 var event_buffer: [100]posix.Kevent = undefined;821 var event_buffer: [100]posix.Kevent = undefined;
817 var n = try Io.Kqueue.kevent(w.os.kq_fd, &.{}, &event_buffer, timeout.toTimespec(&timespec_buffer));822 var n = try Io.Kqueue.kevent(w.os.kq_fd, &.{}, &event_buffer, timeout.toTimespec(&timespec_buffer));
818 if (n == 0) return .timeout;823 if (n == 0) return .timeout;
819 const reaction_sets = w.os.handles.items(.rs);824 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);
821 timespec_buffer = .{ .sec = 0, .nsec = 0 };826 timespec_buffer = .{ .sec = 0, .nsec = 0 };
822 while (n == event_buffer.len) {827 while (n == event_buffer.len) {
823 n = try Io.Kqueue.kevent(w.os.kq_fd, &.{}, &event_buffer, &timespec_buffer);828 n = try Io.Kqueue.kevent(w.os.kq_fd, &.{}, &event_buffer, &timespec_buffer);
824 if (n == 0) break;829 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);
826 }831 }
827 return if (any_dirty) .dirty else .clean;832 return if (any_dirty) .dirty else .clean;
828 }833 }
829834
830 fn markDirtySteps(835 fn markDirtySteps(
831 gpa: Allocator,836 maker: *Maker,
832 reaction_sets: []ReactionSet,837 reaction_sets: []ReactionSet,
833 events: []const std.c.Kevent,838 events: []const std.c.Kevent,
834 start_any_dirty: bool,839 start_any_dirty: bool,
...@@ -840,13 +845,13 @@ const Os = switch (builtin.os.tag) {...@@ -840,13 +845,13 @@ const Os = switch (builtin.os.tag) {
840 // If we knew the basename of the changed file, here we would845 // If we knew the basename of the changed file, here we would
841 // mark only the step set dirty, and possibly the glob set:846 // mark only the step set dirty, and possibly the glob set:
842 //if (reaction_set.getPtr(".")) |glob_set|847 //if (reaction_set.getPtr(".")) |glob_set|
843 // any_dirty = markStepSetDirty(gpa, glob_set, any_dirty);848 // any_dirty = markStepSetDirty(maker, glob_set, any_dirty);
844 //if (reaction_set.getPtr(file_name)) |step_set|849 //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);
846 // However we don't know the file name so just mark all the851 // However we don't know the file name so just mark all the
847 // sets dirty for this directory.852 // sets dirty for this directory.
848 for (reaction_set.values()) |*step_set| {853 for (reaction_set.values()) |*step_set| {
849 any_dirty = markStepSetDirty(gpa, step_set, any_dirty);854 any_dirty = markStepSetDirty(maker, step_set, any_dirty);
850 }855 }
851 }856 }
852 return any_dirty;857 return any_dirty;
...@@ -878,8 +883,8 @@ const Os = switch (builtin.os.tag) {...@@ -878,8 +883,8 @@ const Os = switch (builtin.os.tag) {
878 else => void,883 else => void,
879};884};
880885
881pub fn init(cwd_path: []const u8, configuration: *const Configuration, make_steps: []Step) !Watch {886pub fn init(maker: *Maker) !Watch {
882 return Os.init(cwd_path, configuration, make_steps);887 return Os.init(maker);
883}888}
884889
885pub const Match = struct {890pub const Match = struct {
...@@ -904,7 +909,9 @@ pub const Match = struct {...@@ -904,7 +909,9 @@ pub const Match = struct {
904 };909 };
905};910};
906911
907fn markAllFilesDirty(w: *Watch, gpa: Allocator) void {912fn markAllFilesDirty(w: *Watch) void {
913 const maker = w.maker;
914
908 for (switch (builtin.os.tag) {915 for (switch (builtin.os.tag) {
909 .windows => w.os.handle_table.keys(),916 .windows => w.os.handle_table.keys(),
910 else => w.os.handle_table.values(),917 else => w.os.handle_table.values(),
...@@ -915,18 +922,18 @@ fn markAllFilesDirty(w: *Watch, gpa: Allocator) void {...@@ -915,18 +922,18 @@ fn markAllFilesDirty(w: *Watch, gpa: Allocator) void {
915 };922 };
916 for (reaction_set.values()) |step_set| {923 for (reaction_set.values()) |step_set| {
917 for (step_set.keys()) |step_index| {924 for (step_set.keys()) |step_index| {
918 const step = &w.make_steps[@intFromEnum(step_index)];925 const step = maker.stepByIndex(step_index);
919 _ = step.invalidateResult(gpa);926 _ = maker.invalidateResult(step);
920 }927 }
921 }928 }
922 }929 }
923}930}
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 {
926 var this_any_dirty = false;933 var this_any_dirty = false;
927 for (step_set.keys()) |step_index| {934 for (step_set.keys()) |step_index| {
928 const step = &make_steps[@intFromEnum(step_index)];935 const step = maker.stepByIndex(step_index);
929 if (step.invalidateResult(gpa)) this_any_dirty = true;936 if (maker.invalidateResult(step)) this_any_dirty = true;
930 }937 }
931 return any_dirty or this_any_dirty;938 return any_dirty or this_any_dirty;
932}939}
...@@ -971,6 +978,6 @@ pub const WaitResult = enum {...@@ -971,6 +978,6 @@ pub const WaitResult = enum {
971 clean,978 clean,
972};979};
973980
974pub fn wait(w: *Watch, gpa: Allocator, io: Io, timeout: Timeout) !WaitResult {981pub fn wait(w: *Watch, timeout: Timeout) !WaitResult {
975 return Os.wait(w, gpa, io, timeout);982 return Os.wait(w, timeout);
976}983}
lib/compiler/Maker/WebServer.zig+94-71
...@@ -14,16 +14,14 @@ const log = std.log.scoped(.web_server);...@@ -14,16 +14,14 @@ const log = std.log.scoped(.web_server);
14const mem = std.mem;14const mem = std.mem;
15const net = std.Io.net;15const net = std.Io.net;
1616
17const Maker = @import("../Maker.zig");
17const Fuzz = @import("Fuzz.zig");18const Fuzz = @import("Fuzz.zig");
18const Graph = @import("Graph.zig");19const Graph = @import("Graph.zig");
19const Step = @import("Step.zig");20const Step = @import("Step.zig");
2021
21gpa: Allocator,22maker: *Maker,
22graph: *const Graph,
23all_steps: []const Configuration.Step.Index,
24listen_address: net.IpAddress,23listen_address: net.IpAddress,
25root_prog_node: std.Progress.Node,24root_prog_node: std.Progress.Node,
26watch: bool,
2725
28tcp_server: ?net.Server,26tcp_server: ?net.Server,
29serve_task: ?Io.Future(Io.Cancelable!void),27serve_task: ?Io.Future(Io.Cancelable!void),
...@@ -65,19 +63,16 @@ pub const base_clock: Io.Clock = .awake;...@@ -65,19 +63,16 @@ pub const base_clock: Io.Clock = .awake;
6563
66/// Thread-safe. Triggers updates to be sent to connected WebSocket clients; see `update_id`.64/// Thread-safe. Triggers updates to be sent to connected WebSocket clients; see `update_id`.
67pub fn notifyUpdate(ws: *WebServer) void {65pub fn notifyUpdate(ws: *WebServer) void {
66 const io = ws.maker.graph.io;
68 _ = ws.update_id.rmw(.Add, 1, .release);67 _ = 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);
70}69}
7170
72pub const Options = struct {71pub const Options = struct {
73 gpa: Allocator,72 maker: *Maker,
74 graph: *const Graph,
75 all_steps: []const Configuration.Step.Index,
76 root_prog_node: std.Progress.Node,73 root_prog_node: std.Progress.Node,
77 watch: bool,
78 listen_address: net.IpAddress,74 listen_address: net.IpAddress,
79 base_timestamp: Io.Clock.Timestamp,75 base_timestamp: Io.Clock.Timestamp,
80 configuration: *const Configuration,
81};76};
82pub fn init(opts: Options) WebServer {77pub fn init(opts: Options) WebServer {
83 // The upcoming `Io` interface should allow us to use `Io.async` and `Io.concurrent`78 // The upcoming `Io` interface should allow us to use `Io.async` and `Io.concurrent`
...@@ -85,10 +80,13 @@ pub fn init(opts: Options) WebServer {...@@ -85,10 +80,13 @@ pub fn init(opts: Options) WebServer {
85 comptime assert(!builtin.single_threaded);80 comptime assert(!builtin.single_threaded);
86 assert(opts.base_timestamp.clock == base_clock);81 assert(opts.base_timestamp.clock == base_clock);
8782
88 const all_steps = opts.all_steps;83 const maker = opts.maker;
89 const c = opts.configuration;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: {
92 var name_bytes: usize = 0;90 var name_bytes: usize = 0;
93 for (all_steps) |step_index| name_bytes += step_index.ptr(c).name.slice(c).len;91 for (all_steps) |step_index| name_bytes += step_index.ptr(c).name.slice(c).len;
94 break :len name_bytes + all_steps.len * 4;92 break :len name_bytes + all_steps.len * 4;
...@@ -105,25 +103,22 @@ pub fn init(opts: Options) WebServer {...@@ -105,25 +103,22 @@ pub fn init(opts: Options) WebServer {
105 assert(idx == step_names_trailing.len);103 assert(idx == step_names_trailing.len);
106 }104 }
107105
108 const step_status_bits = opts.gpa.alloc(106 const step_status_bits = gpa.alloc(
109 u8,107 u8,
110 std.math.divCeil(usize, all_steps.len, 4) catch unreachable,108 std.math.divCeil(usize, all_steps.len, 4) catch unreachable,
111 ) catch @panic("out of memory");109 ) catch @panic("out of memory");
112 @memset(step_status_bits, 0);110 @memset(step_status_bits, 0);
113111
114 const time_reports_len: usize = if (opts.graph.time_report) all_steps.len else 0;112 const time_reports_len: usize = if (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");113 const time_report_msgs = 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");114 const time_report_update_times = gpa.alloc(i64, time_reports_len) catch @panic("out of memory");
117 @memset(time_report_msgs, &.{});115 @memset(time_report_msgs, &.{});
118 @memset(time_report_update_times, std.math.minInt(i64));116 @memset(time_report_update_times, std.math.minInt(i64));
119117
120 return .{118 return .{
121 .gpa = opts.gpa,119 .maker = maker,
122 .graph = opts.graph,
123 .all_steps = all_steps,
124 .listen_address = opts.listen_address,120 .listen_address = opts.listen_address,
125 .root_prog_node = opts.root_prog_node,121 .root_prog_node = opts.root_prog_node,
126 .watch = opts.watch,
127122
128 .tcp_server = null,123 .tcp_server = null,
129 .serve_task = null,124 .serve_task = null,
...@@ -148,8 +143,9 @@ pub fn init(opts: Options) WebServer {...@@ -148,8 +143,9 @@ pub fn init(opts: Options) WebServer {
148 };143 };
149}144}
150pub fn deinit(ws: *WebServer) void {145pub fn deinit(ws: *WebServer) void {
151 const gpa = ws.gpa;146 const maker = ws.maker;
152 const io = ws.graph.io;147 const gpa = maker.gpa;
148 const io = maker.graph.io;
153149
154 gpa.free(ws.step_names_trailing);150 gpa.free(ws.step_names_trailing);
155 gpa.free(ws.step_status_bits);151 gpa.free(ws.step_status_bits);
...@@ -170,7 +166,8 @@ pub fn deinit(ws: *WebServer) void {...@@ -170,7 +166,8 @@ pub fn deinit(ws: *WebServer) void {
170pub fn start(ws: *WebServer) error{AlreadyReported}!void {166pub fn start(ws: *WebServer) error{AlreadyReported}!void {
171 assert(ws.tcp_server == null);167 assert(ws.tcp_server == null);
172 assert(ws.serve_task == null);168 assert(ws.serve_task == null);
173 const io = ws.graph.io;169 const maker = ws.maker;
170 const io = maker.graph.io;
174171
175 ws.tcp_server = ws.listen_address.listen(io, .{ .reuse_address = true }) catch |err| {172 ws.tcp_server = ws.listen_address.listen(io, .{ .reuse_address = true }) catch |err| {
176 log.err("failed to listen to port {d}: {t}", .{ ws.listen_address.getPort(), err });173 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 {...@@ -189,9 +186,12 @@ pub fn start(ws: *WebServer) error{AlreadyReported}!void {
189 }186 }
190}187}
191fn serve(ws: *WebServer) Io.Cancelable!void {188fn serve(ws: *WebServer) Io.Cancelable!void {
192 const io = ws.graph.io;189 const maker = ws.maker;
190 const io = maker.graph.io;
191
193 var group: Io.Group = .init;192 var group: Io.Group = .init;
194 defer group.cancel(io);193 defer group.cancel(io);
194
195 while (true) {195 while (true) {
196 var stream = ws.tcp_server.?.accept(io) catch |err| switch (err) {196 var stream = ws.tcp_server.?.accept(io) catch |err| switch (err) {
197 error.Canceled => |e| return e,197 error.Canceled => |e| return e,
...@@ -223,8 +223,10 @@ pub fn updateStepStatus(...@@ -223,8 +223,10 @@ pub fn updateStepStatus(
223 step_index: Configuration.Step.Index,223 step_index: Configuration.Step.Index,
224 new_status: abi.StepUpdate.Status,224 new_status: abi.StepUpdate.Status,
225) void {225) void {
226 const maker = ws.maker;
227 const all_steps = maker.step_stack.keys();
226 // TODO don't do linear search, especially in a hot loop like this228 // 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| {
228 if (s == step_index) break @intCast(i);230 if (s == step_index) break @intCast(i);
229 } else unreachable;231 } else unreachable;
230 const ptr = &ws.step_status_bits[step_idx / 4];232 const ptr = &ws.step_status_bits[step_idx / 4];
...@@ -238,13 +240,16 @@ pub fn updateStepStatus(...@@ -238,13 +240,16 @@ pub fn updateStepStatus(
238pub fn finishBuild(ws: *WebServer, opts: struct {240pub fn finishBuild(ws: *WebServer, opts: struct {
239 fuzz: bool,241 fuzz: bool,
240}) void {242}) void {
243 const maker = ws.maker;
244 const all_steps = maker.step_stack.keys();
245
241 if (opts.fuzz) {246 if (opts.fuzz) {
242 switch (builtin.os.tag) {247 switch (builtin.os.tag) {
243 // Current implementation depends on two things that need to be ported to Windows:248 // Current implementation depends on two things that need to be ported to Windows:
244 // * Memory-mapping to share data between the fuzzer and build runner.249 // * Memory-mapping to share data between the fuzzer and build runner.
245 // * COFF/PE support added to `std.debug.Info` (it needs a batching API for resolving250 // * COFF/PE support added to `std.debug.Info` (it needs a batching API for resolving
246 // many addresses to source locations).251 // 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}),
248 else => {},253 else => {},
249 }254 }
250 if (@bitSizeOf(usize) != 64) {255 if (@bitSizeOf(usize) != 64) {
...@@ -260,28 +265,26 @@ pub fn finishBuild(ws: *WebServer, opts: struct {...@@ -260,28 +265,26 @@ pub fn finishBuild(ws: *WebServer, opts: struct {
260 ws.build_status.store(.fuzz_init, .monotonic);265 ws.build_status.store(.fuzz_init, .monotonic);
261 ws.notifyUpdate();266 ws.notifyUpdate();
262267
263 ws.fuzz = Fuzz.init(268 ws.fuzz = Fuzz.init(maker, all_steps, ws.root_prog_node, .{ .forever = .{ .ws = ws } }) catch |err|
264 ws.gpa,269 std.process.fatal("failed to start fuzzer: {t}", .{err});
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)});
270 ws.fuzz.?.start();270 ws.fuzz.?.start();
271 }271 }
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);
274 ws.notifyUpdate();274 ws.notifyUpdate();
275}275}
276276
277pub fn now(s: *const WebServer) i64 {277pub fn now(ws: *const WebServer) i64 {
278 const io = s.graph.io;278 const maker = ws.maker;
279 const io = maker.graph.io;
279 const ts = base_clock.now(io);280 const ts = base_clock.now(io);
280 return @intCast(s.base_timestamp.durationTo(ts).toNanoseconds());281 return @intCast(ws.base_timestamp.durationTo(ts).toNanoseconds());
281}282}
282283
283fn accept(ws: *WebServer, stream: net.Stream) void {284fn 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
285 defer {288 defer {
286 // `net.Stream.close` wants to helpfully overwrite `stream` with289 // `net.Stream.close` wants to helpfully overwrite `stream` with
287 // `undefined`, but it cannot do so since it is an immutable parameter.290 // `undefined`, but it cannot do so since it is an immutable parameter.
...@@ -326,12 +329,16 @@ fn accept(ws: *WebServer, stream: net.Stream) void {...@@ -326,12 +329,16 @@ fn accept(ws: *WebServer, stream: net.Stream) void {
326}329}
327330
328fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn {331fn 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
331 var prev_build_status = ws.build_status.load(.monotonic);338 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);340 const prev_step_status_bits = try gpa.alloc(u8, ws.step_status_bits.len);
334 defer ws.gpa.free(prev_step_status_bits);341 defer gpa.free(prev_step_status_bits);
335 for (prev_step_status_bits, ws.step_status_bits) |*copy, *shared| {342 for (prev_step_status_bits, ws.step_status_bits) |*copy, *shared| {
336 copy.* = @atomicLoad(u8, shared, .monotonic);343 copy.* = @atomicLoad(u8, shared, .monotonic);
337 }344 }
...@@ -343,10 +350,10 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn {...@@ -343,10 +350,10 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn {
343 const hello_header: abi.Hello = .{350 const hello_header: abi.Hello = .{
344 .status = prev_build_status,351 .status = prev_build_status,
345 .flags = .{352 .flags = .{
346 .time_report = ws.graph.time_report,353 .time_report = graph.time_report,
347 },354 },
348 .timestamp = ws.now(),355 .timestamp = ws.now(),
349 .steps_len = @intCast(ws.all_steps.len),356 .steps_len = @intCast(all_steps.len),
350 };357 };
351 var bufs: [3][]const u8 = .{ @ptrCast(&hello_header), ws.step_names_trailing, prev_step_status_bits };358 var bufs: [3][]const u8 = .{ @ptrCast(&hello_header), ws.step_names_trailing, prev_step_status_bits };
352 try sock.writeMessageVec(&bufs, .binary);359 try sock.writeMessageVec(&bufs, .binary);
...@@ -369,8 +376,8 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn {...@@ -369,8 +376,8 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn {
369 if (update_time <= prev_time) continue;376 if (update_time <= prev_time) continue;
370 // We want to send `msg`, but shouldn't block `ws.time_report_mutex` while we do, so377 // We want to send `msg`, but shouldn't block `ws.time_report_mutex` while we do, so
371 // that we don't hold up the build system on the client accepting this packet.378 // 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);379 const owned_msg = try gpa.dupe(u8, msg);
373 defer ws.gpa.free(owned_msg);380 defer gpa.free(owned_msg);
374 // Temporarily unlock, then re-lock after the message is sent.381 // Temporarily unlock, then re-lock after the message is sent.
375 ws.time_report_mutex.unlock(io);382 ws.time_report_mutex.unlock(io);
376 defer ws.time_report_mutex.lockUncancelable(io);383 defer ws.time_report_mutex.lockUncancelable(io);
...@@ -427,7 +434,8 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn {...@@ -427,7 +434,8 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn {
427 }434 }
428}435}
429fn recvWebSocketMessages(ws: *WebServer, sock: *http.Server.WebSocket) void {436fn 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
432 while (true) {440 while (true) {
433 const msg = sock.readSmallMessage() catch return;441 const msg = sock.readSmallMessage() catch return;
...@@ -485,8 +493,11 @@ fn serveLibFile(...@@ -485,8 +493,11 @@ fn serveLibFile(
485 sub_path: []const u8,493 sub_path: []const u8,
486 content_type: []const u8,494 content_type: []const u8,
487) !void {495) !void {
496 const maker = ws.maker;
497 const graph = maker.graph;
498
488 return serveFile(ws, request, .{499 return serveFile(ws, request, .{
489 .root_dir = ws.graph.zig_lib_directory,500 .root_dir = graph.zig_lib_directory,
490 .sub_path = sub_path,501 .sub_path = sub_path,
491 }, content_type);502 }, content_type);
492}503}
...@@ -495,7 +506,9 @@ fn serveClientWasm(...@@ -495,7 +506,9 @@ fn serveClientWasm(
495 req: *http.Server.Request,506 req: *http.Server.Request,
496 optimize_mode: std.builtin.OptimizeMode,507 optimize_mode: std.builtin.OptimizeMode,
497) !void {508) !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);
499 defer arena_state.deinit();512 defer arena_state.deinit();
500 const arena = arena_state.allocator();513 const arena = arena_state.allocator();
501514
...@@ -510,8 +523,10 @@ pub fn serveFile(...@@ -510,8 +523,10 @@ pub fn serveFile(
510 path: Cache.Path,523 path: Cache.Path,
511 content_type: []const u8,524 content_type: []const u8,
512) !void {525) !void {
513 const gpa = ws.gpa;526 const maker = ws.maker;
514 const io = ws.graph.io;527 const gpa = ws.maker.gpa;
528 const io = maker.graph.io;
529
515 // The desired API is actually sendfile, which will require enhancing http.Server.530 // The desired API is actually sendfile, which will require enhancing http.Server.
516 // We load the file with every request so that the user can make changes to the file531 // We load the file with every request so that the user can make changes to the file
517 // and refresh the HTML page without restarting this server.532 // and refresh the HTML page without restarting this server.
...@@ -528,7 +543,8 @@ pub fn serveFile(...@@ -528,7 +543,8 @@ pub fn serveFile(
528 });543 });
529}544}
530pub fn serveTarFile(ws: *WebServer, request: *http.Server.Request, paths: []const Cache.Path) !void {545pub 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;
532 const io = graph.io;548 const io = graph.io;
533549
534 var send_buffer: [0x4000]u8 = undefined;550 var send_buffer: [0x4000]u8 = undefined;
...@@ -576,8 +592,9 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim...@@ -576,8 +592,9 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim
576 const arch_os_abi = "wasm32-freestanding";592 const arch_os_abi = "wasm32-freestanding";
577 const cpu_features = "baseline+atomics+bulk_memory+multivalue+mutable_globals+nontrapping_fptoint+reference_types+sign_ext";593 const cpu_features = "baseline+atomics+bulk_memory+multivalue+mutable_globals+nontrapping_fptoint+reference_types+sign_ext";
578594
579 const gpa = ws.gpa;595 const maker = ws.maker;
580 const graph = ws.graph;596 const gpa = maker.gpa;
597 const graph = maker.graph;
581 const io = graph.io;598 const io = graph.io;
582599
583 const main_src_path: Cache.Path = .{600 const main_src_path: Cache.Path = .{
...@@ -697,7 +714,7 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim...@@ -697,7 +714,7 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim
697 if (code != 0) {714 if (code != 0) {
698 log.err(715 log.err(
699 "the following command exited with error code {d}:\n{s}",716 "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) },
701 );718 );
702 return error.WasmCompilationFailed;719 return error.WasmCompilationFailed;
703 }720 }
...@@ -705,21 +722,21 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim...@@ -705,21 +722,21 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim
705 .signal => |sig| {722 .signal => |sig| {
706 log.err(723 log.err(
707 "the following command terminated with signal {t}:\n{s}",724 "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) },
709 );726 );
710 return error.WasmCompilationFailed;727 return error.WasmCompilationFailed;
711 },728 },
712 .stopped => |sig| {729 .stopped => |sig| {
713 log.err(730 log.err(
714 "the following command stopped unexpectedly with signal {t}:\n{s}",731 "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) },
716 );733 );
717 return error.WasmCompilationFailed;734 return error.WasmCompilationFailed;
718 },735 },
719 .unknown => {736 .unknown => {
720 log.err(737 log.err(
721 "the following command terminated unexpectedly:\n{s}",738 "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)},
723 );740 );
724 return error.WasmCompilationFailed;741 return error.WasmCompilationFailed;
725 },742 },
...@@ -729,14 +746,14 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim...@@ -729,14 +746,14 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim
729 try result_error_bundle.renderToStderr(io, .{}, .auto);746 try result_error_bundle.renderToStderr(io, .{}, .auto);
730 log.err("the following command failed with {d} compilation errors:\n{s}", .{747 log.err("the following command failed with {d} compilation errors:\n{s}", .{
731 result_error_bundle.errorMessageCount(),748 result_error_bundle.errorMessageCount(),
732 try Step.allocPrintCmd(arena, .inherit, null, argv.items),749 try std.zig.allocPrintCmd(arena, .inherit, null, argv.items),
733 });750 });
734 return error.WasmCompilationFailed;751 return error.WasmCompilationFailed;
735 }752 }
736753
737 const base_path = result orelse {754 const base_path = result orelse {
738 log.err("child process failed to report result\n{s}", .{755 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),
740 });757 });
741 return error.WasmCompilationFailed;758 return error.WasmCompilationFailed;
742 };759 };
...@@ -773,11 +790,13 @@ pub fn updateTimeReportCompile(ws: *WebServer, opts: struct {...@@ -773,11 +790,13 @@ pub fn updateTimeReportCompile(ws: *WebServer, opts: struct {
773 /// The trailing data of `abi.time_report.CompileResult`, except the step name.790 /// The trailing data of `abi.time_report.CompileResult`, except the step name.
774 trailing: []const u8,791 trailing: []const u8,
775}) void {792}) void {
776 const gpa = ws.gpa;793 const maker = ws.maker;
777 const io = ws.graph.io;794 const gpa = maker.gpa;
795 const io = maker.graph.io;
796 const all_steps = maker.step_stack.keys();
778797
779 // TODO don't do linear search798 // 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| {
781 if (s == opts.compile_step) break @intCast(i);800 if (s == opts.compile_step) break @intCast(i);
782 } else unreachable;801 } else unreachable;
783802
...@@ -815,11 +834,13 @@ pub fn updateTimeReportCompile(ws: *WebServer, opts: struct {...@@ -815,11 +834,13 @@ pub fn updateTimeReportCompile(ws: *WebServer, opts: struct {
815}834}
816835
817pub fn updateTimeReportGeneric(ws: *WebServer, step_index: Configuration.Step.Index, duration: Io.Duration) void {836pub fn updateTimeReportGeneric(ws: *WebServer, step_index: Configuration.Step.Index, duration: Io.Duration) void {
818 const gpa = ws.gpa;837 const maker = ws.maker;
819 const io = ws.graph.io;838 const gpa = maker.gpa;
839 const io = maker.graph.io;
840 const all_steps = maker.step_stack.keys();
820841
821 // TODO don't do linear search842 // 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| {
823 if (s == step_index) break @intCast(i);844 if (s == step_index) break @intCast(i);
824 } else unreachable;845 } else unreachable;
825846
...@@ -852,11 +873,13 @@ pub fn updateTimeReportRunTest(...@@ -852,11 +873,13 @@ pub fn updateTimeReportRunTest(
852 tests: *const Step.Run.CachedTestMetadata,873 tests: *const Step.Run.CachedTestMetadata,
853 ns_per_test: []const u64,874 ns_per_test: []const u64,
854) void {875) void {
855 const gpa = ws.gpa;876 const maker = ws.maker;
856 const io = ws.graph.io;877 const gpa = maker.gpa;
878 const io = maker.graph.io;
879 const all_steps = maker.step_stack.keys();
857880
858 // TODO don't do linear search881 // 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| {
860 if (s == run_step_index) break @intCast(i);883 if (s == run_step_index) break @intCast(i);
861 } else unreachable;884 } else unreachable;
862885
...@@ -910,7 +933,7 @@ const RunnerRequest = union(enum) {...@@ -910,7 +933,7 @@ const RunnerRequest = union(enum) {
910 rebuild,933 rebuild,
911};934};
912pub fn getRunnerRequest(ws: *WebServer) ?RunnerRequest {935pub fn getRunnerRequest(ws: *WebServer) ?RunnerRequest {
913 const io = ws.graph.io;936 const io = ws.maker.graph.io;
914 ws.runner_request_mutex.lock(io) catch return;937 ws.runner_request_mutex.lock(io) catch return;
915 defer ws.runner_request_mutex.unlock(io);938 defer ws.runner_request_mutex.unlock(io);
916 if (ws.runner_request) |req| {939 if (ws.runner_request) |req| {
...@@ -921,7 +944,7 @@ pub fn getRunnerRequest(ws: *WebServer) ?RunnerRequest {...@@ -921,7 +944,7 @@ pub fn getRunnerRequest(ws: *WebServer) ?RunnerRequest {
921 return null;944 return null;
922}945}
923pub fn wait(ws: *WebServer) Io.Cancelable!RunnerRequest {946pub fn wait(ws: *WebServer) Io.Cancelable!RunnerRequest {
924 const io = ws.graph.io;947 const io = ws.maker.graph.io;
925 try ws.runner_request_mutex.lock(io);948 try ws.runner_request_mutex.lock(io);
926 defer ws.runner_request_mutex.unlock(io);949 defer ws.runner_request_mutex.unlock(io);
927 while (true) {950 while (true) {
lib/std/Build.zig-13
...@@ -45,7 +45,6 @@ install_prefix: []const u8,...@@ -45,7 +45,6 @@ install_prefix: []const u8,
45/// Path to the directory containing build.zig.45/// Path to the directory containing build.zig.
46build_root: Cache.Directory,46build_root: Cache.Directory,
47cache_root: Cache.Directory,47cache_root: Cache.Directory,
48pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null,
49debug_log_scopes: []const []const u8 = &.{},48debug_log_scopes: []const []const u8 = &.{},
50debug_compile_errors: bool = false,49debug_compile_errors: bool = false,
51debug_incremental: bool = false,50debug_incremental: bool = false,
...@@ -176,18 +175,6 @@ pub const RunError = error{...@@ -176,18 +175,6 @@ pub const RunError = error{
176 ExecNotSupported,175 ExecNotSupported,
177} || std.process.SpawnError;176} || 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
191const UserInputOptionsMap = StringHashMap(UserInputOption);178const UserInputOptionsMap = StringHashMap(UserInputOption);
192const AvailableOptionsMap = StringHashMap(AvailableOption);179const AvailableOptionsMap = StringHashMap(AvailableOption);
193180
lib/std/Build/Step/Compile.zig-40
...@@ -8,13 +8,9 @@ const fs = std.fs;...@@ -8,13 +8,9 @@ const fs = std.fs;
8const assert = std.debug.assert;8const assert = std.debug.assert;
9const panic = std.debug.panic;9const panic = std.debug.panic;
10const StringHashMap = std.StringHashMap;10const StringHashMap = std.StringHashMap;
11const Sha256 = std.crypto.hash.sha2.Sha256;
12const Allocator = std.mem.Allocator;11const Allocator = std.mem.Allocator;
13const Step = std.Build.Step;12const Step = std.Build.Step;
14const LazyPath = std.Build.LazyPath;13const LazyPath = std.Build.LazyPath;
15const PkgConfigPkg = std.Build.PkgConfigPkg;
16const PkgConfigError = std.Build.PkgConfigError;
17const RunError = std.Build.RunError;
18const Module = std.Build.Module;14const Module = std.Build.Module;
19const InstallDir = std.Build.InstallDir;15const InstallDir = std.Build.InstallDir;
20const GeneratedFile = std.Build.GeneratedFile;16const GeneratedFile = std.Build.GeneratedFile;
...@@ -777,42 +773,6 @@ pub fn setExecCmd(compile: *Compile, args: []const ?[]const u8) void {...@@ -777,42 +773,6 @@ pub fn setExecCmd(compile: *Compile, args: []const ?[]const u8) void {
777 compile.exec_cmd_args = duped_args;773 compile.exec_cmd_args = duped_args;
778}774}
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
816fn getGeneratedFilePath(compile: *Compile, comptime tag_name: []const u8, asking_step: ?*Step) ![]const u8 {776fn getGeneratedFilePath(compile: *Compile, comptime tag_name: []const u8, asking_step: ?*Step) ![]const u8 {
817 const step = &compile.step;777 const step = &compile.step;
818 const b = step.owner;778 const b = step.owner;
lib/std/zig/Configuration.zig+15-11
...@@ -436,23 +436,23 @@ pub const Step = extern struct {...@@ -436,23 +436,23 @@ pub const Step = extern struct {
436 };436 };
437437
438 pub const Tag = enum(u5) {438 pub const Tag = enum(u5) {
439 top_level,439 check_file,
440 check_object,
440 compile,441 compile,
442 config_header,
443 fail,
444 fmt,
441 install_artifact,445 install_artifact,
442 install_file,
443 install_dir,446 install_dir,
447 install_file,
448 objcopy,
449 options,
444 remove_dir,450 remove_dir,
445 fail,451 run,
446 fmt,452 top_level,
447 translate_c,453 translate_c,
448 write_file,
449 update_source_files,454 update_source_files,
450 run,455 write_file,
451 check_file,
452 check_object,
453 config_header,
454 objcopy,
455 options,
456 };456 };
457457
458 pub const TopLevel = struct {458 pub const TopLevel = struct {
...@@ -808,6 +808,10 @@ pub const Step = extern struct {...@@ -808,6 +808,10 @@ pub const Step = extern struct {
808 _: u23 = 0,808 _: u23 = 0,
809 };809 };
810 };810 };
811
812 pub fn flags(s: *const Step, c: *const Configuration) Flags {
813 return @bitCast(c.extra[s.extra_index]);
814 }
811};815};
812816
813pub const MaxRss = enum(u32) {817pub const MaxRss = enum(u32) {