std.process: adding hasNonEmptyEnvVar() and using for NO_COLOR

This commit is contained in:
John Benediktsson 2025-02-04 21:19:02 -08:00 committed by Alex Rønne Petersen
parent 62e251dcaa
commit 1c07eacc7f
4 changed files with 50 additions and 16 deletions

View File

@ -12,9 +12,9 @@ const native_os = builtin.os.tag;
pub fn detectConfig(file: File) Config { pub fn detectConfig(file: File) Config {
const force_color: ?bool = if (builtin.os.tag == .wasi) const force_color: ?bool = if (builtin.os.tag == .wasi)
null // wasi does not support environment variables null // wasi does not support environment variables
else if (process.hasEnvVarConstant("NO_COLOR")) else if (process.hasNonEmptyEnvVarConstant("NO_COLOR"))
false false
else if (process.hasEnvVarConstant("CLICOLOR_FORCE")) else if (process.hasNonEmptyEnvVarConstant("CLICOLOR_FORCE"))
true true
else else
null; null;

View File

@ -431,6 +431,20 @@ pub fn hasEnvVarConstant(comptime key: []const u8) bool {
} }
} }
/// On Windows, `key` must be valid UTF-8.
pub fn hasNonEmptyEnvVarConstant(comptime key: []const u8) bool {
if (native_os == .windows) {
const key_w = comptime unicode.utf8ToUtf16LeStringLiteral(key);
const value = getenvW(key_w) orelse return false;
return value.len != 0;
} else if (native_os == .wasi and !builtin.link_libc) {
@compileError("hasNonEmptyEnvVarConstant is not supported for WASI without libc");
} else {
const value = posix.getenv(key) orelse return false;
return value.len != 0;
}
}
pub const ParseEnvVarIntError = std.fmt.ParseIntError || error{EnvironmentVariableNotFound}; pub const ParseEnvVarIntError = std.fmt.ParseIntError || error{EnvironmentVariableNotFound};
/// Parses an environment variable as an integer. /// Parses an environment variable as an integer.
@ -477,6 +491,27 @@ pub fn hasEnvVar(allocator: Allocator, key: []const u8) HasEnvVarError!bool {
} }
} }
/// On Windows, if `key` is not valid [WTF-8](https://simonsapin.github.io/wtf-8/),
/// then `error.InvalidWtf8` is returned.
pub fn hasNonEmptyEnvVar(allocator: Allocator, key: []const u8) HasEnvVarError!bool {
if (native_os == .windows) {
var stack_alloc = std.heap.stackFallback(256 * @sizeOf(u16), allocator);
const stack_allocator = stack_alloc.get();
const key_w = try unicode.wtf8ToWtf16LeAllocZ(stack_allocator, key);
defer stack_allocator.free(key_w);
const value = getenvW(key_w) orelse return false;
return value.len != 0;
} else if (native_os == .wasi and !builtin.link_libc) {
var envmap = getEnvMap(allocator) catch return error.OutOfMemory;
defer envmap.deinit();
const value = envmap.getPtr(key) orelse return false;
return value.len != 0;
} else {
const value = posix.getenv(key) orelse return false;
return value.len != 0;
}
}
/// Windows-only. Get an environment variable with a null-terminated, WTF-16 encoded name. /// Windows-only. Get an environment variable with a null-terminated, WTF-16 encoded name.
/// ///
/// This function performs a Unicode-aware case-insensitive lookup using RtlEqualUnicodeString. /// This function performs a Unicode-aware case-insensitive lookup using RtlEqualUnicodeString.

View File

@ -697,6 +697,10 @@ pub const EnvVar = enum {
XDG_CACHE_HOME, XDG_CACHE_HOME,
HOME, HOME,
pub fn isSet(comptime ev: EnvVar) bool {
return std.process.hasNonEmptyEnvVarConstant(@tagName(ev));
}
pub fn get(ev: EnvVar, arena: std.mem.Allocator) !?[]u8 { pub fn get(ev: EnvVar, arena: std.mem.Allocator) !?[]u8 {
if (std.process.getEnvVarOwned(arena, @tagName(ev))) |value| { if (std.process.getEnvVarOwned(arena, @tagName(ev))) |value| {
return value; return value;
@ -709,11 +713,6 @@ pub const EnvVar = enum {
pub fn getPosix(comptime ev: EnvVar) ?[:0]const u8 { pub fn getPosix(comptime ev: EnvVar) ?[:0]const u8 {
return std.posix.getenvZ(@tagName(ev)); return std.posix.getenvZ(@tagName(ev));
} }
pub fn isSet(ev: EnvVar, arena: std.mem.Allocator) !bool {
const value = try ev.get(arena) orelse return false;
return value.len != 0;
}
}; };
pub const SimpleComptimeReason = enum(u32) { pub const SimpleComptimeReason = enum(u32) {

View File

@ -826,9 +826,9 @@ fn buildOutputType(
var listen: Listen = .none; var listen: Listen = .none;
var debug_compile_errors = false; var debug_compile_errors = false;
var verbose_link = (native_os != .wasi or builtin.link_libc) and var verbose_link = (native_os != .wasi or builtin.link_libc) and
try EnvVar.ZIG_VERBOSE_LINK.isSet(arena); EnvVar.ZIG_VERBOSE_LINK.isSet();
var verbose_cc = (native_os != .wasi or builtin.link_libc) and var verbose_cc = (native_os != .wasi or builtin.link_libc) and
try EnvVar.ZIG_VERBOSE_CC.isSet(arena); EnvVar.ZIG_VERBOSE_CC.isSet();
var verbose_air = false; var verbose_air = false;
var verbose_intern_pool = false; var verbose_intern_pool = false;
var verbose_generic_instances = false; var verbose_generic_instances = false;
@ -1006,9 +1006,9 @@ fn buildOutputType(
// if set, default the color setting to .off or .on, respectively // if set, default the color setting to .off or .on, respectively
// explicit --color arguments will still override this setting. // explicit --color arguments will still override this setting.
// Disable color on WASI per https://github.com/WebAssembly/WASI/issues/162 // Disable color on WASI per https://github.com/WebAssembly/WASI/issues/162
var color: Color = if (native_os == .wasi or try EnvVar.NO_COLOR.isSet(arena)) var color: Color = if (native_os == .wasi or EnvVar.NO_COLOR.isSet())
.off .off
else if (try EnvVar.CLICOLOR_FORCE.isSet(arena)) else if (EnvVar.CLICOLOR_FORCE.isSet())
.on .on
else else
.auto; .auto;
@ -4769,9 +4769,9 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
var reference_trace: ?u32 = null; var reference_trace: ?u32 = null;
var debug_compile_errors = false; var debug_compile_errors = false;
var verbose_link = (native_os != .wasi or builtin.link_libc) and var verbose_link = (native_os != .wasi or builtin.link_libc) and
try EnvVar.ZIG_VERBOSE_LINK.isSet(arena); EnvVar.ZIG_VERBOSE_LINK.isSet();
var verbose_cc = (native_os != .wasi or builtin.link_libc) and var verbose_cc = (native_os != .wasi or builtin.link_libc) and
try EnvVar.ZIG_VERBOSE_CC.isSet(arena); EnvVar.ZIG_VERBOSE_CC.isSet();
var verbose_air = false; var verbose_air = false;
var verbose_intern_pool = false; var verbose_intern_pool = false;
var verbose_generic_instances = false; var verbose_generic_instances = false;
@ -4954,7 +4954,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
} }
const work_around_btrfs_bug = native_os == .linux and const work_around_btrfs_bug = native_os == .linux and
try EnvVar.ZIG_BTRFS_WORKAROUND.isSet(arena); EnvVar.ZIG_BTRFS_WORKAROUND.isSet();
const root_prog_node = std.Progress.start(.{ const root_prog_node = std.Progress.start(.{
.disable_printing = (color == .off), .disable_printing = (color == .off),
.root_name = "Compile Build Script", .root_name = "Compile Build Script",
@ -5461,7 +5461,7 @@ fn jitCmd(
fatal("unable to find self exe path: {s}", .{@errorName(err)}); fatal("unable to find self exe path: {s}", .{@errorName(err)});
}; };
const optimize_mode: std.builtin.OptimizeMode = if (try EnvVar.ZIG_DEBUG_CMD.isSet(arena)) const optimize_mode: std.builtin.OptimizeMode = if (EnvVar.ZIG_DEBUG_CMD.isSet())
.Debug .Debug
else else
.ReleaseFast; .ReleaseFast;
@ -6976,7 +6976,7 @@ fn cmdFetch(
const color: Color = .auto; const color: Color = .auto;
const work_around_btrfs_bug = native_os == .linux and const work_around_btrfs_bug = native_os == .linux and
try EnvVar.ZIG_BTRFS_WORKAROUND.isSet(arena); EnvVar.ZIG_BTRFS_WORKAROUND.isSet();
var opt_path_or_url: ?[]const u8 = null; var opt_path_or_url: ?[]const u8 = null;
var override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena); var override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena);
var debug_hash: bool = false; var debug_hash: bool = false;