| ... | @@ -103,12 +103,13 @@ pub fn main() !void { | ... | @@ -103,12 +103,13 @@ pub fn main() !void { |
| 103 | | 103 | |
| 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; |
| 119 | | 120 | |
| | 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) orelse | 226 | 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, | | |
| 470 | | 492 | |
| 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 | } |
| 486 | | 510 | |
| 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 | }; |
| 491 | | 521 | |
| 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 | } |
| 518 | | 548 | |
| 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(); |
| 521 | | 555 | |
| 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 | }; | | |
| 535 | | 563 | |
| 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 | } |
| 544 | | 572 | |
| 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 parsing | 574 | 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, |
| 631 | | 654 | |
| 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 | }; |
| 642 | | 662 | |
| 643 | fn prepare( | 663 | fn 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); |
| 672 | | 692 | |
| 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 | } |
| 679 | | 696 | |
| 680 | { | 697 | { |
| ... | @@ -827,26 +844,25 @@ fn runStepNames( | ... | @@ -827,26 +844,25 @@ fn runStepNames( |
| 827 | // Every test has a state | 844 | // 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); |
| 829 | | 846 | |
| 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 | } |
| 842 | | 852 | |
| 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(); |
| 846 | | 862 | |
| 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( |
| 862 | | 878 | |
| 863 | w.writeAll("\n") catch {}; | 879 | w.writeAll("\n") catch {}; |
| 864 | | 880 | |
| | 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 | } |
| 896 | | 914 | |
| 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 | } | | |
| 908 | | 916 | |
| 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. The | 918 | // --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 | } | | |
| 917 | | 920 | |
| 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 | } |
| 920 | | 929 | |
| 921 | const PrintNode = struct { | 930 | const 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( |
| 1157 | | 1166 | |
| 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 | }); |
| 1317 | | 1326 | |
| 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 and | 1328 | 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 | } |
| 1329 | | 1336 | |
| 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 | } |
| 1412 | | 1412 | |
| 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 | } |
| 1414 | | 1421 | |
| 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); | | |
| 1422 | | 1438 | |
| 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 | } |
| 1429 | | 1445 | |
| 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 | } | | |
| 1433 | | 1447 | |
| 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 | } |
| 1446 | | 1466 | |
| 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 | } |
| 1455 | | 1479 | |
| 1456 | fn printSteps(builder: *std.Build, w: *Writer) !void { | 1480 | fn 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 steps | 1529 | \\ -l, --list-steps Print available steps |
| 1506 | \\ --verbose Print commands before executing them | 1530 | \\ --verbose Print commands before executing them |
| 1507 | \\ --color [auto|off|on] Enable or disable colored error messages | 1531 | \\ --color [auto|off|on] Enable or disable colored error messages |
| 1508 | \\ --prominent-compile-errors Buffer compile errors and display at end | 1532 | \\ --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 summary | 1541 | \\ --summary [mode] Control the printing of the build summary |
| 1510 | \\ all Print the build summary in its entirety | 1542 | \\ all Print the build summary in its entirety |
| 1511 | \\ new Omit cached steps | 1543 | \\ new Omit cached steps |
| 1512 | \\ failures (Default) Only print failed steps | 1544 | \\ 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 summary | 1546 | \\ 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 | } |
| 1635 | | 1668 | |
| 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. | | |
| 1639 | fn 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. | | |
| 1647 | fn uncleanExit() error{UncleanExit} { | | |
| 1648 | std.debug.lockStdErr(); | | |
| 1649 | process.exit(1); | | |
| 1650 | } | | |
| 1651 | | | |
| 1652 | const Color = std.zig.Color; | 1669 | const Color = std.zig.Color; |
| 1653 | const Summary = enum { all, new, failures, none }; | 1670 | const 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 | }; |
| | 1688 | const MultilineErrors = enum { indent, newline, none }; |
| | 1689 | const Summary = enum { all, new, failures, line, none }; |
| 1654 | | 1690 | |
| 1655 | fn get_tty_conf(color: Color, stderr: File) tty.Config { | 1691 | fn get_tty_conf(color: Color, stderr: File) tty.Config { |
| 1656 | return switch (color) { | 1692 | return switch (color) { |