authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-02 11:16:20-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-07 22:43:52-07:00
logcce32bd1d505ee5c5ea7878c9e9e57b6b63856f2
tree944f136cbdd9d6662cbae2c14bbf7d8d862739bb
parentec3b5f0c7474b22dfbf2f19e0121a1f87a58efd0

fix build runner


8 files changed, 254 insertions(+), 251 deletions(-)

lib/compiler/build_runner.zig+81-68
...@@ -12,6 +12,7 @@ const Watch = std.Build.Watch;...@@ -12,6 +12,7 @@ const Watch = std.Build.Watch;
12const Fuzz = std.Build.Fuzz;12const Fuzz = std.Build.Fuzz;
13const Allocator = std.mem.Allocator;13const Allocator = std.mem.Allocator;
14const fatal = std.process.fatal;14const fatal = std.process.fatal;
15const Writer = std.io.Writer;
15const runner = @This();16const runner = @This();
1617
17pub const root = @import("@build");18pub const root = @import("@build");
...@@ -330,7 +331,7 @@ pub fn main() !void {...@@ -330,7 +331,7 @@ pub fn main() !void {
330 }331 }
331 }332 }
332333
333 const stderr = std.fs.File.stderr();334 const stderr: std.fs.File = .stderr();
334 const ttyconf = get_tty_conf(color, stderr);335 const ttyconf = get_tty_conf(color, stderr);
335 switch (ttyconf) {336 switch (ttyconf) {
336 .no_color => try graph.env_map.put("NO_COLOR", "1"),337 .no_color => try graph.env_map.put("NO_COLOR", "1"),
...@@ -378,13 +379,19 @@ pub fn main() !void {...@@ -378,13 +379,19 @@ pub fn main() !void {
378379
379 validateSystemLibraryOptions(builder);380 validateSystemLibraryOptions(builder);
380381
381 const stdout_writer = std.fs.File.stdout().deprecatedWriter();382 if (help_menu) {
382383 var w = initStdoutWriter();
383 if (help_menu)384 printUsage(builder, w) catch return stdout_writer_allocation.err.?;
384 return usage(builder, stdout_writer);385 w.flush() catch return stdout_writer_allocation.err.?;
386 return;
387 }
385388
386 if (steps_menu)389 if (steps_menu) {
387 return steps(builder, stdout_writer);390 var w = initStdoutWriter();
391 printSteps(builder, w) catch return stdout_writer_allocation.err.?;
392 w.flush() catch return stdout_writer_allocation.err.?;
393 return;
394 }
388395
389 var run: Run = .{396 var run: Run = .{
390 .max_rss = max_rss,397 .max_rss = max_rss,
...@@ -696,24 +703,21 @@ fn runStepNames(...@@ -696,24 +703,21 @@ fn runStepNames(
696 const ttyconf = run.ttyconf;703 const ttyconf = run.ttyconf;
697704
698 if (run.summary != .none) {705 if (run.summary != .none) {
699 std.debug.lockStdErr();706 const w = std.debug.lockStderrWriter(&stdio_buffer_allocation);
700 defer std.debug.unlockStdErr();707 defer std.debug.unlockStderrWriter();
701 const stderr = run.stderr;
702708
703 const total_count = success_count + failure_count + pending_count + skipped_count;709 const total_count = success_count + failure_count + pending_count + skipped_count;
704 ttyconf.setColor(stderr, .cyan) catch {};710 ttyconf.setColor(w, .cyan) catch {};
705 stderr.writeAll("Build Summary:") catch {};711 w.writeAll("Build Summary:") catch {};
706 ttyconf.setColor(stderr, .reset) catch {};712 ttyconf.setColor(w, .reset) catch {};
707 stderr.deprecatedWriter().print(" {d}/{d} steps succeeded", .{ success_count, total_count }) catch {};713 w.print(" {d}/{d} steps succeeded", .{ success_count, total_count }) catch {};
708 if (skipped_count > 0) stderr.deprecatedWriter().print("; {d} skipped", .{skipped_count}) catch {};714 if (skipped_count > 0) w.print("; {d} skipped", .{skipped_count}) catch {};
709 if (failure_count > 0) stderr.deprecatedWriter().print("; {d} failed", .{failure_count}) catch {};715 if (failure_count > 0) w.print("; {d} failed", .{failure_count}) catch {};
710716
711 if (test_count > 0) stderr.deprecatedWriter().print("; {d}/{d} tests passed", .{ test_pass_count, test_count }) catch {};717 if (test_count > 0) w.print("; {d}/{d} tests passed", .{ test_pass_count, test_count }) catch {};
712 if (test_skip_count > 0) stderr.deprecatedWriter().print("; {d} skipped", .{test_skip_count}) catch {};718 if (test_skip_count > 0) w.print("; {d} skipped", .{test_skip_count}) catch {};
713 if (test_fail_count > 0) stderr.deprecatedWriter().print("; {d} failed", .{test_fail_count}) catch {};719 if (test_fail_count > 0) w.print("; {d} failed", .{test_fail_count}) catch {};
714 if (test_leak_count > 0) stderr.deprecatedWriter().print("; {d} leaked", .{test_leak_count}) catch {};720 if (test_leak_count > 0) w.print("; {d} leaked", .{test_leak_count}) catch {};
715
716 stderr.writeAll("\n") catch {};
717721
718 // Print a fancy tree with build results.722 // Print a fancy tree with build results.
719 var step_stack_copy = try step_stack.clone(gpa);723 var step_stack_copy = try step_stack.clone(gpa);
...@@ -722,7 +726,7 @@ fn runStepNames(...@@ -722,7 +726,7 @@ fn runStepNames(
722 var print_node: PrintNode = .{ .parent = null };726 var print_node: PrintNode = .{ .parent = null };
723 if (step_names.len == 0) {727 if (step_names.len == 0) {
724 print_node.last = true;728 print_node.last = true;
725 printTreeStep(b, b.default_step, run, stderr, ttyconf, &print_node, &step_stack_copy) catch {};729 printTreeStep(b, b.default_step, run, w, ttyconf, &print_node, &step_stack_copy) catch {};
726 } else {730 } else {
727 const last_index = if (run.summary == .all) b.top_level_steps.count() else blk: {731 const last_index = if (run.summary == .all) b.top_level_steps.count() else blk: {
728 var i: usize = step_names.len;732 var i: usize = step_names.len;
...@@ -741,9 +745,10 @@ fn runStepNames(...@@ -741,9 +745,10 @@ fn runStepNames(
741 for (step_names, 0..) |step_name, i| {745 for (step_names, 0..) |step_name, i| {
742 const tls = b.top_level_steps.get(step_name).?;746 const tls = b.top_level_steps.get(step_name).?;
743 print_node.last = i + 1 == last_index;747 print_node.last = i + 1 == last_index;
744 printTreeStep(b, &tls.step, run, stderr, ttyconf, &print_node, &step_stack_copy) catch {};748 printTreeStep(b, &tls.step, run, w, ttyconf, &print_node, &step_stack_copy) catch {};
745 }749 }
746 }750 }
751 w.writeByte('\n') catch {};
747 }752 }
748753
749 if (failure_count == 0) {754 if (failure_count == 0) {
...@@ -775,7 +780,7 @@ const PrintNode = struct {...@@ -775,7 +780,7 @@ const PrintNode = struct {
775 last: bool = false,780 last: bool = false,
776};781};
777782
778fn printPrefix(node: *PrintNode, stderr: File, ttyconf: std.io.tty.Config) !void {783fn printPrefix(node: *PrintNode, stderr: *Writer, ttyconf: std.io.tty.Config) !void {
779 const parent = node.parent orelse return;784 const parent = node.parent orelse return;
780 if (parent.parent == null) return;785 if (parent.parent == null) return;
781 try printPrefix(parent, stderr, ttyconf);786 try printPrefix(parent, stderr, ttyconf);
...@@ -789,7 +794,7 @@ fn printPrefix(node: *PrintNode, stderr: File, ttyconf: std.io.tty.Config) !void...@@ -789,7 +794,7 @@ fn printPrefix(node: *PrintNode, stderr: File, ttyconf: std.io.tty.Config) !void
789 }794 }
790}795}
791796
792fn printChildNodePrefix(stderr: File, ttyconf: std.io.tty.Config) !void {797fn printChildNodePrefix(stderr: *Writer, ttyconf: std.io.tty.Config) !void {
793 try stderr.writeAll(switch (ttyconf) {798 try stderr.writeAll(switch (ttyconf) {
794 .no_color, .windows_api => "+- ",799 .no_color, .windows_api => "+- ",
795 .escape_codes => "\x1B\x28\x30\x6d\x71\x1B\x28\x42 ", // └─800 .escape_codes => "\x1B\x28\x30\x6d\x71\x1B\x28\x42 ", // └─
...@@ -798,7 +803,7 @@ fn printChildNodePrefix(stderr: File, ttyconf: std.io.tty.Config) !void {...@@ -798,7 +803,7 @@ fn printChildNodePrefix(stderr: File, ttyconf: std.io.tty.Config) !void {
798803
799fn printStepStatus(804fn printStepStatus(
800 s: *Step,805 s: *Step,
801 stderr: File,806 stderr: *Writer,
802 ttyconf: std.io.tty.Config,807 ttyconf: std.io.tty.Config,
803 run: *const Run,808 run: *const Run,
804) !void {809) !void {
...@@ -820,10 +825,10 @@ fn printStepStatus(...@@ -820,10 +825,10 @@ fn printStepStatus(
820 try stderr.writeAll(" cached");825 try stderr.writeAll(" cached");
821 } else if (s.test_results.test_count > 0) {826 } else if (s.test_results.test_count > 0) {
822 const pass_count = s.test_results.passCount();827 const pass_count = s.test_results.passCount();
823 try stderr.deprecatedWriter().print(" {d} passed", .{pass_count});828 try stderr.print(" {d} passed", .{pass_count});
824 if (s.test_results.skip_count > 0) {829 if (s.test_results.skip_count > 0) {
825 try ttyconf.setColor(stderr, .yellow);830 try ttyconf.setColor(stderr, .yellow);
826 try stderr.deprecatedWriter().print(" {d} skipped", .{s.test_results.skip_count});831 try stderr.print(" {d} skipped", .{s.test_results.skip_count});
827 }832 }
828 } else {833 } else {
829 try stderr.writeAll(" success");834 try stderr.writeAll(" success");
...@@ -832,15 +837,15 @@ fn printStepStatus(...@@ -832,15 +837,15 @@ fn printStepStatus(
832 if (s.result_duration_ns) |ns| {837 if (s.result_duration_ns) |ns| {
833 try ttyconf.setColor(stderr, .dim);838 try ttyconf.setColor(stderr, .dim);
834 if (ns >= std.time.ns_per_min) {839 if (ns >= std.time.ns_per_min) {
835 try stderr.deprecatedWriter().print(" {d}m", .{ns / std.time.ns_per_min});840 try stderr.print(" {d}m", .{ns / std.time.ns_per_min});
836 } else if (ns >= std.time.ns_per_s) {841 } else if (ns >= std.time.ns_per_s) {
837 try stderr.deprecatedWriter().print(" {d}s", .{ns / std.time.ns_per_s});842 try stderr.print(" {d}s", .{ns / std.time.ns_per_s});
838 } else if (ns >= std.time.ns_per_ms) {843 } else if (ns >= std.time.ns_per_ms) {
839 try stderr.deprecatedWriter().print(" {d}ms", .{ns / std.time.ns_per_ms});844 try stderr.print(" {d}ms", .{ns / std.time.ns_per_ms});
840 } else if (ns >= std.time.ns_per_us) {845 } else if (ns >= std.time.ns_per_us) {
841 try stderr.deprecatedWriter().print(" {d}us", .{ns / std.time.ns_per_us});846 try stderr.print(" {d}us", .{ns / std.time.ns_per_us});
842 } else {847 } else {
843 try stderr.deprecatedWriter().print(" {d}ns", .{ns});848 try stderr.print(" {d}ns", .{ns});
844 }849 }
845 try ttyconf.setColor(stderr, .reset);850 try ttyconf.setColor(stderr, .reset);
846 }851 }
...@@ -848,13 +853,13 @@ fn printStepStatus(...@@ -848,13 +853,13 @@ fn printStepStatus(
848 const rss = s.result_peak_rss;853 const rss = s.result_peak_rss;
849 try ttyconf.setColor(stderr, .dim);854 try ttyconf.setColor(stderr, .dim);
850 if (rss >= 1000_000_000) {855 if (rss >= 1000_000_000) {
851 try stderr.deprecatedWriter().print(" MaxRSS:{d}G", .{rss / 1000_000_000});856 try stderr.print(" MaxRSS:{d}G", .{rss / 1000_000_000});
852 } else if (rss >= 1000_000) {857 } else if (rss >= 1000_000) {
853 try stderr.deprecatedWriter().print(" MaxRSS:{d}M", .{rss / 1000_000});858 try stderr.print(" MaxRSS:{d}M", .{rss / 1000_000});
854 } else if (rss >= 1000) {859 } else if (rss >= 1000) {
855 try stderr.deprecatedWriter().print(" MaxRSS:{d}K", .{rss / 1000});860 try stderr.print(" MaxRSS:{d}K", .{rss / 1000});
856 } else {861 } else {
857 try stderr.deprecatedWriter().print(" MaxRSS:{d}B", .{rss});862 try stderr.print(" MaxRSS:{d}B", .{rss});
858 }863 }
859 try ttyconf.setColor(stderr, .reset);864 try ttyconf.setColor(stderr, .reset);
860 }865 }
...@@ -866,7 +871,7 @@ fn printStepStatus(...@@ -866,7 +871,7 @@ fn printStepStatus(
866 if (skip == .skipped_oom) {871 if (skip == .skipped_oom) {
867 try stderr.writeAll(" (not enough memory)");872 try stderr.writeAll(" (not enough memory)");
868 try ttyconf.setColor(stderr, .dim);873 try ttyconf.setColor(stderr, .dim);
869 try stderr.deprecatedWriter().print(" upper bound of {d} exceeded runner limit ({d})", .{ s.max_rss, run.max_rss });874 try stderr.print(" upper bound of {d} exceeded runner limit ({d})", .{ s.max_rss, run.max_rss });
870 try ttyconf.setColor(stderr, .yellow);875 try ttyconf.setColor(stderr, .yellow);
871 }876 }
872 try stderr.writeAll("\n");877 try stderr.writeAll("\n");
...@@ -878,23 +883,23 @@ fn printStepStatus(...@@ -878,23 +883,23 @@ fn printStepStatus(
878883
879fn printStepFailure(884fn printStepFailure(
880 s: *Step,885 s: *Step,
881 stderr: File,886 stderr: *Writer,
882 ttyconf: std.io.tty.Config,887 ttyconf: std.io.tty.Config,
883) !void {888) !void {
884 if (s.result_error_bundle.errorMessageCount() > 0) {889 if (s.result_error_bundle.errorMessageCount() > 0) {
885 try ttyconf.setColor(stderr, .red);890 try ttyconf.setColor(stderr, .red);
886 try stderr.deprecatedWriter().print(" {d} errors\n", .{891 try stderr.print(" {d} errors\n", .{
887 s.result_error_bundle.errorMessageCount(),892 s.result_error_bundle.errorMessageCount(),
888 });893 });
889 try ttyconf.setColor(stderr, .reset);894 try ttyconf.setColor(stderr, .reset);
890 } else if (!s.test_results.isSuccess()) {895 } else if (!s.test_results.isSuccess()) {
891 try stderr.deprecatedWriter().print(" {d}/{d} passed", .{896 try stderr.print(" {d}/{d} passed", .{
892 s.test_results.passCount(), s.test_results.test_count,897 s.test_results.passCount(), s.test_results.test_count,
893 });898 });
894 if (s.test_results.fail_count > 0) {899 if (s.test_results.fail_count > 0) {
895 try stderr.writeAll(", ");900 try stderr.writeAll(", ");
896 try ttyconf.setColor(stderr, .red);901 try ttyconf.setColor(stderr, .red);
897 try stderr.deprecatedWriter().print("{d} failed", .{902 try stderr.print("{d} failed", .{
898 s.test_results.fail_count,903 s.test_results.fail_count,
899 });904 });
900 try ttyconf.setColor(stderr, .reset);905 try ttyconf.setColor(stderr, .reset);
...@@ -902,7 +907,7 @@ fn printStepFailure(...@@ -902,7 +907,7 @@ fn printStepFailure(
902 if (s.test_results.skip_count > 0) {907 if (s.test_results.skip_count > 0) {
903 try stderr.writeAll(", ");908 try stderr.writeAll(", ");
904 try ttyconf.setColor(stderr, .yellow);909 try ttyconf.setColor(stderr, .yellow);
905 try stderr.deprecatedWriter().print("{d} skipped", .{910 try stderr.print("{d} skipped", .{
906 s.test_results.skip_count,911 s.test_results.skip_count,
907 });912 });
908 try ttyconf.setColor(stderr, .reset);913 try ttyconf.setColor(stderr, .reset);
...@@ -910,7 +915,7 @@ fn printStepFailure(...@@ -910,7 +915,7 @@ fn printStepFailure(
910 if (s.test_results.leak_count > 0) {915 if (s.test_results.leak_count > 0) {
911 try stderr.writeAll(", ");916 try stderr.writeAll(", ");
912 try ttyconf.setColor(stderr, .red);917 try ttyconf.setColor(stderr, .red);
913 try stderr.deprecatedWriter().print("{d} leaked", .{918 try stderr.print("{d} leaked", .{
914 s.test_results.leak_count,919 s.test_results.leak_count,
915 });920 });
916 try ttyconf.setColor(stderr, .reset);921 try ttyconf.setColor(stderr, .reset);
...@@ -932,7 +937,7 @@ fn printTreeStep(...@@ -932,7 +937,7 @@ fn printTreeStep(
932 b: *std.Build,937 b: *std.Build,
933 s: *Step,938 s: *Step,
934 run: *const Run,939 run: *const Run,
935 stderr: File,940 stderr: *Writer,
936 ttyconf: std.io.tty.Config,941 ttyconf: std.io.tty.Config,
937 parent_node: *PrintNode,942 parent_node: *PrintNode,
938 step_stack: *std.AutoArrayHashMapUnmanaged(*Step, void),943 step_stack: *std.AutoArrayHashMapUnmanaged(*Step, void),
...@@ -992,7 +997,7 @@ fn printTreeStep(...@@ -992,7 +997,7 @@ fn printTreeStep(
992 if (s.dependencies.items.len == 0) {997 if (s.dependencies.items.len == 0) {
993 try stderr.writeAll(" (reused)\n");998 try stderr.writeAll(" (reused)\n");
994 } else {999 } else {
995 try stderr.deprecatedWriter().print(" (+{d} more reused dependencies)\n", .{1000 try stderr.print(" (+{d} more reused dependencies)\n", .{
996 s.dependencies.items.len,1001 s.dependencies.items.len,
997 });1002 });
998 }1003 }
...@@ -1129,11 +1134,11 @@ fn workerMakeOneStep(...@@ -1129,11 +1134,11 @@ fn workerMakeOneStep(
1129 const show_stderr = s.result_stderr.len > 0;1134 const show_stderr = s.result_stderr.len > 0;
11301135
1131 if (show_error_msgs or show_compile_errors or show_stderr) {1136 if (show_error_msgs or show_compile_errors or show_stderr) {
1132 std.debug.lockStdErr();1137 const bw = std.debug.lockStderrWriter(&stdio_buffer_allocation);
1133 defer std.debug.unlockStdErr();1138 defer std.debug.unlockStderrWriter();
11341139
1135 const gpa = b.allocator;1140 const gpa = b.allocator;
1136 printErrorMessages(gpa, s, .{ .ttyconf = run.ttyconf }, run.stderr, run.prominent_compile_errors) catch {};1141 printErrorMessages(gpa, s, .{ .ttyconf = run.ttyconf }, bw, run.prominent_compile_errors) catch {};
1137 }1142 }
11381143
1139 handle_result: {1144 handle_result: {
...@@ -1190,7 +1195,7 @@ pub fn printErrorMessages(...@@ -1190,7 +1195,7 @@ pub fn printErrorMessages(
1190 gpa: Allocator,1195 gpa: Allocator,
1191 failing_step: *Step,1196 failing_step: *Step,
1192 options: std.zig.ErrorBundle.RenderOptions,1197 options: std.zig.ErrorBundle.RenderOptions,
1193 stderr: File,1198 stderr: *Writer,
1194 prominent_compile_errors: bool,1199 prominent_compile_errors: bool,
1195) !void {1200) !void {
1196 // Provide context for where these error messages are coming from by1201 // Provide context for where these error messages are coming from by
...@@ -1209,7 +1214,7 @@ pub fn printErrorMessages(...@@ -1209,7 +1214,7 @@ pub fn printErrorMessages(
1209 var indent: usize = 0;1214 var indent: usize = 0;
1210 while (step_stack.pop()) |s| : (indent += 1) {1215 while (step_stack.pop()) |s| : (indent += 1) {
1211 if (indent > 0) {1216 if (indent > 0) {
1212 try stderr.deprecatedWriter().writeByteNTimes(' ', (indent - 1) * 3);1217 try stderr.splatByteAll(' ', (indent - 1) * 3);
1213 try printChildNodePrefix(stderr, ttyconf);1218 try printChildNodePrefix(stderr, ttyconf);
1214 }1219 }
12151220
...@@ -1231,7 +1236,7 @@ pub fn printErrorMessages(...@@ -1231,7 +1236,7 @@ pub fn printErrorMessages(
1231 }1236 }
12321237
1233 if (!prominent_compile_errors and failing_step.result_error_bundle.errorMessageCount() > 0) {1238 if (!prominent_compile_errors and failing_step.result_error_bundle.errorMessageCount() > 0) {
1234 try failing_step.result_error_bundle.renderToWriter(options, stderr.deprecatedWriter());1239 try failing_step.result_error_bundle.renderToWriter(options, stderr);
1235 }1240 }
12361241
1237 for (failing_step.result_error_msgs.items) |msg| {1242 for (failing_step.result_error_msgs.items) |msg| {
...@@ -1243,27 +1248,27 @@ pub fn printErrorMessages(...@@ -1243,27 +1248,27 @@ pub fn printErrorMessages(
1243 }1248 }
1244}1249}
12451250
1246fn steps(builder: *std.Build, out_stream: anytype) !void {1251fn printSteps(builder: *std.Build, w: *Writer) !void {
1247 const allocator = builder.allocator;1252 const allocator = builder.allocator;
1248 for (builder.top_level_steps.values()) |top_level_step| {1253 for (builder.top_level_steps.values()) |top_level_step| {
1249 const name = if (&top_level_step.step == builder.default_step)1254 const name = if (&top_level_step.step == builder.default_step)
1250 try fmt.allocPrint(allocator, "{s} (default)", .{top_level_step.step.name})1255 try fmt.allocPrint(allocator, "{s} (default)", .{top_level_step.step.name})
1251 else1256 else
1252 top_level_step.step.name;1257 top_level_step.step.name;
1253 try out_stream.print(" {s:<28} {s}\n", .{ name, top_level_step.description });1258 try w.print(" {s:<28} {s}\n", .{ name, top_level_step.description });
1254 }1259 }
1255}1260}
12561261
1257fn usage(b: *std.Build, out_stream: anytype) !void {1262fn printUsage(b: *std.Build, w: *Writer) !void {
1258 try out_stream.print(1263 try w.print(
1259 \\Usage: {s} build [steps] [options]1264 \\Usage: {s} build [steps] [options]
1260 \\1265 \\
1261 \\Steps:1266 \\Steps:
1262 \\1267 \\
1263 , .{b.graph.zig_exe});1268 , .{b.graph.zig_exe});
1264 try steps(b, out_stream);1269 try printSteps(b, w);
12651270
1266 try out_stream.writeAll(1271 try w.writeAll(
1267 \\1272 \\
1268 \\General Options:1273 \\General Options:
1269 \\ -p, --prefix [path] Where to install files (default: zig-out)1274 \\ -p, --prefix [path] Where to install files (default: zig-out)
...@@ -1319,25 +1324,25 @@ fn usage(b: *std.Build, out_stream: anytype) !void {...@@ -1319,25 +1324,25 @@ fn usage(b: *std.Build, out_stream: anytype) !void {
13191324
1320 const arena = b.allocator;1325 const arena = b.allocator;
1321 if (b.available_options_list.items.len == 0) {1326 if (b.available_options_list.items.len == 0) {
1322 try out_stream.print(" (none)\n", .{});1327 try w.print(" (none)\n", .{});
1323 } else {1328 } else {
1324 for (b.available_options_list.items) |option| {1329 for (b.available_options_list.items) |option| {
1325 const name = try fmt.allocPrint(arena, " -D{s}=[{s}]", .{1330 const name = try fmt.allocPrint(arena, " -D{s}=[{s}]", .{
1326 option.name,1331 option.name,
1327 @tagName(option.type_id),1332 @tagName(option.type_id),
1328 });1333 });
1329 try out_stream.print("{s:<30} {s}\n", .{ name, option.description });1334 try w.print("{s:<30} {s}\n", .{ name, option.description });
1330 if (option.enum_options) |enum_options| {1335 if (option.enum_options) |enum_options| {
1331 const padding = " " ** 33;1336 const padding = " " ** 33;
1332 try out_stream.writeAll(padding ++ "Supported Values:\n");1337 try w.writeAll(padding ++ "Supported Values:\n");
1333 for (enum_options) |enum_option| {1338 for (enum_options) |enum_option| {
1334 try out_stream.print(padding ++ " {s}\n", .{enum_option});1339 try w.print(padding ++ " {s}\n", .{enum_option});
1335 }1340 }
1336 }1341 }
1337 }1342 }
1338 }1343 }
13391344
1340 try out_stream.writeAll(1345 try w.writeAll(
1341 \\1346 \\
1342 \\System Integration Options:1347 \\System Integration Options:
1343 \\ --search-prefix [path] Add a path to look for binaries, libraries, headers1348 \\ --search-prefix [path] Add a path to look for binaries, libraries, headers
...@@ -1352,7 +1357,7 @@ fn usage(b: *std.Build, out_stream: anytype) !void {...@@ -1352,7 +1357,7 @@ fn usage(b: *std.Build, out_stream: anytype) !void {
1352 \\1357 \\
1353 );1358 );
1354 if (b.graph.system_library_options.entries.len == 0) {1359 if (b.graph.system_library_options.entries.len == 0) {
1355 try out_stream.writeAll(" (none) -\n");1360 try w.writeAll(" (none) -\n");
1356 } else {1361 } else {
1357 for (b.graph.system_library_options.keys(), b.graph.system_library_options.values()) |k, v| {1362 for (b.graph.system_library_options.keys(), b.graph.system_library_options.values()) |k, v| {
1358 const status = switch (v) {1363 const status = switch (v) {
...@@ -1360,11 +1365,11 @@ fn usage(b: *std.Build, out_stream: anytype) !void {...@@ -1360,11 +1365,11 @@ fn usage(b: *std.Build, out_stream: anytype) !void {
1360 .declared_disabled => "no",1365 .declared_disabled => "no",
1361 .user_enabled, .user_disabled => unreachable, // already emitted error1366 .user_enabled, .user_disabled => unreachable, // already emitted error
1362 };1367 };
1363 try out_stream.print(" {s:<43} {s}\n", .{ k, status });1368 try w.print(" {s:<43} {s}\n", .{ k, status });
1364 }1369 }
1365 }1370 }
13661371
1367 try out_stream.writeAll(1372 try w.writeAll(
1368 \\1373 \\
1369 \\Advanced Options:1374 \\Advanced Options:
1370 \\ -freference-trace[=num] How many lines of reference trace should be shown per compile error1375 \\ -freference-trace[=num] How many lines of reference trace should be shown per compile error
...@@ -1544,3 +1549,11 @@ fn createModuleDependenciesForStep(step: *Step) Allocator.Error!void {...@@ -1544,3 +1549,11 @@ fn createModuleDependenciesForStep(step: *Step) Allocator.Error!void {
1544 };1549 };
1545 }1550 }
1546}1551}
1552
1553var stdio_buffer_allocation: [256]u8 = undefined;
1554var stdout_writer_allocation: std.fs.File.Writer = undefined;
1555
1556fn initStdoutWriter() *Writer {
1557 stdout_writer_allocation = std.fs.File.stdout().writerStreaming(&stdio_buffer_allocation);
1558 return &stdout_writer_allocation.interface;
1559}
lib/std/Build.zig+31-41
...@@ -284,7 +284,7 @@ pub fn create(...@@ -284,7 +284,7 @@ pub fn create(
284 .h_dir = undefined,284 .h_dir = undefined,
285 .dest_dir = graph.env_map.get("DESTDIR"),285 .dest_dir = graph.env_map.get("DESTDIR"),
286 .install_tls = .{286 .install_tls = .{
287 .step = Step.init(.{287 .step = .init(.{
288 .id = TopLevelStep.base_id,288 .id = TopLevelStep.base_id,
289 .name = "install",289 .name = "install",
290 .owner = b,290 .owner = b,
...@@ -292,7 +292,7 @@ pub fn create(...@@ -292,7 +292,7 @@ pub fn create(
292 .description = "Copy build artifacts to prefix path",292 .description = "Copy build artifacts to prefix path",
293 },293 },
294 .uninstall_tls = .{294 .uninstall_tls = .{
295 .step = Step.init(.{295 .step = .init(.{
296 .id = TopLevelStep.base_id,296 .id = TopLevelStep.base_id,
297 .name = "uninstall",297 .name = "uninstall",
298 .owner = b,298 .owner = b,
...@@ -342,7 +342,7 @@ fn createChildOnly(...@@ -342,7 +342,7 @@ fn createChildOnly(
342 .graph = parent.graph,342 .graph = parent.graph,
343 .allocator = allocator,343 .allocator = allocator,
344 .install_tls = .{344 .install_tls = .{
345 .step = Step.init(.{345 .step = .init(.{
346 .id = TopLevelStep.base_id,346 .id = TopLevelStep.base_id,
347 .name = "install",347 .name = "install",
348 .owner = child,348 .owner = child,
...@@ -350,7 +350,7 @@ fn createChildOnly(...@@ -350,7 +350,7 @@ fn createChildOnly(
350 .description = "Copy build artifacts to prefix path",350 .description = "Copy build artifacts to prefix path",
351 },351 },
352 .uninstall_tls = .{352 .uninstall_tls = .{
353 .step = Step.init(.{353 .step = .init(.{
354 .id = TopLevelStep.base_id,354 .id = TopLevelStep.base_id,
355 .name = "uninstall",355 .name = "uninstall",
356 .owner = child,356 .owner = child,
...@@ -1525,7 +1525,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw...@@ -1525,7 +1525,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
1525pub fn step(b: *Build, name: []const u8, description: []const u8) *Step {1525pub fn step(b: *Build, name: []const u8, description: []const u8) *Step {
1526 const step_info = b.allocator.create(TopLevelStep) catch @panic("OOM");1526 const step_info = b.allocator.create(TopLevelStep) catch @panic("OOM");
1527 step_info.* = .{1527 step_info.* = .{
1528 .step = Step.init(.{1528 .step = .init(.{
1529 .id = TopLevelStep.base_id,1529 .id = TopLevelStep.base_id,
1530 .name = name,1530 .name = name,
1531 .owner = b,1531 .owner = b,
...@@ -1824,13 +1824,13 @@ pub fn validateUserInputDidItFail(b: *Build) bool {...@@ -1824,13 +1824,13 @@ pub fn validateUserInputDidItFail(b: *Build) bool {
1824 return b.invalid_user_input;1824 return b.invalid_user_input;
1825}1825}
18261826
1827fn allocPrintCmd(ally: Allocator, opt_cwd: ?[]const u8, argv: []const []const u8) error{OutOfMemory}![]u8 {1827fn allocPrintCmd(gpa: Allocator, opt_cwd: ?[]const u8, argv: []const []const u8) error{OutOfMemory}![]u8 {
1828 var buf = ArrayList(u8).init(ally);1828 var buf: std.ArrayListUnmanaged(u8) = .empty;
1829 if (opt_cwd) |cwd| try buf.writer().print("cd {s} && ", .{cwd});1829 if (opt_cwd) |cwd| try buf.print(gpa, "cd {s} && ", .{cwd});
1830 for (argv) |arg| {1830 for (argv) |arg| {
1831 try buf.writer().print("{s} ", .{arg});1831 try buf.print(gpa, "{s} ", .{arg});
1832 }1832 }
1833 return buf.toOwnedSlice();1833 return buf.toOwnedSlice(gpa);
1834}1834}
18351835
1836fn printCmd(ally: Allocator, cwd: ?[]const u8, argv: []const []const u8) void {1836fn printCmd(ally: Allocator, cwd: ?[]const u8, argv: []const []const u8) void {
...@@ -2466,10 +2466,9 @@ pub const GeneratedFile = struct {...@@ -2466,10 +2466,9 @@ pub const GeneratedFile = struct {
24662466
2467 pub fn getPath2(gen: GeneratedFile, src_builder: *Build, asking_step: ?*Step) []const u8 {2467 pub fn getPath2(gen: GeneratedFile, src_builder: *Build, asking_step: ?*Step) []const u8 {
2468 return gen.path orelse {2468 return gen.path orelse {
2469 std.debug.lockStdErr();2469 const w = debug.lockStderrWriter(&.{});
2470 const stderr = std.fs.File.stderr();2470 dumpBadGetPathHelp(gen.step, w, .detect(.stderr()), src_builder, asking_step) catch {};
2471 dumpBadGetPathHelp(gen.step, stderr, src_builder, asking_step) catch {};2471 debug.unlockStderrWriter();
2472 std.debug.unlockStdErr();
2473 @panic("misconfigured build script");2472 @panic("misconfigured build script");
2474 };2473 };
2475 }2474 }
...@@ -2676,10 +2675,9 @@ pub const LazyPath = union(enum) {...@@ -2676,10 +2675,9 @@ pub const LazyPath = union(enum) {
2676 var file_path: Cache.Path = .{2675 var file_path: Cache.Path = .{
2677 .root_dir = Cache.Directory.cwd(),2676 .root_dir = Cache.Directory.cwd(),
2678 .sub_path = gen.file.path orelse {2677 .sub_path = gen.file.path orelse {
2679 std.debug.lockStdErr();2678 const w = debug.lockStderrWriter(&.{});
2680 const stderr: fs.File = .stderr();2679 dumpBadGetPathHelp(gen.file.step, w, .detect(.stderr()), src_builder, asking_step) catch {};
2681 dumpBadGetPathHelp(gen.file.step, stderr, src_builder, asking_step) catch {};2680 debug.unlockStderrWriter();
2682 std.debug.unlockStdErr();
2683 @panic("misconfigured build script");2681 @panic("misconfigured build script");
2684 },2682 },
2685 };2683 };
...@@ -2766,44 +2764,42 @@ fn dumpBadDirnameHelp(...@@ -2766,44 +2764,42 @@ fn dumpBadDirnameHelp(
2766 comptime msg: []const u8,2764 comptime msg: []const u8,
2767 args: anytype,2765 args: anytype,
2768) anyerror!void {2766) anyerror!void {
2769 debug.lockStdErr();2767 const w = debug.lockStderrWriter(&.{});
2770 defer debug.unlockStdErr();2768 defer debug.unlockStderrWriter();
27712769
2772 const stderr: fs.File = .stderr();
2773 const w = stderr.deprecatedWriter();
2774 try w.print(msg, args);2770 try w.print(msg, args);
27752771
2776 const tty_config = std.io.tty.detectConfig(stderr);2772 const tty_config = std.io.tty.detectConfig(.stderr());
27772773
2778 if (fail_step) |s| {2774 if (fail_step) |s| {
2779 tty_config.setColor(w, .red) catch {};2775 tty_config.setColor(w, .red) catch {};
2780 try stderr.writeAll(" The step was created by this stack trace:\n");2776 try w.writeAll(" The step was created by this stack trace:\n");
2781 tty_config.setColor(w, .reset) catch {};2777 tty_config.setColor(w, .reset) catch {};
27822778
2783 s.dump(stderr);2779 s.dump(w, tty_config);
2784 }2780 }
27852781
2786 if (asking_step) |as| {2782 if (asking_step) |as| {
2787 tty_config.setColor(w, .red) catch {};2783 tty_config.setColor(w, .red) catch {};
2788 try stderr.deprecatedWriter().print(" The step '{s}' that is missing a dependency on the above step was created by this stack trace:\n", .{as.name});2784 try w.print(" The step '{s}' that is missing a dependency on the above step was created by this stack trace:\n", .{as.name});
2789 tty_config.setColor(w, .reset) catch {};2785 tty_config.setColor(w, .reset) catch {};
27902786
2791 as.dump(stderr);2787 as.dump(w, tty_config);
2792 }2788 }
27932789
2794 tty_config.setColor(w, .red) catch {};2790 tty_config.setColor(w, .red) catch {};
2795 try stderr.writeAll(" Hope that helps. Proceeding to panic.\n");2791 try w.writeAll(" Hope that helps. Proceeding to panic.\n");
2796 tty_config.setColor(w, .reset) catch {};2792 tty_config.setColor(w, .reset) catch {};
2797}2793}
27982794
2799/// In this function the stderr mutex has already been locked.2795/// In this function the stderr mutex has already been locked.
2800pub fn dumpBadGetPathHelp(2796pub fn dumpBadGetPathHelp(
2801 s: *Step,2797 s: *Step,
2802 stderr: fs.File,2798 w: *std.io.Writer,
2799 tty_config: std.io.tty.Config,
2803 src_builder: *Build,2800 src_builder: *Build,
2804 asking_step: ?*Step,2801 asking_step: ?*Step,
2805) anyerror!void {2802) anyerror!void {
2806 const w = stderr.deprecatedWriter();
2807 try w.print(2803 try w.print(
2808 \\getPath() was called on a GeneratedFile that wasn't built yet.2804 \\getPath() was called on a GeneratedFile that wasn't built yet.
2809 \\ source package path: {s}2805 \\ source package path: {s}
...@@ -2814,21 +2810,20 @@ pub fn dumpBadGetPathHelp(...@@ -2814,21 +2810,20 @@ pub fn dumpBadGetPathHelp(
2814 s.name,2810 s.name,
2815 });2811 });
28162812
2817 const tty_config = std.io.tty.detectConfig(stderr);
2818 tty_config.setColor(w, .red) catch {};2813 tty_config.setColor(w, .red) catch {};
2819 try stderr.writeAll(" The step was created by this stack trace:\n");2814 try w.writeAll(" The step was created by this stack trace:\n");
2820 tty_config.setColor(w, .reset) catch {};2815 tty_config.setColor(w, .reset) catch {};
28212816
2822 s.dump(stderr);2817 s.dump(w, tty_config);
2823 if (asking_step) |as| {2818 if (asking_step) |as| {
2824 tty_config.setColor(w, .red) catch {};2819 tty_config.setColor(w, .red) catch {};
2825 try stderr.deprecatedWriter().print(" The step '{s}' that is missing a dependency on the above step was created by this stack trace:\n", .{as.name});2820 try w.print(" The step '{s}' that is missing a dependency on the above step was created by this stack trace:\n", .{as.name});
2826 tty_config.setColor(w, .reset) catch {};2821 tty_config.setColor(w, .reset) catch {};
28272822
2828 as.dump(stderr);2823 as.dump(w, tty_config);
2829 }2824 }
2830 tty_config.setColor(w, .red) catch {};2825 tty_config.setColor(w, .red) catch {};
2831 try stderr.writeAll(" Hope that helps. Proceeding to panic.\n");2826 try w.writeAll(" Hope that helps. Proceeding to panic.\n");
2832 tty_config.setColor(w, .reset) catch {};2827 tty_config.setColor(w, .reset) catch {};
2833}2828}
28342829
...@@ -2866,11 +2861,6 @@ pub fn makeTempPath(b: *Build) []const u8 {...@@ -2866,11 +2861,6 @@ pub fn makeTempPath(b: *Build) []const u8 {
2866 return result_path;2861 return result_path;
2867}2862}
28682863
2869/// Deprecated; use `std.fmt.hex` instead.
2870pub fn hex64(x: u64) [16]u8 {
2871 return std.fmt.hex(x);
2872}
2873
2874/// A pair of target query and fully resolved target.2864/// A pair of target query and fully resolved target.
2875/// This type is generally required by build system API that need to be given a2865/// This type is generally required by build system API that need to be given a
2876/// target. The query is kept because the Zig toolchain needs to know which parts2866/// target. The query is kept because the Zig toolchain needs to know which parts
lib/std/Build/Fuzz.zig+8-8
...@@ -112,7 +112,6 @@ fn rebuildTestsWorkerRun(run: *Step.Run, ttyconf: std.io.tty.Config, parent_prog...@@ -112,7 +112,6 @@ fn rebuildTestsWorkerRun(run: *Step.Run, ttyconf: std.io.tty.Config, parent_prog
112112
113fn rebuildTestsWorkerRunFallible(run: *Step.Run, ttyconf: std.io.tty.Config, parent_prog_node: std.Progress.Node) !void {113fn rebuildTestsWorkerRunFallible(run: *Step.Run, ttyconf: std.io.tty.Config, parent_prog_node: std.Progress.Node) !void {
114 const gpa = run.step.owner.allocator;114 const gpa = run.step.owner.allocator;
115 const stderr = std.fs.File.stderr();
116115
117 const compile = run.producer.?;116 const compile = run.producer.?;
118 const prog_node = parent_prog_node.start(compile.step.name, 0);117 const prog_node = parent_prog_node.start(compile.step.name, 0);
...@@ -125,9 +124,10 @@ fn rebuildTestsWorkerRunFallible(run: *Step.Run, ttyconf: std.io.tty.Config, par...@@ -125,9 +124,10 @@ fn rebuildTestsWorkerRunFallible(run: *Step.Run, ttyconf: std.io.tty.Config, par
125 const show_stderr = compile.step.result_stderr.len > 0;124 const show_stderr = compile.step.result_stderr.len > 0;
126125
127 if (show_error_msgs or show_compile_errors or show_stderr) {126 if (show_error_msgs or show_compile_errors or show_stderr) {
128 std.debug.lockStdErr();127 var buf: [256]u8 = undefined;
129 defer std.debug.unlockStdErr();128 const w = std.debug.lockStderrWriter(&buf);
130 build_runner.printErrorMessages(gpa, &compile.step, .{ .ttyconf = ttyconf }, stderr, false) catch {};129 defer std.debug.unlockStderrWriter();
130 build_runner.printErrorMessages(gpa, &compile.step, .{ .ttyconf = ttyconf }, w, false) catch {};
131 }131 }
132132
133 const rebuilt_bin_path = result catch |err| switch (err) {133 const rebuilt_bin_path = result catch |err| switch (err) {
...@@ -152,10 +152,10 @@ fn fuzzWorkerRun(...@@ -152,10 +152,10 @@ fn fuzzWorkerRun(
152152
153 run.rerunInFuzzMode(web_server, unit_test_index, prog_node) catch |err| switch (err) {153 run.rerunInFuzzMode(web_server, unit_test_index, prog_node) catch |err| switch (err) {
154 error.MakeFailed => {154 error.MakeFailed => {
155 const stderr = std.fs.File.stderr();155 var buf: [256]u8 = undefined;
156 std.debug.lockStdErr();156 const w = std.debug.lockStderrWriter(&buf);
157 defer std.debug.unlockStdErr();157 defer std.debug.unlockStderrWriter();
158 build_runner.printErrorMessages(gpa, &run.step, .{ .ttyconf = ttyconf }, stderr, false) catch {};158 build_runner.printErrorMessages(gpa, &run.step, .{ .ttyconf = ttyconf }, w, false) catch {};
159 return;159 return;
160 },160 },
161 else => {161 else => {
lib/std/Build/Step.zig+1-4
...@@ -286,10 +286,7 @@ pub fn cast(step: *Step, comptime T: type) ?*T {...@@ -286,10 +286,7 @@ pub fn cast(step: *Step, comptime T: type) ?*T {
286}286}
287287
288/// For debugging purposes, prints identifying information about this Step.288/// For debugging purposes, prints identifying information about this Step.
289pub fn dump(step: *Step, file: std.fs.File) void {289pub fn dump(step: *Step, w: *std.io.Writer, tty_config: std.io.tty.Config) void {
290 var fw = file.writer(&.{});
291 const w = &fw.interface;
292 const tty_config = std.io.tty.detectConfig(file);
293 const debug_info = std.debug.getSelfDebugInfo() catch |err| {290 const debug_info = std.debug.getSelfDebugInfo() catch |err| {
294 w.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{291 w.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{
295 @errorName(err),292 @errorName(err),
lib/std/Build/Step/Compile.zig+30-31
...@@ -409,7 +409,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile {...@@ -409,7 +409,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
409 .linkage = options.linkage,409 .linkage = options.linkage,
410 .kind = options.kind,410 .kind = options.kind,
411 .name = name,411 .name = name,
412 .step = Step.init(.{412 .step = .init(.{
413 .id = base_id,413 .id = base_id,
414 .name = step_name,414 .name = step_name,
415 .owner = owner,415 .owner = owner,
...@@ -1017,20 +1017,16 @@ fn getGeneratedFilePath(compile: *Compile, comptime tag_name: []const u8, asking...@@ -1017,20 +1017,16 @@ fn getGeneratedFilePath(compile: *Compile, comptime tag_name: []const u8, asking
1017 const maybe_path: ?*GeneratedFile = @field(compile, tag_name);1017 const maybe_path: ?*GeneratedFile = @field(compile, tag_name);
10181018
1019 const generated_file = maybe_path orelse {1019 const generated_file = maybe_path orelse {
1020 std.debug.lockStdErr();1020 const w = std.debug.lockStderrWriter(&.{});
1021 const stderr: fs.File = .stderr();1021 std.Build.dumpBadGetPathHelp(&compile.step, w, .detect(.stderr()), compile.step.owner, asking_step) catch {};
10221022 std.debug.unlockStderrWriter();
1023 std.Build.dumpBadGetPathHelp(&compile.step, stderr, compile.step.owner, asking_step) catch {};
1024
1025 @panic("missing emit option for " ++ tag_name);1023 @panic("missing emit option for " ++ tag_name);
1026 };1024 };
10271025
1028 const path = generated_file.path orelse {1026 const path = generated_file.path orelse {
1029 std.debug.lockStdErr();1027 const w = std.debug.lockStderrWriter(&.{});
1030 const stderr: fs.File = .stderr();1028 std.Build.dumpBadGetPathHelp(&compile.step, w, .detect(.stderr()), compile.step.owner, asking_step) catch {};
10311029 std.debug.unlockStderrWriter();
1032 std.Build.dumpBadGetPathHelp(&compile.step, stderr, compile.step.owner, asking_step) catch {};
1033
1034 @panic(tag_name ++ " is null. Is there a missing step dependency?");1030 @panic(tag_name ++ " is null. Is there a missing step dependency?");
1035 };1031 };
10361032
...@@ -1768,12 +1764,12 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {...@@ -1768,12 +1764,12 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
1768 for (arg, 0..) |c, arg_idx| {1764 for (arg, 0..) |c, arg_idx| {
1769 if (c == '\\' or c == '"') {1765 if (c == '\\' or c == '"') {
1770 // Slow path for arguments that need to be escaped. We'll need to allocate and copy1766 // Slow path for arguments that need to be escaped. We'll need to allocate and copy
1771 var escaped = try ArrayList(u8).initCapacity(arena, arg.len + 1);1767 var escaped: std.ArrayListUnmanaged(u8) = .empty;
1772 const writer = escaped.writer();1768 try escaped.ensureTotalCapacityPrecise(arena, arg.len + 1);
1773 try writer.writeAll(arg[0..arg_idx]);1769 try escaped.appendSlice(arena, arg[0..arg_idx]);
1774 for (arg[arg_idx..]) |to_escape| {1770 for (arg[arg_idx..]) |to_escape| {
1775 if (to_escape == '\\' or to_escape == '"') try writer.writeByte('\\');1771 if (to_escape == '\\' or to_escape == '"') try escaped.append(arena, '\\');
1776 try writer.writeByte(to_escape);1772 try escaped.append(arena, to_escape);
1777 }1773 }
1778 escaped_args.appendAssumeCapacity(escaped.items);1774 escaped_args.appendAssumeCapacity(escaped.items);
1779 continue :arg_blk;1775 continue :arg_blk;
...@@ -1963,20 +1959,23 @@ fn addFlag(args: *ArrayList([]const u8), comptime name: []const u8, opt: ?bool)...@@ -1963,20 +1959,23 @@ fn addFlag(args: *ArrayList([]const u8), comptime name: []const u8, opt: ?bool)
1963fn checkCompileErrors(compile: *Compile) !void {1959fn checkCompileErrors(compile: *Compile) !void {
1964 // Clear this field so that it does not get printed by the build runner.1960 // Clear this field so that it does not get printed by the build runner.
1965 const actual_eb = compile.step.result_error_bundle;1961 const actual_eb = compile.step.result_error_bundle;
1966 compile.step.result_error_bundle = std.zig.ErrorBundle.empty;1962 compile.step.result_error_bundle = .empty;
19671963
1968 const arena = compile.step.owner.allocator;1964 const arena = compile.step.owner.allocator;
19691965
1970 var actual_errors_list = std.ArrayList(u8).init(arena);1966 const actual_errors = ae: {
1971 try actual_eb.renderToWriter(.{1967 var aw: std.io.Writer.Allocating = .init(arena);
1972 .ttyconf = .no_color,1968 defer aw.deinit();
1973 .include_reference_trace = false,1969 try actual_eb.renderToWriter(.{
1974 .include_source_line = false,1970 .ttyconf = .no_color,
1975 }, actual_errors_list.writer());1971 .include_reference_trace = false,
1976 const actual_errors = try actual_errors_list.toOwnedSlice();1972 .include_source_line = false,
1973 }, &aw.writer);
1974 break :ae try aw.toOwnedSlice();
1975 };
19771976
1978 // Render the expected lines into a string that we can compare verbatim.1977 // Render the expected lines into a string that we can compare verbatim.
1979 var expected_generated = std.ArrayList(u8).init(arena);1978 var expected_generated: std.ArrayListUnmanaged(u8) = .empty;
1980 const expect_errors = compile.expect_errors.?;1979 const expect_errors = compile.expect_errors.?;
19811980
1982 var actual_line_it = mem.splitScalar(u8, actual_errors, '\n');1981 var actual_line_it = mem.splitScalar(u8, actual_errors, '\n');
...@@ -2035,17 +2034,17 @@ fn checkCompileErrors(compile: *Compile) !void {...@@ -2035,17 +2034,17 @@ fn checkCompileErrors(compile: *Compile) !void {
2035 .exact => |expect_lines| {2034 .exact => |expect_lines| {
2036 for (expect_lines) |expect_line| {2035 for (expect_lines) |expect_line| {
2037 const actual_line = actual_line_it.next() orelse {2036 const actual_line = actual_line_it.next() orelse {
2038 try expected_generated.appendSlice(expect_line);2037 try expected_generated.appendSlice(arena, expect_line);
2039 try expected_generated.append('\n');2038 try expected_generated.append(arena, '\n');
2040 continue;2039 continue;
2041 };2040 };
2042 if (matchCompileError(actual_line, expect_line)) {2041 if (matchCompileError(actual_line, expect_line)) {
2043 try expected_generated.appendSlice(actual_line);2042 try expected_generated.appendSlice(arena, actual_line);
2044 try expected_generated.append('\n');2043 try expected_generated.append(arena, '\n');
2045 continue;2044 continue;
2046 }2045 }
2047 try expected_generated.appendSlice(expect_line);2046 try expected_generated.appendSlice(arena, expect_line);
2048 try expected_generated.append('\n');2047 try expected_generated.append(arena, '\n');
2049 }2048 }
20502049
2051 if (mem.eql(u8, expected_generated.items, actual_errors)) return;2050 if (mem.eql(u8, expected_generated.items, actual_errors)) return;
lib/std/fs/File.zig-4
...@@ -1725,10 +1725,6 @@ pub const Writer = struct {...@@ -1725,10 +1725,6 @@ pub const Writer = struct {
1725 iovecs[len] = .{ .base = splat_buffer.ptr, .len = remaining_splat };1725 iovecs[len] = .{ .base = splat_buffer.ptr, .len = remaining_splat };
1726 len += 1;1726 len += 1;
1727 }1727 }
1728 return std.posix.writev(handle, iovecs[0..len]) catch |err| {
1729 w.err = err;
1730 return error.WriteFailed;
1731 };
1732 },1728 },
1733 else => for (0..splat - 1) |_| {1729 else => for (0..splat - 1) |_| {
1734 if (iovecs.len - len == 0) break;1730 if (iovecs.len - len == 0) break;
lib/std/io/tty.zig+35-29
...@@ -5,36 +5,9 @@ const process = std.process;...@@ -5,36 +5,9 @@ const process = std.process;
5const windows = std.os.windows;5const windows = std.os.windows;
6const native_os = builtin.os.tag;6const native_os = builtin.os.tag;
77
8/// Detect suitable TTY configuration options for the given file (commonly stdout/stderr).8/// Deprecated in favor of `Config.detect`.
9/// This includes feature checks for ANSI escape codes and the Windows console API, as well as
10/// respecting the `NO_COLOR` and `CLICOLOR_FORCE` environment variables to override the default.
11/// Will attempt to enable ANSI escape code support if necessary/possible.
12pub fn detectConfig(file: File) Config {9pub fn detectConfig(file: File) Config {
13 const force_color: ?bool = if (builtin.os.tag == .wasi)10 return .detect(file);
14 null // wasi does not support environment variables
15 else if (process.hasNonEmptyEnvVarConstant("NO_COLOR"))
16 false
17 else if (process.hasNonEmptyEnvVarConstant("CLICOLOR_FORCE"))
18 true
19 else
20 null;
21
22 if (force_color == false) return .no_color;
23
24 if (file.getOrEnableAnsiEscapeSupport()) return .escape_codes;
25
26 if (native_os == .windows and file.isTty()) {
27 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
28 if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) == windows.FALSE) {
29 return if (force_color == true) .escape_codes else .no_color;
30 }
31 return .{ .windows_api = .{
32 .handle = file.handle,
33 .reset_attributes = info.wAttributes,
34 } };
35 }
36
37 return if (force_color == true) .escape_codes else .no_color;
38}11}
3912
40pub const Color = enum {13pub const Color = enum {
...@@ -66,6 +39,38 @@ pub const Config = union(enum) {...@@ -66,6 +39,38 @@ pub const Config = union(enum) {
66 escape_codes,39 escape_codes,
67 windows_api: if (native_os == .windows) WindowsContext else void,40 windows_api: if (native_os == .windows) WindowsContext else void,
6841
42 /// Detect suitable TTY configuration options for the given file (commonly stdout/stderr).
43 /// This includes feature checks for ANSI escape codes and the Windows console API, as well as
44 /// respecting the `NO_COLOR` and `CLICOLOR_FORCE` environment variables to override the default.
45 /// Will attempt to enable ANSI escape code support if necessary/possible.
46 pub fn detect(file: File) Config {
47 const force_color: ?bool = if (builtin.os.tag == .wasi)
48 null // wasi does not support environment variables
49 else if (process.hasNonEmptyEnvVarConstant("NO_COLOR"))
50 false
51 else if (process.hasNonEmptyEnvVarConstant("CLICOLOR_FORCE"))
52 true
53 else
54 null;
55
56 if (force_color == false) return .no_color;
57
58 if (file.getOrEnableAnsiEscapeSupport()) return .escape_codes;
59
60 if (native_os == .windows and file.isTty()) {
61 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
62 if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) == windows.FALSE) {
63 return if (force_color == true) .escape_codes else .no_color;
64 }
65 return .{ .windows_api = .{
66 .handle = file.handle,
67 .reset_attributes = info.wAttributes,
68 } };
69 }
70
71 return if (force_color == true) .escape_codes else .no_color;
72 }
73
69 pub const WindowsContext = struct {74 pub const WindowsContext = struct {
70 handle: File.Handle,75 handle: File.Handle,
71 reset_attributes: u16,76 reset_attributes: u16,
...@@ -123,6 +128,7 @@ pub const Config = union(enum) {...@@ -123,6 +128,7 @@ pub const Config = union(enum) {
123 .dim => windows.FOREGROUND_INTENSITY,128 .dim => windows.FOREGROUND_INTENSITY,
124 .reset => ctx.reset_attributes,129 .reset => ctx.reset_attributes,
125 };130 };
131 try w.flush();
126 try windows.SetConsoleTextAttribute(ctx.handle, attributes);132 try windows.SetConsoleTextAttribute(ctx.handle, attributes);
127 } else {133 } else {
128 unreachable;134 unreachable;
lib/std/zig/ErrorBundle.zig+68-66
...@@ -11,6 +11,7 @@ const std = @import("std");...@@ -11,6 +11,7 @@ const std = @import("std");
11const ErrorBundle = @This();11const ErrorBundle = @This();
12const Allocator = std.mem.Allocator;12const Allocator = std.mem.Allocator;
13const assert = std.debug.assert;13const assert = std.debug.assert;
14const Writer = std.io.Writer;
1415
15string_bytes: []const u8,16string_bytes: []const u8,
16/// The first thing in this array is an `ErrorMessageList`.17/// The first thing in this array is an `ErrorMessageList`.
...@@ -162,23 +163,23 @@ pub const RenderOptions = struct {...@@ -162,23 +163,23 @@ pub const RenderOptions = struct {
162};163};
163164
164pub fn renderToStdErr(eb: ErrorBundle, options: RenderOptions) void {165pub fn renderToStdErr(eb: ErrorBundle, options: RenderOptions) void {
165 std.debug.lockStdErr();166 var buffer: [256]u8 = undefined;
166 defer std.debug.unlockStdErr();167 const w = std.debug.lockStderrWriter(&buffer);
167 const stderr: std.fs.File = .stderr();168 defer std.debug.unlockStderrWriter();
168 return renderToWriter(eb, options, stderr.deprecatedWriter()) catch return;169 renderToWriter(eb, options, w) catch return;
169}170}
170171
171pub fn renderToWriter(eb: ErrorBundle, options: RenderOptions, writer: anytype) anyerror!void {172pub fn renderToWriter(eb: ErrorBundle, options: RenderOptions, w: *Writer) (Writer.Error || std.posix.UnexpectedError)!void {
172 if (eb.extra.len == 0) return;173 if (eb.extra.len == 0) return;
173 for (eb.getMessages()) |err_msg| {174 for (eb.getMessages()) |err_msg| {
174 try renderErrorMessageToWriter(eb, options, err_msg, writer, "error", .red, 0);175 try renderErrorMessageToWriter(eb, options, err_msg, w, "error", .red, 0);
175 }176 }
176177
177 if (options.include_log_text) {178 if (options.include_log_text) {
178 const log_text = eb.getCompileLogOutput();179 const log_text = eb.getCompileLogOutput();
179 if (log_text.len != 0) {180 if (log_text.len != 0) {
180 try writer.writeAll("\nCompile Log Output:\n");181 try w.writeAll("\nCompile Log Output:\n");
181 try writer.writeAll(log_text);182 try w.writeAll(log_text);
182 }183 }
183 }184 }
184}185}
...@@ -187,74 +188,73 @@ fn renderErrorMessageToWriter(...@@ -187,74 +188,73 @@ fn renderErrorMessageToWriter(
187 eb: ErrorBundle,188 eb: ErrorBundle,
188 options: RenderOptions,189 options: RenderOptions,
189 err_msg_index: MessageIndex,190 err_msg_index: MessageIndex,
190 stderr: anytype,191 w: *Writer,
191 kind: []const u8,192 kind: []const u8,
192 color: std.io.tty.Color,193 color: std.io.tty.Color,
193 indent: usize,194 indent: usize,
194) anyerror!void {195) (Writer.Error || std.posix.UnexpectedError)!void {
195 const ttyconf = options.ttyconf;196 const ttyconf = options.ttyconf;
196 var counting_writer = std.io.countingWriter(stderr);
197 const counting_stderr = counting_writer.writer();
198 const err_msg = eb.getErrorMessage(err_msg_index);197 const err_msg = eb.getErrorMessage(err_msg_index);
198 const prefix_start = w.count;
199 if (err_msg.src_loc != .none) {199 if (err_msg.src_loc != .none) {
200 const src = eb.extraData(SourceLocation, @intFromEnum(err_msg.src_loc));200 const src = eb.extraData(SourceLocation, @intFromEnum(err_msg.src_loc));
201 try counting_stderr.writeByteNTimes(' ', indent);201 try w.splatByteAll(' ', indent);
202 try ttyconf.setColor(stderr, .bold);202 try ttyconf.setColor(w, .bold);
203 try counting_stderr.print("{s}:{d}:{d}: ", .{203 try w.print("{s}:{d}:{d}: ", .{
204 eb.nullTerminatedString(src.data.src_path),204 eb.nullTerminatedString(src.data.src_path),
205 src.data.line + 1,205 src.data.line + 1,
206 src.data.column + 1,206 src.data.column + 1,
207 });207 });
208 try ttyconf.setColor(stderr, color);208 try ttyconf.setColor(w, color);
209 try counting_stderr.writeAll(kind);209 try w.writeAll(kind);
210 try counting_stderr.writeAll(": ");210 try w.writeAll(": ");
211 // This is the length of the part before the error message:211 // This is the length of the part before the error message:
212 // e.g. "file.zig:4:5: error: "212 // e.g. "file.zig:4:5: error: "
213 const prefix_len: usize = @intCast(counting_stderr.context.bytes_written);213 const prefix_len = w.count - prefix_start;
214 try ttyconf.setColor(stderr, .reset);214 try ttyconf.setColor(w, .reset);
215 try ttyconf.setColor(stderr, .bold);215 try ttyconf.setColor(w, .bold);
216 if (err_msg.count == 1) {216 if (err_msg.count == 1) {
217 try writeMsg(eb, err_msg, stderr, prefix_len);217 try writeMsg(eb, err_msg, w, prefix_len);
218 try stderr.writeByte('\n');218 try w.writeByte('\n');
219 } else {219 } else {
220 try writeMsg(eb, err_msg, stderr, prefix_len);220 try writeMsg(eb, err_msg, w, prefix_len);
221 try ttyconf.setColor(stderr, .dim);221 try ttyconf.setColor(w, .dim);
222 try stderr.print(" ({d} times)\n", .{err_msg.count});222 try w.print(" ({d} times)\n", .{err_msg.count});
223 }223 }
224 try ttyconf.setColor(stderr, .reset);224 try ttyconf.setColor(w, .reset);
225 if (src.data.source_line != 0 and options.include_source_line) {225 if (src.data.source_line != 0 and options.include_source_line) {
226 const line = eb.nullTerminatedString(src.data.source_line);226 const line = eb.nullTerminatedString(src.data.source_line);
227 for (line) |b| switch (b) {227 for (line) |b| switch (b) {
228 '\t' => try stderr.writeByte(' '),228 '\t' => try w.writeByte(' '),
229 else => try stderr.writeByte(b),229 else => try w.writeByte(b),
230 };230 };
231 try stderr.writeByte('\n');231 try w.writeByte('\n');
232 // TODO basic unicode code point monospace width232 // TODO basic unicode code point monospace width
233 const before_caret = src.data.span_main - src.data.span_start;233 const before_caret = src.data.span_main - src.data.span_start;
234 // -1 since span.main includes the caret234 // -1 since span.main includes the caret
235 const after_caret = src.data.span_end -| src.data.span_main -| 1;235 const after_caret = src.data.span_end -| src.data.span_main -| 1;
236 try stderr.writeByteNTimes(' ', src.data.column - before_caret);236 try w.splatByteAll(' ', src.data.column - before_caret);
237 try ttyconf.setColor(stderr, .green);237 try ttyconf.setColor(w, .green);
238 try stderr.writeByteNTimes('~', before_caret);238 try w.splatByteAll('~', before_caret);
239 try stderr.writeByte('^');239 try w.writeByte('^');
240 try stderr.writeByteNTimes('~', after_caret);240 try w.splatByteAll('~', after_caret);
241 try stderr.writeByte('\n');241 try w.writeByte('\n');
242 try ttyconf.setColor(stderr, .reset);242 try ttyconf.setColor(w, .reset);
243 }243 }
244 for (eb.getNotes(err_msg_index)) |note| {244 for (eb.getNotes(err_msg_index)) |note| {
245 try renderErrorMessageToWriter(eb, options, note, stderr, "note", .cyan, indent);245 try renderErrorMessageToWriter(eb, options, note, w, "note", .cyan, indent);
246 }246 }
247 if (src.data.reference_trace_len > 0 and options.include_reference_trace) {247 if (src.data.reference_trace_len > 0 and options.include_reference_trace) {
248 try ttyconf.setColor(stderr, .reset);248 try ttyconf.setColor(w, .reset);
249 try ttyconf.setColor(stderr, .dim);249 try ttyconf.setColor(w, .dim);
250 try stderr.print("referenced by:\n", .{});250 try w.print("referenced by:\n", .{});
251 var ref_index = src.end;251 var ref_index = src.end;
252 for (0..src.data.reference_trace_len) |_| {252 for (0..src.data.reference_trace_len) |_| {
253 const ref_trace = eb.extraData(ReferenceTrace, ref_index);253 const ref_trace = eb.extraData(ReferenceTrace, ref_index);
254 ref_index = ref_trace.end;254 ref_index = ref_trace.end;
255 if (ref_trace.data.src_loc != .none) {255 if (ref_trace.data.src_loc != .none) {
256 const ref_src = eb.getSourceLocation(ref_trace.data.src_loc);256 const ref_src = eb.getSourceLocation(ref_trace.data.src_loc);
257 try stderr.print(" {s}: {s}:{d}:{d}\n", .{257 try w.print(" {s}: {s}:{d}:{d}\n", .{
258 eb.nullTerminatedString(ref_trace.data.decl_name),258 eb.nullTerminatedString(ref_trace.data.decl_name),
259 eb.nullTerminatedString(ref_src.src_path),259 eb.nullTerminatedString(ref_src.src_path),
260 ref_src.line + 1,260 ref_src.line + 1,
...@@ -262,36 +262,36 @@ fn renderErrorMessageToWriter(...@@ -262,36 +262,36 @@ fn renderErrorMessageToWriter(
262 });262 });
263 } else if (ref_trace.data.decl_name != 0) {263 } else if (ref_trace.data.decl_name != 0) {
264 const count = ref_trace.data.decl_name;264 const count = ref_trace.data.decl_name;
265 try stderr.print(265 try w.print(
266 " {d} reference(s) hidden; use '-freference-trace={d}' to see all references\n",266 " {d} reference(s) hidden; use '-freference-trace={d}' to see all references\n",
267 .{ count, count + src.data.reference_trace_len - 1 },267 .{ count, count + src.data.reference_trace_len - 1 },
268 );268 );
269 } else {269 } else {
270 try stderr.print(270 try w.print(
271 " remaining reference traces hidden; use '-freference-trace' to see all reference traces\n",271 " remaining reference traces hidden; use '-freference-trace' to see all reference traces\n",
272 .{},272 .{},
273 );273 );
274 }274 }
275 }275 }
276 try ttyconf.setColor(stderr, .reset);276 try ttyconf.setColor(w, .reset);
277 }277 }
278 } else {278 } else {
279 try ttyconf.setColor(stderr, color);279 try ttyconf.setColor(w, color);
280 try stderr.writeByteNTimes(' ', indent);280 try w.splatByteAll(' ', indent);
281 try stderr.writeAll(kind);281 try w.writeAll(kind);
282 try stderr.writeAll(": ");282 try w.writeAll(": ");
283 try ttyconf.setColor(stderr, .reset);283 try ttyconf.setColor(w, .reset);
284 const msg = eb.nullTerminatedString(err_msg.msg);284 const msg = eb.nullTerminatedString(err_msg.msg);
285 if (err_msg.count == 1) {285 if (err_msg.count == 1) {
286 try stderr.print("{s}\n", .{msg});286 try w.print("{s}\n", .{msg});
287 } else {287 } else {
288 try stderr.print("{s}", .{msg});288 try w.print("{s}", .{msg});
289 try ttyconf.setColor(stderr, .dim);289 try ttyconf.setColor(w, .dim);
290 try stderr.print(" ({d} times)\n", .{err_msg.count});290 try w.print(" ({d} times)\n", .{err_msg.count});
291 }291 }
292 try ttyconf.setColor(stderr, .reset);292 try ttyconf.setColor(w, .reset);
293 for (eb.getNotes(err_msg_index)) |note| {293 for (eb.getNotes(err_msg_index)) |note| {
294 try renderErrorMessageToWriter(eb, options, note, stderr, "note", .cyan, indent + 4);294 try renderErrorMessageToWriter(eb, options, note, w, "note", .cyan, indent + 4);
295 }295 }
296 }296 }
297}297}
...@@ -300,13 +300,13 @@ fn renderErrorMessageToWriter(...@@ -300,13 +300,13 @@ fn renderErrorMessageToWriter(
300/// to allow for long, good-looking error messages.300/// to allow for long, good-looking error messages.
301///301///
302/// This is used to split the message in `@compileError("hello\nworld")` for example.302/// This is used to split the message in `@compileError("hello\nworld")` for example.
303fn writeMsg(eb: ErrorBundle, err_msg: ErrorMessage, stderr: anytype, indent: usize) !void {303fn writeMsg(eb: ErrorBundle, err_msg: ErrorMessage, w: *Writer, indent: usize) !void {
304 var lines = std.mem.splitScalar(u8, eb.nullTerminatedString(err_msg.msg), '\n');304 var lines = std.mem.splitScalar(u8, eb.nullTerminatedString(err_msg.msg), '\n');
305 while (lines.next()) |line| {305 while (lines.next()) |line| {
306 try stderr.writeAll(line);306 try w.writeAll(line);
307 if (lines.index == null) break;307 if (lines.index == null) break;
308 try stderr.writeByte('\n');308 try w.writeByte('\n');
309 try stderr.writeByteNTimes(' ', indent);309 try w.splatByteAll(' ', indent);
310 }310 }
311}311}
312312
...@@ -398,7 +398,7 @@ pub const Wip = struct {...@@ -398,7 +398,7 @@ pub const Wip = struct {
398 pub fn printString(wip: *Wip, comptime fmt: []const u8, args: anytype) Allocator.Error!String {398 pub fn printString(wip: *Wip, comptime fmt: []const u8, args: anytype) Allocator.Error!String {
399 const gpa = wip.gpa;399 const gpa = wip.gpa;
400 const index: String = @intCast(wip.string_bytes.items.len);400 const index: String = @intCast(wip.string_bytes.items.len);
401 try wip.string_bytes.writer(gpa).print(fmt, args);401 try wip.string_bytes.print(gpa, fmt, args);
402 try wip.string_bytes.append(gpa, 0);402 try wip.string_bytes.append(gpa, 0);
403 return index;403 return index;
404 }404 }
...@@ -788,9 +788,10 @@ pub const Wip = struct {...@@ -788,9 +788,10 @@ pub const Wip = struct {
788788
789 const ttyconf: std.io.tty.Config = .no_color;789 const ttyconf: std.io.tty.Config = .no_color;
790790
791 var bundle_buf = std.ArrayList(u8).init(std.testing.allocator);791 var bundle_buf: std.io.Writer.Allocating = .init(std.testing.allocator);
792 const bundle_bw = &bundle_buf.interface;
792 defer bundle_buf.deinit();793 defer bundle_buf.deinit();
793 try bundle.renderToWriter(.{ .ttyconf = ttyconf }, bundle_buf.writer());794 try bundle.renderToWriter(.{ .ttyconf = ttyconf }, bundle_bw);
794795
795 var copy = copy: {796 var copy = copy: {
796 var wip: ErrorBundle.Wip = undefined;797 var wip: ErrorBundle.Wip = undefined;
...@@ -803,10 +804,11 @@ pub const Wip = struct {...@@ -803,10 +804,11 @@ pub const Wip = struct {
803 };804 };
804 defer copy.deinit(std.testing.allocator);805 defer copy.deinit(std.testing.allocator);
805806
806 var copy_buf = std.ArrayList(u8).init(std.testing.allocator);807 var copy_buf: std.io.Writer.Allocating = .init(std.testing.allocator);
808 const copy_bw = &copy_buf.interface;
807 defer copy_buf.deinit();809 defer copy_buf.deinit();
808 try copy.renderToWriter(.{ .ttyconf = ttyconf }, copy_buf.writer());810 try copy.renderToWriter(.{ .ttyconf = ttyconf }, copy_bw);
809811
810 try std.testing.expectEqualStrings(bundle_buf.items, copy_buf.items);812 try std.testing.expectEqualStrings(bundle_bw.getWritten(), copy_bw.getWritten());
811 }813 }
812};814};