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;
1212const Fuzz = std.Build.Fuzz;
1313const Allocator = std.mem.Allocator;
1414const fatal = std.process.fatal;
15const Writer = std.io.Writer;
1516const runner = @This();
1617
1718pub const root = @import("@build");
......@@ -330,7 +331,7 @@ pub fn main() !void {
330331 }
331332 }
332333
333 const stderr = std.fs.File.stderr();
334 const stderr: std.fs.File = .stderr();
334335 const ttyconf = get_tty_conf(color, stderr);
335336 switch (ttyconf) {
336337 .no_color => try graph.env_map.put("NO_COLOR", "1"),
......@@ -378,13 +379,19 @@ pub fn main() !void {
378379
379380 validateSystemLibraryOptions(builder);
380381
381 const stdout_writer = std.fs.File.stdout().deprecatedWriter();
382
383 if (help_menu)
384 return usage(builder, stdout_writer);
382 if (help_menu) {
383 var w = initStdoutWriter();
384 printUsage(builder, w) catch return stdout_writer_allocation.err.?;
385 w.flush() catch return stdout_writer_allocation.err.?;
386 return;
387 }
385388
386 if (steps_menu)
387 return steps(builder, stdout_writer);
389 if (steps_menu) {
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
389396 var run: Run = .{
390397 .max_rss = max_rss,
......@@ -696,24 +703,21 @@ fn runStepNames(
696703 const ttyconf = run.ttyconf;
697704
698705 if (run.summary != .none) {
699 std.debug.lockStdErr();
700 defer std.debug.unlockStdErr();
701 const stderr = run.stderr;
706 const w = std.debug.lockStderrWriter(&stdio_buffer_allocation);
707 defer std.debug.unlockStderrWriter();
702708
703709 const total_count = success_count + failure_count + pending_count + skipped_count;
704 ttyconf.setColor(stderr, .cyan) catch {};
705 stderr.writeAll("Build Summary:") catch {};
706 ttyconf.setColor(stderr, .reset) catch {};
707 stderr.deprecatedWriter().print(" {d}/{d} steps succeeded", .{ success_count, total_count }) catch {};
708 if (skipped_count > 0) stderr.deprecatedWriter().print("; {d} skipped", .{skipped_count}) catch {};
709 if (failure_count > 0) stderr.deprecatedWriter().print("; {d} failed", .{failure_count}) catch {};
710
711 if (test_count > 0) stderr.deprecatedWriter().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 {};
713 if (test_fail_count > 0) stderr.deprecatedWriter().print("; {d} failed", .{test_fail_count}) catch {};
714 if (test_leak_count > 0) stderr.deprecatedWriter().print("; {d} leaked", .{test_leak_count}) catch {};
715
716 stderr.writeAll("\n") catch {};
710 ttyconf.setColor(w, .cyan) catch {};
711 w.writeAll("Build Summary:") catch {};
712 ttyconf.setColor(w, .reset) catch {};
713 w.print(" {d}/{d} steps succeeded", .{ success_count, total_count }) catch {};
714 if (skipped_count > 0) w.print("; {d} skipped", .{skipped_count}) catch {};
715 if (failure_count > 0) w.print("; {d} failed", .{failure_count}) catch {};
716
717 if (test_count > 0) w.print("; {d}/{d} tests passed", .{ test_pass_count, test_count }) catch {};
718 if (test_skip_count > 0) w.print("; {d} skipped", .{test_skip_count}) catch {};
719 if (test_fail_count > 0) w.print("; {d} failed", .{test_fail_count}) catch {};
720 if (test_leak_count > 0) w.print("; {d} leaked", .{test_leak_count}) catch {};
717721
718722 // Print a fancy tree with build results.
719723 var step_stack_copy = try step_stack.clone(gpa);
......@@ -722,7 +726,7 @@ fn runStepNames(
722726 var print_node: PrintNode = .{ .parent = null };
723727 if (step_names.len == 0) {
724728 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 {};
726730 } else {
727731 const last_index = if (run.summary == .all) b.top_level_steps.count() else blk: {
728732 var i: usize = step_names.len;
......@@ -741,9 +745,10 @@ fn runStepNames(
741745 for (step_names, 0..) |step_name, i| {
742746 const tls = b.top_level_steps.get(step_name).?;
743747 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 {};
745749 }
746750 }
751 w.writeByte('\n') catch {};
747752 }
748753
749754 if (failure_count == 0) {
......@@ -775,7 +780,7 @@ const PrintNode = struct {
775780 last: bool = false,
776781};
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 {
779784 const parent = node.parent orelse return;
780785 if (parent.parent == null) return;
781786 try printPrefix(parent, stderr, ttyconf);
......@@ -789,7 +794,7 @@ fn printPrefix(node: *PrintNode, stderr: File, ttyconf: std.io.tty.Config) !void
789794 }
790795}
791796
792fn printChildNodePrefix(stderr: File, ttyconf: std.io.tty.Config) !void {
797fn printChildNodePrefix(stderr: *Writer, ttyconf: std.io.tty.Config) !void {
793798 try stderr.writeAll(switch (ttyconf) {
794799 .no_color, .windows_api => "+- ",
795800 .escape_codes => "\x1B\x28\x30\x6d\x71\x1B\x28\x42 ", // └─
......@@ -798,7 +803,7 @@ fn printChildNodePrefix(stderr: File, ttyconf: std.io.tty.Config) !void {
798803
799804fn printStepStatus(
800805 s: *Step,
801 stderr: File,
806 stderr: *Writer,
802807 ttyconf: std.io.tty.Config,
803808 run: *const Run,
804809) !void {
......@@ -820,10 +825,10 @@ fn printStepStatus(
820825 try stderr.writeAll(" cached");
821826 } else if (s.test_results.test_count > 0) {
822827 const pass_count = s.test_results.passCount();
823 try stderr.deprecatedWriter().print(" {d} passed", .{pass_count});
828 try stderr.print(" {d} passed", .{pass_count});
824829 if (s.test_results.skip_count > 0) {
825830 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});
827832 }
828833 } else {
829834 try stderr.writeAll(" success");
......@@ -832,15 +837,15 @@ fn printStepStatus(
832837 if (s.result_duration_ns) |ns| {
833838 try ttyconf.setColor(stderr, .dim);
834839 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});
836841 } 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});
838843 } 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});
840845 } 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});
842847 } else {
843 try stderr.deprecatedWriter().print(" {d}ns", .{ns});
848 try stderr.print(" {d}ns", .{ns});
844849 }
845850 try ttyconf.setColor(stderr, .reset);
846851 }
......@@ -848,13 +853,13 @@ fn printStepStatus(
848853 const rss = s.result_peak_rss;
849854 try ttyconf.setColor(stderr, .dim);
850855 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});
852857 } 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});
854859 } else if (rss >= 1000) {
855 try stderr.deprecatedWriter().print(" MaxRSS:{d}K", .{rss / 1000});
860 try stderr.print(" MaxRSS:{d}K", .{rss / 1000});
856861 } else {
857 try stderr.deprecatedWriter().print(" MaxRSS:{d}B", .{rss});
862 try stderr.print(" MaxRSS:{d}B", .{rss});
858863 }
859864 try ttyconf.setColor(stderr, .reset);
860865 }
......@@ -866,7 +871,7 @@ fn printStepStatus(
866871 if (skip == .skipped_oom) {
867872 try stderr.writeAll(" (not enough memory)");
868873 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 });
870875 try ttyconf.setColor(stderr, .yellow);
871876 }
872877 try stderr.writeAll("\n");
......@@ -878,23 +883,23 @@ fn printStepStatus(
878883
879884fn printStepFailure(
880885 s: *Step,
881 stderr: File,
886 stderr: *Writer,
882887 ttyconf: std.io.tty.Config,
883888) !void {
884889 if (s.result_error_bundle.errorMessageCount() > 0) {
885890 try ttyconf.setColor(stderr, .red);
886 try stderr.deprecatedWriter().print(" {d} errors\n", .{
891 try stderr.print(" {d} errors\n", .{
887892 s.result_error_bundle.errorMessageCount(),
888893 });
889894 try ttyconf.setColor(stderr, .reset);
890895 } else if (!s.test_results.isSuccess()) {
891 try stderr.deprecatedWriter().print(" {d}/{d} passed", .{
896 try stderr.print(" {d}/{d} passed", .{
892897 s.test_results.passCount(), s.test_results.test_count,
893898 });
894899 if (s.test_results.fail_count > 0) {
895900 try stderr.writeAll(", ");
896901 try ttyconf.setColor(stderr, .red);
897 try stderr.deprecatedWriter().print("{d} failed", .{
902 try stderr.print("{d} failed", .{
898903 s.test_results.fail_count,
899904 });
900905 try ttyconf.setColor(stderr, .reset);
......@@ -902,7 +907,7 @@ fn printStepFailure(
902907 if (s.test_results.skip_count > 0) {
903908 try stderr.writeAll(", ");
904909 try ttyconf.setColor(stderr, .yellow);
905 try stderr.deprecatedWriter().print("{d} skipped", .{
910 try stderr.print("{d} skipped", .{
906911 s.test_results.skip_count,
907912 });
908913 try ttyconf.setColor(stderr, .reset);
......@@ -910,7 +915,7 @@ fn printStepFailure(
910915 if (s.test_results.leak_count > 0) {
911916 try stderr.writeAll(", ");
912917 try ttyconf.setColor(stderr, .red);
913 try stderr.deprecatedWriter().print("{d} leaked", .{
918 try stderr.print("{d} leaked", .{
914919 s.test_results.leak_count,
915920 });
916921 try ttyconf.setColor(stderr, .reset);
......@@ -932,7 +937,7 @@ fn printTreeStep(
932937 b: *std.Build,
933938 s: *Step,
934939 run: *const Run,
935 stderr: File,
940 stderr: *Writer,
936941 ttyconf: std.io.tty.Config,
937942 parent_node: *PrintNode,
938943 step_stack: *std.AutoArrayHashMapUnmanaged(*Step, void),
......@@ -992,7 +997,7 @@ fn printTreeStep(
992997 if (s.dependencies.items.len == 0) {
993998 try stderr.writeAll(" (reused)\n");
994999 } else {
995 try stderr.deprecatedWriter().print(" (+{d} more reused dependencies)\n", .{
1000 try stderr.print(" (+{d} more reused dependencies)\n", .{
9961001 s.dependencies.items.len,
9971002 });
9981003 }
......@@ -1129,11 +1134,11 @@ fn workerMakeOneStep(
11291134 const show_stderr = s.result_stderr.len > 0;
11301135
11311136 if (show_error_msgs or show_compile_errors or show_stderr) {
1132 std.debug.lockStdErr();
1133 defer std.debug.unlockStdErr();
1137 const bw = std.debug.lockStderrWriter(&stdio_buffer_allocation);
1138 defer std.debug.unlockStderrWriter();
11341139
11351140 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 {};
11371142 }
11381143
11391144 handle_result: {
......@@ -1190,7 +1195,7 @@ pub fn printErrorMessages(
11901195 gpa: Allocator,
11911196 failing_step: *Step,
11921197 options: std.zig.ErrorBundle.RenderOptions,
1193 stderr: File,
1198 stderr: *Writer,
11941199 prominent_compile_errors: bool,
11951200) !void {
11961201 // Provide context for where these error messages are coming from by
......@@ -1209,7 +1214,7 @@ pub fn printErrorMessages(
12091214 var indent: usize = 0;
12101215 while (step_stack.pop()) |s| : (indent += 1) {
12111216 if (indent > 0) {
1212 try stderr.deprecatedWriter().writeByteNTimes(' ', (indent - 1) * 3);
1217 try stderr.splatByteAll(' ', (indent - 1) * 3);
12131218 try printChildNodePrefix(stderr, ttyconf);
12141219 }
12151220
......@@ -1231,7 +1236,7 @@ pub fn printErrorMessages(
12311236 }
12321237
12331238 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);
12351240 }
12361241
12371242 for (failing_step.result_error_msgs.items) |msg| {
......@@ -1243,27 +1248,27 @@ pub fn printErrorMessages(
12431248 }
12441249}
12451250
1246fn steps(builder: *std.Build, out_stream: anytype) !void {
1251fn printSteps(builder: *std.Build, w: *Writer) !void {
12471252 const allocator = builder.allocator;
12481253 for (builder.top_level_steps.values()) |top_level_step| {
12491254 const name = if (&top_level_step.step == builder.default_step)
12501255 try fmt.allocPrint(allocator, "{s} (default)", .{top_level_step.step.name})
12511256 else
12521257 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 });
12541259 }
12551260}
12561261
1257fn usage(b: *std.Build, out_stream: anytype) !void {
1258 try out_stream.print(
1262fn printUsage(b: *std.Build, w: *Writer) !void {
1263 try w.print(
12591264 \\Usage: {s} build [steps] [options]
12601265 \\
12611266 \\Steps:
12621267 \\
12631268 , .{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(
12671272 \\
12681273 \\General Options:
12691274 \\ -p, --prefix [path] Where to install files (default: zig-out)
......@@ -1319,25 +1324,25 @@ fn usage(b: *std.Build, out_stream: anytype) !void {
13191324
13201325 const arena = b.allocator;
13211326 if (b.available_options_list.items.len == 0) {
1322 try out_stream.print(" (none)\n", .{});
1327 try w.print(" (none)\n", .{});
13231328 } else {
13241329 for (b.available_options_list.items) |option| {
13251330 const name = try fmt.allocPrint(arena, " -D{s}=[{s}]", .{
13261331 option.name,
13271332 @tagName(option.type_id),
13281333 });
1329 try out_stream.print("{s:<30} {s}\n", .{ name, option.description });
1334 try w.print("{s:<30} {s}\n", .{ name, option.description });
13301335 if (option.enum_options) |enum_options| {
13311336 const padding = " " ** 33;
1332 try out_stream.writeAll(padding ++ "Supported Values:\n");
1337 try w.writeAll(padding ++ "Supported Values:\n");
13331338 for (enum_options) |enum_option| {
1334 try out_stream.print(padding ++ " {s}\n", .{enum_option});
1339 try w.print(padding ++ " {s}\n", .{enum_option});
13351340 }
13361341 }
13371342 }
13381343 }
13391344
1340 try out_stream.writeAll(
1345 try w.writeAll(
13411346 \\
13421347 \\System Integration Options:
13431348 \\ --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 {
13521357 \\
13531358 );
13541359 if (b.graph.system_library_options.entries.len == 0) {
1355 try out_stream.writeAll(" (none) -\n");
1360 try w.writeAll(" (none) -\n");
13561361 } else {
13571362 for (b.graph.system_library_options.keys(), b.graph.system_library_options.values()) |k, v| {
13581363 const status = switch (v) {
......@@ -1360,11 +1365,11 @@ fn usage(b: *std.Build, out_stream: anytype) !void {
13601365 .declared_disabled => "no",
13611366 .user_enabled, .user_disabled => unreachable, // already emitted error
13621367 };
1363 try out_stream.print(" {s:<43} {s}\n", .{ k, status });
1368 try w.print(" {s:<43} {s}\n", .{ k, status });
13641369 }
13651370 }
13661371
1367 try out_stream.writeAll(
1372 try w.writeAll(
13681373 \\
13691374 \\Advanced Options:
13701375 \\ -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 {
15441549 };
15451550 }
15461551}
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(
284284 .h_dir = undefined,
285285 .dest_dir = graph.env_map.get("DESTDIR"),
286286 .install_tls = .{
287 .step = Step.init(.{
287 .step = .init(.{
288288 .id = TopLevelStep.base_id,
289289 .name = "install",
290290 .owner = b,
......@@ -292,7 +292,7 @@ pub fn create(
292292 .description = "Copy build artifacts to prefix path",
293293 },
294294 .uninstall_tls = .{
295 .step = Step.init(.{
295 .step = .init(.{
296296 .id = TopLevelStep.base_id,
297297 .name = "uninstall",
298298 .owner = b,
......@@ -342,7 +342,7 @@ fn createChildOnly(
342342 .graph = parent.graph,
343343 .allocator = allocator,
344344 .install_tls = .{
345 .step = Step.init(.{
345 .step = .init(.{
346346 .id = TopLevelStep.base_id,
347347 .name = "install",
348348 .owner = child,
......@@ -350,7 +350,7 @@ fn createChildOnly(
350350 .description = "Copy build artifacts to prefix path",
351351 },
352352 .uninstall_tls = .{
353 .step = Step.init(.{
353 .step = .init(.{
354354 .id = TopLevelStep.base_id,
355355 .name = "uninstall",
356356 .owner = child,
......@@ -1525,7 +1525,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
15251525pub fn step(b: *Build, name: []const u8, description: []const u8) *Step {
15261526 const step_info = b.allocator.create(TopLevelStep) catch @panic("OOM");
15271527 step_info.* = .{
1528 .step = Step.init(.{
1528 .step = .init(.{
15291529 .id = TopLevelStep.base_id,
15301530 .name = name,
15311531 .owner = b,
......@@ -1824,13 +1824,13 @@ pub fn validateUserInputDidItFail(b: *Build) bool {
18241824 return b.invalid_user_input;
18251825}
18261826
1827fn allocPrintCmd(ally: Allocator, opt_cwd: ?[]const u8, argv: []const []const u8) error{OutOfMemory}![]u8 {
1828 var buf = ArrayList(u8).init(ally);
1829 if (opt_cwd) |cwd| try buf.writer().print("cd {s} && ", .{cwd});
1827fn allocPrintCmd(gpa: Allocator, opt_cwd: ?[]const u8, argv: []const []const u8) error{OutOfMemory}![]u8 {
1828 var buf: std.ArrayListUnmanaged(u8) = .empty;
1829 if (opt_cwd) |cwd| try buf.print(gpa, "cd {s} && ", .{cwd});
18301830 for (argv) |arg| {
1831 try buf.writer().print("{s} ", .{arg});
1831 try buf.print(gpa, "{s} ", .{arg});
18321832 }
1833 return buf.toOwnedSlice();
1833 return buf.toOwnedSlice(gpa);
18341834}
18351835
18361836fn printCmd(ally: Allocator, cwd: ?[]const u8, argv: []const []const u8) void {
......@@ -2466,10 +2466,9 @@ pub const GeneratedFile = struct {
24662466
24672467 pub fn getPath2(gen: GeneratedFile, src_builder: *Build, asking_step: ?*Step) []const u8 {
24682468 return gen.path orelse {
2469 std.debug.lockStdErr();
2470 const stderr = std.fs.File.stderr();
2471 dumpBadGetPathHelp(gen.step, stderr, src_builder, asking_step) catch {};
2472 std.debug.unlockStdErr();
2469 const w = debug.lockStderrWriter(&.{});
2470 dumpBadGetPathHelp(gen.step, w, .detect(.stderr()), src_builder, asking_step) catch {};
2471 debug.unlockStderrWriter();
24732472 @panic("misconfigured build script");
24742473 };
24752474 }
......@@ -2676,10 +2675,9 @@ pub const LazyPath = union(enum) {
26762675 var file_path: Cache.Path = .{
26772676 .root_dir = Cache.Directory.cwd(),
26782677 .sub_path = gen.file.path orelse {
2679 std.debug.lockStdErr();
2680 const stderr: fs.File = .stderr();
2681 dumpBadGetPathHelp(gen.file.step, stderr, src_builder, asking_step) catch {};
2682 std.debug.unlockStdErr();
2678 const w = debug.lockStderrWriter(&.{});
2679 dumpBadGetPathHelp(gen.file.step, w, .detect(.stderr()), src_builder, asking_step) catch {};
2680 debug.unlockStderrWriter();
26832681 @panic("misconfigured build script");
26842682 },
26852683 };
......@@ -2766,44 +2764,42 @@ fn dumpBadDirnameHelp(
27662764 comptime msg: []const u8,
27672765 args: anytype,
27682766) anyerror!void {
2769 debug.lockStdErr();
2770 defer debug.unlockStdErr();
2767 const w = debug.lockStderrWriter(&.{});
2768 defer debug.unlockStderrWriter();
27712769
2772 const stderr: fs.File = .stderr();
2773 const w = stderr.deprecatedWriter();
27742770 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
27782774 if (fail_step) |s| {
27792775 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");
27812777 tty_config.setColor(w, .reset) catch {};
27822778
2783 s.dump(stderr);
2779 s.dump(w, tty_config);
27842780 }
27852781
27862782 if (asking_step) |as| {
27872783 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});
27892785 tty_config.setColor(w, .reset) catch {};
27902786
2791 as.dump(stderr);
2787 as.dump(w, tty_config);
27922788 }
27932789
27942790 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");
27962792 tty_config.setColor(w, .reset) catch {};
27972793}
27982794
27992795/// In this function the stderr mutex has already been locked.
28002796pub fn dumpBadGetPathHelp(
28012797 s: *Step,
2802 stderr: fs.File,
2798 w: *std.io.Writer,
2799 tty_config: std.io.tty.Config,
28032800 src_builder: *Build,
28042801 asking_step: ?*Step,
28052802) anyerror!void {
2806 const w = stderr.deprecatedWriter();
28072803 try w.print(
28082804 \\getPath() was called on a GeneratedFile that wasn't built yet.
28092805 \\ source package path: {s}
......@@ -2814,21 +2810,20 @@ pub fn dumpBadGetPathHelp(
28142810 s.name,
28152811 });
28162812
2817 const tty_config = std.io.tty.detectConfig(stderr);
28182813 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");
28202815 tty_config.setColor(w, .reset) catch {};
28212816
2822 s.dump(stderr);
2817 s.dump(w, tty_config);
28232818 if (asking_step) |as| {
28242819 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});
28262821 tty_config.setColor(w, .reset) catch {};
28272822
2828 as.dump(stderr);
2823 as.dump(w, tty_config);
28292824 }
28302825 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");
28322827 tty_config.setColor(w, .reset) catch {};
28332828}
28342829
......@@ -2866,11 +2861,6 @@ pub fn makeTempPath(b: *Build) []const u8 {
28662861 return result_path;
28672862}
28682863
2869/// Deprecated; use `std.fmt.hex` instead.
2870pub fn hex64(x: u64) [16]u8 {
2871 return std.fmt.hex(x);
2872}
2873
28742864/// A pair of target query and fully resolved target.
28752865/// This type is generally required by build system API that need to be given a
28762866/// 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
112112
113113fn rebuildTestsWorkerRunFallible(run: *Step.Run, ttyconf: std.io.tty.Config, parent_prog_node: std.Progress.Node) !void {
114114 const gpa = run.step.owner.allocator;
115 const stderr = std.fs.File.stderr();
116115
117116 const compile = run.producer.?;
118117 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
125124 const show_stderr = compile.step.result_stderr.len > 0;
126125
127126 if (show_error_msgs or show_compile_errors or show_stderr) {
128 std.debug.lockStdErr();
129 defer std.debug.unlockStdErr();
130 build_runner.printErrorMessages(gpa, &compile.step, .{ .ttyconf = ttyconf }, stderr, false) catch {};
127 var buf: [256]u8 = undefined;
128 const w = std.debug.lockStderrWriter(&buf);
129 defer std.debug.unlockStderrWriter();
130 build_runner.printErrorMessages(gpa, &compile.step, .{ .ttyconf = ttyconf }, w, false) catch {};
131131 }
132132
133133 const rebuilt_bin_path = result catch |err| switch (err) {
......@@ -152,10 +152,10 @@ fn fuzzWorkerRun(
152152
153153 run.rerunInFuzzMode(web_server, unit_test_index, prog_node) catch |err| switch (err) {
154154 error.MakeFailed => {
155 const stderr = std.fs.File.stderr();
156 std.debug.lockStdErr();
157 defer std.debug.unlockStdErr();
158 build_runner.printErrorMessages(gpa, &run.step, .{ .ttyconf = ttyconf }, stderr, false) catch {};
155 var buf: [256]u8 = undefined;
156 const w = std.debug.lockStderrWriter(&buf);
157 defer std.debug.unlockStderrWriter();
158 build_runner.printErrorMessages(gpa, &run.step, .{ .ttyconf = ttyconf }, w, false) catch {};
159159 return;
160160 },
161161 else => {
lib/std/Build/Step.zig+1-4
......@@ -286,10 +286,7 @@ pub fn cast(step: *Step, comptime T: type) ?*T {
286286}
287287
288288/// For debugging purposes, prints identifying information about this Step.
289pub fn dump(step: *Step, file: std.fs.File) void {
290 var fw = file.writer(&.{});
291 const w = &fw.interface;
292 const tty_config = std.io.tty.detectConfig(file);
289pub fn dump(step: *Step, w: *std.io.Writer, tty_config: std.io.tty.Config) void {
293290 const debug_info = std.debug.getSelfDebugInfo() catch |err| {
294291 w.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{
295292 @errorName(err),
lib/std/Build/Step/Compile.zig+30-31
......@@ -409,7 +409,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
409409 .linkage = options.linkage,
410410 .kind = options.kind,
411411 .name = name,
412 .step = Step.init(.{
412 .step = .init(.{
413413 .id = base_id,
414414 .name = step_name,
415415 .owner = owner,
......@@ -1017,20 +1017,16 @@ fn getGeneratedFilePath(compile: *Compile, comptime tag_name: []const u8, asking
10171017 const maybe_path: ?*GeneratedFile = @field(compile, tag_name);
10181018
10191019 const generated_file = maybe_path orelse {
1020 std.debug.lockStdErr();
1021 const stderr: fs.File = .stderr();
1022
1023 std.Build.dumpBadGetPathHelp(&compile.step, stderr, compile.step.owner, asking_step) catch {};
1024
1020 const w = std.debug.lockStderrWriter(&.{});
1021 std.Build.dumpBadGetPathHelp(&compile.step, w, .detect(.stderr()), compile.step.owner, asking_step) catch {};
1022 std.debug.unlockStderrWriter();
10251023 @panic("missing emit option for " ++ tag_name);
10261024 };
10271025
10281026 const path = generated_file.path orelse {
1029 std.debug.lockStdErr();
1030 const stderr: fs.File = .stderr();
1031
1032 std.Build.dumpBadGetPathHelp(&compile.step, stderr, compile.step.owner, asking_step) catch {};
1033
1027 const w = std.debug.lockStderrWriter(&.{});
1028 std.Build.dumpBadGetPathHelp(&compile.step, w, .detect(.stderr()), compile.step.owner, asking_step) catch {};
1029 std.debug.unlockStderrWriter();
10341030 @panic(tag_name ++ " is null. Is there a missing step dependency?");
10351031 };
10361032
......@@ -1768,12 +1764,12 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
17681764 for (arg, 0..) |c, arg_idx| {
17691765 if (c == '\\' or c == '"') {
17701766 // 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);
1772 const writer = escaped.writer();
1773 try writer.writeAll(arg[0..arg_idx]);
1767 var escaped: std.ArrayListUnmanaged(u8) = .empty;
1768 try escaped.ensureTotalCapacityPrecise(arena, arg.len + 1);
1769 try escaped.appendSlice(arena, arg[0..arg_idx]);
17741770 for (arg[arg_idx..]) |to_escape| {
1775 if (to_escape == '\\' or to_escape == '"') try writer.writeByte('\\');
1776 try writer.writeByte(to_escape);
1771 if (to_escape == '\\' or to_escape == '"') try escaped.append(arena, '\\');
1772 try escaped.append(arena, to_escape);
17771773 }
17781774 escaped_args.appendAssumeCapacity(escaped.items);
17791775 continue :arg_blk;
......@@ -1963,20 +1959,23 @@ fn addFlag(args: *ArrayList([]const u8), comptime name: []const u8, opt: ?bool)
19631959fn checkCompileErrors(compile: *Compile) !void {
19641960 // Clear this field so that it does not get printed by the build runner.
19651961 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
19681964 const arena = compile.step.owner.allocator;
19691965
1970 var actual_errors_list = std.ArrayList(u8).init(arena);
1971 try actual_eb.renderToWriter(.{
1972 .ttyconf = .no_color,
1973 .include_reference_trace = false,
1974 .include_source_line = false,
1975 }, actual_errors_list.writer());
1976 const actual_errors = try actual_errors_list.toOwnedSlice();
1966 const actual_errors = ae: {
1967 var aw: std.io.Writer.Allocating = .init(arena);
1968 defer aw.deinit();
1969 try actual_eb.renderToWriter(.{
1970 .ttyconf = .no_color,
1971 .include_reference_trace = false,
1972 .include_source_line = false,
1973 }, &aw.writer);
1974 break :ae try aw.toOwnedSlice();
1975 };
19771976
19781977 // 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;
19801979 const expect_errors = compile.expect_errors.?;
19811980
19821981 var actual_line_it = mem.splitScalar(u8, actual_errors, '\n');
......@@ -2035,17 +2034,17 @@ fn checkCompileErrors(compile: *Compile) !void {
20352034 .exact => |expect_lines| {
20362035 for (expect_lines) |expect_line| {
20372036 const actual_line = actual_line_it.next() orelse {
2038 try expected_generated.appendSlice(expect_line);
2039 try expected_generated.append('\n');
2037 try expected_generated.appendSlice(arena, expect_line);
2038 try expected_generated.append(arena, '\n');
20402039 continue;
20412040 };
20422041 if (matchCompileError(actual_line, expect_line)) {
2043 try expected_generated.appendSlice(actual_line);
2044 try expected_generated.append('\n');
2042 try expected_generated.appendSlice(arena, actual_line);
2043 try expected_generated.append(arena, '\n');
20452044 continue;
20462045 }
2047 try expected_generated.appendSlice(expect_line);
2048 try expected_generated.append('\n');
2046 try expected_generated.appendSlice(arena, expect_line);
2047 try expected_generated.append(arena, '\n');
20492048 }
20502049
20512050 if (mem.eql(u8, expected_generated.items, actual_errors)) return;
lib/std/fs/File.zig-4
......@@ -1725,10 +1725,6 @@ pub const Writer = struct {
17251725 iovecs[len] = .{ .base = splat_buffer.ptr, .len = remaining_splat };
17261726 len += 1;
17271727 }
1728 return std.posix.writev(handle, iovecs[0..len]) catch |err| {
1729 w.err = err;
1730 return error.WriteFailed;
1731 };
17321728 },
17331729 else => for (0..splat - 1) |_| {
17341730 if (iovecs.len - len == 0) break;
lib/std/io/tty.zig+35-29
......@@ -5,36 +5,9 @@ const process = std.process;
55const windows = std.os.windows;
66const native_os = builtin.os.tag;
77
8/// Detect suitable TTY configuration options for the given file (commonly stdout/stderr).
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.
8/// Deprecated in favor of `Config.detect`.
129pub fn detectConfig(file: File) Config {
13 const force_color: ?bool = if (builtin.os.tag == .wasi)
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;
10 return .detect(file);
3811}
3912
4013pub const Color = enum {
......@@ -66,6 +39,38 @@ pub const Config = union(enum) {
6639 escape_codes,
6740 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
6974 pub const WindowsContext = struct {
7075 handle: File.Handle,
7176 reset_attributes: u16,
......@@ -123,6 +128,7 @@ pub const Config = union(enum) {
123128 .dim => windows.FOREGROUND_INTENSITY,
124129 .reset => ctx.reset_attributes,
125130 };
131 try w.flush();
126132 try windows.SetConsoleTextAttribute(ctx.handle, attributes);
127133 } else {
128134 unreachable;
lib/std/zig/ErrorBundle.zig+68-66
......@@ -11,6 +11,7 @@ const std = @import("std");
1111const ErrorBundle = @This();
1212const Allocator = std.mem.Allocator;
1313const assert = std.debug.assert;
14const Writer = std.io.Writer;
1415
1516string_bytes: []const u8,
1617/// The first thing in this array is an `ErrorMessageList`.
......@@ -162,23 +163,23 @@ pub const RenderOptions = struct {
162163};
163164
164165pub fn renderToStdErr(eb: ErrorBundle, options: RenderOptions) void {
165 std.debug.lockStdErr();
166 defer std.debug.unlockStdErr();
167 const stderr: std.fs.File = .stderr();
168 return renderToWriter(eb, options, stderr.deprecatedWriter()) catch return;
166 var buffer: [256]u8 = undefined;
167 const w = std.debug.lockStderrWriter(&buffer);
168 defer std.debug.unlockStderrWriter();
169 renderToWriter(eb, options, w) catch return;
169170}
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 {
172173 if (eb.extra.len == 0) return;
173174 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);
175176 }
176177
177178 if (options.include_log_text) {
178179 const log_text = eb.getCompileLogOutput();
179180 if (log_text.len != 0) {
180 try writer.writeAll("\nCompile Log Output:\n");
181 try writer.writeAll(log_text);
181 try w.writeAll("\nCompile Log Output:\n");
182 try w.writeAll(log_text);
182183 }
183184 }
184185}
......@@ -187,74 +188,73 @@ fn renderErrorMessageToWriter(
187188 eb: ErrorBundle,
188189 options: RenderOptions,
189190 err_msg_index: MessageIndex,
190 stderr: anytype,
191 w: *Writer,
191192 kind: []const u8,
192193 color: std.io.tty.Color,
193194 indent: usize,
194) anyerror!void {
195) (Writer.Error || std.posix.UnexpectedError)!void {
195196 const ttyconf = options.ttyconf;
196 var counting_writer = std.io.countingWriter(stderr);
197 const counting_stderr = counting_writer.writer();
198197 const err_msg = eb.getErrorMessage(err_msg_index);
198 const prefix_start = w.count;
199199 if (err_msg.src_loc != .none) {
200200 const src = eb.extraData(SourceLocation, @intFromEnum(err_msg.src_loc));
201 try counting_stderr.writeByteNTimes(' ', indent);
202 try ttyconf.setColor(stderr, .bold);
203 try counting_stderr.print("{s}:{d}:{d}: ", .{
201 try w.splatByteAll(' ', indent);
202 try ttyconf.setColor(w, .bold);
203 try w.print("{s}:{d}:{d}: ", .{
204204 eb.nullTerminatedString(src.data.src_path),
205205 src.data.line + 1,
206206 src.data.column + 1,
207207 });
208 try ttyconf.setColor(stderr, color);
209 try counting_stderr.writeAll(kind);
210 try counting_stderr.writeAll(": ");
208 try ttyconf.setColor(w, color);
209 try w.writeAll(kind);
210 try w.writeAll(": ");
211211 // This is the length of the part before the error message:
212212 // e.g. "file.zig:4:5: error: "
213 const prefix_len: usize = @intCast(counting_stderr.context.bytes_written);
214 try ttyconf.setColor(stderr, .reset);
215 try ttyconf.setColor(stderr, .bold);
213 const prefix_len = w.count - prefix_start;
214 try ttyconf.setColor(w, .reset);
215 try ttyconf.setColor(w, .bold);
216216 if (err_msg.count == 1) {
217 try writeMsg(eb, err_msg, stderr, prefix_len);
218 try stderr.writeByte('\n');
217 try writeMsg(eb, err_msg, w, prefix_len);
218 try w.writeByte('\n');
219219 } else {
220 try writeMsg(eb, err_msg, stderr, prefix_len);
221 try ttyconf.setColor(stderr, .dim);
222 try stderr.print(" ({d} times)\n", .{err_msg.count});
220 try writeMsg(eb, err_msg, w, prefix_len);
221 try ttyconf.setColor(w, .dim);
222 try w.print(" ({d} times)\n", .{err_msg.count});
223223 }
224 try ttyconf.setColor(stderr, .reset);
224 try ttyconf.setColor(w, .reset);
225225 if (src.data.source_line != 0 and options.include_source_line) {
226226 const line = eb.nullTerminatedString(src.data.source_line);
227227 for (line) |b| switch (b) {
228 '\t' => try stderr.writeByte(' '),
229 else => try stderr.writeByte(b),
228 '\t' => try w.writeByte(' '),
229 else => try w.writeByte(b),
230230 };
231 try stderr.writeByte('\n');
231 try w.writeByte('\n');
232232 // TODO basic unicode code point monospace width
233233 const before_caret = src.data.span_main - src.data.span_start;
234234 // -1 since span.main includes the caret
235235 const after_caret = src.data.span_end -| src.data.span_main -| 1;
236 try stderr.writeByteNTimes(' ', src.data.column - before_caret);
237 try ttyconf.setColor(stderr, .green);
238 try stderr.writeByteNTimes('~', before_caret);
239 try stderr.writeByte('^');
240 try stderr.writeByteNTimes('~', after_caret);
241 try stderr.writeByte('\n');
242 try ttyconf.setColor(stderr, .reset);
236 try w.splatByteAll(' ', src.data.column - before_caret);
237 try ttyconf.setColor(w, .green);
238 try w.splatByteAll('~', before_caret);
239 try w.writeByte('^');
240 try w.splatByteAll('~', after_caret);
241 try w.writeByte('\n');
242 try ttyconf.setColor(w, .reset);
243243 }
244244 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);
246246 }
247247 if (src.data.reference_trace_len > 0 and options.include_reference_trace) {
248 try ttyconf.setColor(stderr, .reset);
249 try ttyconf.setColor(stderr, .dim);
250 try stderr.print("referenced by:\n", .{});
248 try ttyconf.setColor(w, .reset);
249 try ttyconf.setColor(w, .dim);
250 try w.print("referenced by:\n", .{});
251251 var ref_index = src.end;
252252 for (0..src.data.reference_trace_len) |_| {
253253 const ref_trace = eb.extraData(ReferenceTrace, ref_index);
254254 ref_index = ref_trace.end;
255255 if (ref_trace.data.src_loc != .none) {
256256 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", .{
258258 eb.nullTerminatedString(ref_trace.data.decl_name),
259259 eb.nullTerminatedString(ref_src.src_path),
260260 ref_src.line + 1,
......@@ -262,36 +262,36 @@ fn renderErrorMessageToWriter(
262262 });
263263 } else if (ref_trace.data.decl_name != 0) {
264264 const count = ref_trace.data.decl_name;
265 try stderr.print(
265 try w.print(
266266 " {d} reference(s) hidden; use '-freference-trace={d}' to see all references\n",
267267 .{ count, count + src.data.reference_trace_len - 1 },
268268 );
269269 } else {
270 try stderr.print(
270 try w.print(
271271 " remaining reference traces hidden; use '-freference-trace' to see all reference traces\n",
272272 .{},
273273 );
274274 }
275275 }
276 try ttyconf.setColor(stderr, .reset);
276 try ttyconf.setColor(w, .reset);
277277 }
278278 } else {
279 try ttyconf.setColor(stderr, color);
280 try stderr.writeByteNTimes(' ', indent);
281 try stderr.writeAll(kind);
282 try stderr.writeAll(": ");
283 try ttyconf.setColor(stderr, .reset);
279 try ttyconf.setColor(w, color);
280 try w.splatByteAll(' ', indent);
281 try w.writeAll(kind);
282 try w.writeAll(": ");
283 try ttyconf.setColor(w, .reset);
284284 const msg = eb.nullTerminatedString(err_msg.msg);
285285 if (err_msg.count == 1) {
286 try stderr.print("{s}\n", .{msg});
286 try w.print("{s}\n", .{msg});
287287 } else {
288 try stderr.print("{s}", .{msg});
289 try ttyconf.setColor(stderr, .dim);
290 try stderr.print(" ({d} times)\n", .{err_msg.count});
288 try w.print("{s}", .{msg});
289 try ttyconf.setColor(w, .dim);
290 try w.print(" ({d} times)\n", .{err_msg.count});
291291 }
292 try ttyconf.setColor(stderr, .reset);
292 try ttyconf.setColor(w, .reset);
293293 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);
295295 }
296296 }
297297}
......@@ -300,13 +300,13 @@ fn renderErrorMessageToWriter(
300300/// to allow for long, good-looking error messages.
301301///
302302/// 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 {
304304 var lines = std.mem.splitScalar(u8, eb.nullTerminatedString(err_msg.msg), '\n');
305305 while (lines.next()) |line| {
306 try stderr.writeAll(line);
306 try w.writeAll(line);
307307 if (lines.index == null) break;
308 try stderr.writeByte('\n');
309 try stderr.writeByteNTimes(' ', indent);
308 try w.writeByte('\n');
309 try w.splatByteAll(' ', indent);
310310 }
311311}
312312
......@@ -398,7 +398,7 @@ pub const Wip = struct {
398398 pub fn printString(wip: *Wip, comptime fmt: []const u8, args: anytype) Allocator.Error!String {
399399 const gpa = wip.gpa;
400400 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);
402402 try wip.string_bytes.append(gpa, 0);
403403 return index;
404404 }
......@@ -788,9 +788,10 @@ pub const Wip = struct {
788788
789789 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;
792793 defer bundle_buf.deinit();
793 try bundle.renderToWriter(.{ .ttyconf = ttyconf }, bundle_buf.writer());
794 try bundle.renderToWriter(.{ .ttyconf = ttyconf }, bundle_bw);
794795
795796 var copy = copy: {
796797 var wip: ErrorBundle.Wip = undefined;
......@@ -803,10 +804,11 @@ pub const Wip = struct {
803804 };
804805 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;
807809 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());
811813 }
812814};