diff --git a/src/introspect.zig b/src/introspect.zig index 2eeae956ec..2cd19b798f 100644 --- a/src/introspect.zig +++ b/src/introspect.zig @@ -4,6 +4,7 @@ const mem = std.mem; const os = std.os; const fs = std.fs; const Compilation = @import("Compilation.zig"); +const build_options = @import("build_options"); /// Returns the sub_path that worked, or `null` if none did. /// The path of the returned Directory is relative to `base`. @@ -80,23 +81,17 @@ pub fn findZigLibDirFromSelfExe( /// Caller owns returned memory. pub fn resolveGlobalCacheDir(allocator: mem.Allocator) ![]u8 { - if (builtin.os.tag == .wasi) { + if (builtin.os.tag == .wasi) @compileError("on WASI the global cache dir must be resolved with preopens"); - } - if (std.process.getEnvVarOwned(allocator, "ZIG_GLOBAL_CACHE_DIR")) |value| { - if (value.len > 0) { - return value; - } else { - allocator.free(value); - } - } else |_| {} + + if (try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(allocator)) |value| return value; const appname = "zig"; if (builtin.os.tag != .windows) { - if (std.os.getenv("XDG_CACHE_HOME")) |cache_root| { + if (EnvVar.XDG_CACHE_HOME.getPosix()) |cache_root| { return fs.path.join(allocator, &[_][]const u8{ cache_root, appname }); - } else if (std.os.getenv("HOME")) |home| { + } else if (EnvVar.HOME.getPosix()) |home| { return fs.path.join(allocator, &[_][]const u8{ home, ".cache", appname }); } } @@ -146,3 +141,42 @@ pub fn resolvePath( pub fn isUpDir(p: []const u8) bool { return mem.startsWith(u8, p, "..") and (p.len == 2 or p[2] == fs.path.sep); } + +/// Collects all the environment variables that Zig could possibly inspect, so +/// that we can do reflection on this and print them with `zig env`. +pub const EnvVar = enum { + ZIG_GLOBAL_CACHE_DIR, + ZIG_LOCAL_CACHE_DIR, + ZIG_LIB_DIR, + ZIG_LIBC, + ZIG_BUILD_RUNNER, + ZIG_VERBOSE_LINK, + ZIG_VERBOSE_CC, + ZIG_BTRFS_WORKAROUND, + CC, + NO_COLOR, + XDG_CACHE_HOME, + HOME, + /// https://github.com/ziglang/zig/issues/17585 + INCLUDE, + + pub fn isSet(comptime ev: EnvVar) bool { + return std.process.hasEnvVarConstant(@tagName(ev)); + } + + pub fn get(ev: EnvVar, arena: mem.Allocator) !?[]u8 { + // Env vars aren't used in the bootstrap stage. + if (build_options.only_c) return null; + + if (std.process.getEnvVarOwned(arena, @tagName(ev))) |value| { + return value; + } else |err| switch (err) { + error.EnvironmentVariableNotFound => return null, + else => |e| return e, + } + } + + pub fn getPosix(comptime ev: EnvVar) ?[:0]const u8 { + return std.os.getenvZ(@tagName(ev)); + } +}; diff --git a/src/libc_installation.zig b/src/libc_installation.zig index c93377c321..1a64e0f8bc 100644 --- a/src/libc_installation.zig +++ b/src/libc_installation.zig @@ -11,6 +11,7 @@ const is_haiku = builtin.target.os.tag == .haiku; const log = std.log.scoped(.libc_installation); const ZigWindowsSDK = @import("windows_sdk.zig").ZigWindowsSDK; +const EnvVar = @import("introspect.zig").EnvVar; /// See the render function implementation for documentation of the fields. pub const LibCInstallation = struct { @@ -694,7 +695,7 @@ fn appendCcExe(args: *std.ArrayList([]const u8), skip_cc_env_var: bool) !void { args.appendAssumeCapacity(default_cc_exe); return; } - const cc_env_var = std.os.getenvZ("CC") orelse { + const cc_env_var = EnvVar.CC.getPosix() orelse { args.appendAssumeCapacity(default_cc_exe); return; }; diff --git a/src/main.zig b/src/main.zig index 6dd4fb0725..2b8aa37462 100644 --- a/src/main.zig +++ b/src/main.zig @@ -18,6 +18,7 @@ const link = @import("link.zig"); const Package = @import("Package.zig"); const build_options = @import("build_options"); const introspect = @import("introspect.zig"); +const EnvVar = introspect.EnvVar; const LibCInstallation = @import("libc_installation.zig").LibCInstallation; const wasi_libc = @import("wasi_libc.zig"); const BuildId = std.Build.CompileStep.BuildId; @@ -231,14 +232,14 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi fatal("expected command argument", .{}); } - if (std.process.can_execv and std.os.getenvZ("ZIG_IS_DETECTING_LIBC_PATHS") != null) { + if (process.can_execv and std.os.getenvZ("ZIG_IS_DETECTING_LIBC_PATHS") != null) { // In this case we have accidentally invoked ourselves as "the system C compiler" // to figure out where libc is installed. This is essentially infinite recursion // via child process execution due to the CC environment variable pointing to Zig. // Here we ignore the CC environment variable and exec `cc` as a child process. // However it's possible Zig is installed as *that* C compiler as well, which is // why we have this additional environment variable here to check. - var env_map = try std.process.getEnvMap(arena); + var env_map = try process.getEnvMap(arena); const inf_loop_env_key = "ZIG_IS_TRYING_TO_NOT_CALL_ITSELF"; if (env_map.get(inf_loop_env_key) != null) { @@ -254,11 +255,11 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi // CC environment variable. We detect and support this scenario here because of // the ZIG_IS_DETECTING_LIBC_PATHS environment variable. if (mem.eql(u8, args[1], "cc")) { - return std.process.execve(arena, args[1..], &env_map); + return process.execve(arena, args[1..], &env_map); } else { const modified_args = try arena.dupe([]const u8, args); modified_args[0] = "cc"; - return std.process.execve(arena, modified_args, &env_map); + return process.execve(arena, modified_args, &env_map); } } @@ -686,19 +687,6 @@ const Emit = union(enum) { } }; -fn optionalStringEnvVar(arena: Allocator, name: []const u8) !?[]const u8 { - // Env vars aren't used in the bootstrap stage. - if (build_options.only_c) { - return null; - } - if (std.process.getEnvVarOwned(arena, name)) |value| { - return value; - } else |err| switch (err) { - error.EnvironmentVariableNotFound => return null, - else => |e| return e, - } -} - const ArgMode = union(enum) { build: std.builtin.OutputMode, cc, @@ -797,8 +785,10 @@ fn buildOutputType( var no_builtin = false; var listen: Listen = .none; var debug_compile_errors = false; - var verbose_link = (builtin.os.tag != .wasi or builtin.link_libc) and std.process.hasEnvVarConstant("ZIG_VERBOSE_LINK"); - var verbose_cc = (builtin.os.tag != .wasi or builtin.link_libc) and std.process.hasEnvVarConstant("ZIG_VERBOSE_CC"); + var verbose_link = (builtin.os.tag != .wasi or builtin.link_libc) and + EnvVar.ZIG_VERBOSE_LINK.isSet(); + var verbose_cc = (builtin.os.tag != .wasi or builtin.link_libc) and + EnvVar.ZIG_VERBOSE_CC.isSet(); var verbose_air = false; var verbose_intern_pool = false; var verbose_generic_instances = false; @@ -892,15 +882,15 @@ fn buildOutputType( var each_lib_rpath: ?bool = null; var build_id: ?BuildId = null; var sysroot: ?[]const u8 = null; - var libc_paths_file: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LIBC"); + var libc_paths_file: ?[]const u8 = try EnvVar.ZIG_LIBC.get(arena); var machine_code_model: std.builtin.CodeModel = .default; var runtime_args_start: ?usize = null; var test_filter: ?[]const u8 = null; var test_name_prefix: ?[]const u8 = null; var test_runner_path: ?[]const u8 = null; - var override_local_cache_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LOCAL_CACHE_DIR"); - var override_global_cache_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_GLOBAL_CACHE_DIR"); - var override_lib_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LIB_DIR"); + var override_local_cache_dir: ?[]const u8 = try EnvVar.ZIG_LOCAL_CACHE_DIR.get(arena); + var override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena); + var override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena); var main_mod_path: ?[]const u8 = null; var clang_preprocessor_mode: Compilation.ClangPreprocessorMode = .no; var subsystem: ?std.Target.SubSystem = null; @@ -960,7 +950,7 @@ fn buildOutputType( // if it exists, default the color setting to .off // explicit --color arguments will still override this setting. // Disable color on WASI per https://github.com/WebAssembly/WASI/issues/162 - color = if (builtin.os.tag == .wasi or std.process.hasEnvVarConstant("NO_COLOR")) .off else .auto; + color = if (builtin.os.tag == .wasi or EnvVar.NO_COLOR.isSet()) .off else .auto; switch (arg_mode) { .build, .translate_c, .zig_test, .run => { @@ -4059,18 +4049,18 @@ fn runOrTest( if (runtime_args_start) |i| { try argv.appendSlice(all_args[i..]); } - var env_map = try std.process.getEnvMap(arena); + var env_map = try process.getEnvMap(arena); try env_map.put("ZIG_EXE", self_exe_path); // We do not execve for tests because if the test fails we want to print // the error message and invocation below. - if (std.process.can_execv and arg_mode == .run) { + if (process.can_execv and arg_mode == .run) { // execv releases the locks; no need to destroy the Compilation here. - const err = std.process.execve(gpa, argv.items, &env_map); + const err = process.execve(gpa, argv.items, &env_map); try warnAboutForeignBinaries(arena, arg_mode, target_info, link_libc); const cmd = try std.mem.join(arena, " ", argv.items); fatal("the following command failed to execve with '{s}':\n{s}", .{ @errorName(err), cmd }); - } else if (std.process.can_spawn) { + } else if (process.can_spawn) { var child = std.ChildProcess.init(argv.items, gpa); child.env_map = &env_map; child.stdin_behavior = .Inherit; @@ -4489,7 +4479,7 @@ fn cmdRc(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { try stdout_writer.print("{s}\n\n", .{argv.items[argv.items.len - 1]}); } - if (std.process.can_spawn) { + if (process.can_spawn) { var result = std.ChildProcess.exec(.{ .allocator = gpa, .argv = argv.items, @@ -4922,7 +4912,7 @@ pub const usage_build = pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { const work_around_btrfs_bug = builtin.os.tag == .linux and - std.process.hasEnvVarConstant("ZIG_BTRFS_WORKAROUND"); + EnvVar.ZIG_BTRFS_WORKAROUND.isSet(); var color: Color = .auto; // We want to release all the locks before executing the child process, so we make a nice @@ -4931,10 +4921,10 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi const self_exe_path = try introspect.findZigExePath(arena); var build_file: ?[]const u8 = null; - var override_lib_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LIB_DIR"); - var override_global_cache_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_GLOBAL_CACHE_DIR"); - var override_local_cache_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LOCAL_CACHE_DIR"); - var override_build_runner: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_BUILD_RUNNER"); + var override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena); + var override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena); + var override_local_cache_dir: ?[]const u8 = try EnvVar.ZIG_LOCAL_CACHE_DIR.get(arena); + var override_build_runner: ?[]const u8 = try EnvVar.ZIG_BUILD_RUNNER.get(arena); var child_argv = std.ArrayList([]const u8).init(arena); var reference_trace: ?u32 = null; var debug_compile_errors = false; @@ -5292,7 +5282,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi break :argv child_argv.items; }; - if (std.process.can_spawn) { + if (process.can_spawn) { var child = std.ChildProcess.init(child_argv, gpa); child.stdin_behavior = .Inherit; child.stdout_behavior = .Inherit; @@ -7003,9 +6993,9 @@ fn cmdFetch( ) !void { const color: Color = .auto; const work_around_btrfs_bug = builtin.os.tag == .linux and - std.process.hasEnvVarConstant("ZIG_BTRFS_WORKAROUND"); + EnvVar.ZIG_BTRFS_WORKAROUND.isSet(); var opt_path_or_url: ?[]const u8 = null; - var override_global_cache_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_GLOBAL_CACHE_DIR"); + var override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena); var debug_hash: bool = false; { diff --git a/src/print_env.zig b/src/print_env.zig index fcfc3f3aeb..ca64889848 100644 --- a/src/print_env.zig +++ b/src/print_env.zig @@ -1,62 +1,52 @@ const std = @import("std"); -const mem = std.mem; const build_options = @import("build_options"); const introspect = @import("introspect.zig"); const Allocator = std.mem.Allocator; const fatal = @import("main.zig").fatal; -const Env = struct { - name: []const u8, - value: []const u8, -}; +pub fn cmdEnv(arena: Allocator, args: []const []const u8, stdout: std.fs.File.Writer) !void { + _ = args; + const self_exe_path = try introspect.findZigExePath(arena); -pub fn cmdEnv(gpa: Allocator, args: []const []const u8, stdout: std.fs.File.Writer) !void { - const self_exe_path = try introspect.findZigExePath(gpa); - defer gpa.free(self_exe_path); - - var zig_lib_directory = introspect.findZigLibDirFromSelfExe(gpa, self_exe_path) catch |err| { + var zig_lib_directory = introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| { fatal("unable to find zig installation directory: {s}\n", .{@errorName(err)}); }; - defer gpa.free(zig_lib_directory.path.?); defer zig_lib_directory.handle.close(); - const zig_std_dir = try std.fs.path.join(gpa, &[_][]const u8{ zig_lib_directory.path.?, "std" }); - defer gpa.free(zig_std_dir); + const zig_std_dir = try std.fs.path.join(arena, &[_][]const u8{ zig_lib_directory.path.?, "std" }); - const global_cache_dir = try introspect.resolveGlobalCacheDir(gpa); - defer gpa.free(global_cache_dir); + const global_cache_dir = try introspect.resolveGlobalCacheDir(arena); const info = try std.zig.system.NativeTargetInfo.detect(.{}); - const triple = try info.target.zigTriple(gpa); - defer gpa.free(triple); - - const envars: []Env = &[_]Env{ - .{ .name = "zig_exe", .value = self_exe_path }, - .{ .name = "lib_dir", .value = zig_lib_directory.path.? }, - .{ .name = "std_dir", .value = zig_std_dir }, - .{ .name = "global_cache_dir", .value = global_cache_dir }, - .{ .name = "version", .value = build_options.version }, - .{ .name = "target", .value = triple }, - }; + const triple = try info.target.zigTriple(arena); var bw = std.io.bufferedWriter(stdout); const w = bw.writer(); - if (args.len > 0) { - for (args) |name| { - for (envars) |env| { - if (mem.eql(u8, name, env.name)) { - try w.print("{s}\n", .{env.value}); - } - } + try w.print( + \\zig_exe={s} + \\lib_dir={s} + \\std_dir={s} + \\global_cache_dir={s} + \\version={s} + \\target={s} + \\ + , .{ + self_exe_path, + zig_lib_directory.path.?, + zig_std_dir, + global_cache_dir, + build_options.version, + triple, + }); + + inline for (@typeInfo(introspect.EnvVar).Enum.fields) |field| { + if (try @field(introspect.EnvVar, field.name).get(arena)) |value| { + try w.print("{s}={s}\n", .{ field.name, value }); + } else { + try w.print("{s}\n", .{field.name}); } - try bw.flush(); - - return; } - for (envars) |env| { - try w.print("{[name]s}=\"{[value]s}\"\n", env); - } try bw.flush(); } diff --git a/src/resinator/compile.zig b/src/resinator/compile.zig index d0c4172d2f..fb4a8a2432 100644 --- a/src/resinator/compile.zig +++ b/src/resinator/compile.zig @@ -28,6 +28,7 @@ const windows1252 = @import("windows1252.zig"); const lang = @import("lang.zig"); const code_pages = @import("code_pages.zig"); const errors = @import("errors.zig"); +const introspect = @import("../introspect.zig"); pub const CompileOptions = struct { cwd: std.fs.Dir, @@ -91,7 +92,7 @@ pub fn compile(allocator: Allocator, source: []const u8, writer: anytype, option // `catch unreachable` since `options.cwd` is expected to be a valid dir handle, so opening // a new handle to it should be fine as well. // TODO: Maybe catch and return an error instead - const cwd_dir = options.cwd.openDir(".", .{}) catch unreachable; + const cwd_dir = options.cwd.openDir(".", .{}) catch @panic("unable to open dir"); try search_dirs.append(.{ .dir = cwd_dir, .path = null }); for (options.extra_include_paths) |extra_include_path| { var dir = openSearchPathDir(options.cwd, extra_include_path) catch { @@ -110,7 +111,7 @@ pub fn compile(allocator: Allocator, source: []const u8, writer: anytype, option try search_dirs.append(.{ .dir = dir, .path = try allocator.dupe(u8, system_include_path) }); } if (!options.ignore_include_env_var) { - const INCLUDE = std.process.getEnvVarOwned(allocator, "INCLUDE") catch ""; + const INCLUDE = (introspect.EnvVar.INCLUDE.get(allocator) catch @panic("OOM")) orelse ""; defer allocator.free(INCLUDE); // TODO: Should this be platform-specific? How does windres/llvm-rc handle this (if at all)? diff --git a/src/resinator/preprocess.zig b/src/resinator/preprocess.zig index e831c8147c..9a214f1fad 100644 --- a/src/resinator/preprocess.zig +++ b/src/resinator/preprocess.zig @@ -1,6 +1,7 @@ const std = @import("std"); const Allocator = std.mem.Allocator; const cli = @import("cli.zig"); +const introspect = @import("../introspect.zig"); pub const IncludeArgs = struct { clang_target: ?[]const u8 = null, @@ -67,7 +68,7 @@ pub fn appendClangArgs(arena: Allocator, argv: *std.ArrayList([]const u8), optio } if (!options.ignore_include_env_var) { - const INCLUDE = std.process.getEnvVarOwned(arena, "INCLUDE") catch ""; + const INCLUDE = (introspect.EnvVar.INCLUDE.get(arena) catch @panic("OOM")) orelse ""; // TODO: Should this be platform-specific? How does windres/llvm-rc handle this (if at all)? var it = std.mem.tokenize(u8, INCLUDE, ";");