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;...@@ -18,10 +18,10 @@ const assert = std.debug.assert;
18const WebServer = @import("WebServer.zig");18const WebServer = @import("WebServer.zig");
19const Maker = @import("../Maker.zig");19const Maker = @import("../Maker.zig");
2020
21const Compile = @import("Step/Compile.zig");21pub const Compile = @import("Step/Compile.zig");
22const Run = @import("Step/Run.zig");22pub const Run = @import("Step/Run.zig");
23const InstallArtifact = @import("Step/InstallArtifact.zig");23pub const InstallArtifact = @import("Step/InstallArtifact.zig");
24const InstallFile = @import("Step/InstallFile.zig");24pub const InstallFile = @import("Step/InstallFile.zig");
2525
26/// Avoid false sharing.26/// Avoid false sharing.
27_: void align(std.atomic.cache_line) = {},27_: void align(std.atomic.cache_line) = {},
lib/compiler/Maker/Step/Run.zig+326-306
...@@ -325,9 +325,10 @@ pub fn make(...@@ -325,9 +325,10 @@ pub fn make(
325/// * The wait fails, indicating the child closed stdout and stderr325/// * The wait fails, indicating the child closed stdout and stderr
326fn waitZigTest(326fn waitZigTest(
327 run: *Run,327 run: *Run,
328 run_index: Configuration.Step.Index,
328 maker: *Maker,329 maker: *Maker,
329 child: *process.Child,330 child: *process.Child,
330 options: Step.MakeOptions,331 progress_node: std.Progress.Node,
331 multi_reader: *Io.File.MultiReader,332 multi_reader: *Io.File.MultiReader,
332 opt_metadata: *?TestMetadata,333 opt_metadata: *?TestMetadata,
333 results: *Step.TestResults,334 results: *Step.TestResults,
...@@ -342,9 +343,11 @@ fn waitZigTest(...@@ -342,9 +343,11 @@ fn waitZigTest(
342 ns_elapsed: u64,343 ns_elapsed: u64,
343 },344 },
344} {345} {
345 const gpa = run.step.owner.allocator;346 const graph = maker.graph;
346 const arena = run.step.owner.allocator;347 const gpa = maker.gpa;
347 const io = run.step.owner.graph.io;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
349 var sub_prog_node: ?std.Progress.Node = null;352 var sub_prog_node: ?std.Progress.Node = null;
350 defer if (sub_prog_node) |n| n.end();353 defer if (sub_prog_node) |n| n.end();
...@@ -367,10 +370,10 @@ fn waitZigTest(...@@ -367,10 +370,10 @@ fn waitZigTest(
367 // start and it acknowledging the test starting, we terminate the child and raise an error. This370 // start and it acknowledging the test starting, we terminate the child and raise an error. This
368 // *should* never happen, but could in theory be caused by some very unlucky IB in a test.371 // *should* never happen, but could in theory be caused by some very unlucky IB in a test.
369 const response_timeout: Io.Clock.Duration = t: {372 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);
371 break :t .{ .clock = .awake, .raw = .fromNanoseconds(ns) };374 break :t .{ .clock = .awake, .raw = .fromNanoseconds(ns) };
372 };375 };
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| .{
374 .clock = .awake,377 .clock = .awake,
375 .raw = .fromNanoseconds(ns),378 .raw = .fromNanoseconds(ns),
376 } else null;379 } else null;
...@@ -428,7 +431,7 @@ fn waitZigTest(...@@ -428,7 +431,7 @@ fn waitZigTest(
428 var body_r: std.Io.Reader = .fixed(body);431 var body_r: std.Io.Reader = .fixed(body);
429 switch (header.tag) {432 switch (header.tag) {
430 .zig_version => {433 .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(
432 maker,435 maker,
433 "zig version mismatch build runner vs compiler: '{s}' vs '{s}'",436 "zig version mismatch build runner vs compiler: '{s}' vs '{s}'",
434 .{ builtin.zig_version_string, body },437 .{ builtin.zig_version_string, body },
...@@ -451,14 +454,14 @@ fn waitZigTest(...@@ -451,14 +454,14 @@ fn waitZigTest(
451454
452 const string_bytes = body_r.take(tm_hdr.string_bytes_len) catch unreachable;455 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);
455 opt_metadata.* = .{458 opt_metadata.* = .{
456 .string_bytes = try arena.dupe(u8, string_bytes),459 .string_bytes = try arena.dupe(u8, string_bytes),
457 .ns_per_test = try arena.alloc(u64, results.test_count),460 .ns_per_test = try arena.alloc(u64, results.test_count),
458 .names = names,461 .names = names,
459 .expected_panic_msgs = expected_panic_msgs,462 .expected_panic_msgs = expected_panic_msgs,
460 .next_index = 0,463 .next_index = 0,
461 .prog_node = options.progress_node,464 .prog_node = progress_node,
462 };465 };
463 @memset(opt_metadata.*.?.ns_per_test, std.math.maxInt(u64));466 @memset(opt_metadata.*.?.ns_per_test, std.math.maxInt(u64));
464467
...@@ -494,20 +497,20 @@ fn waitZigTest(...@@ -494,20 +497,20 @@ fn waitZigTest(
494 const stderr_bytes = std.mem.trim(u8, stderr.buffered(), "\n");497 const stderr_bytes = std.mem.trim(u8, stderr.buffered(), "\n");
495 stderr.tossBuffered();498 stderr.tossBuffered();
496 if (stderr_bytes.len == 0) {499 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});
498 } else {501 } 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 });
500 }503 }
501 } else if (leak_count > 0) {504 } else if (leak_count > 0) {
502 const name = md.testName(tr_hdr.index);505 const name = md.testName(tr_hdr.index);
503 const stderr_bytes = std.mem.trim(u8, stderr.buffered(), "\n");506 const stderr_bytes = std.mem.trim(u8, stderr.buffered(), "\n");
504 stderr.tossBuffered();507 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 });
506 } else if (log_err_count > 0) {509 } else if (log_err_count > 0) {
507 const name = md.testName(tr_hdr.index);510 const name = md.testName(tr_hdr.index);
508 const stderr_bytes = std.mem.trim(u8, stderr.buffered(), "\n");511 const stderr_bytes = std.mem.trim(u8, stderr.buffered(), "\n");
509 stderr.tossBuffered();512 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 });
511 }514 }
512515
513 active_test_index = null;516 active_test_index = null;
...@@ -525,6 +528,7 @@ fn waitZigTest(...@@ -525,6 +528,7 @@ fn waitZigTest(
525528
526const FuzzTestRunner = struct {529const FuzzTestRunner = struct {
527 run: *Run,530 run: *Run,
531 run_index: Configuration.Step.Index,
528 ctx: FuzzContext,532 ctx: FuzzContext,
529 coverage_id: ?u64,533 coverage_id: ?u64,
530534
...@@ -572,16 +576,18 @@ const FuzzTestRunner = struct {...@@ -572,16 +576,18 @@ const FuzzTestRunner = struct {
572576
573 fn init(577 fn init(
574 run: *Run,578 run: *Run,
579 run_index: Configuration.Step.Index,
575 ctx: FuzzContext,580 ctx: FuzzContext,
576 progress_node: std.Progress.Node,581 progress_node: std.Progress.Node,
577 spawn_options: process.SpawnOptions,582 spawn_options: process.SpawnOptions,
578 ) !FuzzTestRunner {583 ) !FuzzTestRunner {
579 const step_owner = run.step.owner;584 const maker = ctx.fuzz.maker;
580 const gpa = step_owner.allocator;585 const graph = maker.graph;
581 const io = step_owner.graph.io;586 const gpa = maker.gpa;
587 const io = graph.io;
582588
583 const n_instances = switch (ctx.fuzz.mode) {589 const n_instances = switch (ctx.fuzz.mode) {
584 .forever => step_owner.graph.max_jobs orelse @min(590 .forever => graph.max_jobs orelse @min(
585 std.Thread.getCpuCount() catch 1,591 std.Thread.getCpuCount() catch 1,
586 (std.math.maxInt(u32) - 2) / 3,592 (std.math.maxInt(u32) - 2) / 3,
587 ),593 ),
...@@ -613,6 +619,7 @@ const FuzzTestRunner = struct {...@@ -613,6 +619,7 @@ const FuzzTestRunner = struct {
613619
614 return .{620 return .{
615 .run = run,621 .run = run,
622 .run_index = run_index,
616 .ctx = ctx,623 .ctx = ctx,
617 .coverage_id = null,624 .coverage_id = null,
618625
...@@ -625,9 +632,13 @@ const FuzzTestRunner = struct {...@@ -625,9 +632,13 @@ const FuzzTestRunner = struct {
625 }632 }
626633
627 fn deinit(f: *FuzzTestRunner) void {634 fn deinit(f: *FuzzTestRunner) void {
628 const step_owner = f.run.step.owner;635 const maker = f.ctx.fuzz.maker;
629 const gpa = step_owner.allocator;636 const run_index = f.run_index;
630 const io = step_owner.graph.io;637
638 const graph = maker.graph;
639 const gpa = maker.gpa;
640 const io = graph.io;
641 const step = maker.stepByIndex(run_index);
631642
632 f.batch.cancel(io);643 f.batch.cancel(io);
633 gpa.free(f.batch.storage);644 gpa.free(f.batch.storage);
...@@ -639,13 +650,18 @@ const FuzzTestRunner = struct {...@@ -639,13 +650,18 @@ const FuzzTestRunner = struct {
639 instance.progress_node.end();650 instance.progress_node.end();
640 total_rss += instance.child.resource_usage_statistics.getMaxRss() orelse 0;651 total_rss += instance.child.resource_usage_statistics.getMaxRss() orelse 0;
641 }652 }
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);
643 gpa.free(f.instances);654 gpa.free(f.instances);
644 }655 }
645656
646 fn startInstances(f: *FuzzTestRunner) !void {657 fn startInstances(f: *FuzzTestRunner) !void {
647 const step_owner = f.run.step.owner;658 const maker = f.ctx.fuzz.maker;
648 const io = step_owner.graph.io;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
650 for (0.., f.instances) |id, *instance| {666 for (0.., f.instances) |id, *instance| {
651 const id32: u32 = @intCast(id);667 const id32: u32 = @intCast(id);
...@@ -653,14 +669,14 @@ const FuzzTestRunner = struct {...@@ -653,14 +669,14 @@ const FuzzTestRunner = struct {
653 .forever => sendRunFuzzTestMessage(669 .forever => sendRunFuzzTestMessage(
654 io,670 io,
655 instance.child.stdin.?,671 instance.child.stdin.?,
656 f.run.fuzz_tests.items,672 run.fuzz_tests.items,
657 .forever,673 .forever,
658 id32,674 id32,
659 ),675 ),
660 .limit => |limit| sendRunFuzzTestMessage(676 .limit => |limit| sendRunFuzzTestMessage(
661 io,677 io,
662 instance.child.stdin.?,678 instance.child.stdin.?,
663 f.run.fuzz_tests.items,679 run.fuzz_tests.items,
664 .iterations,680 .iterations,
665 limit.amount,681 limit.amount,
666 ),682 ),
...@@ -670,7 +686,8 @@ const FuzzTestRunner = struct {...@@ -670,7 +686,8 @@ const FuzzTestRunner = struct {
670 instance.child.stdin.?.close(io);686 instance.child.stdin.?.close(io);
671 instance.child.stdin = null;687 instance.child.stdin = null;
672 const term = try instance.child.wait(io);688 const term = try instance.child.wait(io);
673 return f.run.step.fail(689 return step.fail(
690 maker,
674 "unable to write stdin ({t}); test process unexpectedly {f}",691 "unable to write stdin ({t}); test process unexpectedly {f}",
675 .{ write_err, fmtTerm(term) },692 .{ write_err, fmtTerm(term) },
676 );693 );
...@@ -682,8 +699,9 @@ const FuzzTestRunner = struct {...@@ -682,8 +699,9 @@ const FuzzTestRunner = struct {
682 }699 }
683700
684 fn listen(f: *FuzzTestRunner) !void {701 fn listen(f: *FuzzTestRunner) !void {
685 const step_owner = f.run.step.owner;702 const maker = f.ctx.fuzz.maker;
686 const io = step_owner.graph.io;703 const graph = maker.graph;
704 const io = graph.io;
687705
688 while (true) {706 while (true) {
689 try f.batch.awaitConcurrent(io, .none);707 try f.batch.awaitConcurrent(io, .none);
...@@ -714,10 +732,15 @@ const FuzzTestRunner = struct {...@@ -714,10 +732,15 @@ const FuzzTestRunner = struct {
714 }732 }
715733
716 fn completeStdoutRead(f: *FuzzTestRunner, id: u32, n: usize) !void {734 fn completeStdoutRead(f: *FuzzTestRunner, id: u32, n: usize) !void {
717 const step_owner = f.run.step.owner;735 const maker = f.ctx.fuzz.maker;
718 const gpa = step_owner.allocator;
719 const io = step_owner.graph.io;
720 const instance = &f.instances[id];736 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
722 instance.message.items.len += n;745 instance.message.items.len += n;
723 const total_read = instance.message.items.len;746 const total_read = instance.message.items.len;
...@@ -735,7 +758,8 @@ const FuzzTestRunner = struct {...@@ -735,7 +758,8 @@ const FuzzTestRunner = struct {
735758
736 switch (header.tag) {759 switch (header.tag) {
737 .zig_version => {760 .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,
739 "zig version mismatch build runner vs compiler: '{s}' vs '{s}'",763 "zig version mismatch build runner vs compiler: '{s}' vs '{s}'",
740 .{ builtin.zig_version_string, body },764 .{ builtin.zig_version_string, body },
741 );765 );
...@@ -750,14 +774,14 @@ const FuzzTestRunner = struct {...@@ -750,14 +774,14 @@ const FuzzTestRunner = struct {
750 const fuzz = f.ctx.fuzz;774 const fuzz = f.ctx.fuzz;
751 fuzz.queue_mutex.lockUncancelable(io);775 fuzz.queue_mutex.lockUncancelable(io);
752 defer fuzz.queue_mutex.unlock(io);776 defer fuzz.queue_mutex.unlock(io);
753 try fuzz.msg_queue.append(fuzz.gpa, .{ .coverage = .{777 try fuzz.msg_queue.append(gpa, .{ .coverage = .{
754 .id = f.coverage_id.?,778 .id = f.coverage_id.?,
755 .cumulative = .{779 .cumulative = .{
756 .runs = cumulative_runs,780 .runs = cumulative_runs,
757 .unique = cumulative_unique,781 .unique = cumulative_unique,
758 .coverage = cumulative_coverage,782 .coverage = cumulative_coverage,
759 },783 },
760 .run = f.run,784 .run = run_index,
761 } });785 } });
762 fuzz.queue_cond.signal(io);786 fuzz.queue_cond.signal(io);
763 },787 },
...@@ -768,7 +792,7 @@ const FuzzTestRunner = struct {...@@ -768,7 +792,7 @@ const FuzzTestRunner = struct {
768792
769 fuzz.queue_mutex.lockUncancelable(io);793 fuzz.queue_mutex.lockUncancelable(io);
770 defer fuzz.queue_mutex.unlock(io);794 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 = .{
772 .addr = addr,796 .addr = addr,
773 .coverage_id = f.coverage_id.?,797 .coverage_id = f.coverage_id.?,
774 } });798 } });
...@@ -776,7 +800,7 @@ const FuzzTestRunner = struct {...@@ -776,7 +800,7 @@ const FuzzTestRunner = struct {
776 },800 },
777 .fuzz_test_change => {801 .fuzz_test_change => {
778 const test_i = std.mem.readInt(u32, body[0..4], .little);802 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]);
780 },804 },
781 .broadcast_fuzz_input => {805 .broadcast_fuzz_input => {
782 if (f.instances.len == 1) {806 if (f.instances.len == 1) {
...@@ -823,8 +847,8 @@ const FuzzTestRunner = struct {...@@ -823,8 +847,8 @@ const FuzzTestRunner = struct {
823 }847 }
824848
825 fn addStdoutRead(f: *FuzzTestRunner, id: u32, end: usize) !void {849 fn addStdoutRead(f: *FuzzTestRunner, id: u32, end: usize) !void {
826 const step_owner = f.run.step.owner;850 const maker = f.ctx.fuzz.maker;
827 const gpa = step_owner.allocator;851 const gpa = maker.gpa;
828 const instance = &f.instances[id];852 const instance = &f.instances[id];
829853
830 try instance.message.ensureTotalCapacity(gpa, end);854 try instance.message.ensureTotalCapacity(gpa, end);
...@@ -837,8 +861,8 @@ const FuzzTestRunner = struct {...@@ -837,8 +861,8 @@ const FuzzTestRunner = struct {
837 }861 }
838862
839 fn addStderrRead(f: *FuzzTestRunner, id: u32) !void {863 fn addStderrRead(f: *FuzzTestRunner, id: u32) !void {
840 const step_owner = f.run.step.owner;864 const maker = f.ctx.fuzz.maker;
841 const gpa = step_owner.allocator;865 const gpa = maker.gpa;
842 const instance = &f.instances[id];866 const instance = &f.instances[id];
843867
844 try instance.stderr.ensureUnusedCapacity(gpa, 1);868 try instance.stderr.ensureUnusedCapacity(gpa, 1);
...@@ -861,24 +885,31 @@ const FuzzTestRunner = struct {...@@ -861,24 +885,31 @@ const FuzzTestRunner = struct {
861 }885 }
862886
863 fn instanceEos(f: *FuzzTestRunner, id: u32) !void {887 fn instanceEos(f: *FuzzTestRunner, id: u32) !void {
864 const step_owner = f.run.step.owner;888 const maker = f.ctx.fuzz.maker;
865 const io = step_owner.graph.io;
866 const instance = &f.instances[id];889 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
868 instance.child.stdin.?.close(io);896 instance.child.stdin.?.close(io);
869 instance.child.stdin = null;897 instance.child.stdin = null;
870 const term = try instance.child.wait(io);898 const term = try instance.child.wait(io);
871 if (!termMatches(.{ .exited = 0 }, term)) {899 if (!termMatches(.{ .exited = 0 }, term)) {
872 f.run.step.result_stderr = try f.mergedStderr();900 step.result_stderr = try f.mergedStderr();
873 try f.saveCrash(id, term);901 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)});
875 }903 }
876 }904 }
877905
878 fn saveCrash(f: *FuzzTestRunner, id: u32, term: process.Child.Term) !void {906 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
880 const maker = fuzz.maker;911 const maker = fuzz.maker;
881 const step = &f.run.step;912 const step = maker.stepByIndex(run_index);
882 const graph = maker.graph;913 const graph = maker.graph;
883 const io = graph.io;914 const io = graph.io;
884 const cache_root = graph.local_cache_root;915 const cache_root = graph.local_cache_root;
...@@ -906,7 +937,7 @@ const FuzzTestRunner = struct {...@@ -906,7 +937,7 @@ const FuzzTestRunner = struct {
906 error.FileNotFound => return,937 error.FileNotFound => return,
907 error.WouldBlock => continue, // Can not be from938 error.WouldBlock => continue, // Can not be from
908 // the crashed instance since it is still locked.939 // 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}", .{
910 cache_root, in_name, e,941 cache_root, in_name, e,
911 }),942 }),
912 };943 };
...@@ -915,7 +946,7 @@ const FuzzTestRunner = struct {...@@ -915,7 +946,7 @@ const FuzzTestRunner = struct {
915 const header = in_r.interface.takeStruct(InputHeader, .little) catch |e| {946 const header = in_r.interface.takeStruct(InputHeader, .little) catch |e| {
916 in_f.close(io);947 in_f.close(io);
917 switch (e) {948 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}", .{
919 cache_root, in_name, in_r.err.?,950 cache_root, in_name, in_r.err.?,
920 }),951 }),
921 error.EndOfStream => continue,952 error.EndOfStream => continue,
...@@ -924,7 +955,7 @@ const FuzzTestRunner = struct {...@@ -924,7 +955,7 @@ const FuzzTestRunner = struct {
924955
925 if (header.pc_digest == f.coverage_id.? and956 if (header.pc_digest == f.coverage_id.? and
926 header.instance_id == id and957 header.instance_id == id and
927 header.test_i < f.run.fuzz_tests.items.len)958 header.test_i < run.fuzz_tests.items.len)
928 {959 {
929 break header;960 break header;
930 }961 }
...@@ -937,7 +968,7 @@ const FuzzTestRunner = struct {...@@ -937,7 +968,7 @@ const FuzzTestRunner = struct {
937 const crash_name = "f" ++ Io.Dir.path.sep_str ++ "crash";968 const crash_name = "f" ++ Io.Dir.path.sep_str ++ "crash";
938 const out = cache_root.handle.createFile(io, crash_name, .{969 const out = cache_root.handle.createFile(io, crash_name, .{
939 .lock = .exclusive, // Multiple run steps could have found a crash at the same time970 .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}", .{
941 cache_root, crash_name, e,972 cache_root, crash_name, e,
942 });973 });
943 defer out.close(io);974 defer out.close(io);
...@@ -945,16 +976,16 @@ const FuzzTestRunner = struct {...@@ -945,16 +976,16 @@ const FuzzTestRunner = struct {
945 var out_w_buf: [512]u8 = undefined;976 var out_w_buf: [512]u8 = undefined;
946 var out_w = out.writerStreaming(io, &out_w_buf);977 var out_w = out.writerStreaming(io, &out_w_buf);
947 _ = out_w.interface.sendFileAll(&in_r, .limited(header.len)) catch |e| switch (e) {978 _ = 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}", .{
949 cache_root, in_name, in_r.err.?,980 cache_root, in_name, in_r.err.?,
950 }),981 }),
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}", .{
952 cache_root, crash_name, out_w.err.?,983 cache_root, crash_name, out_w.err.?,
953 }),984 }),
954 };985 };
955986
956 return f.run.step.fail("test '{s}' {f}; input saved to '{f}{s}'", .{987 return step.fail(maker, "test '{s}' {f}; input saved to '{f}{s}'", .{
957 f.run.fuzz_tests.items[header.test_i],988 run.fuzz_tests.items[header.test_i],
958 fmtTerm(term),989 fmtTerm(term),
959 cache_root,990 cache_root,
960 crash_name,991 crash_name,
...@@ -967,8 +998,8 @@ const FuzzTestRunner = struct {...@@ -967,8 +998,8 @@ const FuzzTestRunner = struct {
967 assert(f.broadcast.items.len == 0);998 assert(f.broadcast.items.len == 0);
968 assert(from_id < f.instances.len);999 assert(from_id < f.instances.len);
9691000
970 const step_owner = f.run.step.owner;1001 const maker = f.ctx.fuzz.maker;
971 const gpa = step_owner.allocator;1002 const gpa = maker.gpa;
9721003
973 var out_header: OutHeader = .{1004 var out_header: OutHeader = .{
974 .tag = .new_fuzz_input,1005 .tag = .new_fuzz_input,
...@@ -1010,8 +1041,9 @@ const FuzzTestRunner = struct {...@@ -1010,8 +1041,9 @@ const FuzzTestRunner = struct {
1010 }1041 }
10111042
1012 fn mergedStderr(f: *FuzzTestRunner) std.mem.Allocator.Error![]const u8 {1043 fn mergedStderr(f: *FuzzTestRunner) std.mem.Allocator.Error![]const u8 {
1013 const step_owner = f.run.step.owner;1044 const maker = f.ctx.fuzz.maker;
1014 const arena = step_owner.allocator;1045 const graph = maker.graph;
1046 const arena = graph.arena; // TODO don't leak into the process arena
10151047
1016 // Collect any available stderr1048 // Collect any available stderr
1017 while (f.batch.next()) |completion| {1049 while (f.batch.next()) |completion| {
...@@ -1035,11 +1067,12 @@ const FuzzTestRunner = struct {...@@ -1035,11 +1067,12 @@ const FuzzTestRunner = struct {
10351067
1036fn evalFuzzTest(1068fn evalFuzzTest(
1037 run: *Run,1069 run: *Run,
1070 run_index: Configuration.Step.Index,
1071 progress_node: std.Progress.Node,
1038 spawn_options: process.SpawnOptions,1072 spawn_options: process.SpawnOptions,
1039 options: Step.MakeOptions,
1040 fuzz_context: FuzzContext,1073 fuzz_context: FuzzContext,
1041) !void {1074) !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);
1043 defer f.deinit();1076 defer f.deinit();
1044 try f.startInstances();1077 try f.startInstances();
1045 try f.listen();1078 try f.listen();
...@@ -1049,23 +1082,25 @@ const StdioPollEnum = enum { stdout, stderr };...@@ -1049,23 +1082,25 @@ const StdioPollEnum = enum { stdout, stderr };
10491082
1050fn evalZigTest(1083fn evalZigTest(
1051 run: *Run,1084 run: *Run,
1085 run_index: Configuration.Step.Index,
1052 maker: *Maker,1086 maker: *Maker,
1087 progress_node: std.Progress.Node,
1053 spawn_options: process.SpawnOptions,1088 spawn_options: process.SpawnOptions,
1054 options: Step.MakeOptions,
1055 fuzz_context: ?FuzzContext,1089 fuzz_context: ?FuzzContext,
1056) !void {1090) !void {
1057 if (fuzz_context != null) {1091 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.?);
1059 return;1093 return;
1060 }1094 }
10611095
1062 const step_owner = run.step.owner;1096 const graph = maker.graph;
1063 const gpa = step_owner.allocator;1097 const gpa = maker.gpa;
1064 const arena = step_owner.allocator;1098 const io = graph.io;
1065 const io = step_owner.graph.io;1099 const arena = graph.arena; // TODO don't leak into the process arena
1100 const step = maker.stepByIndex(run_index);
10661101
1067 // We will update this every time a child runs.1102 // We will update this every time a child runs.
1068 run.step.result_peak_rss = 0;1103 step.result_peak_rss = 0;
10691104
1070 var test_results: Step.TestResults = .{1105 var test_results: Step.TestResults = .{
1071 .test_count = 0,1106 .test_count = 0,
...@@ -1087,16 +1122,18 @@ fn evalZigTest(...@@ -1087,16 +1122,18 @@ fn evalZigTest(
1087 defer if (!child_killed) {1122 defer if (!child_killed) {
1088 child.kill(io);1123 child.kill(io);
1089 multi_reader.deinit();1124 multi_reader.deinit();
1090 run.step.result_peak_rss = @max(1125 step.result_peak_rss = @max(
1091 run.step.result_peak_rss,1126 step.result_peak_rss,
1092 child.resource_usage_statistics.getMaxRss() orelse 0,1127 child.resource_usage_statistics.getMaxRss() orelse 0,
1093 );1128 );
1094 };1129 };
10951130
1096 switch (try waitZigTest(1131 switch (try waitZigTest(
1097 run,1132 run,
1133 run_index,
1134 maker,
1098 &child,1135 &child,
1099 options,1136 progress_node,
1100 &multi_reader,1137 &multi_reader,
1101 &test_metadata,1138 &test_metadata,
1102 &test_results,1139 &test_results,
...@@ -1109,7 +1146,7 @@ fn evalZigTest(...@@ -1109,7 +1146,7 @@ fn evalZigTest(
1109 error.ReadFailed => return stderr_fr.err.?,1146 error.ReadFailed => return stderr_fr.err.?,
1110 error.EndOfStream => {},1147 error.EndOfStream => {},
1111 }1148 }
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
1114 // Clean up everything and wait for the child to exit.1151 // Clean up everything and wait for the child to exit.
1115 child.stdin.?.close(io);1152 child.stdin.?.close(io);
...@@ -1117,14 +1154,16 @@ fn evalZigTest(...@@ -1117,14 +1154,16 @@ fn evalZigTest(
1117 multi_reader.deinit();1154 multi_reader.deinit();
1118 child_killed = true;1155 child_killed = true;
1119 const term = try child.wait(io);1156 const term = try child.wait(io);
1120 run.step.result_peak_rss = @max(1157 step.result_peak_rss = @max(
1121 run.step.result_peak_rss,1158 step.result_peak_rss,
1122 child.resource_usage_statistics.getMaxRss() orelse 0,1159 child.resource_usage_statistics.getMaxRss() orelse 0,
1123 );1160 );
11241161
1125 // The individual unit test results are irrelevant: the test runner itself broke!1162 // The individual unit test results are irrelevant: the test runner itself broke!
1126 // Fail immediately without populating `s.test_results`.1163 // 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 });
1128 },1167 },
1129 .no_poll => |no_poll| {1168 .no_poll => |no_poll| {
1130 // This might be a success (we requested exit and the child dutifully closed stdout) or1169 // This might be a success (we requested exit and the child dutifully closed stdout) or
...@@ -1138,8 +1177,8 @@ fn evalZigTest(...@@ -1138,8 +1177,8 @@ fn evalZigTest(
1138 multi_reader.deinit();1177 multi_reader.deinit();
1139 child_killed = true;1178 child_killed = true;
1140 const term = try child.wait(io);1179 const term = try child.wait(io);
1141 run.step.result_peak_rss = @max(1180 step.result_peak_rss = @max(
1142 run.step.result_peak_rss,1181 step.result_peak_rss,
1143 child.resource_usage_statistics.getMaxRss() orelse 0,1182 child.resource_usage_statistics.getMaxRss() orelse 0,
1144 );1183 );
11451184
...@@ -1148,7 +1187,7 @@ fn evalZigTest(...@@ -1148,7 +1187,7 @@ fn evalZigTest(
1148 // test, and continue to the next test.1187 // test, and continue to the next test.
1149 test_metadata.?.ns_per_test[test_index] = no_poll.ns_elapsed;1188 test_metadata.?.ns_per_test[test_index] = no_poll.ns_elapsed;
1150 test_results.crash_count += 1;1189 test_results.crash_count += 1;
1151 try run.step.addError("'{s}' {f}{s}{s}", .{1190 try step.addError(maker, "'{s}' {f}{s}{s}", .{
1152 test_metadata.?.testName(test_index),1191 test_metadata.?.testName(test_index),
1153 fmtTerm(term),1192 fmtTerm(term),
1154 if (stderr_owned.len != 0) " with stderr:\n" else "",1193 if (stderr_owned.len != 0) " with stderr:\n" else "",
...@@ -1158,22 +1197,22 @@ fn evalZigTest(...@@ -1158,22 +1197,22 @@ fn evalZigTest(
1158 }1197 }
11591198
1160 // Report an error if the child terminated uncleanly or if we were still trying to run more tests.1199 // 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;
1162 const tests_done = test_metadata != null and test_metadata.?.next_index == std.math.maxInt(u32);1201 const tests_done = test_metadata != null and test_metadata.?.next_index == std.math.maxInt(u32);
1163 if (!tests_done or !termMatches(.{ .exited = 0 }, term)) {1202 if (!tests_done or !termMatches(.{ .exited = 0 }, term)) {
1164 // The individual unit test results are irrelevant: the test runner itself broke!1203 // The individual unit test results are irrelevant: the test runner itself broke!
1165 // Fail immediately without populating `s.test_results`.1204 // 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)});
1167 }1206 }
11681207
1169 // We're done with all of the tests! Commit the test results and return.1208 // 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;
1171 if (test_metadata) |tm| {1210 if (test_metadata) |tm| {
1172 run.cached_test_metadata = tm.toCachedTestMetadata();1211 run.cached_test_metadata = tm.toCachedTestMetadata();
1173 if (options.web_server) |ws| {1212 if (maker.web_server) |*ws| {
1174 if (run.step.owner.graph.time_report) {1213 if (graph.time_report) {
1175 ws.updateTimeReportRunTest(1214 ws.updateTimeReportRunTest(
1176 run,1215 run_index,
1177 &run.cached_test_metadata.?,1216 &run.cached_test_metadata.?,
1178 tm.ns_per_test,1217 tm.ns_per_test,
1179 );1218 );
...@@ -1191,7 +1230,7 @@ fn evalZigTest(...@@ -1191,7 +1230,7 @@ fn evalZigTest(
1191 // the next test.1230 // the next test.
1192 test_metadata.?.ns_per_test[test_index] = timeout.ns_elapsed;1231 test_metadata.?.ns_per_test[test_index] = timeout.ns_elapsed;
1193 test_results.timeout_count += 1;1232 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}", .{
1195 test_metadata.?.testName(test_index),1234 test_metadata.?.testName(test_index),
1196 Io.Duration{ .nanoseconds = timeout.ns_elapsed },1235 Io.Duration{ .nanoseconds = timeout.ns_elapsed },
1197 if (stderr.len != 0) " with stderr:\n" else "",1236 if (stderr.len != 0) " with stderr:\n" else "",
...@@ -1200,10 +1239,10 @@ fn evalZigTest(...@@ -1200,10 +1239,10 @@ fn evalZigTest(
1200 continue;1239 continue;
1201 }1240 }
1202 // Just log an error and let the child be killed.1241 // 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);
1204 // The individual unit test results in `results` are irrelevant: the test runner1243 // The individual unit test results in `results` are irrelevant: the test runner
1205 // is broken! Fail immediately without populating `s.test_results`.1244 // 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 }});
1207 },1246 },
1208 }1247 }
1209 comptime unreachable;1248 comptime unreachable;
...@@ -1323,27 +1362,35 @@ fn sendRunFuzzTestMessage(...@@ -1323,27 +1362,35 @@ fn sendRunFuzzTestMessage(
1323 }1362 }
1324}1363}
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 {
1327 const graph = maker.graph;1370 const graph = maker.graph;
1328 const io = graph.io;1371 const io = graph.io;
1329 const arena = graph.allocator; // TODO don't leak into the process arena1372 const arena = graph.arena; // TODO don't leak into the process arena
1330 const gpa = maker.gpa;1373 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
1332 var child = try process.spawn(io, spawn_options);1379 var child = try process.spawn(io, spawn_options);
1333 defer child.kill(io);1380 defer child.kill(io);
13341381
1335 switch (run.stdin) {1382 switch (conf_run.stdin.u) {
1336 .bytes => |bytes| {1383 .bytes => |bytes| {
1337 child.stdin.?.writeStreamingAll(io, bytes) catch |err| {1384 child.stdin.?.writeStreamingAll(io, bytes.slice(conf)) catch |err| {
1338 return run.step.fail(maker, "unable to write stdin: {t}", .{err});1385 return step.fail(maker, "failed to write stdin: {t}", .{err});
1339 };1386 };
1340 child.stdin.?.close(io);1387 child.stdin.?.close(io);
1341 child.stdin = null;1388 child.stdin = null;
1342 },1389 },
1343 .lazy_path => |lazy_path| {1390 .lazy_path => |lazy_path| {
1344 const path = lazy_path.getPath3(graph, &run.step);1391 const path = try maker.resolveLazyPathIndex(arena, lazy_path, run_index);
1345 const file = path.root_dir.handle.openFile(io, path.subPathOrDot(), .{}) catch |err| {1392 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});
1347 };1394 };
1348 defer file.close(io);1395 defer file.close(io);
1349 // TODO https://github.com/ziglang/zig/issues/239551396 // TODO https://github.com/ziglang/zig/issues/23955
...@@ -1352,15 +1399,15 @@ fn evalGeneric(run: *Run, maker: *Maker, spawn_options: process.SpawnOptions) !E...@@ -1352,15 +1399,15 @@ fn evalGeneric(run: *Run, maker: *Maker, spawn_options: process.SpawnOptions) !E
1352 var write_buffer: [1024]u8 = undefined;1399 var write_buffer: [1024]u8 = undefined;
1353 var stdin_writer = child.stdin.?.writerStreaming(io, &write_buffer);1400 var stdin_writer = child.stdin.?.writerStreaming(io, &write_buffer);
1354 _ = stdin_writer.interface.sendFileAll(&file_reader, .unlimited) catch |err| switch (err) {1401 _ = 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}", .{
1356 path, file_reader.err.?,1403 path, file_reader.err.?,
1357 }),1404 }),
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}", .{
1359 stdin_writer.err.?,1406 stdin_writer.err.?,
1360 }),1407 }),
1361 };1408 };
1362 stdin_writer.interface.flush() catch |err| switch (err) {1409 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}", .{
1364 stdin_writer.err.?,1411 stdin_writer.err.?,
1365 }),1412 }),
1366 };1413 };
...@@ -1384,7 +1431,7 @@ fn evalGeneric(run: *Run, maker: *Maker, spawn_options: process.SpawnOptions) !E...@@ -1384,7 +1431,7 @@ fn evalGeneric(run: *Run, maker: *Maker, spawn_options: process.SpawnOptions) !E
1384 const stderr_reader = multi_reader.reader(1);1431 const stderr_reader = multi_reader.reader(1);
13851432
1386 while (multi_reader.fill(64, .none)) |_| {1433 while (multi_reader.fill(64, .none)) |_| {
1387 if (run.stdio_limit.toInt()) |limit| {1434 if (conf_run.stdio_limit.value) |limit| {
1388 if (stdout_reader.buffered().len > limit)1435 if (stdout_reader.buffered().len > limit)
1389 return error.StdoutStreamTooLong;1436 return error.StdoutStreamTooLong;
1390 if (stderr_reader.buffered().len > limit)1437 if (stderr_reader.buffered().len > limit)
...@@ -1404,7 +1451,8 @@ fn evalGeneric(run: *Run, maker: *Maker, spawn_options: process.SpawnOptions) !E...@@ -1404,7 +1451,8 @@ fn evalGeneric(run: *Run, maker: *Maker, spawn_options: process.SpawnOptions) !E
1404 stderr_bytes = try multi_reader.toOwnedSlice(1);1451 stderr_bytes = try multi_reader.toOwnedSlice(1);
1405 } else {1452 } else {
1406 var stdout_reader = stdout.readerStreaming(io, &.{});1453 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) {
1408 error.OutOfMemory => |e| return e,1456 error.OutOfMemory => |e| return e,
1409 error.ReadFailed => return stdout_reader.err.?,1457 error.ReadFailed => return stdout_reader.err.?,
1410 error.StreamTooLong => return error.StdoutStreamTooLong,1458 error.StreamTooLong => return error.StdoutStreamTooLong,
...@@ -1412,7 +1460,8 @@ fn evalGeneric(run: *Run, maker: *Maker, spawn_options: process.SpawnOptions) !E...@@ -1412,7 +1460,8 @@ fn evalGeneric(run: *Run, maker: *Maker, spawn_options: process.SpawnOptions) !E
1412 }1460 }
1413 } else if (child.stderr) |stderr| {1461 } else if (child.stderr) |stderr| {
1414 var stderr_reader = stderr.readerStreaming(io, &.{});1462 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) {
1416 error.OutOfMemory => |e| return e,1465 error.OutOfMemory => |e| return e,
1417 error.ReadFailed => return stderr_reader.err.?,1466 error.ReadFailed => return stderr_reader.err.?,
1418 error.StreamTooLong => return error.StderrStreamTooLong,1467 error.StreamTooLong => return error.StderrStreamTooLong,
...@@ -1421,16 +1470,16 @@ fn evalGeneric(run: *Run, maker: *Maker, spawn_options: process.SpawnOptions) !E...@@ -1421,16 +1470,16 @@ fn evalGeneric(run: *Run, maker: *Maker, spawn_options: process.SpawnOptions) !E
14211470
1422 if (stderr_bytes) |bytes| if (bytes.len > 0) {1471 if (stderr_bytes) |bytes| if (bytes.len > 0) {
1423 // Treat stderr as an error message.1472 // Treat stderr as an error message.
1424 const stderr_is_diagnostic = run.captured_stderr == null and switch (run.stdio) {1473 const stderr_is_diagnostic = conf_run.captured_stderr.value == null and switch (conf_run.flags.stdio) {
1425 .check => |checks| !checksContainStderr(checks.items),1474 .check => !checksContainStderr(&conf_run),
1426 else => true,1475 else => true,
1427 };1476 };
1428 if (stderr_is_diagnostic) {1477 if (stderr_is_diagnostic) {
1429 run.step.result_stderr = bytes;1478 step.result_stderr = bytes;
1430 }1479 }
1431 };1480 };
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
1435 return .{1484 return .{
1436 .term = try child.wait(io),1485 .term = try child.wait(io),
...@@ -1452,7 +1501,7 @@ pub fn rerunInFuzzMode(...@@ -1452,7 +1501,7 @@ pub fn rerunInFuzzMode(
1452) !void {1501) !void {
1453 const maker = fuzz.maker;1502 const maker = fuzz.maker;
1454 const graph = maker.graph;1503 const graph = maker.graph;
1455 const step = &run.step;1504 const step = maker.stepByIndex(run_index);
1456 const io = graph.io;1505 const io = graph.io;
1457 const arena = graph.arena; // TODO don't leak into the process arena1506 const arena = graph.arena; // TODO don't leak into the process arena
1458 const gpa = maker.gpa;1507 const gpa = maker.gpa;
...@@ -1535,9 +1584,9 @@ pub fn rerunInFuzzMode(...@@ -1535,9 +1584,9 @@ pub fn rerunInFuzzMode(
1535 }1584 }
1536 }1585 }
15371586
1538 if (run.step.result_failed_command) |cmd| {1587 if (step.result_failed_command) |cmd| {
1539 fuzz.gpa.free(cmd);1588 gpa.free(cmd);
1540 run.step.result_failed_command = null;1589 step.result_failed_command = null;
1541 }1590 }
15421591
1543 const has_side_effects = false;1592 const has_side_effects = false;
...@@ -1549,8 +1598,6 @@ pub fn rerunInFuzzMode(...@@ -1549,8 +1598,6 @@ pub fn rerunInFuzzMode(
1549 });1598 });
1550}1599}
15511600
1552const CapturedStdIo = void; // TODO get it from Configuration
1553
1554fn populateGeneratedPaths(1601fn populateGeneratedPaths(
1555 maker: *Maker,1602 maker: *Maker,
1556 output_placeholders: []const IndexedOutput,1603 output_placeholders: []const IndexedOutput,
...@@ -1712,142 +1759,153 @@ fn runCommand(...@@ -1712,142 +1759,153 @@ fn runCommand(
17121759
1713 if (true) @panic("TODO");1760 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: {1762 const opt_generic_result = spawnChildAndCollect(
1716 // InvalidExe: cpu arch mismatch1763 run_index,
1717 // FileNotFound: can happen with a wrong dynamic linker path1764 run,
1718 if (err == error.InvalidExe or err == error.FileNotFound) interpret: {1765 maker,
1719 // TODO: learn the target from the binary directly rather than from1766 progress_node,
1720 // relying on it being a Compile step. This will make this logic1767 argv,
1721 // work even for the edge case that the binary was produced by a1768 environ_map,
1722 // third party.1769 has_side_effects,
1723 const exe = switch (run.argv.items[0]) {1770 fuzz_context,
1724 .artifact => |exe| exe.artifact,1771 ) catch |err| term: {
1725 else => break :interpret,1772 switch (err) {
1726 };1773 error.InvalidExe, // cpu arch mismatch
1727 switch (exe.kind) {1774 error.FileNotFound, // can happen with a wrong dynamic linker path
1728 .exe, .@"test" => {},1775 => interpret: {
1729 else => break :interpret,1776 // TODO: learn the target from the binary directly rather than from
1730 }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();1789 const root_target = exe.rootModuleTarget();
1733 const need_cross_libc = exe.is_linking_libc and1790 const need_cross_libc = exe.is_linking_libc and
1734 (root_target.isGnuLibC() or (root_target.isMuslLibC() and exe.linkage == .dynamic));1791 (root_target.isGnuLibC() or (root_target.isMuslLibC() and exe.linkage == .dynamic));
1735 const other_target = exe.root_module.resolved_target.?.result;1792 const other_target = exe.root_module.resolved_target.?.result;
1736 switch (std.zig.system.getExternalExecutor(io, &graph.host.result, &other_target, .{1793 switch (std.zig.system.getExternalExecutor(io, &graph.host.result, &other_target, .{
1737 .qemu_fixes_dl = need_cross_libc and graph.libc_runtimes_dir != null,1794 .qemu_fixes_dl = need_cross_libc and graph.libc_runtimes_dir != null,
1738 .link_libc = exe.is_linking_libc,1795 .link_libc = exe.is_linking_libc,
1739 })) {1796 })) {
1740 .native, .rosetta => {1797 .native, .rosetta => {
1741 if (allow_skip) return error.MakeSkipped;1798 if (allow_skip) return error.MakeSkipped;
1742 break :interpret;1799 break :interpret;
1743 },1800 },
1744 .wine => |bin_name| {1801 .wine => |bin_name| {
1745 if (graph.enable_wine) {1802 if (graph.enable_wine) {
1746 try interp_argv.append(bin_name);1803 try interp_argv.append(bin_name);
1747 try interp_argv.appendSlice(argv);1804 try interp_argv.appendSlice(argv);
17481805
1749 // Wine's excessive stderr logging is only situationally helpful. Disable it by default, but1806 // 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.1807 // allow the user to override it (e.g. with `WINEDEBUG=err+all`) if desired.
1751 if (environ_map.get("WINEDEBUG") == null) {1808 if (environ_map.get("WINEDEBUG") == null) {
1752 try environ_map.put("WINEDEBUG", "-all");1809 try environ_map.put("WINEDEBUG", "-all");
1810 }
1811 } else {
1812 return failForeign(conf_run, maker, run_index, "-fwine", argv[0], exe);
1753 }1813 }
1754 } else {1814 },
1755 return failForeign(run, "-fwine", argv[0], exe);1815 .qemu => |bin_name| {
1756 }1816 if (graph.enable_qemu) {
1757 },1817 try interp_argv.append(bin_name);
1758 .qemu => |bin_name| {1818
1759 if (graph.enable_qemu) {1819 if (need_cross_libc) {
1760 try interp_argv.append(bin_name);1820 if (graph.libc_runtimes_dir) |dir| {
17611821 try interp_argv.append("-L");
1762 if (need_cross_libc) {1822 try interp_argv.append(try Dir.path.join(arena, &.{
1763 if (graph.libc_runtimes_dir) |dir| {1823 dir,
1764 try interp_argv.append("-L");1824 try if (root_target.isGnuLibC()) std.zig.target.glibcRuntimeTriple(
1765 try interp_argv.append(try Dir.path.join(arena, &.{1825 arena,
1766 dir,1826 root_target.cpu.arch,
1767 try if (root_target.isGnuLibC()) std.zig.target.glibcRuntimeTriple(1827 root_target.os.tag,
1768 arena,1828 root_target.abi,
1769 root_target.cpu.arch,1829 ) else if (root_target.isMuslLibC()) std.zig.target.muslRuntimeTriple(
1770 root_target.os.tag,1830 arena,
1771 root_target.abi,1831 root_target.cpu.arch,
1772 ) else if (root_target.isMuslLibC()) std.zig.target.muslRuntimeTriple(1832 root_target.abi,
1773 arena,1833 ) else unreachable,
1774 root_target.cpu.arch,1834 }));
1775 root_target.abi,1835 } else return failForeign(conf_run, maker, run_index, "--libc-runtimes", argv[0], exe);
1776 ) else unreachable,1836 }
1777 }));1837
1778 } else return failForeign(run, "--libc-runtimes", argv[0], exe);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);
1779 }1860 }
1861 },
1862 .bad_dl => |foreign_dl| {
1863 if (allow_skip) return error.MakeSkipped;
17801864
1781 try interp_argv.appendSlice(argv);1865 const host_dl = graph.host.result.dynamic_linker.get() orelse "(none)";
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;
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,1877 const host_name = try graph.host.result.zigTriple(arena);
1811 \\the host system is unable to execute binaries from the target1878 const foreign_name = try root_target.zigTriple(arena);
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 }
18281879
1829 if (root_target.os.tag == .windows) {1880 return step.fail(maker, "the host system ({s}) is unable to execute binaries from the target ({s})", .{
1830 // On Windows we don't have rpaths so we have to add .dll search paths to PATH1881 host_name, foreign_name,
1831 addPathForDynLibs(exe);1882 });
1832 }1883 },
1884 }
18331885
1834 gpa.free(step.result_failed_command.?);1886 if (root_target.os.tag == .windows) {
1835 step.result_failed_command = null;1887 // On Windows we don't have rpaths so we have to add .dll search paths to PATH
1836 try Step.handleVerbose(step.owner, cwd, run.environ_map, interp_argv.items);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| {1891 gpa.free(step.result_failed_command.?);
1839 if (!run.failing_to_execute_foreign_is_an_error) return error.MakeSkipped;1892 step.result_failed_command = null;
1840 if (e == error.MakeFailed) return error.MakeFailed; // error already reported1893 try graph.handleVerbose(cwd, run.environ_map, interp_argv.items);
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
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 }
1846 return step.fail(maker, "failed to spawn and capture stdio from {s}: {t}", .{ argv[0], err });1904 return step.fail(maker, "failed to spawn and capture stdio from {s}: {t}", .{ argv[0], err });
1847 };1905 };
18481906
1849 const generic_result = opt_generic_result orelse {1907 const generic_result = opt_generic_result orelse {
1850 assert(run.stdio == .zig_test);1908 assert(conf_run.flags.stdio == .zig_test);
1851 // Specific errors have already been reported, and test results are populated. All we need1909 // Specific errors have already been reported, and test results are populated. All we need
1852 // to do is report step failure if any test failed.1910 // to do is report step failure if any test failed.
1853 if (!step.test_results.isSuccess()) return error.MakeFailed;1911 if (!step.test_results.isSuccess()) return error.MakeFailed;
...@@ -1855,20 +1913,20 @@ fn runCommand(...@@ -1855,20 +1913,20 @@ fn runCommand(
1855 };1913 };
18561914
1857 assert(fuzz_context == null);1915 assert(fuzz_context == null);
1858 assert(run.stdio != .zig_test);1916 assert(conf_run.flags.stdio != .zig_test);
18591917
1860 // Capture stdout and stderr to GeneratedFile objects.1918 // Capture stdout and stderr to GeneratedFile objects.
1861 const Stream = struct {1919 const Stream = struct {
1862 captured: ?*CapturedStdIo,1920 captured: ?Configuration.Step.Run.CapturedStream,
1863 bytes: ?[]const u8,1921 bytes: ?[]const u8,
1864 };1922 };
1865 for ([_]Stream{1923 for ([_]Stream{
1866 .{1924 .{
1867 .captured = run.captured_stdout,1925 .captured = conf_run.captured_stdout.value,
1868 .bytes = generic_result.stdout,1926 .bytes = generic_result.stdout,
1869 },1927 },
1870 .{1928 .{
1871 .captured = run.captured_stderr,1929 .captured = conf_run.captured_stderr.value,
1872 .bytes = generic_result.stderr,1930 .bytes = generic_result.stderr,
1873 },1931 },
1874 }) |stream| {1932 }) |stream| {
...@@ -1898,7 +1956,7 @@ fn runCommand(...@@ -1898,7 +1956,7 @@ fn runCommand(
1898 }1956 }
1899 }1957 }
19001958
1901 switch (run.stdio) {1959 switch (conf_run.flags.stdio) {
1902 .zig_test => unreachable,1960 .zig_test => unreachable,
1903 .check => |checks| for (checks.items) |check| switch (check) {1961 .check => |checks| for (checks.items) |check| switch (check) {
1904 .expect_stderr_exact => |expected_bytes| {1962 .expect_stderr_exact => |expected_bytes| {
...@@ -1970,7 +2028,7 @@ fn runCommand(...@@ -1970,7 +2028,7 @@ fn runCommand(
1970 };2028 };
1971 if (bad_exit) {2029 if (bad_exit) {
1972 if (generic_result.stderr) |bytes| {2030 if (generic_result.stderr) |bytes| {
1973 run.step.result_stderr = bytes;2031 step.result_stderr = bytes;
1974 }2032 }
1975 }2033 }
19762034
...@@ -1995,9 +2053,8 @@ fn spawnChildAndCollect(...@@ -1995,9 +2053,8 @@ fn spawnChildAndCollect(
1995 has_side_effects: bool,2053 has_side_effects: bool,
1996 fuzz_context: ?FuzzContext,2054 fuzz_context: ?FuzzContext,
1997) !?EvalGenericResult {2055) !?EvalGenericResult {
1998 const step = run.step;2056 const step = maker.stepByIndex(run_index);
1999 const graph = maker.graph;2057 const graph = maker.graph;
2000 const gpa = maker.gpa;
2001 const io = graph.io;2058 const io = graph.io;
2002 const arena = graph.arena; // TODO don't leak into process arena2059 const arena = graph.arena; // TODO don't leak into process arena
2003 const conf = &maker.scanned_config.configuration;2060 const conf = &maker.scanned_config.configuration;
...@@ -2006,17 +2063,17 @@ fn spawnChildAndCollect(...@@ -2006,17 +2063,17 @@ fn spawnChildAndCollect(
20062063
2007 if (fuzz_context != null) {2064 if (fuzz_context != null) {
2008 assert(!has_side_effects);2065 assert(!has_side_effects);
2009 assert(run.stdio == .zig_test);2066 assert(conf_run.flags.stdio == .zig_test);
2010 }2067 }
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|
2013 .{ .path = try maker.resolveLazyPathIndexAbs(arena, lazy_cwd, run_index) }2070 .{ .path = try maker.resolveLazyPathIndexAbs(arena, lazy_cwd, run_index) }
2014 else2071 else
2015 .inherit;2072 .inherit;
20162073
2017 // If an error occurs, it's caused by this command:2074 // If an error occurs, it's caused by this command:
2018 assert(step.result_failed_command == null);2075 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, .{
2020 .child = environ_map,2077 .child = environ_map,
2021 .parent = &graph.environ_map,2078 .parent = &graph.environ_map,
2022 }, argv);2079 }, argv);
...@@ -2028,22 +2085,22 @@ fn spawnChildAndCollect(...@@ -2028,22 +2085,22 @@ fn spawnChildAndCollect(
2028 .cwd = child_cwd,2085 .cwd = child_cwd,
2029 .environ_map = environ_map,2086 .environ_map = environ_map,
2030 .request_resource_usage_statistics = true,2087 .request_resource_usage_statistics = true,
2031 .stdin = if (run.stdin != .none) s: {2088 .stdin = if (conf_run.stdin.u != .none) s: {
2032 assert(run.stdio != .inherit);2089 assert(conf_run.flags.stdio != .inherit);
2033 break :s .pipe;2090 break :s .pipe;
2034 } else switch (run.stdio) {2091 } else switch (conf_run.flags.stdio) {
2035 .infer_from_args => if (has_side_effects) .inherit else .ignore,2092 .infer_from_args => if (has_side_effects) .inherit else .ignore,
2036 .inherit => .inherit,2093 .inherit => .inherit,
2037 .check => .ignore,2094 .check => .ignore,
2038 .zig_test => .pipe,2095 .zig_test => .pipe,
2039 },2096 },
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) {
2041 .infer_from_args => if (has_side_effects) .inherit else .ignore,2098 .infer_from_args => if (has_side_effects) .inherit else .ignore,
2042 .inherit => .inherit,2099 .inherit => .inherit,
2043 .check => |checks| if (checksContainStdout(checks.items)) .pipe else .ignore,2100 .check => if (checksContainStdout(&conf_run)) .pipe else .ignore,
2044 .zig_test => .pipe,2101 .zig_test => .pipe,
2045 },2102 },
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) {
2047 .infer_from_args => if (has_side_effects) .inherit else .pipe,2104 .infer_from_args => if (has_side_effects) .inherit else .pipe,
2048 .inherit => .inherit,2105 .inherit => .inherit,
2049 .check => .pipe,2106 .check => .pipe,
...@@ -2051,9 +2108,9 @@ fn spawnChildAndCollect(...@@ -2051,9 +2108,9 @@ fn spawnChildAndCollect(
2051 },2108 },
2052 };2109 };
20532110
2054 if (run.stdio == .zig_test) {2111 if (conf_run.flags.stdio == .zig_test) {
2055 const started: Io.Clock.Timestamp = .now(io, .awake);2112 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) {
2057 error.Canceled => |e| return e,2114 error.Canceled => |e| return e,
2058 else => |e| e,2115 else => |e| e,
2059 };2116 };
...@@ -2062,7 +2119,7 @@ fn spawnChildAndCollect(...@@ -2062,7 +2119,7 @@ fn spawnChildAndCollect(
2062 return null;2119 return null;
2063 } else {2120 } else {
2064 const inherit = spawn_options.stdout == .inherit or spawn_options.stderr == .inherit;2121 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) {
2066 spawn_options.progress_node = progress_node;2123 spawn_options.progress_node = progress_node;
2067 }2124 }
2068 const terminal_mode: Io.Terminal.Mode = if (inherit) m: {2125 const terminal_mode: Io.Terminal.Mode = if (inherit) m: {
...@@ -2070,10 +2127,10 @@ fn spawnChildAndCollect(...@@ -2070,10 +2127,10 @@ fn spawnChildAndCollect(
2070 break :m stderr.terminal_mode;2127 break :m stderr.terminal_mode;
2071 } else .no_color;2128 } else .no_color;
2072 defer if (inherit) io.unlockStderr();2129 defer if (inherit) io.unlockStderr();
2073 try setColorEnvironmentVariables(run, environ_map, terminal_mode);2130 try setColorEnvironmentVariables(&conf_run, environ_map, terminal_mode);
20742131
2075 const started: Io.Clock.Timestamp = .now(io, .awake);2132 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) {
2077 error.Canceled => |e| return e,2134 error.Canceled => |e| return e,
2078 else => |e| e,2135 else => |e| e,
2079 };2136 };
...@@ -2106,8 +2163,12 @@ fn termMatches(expected: ?process.Child.Term, actual: process.Child.Term) bool {...@@ -2106,8 +2163,12 @@ fn termMatches(expected: ?process.Child.Term, actual: process.Child.Term) bool {
2106 };2163 };
2107}2164}
21082165
2109fn setColorEnvironmentVariables(run: *Run, environ_map: *EnvMap, terminal_mode: Io.Terminal.Mode) !void {2166fn setColorEnvironmentVariables(
2110 color: switch (run.color) {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) {
2111 .manual => {},2172 .manual => {},
2112 .enable => {2173 .enable => {
2113 try environ_map.put("CLICOLOR_FORCE", "1");2174 try environ_map.put("CLICOLOR_FORCE", "1");
...@@ -2122,8 +2183,8 @@ fn setColorEnvironmentVariables(run: *Run, environ_map: *EnvMap, terminal_mode:...@@ -2122,8 +2183,8 @@ fn setColorEnvironmentVariables(run: *Run, environ_map: *EnvMap, terminal_mode:
2122 .escape_codes => continue :color .enable,2183 .escape_codes => continue :color .enable,
2123 },2184 },
2124 .auto => {2185 .auto => {
2125 const capture_stderr = run.captured_stderr != null or switch (run.stdio) {2186 const capture_stderr = conf_run.captured_stderr.value != null or switch (conf_run.flags.stdio) {
2126 .check => |checks| checksContainStderr(checks.items),2187 .check => checksContainStderr(conf_run),
2127 .infer_from_args, .inherit, .zig_test => false,2188 .infer_from_args, .inherit, .zig_test => false,
2128 };2189 };
2129 if (capture_stderr) {2190 if (capture_stderr) {
...@@ -2135,53 +2196,12 @@ fn setColorEnvironmentVariables(run: *Run, environ_map: *EnvMap, terminal_mode:...@@ -2135,53 +2196,12 @@ fn setColorEnvironmentVariables(run: *Run, environ_map: *EnvMap, terminal_mode:
2135 }2196 }
2136}2197}
21372198
2138fn checksContainStdout(checks: []const @This().StdIo.Check) bool {2199fn checksContainStdout(conf_run: *const Configuration.Step.Run) bool {
2139 for (checks) |check| switch (check) {2200 return conf_run.expect_stdout_exact.value != null or conf_run.expect_stdout_match.slice.len != 0;
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 };
2175}2201}
21762202
2177fn hasAnyOutputArgs(run: Run) bool {2203fn checksContainStderr(conf_run: *const Configuration.Step.Run) bool {
2178 if (run.captured_stdout != null) return true;2204 return conf_run.expect_stderr_exact.value != null or conf_run.expect_stderr_match.slice.len != 0;
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;
2185}2205}
21862206
2187/// If `path` is cwd-relative, make it relative to the cwd of the child instead.2207/// 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 {...@@ -2225,13 +2245,13 @@ fn addPathForDynLibs(artifact: Configuration.Step.Index) void {
2225 compile.isDynamicLibrary())2245 compile.isDynamicLibrary())
2226 {2246 {
2227 @panic("TODO");2247 @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)).?);
2229 }2249 }
2230 }2250 }
2231}2251}
22322252
2233fn failForeign(2253fn failForeign(
2234 run: *Run,2254 conf_run: *const Configuration.Step.Run,
2235 maker: *Maker,2255 maker: *Maker,
2236 step_index: Configuration.Step.Index,2256 step_index: Configuration.Step.Index,
2237 suggested_flag: []const u8,2257 suggested_flag: []const u8,
...@@ -2239,9 +2259,9 @@ fn failForeign(...@@ -2239,9 +2259,9 @@ fn failForeign(
2239 exe: *Step.Compile,2259 exe: *Step.Compile,
2240) Step.ExtendedMakeError {2260) Step.ExtendedMakeError {
2241 const step = maker.stepByIndex(step_index);2261 const step = maker.stepByIndex(step_index);
2242 switch (run.stdio) {2262 switch (conf_run.flags.stdio) {
2243 .check, .zig_test => {2263 .check, .zig_test => {
2244 if (run.skip_foreign_checks) return error.MakeSkipped;2264 if (conf_run.flags.skip_foreign_checks) return error.MakeSkipped;
22452265
2246 const graph = maker.graph;2266 const graph = maker.graph;
2247 const process_arena = graph.arena; // TODO don't leak into process arena2267 const process_arena = graph.arena; // TODO don't leak into process arena