rework zig env
Introduce introspect.EnvVar which tracks all the environment variables that are observed by the compiler, so that we can print them with `zig env`. The `zig env` command now prints both the resolved values as well as all the possibly observed environment variables.
This commit is contained in:
@@ -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));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
64
src/main.zig
64
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;
|
||||
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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)?
|
||||
|
||||
@@ -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, ";");
|
||||
|
||||
Reference in New Issue
Block a user