diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2025-12-10 20:55:11 -0800 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2025-12-23 22:15:09 -0800 |
| commit | b042e935228db5d46271d4d3d17afeb9ba5d7ce3 (patch) | |
| tree | fd02941ee06e6abed96f0f1193a27ce1dfe41cc3 | |
| parent | 7837d975dcaa17ff22f536607c4ff6db7b697b04 (diff) | |
| download | zig-b042e935228db5d46271d4d3d17afeb9ba5d7ce3.tar.gz zig-b042e935228db5d46271d4d3d17afeb9ba5d7ce3.zip | |
std: update tty config references in the build system
| -rw-r--r-- | lib/compiler/build_runner.zig | 208 | ||||
| -rw-r--r-- | lib/std/Build.zig | 60 | ||||
| -rw-r--r-- | lib/std/Build/Fuzz.zig | 20 | ||||
| -rw-r--r-- | lib/std/Build/Step.zig | 9 | ||||
| -rw-r--r-- | lib/std/Build/Step/Compile.zig | 10 | ||||
| -rw-r--r-- | lib/std/Build/Step/Run.zig | 68 | ||||
| -rw-r--r-- | lib/std/Build/WebServer.zig | 4 | ||||
| -rw-r--r-- | lib/std/Io/File/Writer.zig | 41 | ||||
| -rw-r--r-- | lib/std/debug.zig | 2 | ||||
| -rw-r--r-- | lib/std/log.zig | 30 | ||||
| -rw-r--r-- | lib/std/testing.zig | 42 | ||||
| -rw-r--r-- | lib/std/zig.zig | 12 | ||||
| -rw-r--r-- | lib/std/zig/ErrorBundle.zig | 59 |
13 files changed, 271 insertions, 294 deletions
diff --git a/lib/compiler/build_runner.zig b/lib/compiler/build_runner.zig index 05cd21ecdb..acba1a4621 100644 --- a/lib/compiler/build_runner.zig +++ b/lib/compiler/build_runner.zig @@ -14,7 +14,6 @@ const WebServer = std.Build.WebServer; const Allocator = std.mem.Allocator; const fatal = std.process.fatal; const Writer = std.Io.Writer; -const tty = std.Io.tty; pub const root = @import("@build"); pub const dependencies = @import("@dependencies"); @@ -435,9 +434,7 @@ pub fn main() !void { if (builtin.single_threaded) fatal("'--webui' is not yet supported on single-threaded hosts", .{}); } - const ttyconf = color.detectTtyConf(io); - - const main_progress_node = std.Progress.start(.{ + const main_progress_node = std.Progress.start(io, .{ .disable_printing = (color == .off), }); defer main_progress_node.end(); @@ -509,8 +506,6 @@ pub fn main() !void { .error_style = error_style, .multiline_errors = multiline_errors, .summary = summary orelse if (watch or webui_listen != null) .line else .failures, - - .ttyconf = ttyconf, }; defer { run.memory_blocked_steps.deinit(gpa); @@ -524,10 +519,10 @@ pub fn main() !void { prepare(arena, builder, targets.items, &run, graph.random_seed) catch |err| switch (err) { error.DependencyLoopDetected => { - // Perhaps in the future there could be an Advanced Options flag such as - // --debug-build-runner-leaks which would make this code return instead of - // calling exit. - std.debug.lockStdErr(); + // Perhaps in the future there could be an Advanced Options flag + // such as --debug-build-runner-leaks which would make this code + // return instead of calling exit. + _ = std.debug.lockStderrWriter(&.{}); process.exit(1); }, else => |e| return e, @@ -545,7 +540,6 @@ pub fn main() !void { if (builtin.single_threaded) unreachable; // `fatal` above break :ws .init(.{ .gpa = gpa, - .ttyconf = ttyconf, .graph = &graph, .all_steps = run.step_stack.keys(), .root_prog_node = main_progress_node, @@ -560,9 +554,9 @@ pub fn main() !void { } rebuild: while (true) : (if (run.error_style.clearOnUpdate()) { - const bw, _ = std.debug.lockStderrWriter(&stdio_buffer_allocation); + const stderr = std.debug.lockStderrWriter(&stdio_buffer_allocation); defer std.debug.unlockStderrWriter(); - try bw.writeAll("\x1B[2J\x1B[3J\x1B[H"); + try stderr.writeAllUnescaped("\x1B[2J\x1B[3J\x1B[H"); }) { if (run.web_server) |*ws| ws.startBuild(); @@ -663,9 +657,6 @@ const Run = struct { memory_blocked_steps: std.ArrayList(*Step), /// Allocated into `gpa`. step_stack: std.AutoArrayHashMapUnmanaged(*Step, void), - /// Similar to the `tty.Config` returned by `std.debug.lockStderrWriter`, - /// but also respects the '--color' flag. - ttyconf: tty.Config, claimed_rss: usize, error_style: ErrorStyle, @@ -839,7 +830,6 @@ fn runStepNames( var f = std.Build.Fuzz.init( gpa, io, - run.ttyconf, step_stack.keys(), parent_prog_node, mode, @@ -866,18 +856,20 @@ fn runStepNames( .none => break :summary, } - const w, _ = std.debug.lockStderrWriter(&stdio_buffer_allocation); + const stderr = std.debug.lockStderrWriter(&stdio_buffer_allocation); defer std.debug.unlockStderrWriter(); - const ttyconf = run.ttyconf; + + const w = &stderr.interface; + const fwm = stderr.mode; const total_count = success_count + failure_count + pending_count + skipped_count; - ttyconf.setColor(w, .cyan) catch {}; - ttyconf.setColor(w, .bold) catch {}; + fwm.setColor(w, .cyan) catch {}; + fwm.setColor(w, .bold) catch {}; w.writeAll("Build Summary: ") catch {}; - ttyconf.setColor(w, .reset) catch {}; + fwm.setColor(w, .reset) catch {}; w.print("{d}/{d} steps succeeded", .{ success_count, total_count }) catch {}; { - ttyconf.setColor(w, .dim) catch {}; + fwm.setColor(w, .dim) catch {}; var first = true; if (skipped_count > 0) { w.print("{s}{d} skipped", .{ if (first) " (" else ", ", skipped_count }) catch {}; @@ -888,12 +880,12 @@ fn runStepNames( first = false; } if (!first) w.writeByte(')') catch {}; - ttyconf.setColor(w, .reset) catch {}; + fwm.setColor(w, .reset) catch {}; } if (test_count > 0) { w.print("; {d}/{d} tests passed", .{ test_pass_count, test_count }) catch {}; - ttyconf.setColor(w, .dim) catch {}; + fwm.setColor(w, .dim) catch {}; var first = true; if (test_skip_count > 0) { w.print("{s}{d} skipped", .{ if (first) " (" else ", ", test_skip_count }) catch {}; @@ -912,7 +904,7 @@ fn runStepNames( first = false; } if (!first) w.writeByte(')') catch {}; - ttyconf.setColor(w, .reset) catch {}; + fwm.setColor(w, .reset) catch {}; } w.writeAll("\n") catch {}; @@ -926,7 +918,7 @@ fn runStepNames( var print_node: PrintNode = .{ .parent = null }; if (step_names.len == 0) { print_node.last = true; - printTreeStep(b, b.default_step, run, w, ttyconf, &print_node, &step_stack_copy) catch {}; + printTreeStep(b, b.default_step, run, w, fwm, &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; @@ -945,7 +937,7 @@ 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, w, ttyconf, &print_node, &step_stack_copy) catch {}; + printTreeStep(b, &tls.step, run, w, fwm, &print_node, &step_stack_copy) catch {}; } } w.writeByte('\n') catch {}; @@ -962,7 +954,7 @@ fn runStepNames( if (run.error_style.verboseContext()) break :code 1; // failure; print build command break :code 2; // failure; do not print build command }; - std.debug.lockStdErr(); + _ = std.debug.lockStderrWriter(&.{}); process.exit(code); } @@ -971,31 +963,31 @@ const PrintNode = struct { last: bool = false, }; -fn printPrefix(node: *PrintNode, stderr: *Writer, ttyconf: tty.Config) !void { +fn printPrefix(node: *PrintNode, w: *Writer, fwm: File.Writer.Mode) !void { const parent = node.parent orelse return; if (parent.parent == null) return; - try printPrefix(parent, stderr, ttyconf); + try printPrefix(parent, w, fwm); if (parent.last) { - try stderr.writeAll(" "); + try w.writeAll(" "); } else { - try stderr.writeAll(switch (ttyconf) { - .no_color, .windows_api => "| ", - .escape_codes => "\x1B\x28\x30\x78\x1B\x28\x42 ", // │ + try w.writeAll(switch (fwm) { + .terminal_escaped => "\x1B\x28\x30\x78\x1B\x28\x42 ", // │ + else => "| ", }); } } -fn printChildNodePrefix(stderr: *Writer, ttyconf: tty.Config) !void { - try stderr.writeAll(switch (ttyconf) { - .no_color, .windows_api => "+- ", - .escape_codes => "\x1B\x28\x30\x6d\x71\x1B\x28\x42 ", // └─ +fn printChildNodePrefix(w: *Writer, fwm: File.Writer.Mode) !void { + try w.writeAll(switch (fwm) { + .terminal_escaped => "\x1B\x28\x30\x6d\x71\x1B\x28\x42 ", // └─ + else => "+- ", }); } fn printStepStatus( s: *Step, stderr: *Writer, - ttyconf: tty.Config, + fwm: File.Writer.Mode, run: *const Run, ) !void { switch (s.state) { @@ -1005,13 +997,13 @@ fn printStepStatus( .running => unreachable, .dependency_failure => { - try ttyconf.setColor(stderr, .dim); + try fwm.setColor(stderr, .dim); try stderr.writeAll(" transitive failure\n"); - try ttyconf.setColor(stderr, .reset); + try fwm.setColor(stderr, .reset); }, .success => { - try ttyconf.setColor(stderr, .green); + try fwm.setColor(stderr, .green); if (s.result_cached) { try stderr.writeAll(" cached"); } else if (s.test_results.test_count > 0) { @@ -1019,19 +1011,19 @@ fn printStepStatus( assert(s.test_results.test_count == pass_count + s.test_results.skip_count); try stderr.print(" {d} pass", .{pass_count}); if (s.test_results.skip_count > 0) { - try ttyconf.setColor(stderr, .reset); + try fwm.setColor(stderr, .reset); try stderr.writeAll(", "); - try ttyconf.setColor(stderr, .yellow); + try fwm.setColor(stderr, .yellow); try stderr.print("{d} skip", .{s.test_results.skip_count}); } - try ttyconf.setColor(stderr, .reset); + try fwm.setColor(stderr, .reset); try stderr.print(" ({d} total)", .{s.test_results.test_count}); } else { try stderr.writeAll(" success"); } - try ttyconf.setColor(stderr, .reset); + try fwm.setColor(stderr, .reset); if (s.result_duration_ns) |ns| { - try ttyconf.setColor(stderr, .dim); + try fwm.setColor(stderr, .dim); if (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) { @@ -1043,11 +1035,11 @@ fn printStepStatus( } else { try stderr.print(" {d}ns", .{ns}); } - try ttyconf.setColor(stderr, .reset); + try fwm.setColor(stderr, .reset); } if (s.result_peak_rss != 0) { const rss = s.result_peak_rss; - try ttyconf.setColor(stderr, .dim); + try fwm.setColor(stderr, .dim); if (rss >= 1000_000_000) { try stderr.print(" MaxRSS:{d}G", .{rss / 1000_000_000}); } else if (rss >= 1000_000) { @@ -1057,25 +1049,25 @@ fn printStepStatus( } else { try stderr.print(" MaxRSS:{d}B", .{rss}); } - try ttyconf.setColor(stderr, .reset); + try fwm.setColor(stderr, .reset); } try stderr.writeAll("\n"); }, .skipped, .skipped_oom => |skip| { - try ttyconf.setColor(stderr, .yellow); + try fwm.setColor(stderr, .yellow); try stderr.writeAll(" skipped"); if (skip == .skipped_oom) { try stderr.writeAll(" (not enough memory)"); - try ttyconf.setColor(stderr, .dim); + try fwm.setColor(stderr, .dim); try stderr.print(" upper bound of {d} exceeded runner limit ({d})", .{ s.max_rss, run.max_rss }); - try ttyconf.setColor(stderr, .yellow); + try fwm.setColor(stderr, .yellow); } try stderr.writeAll("\n"); - try ttyconf.setColor(stderr, .reset); + try fwm.setColor(stderr, .reset); }, .failure => { - try printStepFailure(s, stderr, ttyconf, false); - try ttyconf.setColor(stderr, .reset); + try printStepFailure(s, stderr, fwm, false); + try fwm.setColor(stderr, .reset); }, } } @@ -1083,48 +1075,48 @@ fn printStepStatus( fn printStepFailure( s: *Step, stderr: *Writer, - ttyconf: tty.Config, + fwm: File.Writer.Mode, dim: bool, ) !void { if (s.result_error_bundle.errorMessageCount() > 0) { - try ttyconf.setColor(stderr, .red); + try fwm.setColor(stderr, .red); try stderr.print(" {d} errors\n", .{ s.result_error_bundle.errorMessageCount(), }); } else if (!s.test_results.isSuccess()) { // These first values include all of the test "statuses". Every test is either passsed, // skipped, failed, crashed, or timed out. - try ttyconf.setColor(stderr, .green); + try fwm.setColor(stderr, .green); try stderr.print(" {d} pass", .{s.test_results.passCount()}); - try ttyconf.setColor(stderr, .reset); - if (dim) try ttyconf.setColor(stderr, .dim); + try fwm.setColor(stderr, .reset); + if (dim) try fwm.setColor(stderr, .dim); if (s.test_results.skip_count > 0) { try stderr.writeAll(", "); - try ttyconf.setColor(stderr, .yellow); + try fwm.setColor(stderr, .yellow); try stderr.print("{d} skip", .{s.test_results.skip_count}); - try ttyconf.setColor(stderr, .reset); - if (dim) try ttyconf.setColor(stderr, .dim); + try fwm.setColor(stderr, .reset); + if (dim) try fwm.setColor(stderr, .dim); } if (s.test_results.fail_count > 0) { try stderr.writeAll(", "); - try ttyconf.setColor(stderr, .red); + try fwm.setColor(stderr, .red); try stderr.print("{d} fail", .{s.test_results.fail_count}); - try ttyconf.setColor(stderr, .reset); - if (dim) try ttyconf.setColor(stderr, .dim); + try fwm.setColor(stderr, .reset); + if (dim) try fwm.setColor(stderr, .dim); } if (s.test_results.crash_count > 0) { try stderr.writeAll(", "); - try ttyconf.setColor(stderr, .red); + try fwm.setColor(stderr, .red); try stderr.print("{d} crash", .{s.test_results.crash_count}); - try ttyconf.setColor(stderr, .reset); - if (dim) try ttyconf.setColor(stderr, .dim); + try fwm.setColor(stderr, .reset); + if (dim) try fwm.setColor(stderr, .dim); } if (s.test_results.timeout_count > 0) { try stderr.writeAll(", "); - try ttyconf.setColor(stderr, .red); + try fwm.setColor(stderr, .red); try stderr.print("{d} timeout", .{s.test_results.timeout_count}); - try ttyconf.setColor(stderr, .reset); - if (dim) try ttyconf.setColor(stderr, .dim); + try fwm.setColor(stderr, .reset); + if (dim) try fwm.setColor(stderr, .dim); } try stderr.print(" ({d} total)", .{s.test_results.test_count}); @@ -1134,10 +1126,10 @@ fn printStepFailure( // 2 pass, 1 skip, 2 fail (5 total); 2 leaks if (s.test_results.leak_count > 0) { try stderr.writeAll("; "); - try ttyconf.setColor(stderr, .red); + try fwm.setColor(stderr, .red); try stderr.print("{d} leaks", .{s.test_results.leak_count}); - try ttyconf.setColor(stderr, .reset); - if (dim) try ttyconf.setColor(stderr, .dim); + try fwm.setColor(stderr, .reset); + if (dim) try fwm.setColor(stderr, .dim); } // It's usually not helpful to know how many error logs there were because they tend to @@ -1151,19 +1143,19 @@ fn printStepFailure( }; if (show_err_logs) { try stderr.writeAll("; "); - try ttyconf.setColor(stderr, .red); + try fwm.setColor(stderr, .red); try stderr.print("{d} error logs", .{s.test_results.log_err_count}); - try ttyconf.setColor(stderr, .reset); - if (dim) try ttyconf.setColor(stderr, .dim); + try fwm.setColor(stderr, .reset); + if (dim) try fwm.setColor(stderr, .dim); } try stderr.writeAll("\n"); } else if (s.result_error_msgs.items.len > 0) { - try ttyconf.setColor(stderr, .red); + try fwm.setColor(stderr, .red); try stderr.writeAll(" failure\n"); } else { assert(s.result_stderr.len > 0); - try ttyconf.setColor(stderr, .red); + try fwm.setColor(stderr, .red); try stderr.writeAll(" stderr\n"); } } @@ -1173,7 +1165,7 @@ fn printTreeStep( s: *Step, run: *const Run, stderr: *Writer, - ttyconf: tty.Config, + fwm: File.Writer.Mode, parent_node: *PrintNode, step_stack: *std.AutoArrayHashMapUnmanaged(*Step, void), ) !void { @@ -1186,26 +1178,26 @@ fn printTreeStep( .failures => s.state == .success, }; if (skip) return; - try printPrefix(parent_node, stderr, ttyconf); + try printPrefix(parent_node, stderr, fwm); if (parent_node.parent != null) { if (parent_node.last) { - try printChildNodePrefix(stderr, ttyconf); + try printChildNodePrefix(stderr, fwm); } else { - try stderr.writeAll(switch (ttyconf) { - .no_color, .windows_api => "+- ", - .escape_codes => "\x1B\x28\x30\x74\x71\x1B\x28\x42 ", // ├─ + try stderr.writeAll(switch (fwm) { + .terminal_escaped => "\x1B\x28\x30\x74\x71\x1B\x28\x42 ", // ├─ + else => "+- ", }); } } - if (!first) try ttyconf.setColor(stderr, .dim); + if (!first) try fwm.setColor(stderr, .dim); // dep_prefix omitted here because it is redundant with the tree. try stderr.writeAll(s.name); if (first) { - try printStepStatus(s, stderr, ttyconf, run); + try printStepStatus(s, stderr, fwm, run); const last_index = if (summary == .all) s.dependencies.items.len -| 1 else blk: { var i: usize = s.dependencies.items.len; @@ -1227,7 +1219,7 @@ fn printTreeStep( .parent = parent_node, .last = i == last_index, }; - try printTreeStep(b, dep, run, stderr, ttyconf, &print_node, step_stack); + try printTreeStep(b, dep, run, stderr, fwm, &print_node, step_stack); } } else { if (s.dependencies.items.len == 0) { @@ -1237,7 +1229,7 @@ fn printTreeStep( s.dependencies.items.len, }); } - try ttyconf.setColor(stderr, .reset); + try fwm.setColor(stderr, .reset); } } @@ -1368,7 +1360,6 @@ fn workerMakeOneStep( .progress_node = sub_prog_node, .watch = run.watch, .web_server = if (run.web_server) |*ws| ws else null, - .ttyconf = run.ttyconf, .unit_test_timeout_ns = run.unit_test_timeout_ns, .gpa = gpa, }); @@ -1378,10 +1369,9 @@ fn workerMakeOneStep( const show_error_msgs = s.result_error_msgs.items.len > 0; const show_stderr = s.result_stderr.len > 0; if (show_error_msgs or show_compile_errors or show_stderr) { - const bw, _ = std.debug.lockStderrWriter(&stdio_buffer_allocation); + const stderr = std.debug.lockStderrWriter(&stdio_buffer_allocation); defer std.debug.unlockStderrWriter(); - const ttyconf = run.ttyconf; - printErrorMessages(gpa, s, .{}, bw, ttyconf, run.error_style, run.multiline_errors) catch {}; + printErrorMessages(gpa, s, .{}, &stderr.interface, stderr.mode, run.error_style, run.multiline_errors) catch {}; } handle_result: { @@ -1449,7 +1439,7 @@ pub fn printErrorMessages( failing_step: *Step, options: std.zig.ErrorBundle.RenderOptions, stderr: *Writer, - ttyconf: tty.Config, + fwm: File.Writer.Mode, error_style: ErrorStyle, multiline_errors: MultilineErrors, ) !void { @@ -1464,29 +1454,29 @@ pub fn printErrorMessages( } // Now, `step_stack` has the subtree that we want to print, in reverse order. - try ttyconf.setColor(stderr, .dim); + try fwm.setColor(stderr, .dim); var indent: usize = 0; while (step_stack.pop()) |s| : (indent += 1) { if (indent > 0) { try stderr.splatByteAll(' ', (indent - 1) * 3); - try printChildNodePrefix(stderr, ttyconf); + try printChildNodePrefix(stderr, fwm); } try stderr.writeAll(s.name); if (s == failing_step) { - try printStepFailure(s, stderr, ttyconf, true); + try printStepFailure(s, stderr, fwm, true); } else { try stderr.writeAll("\n"); } } - try ttyconf.setColor(stderr, .reset); + try fwm.setColor(stderr, .reset); } else { // Just print the failing step itself. - try ttyconf.setColor(stderr, .dim); + try fwm.setColor(stderr, .dim); try stderr.writeAll(failing_step.name); - try printStepFailure(failing_step, stderr, ttyconf, true); - try ttyconf.setColor(stderr, .reset); + try printStepFailure(failing_step, stderr, fwm, true); + try fwm.setColor(stderr, .reset); } if (failing_step.result_stderr.len > 0) { @@ -1496,12 +1486,12 @@ pub fn printErrorMessages( } } - try failing_step.result_error_bundle.renderToWriter(options, stderr, ttyconf); + try failing_step.result_error_bundle.renderToWriter(options, stderr, fwm); for (failing_step.result_error_msgs.items) |msg| { - try ttyconf.setColor(stderr, .red); + try fwm.setColor(stderr, .red); try stderr.writeAll("error:"); - try ttyconf.setColor(stderr, .reset); + try fwm.setColor(stderr, .reset); if (std.mem.indexOfScalar(u8, msg, '\n') == null) { try stderr.print(" {s}\n", .{msg}); } else switch (multiline_errors) { @@ -1519,9 +1509,9 @@ pub fn printErrorMessages( if (error_style.verboseContext()) { if (failing_step.result_failed_command) |cmd_str| { - try ttyconf.setColor(stderr, .red); + try fwm.setColor(stderr, .red); try stderr.writeAll("failed command: "); - try ttyconf.setColor(stderr, .reset); + try fwm.setColor(stderr, .reset); try stderr.writeAll(cmd_str); try stderr.writeByte('\n'); } diff --git a/lib/std/Build.zig b/lib/std/Build.zig index ae2ab1c4d0..085254fdde 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -5,9 +5,8 @@ const std = @import("std.zig"); const Io = std.Io; const fs = std.fs; const mem = std.mem; -const debug = std.debug; const panic = std.debug.panic; -const assert = debug.assert; +const assert = std.debug.assert; const log = std.log; const StringHashMap = std.StringHashMap; const Allocator = std.mem.Allocator; @@ -2090,7 +2089,7 @@ pub fn dependencyFromBuildZig( } const full_path = b.pathFromRoot("build.zig.zon"); - debug.panic("'{}' is not a build.zig struct of a dependency in '{s}'", .{ build_zig, full_path }); + std.debug.panic("'{}' is not a build.zig struct of a dependency in '{s}'", .{ build_zig, full_path }); } fn userValuesAreSame(lhs: UserValue, rhs: UserValue) bool { @@ -2249,9 +2248,9 @@ pub const GeneratedFile = struct { pub fn getPath2(gen: GeneratedFile, src_builder: *Build, asking_step: ?*Step) []const u8 { return gen.path orelse { - const w, const ttyconf = debug.lockStderrWriter(&.{}); - dumpBadGetPathHelp(gen.step, w, ttyconf, src_builder, asking_step) catch {}; - debug.unlockStderrWriter(); + const stderr = std.debug.lockStderrWriter(&.{}); + dumpBadGetPathHelp(gen.step, &stderr.interface, stderr.mode, src_builder, asking_step) catch {}; + std.debug.unlockStderrWriter(); @panic("misconfigured build script"); }; } @@ -2458,9 +2457,9 @@ pub const LazyPath = union(enum) { var file_path: Cache.Path = .{ .root_dir = Cache.Directory.cwd(), .sub_path = gen.file.path orelse { - const w, const ttyconf = debug.lockStderrWriter(&.{}); - dumpBadGetPathHelp(gen.file.step, w, ttyconf, src_builder, asking_step) catch {}; - debug.unlockStderrWriter(); + const stderr = std.debug.lockStderrWriter(&.{}); + dumpBadGetPathHelp(gen.file.step, &stderr.interface, stderr.mode, src_builder, asking_step) catch {}; + std.debug.unlockStderrWriter(); @panic("misconfigured build script"); }, }; @@ -2550,37 +2549,40 @@ fn dumpBadDirnameHelp( comptime msg: []const u8, args: anytype, ) anyerror!void { - const w, const tty_config = debug.lockStderrWriter(&.{}); - defer debug.unlockStderrWriter(); + const stderr = std.debug.lockStderrWriter(&.{}); + defer std.debug.unlockStderrWriter(); + + const w = &stderr.interface; + const fwm = stderr.mode; try w.print(msg, args); if (fail_step) |s| { - tty_config.setColor(w, .red) catch {}; + fwm.setColor(w, .red) catch {}; try w.writeAll(" The step was created by this stack trace:\n"); - tty_config.setColor(w, .reset) catch {}; + fwm.setColor(w, .reset) catch {}; - s.dump(w, tty_config); + s.dump(w, fwm); } if (asking_step) |as| { - tty_config.setColor(w, .red) catch {}; + fwm.setColor(w, .red) catch {}; 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 {}; + fwm.setColor(w, .reset) catch {}; - as.dump(w, tty_config); + as.dump(w, fwm); } - tty_config.setColor(w, .red) catch {}; + fwm.setColor(w, .red) catch {}; try w.writeAll(" Hope that helps. Proceeding to panic.\n"); - tty_config.setColor(w, .reset) catch {}; + fwm.setColor(w, .reset) catch {}; } /// In this function the stderr mutex has already been locked. pub fn dumpBadGetPathHelp( s: *Step, - w: *std.Io.Writer, - tty_config: std.Io.tty.Config, + w: *Io.Writer, + fwm: File.Writer.Mode, src_builder: *Build, asking_step: ?*Step, ) anyerror!void { @@ -2594,21 +2596,21 @@ pub fn dumpBadGetPathHelp( s.name, }); - tty_config.setColor(w, .red) catch {}; + fwm.setColor(w, .red) catch {}; try w.writeAll(" The step was created by this stack trace:\n"); - tty_config.setColor(w, .reset) catch {}; + fwm.setColor(w, .reset) catch {}; - s.dump(w, tty_config); + s.dump(w, fwm); if (asking_step) |as| { - tty_config.setColor(w, .red) catch {}; + fwm.setColor(w, .red) catch {}; 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 {}; + fwm.setColor(w, .reset) catch {}; - as.dump(w, tty_config); + as.dump(w, fwm); } - tty_config.setColor(w, .red) catch {}; + fwm.setColor(w, .red) catch {}; try w.writeAll(" Hope that helps. Proceeding to panic.\n"); - tty_config.setColor(w, .reset) catch {}; + fwm.setColor(w, .reset) catch {}; } pub const InstallDir = union(enum) { diff --git a/lib/std/Build/Fuzz.zig b/lib/std/Build/Fuzz.zig index 8837d7d527..c2b7d1e9e9 100644 --- a/lib/std/Build/Fuzz.zig +++ b/lib/std/Build/Fuzz.zig @@ -9,14 +9,12 @@ const Allocator = std.mem.Allocator; const log = std.log; const Coverage = std.debug.Coverage; const abi = Build.abi.fuzz; -const tty = std.Io.tty; const Fuzz = @This(); const build_runner = @import("root"); gpa: Allocator, io: Io, -ttyconf: tty.Config, mode: Mode, /// Allocated into `gpa`. @@ -77,7 +75,6 @@ const CoverageMap = struct { pub fn init( gpa: Allocator, io: Io, - ttyconf: tty.Config, all_steps: []const *Build.Step, root_prog_node: std.Progress.Node, mode: Mode, @@ -95,7 +92,7 @@ pub fn init( if (run.producer == null) continue; if (run.fuzz_tests.items.len == 0) continue; try steps.append(gpa, run); - rebuild_group.async(io, rebuildTestsWorkerRun, .{ run, gpa, ttyconf, rebuild_node }); + rebuild_group.async(io, rebuildTestsWorkerRun, .{ run, gpa, rebuild_node }); } if (steps.items.len == 0) fatal("no fuzz tests found", .{}); @@ -115,7 +112,6 @@ pub fn init( return .{ .gpa = gpa, .io = io, - .ttyconf = ttyconf, .mode = mode, .run_steps = run_steps, .group = .init, @@ -154,14 +150,14 @@ pub fn deinit(fuzz: *Fuzz) void { fuzz.gpa.free(fuzz.run_steps); } -fn rebuildTestsWorkerRun(run: *Step.Run, gpa: Allocator, ttyconf: tty.Config, parent_prog_node: std.Progress.Node) void { - rebuildTestsWorkerRunFallible(run, gpa, ttyconf, parent_prog_node) catch |err| { +fn rebuildTestsWorkerRun(run: *Step.Run, gpa: Allocator, parent_prog_node: std.Progress.Node) void { + rebuildTestsWorkerRunFallible(run, gpa, parent_prog_node) catch |err| { const compile = run.producer.?; log.err("step '{s}': failed to rebuild in fuzz mode: {t}", .{ compile.step.name, err }); }; } -fn rebuildTestsWorkerRunFallible(run: *Step.Run, gpa: Allocator, ttyconf: tty.Config, parent_prog_node: std.Progress.Node) !void { +fn rebuildTestsWorkerRunFallible(run: *Step.Run, gpa: Allocator, parent_prog_node: std.Progress.Node) !void { const compile = run.producer.?; const prog_node = parent_prog_node.start(compile.step.name, 0); defer prog_node.end(); @@ -174,9 +170,9 @@ fn rebuildTestsWorkerRunFallible(run: *Step.Run, gpa: Allocator, ttyconf: tty.Co if (show_error_msgs or show_compile_errors or show_stderr) { var buf: [256]u8 = undefined; - const w, _ = std.debug.lockStderrWriter(&buf); + const stderr = std.debug.lockStderrWriter(&buf); defer std.debug.unlockStderrWriter(); - build_runner.printErrorMessages(gpa, &compile.step, .{}, w, ttyconf, .verbose, .indent) catch {}; + build_runner.printErrorMessages(gpa, &compile.step, .{}, &stderr.interface, stderr.mode, .verbose, .indent) catch {}; } const rebuilt_bin_path = result catch |err| switch (err) { @@ -200,9 +196,9 @@ fn fuzzWorkerRun( run.rerunInFuzzMode(fuzz, unit_test_index, prog_node) catch |err| switch (err) { error.MakeFailed => { var buf: [256]u8 = undefined; - const w, _ = std.debug.lockStderrWriter(&buf); + const stderr = std.debug.lockStderrWriter(&buf); defer std.debug.unlockStderrWriter(); - build_runner.printErrorMessages(gpa, &run.step, .{}, w, fuzz.ttyconf, .verbose, .indent) catch {}; + build_runner.printErrorMessages(gpa, &run.step, .{}, &stderr.interface, stderr.mode, .verbose, .indent) catch {}; return; }, else => { diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 9c7fcc757f..0df58b24b7 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -117,7 +117,6 @@ pub const MakeOptions = struct { // it currently breaks because `std.net.Address` doesn't work there. Work around for now. .wasm32 => void, }, - ttyconf: std.Io.tty.Config, /// If set, this is a timeout to enforce on all individual unit tests, in nanoseconds. unit_test_timeout_ns: ?u64, /// Not to be confused with `Build.allocator`, which is an alias of `Build.graph.arena`. @@ -329,16 +328,16 @@ pub fn cast(step: *Step, comptime T: type) ?*T { } /// For debugging purposes, prints identifying information about this Step. -pub fn dump(step: *Step, w: *Io.Writer, tty_config: Io.tty.Config) void { +pub fn dump(step: *Step, w: *Io.Writer, fwm: Io.File.Writer.Mode) void { if (step.debug_stack_trace.instruction_addresses.len > 0) { w.print("name: '{s}'. creation stack trace:\n", .{step.name}) catch {}; - std.debug.writeStackTrace(&step.debug_stack_trace, w, tty_config) catch {}; + std.debug.writeStackTrace(&step.debug_stack_trace, w, fwm) catch {}; } else { const field = "debug_stack_frames_count"; comptime assert(@hasField(Build, field)); - tty_config.setColor(w, .yellow) catch {}; + fwm.setColor(w, .yellow) catch {}; w.print("name: '{s}'. no stack trace collected for this step, see std.Build." ++ field ++ "\n", .{step.name}) catch {}; - tty_config.setColor(w, .reset) catch {}; + fwm.setColor(w, .reset) catch {}; } } diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig index dfa5460981..d703e55b87 100644 --- a/lib/std/Build/Step/Compile.zig +++ b/lib/std/Build/Step/Compile.zig @@ -926,15 +926,15 @@ fn getGeneratedFilePath(compile: *Compile, comptime tag_name: []const u8, asking const maybe_path: ?*GeneratedFile = @field(compile, tag_name); const generated_file = maybe_path orelse { - const w, const ttyconf = std.debug.lockStderrWriter(&.{}); - std.Build.dumpBadGetPathHelp(&compile.step, w, ttyconf, compile.step.owner, asking_step) catch {}; + const stderr = std.debug.lockStderrWriter(&.{}); + std.Build.dumpBadGetPathHelp(&compile.step, &stderr.interface, stderr.mode, compile.step.owner, asking_step) catch {}; std.debug.unlockStderrWriter(); @panic("missing emit option for " ++ tag_name); }; const path = generated_file.path orelse { - const w, const ttyconf = std.debug.lockStderrWriter(&.{}); - std.Build.dumpBadGetPathHelp(&compile.step, w, ttyconf, compile.step.owner, asking_step) catch {}; + const stderr = std.debug.lockStderrWriter(&.{}); + std.Build.dumpBadGetPathHelp(&compile.step, &stderr.interface, stderr.mode, compile.step.owner, asking_step) catch {}; std.debug.unlockStderrWriter(); @panic(tag_name ++ " is null. Is there a missing step dependency?"); }; @@ -1904,7 +1904,7 @@ fn checkCompileErrors(compile: *Compile) !void { try actual_eb.renderToWriter(.{ .include_reference_trace = false, .include_source_line = false, - }, &aw.writer, .no_color); + }, &aw.writer, .streaming); break :ae try aw.toOwnedSlice(); }; diff --git a/lib/std/Build/Step/Run.zig b/lib/std/Build/Step/Run.zig index 95024061d8..8aafc77820 100644 --- a/lib/std/Build/Step/Run.zig +++ b/lib/std/Build/Step/Run.zig @@ -5,7 +5,7 @@ const std = @import("std"); const Io = std.Io; const Build = std.Build; const Step = std.Build.Step; -const fs = std.fs; +const Dir = std.Io.Dir; const mem = std.mem; const process = std.process; const EnvMap = std.process.EnvMap; @@ -26,19 +26,7 @@ cwd: ?Build.LazyPath, env_map: ?*EnvMap, /// Controls the `NO_COLOR` and `CLICOLOR_FORCE` environment variables. -color: enum { - /// `CLICOLOR_FORCE` is set, and `NO_COLOR` is unset. - enable, - /// `NO_COLOR` is set, and `CLICOLOR_FORCE` is unset. - disable, - /// If the build runner is using color, equivalent to `.enable`. Otherwise, equivalent to `.disable`. - inherit, - /// If stderr is captured or checked, equivalent to `.disable`. Otherwise, equivalent to `.inherit`. - auto, - /// The build runner does not modify the `CLICOLOR_FORCE` or `NO_COLOR` environment variables. - /// They are treated like normal variables, so can be controlled through `setEnvironmentVariable`. - manual, -} = .auto, +color: Color = .auto, /// When `true` prevents `ZIG_PROGRESS` environment variable from being passed /// to the child process, which otherwise would be used for the child to send @@ -112,6 +100,20 @@ rebuilt_executable: ?Path, /// If this Run step was produced by a Compile step, it is tracked here. producer: ?*Step.Compile, +pub const Color = enum { + /// `CLICOLOR_FORCE` is set, and `NO_COLOR` is unset. + enable, + /// `NO_COLOR` is set, and `CLICOLOR_FORCE` is unset. + disable, + /// If the build runner is using color, equivalent to `.enable`. Otherwise, equivalent to `.disable`. + inherit, + /// If stderr is captured or checked, equivalent to `.disable`. Otherwise, equivalent to `.inherit`. + auto, + /// The build runner does not modify the `CLICOLOR_FORCE` or `NO_COLOR` environment variables. + /// They are treated like normal variables, so can be controlled through `setEnvironmentVariable`. + manual, +}; + pub const StdIn = union(enum) { none, bytes: []const u8, @@ -565,7 +567,7 @@ pub fn addPathDir(run: *Run, search_path: []const u8) void { if (prev_path) |pp| { const new_path = b.fmt("{s}{c}{s}", .{ pp, - if (use_wine) fs.path.delimiter_windows else fs.path.delimiter, + if (use_wine) Dir.path.delimiter_windows else Dir.path.delimiter, search_path, }); env_map.put(key, new_path) catch @panic("OOM"); @@ -748,7 +750,7 @@ fn checksContainStderr(checks: []const StdIo.Check) bool { fn convertPathArg(run: *Run, path: Build.Cache.Path) []const u8 { const b = run.step.owner; const path_str = path.toString(b.graph.arena) catch @panic("OOM"); - if (std.fs.path.isAbsolute(path_str)) { + if (Dir.path.isAbsolute(path_str)) { // Absolute paths don't need changing. return path_str; } @@ -756,19 +758,19 @@ fn convertPathArg(run: *Run, path: Build.Cache.Path) []const u8 { const child_lazy_cwd = run.cwd orelse break :rel path_str; const child_cwd = child_lazy_cwd.getPath3(b, &run.step).toString(b.graph.arena) catch @panic("OOM"); // Convert it from relative to *our* cwd, to relative to the *child's* cwd. - break :rel std.fs.path.relative(b.graph.arena, child_cwd, path_str) catch @panic("OOM"); + break :rel Dir.path.relative(b.graph.arena, child_cwd, path_str) catch @panic("OOM"); }; // Not every path can be made relative, e.g. if the path and the child cwd are on different // disk designators on Windows. In that case, `relative` will return an absolute path which we can // just return. - if (std.fs.path.isAbsolute(child_cwd_rel)) { + if (Dir.path.isAbsolute(child_cwd_rel)) { return child_cwd_rel; } // We're not done yet. In some cases this path must be prefixed with './': // * On POSIX, the executable name cannot be a single component like 'foo' // * Some executables might treat a leading '-' like a flag, which we must avoid // There's no harm in it, so just *always* apply this prefix. - return std.fs.path.join(b.graph.arena, &.{ ".", child_cwd_rel }) catch @panic("OOM"); + return Dir.path.join(b.graph.arena, &.{ ".", child_cwd_rel }) catch @panic("OOM"); } const IndexedOutput = struct { @@ -965,11 +967,11 @@ fn make(step: *Step, options: Step.MakeOptions) !void { &digest, ); - const output_dir_path = "o" ++ fs.path.sep_str ++ &digest; + const output_dir_path = "o" ++ Dir.path.sep_str ++ &digest; for (output_placeholders.items) |placeholder| { const output_sub_path = b.pathJoin(&.{ output_dir_path, placeholder.output.basename }); const output_sub_dir_path = switch (placeholder.tag) { - .output_file => fs.path.dirname(output_sub_path).?, + .output_file => Dir.path.dirname(output_sub_path).?, .output_directory => output_sub_path, else => unreachable, }; @@ -995,13 +997,13 @@ fn make(step: *Step, options: Step.MakeOptions) !void { // We do not know the final output paths yet, use temp paths to run the command. const rand_int = std.crypto.random.int(u64); - const tmp_dir_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int); + const tmp_dir_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int); for (output_placeholders.items) |placeholder| { const output_components = .{ tmp_dir_path, placeholder.output.basename }; const output_sub_path = b.pathJoin(&output_components); const output_sub_dir_path = switch (placeholder.tag) { - .output_file => fs.path.dirname(output_sub_path).?, + .output_file => Dir.path.dirname(output_sub_path).?, .output_directory => output_sub_path, else => unreachable, }; @@ -1023,7 +1025,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, options, null); - const dep_file_dir = Io.Dir.cwd(); + const dep_file_dir = Dir.cwd(); const dep_file_basename = dep_output_file.generated_file.getPath2(b, step); if (has_side_effects) try man.addDepFile(dep_file_dir, dep_file_basename) @@ -1040,7 +1042,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { // Rename into place if (any_output) { - const o_sub_path = "o" ++ fs.path.sep_str ++ &digest; + const o_sub_path = "o" ++ Dir.path.sep_str ++ &digest; b.cache_root.handle.rename(tmp_dir_path, b.cache_root.handle, o_sub_path, io) catch |err| { if (err == error.PathAlreadyExists) { @@ -1139,12 +1141,11 @@ pub fn rerunInFuzzMode( const has_side_effects = false; const rand_int = std.crypto.random.int(u64); - const tmp_dir_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int); + const tmp_dir_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int); try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, .{ .progress_node = prog_node, .watch = undefined, // not used by `runCommand` .web_server = null, // only needed for time reports - .ttyconf = fuzz.ttyconf, .unit_test_timeout_ns = null, // don't time out fuzz tests for now .gpa = fuzz.gpa, }, .{ @@ -1266,10 +1267,7 @@ fn runCommand( try env_map.put("NO_COLOR", "1"); env_map.remove("CLICOLOR_FORCE"); }, - .inherit => switch (options.ttyconf) { - .no_color, .windows_api => continue :color .disable, - .escape_codes => continue :color .enable, - }, + .inherit => {}, .auto => { const capture_stderr = run.captured_stderr != null or switch (run.stdio) { .check => |checks| checksContainStderr(checks.items), @@ -1464,7 +1462,7 @@ fn runCommand( captured.output.generated_file.path = output_path; const sub_path = b.pathJoin(&output_components); - const sub_path_dirname = fs.path.dirname(sub_path).?; + const sub_path_dirname = Dir.path.dirname(sub_path).?; b.cache_root.handle.makePath(io, sub_path_dirname) catch |err| { return step.fail("unable to make path '{f}{s}': {s}", .{ b.cache_root, sub_path_dirname, @errorName(err), @@ -1650,8 +1648,8 @@ fn spawnChildAndCollect( if (!run.disable_zig_progress and !inherit) { child.progress_node = options.progress_node; } - if (inherit) std.debug.lockStdErr(); - defer if (inherit) std.debug.unlockStdErr(); + if (inherit) _ = std.debug.lockStderrWriter(&.{}); + defer if (inherit) std.debug.unlockStderrWriter(); var timer = try std.time.Timer.start(); const res = try evalGeneric(run, &child); run.step.result_duration_ns = timer.read(); @@ -2277,7 +2275,7 @@ fn addPathForDynLibs(run: *Run, artifact: *Step.Compile) void { if (compile.root_module.resolved_target.?.result.os.tag == .windows and compile.isDynamicLibrary()) { - addPathDir(run, fs.path.dirname(compile.getEmittedBin().getPath2(b, &run.step)).?); + addPathDir(run, Dir.path.dirname(compile.getEmittedBin().getPath2(b, &run.step)).?); } } } diff --git a/lib/std/Build/WebServer.zig b/lib/std/Build/WebServer.zig index 5f633c5948..ccb7159192 100644 --- a/lib/std/Build/WebServer.zig +++ b/lib/std/Build/WebServer.zig @@ -2,7 +2,6 @@ gpa: Allocator, graph: *const Build.Graph, all_steps: []const *Build.Step, listen_address: net.IpAddress, -ttyconf: Io.tty.Config, root_prog_node: std.Progress.Node, watch: bool, @@ -52,7 +51,6 @@ pub fn notifyUpdate(ws: *WebServer) void { pub const Options = struct { gpa: Allocator, - ttyconf: Io.tty.Config, graph: *const std.Build.Graph, all_steps: []const *Build.Step, root_prog_node: std.Progress.Node, @@ -98,7 +96,6 @@ pub fn init(opts: Options) WebServer { return .{ .gpa = opts.gpa, - .ttyconf = opts.ttyconf, .graph = opts.graph, .all_steps = all_steps, .listen_address = opts.listen_address, @@ -233,7 +230,6 @@ pub fn finishBuild(ws: *WebServer, opts: struct { ws.fuzz = Fuzz.init( ws.gpa, ws.graph.io, - ws.ttyconf, ws.all_steps, ws.root_prog_node, .{ .forever = .{ .ws = ws } }, diff --git a/lib/std/Io/File/Writer.zig b/lib/std/Io/File/Writer.zig index 8f9f573a4b..03e4ad8a15 100644 --- a/lib/std/Io/File/Writer.zig +++ b/lib/std/Io/File/Writer.zig @@ -99,7 +99,7 @@ pub const Mode = union(enum) { pub const SetColorError = std.os.windows.SetConsoleTextAttributeError || Io.Writer.Error; - pub fn setColor(mode: Mode, io_w: *Io.Writer, color: Color) Mode.SetColorError!void { + pub fn setColor(mode: Mode, io_w: *Io.Writer, color: Color) SetColorError!void { switch (mode) { .streaming, .positional, .streaming_simple, .positional_simple, .failure => return, .terminal_escaped => { @@ -155,6 +155,34 @@ pub const Mode = union(enum) { }, } } + + fn DecorateArgs(comptime Args: type) type { + const fields = @typeInfo(Args).@"struct".fields; + var new_fields: [fields.len]type = undefined; + for (fields, &new_fields) |old, *new| { + if (old.type == std.debug.FormatStackTrace) { + new.* = std.debug.FormatStackTrace.Decorated; + } else { + new.* = old.type; + } + } + return @Tuple(&new_fields); + } + + pub fn decorateArgs(file_writer_mode: std.Io.File.Writer.Mode, args: anytype) DecorateArgs(@TypeOf(args)) { + var new_args: DecorateArgs(@TypeOf(args)) = undefined; + inline for (args, &new_args) |old, *new| { + if (@TypeOf(old) == std.debug.FormatStackTrace) { + new.* = .{ + .stack_trace = old.stack_trace, + .file_writer_mode = file_writer_mode, + }; + } else { + new.* = old; + } + } + return new_args; + } }; pub const Error = error{ @@ -426,6 +454,8 @@ pub fn end(w: *Writer) EndError!void { .streaming, .streaming_simple, + .terminal_escaped, + .terminal_winapi, .failure, => {}, } @@ -453,10 +483,11 @@ pub const Color = enum { reset, }; -pub const SetColorError = Mode.SetColorError; - -pub fn setColor(w: *Writer, color: Color) SetColorError!void { - return w.mode.setColor(&w.interface, color); +pub fn setColor(w: *Writer, color: Color) Io.Writer.Error!void { + return w.mode.setColor(&w.interface, color) catch |err| switch (err) { + error.WriteFailed => |e| return e, + else => |e| w.err = e, + }; } pub fn disableEscape(w: *Writer) Mode { diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 69d8c942c4..67f7d3a9fe 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -306,7 +306,7 @@ pub fn print(comptime fmt: []const u8, args: anytype) void { var buffer: [64]u8 = undefined; const stderr = lockStderrWriter(&buffer); defer unlockStderrWriter(); - stderr.interface.print(fmt, args) catch return; + stderr.interface.print(fmt, stderr.mode.decorateArgs(args)) catch return; } } diff --git a/lib/std/log.zig b/lib/std/log.zig index 029f724f44..b75bada580 100644 --- a/lib/std/log.zig +++ b/lib/std/log.zig @@ -120,35 +120,7 @@ pub fn defaultLogFileWriter( } fw.interface.writeAll(": ") catch return; fw.setColor(.reset) catch {}; - fw.interface.print(format ++ "\n", decorateArgs(args, fw.mode)) catch return; -} - -fn DecorateArgs(comptime Args: type) type { - const fields = @typeInfo(Args).@"struct".fields; - var new_fields: [fields.len]type = undefined; - for (fields, &new_fields) |old, *new| { - if (old.type == std.debug.FormatStackTrace) { - new.* = std.debug.FormatStackTrace.Decorated; - } else { - new.* = old.type; - } - } - return @Tuple(&new_fields); -} - -fn decorateArgs(args: anytype, file_writer_mode: std.Io.File.Writer.Mode) DecorateArgs(@TypeOf(args)) { - var new_args: DecorateArgs(@TypeOf(args)) = undefined; - inline for (args, &new_args) |old, *new| { - if (@TypeOf(old) == std.debug.FormatStackTrace) { - new.* = .{ - .stack_trace = old.stack_trace, - .file_writer_mode = file_writer_mode, - }; - } else { - new.* = old; - } - } - return new_args; + fw.interface.print(format ++ "\n", fw.mode.decorateArgs(args)) catch return; } /// Returns a scoped logging namespace that logs all messages using the scope diff --git a/lib/std/testing.zig b/lib/std/testing.zig index ab9f1f73c3..092b65d71a 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -354,11 +354,10 @@ test expectApproxEqRel { } } -/// This function is intended to be used only in tests. When the two slices are not -/// equal, prints diagnostics to stderr to show exactly how they are not equal (with -/// the differences highlighted in red), then returns a test failure error. -/// The colorized output is optional and controlled by the return of `Io.tty.Config.detect`. -/// If your inputs are UTF-8 encoded strings, consider calling `expectEqualStrings` instead. +/// This function is intended to be used only in tests. When the two slices are +/// not equal, prints diagnostics to stderr to show exactly how they are not +/// equal (with the differences highlighted in red), then returns a test +/// failure error. pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const T) !void { const diff_index: usize = diff_index: { const shortest = @min(expected.len, actual.len); @@ -369,20 +368,13 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const break :diff_index if (expected.len == actual.len) return else shortest; }; if (!backend_can_print) return error.TestExpectedEqual; - const stderr_w, const ttyconf = std.debug.lockStderrWriter(&.{}); + const stderr = std.debug.lockStderrWriter(&.{}); defer std.debug.unlockStderrWriter(); - failEqualSlices(T, expected, actual, diff_index, stderr_w, ttyconf) catch {}; + failEqualSlices(T, expected, actual, diff_index, &stderr.interface, stderr.mode) catch {}; return error.TestExpectedEqual; } -fn failEqualSlices( - comptime T: type, - expected: []const T, - actual: []const T, - diff_index: usize, - w: *Io.Writer, - ttyconf: Io.tty.Config, -) !void { +fn failEqualSlices(comptime T: type, expected: []const T, actual: []const T, diff_index: usize, w: *Io.Writer, fwm: Io.File.Writer.Mode) !void { try w.print("slices differ. first difference occurs at index {d} (0x{X})\n", .{ diff_index, diff_index }); // TODO: Should this be configurable by the caller? @@ -404,12 +396,12 @@ fn failEqualSlices( var differ = if (T == u8) BytesDiffer{ .expected = expected_window, .actual = actual_window, - .ttyconf = ttyconf, + .file_writer_mode = fwm, } else SliceDiffer(T){ .start_index = window_start, .expected = expected_window, .actual = actual_window, - .ttyconf = ttyconf, + .file_writer_mode = fwm, }; // Print indexes as hex for slices of u8 since it's more likely to be binary data where @@ -466,7 +458,7 @@ fn SliceDiffer(comptime T: type) type { start_index: usize, expected: []const T, actual: []const T, - ttyconf: Io.tty.Config, + file_writer_mode: Io.File.Writer.Mode, const Self = @This(); @@ -474,13 +466,13 @@ fn SliceDiffer(comptime T: type) type { for (self.expected, 0..) |value, i| { const full_index = self.start_index + i; const diff = if (i < self.actual.len) !std.meta.eql(self.actual[i], value) else true; - if (diff) try self.ttyconf.setColor(writer, .red); + if (diff) try self.file_writer_mode.setColor(writer, .red); if (@typeInfo(T) == .pointer) { try writer.print("[{}]{*}: {any}\n", .{ full_index, value, value }); } else { try writer.print("[{}]: {any}\n", .{ full_index, value }); } - if (diff) try self.ttyconf.setColor(writer, .reset); + if (diff) try self.file_writer_mode.setColor(writer, .reset); } } }; @@ -489,7 +481,7 @@ fn SliceDiffer(comptime T: type) type { const BytesDiffer = struct { expected: []const u8, actual: []const u8, - ttyconf: Io.tty.Config, + file_writer_mode: Io.File.Writer.Mode, pub fn write(self: BytesDiffer, writer: *Io.Writer) !void { var expected_iterator = std.mem.window(u8, self.expected, 16, 16); @@ -516,7 +508,7 @@ const BytesDiffer = struct { try self.writeDiff(writer, "{c}", .{byte}, diff); } else { // TODO: remove this `if` when https://github.com/ziglang/zig/issues/7600 is fixed - if (self.ttyconf == .windows_api) { + if (self.file_writer_mode == .terminal_winapi) { try self.writeDiff(writer, ".", .{}, diff); continue; } @@ -538,9 +530,9 @@ const BytesDiffer = struct { } fn writeDiff(self: BytesDiffer, writer: *Io.Writer, comptime fmt: []const u8, args: anytype, diff: bool) !void { - if (diff) try self.ttyconf.setColor(writer, .red); + if (diff) try self.file_writer_mode.setColor(writer, .red); try writer.print(fmt, args); - if (diff) try self.ttyconf.setColor(writer, .reset); + if (diff) try self.file_writer_mode.setColor(writer, .reset); } }; @@ -1162,7 +1154,6 @@ pub fn checkAllAllocationFailures(backing_allocator: std.mem.Allocator, comptime } else |err| switch (err) { error.OutOfMemory => { if (failing_allocator_inst.allocated_bytes != failing_allocator_inst.freed_bytes) { - const tty_config: Io.tty.Config = .detect(.stderr()); print( "\nfail_index: {d}/{d}\nallocated bytes: {d}\nfreed bytes: {d}\nallocations: {d}\ndeallocations: {d}\nallocation that was made to fail: {f}", .{ @@ -1174,7 +1165,6 @@ pub fn checkAllAllocationFailures(backing_allocator: std.mem.Allocator, comptime failing_allocator_inst.deallocations, std.debug.FormatStackTrace{ .stack_trace = failing_allocator_inst.getStackTrace(), - .tty_config = tty_config, }, }, ); diff --git a/lib/std/zig.zig b/lib/std/zig.zig index ac6a0787de..b6203326f2 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -53,18 +53,18 @@ pub const Color = enum { /// Assume stderr is a terminal. on, - pub fn getTtyConf(color: Color, detected: Io.tty.Config) Io.tty.Config { + pub fn getTtyConf(color: Color, detected: Io.File.Writer.Mode) Io.File.Writer.Mode { return switch (color) { .auto => detected, - .on => .escape_codes, - .off => .no_color, + .on => .terminal_escaped, + .off => .streaming, }; } - pub fn detectTtyConf(color: Color, io: Io) Io.tty.Config { + pub fn detectTtyConf(color: Color, io: Io) Io.File.Writer.Mode { return switch (color) { .auto => .detect(io, .stderr()), - .on => .escape_codes, - .off => .no_color, + .on => .terminal_escaped, + .off => .streaming, }; } }; diff --git a/lib/std/zig/ErrorBundle.zig b/lib/std/zig/ErrorBundle.zig index ef6203cbe8..cc6182c8aa 100644 --- a/lib/std/zig/ErrorBundle.zig +++ b/lib/std/zig/ErrorBundle.zig @@ -164,15 +164,20 @@ pub const RenderOptions = struct { pub fn renderToStdErr(eb: ErrorBundle, options: RenderOptions, color: std.zig.Color) void { var buffer: [256]u8 = undefined; - const w, const ttyconf = std.debug.lockStderrWriter(&buffer); + const stderr = std.debug.lockStderrWriter(&buffer); defer std.debug.unlockStderrWriter(); - renderToWriter(eb, options, w, color.getTtyConf(ttyconf)) catch return; + renderToWriter(eb, options, &stderr.interface, color.getTtyConf(stderr.mode)) catch return; } -pub fn renderToWriter(eb: ErrorBundle, options: RenderOptions, w: *Writer, ttyconf: Io.tty.Config) (Writer.Error || std.posix.UnexpectedError)!void { +pub fn renderToWriter( + eb: ErrorBundle, + options: RenderOptions, + w: *Writer, + fwm: Io.File.Writer.Mode, +) Io.File.Writer.Mode.SetColorError!void { if (eb.extra.len == 0) return; for (eb.getMessages()) |err_msg| { - try renderErrorMessageToWriter(eb, options, err_msg, w, ttyconf, "error", .red, 0); + try renderErrorMessageToWriter(eb, options, err_msg, w, fwm, "error", .red, 0); } if (options.include_log_text) { @@ -189,18 +194,18 @@ fn renderErrorMessageToWriter( options: RenderOptions, err_msg_index: MessageIndex, w: *Writer, - ttyconf: Io.tty.Config, + fwm: Io.File.Writer.Mode, kind: []const u8, - color: Io.tty.Color, + color: Io.File.Writer.Color, indent: usize, -) (Writer.Error || std.posix.UnexpectedError)!void { +) Io.File.Writer.Mode.SetColorError!void { const err_msg = eb.getErrorMessage(err_msg_index); if (err_msg.src_loc != .none) { const src = eb.extraData(SourceLocation, @intFromEnum(err_msg.src_loc)); var prefix: Writer.Discarding = .init(&.{}); try w.splatByteAll(' ', indent); prefix.count += indent; - try ttyconf.setColor(w, .bold); + try fwm.setColor(w, .bold); try w.print("{s}:{d}:{d}: ", .{ eb.nullTerminatedString(src.data.src_path), src.data.line + 1, @@ -211,7 +216,7 @@ fn renderErrorMessageToWriter( src.data.line + 1, src.data.column + 1, }); - try ttyconf.setColor(w, color); + try fwm.setColor(w, color); try w.writeAll(kind); prefix.count += kind.len; try w.writeAll(": "); @@ -219,17 +224,17 @@ fn renderErrorMessageToWriter( // This is the length of the part before the error message: // e.g. "file.zig:4:5: error: " const prefix_len: usize = @intCast(prefix.count); - try ttyconf.setColor(w, .reset); - try ttyconf.setColor(w, .bold); + try fwm.setColor(w, .reset); + try fwm.setColor(w, .bold); if (err_msg.count == 1) { try writeMsg(eb, err_msg, w, prefix_len); try w.writeByte('\n'); } else { try writeMsg(eb, err_msg, w, prefix_len); - try ttyconf.setColor(w, .dim); + try fwm.setColor(w, .dim); try w.print(" ({d} times)\n", .{err_msg.count}); } - try ttyconf.setColor(w, .reset); + try fwm.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) { @@ -242,19 +247,19 @@ fn renderErrorMessageToWriter( // -1 since span.main includes the caret const after_caret = src.data.span_end -| src.data.span_main -| 1; try w.splatByteAll(' ', src.data.column - before_caret); - try ttyconf.setColor(w, .green); + try fwm.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); + try fwm.setColor(w, .reset); } for (eb.getNotes(err_msg_index)) |note| { - try renderErrorMessageToWriter(eb, options, note, w, ttyconf, "note", .cyan, indent); + try renderErrorMessageToWriter(eb, options, note, w, fwm, "note", .cyan, indent); } if (src.data.reference_trace_len > 0 and options.include_reference_trace) { - try ttyconf.setColor(w, .reset); - try ttyconf.setColor(w, .dim); + try fwm.setColor(w, .reset); + try fwm.setColor(w, .dim); try w.print("referenced by:\n", .{}); var ref_index = src.end; for (0..src.data.reference_trace_len) |_| { @@ -281,25 +286,25 @@ fn renderErrorMessageToWriter( ); } } - try ttyconf.setColor(w, .reset); + try fwm.setColor(w, .reset); } } else { - try ttyconf.setColor(w, color); + try fwm.setColor(w, color); try w.splatByteAll(' ', indent); try w.writeAll(kind); try w.writeAll(": "); - try ttyconf.setColor(w, .reset); + try fwm.setColor(w, .reset); const msg = eb.nullTerminatedString(err_msg.msg); if (err_msg.count == 1) { try w.print("{s}\n", .{msg}); } else { try w.print("{s}", .{msg}); - try ttyconf.setColor(w, .dim); + try fwm.setColor(w, .dim); try w.print(" ({d} times)\n", .{err_msg.count}); } - try ttyconf.setColor(w, .reset); + try fwm.setColor(w, .reset); for (eb.getNotes(err_msg_index)) |note| { - try renderErrorMessageToWriter(eb, options, note, w, ttyconf, "note", .cyan, indent + 4); + try renderErrorMessageToWriter(eb, options, note, w, fwm, "note", .cyan, indent + 4); } } } @@ -806,12 +811,10 @@ pub const Wip = struct { }; defer bundle.deinit(std.testing.allocator); - const ttyconf: Io.tty.Config = .no_color; - var bundle_buf: Writer.Allocating = .init(std.testing.allocator); const bundle_bw = &bundle_buf.interface; defer bundle_buf.deinit(); - try bundle.renderToWriter(.{ .ttyconf = ttyconf }, bundle_bw); + try bundle.renderToWriter(bundle_bw); var copy = copy: { var wip: ErrorBundle.Wip = undefined; @@ -827,7 +830,7 @@ pub const Wip = struct { var copy_buf: Writer.Allocating = .init(std.testing.allocator); const copy_bw = ©_buf.interface; defer copy_buf.deinit(); - try copy.renderToWriter(.{ .ttyconf = ttyconf }, copy_bw); + try copy.renderToWriter(copy_bw); try std.testing.expectEqualStrings(bundle_bw.written(), copy_bw.written()); } |
