Commit cce32bd1d5

Andrew Kelley <andrew@ziglang.org>
2025-07-02 20:16:20
fix build runner
1 parent ec3b5f0
Changed files (8)
lib/compiler/build_runner.zig
@@ -12,6 +12,7 @@ const Watch = std.Build.Watch;
 const Fuzz = std.Build.Fuzz;
 const Allocator = std.mem.Allocator;
 const fatal = std.process.fatal;
+const Writer = std.io.Writer;
 const runner = @This();
 
 pub const root = @import("@build");
@@ -330,7 +331,7 @@ pub fn main() !void {
         }
     }
 
-    const stderr = std.fs.File.stderr();
+    const stderr: std.fs.File = .stderr();
     const ttyconf = get_tty_conf(color, stderr);
     switch (ttyconf) {
         .no_color => try graph.env_map.put("NO_COLOR", "1"),
@@ -378,13 +379,19 @@ pub fn main() !void {
 
     validateSystemLibraryOptions(builder);
 
-    const stdout_writer = std.fs.File.stdout().deprecatedWriter();
-
-    if (help_menu)
-        return usage(builder, stdout_writer);
+    if (help_menu) {
+        var w = initStdoutWriter();
+        printUsage(builder, w) catch return stdout_writer_allocation.err.?;
+        w.flush() catch return stdout_writer_allocation.err.?;
+        return;
+    }
 
-    if (steps_menu)
-        return steps(builder, stdout_writer);
+    if (steps_menu) {
+        var w = initStdoutWriter();
+        printSteps(builder, w) catch return stdout_writer_allocation.err.?;
+        w.flush() catch return stdout_writer_allocation.err.?;
+        return;
+    }
 
     var run: Run = .{
         .max_rss = max_rss,
@@ -696,24 +703,21 @@ fn runStepNames(
     const ttyconf = run.ttyconf;
 
     if (run.summary != .none) {
-        std.debug.lockStdErr();
-        defer std.debug.unlockStdErr();
-        const stderr = run.stderr;
+        const w = std.debug.lockStderrWriter(&stdio_buffer_allocation);
+        defer std.debug.unlockStderrWriter();
 
         const total_count = success_count + failure_count + pending_count + skipped_count;
-        ttyconf.setColor(stderr, .cyan) catch {};
-        stderr.writeAll("Build Summary:") catch {};
-        ttyconf.setColor(stderr, .reset) catch {};
-        stderr.deprecatedWriter().print(" {d}/{d} steps succeeded", .{ success_count, total_count }) catch {};
-        if (skipped_count > 0) stderr.deprecatedWriter().print("; {d} skipped", .{skipped_count}) catch {};
-        if (failure_count > 0) stderr.deprecatedWriter().print("; {d} failed", .{failure_count}) catch {};
-
-        if (test_count > 0) stderr.deprecatedWriter().print("; {d}/{d} tests passed", .{ test_pass_count, test_count }) catch {};
-        if (test_skip_count > 0) stderr.deprecatedWriter().print("; {d} skipped", .{test_skip_count}) catch {};
-        if (test_fail_count > 0) stderr.deprecatedWriter().print("; {d} failed", .{test_fail_count}) catch {};
-        if (test_leak_count > 0) stderr.deprecatedWriter().print("; {d} leaked", .{test_leak_count}) catch {};
-
-        stderr.writeAll("\n") catch {};
+        ttyconf.setColor(w, .cyan) catch {};
+        w.writeAll("Build Summary:") catch {};
+        ttyconf.setColor(w, .reset) catch {};
+        w.print(" {d}/{d} steps succeeded", .{ success_count, total_count }) catch {};
+        if (skipped_count > 0) w.print("; {d} skipped", .{skipped_count}) catch {};
+        if (failure_count > 0) w.print("; {d} failed", .{failure_count}) catch {};
+
+        if (test_count > 0) w.print("; {d}/{d} tests passed", .{ test_pass_count, test_count }) catch {};
+        if (test_skip_count > 0) w.print("; {d} skipped", .{test_skip_count}) catch {};
+        if (test_fail_count > 0) w.print("; {d} failed", .{test_fail_count}) catch {};
+        if (test_leak_count > 0) w.print("; {d} leaked", .{test_leak_count}) catch {};
 
         // Print a fancy tree with build results.
         var step_stack_copy = try step_stack.clone(gpa);
@@ -722,7 +726,7 @@ fn runStepNames(
         var print_node: PrintNode = .{ .parent = null };
         if (step_names.len == 0) {
             print_node.last = true;
-            printTreeStep(b, b.default_step, run, stderr, ttyconf, &print_node, &step_stack_copy) catch {};
+            printTreeStep(b, b.default_step, run, w, ttyconf, &print_node, &step_stack_copy) catch {};
         } else {
             const last_index = if (run.summary == .all) b.top_level_steps.count() else blk: {
                 var i: usize = step_names.len;
@@ -741,9 +745,10 @@ fn runStepNames(
             for (step_names, 0..) |step_name, i| {
                 const tls = b.top_level_steps.get(step_name).?;
                 print_node.last = i + 1 == last_index;
-                printTreeStep(b, &tls.step, run, stderr, ttyconf, &print_node, &step_stack_copy) catch {};
+                printTreeStep(b, &tls.step, run, w, ttyconf, &print_node, &step_stack_copy) catch {};
             }
         }
+        w.writeByte('\n') catch {};
     }
 
     if (failure_count == 0) {
@@ -775,7 +780,7 @@ const PrintNode = struct {
     last: bool = false,
 };
 
-fn printPrefix(node: *PrintNode, stderr: File, ttyconf: std.io.tty.Config) !void {
+fn printPrefix(node: *PrintNode, stderr: *Writer, ttyconf: std.io.tty.Config) !void {
     const parent = node.parent orelse return;
     if (parent.parent == null) return;
     try printPrefix(parent, stderr, ttyconf);
@@ -789,7 +794,7 @@ fn printPrefix(node: *PrintNode, stderr: File, ttyconf: std.io.tty.Config) !void
     }
 }
 
-fn printChildNodePrefix(stderr: File, ttyconf: std.io.tty.Config) !void {
+fn printChildNodePrefix(stderr: *Writer, ttyconf: std.io.tty.Config) !void {
     try stderr.writeAll(switch (ttyconf) {
         .no_color, .windows_api => "+- ",
         .escape_codes => "\x1B\x28\x30\x6d\x71\x1B\x28\x42 ", // └─
@@ -798,7 +803,7 @@ fn printChildNodePrefix(stderr: File, ttyconf: std.io.tty.Config) !void {
 
 fn printStepStatus(
     s: *Step,
-    stderr: File,
+    stderr: *Writer,
     ttyconf: std.io.tty.Config,
     run: *const Run,
 ) !void {
@@ -820,10 +825,10 @@ fn printStepStatus(
                 try stderr.writeAll(" cached");
             } else if (s.test_results.test_count > 0) {
                 const pass_count = s.test_results.passCount();
-                try stderr.deprecatedWriter().print(" {d} passed", .{pass_count});
+                try stderr.print(" {d} passed", .{pass_count});
                 if (s.test_results.skip_count > 0) {
                     try ttyconf.setColor(stderr, .yellow);
-                    try stderr.deprecatedWriter().print(" {d} skipped", .{s.test_results.skip_count});
+                    try stderr.print(" {d} skipped", .{s.test_results.skip_count});
                 }
             } else {
                 try stderr.writeAll(" success");
@@ -832,15 +837,15 @@ fn printStepStatus(
             if (s.result_duration_ns) |ns| {
                 try ttyconf.setColor(stderr, .dim);
                 if (ns >= std.time.ns_per_min) {
-                    try stderr.deprecatedWriter().print(" {d}m", .{ns / std.time.ns_per_min});
+                    try stderr.print(" {d}m", .{ns / std.time.ns_per_min});
                 } else if (ns >= std.time.ns_per_s) {
-                    try stderr.deprecatedWriter().print(" {d}s", .{ns / std.time.ns_per_s});
+                    try stderr.print(" {d}s", .{ns / std.time.ns_per_s});
                 } else if (ns >= std.time.ns_per_ms) {
-                    try stderr.deprecatedWriter().print(" {d}ms", .{ns / std.time.ns_per_ms});
+                    try stderr.print(" {d}ms", .{ns / std.time.ns_per_ms});
                 } else if (ns >= std.time.ns_per_us) {
-                    try stderr.deprecatedWriter().print(" {d}us", .{ns / std.time.ns_per_us});
+                    try stderr.print(" {d}us", .{ns / std.time.ns_per_us});
                 } else {
-                    try stderr.deprecatedWriter().print(" {d}ns", .{ns});
+                    try stderr.print(" {d}ns", .{ns});
                 }
                 try ttyconf.setColor(stderr, .reset);
             }
@@ -848,13 +853,13 @@ fn printStepStatus(
                 const rss = s.result_peak_rss;
                 try ttyconf.setColor(stderr, .dim);
                 if (rss >= 1000_000_000) {
-                    try stderr.deprecatedWriter().print(" MaxRSS:{d}G", .{rss / 1000_000_000});
+                    try stderr.print(" MaxRSS:{d}G", .{rss / 1000_000_000});
                 } else if (rss >= 1000_000) {
-                    try stderr.deprecatedWriter().print(" MaxRSS:{d}M", .{rss / 1000_000});
+                    try stderr.print(" MaxRSS:{d}M", .{rss / 1000_000});
                 } else if (rss >= 1000) {
-                    try stderr.deprecatedWriter().print(" MaxRSS:{d}K", .{rss / 1000});
+                    try stderr.print(" MaxRSS:{d}K", .{rss / 1000});
                 } else {
-                    try stderr.deprecatedWriter().print(" MaxRSS:{d}B", .{rss});
+                    try stderr.print(" MaxRSS:{d}B", .{rss});
                 }
                 try ttyconf.setColor(stderr, .reset);
             }
@@ -866,7 +871,7 @@ fn printStepStatus(
             if (skip == .skipped_oom) {
                 try stderr.writeAll(" (not enough memory)");
                 try ttyconf.setColor(stderr, .dim);
-                try stderr.deprecatedWriter().print(" upper bound of {d} exceeded runner limit ({d})", .{ s.max_rss, run.max_rss });
+                try stderr.print(" upper bound of {d} exceeded runner limit ({d})", .{ s.max_rss, run.max_rss });
                 try ttyconf.setColor(stderr, .yellow);
             }
             try stderr.writeAll("\n");
@@ -878,23 +883,23 @@ fn printStepStatus(
 
 fn printStepFailure(
     s: *Step,
-    stderr: File,
+    stderr: *Writer,
     ttyconf: std.io.tty.Config,
 ) !void {
     if (s.result_error_bundle.errorMessageCount() > 0) {
         try ttyconf.setColor(stderr, .red);
-        try stderr.deprecatedWriter().print(" {d} errors\n", .{
+        try stderr.print(" {d} errors\n", .{
             s.result_error_bundle.errorMessageCount(),
         });
         try ttyconf.setColor(stderr, .reset);
     } else if (!s.test_results.isSuccess()) {
-        try stderr.deprecatedWriter().print(" {d}/{d} passed", .{
+        try stderr.print(" {d}/{d} passed", .{
             s.test_results.passCount(), s.test_results.test_count,
         });
         if (s.test_results.fail_count > 0) {
             try stderr.writeAll(", ");
             try ttyconf.setColor(stderr, .red);
-            try stderr.deprecatedWriter().print("{d} failed", .{
+            try stderr.print("{d} failed", .{
                 s.test_results.fail_count,
             });
             try ttyconf.setColor(stderr, .reset);
@@ -902,7 +907,7 @@ fn printStepFailure(
         if (s.test_results.skip_count > 0) {
             try stderr.writeAll(", ");
             try ttyconf.setColor(stderr, .yellow);
-            try stderr.deprecatedWriter().print("{d} skipped", .{
+            try stderr.print("{d} skipped", .{
                 s.test_results.skip_count,
             });
             try ttyconf.setColor(stderr, .reset);
@@ -910,7 +915,7 @@ fn printStepFailure(
         if (s.test_results.leak_count > 0) {
             try stderr.writeAll(", ");
             try ttyconf.setColor(stderr, .red);
-            try stderr.deprecatedWriter().print("{d} leaked", .{
+            try stderr.print("{d} leaked", .{
                 s.test_results.leak_count,
             });
             try ttyconf.setColor(stderr, .reset);
@@ -932,7 +937,7 @@ fn printTreeStep(
     b: *std.Build,
     s: *Step,
     run: *const Run,
-    stderr: File,
+    stderr: *Writer,
     ttyconf: std.io.tty.Config,
     parent_node: *PrintNode,
     step_stack: *std.AutoArrayHashMapUnmanaged(*Step, void),
@@ -992,7 +997,7 @@ fn printTreeStep(
         if (s.dependencies.items.len == 0) {
             try stderr.writeAll(" (reused)\n");
         } else {
-            try stderr.deprecatedWriter().print(" (+{d} more reused dependencies)\n", .{
+            try stderr.print(" (+{d} more reused dependencies)\n", .{
                 s.dependencies.items.len,
             });
         }
@@ -1129,11 +1134,11 @@ fn workerMakeOneStep(
     const show_stderr = s.result_stderr.len > 0;
 
     if (show_error_msgs or show_compile_errors or show_stderr) {
-        std.debug.lockStdErr();
-        defer std.debug.unlockStdErr();
+        const bw = std.debug.lockStderrWriter(&stdio_buffer_allocation);
+        defer std.debug.unlockStderrWriter();
 
         const gpa = b.allocator;
-        printErrorMessages(gpa, s, .{ .ttyconf = run.ttyconf }, run.stderr, run.prominent_compile_errors) catch {};
+        printErrorMessages(gpa, s, .{ .ttyconf = run.ttyconf }, bw, run.prominent_compile_errors) catch {};
     }
 
     handle_result: {
@@ -1190,7 +1195,7 @@ pub fn printErrorMessages(
     gpa: Allocator,
     failing_step: *Step,
     options: std.zig.ErrorBundle.RenderOptions,
-    stderr: File,
+    stderr: *Writer,
     prominent_compile_errors: bool,
 ) !void {
     // Provide context for where these error messages are coming from by
@@ -1209,7 +1214,7 @@ pub fn printErrorMessages(
     var indent: usize = 0;
     while (step_stack.pop()) |s| : (indent += 1) {
         if (indent > 0) {
-            try stderr.deprecatedWriter().writeByteNTimes(' ', (indent - 1) * 3);
+            try stderr.splatByteAll(' ', (indent - 1) * 3);
             try printChildNodePrefix(stderr, ttyconf);
         }
 
@@ -1231,7 +1236,7 @@ pub fn printErrorMessages(
     }
 
     if (!prominent_compile_errors and failing_step.result_error_bundle.errorMessageCount() > 0) {
-        try failing_step.result_error_bundle.renderToWriter(options, stderr.deprecatedWriter());
+        try failing_step.result_error_bundle.renderToWriter(options, stderr);
     }
 
     for (failing_step.result_error_msgs.items) |msg| {
@@ -1243,27 +1248,27 @@ pub fn printErrorMessages(
     }
 }
 
-fn steps(builder: *std.Build, out_stream: anytype) !void {
+fn printSteps(builder: *std.Build, w: *Writer) !void {
     const allocator = builder.allocator;
     for (builder.top_level_steps.values()) |top_level_step| {
         const name = if (&top_level_step.step == builder.default_step)
             try fmt.allocPrint(allocator, "{s} (default)", .{top_level_step.step.name})
         else
             top_level_step.step.name;
-        try out_stream.print("  {s:<28} {s}\n", .{ name, top_level_step.description });
+        try w.print("  {s:<28} {s}\n", .{ name, top_level_step.description });
     }
 }
 
-fn usage(b: *std.Build, out_stream: anytype) !void {
-    try out_stream.print(
+fn printUsage(b: *std.Build, w: *Writer) !void {
+    try w.print(
         \\Usage: {s} build [steps] [options]
         \\
         \\Steps:
         \\
     , .{b.graph.zig_exe});
-    try steps(b, out_stream);
+    try printSteps(b, w);
 
-    try out_stream.writeAll(
+    try w.writeAll(
         \\
         \\General Options:
         \\  -p, --prefix [path]          Where to install files (default: zig-out)
@@ -1319,25 +1324,25 @@ fn usage(b: *std.Build, out_stream: anytype) !void {
 
     const arena = b.allocator;
     if (b.available_options_list.items.len == 0) {
-        try out_stream.print("  (none)\n", .{});
+        try w.print("  (none)\n", .{});
     } else {
         for (b.available_options_list.items) |option| {
             const name = try fmt.allocPrint(arena, "  -D{s}=[{s}]", .{
                 option.name,
                 @tagName(option.type_id),
             });
-            try out_stream.print("{s:<30} {s}\n", .{ name, option.description });
+            try w.print("{s:<30} {s}\n", .{ name, option.description });
             if (option.enum_options) |enum_options| {
                 const padding = " " ** 33;
-                try out_stream.writeAll(padding ++ "Supported Values:\n");
+                try w.writeAll(padding ++ "Supported Values:\n");
                 for (enum_options) |enum_option| {
-                    try out_stream.print(padding ++ "  {s}\n", .{enum_option});
+                    try w.print(padding ++ "  {s}\n", .{enum_option});
                 }
             }
         }
     }
 
-    try out_stream.writeAll(
+    try w.writeAll(
         \\
         \\System Integration Options:
         \\  --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 {
         \\
     );
     if (b.graph.system_library_options.entries.len == 0) {
-        try out_stream.writeAll("  (none)                                        -\n");
+        try w.writeAll("  (none)                                        -\n");
     } else {
         for (b.graph.system_library_options.keys(), b.graph.system_library_options.values()) |k, v| {
             const status = switch (v) {
@@ -1360,11 +1365,11 @@ fn usage(b: *std.Build, out_stream: anytype) !void {
                 .declared_disabled => "no",
                 .user_enabled, .user_disabled => unreachable, // already emitted error
             };
-            try out_stream.print("    {s:<43} {s}\n", .{ k, status });
+            try w.print("    {s:<43} {s}\n", .{ k, status });
         }
     }
 
-    try out_stream.writeAll(
+    try w.writeAll(
         \\
         \\Advanced Options:
         \\  -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 {
         };
     }
 }
+
+var stdio_buffer_allocation: [256]u8 = undefined;
+var stdout_writer_allocation: std.fs.File.Writer = undefined;
+
+fn initStdoutWriter() *Writer {
+    stdout_writer_allocation = std.fs.File.stdout().writerStreaming(&stdio_buffer_allocation);
+    return &stdout_writer_allocation.interface;
+}
lib/std/Build/Step/Compile.zig
@@ -409,7 +409,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
         .linkage = options.linkage,
         .kind = options.kind,
         .name = name,
-        .step = Step.init(.{
+        .step = .init(.{
             .id = base_id,
             .name = step_name,
             .owner = owner,
@@ -1017,20 +1017,16 @@ fn getGeneratedFilePath(compile: *Compile, comptime tag_name: []const u8, asking
     const maybe_path: ?*GeneratedFile = @field(compile, tag_name);
 
     const generated_file = maybe_path orelse {
-        std.debug.lockStdErr();
-        const stderr: fs.File = .stderr();
-
-        std.Build.dumpBadGetPathHelp(&compile.step, stderr, compile.step.owner, asking_step) catch {};
-
+        const w = std.debug.lockStderrWriter(&.{});
+        std.Build.dumpBadGetPathHelp(&compile.step, w, .detect(.stderr()), compile.step.owner, asking_step) catch {};
+        std.debug.unlockStderrWriter();
         @panic("missing emit option for " ++ tag_name);
     };
 
     const path = generated_file.path orelse {
-        std.debug.lockStdErr();
-        const stderr: fs.File = .stderr();
-
-        std.Build.dumpBadGetPathHelp(&compile.step, stderr, compile.step.owner, asking_step) catch {};
-
+        const w = std.debug.lockStderrWriter(&.{});
+        std.Build.dumpBadGetPathHelp(&compile.step, w, .detect(.stderr()), compile.step.owner, asking_step) catch {};
+        std.debug.unlockStderrWriter();
         @panic(tag_name ++ " is null. Is there a missing step dependency?");
     };
 
@@ -1768,12 +1764,12 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
             for (arg, 0..) |c, arg_idx| {
                 if (c == '\\' or c == '"') {
                     // Slow path for arguments that need to be escaped. We'll need to allocate and copy
-                    var escaped = try ArrayList(u8).initCapacity(arena, arg.len + 1);
-                    const writer = escaped.writer();
-                    try writer.writeAll(arg[0..arg_idx]);
+                    var escaped: std.ArrayListUnmanaged(u8) = .empty;
+                    try escaped.ensureTotalCapacityPrecise(arena, arg.len + 1);
+                    try escaped.appendSlice(arena, arg[0..arg_idx]);
                     for (arg[arg_idx..]) |to_escape| {
-                        if (to_escape == '\\' or to_escape == '"') try writer.writeByte('\\');
-                        try writer.writeByte(to_escape);
+                        if (to_escape == '\\' or to_escape == '"') try escaped.append(arena, '\\');
+                        try escaped.append(arena, to_escape);
                     }
                     escaped_args.appendAssumeCapacity(escaped.items);
                     continue :arg_blk;
@@ -1963,20 +1959,23 @@ fn addFlag(args: *ArrayList([]const u8), comptime name: []const u8, opt: ?bool)
 fn checkCompileErrors(compile: *Compile) !void {
     // Clear this field so that it does not get printed by the build runner.
     const actual_eb = compile.step.result_error_bundle;
-    compile.step.result_error_bundle = std.zig.ErrorBundle.empty;
+    compile.step.result_error_bundle = .empty;
 
     const arena = compile.step.owner.allocator;
 
-    var actual_errors_list = std.ArrayList(u8).init(arena);
-    try actual_eb.renderToWriter(.{
-        .ttyconf = .no_color,
-        .include_reference_trace = false,
-        .include_source_line = false,
-    }, actual_errors_list.writer());
-    const actual_errors = try actual_errors_list.toOwnedSlice();
+    const actual_errors = ae: {
+        var aw: std.io.Writer.Allocating = .init(arena);
+        defer aw.deinit();
+        try actual_eb.renderToWriter(.{
+            .ttyconf = .no_color,
+            .include_reference_trace = false,
+            .include_source_line = false,
+        }, &aw.writer);
+        break :ae try aw.toOwnedSlice();
+    };
 
     // Render the expected lines into a string that we can compare verbatim.
-    var expected_generated = std.ArrayList(u8).init(arena);
+    var expected_generated: std.ArrayListUnmanaged(u8) = .empty;
     const expect_errors = compile.expect_errors.?;
 
     var actual_line_it = mem.splitScalar(u8, actual_errors, '\n');
@@ -2035,17 +2034,17 @@ fn checkCompileErrors(compile: *Compile) !void {
         .exact => |expect_lines| {
             for (expect_lines) |expect_line| {
                 const actual_line = actual_line_it.next() orelse {
-                    try expected_generated.appendSlice(expect_line);
-                    try expected_generated.append('\n');
+                    try expected_generated.appendSlice(arena, expect_line);
+                    try expected_generated.append(arena, '\n');
                     continue;
                 };
                 if (matchCompileError(actual_line, expect_line)) {
-                    try expected_generated.appendSlice(actual_line);
-                    try expected_generated.append('\n');
+                    try expected_generated.appendSlice(arena, actual_line);
+                    try expected_generated.append(arena, '\n');
                     continue;
                 }
-                try expected_generated.appendSlice(expect_line);
-                try expected_generated.append('\n');
+                try expected_generated.appendSlice(arena, expect_line);
+                try expected_generated.append(arena, '\n');
             }
 
             if (mem.eql(u8, expected_generated.items, actual_errors)) return;
lib/std/Build/Fuzz.zig
@@ -112,7 +112,6 @@ fn rebuildTestsWorkerRun(run: *Step.Run, ttyconf: std.io.tty.Config, parent_prog
 
 fn rebuildTestsWorkerRunFallible(run: *Step.Run, ttyconf: std.io.tty.Config, parent_prog_node: std.Progress.Node) !void {
     const gpa = run.step.owner.allocator;
-    const stderr = std.fs.File.stderr();
 
     const compile = run.producer.?;
     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
     const show_stderr = compile.step.result_stderr.len > 0;
 
     if (show_error_msgs or show_compile_errors or show_stderr) {
-        std.debug.lockStdErr();
-        defer std.debug.unlockStdErr();
-        build_runner.printErrorMessages(gpa, &compile.step, .{ .ttyconf = ttyconf }, stderr, false) catch {};
+        var buf: [256]u8 = undefined;
+        const w = std.debug.lockStderrWriter(&buf);
+        defer std.debug.unlockStderrWriter();
+        build_runner.printErrorMessages(gpa, &compile.step, .{ .ttyconf = ttyconf }, w, false) catch {};
     }
 
     const rebuilt_bin_path = result catch |err| switch (err) {
@@ -152,10 +152,10 @@ fn fuzzWorkerRun(
 
     run.rerunInFuzzMode(web_server, unit_test_index, prog_node) catch |err| switch (err) {
         error.MakeFailed => {
-            const stderr = std.fs.File.stderr();
-            std.debug.lockStdErr();
-            defer std.debug.unlockStdErr();
-            build_runner.printErrorMessages(gpa, &run.step, .{ .ttyconf = ttyconf }, stderr, false) catch {};
+            var buf: [256]u8 = undefined;
+            const w = std.debug.lockStderrWriter(&buf);
+            defer std.debug.unlockStderrWriter();
+            build_runner.printErrorMessages(gpa, &run.step, .{ .ttyconf = ttyconf }, w, false) catch {};
             return;
         },
         else => {
lib/std/Build/Step.zig
@@ -286,10 +286,7 @@ pub fn cast(step: *Step, comptime T: type) ?*T {
 }
 
 /// For debugging purposes, prints identifying information about this Step.
-pub fn dump(step: *Step, file: std.fs.File) void {
-    var fw = file.writer(&.{});
-    const w = &fw.interface;
-    const tty_config = std.io.tty.detectConfig(file);
+pub fn dump(step: *Step, w: *std.io.Writer, tty_config: std.io.tty.Config) void {
     const debug_info = std.debug.getSelfDebugInfo() catch |err| {
         w.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{
             @errorName(err),
lib/std/fs/File.zig
@@ -1725,10 +1725,6 @@ pub const Writer = struct {
                         iovecs[len] = .{ .base = splat_buffer.ptr, .len = remaining_splat };
                         len += 1;
                     }
-                    return std.posix.writev(handle, iovecs[0..len]) catch |err| {
-                        w.err = err;
-                        return error.WriteFailed;
-                    };
                 },
                 else => for (0..splat - 1) |_| {
                     if (iovecs.len - len == 0) break;
lib/std/io/tty.zig
@@ -5,36 +5,9 @@ const process = std.process;
 const windows = std.os.windows;
 const native_os = builtin.os.tag;
 
-/// Detect suitable TTY configuration options for the given file (commonly stdout/stderr).
-/// This includes feature checks for ANSI escape codes and the Windows console API, as well as
-/// respecting the `NO_COLOR` and `CLICOLOR_FORCE` environment variables to override the default.
-/// Will attempt to enable ANSI escape code support if necessary/possible.
+/// Deprecated in favor of `Config.detect`.
 pub fn detectConfig(file: File) Config {
-    const force_color: ?bool = if (builtin.os.tag == .wasi)
-        null // wasi does not support environment variables
-    else if (process.hasNonEmptyEnvVarConstant("NO_COLOR"))
-        false
-    else if (process.hasNonEmptyEnvVarConstant("CLICOLOR_FORCE"))
-        true
-    else
-        null;
-
-    if (force_color == false) return .no_color;
-
-    if (file.getOrEnableAnsiEscapeSupport()) return .escape_codes;
-
-    if (native_os == .windows and file.isTty()) {
-        var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
-        if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) == windows.FALSE) {
-            return if (force_color == true) .escape_codes else .no_color;
-        }
-        return .{ .windows_api = .{
-            .handle = file.handle,
-            .reset_attributes = info.wAttributes,
-        } };
-    }
-
-    return if (force_color == true) .escape_codes else .no_color;
+    return .detect(file);
 }
 
 pub const Color = enum {
@@ -66,6 +39,38 @@ pub const Config = union(enum) {
     escape_codes,
     windows_api: if (native_os == .windows) WindowsContext else void,
 
+    /// Detect suitable TTY configuration options for the given file (commonly stdout/stderr).
+    /// This includes feature checks for ANSI escape codes and the Windows console API, as well as
+    /// respecting the `NO_COLOR` and `CLICOLOR_FORCE` environment variables to override the default.
+    /// Will attempt to enable ANSI escape code support if necessary/possible.
+    pub fn detect(file: File) Config {
+        const force_color: ?bool = if (builtin.os.tag == .wasi)
+            null // wasi does not support environment variables
+        else if (process.hasNonEmptyEnvVarConstant("NO_COLOR"))
+            false
+        else if (process.hasNonEmptyEnvVarConstant("CLICOLOR_FORCE"))
+            true
+        else
+            null;
+
+        if (force_color == false) return .no_color;
+
+        if (file.getOrEnableAnsiEscapeSupport()) return .escape_codes;
+
+        if (native_os == .windows and file.isTty()) {
+            var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
+            if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) == windows.FALSE) {
+                return if (force_color == true) .escape_codes else .no_color;
+            }
+            return .{ .windows_api = .{
+                .handle = file.handle,
+                .reset_attributes = info.wAttributes,
+            } };
+        }
+
+        return if (force_color == true) .escape_codes else .no_color;
+    }
+
     pub const WindowsContext = struct {
         handle: File.Handle,
         reset_attributes: u16,
@@ -123,6 +128,7 @@ pub const Config = union(enum) {
                     .dim => windows.FOREGROUND_INTENSITY,
                     .reset => ctx.reset_attributes,
                 };
+                try w.flush();
                 try windows.SetConsoleTextAttribute(ctx.handle, attributes);
             } else {
                 unreachable;
lib/std/zig/ErrorBundle.zig
@@ -11,6 +11,7 @@ const std = @import("std");
 const ErrorBundle = @This();
 const Allocator = std.mem.Allocator;
 const assert = std.debug.assert;
+const Writer = std.io.Writer;
 
 string_bytes: []const u8,
 /// The first thing in this array is an `ErrorMessageList`.
@@ -162,23 +163,23 @@ pub const RenderOptions = struct {
 };
 
 pub fn renderToStdErr(eb: ErrorBundle, options: RenderOptions) void {
-    std.debug.lockStdErr();
-    defer std.debug.unlockStdErr();
-    const stderr: std.fs.File = .stderr();
-    return renderToWriter(eb, options, stderr.deprecatedWriter()) catch return;
+    var buffer: [256]u8 = undefined;
+    const w = std.debug.lockStderrWriter(&buffer);
+    defer std.debug.unlockStderrWriter();
+    renderToWriter(eb, options, w) catch return;
 }
 
-pub fn renderToWriter(eb: ErrorBundle, options: RenderOptions, writer: anytype) anyerror!void {
+pub fn renderToWriter(eb: ErrorBundle, options: RenderOptions, w: *Writer) (Writer.Error || std.posix.UnexpectedError)!void {
     if (eb.extra.len == 0) return;
     for (eb.getMessages()) |err_msg| {
-        try renderErrorMessageToWriter(eb, options, err_msg, writer, "error", .red, 0);
+        try renderErrorMessageToWriter(eb, options, err_msg, w, "error", .red, 0);
     }
 
     if (options.include_log_text) {
         const log_text = eb.getCompileLogOutput();
         if (log_text.len != 0) {
-            try writer.writeAll("\nCompile Log Output:\n");
-            try writer.writeAll(log_text);
+            try w.writeAll("\nCompile Log Output:\n");
+            try w.writeAll(log_text);
         }
     }
 }
@@ -187,74 +188,73 @@ fn renderErrorMessageToWriter(
     eb: ErrorBundle,
     options: RenderOptions,
     err_msg_index: MessageIndex,
-    stderr: anytype,
+    w: *Writer,
     kind: []const u8,
     color: std.io.tty.Color,
     indent: usize,
-) anyerror!void {
+) (Writer.Error || std.posix.UnexpectedError)!void {
     const ttyconf = options.ttyconf;
-    var counting_writer = std.io.countingWriter(stderr);
-    const counting_stderr = counting_writer.writer();
     const err_msg = eb.getErrorMessage(err_msg_index);
+    const prefix_start = w.count;
     if (err_msg.src_loc != .none) {
         const src = eb.extraData(SourceLocation, @intFromEnum(err_msg.src_loc));
-        try counting_stderr.writeByteNTimes(' ', indent);
-        try ttyconf.setColor(stderr, .bold);
-        try counting_stderr.print("{s}:{d}:{d}: ", .{
+        try w.splatByteAll(' ', indent);
+        try ttyconf.setColor(w, .bold);
+        try w.print("{s}:{d}:{d}: ", .{
             eb.nullTerminatedString(src.data.src_path),
             src.data.line + 1,
             src.data.column + 1,
         });
-        try ttyconf.setColor(stderr, color);
-        try counting_stderr.writeAll(kind);
-        try counting_stderr.writeAll(": ");
+        try ttyconf.setColor(w, color);
+        try w.writeAll(kind);
+        try w.writeAll(": ");
         // This is the length of the part before the error message:
         // e.g. "file.zig:4:5: error: "
-        const prefix_len: usize = @intCast(counting_stderr.context.bytes_written);
-        try ttyconf.setColor(stderr, .reset);
-        try ttyconf.setColor(stderr, .bold);
+        const prefix_len = w.count - prefix_start;
+        try ttyconf.setColor(w, .reset);
+        try ttyconf.setColor(w, .bold);
         if (err_msg.count == 1) {
-            try writeMsg(eb, err_msg, stderr, prefix_len);
-            try stderr.writeByte('\n');
+            try writeMsg(eb, err_msg, w, prefix_len);
+            try w.writeByte('\n');
         } else {
-            try writeMsg(eb, err_msg, stderr, prefix_len);
-            try ttyconf.setColor(stderr, .dim);
-            try stderr.print(" ({d} times)\n", .{err_msg.count});
+            try writeMsg(eb, err_msg, w, prefix_len);
+            try ttyconf.setColor(w, .dim);
+            try w.print(" ({d} times)\n", .{err_msg.count});
         }
-        try ttyconf.setColor(stderr, .reset);
+        try ttyconf.setColor(w, .reset);
         if (src.data.source_line != 0 and options.include_source_line) {
             const line = eb.nullTerminatedString(src.data.source_line);
             for (line) |b| switch (b) {
-                '\t' => try stderr.writeByte(' '),
-                else => try stderr.writeByte(b),
+                '\t' => try w.writeByte(' '),
+                else => try w.writeByte(b),
             };
-            try stderr.writeByte('\n');
+            try w.writeByte('\n');
             // TODO basic unicode code point monospace width
             const before_caret = src.data.span_main - src.data.span_start;
             // -1 since span.main includes the caret
             const after_caret = src.data.span_end -| src.data.span_main -| 1;
-            try stderr.writeByteNTimes(' ', src.data.column - before_caret);
-            try ttyconf.setColor(stderr, .green);
-            try stderr.writeByteNTimes('~', before_caret);
-            try stderr.writeByte('^');
-            try stderr.writeByteNTimes('~', after_caret);
-            try stderr.writeByte('\n');
-            try ttyconf.setColor(stderr, .reset);
+            try w.splatByteAll(' ', src.data.column - before_caret);
+            try ttyconf.setColor(w, .green);
+            try w.splatByteAll('~', before_caret);
+            try w.writeByte('^');
+            try w.splatByteAll('~', after_caret);
+            try w.writeByte('\n');
+            try ttyconf.setColor(w, .reset);
         }
         for (eb.getNotes(err_msg_index)) |note| {
-            try renderErrorMessageToWriter(eb, options, note, stderr, "note", .cyan, indent);
+            try renderErrorMessageToWriter(eb, options, note, w, "note", .cyan, indent);
         }
         if (src.data.reference_trace_len > 0 and options.include_reference_trace) {
-            try ttyconf.setColor(stderr, .reset);
-            try ttyconf.setColor(stderr, .dim);
-            try stderr.print("referenced by:\n", .{});
+            try ttyconf.setColor(w, .reset);
+            try ttyconf.setColor(w, .dim);
+            try w.print("referenced by:\n", .{});
             var ref_index = src.end;
             for (0..src.data.reference_trace_len) |_| {
                 const ref_trace = eb.extraData(ReferenceTrace, ref_index);
                 ref_index = ref_trace.end;
                 if (ref_trace.data.src_loc != .none) {
                     const ref_src = eb.getSourceLocation(ref_trace.data.src_loc);
-                    try stderr.print("    {s}: {s}:{d}:{d}\n", .{
+                    try w.print("    {s}: {s}:{d}:{d}\n", .{
                         eb.nullTerminatedString(ref_trace.data.decl_name),
                         eb.nullTerminatedString(ref_src.src_path),
                         ref_src.line + 1,
@@ -262,36 +262,36 @@ fn renderErrorMessageToWriter(
                     });
                 } else if (ref_trace.data.decl_name != 0) {
                     const count = ref_trace.data.decl_name;
-                    try stderr.print(
+                    try w.print(
                         "    {d} reference(s) hidden; use '-freference-trace={d}' to see all references\n",
                         .{ count, count + src.data.reference_trace_len - 1 },
                     );
                 } else {
-                    try stderr.print(
+                    try w.print(
                         "    remaining reference traces hidden; use '-freference-trace' to see all reference traces\n",
                         .{},
                     );
                 }
             }
-            try ttyconf.setColor(stderr, .reset);
+            try ttyconf.setColor(w, .reset);
         }
     } else {
-        try ttyconf.setColor(stderr, color);
-        try stderr.writeByteNTimes(' ', indent);
-        try stderr.writeAll(kind);
-        try stderr.writeAll(": ");
-        try ttyconf.setColor(stderr, .reset);
+        try ttyconf.setColor(w, color);
+        try w.splatByteAll(' ', indent);
+        try w.writeAll(kind);
+        try w.writeAll(": ");
+        try ttyconf.setColor(w, .reset);
         const msg = eb.nullTerminatedString(err_msg.msg);
         if (err_msg.count == 1) {
-            try stderr.print("{s}\n", .{msg});
+            try w.print("{s}\n", .{msg});
         } else {
-            try stderr.print("{s}", .{msg});
-            try ttyconf.setColor(stderr, .dim);
-            try stderr.print(" ({d} times)\n", .{err_msg.count});
+            try w.print("{s}", .{msg});
+            try ttyconf.setColor(w, .dim);
+            try w.print(" ({d} times)\n", .{err_msg.count});
         }
-        try ttyconf.setColor(stderr, .reset);
+        try ttyconf.setColor(w, .reset);
         for (eb.getNotes(err_msg_index)) |note| {
-            try renderErrorMessageToWriter(eb, options, note, stderr, "note", .cyan, indent + 4);
+            try renderErrorMessageToWriter(eb, options, note, w, "note", .cyan, indent + 4);
         }
     }
 }
@@ -300,13 +300,13 @@ fn renderErrorMessageToWriter(
 /// to allow for long, good-looking error messages.
 ///
 /// This is used to split the message in `@compileError("hello\nworld")` for example.
-fn writeMsg(eb: ErrorBundle, err_msg: ErrorMessage, stderr: anytype, indent: usize) !void {
+fn writeMsg(eb: ErrorBundle, err_msg: ErrorMessage, w: *Writer, indent: usize) !void {
     var lines = std.mem.splitScalar(u8, eb.nullTerminatedString(err_msg.msg), '\n');
     while (lines.next()) |line| {
-        try stderr.writeAll(line);
+        try w.writeAll(line);
         if (lines.index == null) break;
-        try stderr.writeByte('\n');
-        try stderr.writeByteNTimes(' ', indent);
+        try w.writeByte('\n');
+        try w.splatByteAll(' ', indent);
     }
 }
 
@@ -398,7 +398,7 @@ pub const Wip = struct {
     pub fn printString(wip: *Wip, comptime fmt: []const u8, args: anytype) Allocator.Error!String {
         const gpa = wip.gpa;
         const index: String = @intCast(wip.string_bytes.items.len);
-        try wip.string_bytes.writer(gpa).print(fmt, args);
+        try wip.string_bytes.print(gpa, fmt, args);
         try wip.string_bytes.append(gpa, 0);
         return index;
     }
@@ -788,9 +788,10 @@ pub const Wip = struct {
 
         const ttyconf: std.io.tty.Config = .no_color;
 
-        var bundle_buf = std.ArrayList(u8).init(std.testing.allocator);
+        var bundle_buf: std.io.Writer.Allocating = .init(std.testing.allocator);
+        const bundle_bw = &bundle_buf.interface;
         defer bundle_buf.deinit();
-        try bundle.renderToWriter(.{ .ttyconf = ttyconf }, bundle_buf.writer());
+        try bundle.renderToWriter(.{ .ttyconf = ttyconf }, bundle_bw);
 
         var copy = copy: {
             var wip: ErrorBundle.Wip = undefined;
@@ -803,10 +804,11 @@ pub const Wip = struct {
         };
         defer copy.deinit(std.testing.allocator);
 
-        var copy_buf = std.ArrayList(u8).init(std.testing.allocator);
+        var copy_buf: std.io.Writer.Allocating = .init(std.testing.allocator);
+        const copy_bw = &copy_buf.interface;
         defer copy_buf.deinit();
-        try copy.renderToWriter(.{ .ttyconf = ttyconf }, copy_buf.writer());
+        try copy.renderToWriter(.{ .ttyconf = ttyconf }, copy_bw);
 
-        try std.testing.expectEqualStrings(bundle_buf.items, copy_buf.items);
+        try std.testing.expectEqualStrings(bundle_bw.getWritten(), copy_bw.getWritten());
     }
 };
lib/std/Build.zig
@@ -284,7 +284,7 @@ pub fn create(
         .h_dir = undefined,
         .dest_dir = graph.env_map.get("DESTDIR"),
         .install_tls = .{
-            .step = Step.init(.{
+            .step = .init(.{
                 .id = TopLevelStep.base_id,
                 .name = "install",
                 .owner = b,
@@ -292,7 +292,7 @@ pub fn create(
             .description = "Copy build artifacts to prefix path",
         },
         .uninstall_tls = .{
-            .step = Step.init(.{
+            .step = .init(.{
                 .id = TopLevelStep.base_id,
                 .name = "uninstall",
                 .owner = b,
@@ -342,7 +342,7 @@ fn createChildOnly(
         .graph = parent.graph,
         .allocator = allocator,
         .install_tls = .{
-            .step = Step.init(.{
+            .step = .init(.{
                 .id = TopLevelStep.base_id,
                 .name = "install",
                 .owner = child,
@@ -350,7 +350,7 @@ fn createChildOnly(
             .description = "Copy build artifacts to prefix path",
         },
         .uninstall_tls = .{
-            .step = Step.init(.{
+            .step = .init(.{
                 .id = TopLevelStep.base_id,
                 .name = "uninstall",
                 .owner = child,
@@ -1525,7 +1525,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
 pub fn step(b: *Build, name: []const u8, description: []const u8) *Step {
     const step_info = b.allocator.create(TopLevelStep) catch @panic("OOM");
     step_info.* = .{
-        .step = Step.init(.{
+        .step = .init(.{
             .id = TopLevelStep.base_id,
             .name = name,
             .owner = b,
@@ -1824,13 +1824,13 @@ pub fn validateUserInputDidItFail(b: *Build) bool {
     return b.invalid_user_input;
 }
 
-fn allocPrintCmd(ally: Allocator, opt_cwd: ?[]const u8, argv: []const []const u8) error{OutOfMemory}![]u8 {
-    var buf = ArrayList(u8).init(ally);
-    if (opt_cwd) |cwd| try buf.writer().print("cd {s} && ", .{cwd});
+fn allocPrintCmd(gpa: Allocator, opt_cwd: ?[]const u8, argv: []const []const u8) error{OutOfMemory}![]u8 {
+    var buf: std.ArrayListUnmanaged(u8) = .empty;
+    if (opt_cwd) |cwd| try buf.print(gpa, "cd {s} && ", .{cwd});
     for (argv) |arg| {
-        try buf.writer().print("{s} ", .{arg});
+        try buf.print(gpa, "{s} ", .{arg});
     }
-    return buf.toOwnedSlice();
+    return buf.toOwnedSlice(gpa);
 }
 
 fn printCmd(ally: Allocator, cwd: ?[]const u8, argv: []const []const u8) void {
@@ -2466,10 +2466,9 @@ pub const GeneratedFile = struct {
 
     pub fn getPath2(gen: GeneratedFile, src_builder: *Build, asking_step: ?*Step) []const u8 {
         return gen.path orelse {
-            std.debug.lockStdErr();
-            const stderr = std.fs.File.stderr();
-            dumpBadGetPathHelp(gen.step, stderr, src_builder, asking_step) catch {};
-            std.debug.unlockStdErr();
+            const w = debug.lockStderrWriter(&.{});
+            dumpBadGetPathHelp(gen.step, w, .detect(.stderr()), src_builder, asking_step) catch {};
+            debug.unlockStderrWriter();
             @panic("misconfigured build script");
         };
     }
@@ -2676,10 +2675,9 @@ pub const LazyPath = union(enum) {
                 var file_path: Cache.Path = .{
                     .root_dir = Cache.Directory.cwd(),
                     .sub_path = gen.file.path orelse {
-                        std.debug.lockStdErr();
-                        const stderr: fs.File = .stderr();
-                        dumpBadGetPathHelp(gen.file.step, stderr, src_builder, asking_step) catch {};
-                        std.debug.unlockStdErr();
+                        const w = debug.lockStderrWriter(&.{});
+                        dumpBadGetPathHelp(gen.file.step, w, .detect(.stderr()), src_builder, asking_step) catch {};
+                        debug.unlockStderrWriter();
                         @panic("misconfigured build script");
                     },
                 };
@@ -2766,44 +2764,42 @@ fn dumpBadDirnameHelp(
     comptime msg: []const u8,
     args: anytype,
 ) anyerror!void {
-    debug.lockStdErr();
-    defer debug.unlockStdErr();
+    const w = debug.lockStderrWriter(&.{});
+    defer debug.unlockStderrWriter();
 
-    const stderr: fs.File = .stderr();
-    const w = stderr.deprecatedWriter();
     try w.print(msg, args);
 
-    const tty_config = std.io.tty.detectConfig(stderr);
+    const tty_config = std.io.tty.detectConfig(.stderr());
 
     if (fail_step) |s| {
         tty_config.setColor(w, .red) catch {};
-        try stderr.writeAll("    The step was created by this stack trace:\n");
+        try w.writeAll("    The step was created by this stack trace:\n");
         tty_config.setColor(w, .reset) catch {};
 
-        s.dump(stderr);
+        s.dump(w, tty_config);
     }
 
     if (asking_step) |as| {
         tty_config.setColor(w, .red) catch {};
-        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});
+        try w.print("    The step '{s}' that is missing a dependency on the above step was created by this stack trace:\n", .{as.name});
         tty_config.setColor(w, .reset) catch {};
 
-        as.dump(stderr);
+        as.dump(w, tty_config);
     }
 
     tty_config.setColor(w, .red) catch {};
-    try stderr.writeAll("    Hope that helps. Proceeding to panic.\n");
+    try w.writeAll("    Hope that helps. Proceeding to panic.\n");
     tty_config.setColor(w, .reset) catch {};
 }
 
 /// In this function the stderr mutex has already been locked.
 pub fn dumpBadGetPathHelp(
     s: *Step,
-    stderr: fs.File,
+    w: *std.io.Writer,
+    tty_config: std.io.tty.Config,
     src_builder: *Build,
     asking_step: ?*Step,
 ) anyerror!void {
-    const w = stderr.deprecatedWriter();
     try w.print(
         \\getPath() was called on a GeneratedFile that wasn't built yet.
         \\  source package path: {s}
@@ -2814,21 +2810,20 @@ pub fn dumpBadGetPathHelp(
         s.name,
     });
 
-    const tty_config = std.io.tty.detectConfig(stderr);
     tty_config.setColor(w, .red) catch {};
-    try stderr.writeAll("    The step was created by this stack trace:\n");
+    try w.writeAll("    The step was created by this stack trace:\n");
     tty_config.setColor(w, .reset) catch {};
 
-    s.dump(stderr);
+    s.dump(w, tty_config);
     if (asking_step) |as| {
         tty_config.setColor(w, .red) catch {};
-        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});
+        try w.print("    The step '{s}' that is missing a dependency on the above step was created by this stack trace:\n", .{as.name});
         tty_config.setColor(w, .reset) catch {};
 
-        as.dump(stderr);
+        as.dump(w, tty_config);
     }
     tty_config.setColor(w, .red) catch {};
-    try stderr.writeAll("    Hope that helps. Proceeding to panic.\n");
+    try w.writeAll("    Hope that helps. Proceeding to panic.\n");
     tty_config.setColor(w, .reset) catch {};
 }
 
@@ -2866,11 +2861,6 @@ pub fn makeTempPath(b: *Build) []const u8 {
     return result_path;
 }
 
-/// Deprecated; use `std.fmt.hex` instead.
-pub fn hex64(x: u64) [16]u8 {
-    return std.fmt.hex(x);
-}
-
 /// A pair of target query and fully resolved target.
 /// This type is generally required by build system API that need to be given a
 /// target. The query is kept because the Zig toolchain needs to know which parts