diff --git a/lib/compiler/build_runner.zig b/lib/compiler/build_runner.zig index c3ecfa14b6..3e1ce6e2e1 100644 --- a/lib/compiler/build_runner.zig +++ b/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) { + var w = initStdoutWriter(); + printUsage(builder, w) catch return stdout_writer_allocation.err.?; + w.flush() catch return stdout_writer_allocation.err.?; + return; + } - if (help_menu) - return usage(builder, stdout_writer); - - 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 {}; + 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) 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 {}; + 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; +} diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 12097bf404..d84b99d02d 100644 --- a/lib/std/Build.zig +++ b/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 diff --git a/lib/std/Build/Fuzz.zig b/lib/std/Build/Fuzz.zig index f4bfe840a2..28f8781dd1 100644 --- a/lib/std/Build/Fuzz.zig +++ b/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 => { diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 77e259795b..5192249f12 100644 --- a/lib/std/Build/Step.zig +++ b/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), diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig index 5b73bd2c76..356ea4e34e 100644 --- a/lib/std/Build/Step/Compile.zig +++ b/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; diff --git a/lib/std/fs/File.zig b/lib/std/fs/File.zig index ebc6e30164..fc30e3af06 100644 --- a/lib/std/fs/File.zig +++ b/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; diff --git a/lib/std/io/tty.zig b/lib/std/io/tty.zig index 1e9cd0d9bc..fa17d9a16d 100644 --- a/lib/std/io/tty.zig +++ b/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; diff --git a/lib/std/zig/ErrorBundle.zig b/lib/std/zig/ErrorBundle.zig index 48a4e9966a..22b3a7b460 100644 --- a/lib/std/zig/ErrorBundle.zig +++ b/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 = ©_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()); } };