authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-08-26 17:45:43+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-10-18 09:28:42+01:00
log75adbf40ca1bd607b11f73513667e81d2b341690
tree44a76c3e56688122609b59d5a21ff0a6dfda2915
parenta388a8e5a7193831a349f6d00e04bd15211a931f
signature Commit is signed but in an unrecognized format.

build runner: remove `--prominent-compile-errors`, introduce `--error-style`

The new `--error-style` option decides how build failures are printed. The default mode "verbose" prints all context including the step graph fragment and the failed command (if any). The alternative mode "minimal" prints only the failed step itself, and does not print the failed command. There are also "verbose_clear" and "minimal_clear" modes, which have the distinction that the output is cleared (through ANSI escape codes) between updates, preventing different updates from being confused in the output. If `--error-style` is not specified, the environment variable `ZIG_BUILD_ERROR_STYLE` is checked before falling back to the default of "verbose"; this means the value can effectively be chosen system-wide since it is generally a personal preference. Also introduced is a `--multiline-errors` option which decides how to print errors which span multiple lines. By default, non-initial lines are indented to align with the first. Alternatively, a leading newline can be printed to align everyting on the first column, or no special treatment can be applied, resulting in misaligned output. Again, there is an environment variable (`ZIG_BUILD_MULTILINE_ERRORS`) to specify a preferred default if the option is not explicitly provided. Resolves: #23472

3 files changed, 167 insertions(+), 129 deletions(-)

lib/compiler/build_runner.zig+163-127
...@@ -103,12 +103,13 @@ pub fn main() !void {...@@ -103,12 +103,13 @@ pub fn main() !void {
103103
104 var install_prefix: ?[]const u8 = null;104 var install_prefix: ?[]const u8 = null;
105 var dir_list = std.Build.DirList{};105 var dir_list = std.Build.DirList{};
106 var error_style: ErrorStyle = .verbose;
107 var multiline_errors: MultilineErrors = .indent;
106 var summary: ?Summary = null;108 var summary: ?Summary = null;
107 var max_rss: u64 = 0;109 var max_rss: u64 = 0;
108 var skip_oom_steps = false;110 var skip_oom_steps = false;
109 var test_timeout_ms: ?u64 = null;111 var test_timeout_ms: ?u64 = null;
110 var color: Color = .auto;112 var color: Color = .auto;
111 var prominent_compile_errors = false;
112 var help_menu = false;113 var help_menu = false;
113 var steps_menu = false;114 var steps_menu = false;
114 var output_tmp_nonce: ?[16]u8 = null;115 var output_tmp_nonce: ?[16]u8 = null;
...@@ -117,6 +118,18 @@ pub fn main() !void {...@@ -117,6 +118,18 @@ pub fn main() !void {
117 var debounce_interval_ms: u16 = 50;118 var debounce_interval_ms: u16 = 50;
118 var webui_listen: ?std.net.Address = null;119 var webui_listen: ?std.net.Address = null;
119120
121 if (try std.zig.EnvVar.ZIG_BUILD_ERROR_STYLE.get(arena)) |str| {
122 if (std.meta.stringToEnum(ErrorStyle, str)) |style| {
123 error_style = style;
124 }
125 }
126
127 if (try std.zig.EnvVar.ZIG_BUILD_MULTILINE_ERRORS.get(arena)) |str| {
128 if (std.meta.stringToEnum(MultilineErrors, str)) |style| {
129 multiline_errors = style;
130 }
131 }
132
120 while (nextArg(args, &arg_idx)) |arg| {133 while (nextArg(args, &arg_idx)) |arg| {
121 if (mem.startsWith(u8, arg, "-Z")) {134 if (mem.startsWith(u8, arg, "-Z")) {
122 if (arg.len != 18) fatalWithHint("bad argument: '{s}'", .{arg});135 if (arg.len != 18) fatalWithHint("bad argument: '{s}'", .{arg});
...@@ -197,11 +210,23 @@ pub fn main() !void {...@@ -197,11 +210,23 @@ pub fn main() !void {
197 arg, next_arg,210 arg, next_arg,
198 });211 });
199 };212 };
213 } else if (mem.eql(u8, arg, "--error-style")) {
214 const next_arg = nextArg(args, &arg_idx) orelse
215 fatalWithHint("expected style after '{s}'", .{arg});
216 error_style = std.meta.stringToEnum(ErrorStyle, next_arg) orelse {
217 fatalWithHint("expected style after '{s}', found '{s}'", .{ arg, next_arg });
218 };
219 } else if (mem.eql(u8, arg, "--multiline-errors")) {
220 const next_arg = nextArg(args, &arg_idx) orelse
221 fatalWithHint("expected style after '{s}'", .{arg});
222 multiline_errors = std.meta.stringToEnum(MultilineErrors, next_arg) orelse {
223 fatalWithHint("expected style after '{s}', found '{s}'", .{ arg, next_arg });
224 };
200 } else if (mem.eql(u8, arg, "--summary")) {225 } else if (mem.eql(u8, arg, "--summary")) {
201 const next_arg = nextArg(args, &arg_idx) orelse226 const next_arg = nextArg(args, &arg_idx) orelse
202 fatalWithHint("expected [all|new|failures|none] after '{s}'", .{arg});227 fatalWithHint("expected [all|new|failures|line|none] after '{s}'", .{arg});
203 summary = std.meta.stringToEnum(Summary, next_arg) orelse {228 summary = std.meta.stringToEnum(Summary, next_arg) orelse {
204 fatalWithHint("expected [all|new|failures|none] after '{s}', found '{s}'", .{229 fatalWithHint("expected [all|new|failures|line|none] after '{s}', found '{s}'", .{
205 arg, next_arg,230 arg, next_arg,
206 });231 });
207 };232 };
...@@ -273,8 +298,6 @@ pub fn main() !void {...@@ -273,8 +298,6 @@ pub fn main() !void {
273 builder.verbose_cc = true;298 builder.verbose_cc = true;
274 } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) {299 } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) {
275 builder.verbose_llvm_cpu_features = true;300 builder.verbose_llvm_cpu_features = true;
276 } else if (mem.eql(u8, arg, "--prominent-compile-errors")) {
277 prominent_compile_errors = true;
278 } else if (mem.eql(u8, arg, "--watch")) {301 } else if (mem.eql(u8, arg, "--watch")) {
279 watch = true;302 watch = true;
280 } else if (mem.eql(u8, arg, "--time-report")) {303 } else if (mem.eql(u8, arg, "--time-report")) {
...@@ -466,10 +489,11 @@ pub fn main() !void {...@@ -466,10 +489,11 @@ pub fn main() !void {
466 .web_server = undefined, // set after `prepare`489 .web_server = undefined, // set after `prepare`
467 .memory_blocked_steps = .empty,490 .memory_blocked_steps = .empty,
468 .step_stack = .empty,491 .step_stack = .empty,
469 .prominent_compile_errors = prominent_compile_errors,
470492
471 .claimed_rss = 0,493 .claimed_rss = 0,
472 .summary = summary orelse if (watch) .new else .failures,494 .error_style = error_style,
495 .multiline_errors = multiline_errors,
496 .summary = summary orelse if (watch or webui_listen != null) .line else .failures,
473 .ttyconf = ttyconf,497 .ttyconf = ttyconf,
474 .stderr = stderr,498 .stderr = stderr,
475 .thread_pool = undefined,499 .thread_pool = undefined,
...@@ -485,8 +509,14 @@ pub fn main() !void {...@@ -485,8 +509,14 @@ pub fn main() !void {
485 }509 }
486510
487 prepare(arena, builder, targets.items, &run, graph.random_seed) catch |err| switch (err) {511 prepare(arena, builder, targets.items, &run, graph.random_seed) catch |err| switch (err) {
488 error.UncleanExit => process.exit(1),512 error.DependencyLoopDetected => {
489 else => return err,513 // Perhaps in the future there could be an Advanced Options flag such as
514 // --debug-build-runner-leaks which would make this code return instead of
515 // calling exit.
516 std.debug.lockStdErr();
517 process.exit(1);
518 },
519 else => |e| return e,
490 };520 };
491521
492 var w: Watch = w: {522 var w: Watch = w: {
...@@ -516,22 +546,20 @@ pub fn main() !void {...@@ -516,22 +546,20 @@ pub fn main() !void {
516 ws.start() catch |err| fatal("failed to start web server: {s}", .{@errorName(err)});546 ws.start() catch |err| fatal("failed to start web server: {s}", .{@errorName(err)});
517 }547 }
518548
519 rebuild: while (true) {549 rebuild: while (true) : (if (run.error_style.clearOnUpdate()) {
550 const bw = std.debug.lockStderrWriter(&stdio_buffer_allocation);
551 defer std.debug.unlockStderrWriter();
552 try bw.writeAll("\x1B[2J\x1B[3J\x1B[H");
553 }) {
520 if (run.web_server) |*ws| ws.startBuild();554 if (run.web_server) |*ws| ws.startBuild();
521555
522 runStepNames(556 try runStepNames(
523 builder,557 builder,
524 targets.items,558 targets.items,
525 main_progress_node,559 main_progress_node,
526 &run,560 &run,
527 fuzz,561 fuzz,
528 ) catch |err| switch (err) {562 );
529 error.UncleanExit => {
530 assert(!run.watch and run.web_server == null);
531 process.exit(1);
532 },
533 else => return err,
534 };
535563
536 if (run.web_server) |*web_server| {564 if (run.web_server) |*web_server| {
537 if (fuzz) |mode| if (mode != .forever) fatal(565 if (fuzz) |mode| if (mode != .forever) fatal(
...@@ -542,10 +570,6 @@ pub fn main() !void {...@@ -542,10 +570,6 @@ pub fn main() !void {
542 web_server.finishBuild(.{ .fuzz = fuzz != null });570 web_server.finishBuild(.{ .fuzz = fuzz != null });
543 }571 }
544572
545 if (!watch and run.web_server == null) {
546 return cleanExit();
547 }
548
549 if (run.web_server) |*ws| {573 if (run.web_server) |*ws| {
550 assert(!watch); // fatal error after CLI parsing574 assert(!watch); // fatal error after CLI parsing
551 while (true) switch (ws.wait()) {575 while (true) switch (ws.wait()) {
...@@ -626,18 +650,14 @@ const Run = struct {...@@ -626,18 +650,14 @@ const Run = struct {
626 memory_blocked_steps: std.ArrayListUnmanaged(*Step),650 memory_blocked_steps: std.ArrayListUnmanaged(*Step),
627 /// Allocated into `gpa`.651 /// Allocated into `gpa`.
628 step_stack: std.AutoArrayHashMapUnmanaged(*Step, void),652 step_stack: std.AutoArrayHashMapUnmanaged(*Step, void),
629 prominent_compile_errors: bool,
630 thread_pool: std.Thread.Pool,653 thread_pool: std.Thread.Pool,
631654
632 claimed_rss: usize,655 claimed_rss: usize,
656 error_style: ErrorStyle,
657 multiline_errors: MultilineErrors,
633 summary: Summary,658 summary: Summary,
634 ttyconf: tty.Config,659 ttyconf: tty.Config,
635 stderr: File,660 stderr: File,
636
637 fn cleanExit(run: Run) void {
638 if (run.watch or run.web_server != null) return;
639 return runner.cleanExit();
640 }
641};661};
642662
643fn prepare(663fn prepare(
...@@ -671,10 +691,7 @@ fn prepare(...@@ -671,10 +691,7 @@ fn prepare(
671 rand.shuffle(*Step, starting_steps);691 rand.shuffle(*Step, starting_steps);
672692
673 for (starting_steps) |s| {693 for (starting_steps) |s| {
674 constructGraphAndCheckForDependencyLoop(gpa, b, s, &run.step_stack, rand) catch |err| switch (err) {694 try constructGraphAndCheckForDependencyLoop(gpa, b, s, &run.step_stack, rand);
675 error.DependencyLoopDetected => return uncleanExit(),
676 else => |e| return e,
677 };
678 }695 }
679696
680 {697 {
...@@ -827,26 +844,25 @@ fn runStepNames(...@@ -827,26 +844,25 @@ fn runStepNames(
827 // Every test has a state844 // Every test has a state
828 assert(test_pass_count + test_skip_count + test_fail_count + test_crash_count + test_timeout_count == test_count);845 assert(test_pass_count + test_skip_count + test_fail_count + test_crash_count + test_timeout_count == test_count);
829846
830 // A proper command line application defaults to silently succeeding.
831 // The user may request verbose mode if they have a different preference.
832 const failures_only = switch (run.summary) {
833 .failures, .none => true,
834 else => false,
835 };
836 if (failure_count == 0) {847 if (failure_count == 0) {
837 std.Progress.setStatus(.success);848 std.Progress.setStatus(.success);
838 if (failures_only) return run.cleanExit();
839 } else {849 } else {
840 std.Progress.setStatus(.failure);850 std.Progress.setStatus(.failure);
841 }851 }
842852
843 if (run.summary != .none) {853 summary: {
854 switch (run.summary) {
855 .all, .new, .line => {},
856 .failures => if (failure_count == 0) break :summary,
857 .none => break :summary,
858 }
859
844 const w = std.debug.lockStderrWriter(&stdio_buffer_allocation);860 const w = std.debug.lockStderrWriter(&stdio_buffer_allocation);
845 defer std.debug.unlockStderrWriter();861 defer std.debug.unlockStderrWriter();
846862
847 const total_count = success_count + failure_count + pending_count + skipped_count;863 const total_count = success_count + failure_count + pending_count + skipped_count;
848 ttyconf.setColor(w, .cyan) catch {};864 ttyconf.setColor(w, .cyan) catch {};
849 w.writeAll("\nBuild Summary:") catch {};865 w.writeAll("Build Summary:") catch {};
850 ttyconf.setColor(w, .reset) catch {};866 ttyconf.setColor(w, .reset) catch {};
851 w.print(" {d}/{d} steps succeeded", .{ success_count, total_count }) catch {};867 w.print(" {d}/{d} steps succeeded", .{ success_count, total_count }) catch {};
852 if (skipped_count > 0) w.print(", {d} skipped", .{skipped_count}) catch {};868 if (skipped_count > 0) w.print(", {d} skipped", .{skipped_count}) catch {};
...@@ -862,6 +878,8 @@ fn runStepNames(...@@ -862,6 +878,8 @@ fn runStepNames(
862878
863 w.writeAll("\n") catch {};879 w.writeAll("\n") catch {};
864880
881 if (run.summary == .line) break :summary;
882
865 // Print a fancy tree with build results.883 // Print a fancy tree with build results.
866 var step_stack_copy = try step_stack.clone(gpa);884 var step_stack_copy = try step_stack.clone(gpa);
867 defer step_stack_copy.deinit(gpa);885 defer step_stack_copy.deinit(gpa);
...@@ -877,7 +895,7 @@ fn runStepNames(...@@ -877,7 +895,7 @@ fn runStepNames(
877 i -= 1;895 i -= 1;
878 const step = b.top_level_steps.get(step_names[i]).?.step;896 const step = b.top_level_steps.get(step_names[i]).?.step;
879 const found = switch (run.summary) {897 const found = switch (run.summary) {
880 .all, .none => unreachable,898 .all, .line, .none => unreachable,
881 .failures => step.state != .success,899 .failures => step.state != .success,
882 .new => !step.result_cached,900 .new => !step.result_cached,
883 };901 };
...@@ -894,28 +912,19 @@ fn runStepNames(...@@ -894,28 +912,19 @@ fn runStepNames(
894 w.writeByte('\n') catch {};912 w.writeByte('\n') catch {};
895 }913 }
896914
897 if (failure_count == 0) {915 if (run.watch or run.web_server != null) return;
898 return run.cleanExit();
899 }
900
901 // Finally, render compile errors at the bottom of the terminal.
902 if (run.prominent_compile_errors and total_compile_errors > 0) {
903 for (step_stack.keys()) |s| {
904 if (s.result_error_bundle.errorMessageCount() > 0) {
905 s.result_error_bundle.renderToStdErr(.{ .ttyconf = ttyconf });
906 }
907 }
908916
909 if (!run.watch and run.web_server == null) {917 // Perhaps in the future there could be an Advanced Options flag such as
910 // Signal to parent process that we have printed compile errors. The918 // --debug-build-runner-leaks which would make this code return instead of
911 // parent process may choose to omit the "following command failed"919 // calling exit.
912 // line in this case.
913 std.debug.lockStdErr();
914 process.exit(2);
915 }
916 }
917920
918 if (!run.watch and run.web_server == null) return uncleanExit();921 const code: u8 = code: {
922 if (failure_count == 0) break :code 0; // success
923 if (run.error_style.verboseContext()) break :code 1; // failure; print build command
924 break :code 2; // failure; do not print build command
925 };
926 std.debug.lockStdErr();
927 process.exit(code);
919}928}
920929
921const PrintNode = struct {930const PrintNode = struct {
...@@ -1124,7 +1133,7 @@ fn printTreeStep(...@@ -1124,7 +1133,7 @@ fn printTreeStep(
1124 const first = step_stack.swapRemove(s);1133 const first = step_stack.swapRemove(s);
1125 const summary = run.summary;1134 const summary = run.summary;
1126 const skip = switch (summary) {1135 const skip = switch (summary) {
1127 .none => unreachable,1136 .none, .line => unreachable,
1128 .all => false,1137 .all => false,
1129 .new => s.result_cached,1138 .new => s.result_cached,
1130 .failures => s.state == .success,1139 .failures => s.state == .success,
...@@ -1157,7 +1166,7 @@ fn printTreeStep(...@@ -1157,7 +1166,7 @@ fn printTreeStep(
11571166
1158 const step = s.dependencies.items[i];1167 const step = s.dependencies.items[i];
1159 const found = switch (summary) {1168 const found = switch (summary) {
1160 .all, .none => unreachable,1169 .all, .line, .none => unreachable,
1161 .failures => step.state != .success,1170 .failures => step.state != .success,
1162 .new => !step.result_cached,1171 .new => !step.result_cached,
1163 };1172 };
...@@ -1316,15 +1325,13 @@ fn workerMakeOneStep(...@@ -1316,15 +1325,13 @@ fn workerMakeOneStep(
1316 });1325 });
13171326
1318 // No matter the result, we want to display error/warning messages.1327 // No matter the result, we want to display error/warning messages.
1319 const show_compile_errors = !run.prominent_compile_errors and1328 const show_compile_errors = s.result_error_bundle.errorMessageCount() > 0;
1320 s.result_error_bundle.errorMessageCount() > 0;
1321 const show_error_msgs = s.result_error_msgs.items.len > 0;1329 const show_error_msgs = s.result_error_msgs.items.len > 0;
1322 const show_stderr = s.result_stderr.len > 0;1330 const show_stderr = s.result_stderr.len > 0;
1323
1324 if (show_error_msgs or show_compile_errors or show_stderr) {1331 if (show_error_msgs or show_compile_errors or show_stderr) {
1325 const bw = std.debug.lockStderrWriter(&stdio_buffer_allocation);1332 const bw = std.debug.lockStderrWriter(&stdio_buffer_allocation);
1326 defer std.debug.unlockStderrWriter();1333 defer std.debug.unlockStderrWriter();
1327 printErrorMessages(run.gpa, s, .{ .ttyconf = run.ttyconf }, bw, run.prominent_compile_errors) catch {};1334 printErrorMessages(run.gpa, s, .{ .ttyconf = run.ttyconf }, bw, run.error_style, run.multiline_errors) catch {};
1328 }1335 }
13291336
1330 handle_result: {1337 handle_result: {
...@@ -1388,37 +1395,46 @@ pub fn printErrorMessages(...@@ -1388,37 +1395,46 @@ pub fn printErrorMessages(
1388 failing_step: *Step,1395 failing_step: *Step,
1389 options: std.zig.ErrorBundle.RenderOptions,1396 options: std.zig.ErrorBundle.RenderOptions,
1390 stderr: *Writer,1397 stderr: *Writer,
1391 prominent_compile_errors: bool,1398 error_style: ErrorStyle,
1399 multiline_errors: MultilineErrors,
1392) !void {1400) !void {
1393 // Provide context for where these error messages are coming from by
1394 // printing the corresponding Step subtree.
1395
1396 var step_stack: std.ArrayListUnmanaged(*Step) = .empty;
1397 defer step_stack.deinit(gpa);
1398 try step_stack.append(gpa, failing_step);
1399 while (step_stack.items[step_stack.items.len - 1].dependants.items.len != 0) {
1400 try step_stack.append(gpa, step_stack.items[step_stack.items.len - 1].dependants.items[0]);
1401 }
1402
1403 // Now, `step_stack` has the subtree that we want to print, in reverse order.
1404 const ttyconf = options.ttyconf;1401 const ttyconf = options.ttyconf;
1405 try ttyconf.setColor(stderr, .dim);1402
1406 var indent: usize = 0;1403 if (error_style.verboseContext()) {
1407 while (step_stack.pop()) |s| : (indent += 1) {1404 // Provide context for where these error messages are coming from by
1408 if (indent > 0) {1405 // printing the corresponding Step subtree.
1409 try stderr.splatByteAll(' ', (indent - 1) * 3);1406 var step_stack: std.ArrayListUnmanaged(*Step) = .empty;
1410 try printChildNodePrefix(stderr, ttyconf);1407 defer step_stack.deinit(gpa);
1408 try step_stack.append(gpa, failing_step);
1409 while (step_stack.items[step_stack.items.len - 1].dependants.items.len != 0) {
1410 try step_stack.append(gpa, step_stack.items[step_stack.items.len - 1].dependants.items[0]);
1411 }1411 }
14121412
1413 try stderr.writeAll(s.name);1413 // Now, `step_stack` has the subtree that we want to print, in reverse order.
1414 try ttyconf.setColor(stderr, .dim);
1415 var indent: usize = 0;
1416 while (step_stack.pop()) |s| : (indent += 1) {
1417 if (indent > 0) {
1418 try stderr.splatByteAll(' ', (indent - 1) * 3);
1419 try printChildNodePrefix(stderr, ttyconf);
1420 }
14141421
1415 if (s == failing_step) {1422 try stderr.writeAll(s.name);
1416 try printStepFailure(s, stderr, ttyconf);1423
1417 } else {1424 if (s == failing_step) {
1418 try stderr.writeAll("\n");1425 try printStepFailure(s, stderr, ttyconf);
1426 } else {
1427 try stderr.writeAll("\n");
1428 }
1419 }1429 }
1430 try ttyconf.setColor(stderr, .reset);
1431 } else {
1432 // Just print the failing step itself.
1433 try ttyconf.setColor(stderr, .dim);
1434 try stderr.writeAll(failing_step.name);
1435 try printStepFailure(failing_step, stderr, ttyconf);
1436 try ttyconf.setColor(stderr, .reset);
1420 }1437 }
1421 try ttyconf.setColor(stderr, .reset);
14221438
1423 if (failing_step.result_stderr.len > 0) {1439 if (failing_step.result_stderr.len > 0) {
1424 try stderr.writeAll(failing_step.result_stderr);1440 try stderr.writeAll(failing_step.result_stderr);
...@@ -1427,30 +1443,38 @@ pub fn printErrorMessages(...@@ -1427,30 +1443,38 @@ pub fn printErrorMessages(
1427 }1443 }
1428 }1444 }
14291445
1430 if (!prominent_compile_errors and failing_step.result_error_bundle.errorMessageCount() > 0) {1446 try failing_step.result_error_bundle.renderToWriter(options, stderr);
1431 try failing_step.result_error_bundle.renderToWriter(options, stderr);
1432 }
14331447
1434 for (failing_step.result_error_msgs.items) |msg| {1448 for (failing_step.result_error_msgs.items) |msg| {
1435 try ttyconf.setColor(stderr, .red);1449 try ttyconf.setColor(stderr, .red);
1436 try stderr.writeAll("error: ");1450 try stderr.writeAll("error:");
1437 try ttyconf.setColor(stderr, .reset);1451 try ttyconf.setColor(stderr, .reset);
1438 // If the message has multiple lines, indent the non-initial ones to align them with the 'error:' text.1452 if (std.mem.indexOfScalar(u8, msg, '\n') == null) {
1439 var it = std.mem.splitScalar(u8, msg, '\n');1453 try stderr.print(" {s}\n", .{msg});
1440 try stderr.writeAll(it.first());1454 } else switch (multiline_errors) {
1441 while (it.next()) |line| {1455 .indent => {
1442 try stderr.print("\n {s}", .{line});1456 var it = std.mem.splitScalar(u8, msg, '\n');
1457 try stderr.print(" {s}\n", .{it.first()});
1458 while (it.next()) |line| {
1459 try stderr.print(" {s}\n", .{line});
1460 }
1461 },
1462 .newline => try stderr.print("\n{s}\n", .{msg}),
1463 .none => try stderr.print(" {s}\n", .{msg}),
1443 }1464 }
1444 try stderr.writeAll("\n");
1445 }1465 }
14461466
1447 if (failing_step.result_failed_command) |cmd_str| {1467 if (error_style.verboseContext()) {
1448 try ttyconf.setColor(stderr, .red);1468 if (failing_step.result_failed_command) |cmd_str| {
1449 try stderr.writeAll("failed command: ");1469 try ttyconf.setColor(stderr, .red);
1450 try ttyconf.setColor(stderr, .reset);1470 try stderr.writeAll("failed command: ");
1451 try stderr.writeAll(cmd_str);1471 try ttyconf.setColor(stderr, .reset);
1452 try stderr.writeByte('\n');1472 try stderr.writeAll(cmd_str);
1473 try stderr.writeByte('\n');
1474 }
1453 }1475 }
1476
1477 try stderr.writeByte('\n');
1454}1478}
14551479
1456fn printSteps(builder: *std.Build, w: *Writer) !void {1480fn printSteps(builder: *std.Build, w: *Writer) !void {
...@@ -1505,11 +1529,20 @@ fn printUsage(b: *std.Build, w: *Writer) !void {...@@ -1505,11 +1529,20 @@ fn printUsage(b: *std.Build, w: *Writer) !void {
1505 \\ -l, --list-steps Print available steps1529 \\ -l, --list-steps Print available steps
1506 \\ --verbose Print commands before executing them1530 \\ --verbose Print commands before executing them
1507 \\ --color [auto|off|on] Enable or disable colored error messages1531 \\ --color [auto|off|on] Enable or disable colored error messages
1508 \\ --prominent-compile-errors Buffer compile errors and display at end1532 \\ --error-style [style] Control how build errors are printed
1533 \\ verbose (Default) Report errors with full context
1534 \\ minimal Report errors after summary, excluding context like command lines
1535 \\ verbose_clear Like 'verbose', but clear the terminal at the start of each update
1536 \\ minimal_clear Like 'minimal', but clear the terminal at the start of each update
1537 \\ --multiline-errors [style] Control how multi-line error messages are printed
1538 \\ indent (Default) Indent non-initial lines to align with initial line
1539 \\ newline Include a leading newline so that the error message is on its own lines
1540 \\ none Print as usual so the first line is misaligned
1509 \\ --summary [mode] Control the printing of the build summary1541 \\ --summary [mode] Control the printing of the build summary
1510 \\ all Print the build summary in its entirety1542 \\ all Print the build summary in its entirety
1511 \\ new Omit cached steps1543 \\ new Omit cached steps
1512 \\ failures (Default) Only print failed steps1544 \\ failures (Default if short-lived) Only print failed steps
1545 \\ line (Default if long-lived) Only print the single-line summary
1513 \\ none Do not print the build summary1546 \\ none Do not print the build summary
1514 \\ -j<N> Limit concurrent jobs (default is to use all CPU cores)1547 \\ -j<N> Limit concurrent jobs (default is to use all CPU cores)
1515 \\ --maxrss <bytes> Limit memory usage (default is to use available memory)1548 \\ --maxrss <bytes> Limit memory usage (default is to use available memory)
...@@ -1633,24 +1666,27 @@ fn argsRest(args: []const [:0]const u8, idx: usize) ?[]const [:0]const u8 {...@@ -1633,24 +1666,27 @@ fn argsRest(args: []const [:0]const u8, idx: usize) ?[]const [:0]const u8 {
1633 return args[idx..];1666 return args[idx..];
1634}1667}
16351668
1636/// Perhaps in the future there could be an Advanced Options flag such as
1637/// --debug-build-runner-leaks which would make this function return instead of
1638/// calling exit.
1639fn cleanExit() void {
1640 std.debug.lockStdErr();
1641 process.exit(0);
1642}
1643
1644/// Perhaps in the future there could be an Advanced Options flag such as
1645/// --debug-build-runner-leaks which would make this function return instead of
1646/// calling exit.
1647fn uncleanExit() error{UncleanExit} {
1648 std.debug.lockStdErr();
1649 process.exit(1);
1650}
1651
1652const Color = std.zig.Color;1669const Color = std.zig.Color;
1653const Summary = enum { all, new, failures, none };1670const ErrorStyle = enum {
1671 verbose,
1672 minimal,
1673 verbose_clear,
1674 minimal_clear,
1675 fn verboseContext(s: ErrorStyle) bool {
1676 return switch (s) {
1677 .verbose, .verbose_clear => true,
1678 .minimal, .minimal_clear => false,
1679 };
1680 }
1681 fn clearOnUpdate(s: ErrorStyle) bool {
1682 return switch (s) {
1683 .verbose, .minimal => false,
1684 .verbose_clear, .minimal_clear => true,
1685 };
1686 }
1687};
1688const MultilineErrors = enum { indent, newline, none };
1689const Summary = enum { all, new, failures, line, none };
16541690
1655fn get_tty_conf(color: Color, stderr: File) tty.Config {1691fn get_tty_conf(color: Color, stderr: File) tty.Config {
1656 return switch (color) {1692 return switch (color) {
lib/std/Build/Fuzz.zig+2-2
...@@ -178,7 +178,7 @@ fn rebuildTestsWorkerRunFallible(run: *Step.Run, gpa: Allocator, ttyconf: std.Io...@@ -178,7 +178,7 @@ fn rebuildTestsWorkerRunFallible(run: *Step.Run, gpa: Allocator, ttyconf: std.Io
178 var buf: [256]u8 = undefined;178 var buf: [256]u8 = undefined;
179 const w = std.debug.lockStderrWriter(&buf);179 const w = std.debug.lockStderrWriter(&buf);
180 defer std.debug.unlockStderrWriter();180 defer std.debug.unlockStderrWriter();
181 build_runner.printErrorMessages(gpa, &compile.step, .{ .ttyconf = ttyconf }, w, false) catch {};181 build_runner.printErrorMessages(gpa, &compile.step, .{ .ttyconf = ttyconf }, w, .verbose, .indent) catch {};
182 }182 }
183183
184 const rebuilt_bin_path = result catch |err| switch (err) {184 const rebuilt_bin_path = result catch |err| switch (err) {
...@@ -204,7 +204,7 @@ fn fuzzWorkerRun(...@@ -204,7 +204,7 @@ fn fuzzWorkerRun(
204 var buf: [256]u8 = undefined;204 var buf: [256]u8 = undefined;
205 const w = std.debug.lockStderrWriter(&buf);205 const w = std.debug.lockStderrWriter(&buf);
206 defer std.debug.unlockStderrWriter();206 defer std.debug.unlockStderrWriter();
207 build_runner.printErrorMessages(gpa, &run.step, .{ .ttyconf = fuzz.ttyconf }, w, false) catch {};207 build_runner.printErrorMessages(gpa, &run.step, .{ .ttyconf = fuzz.ttyconf }, w, .verbose, .indent) catch {};
208 return;208 return;
209 },209 },
210 else => {210 else => {
lib/std/zig.zig+2
...@@ -697,6 +697,8 @@ pub const EnvVar = enum {...@@ -697,6 +697,8 @@ pub const EnvVar = enum {
697 ZIG_LIB_DIR,697 ZIG_LIB_DIR,
698 ZIG_LIBC,698 ZIG_LIBC,
699 ZIG_BUILD_RUNNER,699 ZIG_BUILD_RUNNER,
700 ZIG_BUILD_ERROR_STYLE,
701 ZIG_BUILD_MULTILINE_ERRORS,
700 ZIG_VERBOSE_LINK,702 ZIG_VERBOSE_LINK,
701 ZIG_VERBOSE_CC,703 ZIG_VERBOSE_CC,
702 ZIG_BTRFS_WORKAROUND,704 ZIG_BTRFS_WORKAROUND,