authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-08-26 16:28:42+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-10-18 09:28:42+01:00
loga388a8e5a7193831a349f6d00e04bd15211a931f
tree0c8a7e5df6bc3e4c88a2fcbb9e8bb77eba6d2ca3
parente4456d03f3642c4d0e19176aa09dbc12099e7a4c
signaturelock-open Commit is signed but in an unrecognized format.

std.Build: separate errors from failed commands

Recording the command in a separate field will give the build runner more freedom to choose how and when the command should be printed.

5 files changed, 71 insertions(+), 99 deletions(-)

lib/compiler/build_runner.zig+8
...@@ -1443,6 +1443,14 @@ pub fn printErrorMessages(...@@ -1443,6 +1443,14 @@ pub fn printErrorMessages(
1443 }1443 }
1444 try stderr.writeAll("\n");1444 try stderr.writeAll("\n");
1445 }1445 }
1446
1447 if (failing_step.result_failed_command) |cmd_str| {
1448 try ttyconf.setColor(stderr, .red);
1449 try stderr.writeAll("failed command: ");
1450 try ttyconf.setColor(stderr, .reset);
1451 try stderr.writeAll(cmd_str);
1452 try stderr.writeByte('\n');
1453 }
1446}1454}
14471455
1448fn printSteps(builder: *std.Build, w: *Writer) !void {1456fn printSteps(builder: *std.Build, w: *Writer) !void {
lib/std/Build.zig+2-16
...@@ -1594,20 +1594,6 @@ pub fn validateUserInputDidItFail(b: *Build) bool {...@@ -1594,20 +1594,6 @@ pub fn validateUserInputDidItFail(b: *Build) bool {
1594 return b.invalid_user_input;1594 return b.invalid_user_input;
1595}1595}
15961596
1597fn allocPrintCmd(gpa: Allocator, opt_cwd: ?[]const u8, argv: []const []const u8) error{OutOfMemory}![]u8 {
1598 var buf: ArrayList(u8) = .empty;
1599 if (opt_cwd) |cwd| try buf.print(gpa, "cd {s} && ", .{cwd});
1600 for (argv) |arg| {
1601 try buf.print(gpa, "{s} ", .{arg});
1602 }
1603 return buf.toOwnedSlice(gpa);
1604}
1605
1606fn printCmd(ally: Allocator, cwd: ?[]const u8, argv: []const []const u8) void {
1607 const text = allocPrintCmd(ally, cwd, argv) catch @panic("OOM");
1608 std.debug.print("{s}\n", .{text});
1609}
1610
1611/// This creates the install step and adds it to the dependencies of the1597/// This creates the install step and adds it to the dependencies of the
1612/// top-level install step, using all the default options.1598/// top-level install step, using all the default options.
1613/// See `addInstallArtifact` for a more flexible function.1599/// See `addInstallArtifact` for a more flexible function.
...@@ -1857,14 +1843,14 @@ pub fn runAllowFail(...@@ -1857,14 +1843,14 @@ pub fn runAllowFail(
1857pub fn run(b: *Build, argv: []const []const u8) []u8 {1843pub fn run(b: *Build, argv: []const []const u8) []u8 {
1858 if (!process.can_spawn) {1844 if (!process.can_spawn) {
1859 std.debug.print("unable to spawn the following command: cannot spawn child process\n{s}\n", .{1845 std.debug.print("unable to spawn the following command: cannot spawn child process\n{s}\n", .{
1860 try allocPrintCmd(b.allocator, null, argv),1846 try Step.allocPrintCmd(b.allocator, null, argv),
1861 });1847 });
1862 process.exit(1);1848 process.exit(1);
1863 }1849 }
18641850
1865 var code: u8 = undefined;1851 var code: u8 = undefined;
1866 return b.runAllowFail(argv, &code, .Inherit) catch |err| {1852 return b.runAllowFail(argv, &code, .Inherit) catch |err| {
1867 const printed_cmd = allocPrintCmd(b.allocator, null, argv) catch @panic("OOM");1853 const printed_cmd = Step.allocPrintCmd(b.allocator, null, argv) catch @panic("OOM");
1868 std.debug.print("unable to spawn the following command: {s}\n{s}\n", .{1854 std.debug.print("unable to spawn the following command: {s}\n{s}\n", .{
1869 @errorName(err), printed_cmd,1855 @errorName(err), printed_cmd,
1870 });1856 });
lib/std/Build/Step.zig+40-46
...@@ -56,6 +56,9 @@ result_cached: bool,...@@ -56,6 +56,9 @@ result_cached: bool,
56result_duration_ns: ?u64,56result_duration_ns: ?u64,
57/// 0 means unavailable or not reported.57/// 0 means unavailable or not reported.
58result_peak_rss: usize,58result_peak_rss: usize,
59/// If the step is failed and this field is populated, this is the command which failed.
60/// This field may be populated even if the step succeeded.
61result_failed_command: ?[]const u8,
59test_results: TestResults,62test_results: TestResults,
6063
61/// The return address associated with creation of this step that can be useful64/// The return address associated with creation of this step that can be useful
...@@ -257,6 +260,7 @@ pub fn init(options: StepOptions) Step {...@@ -257,6 +260,7 @@ pub fn init(options: StepOptions) Step {
257 .result_cached = false,260 .result_cached = false,
258 .result_duration_ns = null,261 .result_duration_ns = null,
259 .result_peak_rss = 0,262 .result_peak_rss = 0,
263 .result_failed_command = null,
260 .test_results = .{},264 .test_results = .{},
261 };265 };
262}266}
...@@ -336,20 +340,20 @@ pub fn dump(step: *Step, w: *std.Io.Writer, tty_config: std.Io.tty.Config) void...@@ -336,20 +340,20 @@ pub fn dump(step: *Step, w: *std.Io.Writer, tty_config: std.Io.tty.Config) void
336 }340 }
337}341}
338342
339pub fn evalChildProcess(s: *Step, argv: []const []const u8) ![]u8 {343/// Populates `s.result_failed_command`.
340 const run_result = try captureChildProcess(s, std.Progress.Node.none, argv);
341 try handleChildProcessTerm(s, run_result.term, null, argv);
342 return run_result.stdout;
343}
344
345pub fn captureChildProcess(344pub fn captureChildProcess(
346 s: *Step,345 s: *Step,
346 gpa: Allocator,
347 progress_node: std.Progress.Node,347 progress_node: std.Progress.Node,
348 argv: []const []const u8,348 argv: []const []const u8,
349) !std.process.Child.RunResult {349) !std.process.Child.RunResult {
350 const arena = s.owner.allocator;350 const arena = s.owner.allocator;
351351
352 try handleChildProcUnsupported(s, null, argv);352 // If an error occurs, it's happened in this command:
353 assert(s.result_failed_command == null);
354 s.result_failed_command = try allocPrintCmd(gpa, null, argv);
355
356 try handleChildProcUnsupported(s);
353 try handleVerbose(s.owner, null, argv);357 try handleVerbose(s.owner, null, argv);
354358
355 const result = std.process.Child.run(.{359 const result = std.process.Child.run(.{
...@@ -386,6 +390,7 @@ pub const ZigProcess = struct {...@@ -386,6 +390,7 @@ pub const ZigProcess = struct {
386390
387/// Assumes that argv contains `--listen=-` and that the process being spawned391/// Assumes that argv contains `--listen=-` and that the process being spawned
388/// is the zig compiler - the same version that compiled the build runner.392/// is the zig compiler - the same version that compiled the build runner.
393/// Populates `s.result_failed_command`.
389pub fn evalZigProcess(394pub fn evalZigProcess(
390 s: *Step,395 s: *Step,
391 argv: []const []const u8,396 argv: []const []const u8,
...@@ -394,6 +399,10 @@ pub fn evalZigProcess(...@@ -394,6 +399,10 @@ pub fn evalZigProcess(
394 web_server: ?*Build.WebServer,399 web_server: ?*Build.WebServer,
395 gpa: Allocator,400 gpa: Allocator,
396) !?Path {401) !?Path {
402 // If an error occurs, it's happened in this command:
403 assert(s.result_failed_command == null);
404 s.result_failed_command = try allocPrintCmd(gpa, null, argv);
405
397 if (s.getZigProcess()) |zp| update: {406 if (s.getZigProcess()) |zp| update: {
398 assert(watch);407 assert(watch);
399 if (std.Progress.have_ipc) if (zp.progress_ipc_fd) |fd| prog_node.setIpcFd(fd);408 if (std.Progress.have_ipc) if (zp.progress_ipc_fd) |fd| prog_node.setIpcFd(fd);
...@@ -410,8 +419,9 @@ pub fn evalZigProcess(...@@ -410,8 +419,9 @@ pub fn evalZigProcess(
410 else => |e| return e,419 else => |e| return e,
411 };420 };
412421
413 if (s.result_error_bundle.errorMessageCount() > 0)422 if (s.result_error_bundle.errorMessageCount() > 0) {
414 return s.fail("{d} compilation errors", .{s.result_error_bundle.errorMessageCount()});423 return s.fail("{d} compilation errors", .{s.result_error_bundle.errorMessageCount()});
424 }
415425
416 if (s.result_error_msgs.items.len > 0 and result == null) {426 if (s.result_error_msgs.items.len > 0 and result == null) {
417 // Crash detected.427 // Crash detected.
...@@ -420,7 +430,7 @@ pub fn evalZigProcess(...@@ -420,7 +430,7 @@ pub fn evalZigProcess(
420 };430 };
421 s.result_peak_rss = zp.child.resource_usage_statistics.getMaxRss() orelse 0;431 s.result_peak_rss = zp.child.resource_usage_statistics.getMaxRss() orelse 0;
422 s.clearZigProcess(gpa);432 s.clearZigProcess(gpa);
423 try handleChildProcessTerm(s, term, null, argv);433 try handleChildProcessTerm(s, term);
424 return error.MakeFailed;434 return error.MakeFailed;
425 }435 }
426436
...@@ -430,7 +440,7 @@ pub fn evalZigProcess(...@@ -430,7 +440,7 @@ pub fn evalZigProcess(
430 const b = s.owner;440 const b = s.owner;
431 const arena = b.allocator;441 const arena = b.allocator;
432442
433 try handleChildProcUnsupported(s, null, argv);443 try handleChildProcUnsupported(s);
434 try handleVerbose(s.owner, null, argv);444 try handleVerbose(s.owner, null, argv);
435445
436 var child = std.process.Child.init(argv, arena);446 var child = std.process.Child.init(argv, arena);
...@@ -484,16 +494,11 @@ pub fn evalZigProcess(...@@ -484,16 +494,11 @@ pub fn evalZigProcess(
484 else => {},494 else => {},
485 };495 };
486496
487 try handleChildProcessTerm(s, term, null, argv);497 try handleChildProcessTerm(s, term);
488 }498 }
489499
490 // This is intentionally printed for failure on the first build but not for
491 // subsequent rebuilds.
492 if (s.result_error_bundle.errorMessageCount() > 0) {500 if (s.result_error_bundle.errorMessageCount() > 0) {
493 return s.fail("the following command failed with {d} compilation errors:\n{s}", .{501 return s.fail("{d} compilation errors", .{s.result_error_bundle.errorMessageCount()});
494 s.result_error_bundle.errorMessageCount(),
495 try allocPrintCmd(arena, null, argv),
496 });
497 }502 }
498503
499 return result;504 return result;
...@@ -696,54 +701,38 @@ pub fn handleVerbose2(...@@ -696,54 +701,38 @@ pub fn handleVerbose2(
696 }701 }
697}702}
698703
699pub inline fn handleChildProcUnsupported(704/// Asserts that the caller has already populated `s.result_failed_command`.
700 s: *Step,705pub inline fn handleChildProcUnsupported(s: *Step) error{ OutOfMemory, MakeFailed }!void {
701 opt_cwd: ?[]const u8,
702 argv: []const []const u8,
703) error{ OutOfMemory, MakeFailed }!void {
704 if (!std.process.can_spawn) {706 if (!std.process.can_spawn) {
705 return s.fail(707 return s.fail("unable to spawn process: host cannot spawn child processes", .{});
706 "unable to execute the following command: host cannot spawn child processes\n{s}",
707 .{try allocPrintCmd(s.owner.allocator, opt_cwd, argv)},
708 );
709 }708 }
710}709}
711710
712pub fn handleChildProcessTerm(711/// Asserts that the caller has already populated `s.result_failed_command`.
713 s: *Step,712pub fn handleChildProcessTerm(s: *Step, term: std.process.Child.Term) error{ MakeFailed, OutOfMemory }!void {
714 term: std.process.Child.Term,713 assert(s.result_failed_command != null);
715 opt_cwd: ?[]const u8,
716 argv: []const []const u8,
717) error{ MakeFailed, OutOfMemory }!void {
718 const arena = s.owner.allocator;
719 switch (term) {714 switch (term) {
720 .Exited => |code| {715 .Exited => |code| {
721 if (code != 0) {716 if (code != 0) {
722 return s.fail(717 return s.fail("process exited with error code {d}", .{code});
723 "the following command exited with error code {d}:\n{s}",
724 .{ code, try allocPrintCmd(arena, opt_cwd, argv) },
725 );
726 }718 }
727 },719 },
728 .Signal, .Stopped, .Unknown => {720 .Signal, .Stopped, .Unknown => {
729 return s.fail(721 return s.fail("process terminated unexpectedly", .{});
730 "the following command terminated unexpectedly:\n{s}",
731 .{try allocPrintCmd(arena, opt_cwd, argv)},
732 );
733 },722 },
734 }723 }
735}724}
736725
737pub fn allocPrintCmd(726pub fn allocPrintCmd(
738 arena: Allocator,727 gpa: Allocator,
739 opt_cwd: ?[]const u8,728 opt_cwd: ?[]const u8,
740 argv: []const []const u8,729 argv: []const []const u8,
741) Allocator.Error![]u8 {730) Allocator.Error![]u8 {
742 return allocPrintCmd2(arena, opt_cwd, null, argv);731 return allocPrintCmd2(gpa, opt_cwd, null, argv);
743}732}
744733
745pub fn allocPrintCmd2(734pub fn allocPrintCmd2(
746 arena: Allocator,735 gpa: Allocator,
747 opt_cwd: ?[]const u8,736 opt_cwd: ?[]const u8,
748 opt_env: ?*const std.process.EnvMap,737 opt_env: ?*const std.process.EnvMap,
749 argv: []const []const u8,738 argv: []const []const u8,
...@@ -783,11 +772,13 @@ pub fn allocPrintCmd2(...@@ -783,11 +772,13 @@ pub fn allocPrintCmd2(
783 }772 }
784 };773 };
785774
786 var aw: std.Io.Writer.Allocating = .init(arena);775 var aw: std.Io.Writer.Allocating = .init(gpa);
776 defer aw.deinit();
787 const writer = &aw.writer;777 const writer = &aw.writer;
788 if (opt_cwd) |cwd| writer.print("cd {s} && ", .{cwd}) catch return error.OutOfMemory;778 if (opt_cwd) |cwd| writer.print("cd {s} && ", .{cwd}) catch return error.OutOfMemory;
789 if (opt_env) |env| {779 if (opt_env) |env| {
790 const process_env_map = std.process.getEnvMap(arena) catch std.process.EnvMap.init(arena);780 var process_env_map = std.process.getEnvMap(gpa) catch std.process.EnvMap.init(gpa);
781 defer process_env_map.deinit();
791 var it = env.iterator();782 var it = env.iterator();
792 while (it.next()) |entry| {783 while (it.next()) |entry| {
793 const key = entry.key_ptr.*;784 const key = entry.key_ptr.*;
...@@ -973,11 +964,14 @@ fn addWatchInputFromPath(step: *Step, path: Build.Cache.Path, basename: []const...@@ -973,11 +964,14 @@ fn addWatchInputFromPath(step: *Step, path: Build.Cache.Path, basename: []const
973pub fn reset(step: *Step, gpa: Allocator) void {964pub fn reset(step: *Step, gpa: Allocator) void {
974 assert(step.state == .precheck_done);965 assert(step.state == .precheck_done);
975966
967 if (step.result_failed_command) |cmd| gpa.free(cmd);
968
976 step.result_error_msgs.clearRetainingCapacity();969 step.result_error_msgs.clearRetainingCapacity();
977 step.result_stderr = "";970 step.result_stderr = "";
978 step.result_cached = false;971 step.result_cached = false;
979 step.result_duration_ns = null;972 step.result_duration_ns = null;
980 step.result_peak_rss = 0;973 step.result_peak_rss = 0;
974 step.result_failed_command = null;
981 step.test_results = .{};975 step.test_results = .{};
982976
983 step.result_error_bundle.deinit(gpa);977 step.result_error_bundle.deinit(gpa);
lib/std/Build/Step/Fmt.zig+2-2
...@@ -67,7 +67,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -67,7 +67,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
67 argv.appendAssumeCapacity(b.pathFromRoot(p));67 argv.appendAssumeCapacity(b.pathFromRoot(p));
68 }68 }
6969
70 const run_result = try step.captureChildProcess(prog_node, argv.items);70 const run_result = try step.captureChildProcess(options.gpa, prog_node, argv.items);
71 if (fmt.check) switch (run_result.term) {71 if (fmt.check) switch (run_result.term) {
72 .Exited => |code| if (code != 0 and run_result.stdout.len != 0) {72 .Exited => |code| if (code != 0 and run_result.stdout.len != 0) {
73 var it = std.mem.tokenizeScalar(u8, run_result.stdout, '\n');73 var it = std.mem.tokenizeScalar(u8, run_result.stdout, '\n');
...@@ -77,5 +77,5 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -77,5 +77,5 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
77 },77 },
78 else => {},78 else => {},
79 };79 };
80 try step.handleChildProcessTerm(run_result.term, null, argv.items);80 try step.handleChildProcessTerm(run_result.term);
81}81}
lib/std/Build/Step/Run.zig+19-35
...@@ -1212,10 +1212,11 @@ fn runCommand(...@@ -1212,10 +1212,11 @@ fn runCommand(
1212 const step = &run.step;1212 const step = &run.step;
1213 const b = step.owner;1213 const b = step.owner;
1214 const arena = b.allocator;1214 const arena = b.allocator;
1215 const gpa = options.gpa;
12151216
1216 const cwd: ?[]const u8 = if (run.cwd) |lazy_cwd| lazy_cwd.getPath2(b, step) else null;1217 const cwd: ?[]const u8 = if (run.cwd) |lazy_cwd| lazy_cwd.getPath2(b, step) else null;
12171218
1218 try step.handleChildProcUnsupported(cwd, argv);1219 try step.handleChildProcUnsupported();
1219 try Step.handleVerbose2(step.owner, cwd, run.env_map, argv);1220 try Step.handleVerbose2(step.owner, cwd, run.env_map, argv);
12201221
1221 const allow_skip = switch (run.stdio) {1222 const allow_skip = switch (run.stdio) {
...@@ -1365,6 +1366,8 @@ fn runCommand(...@@ -1365,6 +1366,8 @@ fn runCommand(
1365 run.addPathForDynLibs(exe);1366 run.addPathForDynLibs(exe);
1366 }1367 }
13671368
1369 gpa.free(step.result_failed_command.?);
1370 step.result_failed_command = null;
1368 try Step.handleVerbose2(step.owner, cwd, run.env_map, interp_argv.items);1371 try Step.handleVerbose2(step.owner, cwd, run.env_map, interp_argv.items);
13691372
1370 break :term spawnChildAndCollect(run, interp_argv.items, env_map, has_side_effects, options, fuzz_context) catch |e| {1373 break :term spawnChildAndCollect(run, interp_argv.items, env_map, has_side_effects, options, fuzz_context) catch |e| {
...@@ -1380,18 +1383,11 @@ fn runCommand(...@@ -1380,18 +1383,11 @@ fn runCommand(
1380 return step.fail("failed to spawn and capture stdio from {s}: {s}", .{ argv[0], @errorName(err) });1383 return step.fail("failed to spawn and capture stdio from {s}: {s}", .{ argv[0], @errorName(err) });
1381 };1384 };
13821385
1383 const final_argv = if (interp_argv.items.len == 0) argv else interp_argv.items;
1384
1385 const generic_result = opt_generic_result orelse {1386 const generic_result = opt_generic_result orelse {
1386 assert(run.stdio == .zig_test);1387 assert(run.stdio == .zig_test);
1387 // Specific errors have already been reported. All we need to do is detect those and1388 // Specific errors have already been reported, and test results are populated. All we need
1388 // report the general "test failed" error, which includes the command argv.1389 // to do is report step failure if any test failed.
1389 if (!step.test_results.isSuccess() or step.result_error_msgs.items.len > 0) {1390 if (!step.test_results.isSuccess()) return error.MakeFailed;
1390 return step.fail(
1391 "the following test command failed:\n{s}",
1392 .{try Step.allocPrintCmd(arena, cwd, final_argv)},
1393 );
1394 }
1395 return;1391 return;
1396 };1392 };
13971393
...@@ -1445,93 +1441,77 @@ fn runCommand(...@@ -1445,93 +1441,77 @@ fn runCommand(
1445 .expect_stderr_exact => |expected_bytes| {1441 .expect_stderr_exact => |expected_bytes| {
1446 if (!mem.eql(u8, expected_bytes, generic_result.stderr.?)) {1442 if (!mem.eql(u8, expected_bytes, generic_result.stderr.?)) {
1447 return step.fail(1443 return step.fail(
1448 \\
1449 \\========= expected this stderr: =========1444 \\========= expected this stderr: =========
1450 \\{s}1445 \\{s}
1451 \\========= but found: ====================1446 \\========= but found: ====================
1452 \\{s}1447 \\{s}
1453 \\========= from the following command: ===
1454 \\{s}
1455 , .{1448 , .{
1456 expected_bytes,1449 expected_bytes,
1457 generic_result.stderr.?,1450 generic_result.stderr.?,
1458 try Step.allocPrintCmd(arena, cwd, final_argv),
1459 });1451 });
1460 }1452 }
1461 },1453 },
1462 .expect_stderr_match => |match| {1454 .expect_stderr_match => |match| {
1463 if (mem.indexOf(u8, generic_result.stderr.?, match) == null) {1455 if (mem.indexOf(u8, generic_result.stderr.?, match) == null) {
1464 return step.fail(1456 return step.fail(
1465 \\
1466 \\========= expected to find in stderr: =========1457 \\========= expected to find in stderr: =========
1467 \\{s}1458 \\{s}
1468 \\========= but stderr does not contain it: =====1459 \\========= but stderr does not contain it: =====
1469 \\{s}1460 \\{s}
1470 \\========= from the following command: =========
1471 \\{s}
1472 , .{1461 , .{
1473 match,1462 match,
1474 generic_result.stderr.?,1463 generic_result.stderr.?,
1475 try Step.allocPrintCmd(arena, cwd, final_argv),
1476 });1464 });
1477 }1465 }
1478 },1466 },
1479 .expect_stdout_exact => |expected_bytes| {1467 .expect_stdout_exact => |expected_bytes| {
1480 if (!mem.eql(u8, expected_bytes, generic_result.stdout.?)) {1468 if (!mem.eql(u8, expected_bytes, generic_result.stdout.?)) {
1481 return step.fail(1469 return step.fail(
1482 \\
1483 \\========= expected this stdout: =========1470 \\========= expected this stdout: =========
1484 \\{s}1471 \\{s}
1485 \\========= but found: ====================1472 \\========= but found: ====================
1486 \\{s}1473 \\{s}
1487 \\========= from the following command: ===
1488 \\{s}
1489 , .{1474 , .{
1490 expected_bytes,1475 expected_bytes,
1491 generic_result.stdout.?,1476 generic_result.stdout.?,
1492 try Step.allocPrintCmd(arena, cwd, final_argv),
1493 });1477 });
1494 }1478 }
1495 },1479 },
1496 .expect_stdout_match => |match| {1480 .expect_stdout_match => |match| {
1497 if (mem.indexOf(u8, generic_result.stdout.?, match) == null) {1481 if (mem.indexOf(u8, generic_result.stdout.?, match) == null) {
1498 return step.fail(1482 return step.fail(
1499 \\
1500 \\========= expected to find in stdout: =========1483 \\========= expected to find in stdout: =========
1501 \\{s}1484 \\{s}
1502 \\========= but stdout does not contain it: =====1485 \\========= but stdout does not contain it: =====
1503 \\{s}1486 \\{s}
1504 \\========= from the following command: =========
1505 \\{s}
1506 , .{1487 , .{
1507 match,1488 match,
1508 generic_result.stdout.?,1489 generic_result.stdout.?,
1509 try Step.allocPrintCmd(arena, cwd, final_argv),
1510 });1490 });
1511 }1491 }
1512 },1492 },
1513 .expect_term => |expected_term| {1493 .expect_term => |expected_term| {
1514 if (!termMatches(expected_term, generic_result.term)) {1494 if (!termMatches(expected_term, generic_result.term)) {
1515 return step.fail("the following command {f} (expected {f}):\n{s}", .{1495 return step.fail("process {f} (expected {f})", .{
1516 fmtTerm(generic_result.term),1496 fmtTerm(generic_result.term),
1517 fmtTerm(expected_term),1497 fmtTerm(expected_term),
1518 try Step.allocPrintCmd(arena, cwd, final_argv),
1519 });1498 });
1520 }1499 }
1521 },1500 },
1522 },1501 },
1523 else => {1502 else => {
1524 // On failure, print stderr if captured.1503 // On failure, report captured stderr like normal standard error output.
1525 const bad_exit = switch (generic_result.term) {1504 const bad_exit = switch (generic_result.term) {
1526 .Exited => |code| code != 0,1505 .Exited => |code| code != 0,
1527 .Signal, .Stopped, .Unknown => true,1506 .Signal, .Stopped, .Unknown => true,
1528 };1507 };
1508 if (bad_exit) {
1509 if (generic_result.stderr) |bytes| {
1510 run.step.result_stderr = bytes;
1511 }
1512 }
15291513
1530 if (bad_exit) if (generic_result.stderr) |err| {1514 try step.handleChildProcessTerm(generic_result.term);
1531 try step.addError("stderr:\n{s}", .{err});
1532 };
1533
1534 try step.handleChildProcessTerm(generic_result.term, cwd, final_argv);
1535 },1515 },
1536 }1516 }
1537}1517}
...@@ -1594,6 +1574,10 @@ fn spawnChildAndCollect(...@@ -1594,6 +1574,10 @@ fn spawnChildAndCollect(
1594 child.stdin_behavior = .Pipe;1574 child.stdin_behavior = .Pipe;
1595 }1575 }
15961576
1577 // If an error occurs, it's caused by this command:
1578 assert(run.step.result_failed_command == null);
1579 run.step.result_failed_command = try Step.allocPrintCmd(options.gpa, child.cwd, argv);
1580
1597 if (run.stdio == .zig_test) {1581 if (run.stdio == .zig_test) {
1598 var timer = try std.time.Timer.start();1582 var timer = try std.time.Timer.start();
1599 const res = try evalZigTest(run, &child, options, fuzz_context);1583 const res = try evalZigTest(run, &child, options, fuzz_context);