From 3223d3a1ac0aa7a5ffb9252c2e8d871f0bf70ec0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carl=20=C3=85stholm?= Date: Mon, 2 Feb 2026 01:24:39 +0100 Subject: [PATCH] Improve debug I/O color detection/handling for WASI and Emscripten When linking libc, these targets can get their environment variables from `std.c.environ`. Additionally, it's okay for WASI to use ANSI escape sequences; nothing in the relevant specs claim otherwise. --- lib/std/Io/Threaded.zig | 7 ------- lib/std/process/Environ.zig | 2 +- lib/std/start.zig | 18 +++++++++++++++--- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index 2fcd4551104797f08b75d8ababb7833455d01590..937f75a531527f99c7b1967c66466f63d844b9b8 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -8729,13 +8729,6 @@ fn supportsAnsiEscapeCodes(file: File) Io.Cancelable!bool { } } - if (native_os == .wasi) { - // WASI sanitizes stdout when fd is a tty so ANSI escape codes will not - // be interpreted as actual cursor commands, and stderr is always - // sanitized. - return false; - } - if (try isTty(file)) return true; return false; diff --git a/lib/std/process/Environ.zig b/lib/std/process/Environ.zig index d9ef7aec2ddf7205056b7c3ede163d7c309ae03c..57d5ea02711885f583f60c65fd26adfb3712f11b 100644 --- a/lib/std/process/Environ.zig +++ b/lib/std/process/Environ.zig @@ -24,7 +24,7 @@ pub const empty: Environ = .{ .block = .empty }; /// operating system `void` is also used. pub const Block = switch (native_os) { .windows => GlobalBlock, - .wasi => switch (builtin.link_libc) { + .wasi, .emscripten => switch (builtin.link_libc) { false => GlobalBlock, true => PosixBlock, }, diff --git a/lib/std/start.zig b/lib/std/start.zig index f6b4064ebdf258330870d30020e78e02a9d4be01..873504273844da12a23ddfa5d4ddd1e84342a66e 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -664,10 +664,22 @@ fn main(c_argc: c_int, c_argv: [*][*:0]c_char, c_envp: [*:null]?[*:0]c_char) cal fn mainWithoutEnv(c_argc: c_int, c_argv: [*][*:0]c_char) callconv(.c) c_int { const argv = @as([*][*:0]u8, @ptrCast(c_argv))[0..@intCast(c_argc)]; - if (@sizeOf(std.Io.Threaded.Argv0) != 0) { - if (std.Options.debug_threaded_io) |t| t.argv0.value = argv[0]; + const environ: [:null]?[*:0]u8 = switch (builtin.os.tag) { + .wasi, .emscripten => environ: { + const c_environ = std.c.environ; + var env_count: usize = 0; + while (c_environ[env_count] != null) : (env_count += 1) {} + break :environ c_environ[0..env_count :null]; + }, + else => &.{}, + }; + const env_block: std.process.Environ.Block = .{ .slice = environ }; + if (std.Options.debug_threaded_io) |t| { + if (@sizeOf(std.Io.Threaded.Argv0) != 0) t.argv0.value = argv[0]; + t.environ = .{ .process_environ = .{ .block = env_block } }; + t.environ_initialized = env_block.isEmpty(); } - return callMain(argv, .empty); + return callMain(argv, env_block); } /// General error message for a malformed return type -- 2.54.0