authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-04-28 22:15:09-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-05-25 18:54:34-07:00
log4e3d14f590160013e655f69e249997ff597f8e93
tree2122544a528159369693286510642f6ec705cdf3
parentc8b583885d75524fc92cc02a9d00a49a76f2ea70

maker: update more Run step logic


2 files changed, 330 insertions(+), 310 deletions(-)

lib/compiler/Maker/Step.zig+4-4
......@@ -18,10 +18,10 @@ const assert = std.debug.assert;
1818const WebServer = @import("WebServer.zig");
1919const Maker = @import("../Maker.zig");
2020
21const Compile = @import("Step/Compile.zig");
22const Run = @import("Step/Run.zig");
23const InstallArtifact = @import("Step/InstallArtifact.zig");
24const InstallFile = @import("Step/InstallFile.zig");
21pub const Compile = @import("Step/Compile.zig");
22pub const Run = @import("Step/Run.zig");
23pub const InstallArtifact = @import("Step/InstallArtifact.zig");
24pub const InstallFile = @import("Step/InstallFile.zig");
2525
2626/// Avoid false sharing.
2727_: void align(std.atomic.cache_line) = {},
lib/compiler/Maker/Step/Run.zig+326-306
......@@ -325,9 +325,10 @@ pub fn make(
325325/// * The wait fails, indicating the child closed stdout and stderr
326326fn waitZigTest(
327327 run: *Run,
328 run_index: Configuration.Step.Index,
328329 maker: *Maker,
329330 child: *process.Child,
330 options: Step.MakeOptions,
331 progress_node: std.Progress.Node,
331332 multi_reader: *Io.File.MultiReader,
332333 opt_metadata: *?TestMetadata,
333334 results: *Step.TestResults,
......@@ -342,9 +343,11 @@ fn waitZigTest(
342343 ns_elapsed: u64,
343344 },
344345} {
345 const gpa = run.step.owner.allocator;
346 const arena = run.step.owner.allocator;
347 const io = run.step.owner.graph.io;
346 const graph = maker.graph;
347 const gpa = maker.gpa;
348 const io = graph.io;
349 const arena = graph.arena; // TODO don't leak into the process arena
350 const step = maker.stepByIndex(run_index);
348351
349352 var sub_prog_node: ?std.Progress.Node = null;
350353 defer if (sub_prog_node) |n| n.end();
......@@ -367,10 +370,10 @@ fn waitZigTest(
367370 // start and it acknowledging the test starting, we terminate the child and raise an error. This
368371 // *should* never happen, but could in theory be caused by some very unlucky IB in a test.
369372 const response_timeout: Io.Clock.Duration = t: {
370 const ns = @max(options.unit_test_timeout_ns orelse 0, 60 * std.time.ns_per_s);
373 const ns = @max(maker.unit_test_timeout_ns orelse 0, 60 * std.time.ns_per_s);
371374 break :t .{ .clock = .awake, .raw = .fromNanoseconds(ns) };
372375 };
373 const test_timeout: ?Io.Clock.Duration = if (options.unit_test_timeout_ns) |ns| .{
376 const test_timeout: ?Io.Clock.Duration = if (maker.unit_test_timeout_ns) |ns| .{
374377 .clock = .awake,
375378 .raw = .fromNanoseconds(ns),
376379 } else null;
......@@ -428,7 +431,7 @@ fn waitZigTest(
428431 var body_r: std.Io.Reader = .fixed(body);
429432 switch (header.tag) {
430433 .zig_version => {
431 if (!std.mem.eql(u8, builtin.zig_version_string, body)) return run.step.fail(
434 if (!std.mem.eql(u8, builtin.zig_version_string, body)) return step.fail(
432435 maker,
433436 "zig version mismatch build runner vs compiler: '{s}' vs '{s}'",
434437 .{ builtin.zig_version_string, body },
......@@ -451,14 +454,14 @@ fn waitZigTest(
451454
452455 const string_bytes = body_r.take(tm_hdr.string_bytes_len) catch unreachable;
453456
454 options.progress_node.setEstimatedTotalItems(names.len);
457 progress_node.setEstimatedTotalItems(names.len);
455458 opt_metadata.* = .{
456459 .string_bytes = try arena.dupe(u8, string_bytes),
457460 .ns_per_test = try arena.alloc(u64, results.test_count),
458461 .names = names,
459462 .expected_panic_msgs = expected_panic_msgs,
460463 .next_index = 0,
461 .prog_node = options.progress_node,
464 .prog_node = progress_node,
462465 };
463466 @memset(opt_metadata.*.?.ns_per_test, std.math.maxInt(u64));
464467
......@@ -494,20 +497,20 @@ fn waitZigTest(
494497 const stderr_bytes = std.mem.trim(u8, stderr.buffered(), "\n");
495498 stderr.tossBuffered();
496499 if (stderr_bytes.len == 0) {
497 try run.step.addError("'{s}' failed without output", .{name});
500 try step.addError(maker, "'{s}' failed without output", .{name});
498501 } else {
499 try run.step.addError("'{s}' failed:\n{s}", .{ name, stderr_bytes });
502 try step.addError(maker, "'{s}' failed:\n{s}", .{ name, stderr_bytes });
500503 }
501504 } else if (leak_count > 0) {
502505 const name = md.testName(tr_hdr.index);
503506 const stderr_bytes = std.mem.trim(u8, stderr.buffered(), "\n");
504507 stderr.tossBuffered();
505 try run.step.addError("'{s}' leaked {d} allocations:\n{s}", .{ name, leak_count, stderr_bytes });
508 try step.addError(maker, "'{s}' leaked {d} allocations:\n{s}", .{ name, leak_count, stderr_bytes });
506509 } else if (log_err_count > 0) {
507510 const name = md.testName(tr_hdr.index);
508511 const stderr_bytes = std.mem.trim(u8, stderr.buffered(), "\n");
509512 stderr.tossBuffered();
510 try run.step.addError("'{s}' logged {d} errors:\n{s}", .{ name, log_err_count, stderr_bytes });
513 try step.addError(maker, "'{s}' logged {d} errors:\n{s}", .{ name, log_err_count, stderr_bytes });
511514 }
512515
513516 active_test_index = null;
......@@ -525,6 +528,7 @@ fn waitZigTest(
525528
526529const FuzzTestRunner = struct {
527530 run: *Run,
531 run_index: Configuration.Step.Index,
528532 ctx: FuzzContext,
529533 coverage_id: ?u64,
530534
......@@ -572,16 +576,18 @@ const FuzzTestRunner = struct {
572576
573577 fn init(
574578 run: *Run,
579 run_index: Configuration.Step.Index,
575580 ctx: FuzzContext,
576581 progress_node: std.Progress.Node,
577582 spawn_options: process.SpawnOptions,
578583 ) !FuzzTestRunner {
579 const step_owner = run.step.owner;
580 const gpa = step_owner.allocator;
581 const io = step_owner.graph.io;
584 const maker = ctx.fuzz.maker;
585 const graph = maker.graph;
586 const gpa = maker.gpa;
587 const io = graph.io;
582588
583589 const n_instances = switch (ctx.fuzz.mode) {
584 .forever => step_owner.graph.max_jobs orelse @min(
590 .forever => graph.max_jobs orelse @min(
585591 std.Thread.getCpuCount() catch 1,
586592 (std.math.maxInt(u32) - 2) / 3,
587593 ),
......@@ -613,6 +619,7 @@ const FuzzTestRunner = struct {
613619
614620 return .{
615621 .run = run,
622 .run_index = run_index,
616623 .ctx = ctx,
617624 .coverage_id = null,
618625
......@@ -625,9 +632,13 @@ const FuzzTestRunner = struct {
625632 }
626633
627634 fn deinit(f: *FuzzTestRunner) void {
628 const step_owner = f.run.step.owner;
629 const gpa = step_owner.allocator;
630 const io = step_owner.graph.io;
635 const maker = f.ctx.fuzz.maker;
636 const run_index = f.run_index;
637
638 const graph = maker.graph;
639 const gpa = maker.gpa;
640 const io = graph.io;
641 const step = maker.stepByIndex(run_index);
631642
632643 f.batch.cancel(io);
633644 gpa.free(f.batch.storage);
......@@ -639,13 +650,18 @@ const FuzzTestRunner = struct {
639650 instance.progress_node.end();
640651 total_rss += instance.child.resource_usage_statistics.getMaxRss() orelse 0;
641652 }
642 f.run.step.result_peak_rss = @max(f.run.step.result_peak_rss, total_rss);
653 step.result_peak_rss = @max(step.result_peak_rss, total_rss);
643654 gpa.free(f.instances);
644655 }
645656
646657 fn startInstances(f: *FuzzTestRunner) !void {
647 const step_owner = f.run.step.owner;
648 const io = step_owner.graph.io;
658 const maker = f.ctx.fuzz.maker;
659 const run_index = f.run_index;
660 const run = f.run;
661
662 const graph = maker.graph;
663 const io = graph.io;
664 const step = maker.stepByIndex(run_index);
649665
650666 for (0.., f.instances) |id, *instance| {
651667 const id32: u32 = @intCast(id);
......@@ -653,14 +669,14 @@ const FuzzTestRunner = struct {
653669 .forever => sendRunFuzzTestMessage(
654670 io,
655671 instance.child.stdin.?,
656 f.run.fuzz_tests.items,
672 run.fuzz_tests.items,
657673 .forever,
658674 id32,
659675 ),
660676 .limit => |limit| sendRunFuzzTestMessage(
661677 io,
662678 instance.child.stdin.?,
663 f.run.fuzz_tests.items,
679 run.fuzz_tests.items,
664680 .iterations,
665681 limit.amount,
666682 ),
......@@ -670,7 +686,8 @@ const FuzzTestRunner = struct {
670686 instance.child.stdin.?.close(io);
671687 instance.child.stdin = null;
672688 const term = try instance.child.wait(io);
673 return f.run.step.fail(
689 return step.fail(
690 maker,
674691 "unable to write stdin ({t}); test process unexpectedly {f}",
675692 .{ write_err, fmtTerm(term) },
676693 );
......@@ -682,8 +699,9 @@ const FuzzTestRunner = struct {
682699 }
683700
684701 fn listen(f: *FuzzTestRunner) !void {
685 const step_owner = f.run.step.owner;
686 const io = step_owner.graph.io;
702 const maker = f.ctx.fuzz.maker;
703 const graph = maker.graph;
704 const io = graph.io;
687705
688706 while (true) {
689707 try f.batch.awaitConcurrent(io, .none);
......@@ -714,10 +732,15 @@ const FuzzTestRunner = struct {
714732 }
715733
716734 fn completeStdoutRead(f: *FuzzTestRunner, id: u32, n: usize) !void {
717 const step_owner = f.run.step.owner;
718 const gpa = step_owner.allocator;
719 const io = step_owner.graph.io;
735 const maker = f.ctx.fuzz.maker;
720736 const instance = &f.instances[id];
737 const run_index = f.run_index;
738 const run = f.run;
739
740 const graph = maker.graph;
741 const gpa = maker.gpa;
742 const io = graph.io;
743 const step = maker.stepByIndex(run_index);
721744
722745 instance.message.items.len += n;
723746 const total_read = instance.message.items.len;
......@@ -735,7 +758,8 @@ const FuzzTestRunner = struct {
735758
736759 switch (header.tag) {
737760 .zig_version => {
738 if (!std.mem.eql(u8, builtin.zig_version_string, body)) return f.run.step.fail(
761 if (!std.mem.eql(u8, builtin.zig_version_string, body)) return step.fail(
762 maker,
739763 "zig version mismatch build runner vs compiler: '{s}' vs '{s}'",
740764 .{ builtin.zig_version_string, body },
741765 );
......@@ -750,14 +774,14 @@ const FuzzTestRunner = struct {
750774 const fuzz = f.ctx.fuzz;
751775 fuzz.queue_mutex.lockUncancelable(io);
752776 defer fuzz.queue_mutex.unlock(io);
753 try fuzz.msg_queue.append(fuzz.gpa, .{ .coverage = .{
777 try fuzz.msg_queue.append(gpa, .{ .coverage = .{
754778 .id = f.coverage_id.?,
755779 .cumulative = .{
756780 .runs = cumulative_runs,
757781 .unique = cumulative_unique,
758782 .coverage = cumulative_coverage,
759783 },
760 .run = f.run,
784 .run = run_index,
761785 } });
762786 fuzz.queue_cond.signal(io);
763787 },
......@@ -768,7 +792,7 @@ const FuzzTestRunner = struct {
768792
769793 fuzz.queue_mutex.lockUncancelable(io);
770794 defer fuzz.queue_mutex.unlock(io);
771 try fuzz.msg_queue.append(fuzz.gpa, .{ .entry_point = .{
795 try fuzz.msg_queue.append(gpa, .{ .entry_point = .{
772796 .addr = addr,
773797 .coverage_id = f.coverage_id.?,
774798 } });
......@@ -776,7 +800,7 @@ const FuzzTestRunner = struct {
776800 },
777801 .fuzz_test_change => {
778802 const test_i = std.mem.readInt(u32, body[0..4], .little);
779 instance.progress_node.setName(f.run.fuzz_tests.items[test_i]);
803 instance.progress_node.setName(run.fuzz_tests.items[test_i]);
780804 },
781805 .broadcast_fuzz_input => {
782806 if (f.instances.len == 1) {
......@@ -823,8 +847,8 @@ const FuzzTestRunner = struct {
823847 }
824848
825849 fn addStdoutRead(f: *FuzzTestRunner, id: u32, end: usize) !void {
826 const step_owner = f.run.step.owner;
827 const gpa = step_owner.allocator;
850 const maker = f.ctx.fuzz.maker;
851 const gpa = maker.gpa;
828852 const instance = &f.instances[id];
829853
830854 try instance.message.ensureTotalCapacity(gpa, end);
......@@ -837,8 +861,8 @@ const FuzzTestRunner = struct {
837861 }
838862
839863 fn addStderrRead(f: *FuzzTestRunner, id: u32) !void {
840 const step_owner = f.run.step.owner;
841 const gpa = step_owner.allocator;
864 const maker = f.ctx.fuzz.maker;
865 const gpa = maker.gpa;
842866 const instance = &f.instances[id];
843867
844868 try instance.stderr.ensureUnusedCapacity(gpa, 1);
......@@ -861,24 +885,31 @@ const FuzzTestRunner = struct {
861885 }
862886
863887 fn instanceEos(f: *FuzzTestRunner, id: u32) !void {
864 const step_owner = f.run.step.owner;
865 const io = step_owner.graph.io;
888 const maker = f.ctx.fuzz.maker;
866889 const instance = &f.instances[id];
890 const run_index = f.run_index;
891
892 const graph = maker.graph;
893 const io = graph.io;
894 const step = maker.stepByIndex(run_index);
867895
868896 instance.child.stdin.?.close(io);
869897 instance.child.stdin = null;
870898 const term = try instance.child.wait(io);
871899 if (!termMatches(.{ .exited = 0 }, term)) {
872 f.run.step.result_stderr = try f.mergedStderr();
900 step.result_stderr = try f.mergedStderr();
873901 try f.saveCrash(id, term);
874 return f.run.step.fail("test process unexpectedly {f}", .{fmtTerm(term)});
902 return step.fail(maker, "test process unexpectedly {f}", .{fmtTerm(term)});
875903 }
876904 }
877905
878906 fn saveCrash(f: *FuzzTestRunner, id: u32, term: process.Child.Term) !void {
879 const fuzz = f.context.fuzz;
907 const fuzz = f.ctx.fuzz;
908 const run_index = f.run_index;
909 const run = f.run;
910
880911 const maker = fuzz.maker;
881 const step = &f.run.step;
912 const step = maker.stepByIndex(run_index);
882913 const graph = maker.graph;
883914 const io = graph.io;
884915 const cache_root = graph.local_cache_root;
......@@ -906,7 +937,7 @@ const FuzzTestRunner = struct {
906937 error.FileNotFound => return,
907938 error.WouldBlock => continue, // Can not be from
908939 // the crashed instance since it is still locked.
909 else => return step.fail("failed to open file '{f}{s}': {t}", .{
940 else => return step.fail(maker, "failed to open file '{f}{s}': {t}", .{
910941 cache_root, in_name, e,
911942 }),
912943 };
......@@ -915,7 +946,7 @@ const FuzzTestRunner = struct {
915946 const header = in_r.interface.takeStruct(InputHeader, .little) catch |e| {
916947 in_f.close(io);
917948 switch (e) {
918 error.ReadFailed => return step.fail("failed to read file '{f}{s}': {t}", .{
949 error.ReadFailed => return step.fail(maker, "failed to read file '{f}{s}': {t}", .{
919950 cache_root, in_name, in_r.err.?,
920951 }),
921952 error.EndOfStream => continue,
......@@ -924,7 +955,7 @@ const FuzzTestRunner = struct {
924955
925956 if (header.pc_digest == f.coverage_id.? and
926957 header.instance_id == id and
927 header.test_i < f.run.fuzz_tests.items.len)
958 header.test_i < run.fuzz_tests.items.len)
928959 {
929960 break header;
930961 }
......@@ -937,7 +968,7 @@ const FuzzTestRunner = struct {
937968 const crash_name = "f" ++ Io.Dir.path.sep_str ++ "crash";
938969 const out = cache_root.handle.createFile(io, crash_name, .{
939970 .lock = .exclusive, // Multiple run steps could have found a crash at the same time
940 }) catch |e| return step.fail("failed to create file '{f}{s}': {t}", .{
971 }) catch |e| return step.fail(maker, "failed to create file '{f}{s}': {t}", .{
941972 cache_root, crash_name, e,
942973 });
943974 defer out.close(io);
......@@ -945,16 +976,16 @@ const FuzzTestRunner = struct {
945976 var out_w_buf: [512]u8 = undefined;
946977 var out_w = out.writerStreaming(io, &out_w_buf);
947978 _ = out_w.interface.sendFileAll(&in_r, .limited(header.len)) catch |e| switch (e) {
948 error.ReadFailed => return step.fail("failed to read file '{f}{s}': {t}", .{
979 error.ReadFailed => return step.fail(maker, "failed to read file '{f}{s}': {t}", .{
949980 cache_root, in_name, in_r.err.?,
950981 }),
951 error.WriteFailed => return step.fail("failed to write file '{f}{s}': {t}", .{
982 error.WriteFailed => return step.fail(maker, "failed to write file '{f}{s}': {t}", .{
952983 cache_root, crash_name, out_w.err.?,
953984 }),
954985 };
955986
956 return f.run.step.fail("test '{s}' {f}; input saved to '{f}{s}'", .{
957 f.run.fuzz_tests.items[header.test_i],
987 return step.fail(maker, "test '{s}' {f}; input saved to '{f}{s}'", .{
988 run.fuzz_tests.items[header.test_i],
958989 fmtTerm(term),
959990 cache_root,
960991 crash_name,
......@@ -967,8 +998,8 @@ const FuzzTestRunner = struct {
967998 assert(f.broadcast.items.len == 0);
968999 assert(from_id < f.instances.len);
9691000
970 const step_owner = f.run.step.owner;
971 const gpa = step_owner.allocator;
1001 const maker = f.ctx.fuzz.maker;
1002 const gpa = maker.gpa;
9721003
9731004 var out_header: OutHeader = .{
9741005 .tag = .new_fuzz_input,
......@@ -1010,8 +1041,9 @@ const FuzzTestRunner = struct {
10101041 }
10111042
10121043 fn mergedStderr(f: *FuzzTestRunner) std.mem.Allocator.Error![]const u8 {
1013 const step_owner = f.run.step.owner;
1014 const arena = step_owner.allocator;
1044 const maker = f.ctx.fuzz.maker;
1045 const graph = maker.graph;
1046 const arena = graph.arena; // TODO don't leak into the process arena
10151047
10161048 // Collect any available stderr
10171049 while (f.batch.next()) |completion| {
......@@ -1035,11 +1067,12 @@ const FuzzTestRunner = struct {
10351067
10361068fn evalFuzzTest(
10371069 run: *Run,
1070 run_index: Configuration.Step.Index,
1071 progress_node: std.Progress.Node,
10381072 spawn_options: process.SpawnOptions,
1039 options: Step.MakeOptions,
10401073 fuzz_context: FuzzContext,
10411074) !void {
1042 var f: FuzzTestRunner = try .init(run, fuzz_context, options.progress_node, spawn_options);
1075 var f: FuzzTestRunner = try .init(run, run_index, fuzz_context, progress_node, spawn_options);
10431076 defer f.deinit();
10441077 try f.startInstances();
10451078 try f.listen();
......@@ -1049,23 +1082,25 @@ const StdioPollEnum = enum { stdout, stderr };
10491082
10501083fn evalZigTest(
10511084 run: *Run,
1085 run_index: Configuration.Step.Index,
10521086 maker: *Maker,
1087 progress_node: std.Progress.Node,
10531088 spawn_options: process.SpawnOptions,
1054 options: Step.MakeOptions,
10551089 fuzz_context: ?FuzzContext,
10561090) !void {
10571091 if (fuzz_context != null) {
1058 try evalFuzzTest(run, spawn_options, options, fuzz_context.?);
1092 try evalFuzzTest(run, run_index, progress_node, spawn_options, fuzz_context.?);
10591093 return;
10601094 }
10611095
1062 const step_owner = run.step.owner;
1063 const gpa = step_owner.allocator;
1064 const arena = step_owner.allocator;
1065 const io = step_owner.graph.io;
1096 const graph = maker.graph;
1097 const gpa = maker.gpa;
1098 const io = graph.io;
1099 const arena = graph.arena; // TODO don't leak into the process arena
1100 const step = maker.stepByIndex(run_index);
10661101
10671102 // We will update this every time a child runs.
1068 run.step.result_peak_rss = 0;
1103 step.result_peak_rss = 0;
10691104
10701105 var test_results: Step.TestResults = .{
10711106 .test_count = 0,
......@@ -1087,16 +1122,18 @@ fn evalZigTest(
10871122 defer if (!child_killed) {
10881123 child.kill(io);
10891124 multi_reader.deinit();
1090 run.step.result_peak_rss = @max(
1091 run.step.result_peak_rss,
1125 step.result_peak_rss = @max(
1126 step.result_peak_rss,
10921127 child.resource_usage_statistics.getMaxRss() orelse 0,
10931128 );
10941129 };
10951130
10961131 switch (try waitZigTest(
10971132 run,
1133 run_index,
1134 maker,
10981135 &child,
1099 options,
1136 progress_node,
11001137 &multi_reader,
11011138 &test_metadata,
11021139 &test_results,
......@@ -1109,7 +1146,7 @@ fn evalZigTest(
11091146 error.ReadFailed => return stderr_fr.err.?,
11101147 error.EndOfStream => {},
11111148 }
1112 run.step.result_stderr = try arena.dupe(u8, stderr_fr.interface.buffered());
1149 step.result_stderr = try arena.dupe(u8, stderr_fr.interface.buffered());
11131150
11141151 // Clean up everything and wait for the child to exit.
11151152 child.stdin.?.close(io);
......@@ -1117,14 +1154,16 @@ fn evalZigTest(
11171154 multi_reader.deinit();
11181155 child_killed = true;
11191156 const term = try child.wait(io);
1120 run.step.result_peak_rss = @max(
1121 run.step.result_peak_rss,
1157 step.result_peak_rss = @max(
1158 step.result_peak_rss,
11221159 child.resource_usage_statistics.getMaxRss() orelse 0,
11231160 );
11241161
11251162 // The individual unit test results are irrelevant: the test runner itself broke!
11261163 // Fail immediately without populating `s.test_results`.
1127 return run.step.fail(maker, "unable to write stdin ({t}); test process unexpectedly {f}", .{ err, fmtTerm(term) });
1164 return step.fail(maker, "unable to write stdin ({t}); test process unexpectedly {f}", .{
1165 err, fmtTerm(term),
1166 });
11281167 },
11291168 .no_poll => |no_poll| {
11301169 // This might be a success (we requested exit and the child dutifully closed stdout) or
......@@ -1138,8 +1177,8 @@ fn evalZigTest(
11381177 multi_reader.deinit();
11391178 child_killed = true;
11401179 const term = try child.wait(io);
1141 run.step.result_peak_rss = @max(
1142 run.step.result_peak_rss,
1180 step.result_peak_rss = @max(
1181 step.result_peak_rss,
11431182 child.resource_usage_statistics.getMaxRss() orelse 0,
11441183 );
11451184
......@@ -1148,7 +1187,7 @@ fn evalZigTest(
11481187 // test, and continue to the next test.
11491188 test_metadata.?.ns_per_test[test_index] = no_poll.ns_elapsed;
11501189 test_results.crash_count += 1;
1151 try run.step.addError("'{s}' {f}{s}{s}", .{
1190 try step.addError(maker, "'{s}' {f}{s}{s}", .{
11521191 test_metadata.?.testName(test_index),
11531192 fmtTerm(term),
11541193 if (stderr_owned.len != 0) " with stderr:\n" else "",
......@@ -1158,22 +1197,22 @@ fn evalZigTest(
11581197 }
11591198
11601199 // Report an error if the child terminated uncleanly or if we were still trying to run more tests.
1161 run.step.result_stderr = stderr_owned;
1200 step.result_stderr = stderr_owned;
11621201 const tests_done = test_metadata != null and test_metadata.?.next_index == std.math.maxInt(u32);
11631202 if (!tests_done or !termMatches(.{ .exited = 0 }, term)) {
11641203 // The individual unit test results are irrelevant: the test runner itself broke!
11651204 // Fail immediately without populating `s.test_results`.
1166 return run.step.fail(maker, "test process unexpectedly {f}", .{fmtTerm(term)});
1205 return step.fail(maker, "test process unexpectedly {f}", .{fmtTerm(term)});
11671206 }
11681207
11691208 // We're done with all of the tests! Commit the test results and return.
1170 run.step.test_results = test_results;
1209 step.test_results = test_results;
11711210 if (test_metadata) |tm| {
11721211 run.cached_test_metadata = tm.toCachedTestMetadata();
1173 if (options.web_server) |ws| {
1174 if (run.step.owner.graph.time_report) {
1212 if (maker.web_server) |*ws| {
1213 if (graph.time_report) {
11751214 ws.updateTimeReportRunTest(
1176 run,
1215 run_index,
11771216 &run.cached_test_metadata.?,
11781217 tm.ns_per_test,
11791218 );
......@@ -1191,7 +1230,7 @@ fn evalZigTest(
11911230 // the next test.
11921231 test_metadata.?.ns_per_test[test_index] = timeout.ns_elapsed;
11931232 test_results.timeout_count += 1;
1194 try run.step.addError("'{s}' timed out after {f}{s}{s}", .{
1233 try step.addError(maker, "'{s}' timed out after {f}{s}{s}", .{
11951234 test_metadata.?.testName(test_index),
11961235 Io.Duration{ .nanoseconds = timeout.ns_elapsed },
11971236 if (stderr.len != 0) " with stderr:\n" else "",
......@@ -1200,10 +1239,10 @@ fn evalZigTest(
12001239 continue;
12011240 }
12021241 // Just log an error and let the child be killed.
1203 run.step.result_stderr = try arena.dupe(u8, stderr);
1242 step.result_stderr = try arena.dupe(u8, stderr);
12041243 // The individual unit test results in `results` are irrelevant: the test runner
12051244 // is broken! Fail immediately without populating `s.test_results`.
1206 return run.step.fail(maker, "test runner failed to respond for {f}", .{Io.Duration{ .nanoseconds = timeout.ns_elapsed }});
1245 return step.fail(maker, "test runner failed to respond for {f}", .{Io.Duration{ .nanoseconds = timeout.ns_elapsed }});
12071246 },
12081247 }
12091248 comptime unreachable;
......@@ -1323,27 +1362,35 @@ fn sendRunFuzzTestMessage(
13231362 }
13241363}
13251364
1326fn evalGeneric(run: *Run, maker: *Maker, spawn_options: process.SpawnOptions) !EvalGenericResult {
1365fn evalGeneric(
1366 run_index: Configuration.Step.Index,
1367 maker: *Maker,
1368 spawn_options: process.SpawnOptions,
1369) !EvalGenericResult {
13271370 const graph = maker.graph;
13281371 const io = graph.io;
1329 const arena = graph.allocator; // TODO don't leak into the process arena
1372 const arena = graph.arena; // TODO don't leak into the process arena
13301373 const gpa = maker.gpa;
1374 const conf = &maker.scanned_config.configuration;
1375 const conf_step = run_index.ptr(conf);
1376 const conf_run = conf_step.extended.get(conf.extra).run;
1377 const step = maker.stepByIndex(run_index);
13311378
13321379 var child = try process.spawn(io, spawn_options);
13331380 defer child.kill(io);
13341381
1335 switch (run.stdin) {
1382 switch (conf_run.stdin.u) {
13361383 .bytes => |bytes| {
1337 child.stdin.?.writeStreamingAll(io, bytes) catch |err| {
1338 return run.step.fail(maker, "unable to write stdin: {t}", .{err});
1384 child.stdin.?.writeStreamingAll(io, bytes.slice(conf)) catch |err| {
1385 return step.fail(maker, "failed to write stdin: {t}", .{err});
13391386 };
13401387 child.stdin.?.close(io);
13411388 child.stdin = null;
13421389 },
13431390 .lazy_path => |lazy_path| {
1344 const path = lazy_path.getPath3(graph, &run.step);
1391 const path = try maker.resolveLazyPathIndex(arena, lazy_path, run_index);
13451392 const file = path.root_dir.handle.openFile(io, path.subPathOrDot(), .{}) catch |err| {
1346 return run.step.fail(maker, "unable to open stdin file: {t}", .{err});
1393 return step.fail(maker, "failed to open stdin file: {t}", .{err});
13471394 };
13481395 defer file.close(io);
13491396 // TODO https://github.com/ziglang/zig/issues/23955
......@@ -1352,15 +1399,15 @@ fn evalGeneric(run: *Run, maker: *Maker, spawn_options: process.SpawnOptions) !E
13521399 var write_buffer: [1024]u8 = undefined;
13531400 var stdin_writer = child.stdin.?.writerStreaming(io, &write_buffer);
13541401 _ = stdin_writer.interface.sendFileAll(&file_reader, .unlimited) catch |err| switch (err) {
1355 error.ReadFailed => return run.step.fail(maker, "failed to read from {f}: {t}", .{
1402 error.ReadFailed => return step.fail(maker, "failed to read from {f}: {t}", .{
13561403 path, file_reader.err.?,
13571404 }),
1358 error.WriteFailed => return run.step.fail(maker, "failed to write to stdin: {t}", .{
1405 error.WriteFailed => return step.fail(maker, "failed to write to stdin: {t}", .{
13591406 stdin_writer.err.?,
13601407 }),
13611408 };
13621409 stdin_writer.interface.flush() catch |err| switch (err) {
1363 error.WriteFailed => return run.step.fail(maker, "failed to write to stdin: {t}", .{
1410 error.WriteFailed => return step.fail(maker, "failed to write to stdin: {t}", .{
13641411 stdin_writer.err.?,
13651412 }),
13661413 };
......@@ -1384,7 +1431,7 @@ fn evalGeneric(run: *Run, maker: *Maker, spawn_options: process.SpawnOptions) !E
13841431 const stderr_reader = multi_reader.reader(1);
13851432
13861433 while (multi_reader.fill(64, .none)) |_| {
1387 if (run.stdio_limit.toInt()) |limit| {
1434 if (conf_run.stdio_limit.value) |limit| {
13881435 if (stdout_reader.buffered().len > limit)
13891436 return error.StdoutStreamTooLong;
13901437 if (stderr_reader.buffered().len > limit)
......@@ -1404,7 +1451,8 @@ fn evalGeneric(run: *Run, maker: *Maker, spawn_options: process.SpawnOptions) !E
14041451 stderr_bytes = try multi_reader.toOwnedSlice(1);
14051452 } else {
14061453 var stdout_reader = stdout.readerStreaming(io, &.{});
1407 stdout_bytes = stdout_reader.interface.allocRemaining(arena, run.stdio_limit) catch |err| switch (err) {
1454 const stdio_limit: Io.Limit = if (conf_run.stdio_limit.value) |x| .limited(x) else .unlimited;
1455 stdout_bytes = stdout_reader.interface.allocRemaining(arena, stdio_limit) catch |err| switch (err) {
14081456 error.OutOfMemory => |e| return e,
14091457 error.ReadFailed => return stdout_reader.err.?,
14101458 error.StreamTooLong => return error.StdoutStreamTooLong,
......@@ -1412,7 +1460,8 @@ fn evalGeneric(run: *Run, maker: *Maker, spawn_options: process.SpawnOptions) !E
14121460 }
14131461 } else if (child.stderr) |stderr| {
14141462 var stderr_reader = stderr.readerStreaming(io, &.{});
1415 stderr_bytes = stderr_reader.interface.allocRemaining(arena, run.stdio_limit) catch |err| switch (err) {
1463 const stdio_limit: Io.Limit = if (conf_run.stdio_limit.value) |x| .limited(x) else .unlimited;
1464 stderr_bytes = stderr_reader.interface.allocRemaining(arena, stdio_limit) catch |err| switch (err) {
14161465 error.OutOfMemory => |e| return e,
14171466 error.ReadFailed => return stderr_reader.err.?,
14181467 error.StreamTooLong => return error.StderrStreamTooLong,
......@@ -1421,16 +1470,16 @@ fn evalGeneric(run: *Run, maker: *Maker, spawn_options: process.SpawnOptions) !E
14211470
14221471 if (stderr_bytes) |bytes| if (bytes.len > 0) {
14231472 // Treat stderr as an error message.
1424 const stderr_is_diagnostic = run.captured_stderr == null and switch (run.stdio) {
1425 .check => |checks| !checksContainStderr(checks.items),
1473 const stderr_is_diagnostic = conf_run.captured_stderr.value == null and switch (conf_run.flags.stdio) {
1474 .check => !checksContainStderr(&conf_run),
14261475 else => true,
14271476 };
14281477 if (stderr_is_diagnostic) {
1429 run.step.result_stderr = bytes;
1478 step.result_stderr = bytes;
14301479 }
14311480 };
14321481
1433 run.step.result_peak_rss = child.resource_usage_statistics.getMaxRss() orelse 0;
1482 step.result_peak_rss = child.resource_usage_statistics.getMaxRss() orelse 0;
14341483
14351484 return .{
14361485 .term = try child.wait(io),
......@@ -1452,7 +1501,7 @@ pub fn rerunInFuzzMode(
14521501) !void {
14531502 const maker = fuzz.maker;
14541503 const graph = maker.graph;
1455 const step = &run.step;
1504 const step = maker.stepByIndex(run_index);
14561505 const io = graph.io;
14571506 const arena = graph.arena; // TODO don't leak into the process arena
14581507 const gpa = maker.gpa;
......@@ -1535,9 +1584,9 @@ pub fn rerunInFuzzMode(
15351584 }
15361585 }
15371586
1538 if (run.step.result_failed_command) |cmd| {
1539 fuzz.gpa.free(cmd);
1540 run.step.result_failed_command = null;
1587 if (step.result_failed_command) |cmd| {
1588 gpa.free(cmd);
1589 step.result_failed_command = null;
15411590 }
15421591
15431592 const has_side_effects = false;
......@@ -1549,8 +1598,6 @@ pub fn rerunInFuzzMode(
15491598 });
15501599}
15511600
1552const CapturedStdIo = void; // TODO get it from Configuration
1553
15541601fn populateGeneratedPaths(
15551602 maker: *Maker,
15561603 output_placeholders: []const IndexedOutput,
......@@ -1712,142 +1759,153 @@ fn runCommand(
17121759
17131760 if (true) @panic("TODO");
17141761
1715 const opt_generic_result = spawnChildAndCollect(run_index, run, maker, progress_node, argv, &environ_map, has_side_effects, fuzz_context) catch |err| term: {
1716 // InvalidExe: cpu arch mismatch
1717 // FileNotFound: can happen with a wrong dynamic linker path
1718 if (err == error.InvalidExe or err == error.FileNotFound) interpret: {
1719 // TODO: learn the target from the binary directly rather than from
1720 // relying on it being a Compile step. This will make this logic
1721 // work even for the edge case that the binary was produced by a
1722 // third party.
1723 const exe = switch (run.argv.items[0]) {
1724 .artifact => |exe| exe.artifact,
1725 else => break :interpret,
1726 };
1727 switch (exe.kind) {
1728 .exe, .@"test" => {},
1729 else => break :interpret,
1730 }
1762 const opt_generic_result = spawnChildAndCollect(
1763 run_index,
1764 run,
1765 maker,
1766 progress_node,
1767 argv,
1768 environ_map,
1769 has_side_effects,
1770 fuzz_context,
1771 ) catch |err| term: {
1772 switch (err) {
1773 error.InvalidExe, // cpu arch mismatch
1774 error.FileNotFound, // can happen with a wrong dynamic linker path
1775 => interpret: {
1776 // TODO: learn the target from the binary directly rather than from
1777 // relying on it being a Compile step. This will make this logic
1778 // work even for the edge case that the binary was produced by a
1779 // third party.
1780 const exe = switch (run.argv.items[0]) {
1781 .artifact => |exe| exe.artifact,
1782 else => break :interpret,
1783 };
1784 switch (exe.kind) {
1785 .exe, .@"test" => {},
1786 else => break :interpret,
1787 }
17311788
1732 const root_target = exe.rootModuleTarget();
1733 const need_cross_libc = exe.is_linking_libc and
1734 (root_target.isGnuLibC() or (root_target.isMuslLibC() and exe.linkage == .dynamic));
1735 const other_target = exe.root_module.resolved_target.?.result;
1736 switch (std.zig.system.getExternalExecutor(io, &graph.host.result, &other_target, .{
1737 .qemu_fixes_dl = need_cross_libc and graph.libc_runtimes_dir != null,
1738 .link_libc = exe.is_linking_libc,
1739 })) {
1740 .native, .rosetta => {
1741 if (allow_skip) return error.MakeSkipped;
1742 break :interpret;
1743 },
1744 .wine => |bin_name| {
1745 if (graph.enable_wine) {
1746 try interp_argv.append(bin_name);
1747 try interp_argv.appendSlice(argv);
1748
1749 // Wine's excessive stderr logging is only situationally helpful. Disable it by default, but
1750 // allow the user to override it (e.g. with `WINEDEBUG=err+all`) if desired.
1751 if (environ_map.get("WINEDEBUG") == null) {
1752 try environ_map.put("WINEDEBUG", "-all");
1789 const root_target = exe.rootModuleTarget();
1790 const need_cross_libc = exe.is_linking_libc and
1791 (root_target.isGnuLibC() or (root_target.isMuslLibC() and exe.linkage == .dynamic));
1792 const other_target = exe.root_module.resolved_target.?.result;
1793 switch (std.zig.system.getExternalExecutor(io, &graph.host.result, &other_target, .{
1794 .qemu_fixes_dl = need_cross_libc and graph.libc_runtimes_dir != null,
1795 .link_libc = exe.is_linking_libc,
1796 })) {
1797 .native, .rosetta => {
1798 if (allow_skip) return error.MakeSkipped;
1799 break :interpret;
1800 },
1801 .wine => |bin_name| {
1802 if (graph.enable_wine) {
1803 try interp_argv.append(bin_name);
1804 try interp_argv.appendSlice(argv);
1805
1806 // Wine's excessive stderr logging is only situationally helpful. Disable it by default, but
1807 // allow the user to override it (e.g. with `WINEDEBUG=err+all`) if desired.
1808 if (environ_map.get("WINEDEBUG") == null) {
1809 try environ_map.put("WINEDEBUG", "-all");
1810 }
1811 } else {
1812 return failForeign(conf_run, maker, run_index, "-fwine", argv[0], exe);
17531813 }
1754 } else {
1755 return failForeign(run, "-fwine", argv[0], exe);
1756 }
1757 },
1758 .qemu => |bin_name| {
1759 if (graph.enable_qemu) {
1760 try interp_argv.append(bin_name);
1761
1762 if (need_cross_libc) {
1763 if (graph.libc_runtimes_dir) |dir| {
1764 try interp_argv.append("-L");
1765 try interp_argv.append(try Dir.path.join(arena, &.{
1766 dir,
1767 try if (root_target.isGnuLibC()) std.zig.target.glibcRuntimeTriple(
1768 arena,
1769 root_target.cpu.arch,
1770 root_target.os.tag,
1771 root_target.abi,
1772 ) else if (root_target.isMuslLibC()) std.zig.target.muslRuntimeTriple(
1773 arena,
1774 root_target.cpu.arch,
1775 root_target.abi,
1776 ) else unreachable,
1777 }));
1778 } else return failForeign(run, "--libc-runtimes", argv[0], exe);
1814 },
1815 .qemu => |bin_name| {
1816 if (graph.enable_qemu) {
1817 try interp_argv.append(bin_name);
1818
1819 if (need_cross_libc) {
1820 if (graph.libc_runtimes_dir) |dir| {
1821 try interp_argv.append("-L");
1822 try interp_argv.append(try Dir.path.join(arena, &.{
1823 dir,
1824 try if (root_target.isGnuLibC()) std.zig.target.glibcRuntimeTriple(
1825 arena,
1826 root_target.cpu.arch,
1827 root_target.os.tag,
1828 root_target.abi,
1829 ) else if (root_target.isMuslLibC()) std.zig.target.muslRuntimeTriple(
1830 arena,
1831 root_target.cpu.arch,
1832 root_target.abi,
1833 ) else unreachable,
1834 }));
1835 } else return failForeign(conf_run, maker, run_index, "--libc-runtimes", argv[0], exe);
1836 }
1837
1838 try interp_argv.appendSlice(argv);
1839 } else return failForeign(conf_run, maker, run_index, "-fqemu", argv[0], exe);
1840 },
1841 .darling => |bin_name| {
1842 if (graph.enable_darling) {
1843 try interp_argv.append(bin_name);
1844 try interp_argv.appendSlice(argv);
1845 } else {
1846 return failForeign(conf_run, maker, run_index, "-fdarling", argv[0], exe);
1847 }
1848 },
1849 .wasmtime => |bin_name| {
1850 if (graph.enable_wasmtime) {
1851 try interp_argv.append(bin_name);
1852 try interp_argv.append("--dir=.");
1853 // Wasmtime doeesn't inherit environment variables from the parent process
1854 // by default. '-S inherit-env' was added in Wasmtime version 20.
1855 try interp_argv.append("-Sinherit-env");
1856 try interp_argv.append(argv[0]);
1857 try interp_argv.appendSlice(argv[1..]);
1858 } else {
1859 return failForeign(conf_run, maker, run_index, "-fwasmtime", argv[0], exe);
17791860 }
1861 },
1862 .bad_dl => |foreign_dl| {
1863 if (allow_skip) return error.MakeSkipped;
17801864
1781 try interp_argv.appendSlice(argv);
1782 } else return failForeign(run, "-fqemu", argv[0], exe);
1783 },
1784 .darling => |bin_name| {
1785 if (graph.enable_darling) {
1786 try interp_argv.append(bin_name);
1787 try interp_argv.appendSlice(argv);
1788 } else {
1789 return failForeign(run, "-fdarling", argv[0], exe);
1790 }
1791 },
1792 .wasmtime => |bin_name| {
1793 if (graph.enable_wasmtime) {
1794 try interp_argv.append(bin_name);
1795 try interp_argv.append("--dir=.");
1796 // Wasmtime doeesn't inherit environment variables from the parent process
1797 // by default. '-S inherit-env' was added in Wasmtime version 20.
1798 try interp_argv.append("-Sinherit-env");
1799 try interp_argv.append(argv[0]);
1800 try interp_argv.appendSlice(argv[1..]);
1801 } else {
1802 return failForeign(run, "-fwasmtime", argv[0], exe);
1803 }
1804 },
1805 .bad_dl => |foreign_dl| {
1806 if (allow_skip) return error.MakeSkipped;
1865 const host_dl = graph.host.result.dynamic_linker.get() orelse "(none)";
18071866
1808 const host_dl = graph.host.result.dynamic_linker.get() orelse "(none)";
1867 return step.fail(maker,
1868 \\the host system is unable to execute binaries from the target
1869 \\ because the host dynamic linker is '{s}',
1870 \\ while the target dynamic linker is '{s}'.
1871 \\ consider setting the dynamic linker or enabling skip_foreign_checks in the Run step
1872 , .{ host_dl, foreign_dl });
1873 },
1874 .bad_os_or_cpu => {
1875 if (allow_skip) return error.MakeSkipped;
18091876
1810 return step.fail(maker,
1811 \\the host system is unable to execute binaries from the target
1812 \\ because the host dynamic linker is '{s}',
1813 \\ while the target dynamic linker is '{s}'.
1814 \\ consider setting the dynamic linker or enabling skip_foreign_checks in the Run step
1815 , .{ host_dl, foreign_dl });
1816 },
1817 .bad_os_or_cpu => {
1818 if (allow_skip) return error.MakeSkipped;
1819
1820 const host_name = try graph.host.result.zigTriple(arena);
1821 const foreign_name = try root_target.zigTriple(arena);
1822
1823 return step.fail(maker, "the host system ({s}) is unable to execute binaries from the target ({s})", .{
1824 host_name, foreign_name,
1825 });
1826 },
1827 }
1877 const host_name = try graph.host.result.zigTriple(arena);
1878 const foreign_name = try root_target.zigTriple(arena);
18281879
1829 if (root_target.os.tag == .windows) {
1830 // On Windows we don't have rpaths so we have to add .dll search paths to PATH
1831 addPathForDynLibs(exe);
1832 }
1880 return step.fail(maker, "the host system ({s}) is unable to execute binaries from the target ({s})", .{
1881 host_name, foreign_name,
1882 });
1883 },
1884 }
18331885
1834 gpa.free(step.result_failed_command.?);
1835 step.result_failed_command = null;
1836 try Step.handleVerbose(step.owner, cwd, run.environ_map, interp_argv.items);
1886 if (root_target.os.tag == .windows) {
1887 // On Windows we don't have rpaths so we have to add .dll search paths to PATH
1888 addPathForDynLibs(exe);
1889 }
18371890
1838 break :term spawnChildAndCollect(run_index, run, maker, progress_node, interp_argv.items, &environ_map, has_side_effects, fuzz_context) catch |e| {
1839 if (!run.failing_to_execute_foreign_is_an_error) return error.MakeSkipped;
1840 if (e == error.MakeFailed) return error.MakeFailed; // error already reported
1841 return step.fail(maker, "unable to spawn interpreter {s}: {t}", .{ interp_argv.items[0], e });
1842 };
1843 }
1844 if (err == error.MakeFailed) return error.MakeFailed; // error already reported
1891 gpa.free(step.result_failed_command.?);
1892 step.result_failed_command = null;
1893 try graph.handleVerbose(cwd, run.environ_map, interp_argv.items);
18451894
1895 break :term spawnChildAndCollect(run_index, run, maker, progress_node, interp_argv.items, &environ_map, has_side_effects, fuzz_context) catch |e| {
1896 if (!run.failing_to_execute_foreign_is_an_error) return error.MakeSkipped;
1897 if (e == error.MakeFailed) return error.MakeFailed; // error already reported
1898 return step.fail(maker, "unable to spawn interpreter {s}: {t}", .{ interp_argv.items[0], e });
1899 };
1900 },
1901 error.MakeFailed, error.OutOfMemory, error.Canceled => |e| return e,
1902 else => {},
1903 }
18461904 return step.fail(maker, "failed to spawn and capture stdio from {s}: {t}", .{ argv[0], err });
18471905 };
18481906
18491907 const generic_result = opt_generic_result orelse {
1850 assert(run.stdio == .zig_test);
1908 assert(conf_run.flags.stdio == .zig_test);
18511909 // Specific errors have already been reported, and test results are populated. All we need
18521910 // to do is report step failure if any test failed.
18531911 if (!step.test_results.isSuccess()) return error.MakeFailed;
......@@ -1855,20 +1913,20 @@ fn runCommand(
18551913 };
18561914
18571915 assert(fuzz_context == null);
1858 assert(run.stdio != .zig_test);
1916 assert(conf_run.flags.stdio != .zig_test);
18591917
18601918 // Capture stdout and stderr to GeneratedFile objects.
18611919 const Stream = struct {
1862 captured: ?*CapturedStdIo,
1920 captured: ?Configuration.Step.Run.CapturedStream,
18631921 bytes: ?[]const u8,
18641922 };
18651923 for ([_]Stream{
18661924 .{
1867 .captured = run.captured_stdout,
1925 .captured = conf_run.captured_stdout.value,
18681926 .bytes = generic_result.stdout,
18691927 },
18701928 .{
1871 .captured = run.captured_stderr,
1929 .captured = conf_run.captured_stderr.value,
18721930 .bytes = generic_result.stderr,
18731931 },
18741932 }) |stream| {
......@@ -1898,7 +1956,7 @@ fn runCommand(
18981956 }
18991957 }
19001958
1901 switch (run.stdio) {
1959 switch (conf_run.flags.stdio) {
19021960 .zig_test => unreachable,
19031961 .check => |checks| for (checks.items) |check| switch (check) {
19041962 .expect_stderr_exact => |expected_bytes| {
......@@ -1970,7 +2028,7 @@ fn runCommand(
19702028 };
19712029 if (bad_exit) {
19722030 if (generic_result.stderr) |bytes| {
1973 run.step.result_stderr = bytes;
2031 step.result_stderr = bytes;
19742032 }
19752033 }
19762034
......@@ -1995,9 +2053,8 @@ fn spawnChildAndCollect(
19952053 has_side_effects: bool,
19962054 fuzz_context: ?FuzzContext,
19972055) !?EvalGenericResult {
1998 const step = run.step;
2056 const step = maker.stepByIndex(run_index);
19992057 const graph = maker.graph;
2000 const gpa = maker.gpa;
20012058 const io = graph.io;
20022059 const arena = graph.arena; // TODO don't leak into process arena
20032060 const conf = &maker.scanned_config.configuration;
......@@ -2006,17 +2063,17 @@ fn spawnChildAndCollect(
20062063
20072064 if (fuzz_context != null) {
20082065 assert(!has_side_effects);
2009 assert(run.stdio == .zig_test);
2066 assert(conf_run.flags.stdio == .zig_test);
20102067 }
20112068
2012 const child_cwd: process.Child.Cwd = if (conf_run.cwd) |lazy_cwd|
2069 const child_cwd: process.Child.Cwd = if (conf_run.cwd.value) |lazy_cwd|
20132070 .{ .path = try maker.resolveLazyPathIndexAbs(arena, lazy_cwd, run_index) }
20142071 else
20152072 .inherit;
20162073
20172074 // If an error occurs, it's caused by this command:
20182075 assert(step.result_failed_command == null);
2019 step.result_failed_command = try Step.allocPrintCmd(gpa, child_cwd, .{
2076 step.result_failed_command = try std.zig.allocPrintCmd(arena, child_cwd, .{
20202077 .child = environ_map,
20212078 .parent = &graph.environ_map,
20222079 }, argv);
......@@ -2028,22 +2085,22 @@ fn spawnChildAndCollect(
20282085 .cwd = child_cwd,
20292086 .environ_map = environ_map,
20302087 .request_resource_usage_statistics = true,
2031 .stdin = if (run.stdin != .none) s: {
2032 assert(run.stdio != .inherit);
2088 .stdin = if (conf_run.stdin.u != .none) s: {
2089 assert(conf_run.flags.stdio != .inherit);
20332090 break :s .pipe;
2034 } else switch (run.stdio) {
2091 } else switch (conf_run.flags.stdio) {
20352092 .infer_from_args => if (has_side_effects) .inherit else .ignore,
20362093 .inherit => .inherit,
20372094 .check => .ignore,
20382095 .zig_test => .pipe,
20392096 },
2040 .stdout = if (run.captured_stdout != null) .pipe else switch (run.stdio) {
2097 .stdout = if (conf_run.captured_stdout.value != null) .pipe else switch (conf_run.flags.stdio) {
20412098 .infer_from_args => if (has_side_effects) .inherit else .ignore,
20422099 .inherit => .inherit,
2043 .check => |checks| if (checksContainStdout(checks.items)) .pipe else .ignore,
2100 .check => if (checksContainStdout(&conf_run)) .pipe else .ignore,
20442101 .zig_test => .pipe,
20452102 },
2046 .stderr = if (run.captured_stderr != null) .pipe else switch (run.stdio) {
2103 .stderr = if (conf_run.captured_stderr.value != null) .pipe else switch (conf_run.flags.stdio) {
20472104 .infer_from_args => if (has_side_effects) .inherit else .pipe,
20482105 .inherit => .inherit,
20492106 .check => .pipe,
......@@ -2051,9 +2108,9 @@ fn spawnChildAndCollect(
20512108 },
20522109 };
20532110
2054 if (run.stdio == .zig_test) {
2111 if (conf_run.flags.stdio == .zig_test) {
20552112 const started: Io.Clock.Timestamp = .now(io, .awake);
2056 const result = evalZigTest(run, maker, progress_node, spawn_options, fuzz_context) catch |err| switch (err) {
2113 const result = evalZigTest(run, run_index, maker, progress_node, spawn_options, fuzz_context) catch |err| switch (err) {
20572114 error.Canceled => |e| return e,
20582115 else => |e| e,
20592116 };
......@@ -2062,7 +2119,7 @@ fn spawnChildAndCollect(
20622119 return null;
20632120 } else {
20642121 const inherit = spawn_options.stdout == .inherit or spawn_options.stderr == .inherit;
2065 if (!run.disable_zig_progress and !inherit) {
2122 if (!conf_run.flags.disable_zig_progress and !inherit) {
20662123 spawn_options.progress_node = progress_node;
20672124 }
20682125 const terminal_mode: Io.Terminal.Mode = if (inherit) m: {
......@@ -2070,10 +2127,10 @@ fn spawnChildAndCollect(
20702127 break :m stderr.terminal_mode;
20712128 } else .no_color;
20722129 defer if (inherit) io.unlockStderr();
2073 try setColorEnvironmentVariables(run, environ_map, terminal_mode);
2130 try setColorEnvironmentVariables(&conf_run, environ_map, terminal_mode);
20742131
20752132 const started: Io.Clock.Timestamp = .now(io, .awake);
2076 const result = evalGeneric(run, maker, spawn_options) catch |err| switch (err) {
2133 const result = evalGeneric(run_index, maker, spawn_options) catch |err| switch (err) {
20772134 error.Canceled => |e| return e,
20782135 else => |e| e,
20792136 };
......@@ -2106,8 +2163,12 @@ fn termMatches(expected: ?process.Child.Term, actual: process.Child.Term) bool {
21062163 };
21072164}
21082165
2109fn setColorEnvironmentVariables(run: *Run, environ_map: *EnvMap, terminal_mode: Io.Terminal.Mode) !void {
2110 color: switch (run.color) {
2166fn setColorEnvironmentVariables(
2167 conf_run: *const Configuration.Step.Run,
2168 environ_map: *EnvMap,
2169 terminal_mode: Io.Terminal.Mode,
2170) !void {
2171 color: switch (conf_run.flags.color) {
21112172 .manual => {},
21122173 .enable => {
21132174 try environ_map.put("CLICOLOR_FORCE", "1");
......@@ -2122,8 +2183,8 @@ fn setColorEnvironmentVariables(run: *Run, environ_map: *EnvMap, terminal_mode:
21222183 .escape_codes => continue :color .enable,
21232184 },
21242185 .auto => {
2125 const capture_stderr = run.captured_stderr != null or switch (run.stdio) {
2126 .check => |checks| checksContainStderr(checks.items),
2186 const capture_stderr = conf_run.captured_stderr.value != null or switch (conf_run.flags.stdio) {
2187 .check => checksContainStderr(conf_run),
21272188 .infer_from_args, .inherit, .zig_test => false,
21282189 };
21292190 if (capture_stderr) {
......@@ -2135,53 +2196,12 @@ fn setColorEnvironmentVariables(run: *Run, environ_map: *EnvMap, terminal_mode:
21352196 }
21362197}
21372198
2138fn checksContainStdout(checks: []const @This().StdIo.Check) bool {
2139 for (checks) |check| switch (check) {
2140 .expect_stderr_exact,
2141 .expect_stderr_match,
2142 .expect_term,
2143 => continue,
2144
2145 .expect_stdout_exact,
2146 .expect_stdout_match,
2147 => return true,
2148 };
2149 return false;
2150}
2151
2152fn checksContainStderr(checks: []const @This().StdIo.Check) bool {
2153 for (checks) |check| switch (check) {
2154 .expect_stdout_exact,
2155 .expect_stdout_match,
2156 .expect_term,
2157 => continue,
2158
2159 .expect_stderr_exact,
2160 .expect_stderr_match,
2161 => return true,
2162 };
2163 return false;
2164}
2165
2166/// Returns whether the Run step has side effects *other than* updating the output arguments.
2167fn hasSideEffects(run: Run) bool {
2168 if (run.has_side_effects) return true;
2169 return switch (run.stdio) {
2170 .infer_from_args => !run.hasAnyOutputArgs(),
2171 .inherit => true,
2172 .check => false,
2173 .zig_test => false,
2174 };
2199fn checksContainStdout(conf_run: *const Configuration.Step.Run) bool {
2200 return conf_run.expect_stdout_exact.value != null or conf_run.expect_stdout_match.slice.len != 0;
21752201}
21762202
2177fn hasAnyOutputArgs(run: Run) bool {
2178 if (run.captured_stdout != null) return true;
2179 if (run.captured_stderr != null) return true;
2180 for (run.argv.items) |arg| switch (arg) {
2181 .output_file, .output_directory => return true,
2182 else => continue,
2183 };
2184 return false;
2203fn checksContainStderr(conf_run: *const Configuration.Step.Run) bool {
2204 return conf_run.expect_stderr_exact.value != null or conf_run.expect_stderr_match.slice.len != 0;
21852205}
21862206
21872207/// If `path` is cwd-relative, make it relative to the cwd of the child instead.
......@@ -2225,13 +2245,13 @@ fn addPathForDynLibs(artifact: Configuration.Step.Index) void {
22252245 compile.isDynamicLibrary())
22262246 {
22272247 @panic("TODO");
2228 //addPathDir(run, Dir.path.dirname(compile.getEmittedBin().getPath2(b, &run.step)).?);
2248 //addPathDir(run, Dir.path.dirname(compile.getEmittedBin().getPath2(b, step)).?);
22292249 }
22302250 }
22312251}
22322252
22332253fn failForeign(
2234 run: *Run,
2254 conf_run: *const Configuration.Step.Run,
22352255 maker: *Maker,
22362256 step_index: Configuration.Step.Index,
22372257 suggested_flag: []const u8,
......@@ -2239,9 +2259,9 @@ fn failForeign(
22392259 exe: *Step.Compile,
22402260) Step.ExtendedMakeError {
22412261 const step = maker.stepByIndex(step_index);
2242 switch (run.stdio) {
2262 switch (conf_run.flags.stdio) {
22432263 .check, .zig_test => {
2244 if (run.skip_foreign_checks) return error.MakeSkipped;
2264 if (conf_run.flags.skip_foreign_checks) return error.MakeSkipped;
22452265
22462266 const graph = maker.graph;
22472267 const process_arena = graph.arena; // TODO don't leak into process arena