zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit de8c4cd64e0599abda0a0c5e1187391352020478 (tree)
parent f612464331a3f878bce2da284960bab349090c00
Author: Andrew Kelley <andrew@ziglang.org>
Date:   Wed, 31 Dec 2025 16:59:31 -0800

compiler: update to new std.process APIs

Diffstat:
Mlib/std/http/Client.zig | 19+++++++++----------
Mlib/std/process/Args.zig | 16++++++++--------
Mlib/std/process/Environ.zig | 2+-
Mlib/std/start.zig | 7+++++--
Mlib/std/zig.zig | 9+++++++++
Mlib/std/zig/LibCDirs.zig | 14+++++++++++---
Mlib/std/zig/LibCInstallation.zig | 25+++++++++++++------------
Mlib/std/zig/WindowsSdk.zig | 13++++++-------
Mlib/std/zig/system/NativePaths.zig | 35++++++++++++++++-------------------
Msrc/Compilation.zig | 38+++++++++++++++++++++++++-------------
Msrc/introspect.zig | 22+++++++++-------------
Msrc/link/Lld.zig | 53++++++++++++++++++++++++++++-------------------------
Msrc/main.zig | 444+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Msrc/print_env.zig | 10++++++----
14 files changed, 386 insertions(+), 321 deletions(-)

diff --git a/lib/std/http/Client.zig b/lib/std/http/Client.zig @@ -1307,7 +1307,7 @@ pub fn deinit(client: *Client) void { /// Asserts the client has no active connections. /// Uses `arena` for a few small allocations that must outlive the client, or /// at least until those fields are set to different values. -pub fn initDefaultProxies(client: *Client, arena: Allocator) !void { +pub fn initDefaultProxies(client: *Client, arena: Allocator, env_map: *std.process.Environ.Map) !void { // Prevent any new connections from being created. client.connection_pool.mutex.lock(); defer client.connection_pool.mutex.unlock(); @@ -1315,27 +1315,26 @@ pub fn initDefaultProxies(client: *Client, arena: Allocator) !void { assert(client.connection_pool.used.first == null); // There are active requests. if (client.http_proxy == null) { - client.http_proxy = try createProxyFromEnvVar(arena, &.{ + client.http_proxy = try createProxyFromEnvVar(arena, env_map, &.{ "http_proxy", "HTTP_PROXY", "all_proxy", "ALL_PROXY", }); } if (client.https_proxy == null) { - client.https_proxy = try createProxyFromEnvVar(arena, &.{ + client.https_proxy = try createProxyFromEnvVar(arena, env_map, &.{ "https_proxy", "HTTPS_PROXY", "all_proxy", "ALL_PROXY", }); } } -fn createProxyFromEnvVar(arena: Allocator, env_var_names: []const []const u8) !?*Proxy { +fn createProxyFromEnvVar( + arena: Allocator, + env_map: *std.process.Environ.Map, + env_var_names: []const []const u8, +) !?*Proxy { const content = for (env_var_names) |name| { - const content = std.process.getEnvVarOwned(arena, name) catch |err| switch (err) { - error.EnvironmentVariableNotFound => continue, - else => |e| return e, - }; - + const content = env_map.get(name) orelse continue; if (content.len == 0) continue; - break content; } else return null; diff --git a/lib/std/process/Args.zig b/lib/std/process/Args.zig @@ -516,7 +516,7 @@ pub fn freeSlice(gpa: Allocator, to_slice_result: []const [:0]u8) void { } test "Iterator.Windows" { - const t = testArgIteratorWindows; + const t = testIteratorWindows; try t( \\"C:\Program Files\zig\zig.exe" run .\src\main.zig -target x86_64-windows-gnu -O ReleaseSafe -- --emoji=🗿 --eval="new Regex(\"Dwayne \\\"The Rock\\\" Johnson\")" @@ -648,7 +648,7 @@ test "Iterator.Windows" { try t("foo.exe \"\xed\xa0\x81\"\xed\xb0\xb7", &.{ "foo.exe", "𐐷" }); } -fn testArgIteratorWindows(cmd_line: []const u8, expected_args: []const []const u8) !void { +fn testIteratorWindows(cmd_line: []const u8, expected_args: []const []const u8) !void { const cmd_line_w = try std.unicode.wtf8ToWtf16LeAllocZ(testing.allocator, cmd_line); defer testing.allocator.free(cmd_line_w); @@ -679,7 +679,7 @@ fn testArgIteratorWindows(cmd_line: []const u8, expected_args: []const []const u } } -test "general arg parsing" { +test "general parsing" { try testGeneralCmdLine("a b\tc d", &.{ "a", "b", "c", "d" }); try testGeneralCmdLine("\"abc\" d e", &.{ "abc", "d", "e" }); try testGeneralCmdLine("a\\\\\\b d\"e f\"g h", &.{ "a\\\\\\b", "de fg", "h" }); @@ -703,7 +703,7 @@ test "general arg parsing" { } fn testGeneralCmdLine(input_cmd_line: []const u8, expected_args: []const []const u8) !void { - var it = try ArgIteratorGeneral(.{}).init(std.testing.allocator, input_cmd_line); + var it = try IteratorGeneral(.{}).init(std.testing.allocator, input_cmd_line); defer it.deinit(); for (expected_args) |expected_arg| { const arg = it.next().?; @@ -712,14 +712,14 @@ fn testGeneralCmdLine(input_cmd_line: []const u8, expected_args: []const []const try testing.expect(it.next() == null); } -/// Optional parameters for `ArgIteratorGeneral` -pub const ArgIteratorGeneralOptions = struct { +/// Optional parameters for `IteratorGeneral` +pub const IteratorGeneralOptions = struct { comments: bool = false, single_quotes: bool = false, }; /// A general Iterator to parse a string into a set of arguments -pub fn ArgIteratorGeneral(comptime options: ArgIteratorGeneralOptions) type { +pub fn IteratorGeneral(comptime options: IteratorGeneralOptions) type { return struct { allocator: Allocator, index: usize = 0, @@ -947,7 +947,7 @@ test "response file arg parsing" { } fn testResponseFileCmdLine(input_cmd_line: []const u8, expected_args: []const []const u8) !void { - var it = try ArgIteratorGeneral(.{ .comments = true, .single_quotes = true }) + var it = try IteratorGeneral(.{ .comments = true, .single_quotes = true }) .init(std.testing.allocator, input_cmd_line); defer it.deinit(); for (expected_args) |expected_arg| { diff --git a/lib/std/process/Environ.zig b/lib/std/process/Environ.zig @@ -20,7 +20,7 @@ const mem = std.mem; block: Block, pub const Block = switch (native_os) { - .windows => []const u16, + .windows => [*:0]const u16, .wasi => switch (builtin.link_libc) { false => void, true => [:null]const ?[*:0]const u8, diff --git a/lib/std/start.zig b/lib/std/start.zig @@ -524,9 +524,12 @@ fn WinStartup() callconv(.withStackAlign(.c, 1)) noreturn { std.debug.maybeEnableSegfaultHandler(); + const peb = std.os.windows.peb(); + const cmd_line = std.os.windows.peb().ProcessParameters.CommandLine; + std.os.windows.ntdll.RtlExitUserProcess(callMain( - std.os.windows.peb().ProcessParameters.CommandLine, - std.os.windows.peb().ProcessParameters.Environment, + cmd_line.Buffer.?[0..@divExact(cmd_line.Length, 2)], + peb.ProcessParameters.Environment, )); } diff --git a/lib/std/zig.zig b/lib/std/zig.zig @@ -741,9 +741,18 @@ pub const EnvVar = enum { ZIG_DEBUG_CMD, ZIG_IS_DETECTING_LIBC_PATHS, ZIG_IS_TRYING_TO_NOT_CALL_ITSELF, + + NIX_CFLAGS_COMPILE, + NIX_CFLAGS_LINK, + NIX_LDFLAGS, + C_INCLUDE_PATH, + CPLUS_INCLUDE_PATH, + LIBRARY_PATH, CC, + NO_COLOR, CLICOLOR_FORCE, + XDG_CACHE_HOME, LOCALAPPDATA, HOME, diff --git a/lib/std/zig/LibCDirs.zig b/lib/std/zig/LibCDirs.zig @@ -28,6 +28,7 @@ pub fn detect( is_native_abi: bool, link_libc: bool, libc_installation: ?*const LibCInstallation, + env_map: *const std.process.Environ.Map, ) LibCInstallation.FindError!LibCDirs { if (!link_libc) { return .{ @@ -47,7 +48,10 @@ pub fn detect( // using the system libc installation. if (is_native_abi and !target.isMinGW()) { const libc = try arena.create(LibCInstallation); - libc.* = LibCInstallation.findNative(arena, io, .{ .target = target }) catch |err| switch (err) { + libc.* = LibCInstallation.findNative(arena, io, .{ + .target = target, + .env_map = env_map, + }) catch |err| switch (err) { error.CCompilerExitCode, error.CCompilerCrashed, error.CCompilerCannotFindHeaders, @@ -84,12 +88,16 @@ pub fn detect( if (use_system_abi) { const libc = try arena.create(LibCInstallation); - libc.* = try LibCInstallation.findNative(arena, io, .{ .verbose = true, .target = target }); + libc.* = try LibCInstallation.findNative(arena, io, .{ + .verbose = true, + .target = target, + .env_map = env_map, + }); return detectFromInstallation(arena, target, libc); } return .{ - .libc_include_dir_list = &[0][]u8{}, + .libc_include_dir_list = &.{}, .libc_installation = null, .libc_framework_dir_list = &.{}, .sysroot = null, diff --git a/lib/std/zig/LibCInstallation.zig b/lib/std/zig/LibCInstallation.zig @@ -167,6 +167,7 @@ pub fn render(self: LibCInstallation, out: *std.Io.Writer) !void { pub const FindNativeOptions = struct { target: *const std.Target, + env_map: *const std.process.Environ.Map, /// If enabled, will print human-friendly errors to stderr. verbose: bool = false, @@ -238,10 +239,7 @@ pub fn deinit(self: *LibCInstallation, allocator: Allocator) void { fn findNativeIncludeDirPosix(self: *LibCInstallation, gpa: Allocator, io: Io, args: FindNativeOptions) FindError!void { // Detect infinite loops. - var env_map = std.process.getEnvMap(gpa) catch |err| switch (err) { - error.Unexpected => unreachable, // WASI-only - else => |e| return e, - }; + var env_map = try args.env_map.clone(gpa); defer env_map.deinit(); const skip_cc_env_var = if (env_map.get(inf_loop_env_key)) |phase| blk: { if (std.mem.eql(u8, phase, "1")) { @@ -260,7 +258,7 @@ fn findNativeIncludeDirPosix(self: *LibCInstallation, gpa: Allocator, io: Io, ar var argv = std.array_list.Managed([]const u8).init(gpa); defer argv.deinit(); - try appendCcExe(&argv, skip_cc_env_var); + try appendCcExe(&argv, skip_cc_env_var, &env_map); try argv.appendSlice(&.{ "-E", "-Wp,-v", @@ -449,6 +447,7 @@ fn findNativeCrtDirWindows( fn findNativeCrtDirPosix(self: *LibCInstallation, gpa: Allocator, io: Io, args: FindNativeOptions) FindError!void { self.crt_dir = try ccPrintFileName(gpa, io, .{ + .env_map = args.env_map, .search_basename = switch (args.target.os.tag) { .linux => if (args.target.abi.isAndroid()) "crtbegin_dynamic.o" else "crt1.o", else => "crt1.o", @@ -553,6 +552,7 @@ fn findNativeMsvcLibDir( } pub const CCPrintFileNameOptions = struct { + env_map: *const std.process.Environ.Map, search_basename: []const u8, want_dirname: enum { full_path, only_dir }, verbose: bool = false, @@ -561,10 +561,7 @@ pub const CCPrintFileNameOptions = struct { /// caller owns returned memory fn ccPrintFileName(gpa: Allocator, io: Io, args: CCPrintFileNameOptions) ![:0]u8 { // Detect infinite loops. - var env_map = std.process.getEnvMap(gpa) catch |err| switch (err) { - error.Unexpected => unreachable, // WASI-only - else => |e| return e, - }; + var env_map = try args.env_map.clone(gpa); defer env_map.deinit(); const skip_cc_env_var = if (env_map.get(inf_loop_env_key)) |phase| blk: { if (std.mem.eql(u8, phase, "1")) { @@ -584,7 +581,7 @@ fn ccPrintFileName(gpa: Allocator, io: Io, args: CCPrintFileNameOptions) ![:0]u8 const arg1 = try std.fmt.allocPrint(gpa, "-print-file-name={s}", .{args.search_basename}); defer gpa.free(arg1); - try appendCcExe(&argv, skip_cc_env_var); + try appendCcExe(&argv, skip_cc_env_var, &env_map); try argv.append(arg1); const run_res = std.process.run(gpa, io, .{ @@ -672,14 +669,18 @@ fn fillInstallations( const inf_loop_env_key = "ZIG_IS_DETECTING_LIBC_PATHS"; -fn appendCcExe(args: *std.array_list.Managed([]const u8), skip_cc_env_var: bool) !void { +fn appendCcExe( + args: *std.array_list.Managed([]const u8), + skip_cc_env_var: bool, + env_map: *const std.process.Environ.Map, +) !void { const default_cc_exe = if (is_windows) "cc.exe" else "cc"; try args.ensureUnusedCapacity(1); if (skip_cc_env_var) { args.appendAssumeCapacity(default_cc_exe); return; } - const cc_env_var = std.zig.EnvVar.CC.getPosix() orelse { + const cc_env_var = std.zig.EnvVar.CC.get(env_map) orelse { args.appendAssumeCapacity(default_cc_exe); return; }; diff --git a/lib/std/zig/WindowsSdk.zig b/lib/std/zig/WindowsSdk.zig @@ -951,15 +951,14 @@ const MsvcLibDir = struct { return msvc_dir; } - fn findViaVs7Key(gpa: Allocator, io: Io, arch: std.Target.Cpu.Arch) error{ OutOfMemory, PathNotFound }![]const u8 { + fn findViaVs7Key( + gpa: Allocator, + io: Io, + arch: std.Target.Cpu.Arch, + env_map: *const std.process.Environ.Map, + ) error{ OutOfMemory, PathNotFound }![]const u8 { var base_path: std.array_list.Managed(u8) = base_path: { try_env: { - var env_map = std.process.getEnvMap(gpa) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - else => break :try_env, - }; - defer env_map.deinit(); - if (env_map.get("VS140COMNTOOLS")) |VS140COMNTOOLS| { if (VS140COMNTOOLS.len < "C:\\Common7\\Tools".len) break :try_env; if (!Dir.path.isAbsolute(VS140COMNTOOLS)) break :try_env; diff --git a/lib/std/zig/system/NativePaths.zig b/lib/std/zig/system/NativePaths.zig @@ -14,10 +14,16 @@ framework_dirs: std.ArrayList([]const u8) = .empty, rpaths: std.ArrayList([]const u8) = .empty, warnings: std.ArrayList([]const u8) = .empty, -pub fn detect(arena: Allocator, io: Io, native_target: *const std.Target) !NativePaths { +pub fn detect( + arena: Allocator, + io: Io, + native_target: *const std.Target, + env_map: *process.Environ.Map, +) !NativePaths { var self: NativePaths = .{ .arena = arena }; var is_nix = false; - if (process.getEnvVarOwned(arena, "NIX_CFLAGS_COMPILE")) |nix_cflags_compile| { + + if (std.zig.EnvVar.NIX_CFLAGS_COMPILE.get(env_map)) |nix_cflags_compile| { is_nix = true; var it = mem.tokenizeScalar(u8, nix_cflags_compile, ' '); while (true) { @@ -41,12 +47,9 @@ pub fn detect(arena: Allocator, io: Io, native_target: *const std.Target) !Nativ try self.addWarningFmt("Unrecognized C flag from NIX_CFLAGS_COMPILE: {s}", .{word}); } } - } else |err| switch (err) { - error.InvalidWtf8 => unreachable, - error.EnvironmentVariableNotFound => {}, - error.OutOfMemory => |e| return e, } - if (process.getEnvVarOwned(arena, "NIX_LDFLAGS")) |nix_ldflags| { + + if (std.zig.EnvVar.NIX_LDFLAGS.get(env_map)) |nix_ldflags| { is_nix = true; var it = mem.tokenizeScalar(u8, nix_ldflags, ' '); while (true) { @@ -73,12 +76,9 @@ pub fn detect(arena: Allocator, io: Io, native_target: *const std.Target) !Nativ break; } } - } else |err| switch (err) { - error.InvalidWtf8 => unreachable, - error.EnvironmentVariableNotFound => {}, - error.OutOfMemory => |e| return e, } - if (process.getEnvVarOwned(arena, "NIX_CFLAGS_LINK")) |nix_cflags_link| { + + if (std.zig.EnvVar.NIX_CFLAGS_LINK.get(env_map)) |nix_cflags_link| { is_nix = true; var it = mem.tokenizeScalar(u8, nix_cflags_link, ' '); while (true) { @@ -105,11 +105,8 @@ pub fn detect(arena: Allocator, io: Io, native_target: *const std.Target) !Nativ break; } } - } else |err| switch (err) { - error.InvalidWtf8 => unreachable, - error.EnvironmentVariableNotFound => {}, - error.OutOfMemory => |e| return e, } + if (is_nix) { return self; } @@ -182,21 +179,21 @@ pub fn detect(arena: Allocator, io: Io, native_target: *const std.Target) !Nativ // variables to search for headers and libraries. // We use os.getenv here since this part won't be executed on // windows, to get rid of unnecessary error handling. - if (std.posix.getenv("C_INCLUDE_PATH")) |c_include_path| { + if (std.zig.EnvVar.C_INCLUDE_PATH.get(env_map)) |c_include_path| { var it = mem.tokenizeScalar(u8, c_include_path, ':'); while (it.next()) |dir| { try self.addIncludeDir(dir); } } - if (std.posix.getenv("CPLUS_INCLUDE_PATH")) |cplus_include_path| { + if (std.zig.EnvVar.CPLUS_INCLUDE_PATH.get(env_map)) |cplus_include_path| { var it = mem.tokenizeScalar(u8, cplus_include_path, ':'); while (it.next()) |dir| { try self.addIncludeDir(dir); } } - if (std.posix.getenv("LIBRARY_PATH")) |library_path| { + if (std.zig.EnvVar.LIBRARY_PATH.get(env_map)) |library_path| { var it = mem.tokenizeScalar(u8, library_path, ':'); while (it.next()) |dir| { try self.addLibDir(dir); diff --git a/src/Compilation.zig b/src/Compilation.zig @@ -54,6 +54,7 @@ gpa: Allocator, /// threads at once. arena: Allocator, io: Io, +environ_map: *std.process.Environ.Map, thread_limit: usize, /// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`. zcu: ?*Zcu, @@ -761,6 +762,7 @@ pub const Directories = struct { .wasi => void, else => []const u8, }, + env_map: *std.process.Environ.Map, ) Directories { const wasi = builtin.target.os.tag == .wasi; @@ -779,7 +781,7 @@ pub const Directories = struct { const global_cache: Cache.Directory = d: { if (override_global_cache) |path| break :d openUnresolved(arena, io, cwd, path, .@"global cache"); if (wasi) break :d openWasiPreopen(wasi_preopens, "/cache"); - const path = introspect.resolveGlobalCacheDir(arena) catch |err| { + const path = introspect.resolveGlobalCacheDir(arena, env_map) catch |err| { fatal("unable to resolve zig cache directory: {t}", .{err}); }; break :d openUnresolved(arena, io, cwd, path, .@"global cache"); @@ -1797,6 +1799,8 @@ pub const CreateOptions = struct { parent_whole_cache: ?ParentWholeCache = null, + environ_map: *std.process.Environ.Map, + pub const Entry = link.File.OpenOptions.Entry; /// Which fields are valid depends on the `cache_mode` given. @@ -1967,6 +1971,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, options.root_mod.resolved_target.is_native_abi, link_libc, options.libc_installation, + options.environ_map, ) catch |err| switch (err) { error.OutOfMemory => |e| return e, // Every other error is specifically related to finding the native installation @@ -2306,6 +2311,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, .emit_llvm_ir = try options.emit_llvm_ir.resolve(arena, &options, .llvm_ir), .emit_llvm_bc = try options.emit_llvm_bc.resolve(arena, &options, .llvm_bc), .emit_docs = try options.emit_docs.resolve(arena, &options, .docs), + .environ_map = options.environ_map, }; errdefer { @@ -5503,6 +5509,7 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) SubU .verbose_llvm_bc = comp.verbose_llvm_bc, .verbose_cimport = comp.verbose_cimport, .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, + .environ_map = comp.environ_map, }) catch |err| switch (err) { error.CreateFail => { comp.lockAndSetMiscFailure(.docs_wasm, "sub-compilation of docs_wasm failed: {f}", .{sub_create_diag}); @@ -5705,6 +5712,7 @@ pub fn translateC( translated_basename: []const u8, owner_mod: *Package.Module, prog_node: std.Progress.Node, + env_map: *std.process.Environ.Map, ) !CImportResult { dev.check(.translate_c_command); @@ -5774,7 +5782,7 @@ pub fn translateC( } var stdout: []u8 = undefined; - try @import("main.zig").translateC(gpa, arena, io, argv.items, prog_node, &stdout); + try @import("main.zig").translateC(gpa, arena, io, argv.items, env_map, prog_node, &stdout); if (out_dep_path) |dep_file_path| add_deps: { if (comp.verbose_cimport) log.info("processing dep file at {s}", .{dep_file_path}); @@ -5861,7 +5869,8 @@ pub fn cImport( defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - break :result try comp.translateC( + break :result try translateC( + comp, arena, &man, .c, @@ -5869,6 +5878,7 @@ pub fn cImport( translated_basename, owner_mod, prog_node, + comp.environ_map, ); }; @@ -6741,15 +6751,16 @@ fn spawnZigRc( var node_name: std.ArrayList(u8) = .empty; defer node_name.deinit(arena); - var child = std.process.Child.init(argv, arena); - child.stdin_behavior = .ignore; - child.stdout_behavior = .pipe; - child.stderr_behavior = .pipe; - child.progress_node = child_progress_node; - - child.spawn(io) catch |err| { - return comp.failWin32Resource(win32_resource, "unable to spawn {s} rc: {t}", .{ argv[0], err }); - }; + var child = std.process.spawn(io, .{ + .argv = argv, + .stdin = .ignore, + .stdout = .pipe, + .stderr = .pipe, + .progress_node = child_progress_node, + }) catch |err| return comp.failWin32Resource(win32_resource, "unable to spawn {s} rc: {t}", .{ + argv[0], err, + }); + defer child.kill(io); var poller = std.Io.poll(comp.gpa, enum { stdout, stderr }, .{ .stdout = child.stdout.?, @@ -6781,7 +6792,7 @@ fn spawnZigRc( const stderr = poller.reader(.stderr); const term = child.wait(io) catch |err| { - return comp.failWin32Resource(win32_resource, "unable to wait for {s} rc: {s}", .{ argv[0], @errorName(err) }); + return comp.failWin32Resource(win32_resource, "unable to wait for {s} rc: {t}", .{ argv[0], err }); }; switch (term) { @@ -7963,6 +7974,7 @@ fn buildOutputFromZig( .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .clang_passthrough_mode = comp.clang_passthrough_mode, .skip_linker_dependencies = true, + .environ_map = comp.environ_map, }) catch |err| switch (err) { error.CreateFail => { comp.lockAndSetMiscFailure(misc_task_tag, "sub-compilation of {t} failed: {f}", .{ misc_task_tag, sub_create_diag }); diff --git a/src/introspect.zig b/src/introspect.zig @@ -101,31 +101,27 @@ pub fn findZigLibDirFromSelfExe( return error.FileNotFound; } -/// Caller owns returned memory. -pub fn resolveGlobalCacheDir(gpa: Allocator) ![]u8 { - if (try std.zig.EnvVar.ZIG_GLOBAL_CACHE_DIR.get(gpa)) |value| return value; +pub fn resolveGlobalCacheDir(arena: Allocator, env_map: *std.process.Environ.Map) ![]const u8 { + if (std.zig.EnvVar.ZIG_GLOBAL_CACHE_DIR.get(env_map)) |value| return value; const app_name = "zig"; switch (builtin.os.tag) { .wasi => @compileError("on WASI the global cache dir must be resolved with preopens"), .windows => { - const local_app_data_dir = (std.zig.EnvVar.LOCALAPPDATA.get(gpa) catch |err| switch (err) { - error.OutOfMemory => |e| return e, - error.InvalidWtf8 => return error.AppDataDirUnavailable, - }) orelse return error.AppDataDirUnavailable; - defer gpa.free(local_app_data_dir); - return Dir.path.join(gpa, &.{ local_app_data_dir, app_name }); + const local_app_data_dir = std.zig.EnvVar.LOCALAPPDATA.get(env_map) orelse + return error.AppDataDirUnavailable; + return Dir.path.join(arena, &.{ local_app_data_dir, app_name }); }, else => { - if (std.zig.EnvVar.XDG_CACHE_HOME.getPosix()) |cache_root| { + if (std.zig.EnvVar.XDG_CACHE_HOME.get(env_map)) |cache_root| { if (cache_root.len > 0) { - return Dir.path.join(gpa, &.{ cache_root, app_name }); + return Dir.path.join(arena, &.{ cache_root, app_name }); } } - if (std.zig.EnvVar.HOME.getPosix()) |home| { + if (std.zig.EnvVar.HOME.get(env_map)) |home| { if (home.len > 0) { - return Dir.path.join(gpa, &.{ home, ".cache", app_name }); + return Dir.path.join(arena, &.{ home, ".cache", app_name }); } } return error.AppDataDirUnavailable; diff --git a/src/link/Lld.zig b/src/link/Lld.zig @@ -1604,19 +1604,24 @@ fn spawnLld(comp: *Compilation, arena: Allocator, argv: []const []const u8) !voi var stderr: []u8 = &.{}; defer gpa.free(stderr); - var child = std.process.Child.init(argv, arena); + // TODO rework this awkward logic to call child.kill() in the failure case const term = (if (comp.clang_passthrough_mode) term: { - child.stdin_behavior = .inherit; - child.stdout_behavior = .inherit; - child.stderr_behavior = .inherit; + var child = std.process.spawn(io, .{ + .argv = argv, + .stdin = .inherit, + .stdout = .inherit, + .stderr = .inherit, + }) catch |err| break :term err; - break :term child.spawnAndWait(io); + break :term child.wait(io); } else term: { - child.stdin_behavior = .ignore; - child.stdout_behavior = .ignore; - child.stderr_behavior = .pipe; + var child = std.process.spawn(io, .{ + .argv = argv, + .stdin = .ignore, + .stdout = .ignore, + .stderr = .pipe, + }) catch |err| break :term err; - child.spawn(io) catch |err| break :term err; var stderr_reader = child.stderr.?.readerStreaming(io, &.{}); stderr = try stderr_reader.interface.allocRemaining(gpa, .unlimited); break :term child.wait(io); @@ -1650,23 +1655,21 @@ fn spawnLld(comp: *Compilation, arena: Allocator, argv: []const []const u8) !voi try rsp_writer.flush(); } - var rsp_child = std.process.Child.init(&.{ argv[0], argv[1], try std.fmt.allocPrint( - arena, - "@{s}", - .{try comp.dirs.local_cache.join(arena, &.{rsp_path})}, - ) }, arena); + var rsp_child = std.process.spawn(io, .{ + .argv = &.{ + argv[0], + argv[1], + try std.fmt.allocPrint(arena, "@{s}", .{ + try comp.dirs.local_cache.join(arena, &.{rsp_path}), + }), + }, + .stdin = if (comp.clang_passthrough_mode) .inherit else .ignore, + .stdout = if (comp.clang_passthrough_mode) .inherit else .ignore, + .stderr = if (comp.clang_passthrough_mode) .inherit else .pipe, + }) catch |err| break :err err; if (comp.clang_passthrough_mode) { - rsp_child.stdin_behavior = .inherit; - rsp_child.stdout_behavior = .inherit; - rsp_child.stderr_behavior = .inherit; - - break :term rsp_child.spawnAndWait(io) catch |err| break :err err; + break :term rsp_child.wait(io) catch |err| break :err err; } else { - rsp_child.stdin_behavior = .ignore; - rsp_child.stdout_behavior = .ignore; - rsp_child.stderr_behavior = .pipe; - - rsp_child.spawn(io) catch |err| break :err err; var stderr_reader = rsp_child.stderr.?.readerStreaming(io, &.{}); stderr = try stderr_reader.interface.allocRemaining(gpa, .unlimited); break :term rsp_child.wait(io) catch |err| break :err err; @@ -1674,7 +1677,7 @@ fn spawnLld(comp: *Compilation, arena: Allocator, argv: []const []const u8) !voi }, else => first_err, }; - log.err("unable to spawn LLD {s}: {s}", .{ argv[0], @errorName(err) }); + log.err("unable to spawn LLD {s}: {t}", .{ argv[0], err }); return error.UnableToSpawnSelf; }; diff --git a/src/main.zig b/src/main.zig @@ -42,7 +42,6 @@ test { const thread_stack_size = 60 << 20; pub const std_options: std.Options = .{ - .wasiCwd = wasi_cwd, .logFn = log, .log_level = switch (builtin.mode) { @@ -51,6 +50,7 @@ pub const std_options: std.Options = .{ .ReleaseSmall => .err, }, }; +pub const std_options_cwd = if (native_os == .wasi) wasi_cwd else null; pub const panic = crash_report.panic; pub const debug = crash_report.debug; @@ -208,7 +208,15 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const [:0]const u8, env_ma fatal("expected command argument", .{}); } - if (process.can_replace and std.zig.EnvVar.ZIG_IS_DETECTING_LIBC_PATHS.isSet(env_map)) { + var threaded: Io.Threaded = .init(gpa, .{ + .argv0 = if (@hasField(Io.Threaded.Argv0, "value")) .{ .value = args[0] } else .{}, + }); + defer threaded.deinit(); + threaded_impl_ptr = &threaded; + threaded.stack_size = thread_stack_size; + const io = threaded.io(); + + if (process.can_replace and EnvVar.ZIG_IS_DETECTING_LIBC_PATHS.isSet(env_map)) { dev.check(.cc_command); // 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 @@ -217,7 +225,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const [:0]const u8, env_ma // 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. - const inf_loop_env_key: std.zig.EnvVar = .ZIG_IS_TRYING_TO_NOT_CALL_ITSELF; + const inf_loop_env_key: EnvVar = .ZIG_IS_TRYING_TO_NOT_CALL_ITSELF; if (inf_loop_env_key.isSet(env_map)) { fatal("{s}", .{ "The compilation links against libc, but Zig is unable to provide a libc " ++ @@ -233,42 +241,34 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const [:0]const u8, env_ma // 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 process.replace(.{ .argv = args[1..], .env_map = env_map }); + return process.replace(io, .{ .argv = args[1..], .env_map = env_map }); } else { const modified_args = try arena.dupe([]const u8, args); modified_args[0] = "cc"; - return process.replace(.{ .argv = modified_args, .env_map = env_map }); + return process.replace(io, .{ .argv = modified_args, .env_map = env_map }); } } - var threaded: Io.Threaded = .init(gpa, .{ - .argv0 = if (@hasField(Io.Threaded.Argv0, "value")) .{ .value = args[0] } else .{}, - }); - defer threaded.deinit(); - threaded_impl_ptr = &threaded; - threaded.stack_size = thread_stack_size; - const io = threaded.io(); - const cmd = args[1]; const cmd_args = args[2..]; if (mem.eql(u8, cmd, "build-exe")) { dev.check(.build_exe_command); - return buildOutputType(gpa, arena, io, args, .{ .build = .Exe }); + return buildOutputType(gpa, arena, io, args, .{ .build = .Exe }, env_map); } else if (mem.eql(u8, cmd, "build-lib")) { dev.check(.build_lib_command); - return buildOutputType(gpa, arena, io, args, .{ .build = .Lib }); + return buildOutputType(gpa, arena, io, args, .{ .build = .Lib }, env_map); } else if (mem.eql(u8, cmd, "build-obj")) { dev.check(.build_obj_command); - return buildOutputType(gpa, arena, io, args, .{ .build = .Obj }); + return buildOutputType(gpa, arena, io, args, .{ .build = .Obj }, env_map); } else if (mem.eql(u8, cmd, "test")) { dev.check(.test_command); - return buildOutputType(gpa, arena, io, args, .zig_test); + return buildOutputType(gpa, arena, io, args, .zig_test, env_map); } else if (mem.eql(u8, cmd, "test-obj")) { dev.check(.test_command); - return buildOutputType(gpa, arena, io, args, .zig_test_obj); + return buildOutputType(gpa, arena, io, args, .zig_test_obj, env_map); } else if (mem.eql(u8, cmd, "run")) { dev.check(.run_command); - return buildOutputType(gpa, arena, io, args, .run); + return buildOutputType(gpa, arena, io, args, .run, env_map); } else if (mem.eql(u8, cmd, "dlltool") or mem.eql(u8, cmd, "ranlib") or mem.eql(u8, cmd, "lib") or @@ -278,7 +278,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const [:0]const u8, env_ma return process.exit(try llvmArMain(arena, args)); } else if (mem.eql(u8, cmd, "build")) { dev.check(.build_command); - return cmdBuild(gpa, arena, io, cmd_args); + return cmdBuild(gpa, arena, io, cmd_args, env_map); } else if (mem.eql(u8, cmd, "clang") or mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as")) { @@ -292,16 +292,16 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const [:0]const u8, env_ma return process.exit(try lldMain(arena, args, true)); } else if (mem.eql(u8, cmd, "cc")) { dev.check(.cc_command); - return buildOutputType(gpa, arena, io, args, .cc); + return buildOutputType(gpa, arena, io, args, .cc, env_map); } else if (mem.eql(u8, cmd, "c++")) { dev.check(.cc_command); - return buildOutputType(gpa, arena, io, args, .cpp); + return buildOutputType(gpa, arena, io, args, .cpp, env_map); } else if (mem.eql(u8, cmd, "translate-c")) { dev.check(.translate_c_command); - return buildOutputType(gpa, arena, io, args, .translate_c); + return buildOutputType(gpa, arena, io, args, .translate_c, env_map); } else if (mem.eql(u8, cmd, "rc")) { const use_server = cmd_args.len > 0 and std.mem.eql(u8, cmd_args[0], "--zig-integration"); - return jitCmd(gpa, arena, io, cmd_args, .{ + return jitCmd(gpa, arena, io, cmd_args, env_map, .{ .cmd_name = "resinator", .root_src_path = "resinator/main.zig", .depend_on_aro = true, @@ -312,20 +312,20 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const [:0]const u8, env_ma dev.check(.fmt_command); return @import("fmt.zig").run(gpa, arena, io, cmd_args); } else if (mem.eql(u8, cmd, "objcopy")) { - return jitCmd(gpa, arena, io, cmd_args, .{ + return jitCmd(gpa, arena, io, cmd_args, env_map, .{ .cmd_name = "objcopy", .root_src_path = "objcopy.zig", }); } else if (mem.eql(u8, cmd, "fetch")) { - return cmdFetch(gpa, arena, io, cmd_args); + return cmdFetch(gpa, arena, io, cmd_args, env_map); } else if (mem.eql(u8, cmd, "libc")) { - return jitCmd(gpa, arena, io, cmd_args, .{ + return jitCmd(gpa, arena, io, cmd_args, env_map, .{ .cmd_name = "libc", .root_src_path = "libc.zig", .prepend_zig_lib_dir_path = true, }); } else if (mem.eql(u8, cmd, "std")) { - return jitCmd(gpa, arena, io, cmd_args, .{ + return jitCmd(gpa, arena, io, cmd_args, env_map, .{ .cmd_name = "std", .root_src_path = "std-docs.zig", .prepend_zig_lib_dir_path = true, @@ -355,10 +355,11 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const [:0]const u8, env_ma args, if (native_os == .wasi) wasi_preopens, &host, + env_map, ); return stdout_writer.interface.flush(); } else if (mem.eql(u8, cmd, "reduce")) { - return jitCmd(gpa, arena, io, cmd_args, .{ + return jitCmd(gpa, arena, io, cmd_args, env_map, .{ .cmd_name = "reduce", .root_src_path = "reduce.zig", }); @@ -803,6 +804,7 @@ fn buildOutputType( io: Io, all_args: []const []const u8, arg_mode: ArgMode, + env_map: *process.Environ.Map, ) !void { var provided_name: ?[]const u8 = null; var root_src_file: ?[]const u8 = null; @@ -815,9 +817,9 @@ fn buildOutputType( var debug_compile_errors = false; var debug_incremental = false; var verbose_link = (native_os != .wasi or builtin.link_libc) and - EnvVar.ZIG_VERBOSE_LINK.isSet(); + EnvVar.ZIG_VERBOSE_LINK.isSet(env_map); var verbose_cc = (native_os != .wasi or builtin.link_libc) and - EnvVar.ZIG_VERBOSE_CC.isSet(); + EnvVar.ZIG_VERBOSE_CC.isSet(env_map); var verbose_air = false; var verbose_intern_pool = false; var verbose_generic_instances = false; @@ -889,9 +891,9 @@ fn buildOutputType( var runtime_args_start: ?usize = null; var test_filters: std.ArrayList([]const u8) = .empty; var test_runner_path: ?[]const u8 = null; - 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 override_local_cache_dir: ?[]const u8 = EnvVar.ZIG_LOCAL_CACHE_DIR.get(env_map); + var override_global_cache_dir: ?[]const u8 = EnvVar.ZIG_GLOBAL_CACHE_DIR.get(env_map); + var override_lib_dir: ?[]const u8 = EnvVar.ZIG_LIB_DIR.get(env_map); var clang_preprocessor_mode: Compilation.ClangPreprocessorMode = .no; var subsystem: ?std.zig.Subsystem = null; var major_subsystem_version: ?u16 = null; @@ -988,7 +990,7 @@ fn buildOutputType( .framework_dirs = .{}, .rpath_list = .{}, .each_lib_rpath = null, - .libc_paths_file = try EnvVar.ZIG_LIBC.get(arena), + .libc_paths_file = EnvVar.ZIG_LIBC.get(env_map), .native_system_include_paths = &.{}, }; defer create_module.link_inputs.deinit(gpa); @@ -997,9 +999,9 @@ fn buildOutputType( // if set, default the color setting to .off or .on, respectively // explicit --color arguments will still override this setting. // Disable color on WASI per https://github.com/WebAssembly/WASI/issues/162 - var color: Color = if (native_os == .wasi or EnvVar.NO_COLOR.isSet()) + var color: Color = if (native_os == .wasi or EnvVar.NO_COLOR.isSet(env_map)) .off - else if (EnvVar.CLICOLOR_FORCE.isSet()) + else if (EnvVar.CLICOLOR_FORCE.isSet(env_map)) .on else .auto; @@ -3097,6 +3099,7 @@ fn buildOutputType( }, if (native_os == .wasi) wasi_preopens, self_exe_path, + env_map, ); defer dirs.deinit(io); @@ -3108,7 +3111,7 @@ fn buildOutputType( create_module.opts.emit_bin = emit_bin != .no; create_module.opts.any_c_source_files = create_module.c_source_files.items.len != 0; - const main_mod = try createModule(gpa, arena, io, &create_module, 0, null, color); + const main_mod = try createModule(gpa, arena, io, &create_module, 0, null, color, env_map); for (create_module.modules.keys(), create_module.modules.values()) |key, cli_mod| { if (cli_mod.resolved == null) fatal("module '{s}' declared but not used", .{key}); @@ -3585,6 +3588,7 @@ fn buildOutputType( .global_cc_argv = try cc_argv.toOwnedSlice(arena), .file_system_inputs = &file_system_inputs, .debug_compiler_runtime_libs = debug_compiler_runtime_libs, + .environ_map = env_map, }) catch |err| switch (err) { error.CreateFail => switch (create_diag) { .cross_libc_unavailable => { @@ -3648,6 +3652,7 @@ fn buildOutputType( arg_mode, all_args, runtime_args_start, + env_map, ); return cleanExit(io); }, @@ -3674,6 +3679,7 @@ fn buildOutputType( arg_mode, all_args, runtime_args_start, + env_map, ); return cleanExit(io); }, @@ -3686,7 +3692,7 @@ fn buildOutputType( defer root_prog_node.end(); if (arg_mode == .translate_c) { - return cmdTranslateC(comp, arena, null, null, root_prog_node); + return cmdTranslateC(comp, arena, null, null, root_prog_node, env_map); } updateModule(comp, color, root_prog_node) catch |err| switch (err) { @@ -3754,6 +3760,7 @@ fn buildOutputType( all_args, runtime_args_start, create_module.resolved_options.link_libc, + env_map, ); } @@ -3809,6 +3816,7 @@ fn createModule( index: usize, parent: ?*Package.Module, color: std.zig.Color, + env_map: *process.Environ.Map, ) Allocator.Error!*Package.Module { const cli_mod = &create_module.modules.values()[index]; if (cli_mod.resolved) |m| return m; @@ -3988,7 +3996,7 @@ fn createModule( resolved_target.is_native_os and resolved_target.is_native_abi and create_module.want_native_include_dirs) { - var paths = std.zig.system.NativePaths.detect(arena, io, target) catch |err| + var paths = std.zig.system.NativePaths.detect(arena, io, target, env_map) catch |err| fatal("unable to detect native system paths: {t}", .{err}); for (paths.warnings.items) |warning| { warn("{s}", .{warning}); @@ -4015,6 +4023,7 @@ fn createModule( create_module.libc_installation = LibCInstallation.findNative(arena, io, .{ .verbose = true, .target = target, + .env_map = env_map, }) catch |err| { fatal("unable to find native libc installation: {t}", .{err}); }; @@ -4119,7 +4128,7 @@ fn createModule( for (cli_mod.deps) |dep| { const dep_index = create_module.modules.getIndex(dep.value) orelse fatal("module '{s}' depends on non-existent module '{s}'", .{ name, dep.key }); - const dep_mod = try createModule(gpa, arena, io, create_module, dep_index, mod, color); + const dep_mod = try createModule(gpa, arena, io, create_module, dep_index, mod, color, env_map); try mod.deps.put(arena, dep.key, dep_mod); } @@ -4128,9 +4137,7 @@ fn createModule( fn saveState(comp: *Compilation, incremental: bool) void { if (incremental) { - comp.saveState() catch |err| { - warn("unable to save incremental compilation state: {s}", .{@errorName(err)}); - }; + comp.saveState() catch |err| warn("unable to save incremental compilation state: {t}", .{err}); } } @@ -4143,6 +4150,7 @@ fn serve( arg_mode: ArgMode, all_args: []const []const u8, runtime_args_start: ?usize, + env_map: *process.Environ.Map, ) !void { const gpa = comp.gpa; const io = comp.io; @@ -4190,7 +4198,7 @@ fn serve( defer arena_instance.deinit(); const arena = arena_instance.allocator(); var output: Compilation.CImportResult = undefined; - try cmdTranslateC(comp, arena, &output, file_system_inputs, main_progress_node); + try cmdTranslateC(comp, arena, &output, file_system_inputs, main_progress_node, env_map); defer output.deinit(gpa); if (file_system_inputs.items.len != 0) { @@ -4390,6 +4398,7 @@ fn runOrTest( all_args: []const []const u8, runtime_args_start: ?usize, link_libc: bool, + env_map: *process.Environ.Map, ) !void { const raw_emit_bin = comp.emit_bin orelse return; const exe_path = switch (comp.cache_use) { @@ -4426,77 +4435,90 @@ fn runOrTest( if (runtime_args_start) |i| { try argv.appendSlice(all_args[i..]); } - 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 (process.can_replace and arg_mode == .run) { - // execv releases the locks; no need to destroy the Compilation here. + // process replacement releases the locks; no need to destroy the Compilation here. _ = try io.lockStderr(&.{}, .no_color); - const err = process.execve(gpa, argv.items, &env_map); + const err = process.replace(io, .{ .argv = argv.items, .env_map = env_map }); io.unlockStderr(); try warnAboutForeignBinaries(io, arena, arg_mode, target, link_libc); const cmd = try std.mem.join(arena, " ", argv.items); fatal("the following command failed to execve with '{t}':\n{s}", .{ err, cmd }); - } else if (process.can_spawn) { - var child = std.process.Child.init(argv.items, gpa); - child.env_map = &env_map; - child.stdin_behavior = .inherit; - child.stdout_behavior = .inherit; - child.stderr_behavior = .inherit; - + } else if (!process.can_spawn) { + const cmd = try std.mem.join(arena, " ", argv.items); + fatal("the following command cannot be executed ({t} does not support spawning a child process):\n{s}", .{ + native_os, cmd, + }); + } + const term_result = (term: { // Here we release all the locks associated with the Compilation so // that whatever this child process wants to do won't deadlock. comp.destroy(); comp_destroyed.* = true; - const term_result = t: { - _ = try io.lockStderr(&.{}, .no_color); - defer io.unlockStderr(); - break :t child.spawnAndWait(io); - }; - const term = term_result catch |err| { - try warnAboutForeignBinaries(io, arena, arg_mode, target, link_libc); - const cmd = try std.mem.join(arena, " ", argv.items); - fatal("the following command failed with '{s}':\n{s}", .{ @errorName(err), cmd }); - }; - switch (arg_mode) { - .run, .build => { - switch (term) { - .Exited => |code| { - if (code == 0) { - return cleanExit(io); - } else { - process.exit(code); - } - }, - else => { - process.exit(1); - }, - } - }, - .zig_test => { - switch (term) { - .Exited => |code| { - if (code == 0) { - return cleanExit(io); - } else { - const cmd = try std.mem.join(arena, " ", argv.items); - fatal("the following test command failed with exit code {d}:\n{s}", .{ code, cmd }); - } - }, - else => { - const cmd = try std.mem.join(arena, " ", argv.items); - fatal("the following test command crashed:\n{s}", .{cmd}); - }, - } - }, - else => unreachable, - } - } else { + _ = try io.lockStderr(&.{}, .no_color); + defer io.unlockStderr(); + + var child = std.process.spawn(io, .{ + .argv = argv.items, + .env_map = env_map, + .stdin = .inherit, + .stdout = .inherit, + .stderr = .inherit, + }) catch |err| break :term err; + defer child.kill(io); + + break :term child.wait(io); + }); + + const term = term_result catch |err| { + try warnAboutForeignBinaries(io, arena, arg_mode, target, link_libc); const cmd = try std.mem.join(arena, " ", argv.items); - fatal("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(native_os), cmd }); + fatal("the following command failed with {t}:\n{s}", .{ err, cmd }); + }; + switch (arg_mode) { + .run, .build => { + switch (term) { + .exited => |code| { + if (code == 0) { + return cleanExit(io); + } else { + process.exit(code); + } + }, + .signal => |sig| { + const cmd = try std.mem.join(arena, " ", argv.items); + fatal("the following command terminated with signal {t}:\n{s}", .{ sig, cmd }); + }, + else => { + process.exit(1); + }, + } + }, + .zig_test => { + switch (term) { + .exited => |code| { + if (code == 0) { + return cleanExit(io); + } else { + const cmd = try std.mem.join(arena, " ", argv.items); + fatal("the following test command failed with exit code {d}:\n{s}", .{ code, cmd }); + } + }, + .signal => |sig| { + const cmd = try std.mem.join(arena, " ", argv.items); + fatal("the following test command terminated with signal {t}:\n{s}", .{ sig, cmd }); + }, + else => { + const cmd = try std.mem.join(arena, " ", argv.items); + fatal("the following test command crashed:\n{s}", .{cmd}); + }, + } + }, + else => unreachable, } } @@ -4559,13 +4581,13 @@ fn runOrTestHotSwap( try argv.appendSlice(all_args[i..]); } - var child = try std.process.spwan(io, .{ + var child = try std.process.spawn(io, .{ .argv = argv.items, .stdin = .inherit, .stdout = .inherit, .stderr = .inherit, }); - return child.id; + return child.id.?; } const UpdateModuleError = Compilation.UpdateError || error{ @@ -4597,6 +4619,7 @@ fn cmdTranslateC( fancy_output: ?*Compilation.CImportResult, file_system_inputs: ?*std.ArrayList(u8), prog_node: std.Progress.Node, + env_map: *process.Environ.Map, ) !void { dev.check(.translate_c_command); @@ -4630,6 +4653,7 @@ fn cmdTranslateC( translated_basename, comp.root_mod, prog_node, + env_map, ); if (result.errors.errorMessageCount() != 0) { @@ -4677,10 +4701,11 @@ pub fn translateC( arena: Allocator, io: Io, argv: []const []const u8, + env_map: *process.Environ.Map, prog_node: std.Progress.Node, capture: ?*[]u8, ) !void { - try jitCmd(gpa, arena, io, argv, .{ + try jitCmd(gpa, arena, io, argv, env_map, .{ .cmd_name = "translate-c", .root_src_path = "translate-c/main.zig", .depend_on_aro = true, @@ -4837,21 +4862,21 @@ test sanitizeExampleName { try std.testing.expectEqualStrings("test_project", try sanitizeExampleName(arena, "test project")); } -fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) !void { +fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8, env_map: *process.Environ.Map) !void { dev.check(.build_command); var build_file: ?[]const u8 = null; - 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 override_lib_dir: ?[]const u8 = EnvVar.ZIG_LIB_DIR.get(env_map); + var override_global_cache_dir: ?[]const u8 = EnvVar.ZIG_GLOBAL_CACHE_DIR.get(env_map); + var override_local_cache_dir: ?[]const u8 = EnvVar.ZIG_LOCAL_CACHE_DIR.get(env_map); + var override_build_runner: ?[]const u8 = EnvVar.ZIG_BUILD_RUNNER.get(env_map); var child_argv = std.array_list.Managed([]const u8).init(arena); var reference_trace: ?u32 = null; var debug_compile_errors = false; var verbose_link = (native_os != .wasi or builtin.link_libc) and - EnvVar.ZIG_VERBOSE_LINK.isSet(); + EnvVar.ZIG_VERBOSE_LINK.isSet(env_map); var verbose_cc = (native_os != .wasi or builtin.link_libc) and - EnvVar.ZIG_VERBOSE_CC.isSet(); + EnvVar.ZIG_VERBOSE_CC.isSet(env_map); var verbose_air = false; var verbose_intern_pool = false; var verbose_generic_instances = false; @@ -5048,7 +5073,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) } const work_around_btrfs_bug = native_os == .linux and - EnvVar.ZIG_BTRFS_WORKAROUND.isSet(); + EnvVar.ZIG_BTRFS_WORKAROUND.isSet(env_map); const root_prog_node = std.Progress.start(io, .{ .disable_printing = (color == .off), .root_name = "Compile Build Script", @@ -5108,6 +5133,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) } }, {}, self_exe_path, + env_map, ); defer dirs.deinit(io); @@ -5210,7 +5236,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) job_queue.read_only = true; cleanup_build_dir = job_queue.global_cache.handle; } else { - try http_client.initDefaultProxies(arena); + try http_client.initDefaultProxies(arena, env_map); } try job_queue.all_fetches.ensureUnusedCapacity(gpa, 1); @@ -5364,6 +5390,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) .cache_mode = .whole, .reference_trace = reference_trace, .debug_compile_errors = debug_compile_errors, + .environ_map = env_map, }) catch |err| switch (err) { error.CreateFail => fatal("failed to create compilation: {f}", .{create_diag}), else => fatal("failed to create compilation: {s}", .{@errorName(err)}), @@ -5385,81 +5412,81 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) }); } - if (process.can_spawn) { - var child = std.process.Child.init(child_argv.items, gpa); - child.stdin_behavior = .inherit; - child.stdout_behavior = .inherit; - child.stderr_behavior = .inherit; - - const term = t: { - _ = try io.lockStderr(&.{}, .no_color); - defer io.unlockStderr(); - break :t child.spawnAndWait(io) catch |err| - fatal("failed to spawn build runner {s}: {t}", .{ child_argv.items[0], err }); - }; - - switch (term) { - .Exited => |code| { - if (code == 0) return cleanExit(io); - // Indicates that the build runner has reported compile errors - // and this parent process does not need to report any further - // diagnostics. - if (code == 2) process.exit(2); - - if (code == 3) { - if (!dev.env.supports(.fetch_command)) process.exit(3); - // Indicates the configure phase failed due to missing lazy - // dependencies and stdout contains the hashes of the ones - // that are missing. - const s = fs.path.sep_str; - const tmp_sub_path = "tmp" ++ s ++ results_tmp_file_nonce; - const stdout = dirs.local_cache.handle.readFileAlloc(io, tmp_sub_path, arena, .limited(50 * 1024 * 1024)) catch |err| { - fatal("unable to read results of configure phase from '{f}{s}': {s}", .{ - dirs.local_cache, tmp_sub_path, @errorName(err), + if (!process.can_spawn) { + const cmd = try std.mem.join(arena, " ", child_argv.items); + fatal("the following command cannot be executed ({t} does not support spawning a child process):\n{s}", .{ native_os, cmd }); + } + switch (term: { + _ = try io.lockStderr(&.{}, .no_color); + defer io.unlockStderr(); + var child = std.process.spawn(io, .{ + .argv = child_argv.items, + }) catch |err| fatal("failed to spawn build runner {s}: {t}", .{ child_argv.items[0], err }); + defer child.kill(io); + break :term child.wait(io) catch |err| + fatal("failed to wait build runner {s}: {t}", .{ child_argv.items[0], err }); + }) { + .exited => |code| { + if (code == 0) return cleanExit(io); + // Indicates that the build runner has reported compile errors + // and this parent process does not need to report any further + // diagnostics. + if (code == 2) process.exit(2); + + if (code == 3) { + if (!dev.env.supports(.fetch_command)) process.exit(3); + // Indicates the configure phase failed due to missing lazy + // dependencies and stdout contains the hashes of the ones + // that are missing. + const s = fs.path.sep_str; + const tmp_sub_path = "tmp" ++ s ++ results_tmp_file_nonce; + const stdout = dirs.local_cache.handle.readFileAlloc(io, tmp_sub_path, arena, .limited(50 * 1024 * 1024)) catch |err| { + fatal("unable to read results of configure phase from '{f}{s}': {t}", .{ + dirs.local_cache, tmp_sub_path, err, + }); + }; + dirs.local_cache.handle.deleteFile(io, tmp_sub_path) catch {}; + + var it = mem.splitScalar(u8, stdout, '\n'); + var any_errors = false; + while (it.next()) |hash| { + if (hash.len == 0) continue; + if (hash.len > Package.Hash.max_len) { + std.log.err("invalid digest (length {d} exceeds maximum): '{s}'", .{ + hash.len, hash, }); - }; - dirs.local_cache.handle.deleteFile(io, tmp_sub_path) catch {}; - - var it = mem.splitScalar(u8, stdout, '\n'); - var any_errors = false; - while (it.next()) |hash| { - if (hash.len == 0) continue; - if (hash.len > Package.Hash.max_len) { - std.log.err("invalid digest (length {d} exceeds maximum): '{s}'", .{ - hash.len, hash, - }); - any_errors = true; - continue; - } - try unlazy_set.put(arena, .fromSlice(hash), {}); + any_errors = true; + continue; } - if (any_errors) process.exit(3); - if (system_pkg_dir_path) |p| { - // In this mode, the system needs to provide these packages; they - // cannot be fetched by Zig. - for (unlazy_set.keys()) |*hash| { - std.log.err("lazy dependency package not found: {s}" ++ s ++ "{s}", .{ - p, hash.toSlice(), - }); - } - std.log.info("remote package fetching disabled due to --system mode", .{}); - std.log.info("dependencies might be avoidable depending on build configuration", .{}); - process.exit(3); + try unlazy_set.put(arena, .fromSlice(hash), {}); + } + if (any_errors) process.exit(3); + if (system_pkg_dir_path) |p| { + // In this mode, the system needs to provide these packages; they + // cannot be fetched by Zig. + for (unlazy_set.keys()) |*hash| { + std.log.err("lazy dependency package not found: {s}" ++ s ++ "{s}", .{ + p, hash.toSlice(), + }); } - continue; + std.log.info("remote package fetching disabled due to --system mode", .{}); + std.log.info("dependencies might be avoidable depending on build configuration", .{}); + process.exit(3); } + continue; + } - const cmd = try std.mem.join(arena, " ", child_argv.items); - fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd }); - }, - else => { - const cmd = try std.mem.join(arena, " ", child_argv.items); - fatal("the following build command crashed:\n{s}", .{cmd}); - }, - } - } else { - const cmd = try std.mem.join(arena, " ", child_argv.items); - fatal("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(native_os), cmd }); + const cmd = try std.mem.join(arena, " ", child_argv.items); + fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd }); + }, + .signal => |sig| { + const cmd = try std.mem.join(arena, " ", child_argv.items); + fatal("the following build command terminated with signal {t}:\n{s}", .{ sig, cmd }); + }, + else => { + const cmd = try std.mem.join(arena, " ", child_argv.items); + fatal("the following build command crashed:\n{s}", .{cmd}); + }, } } } @@ -5482,6 +5509,7 @@ fn jitCmd( arena: Allocator, io: Io, args: []const []const u8, + env_map: *process.Environ.Map, options: JitCmdOptions, ) !void { dev.check(.jit_command); @@ -5503,13 +5531,13 @@ fn jitCmd( const self_exe_path = process.executablePathAlloc(io, arena) catch |err| fatal("unable to find self exe path: {t}", .{err}); - const optimize_mode: std.builtin.OptimizeMode = if (EnvVar.ZIG_DEBUG_CMD.isSet()) + const optimize_mode: std.builtin.OptimizeMode = if (EnvVar.ZIG_DEBUG_CMD.isSet(env_map)) .Debug else .ReleaseFast; const strip = optimize_mode != .Debug; - const override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena); - const override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena); + const override_lib_dir: ?[]const u8 = EnvVar.ZIG_LIB_DIR.get(env_map); + const override_global_cache_dir: ?[]const u8 = EnvVar.ZIG_GLOBAL_CACHE_DIR.get(env_map); // This `init` calls `fatal` on error. var dirs: Compilation.Directories = .init( @@ -5520,6 +5548,7 @@ fn jitCmd( .global, if (native_os == .wasi) wasi_preopens, self_exe_path, + env_map, ); defer dirs.deinit(io); @@ -5593,6 +5622,7 @@ fn jitCmd( .self_exe_path = self_exe_path, .thread_limit = thread_limit, .cache_mode = .whole, + .environ_map = env_map, }) catch |err| switch (err) { error.CreateFail => fatal("failed to create compilation: {f}", .{create_diag}), else => fatal("failed to create compilation: {s}", .{@errorName(err)}), @@ -5639,31 +5669,33 @@ fn jitCmd( child_argv.appendSliceAssumeCapacity(args); if (process.can_replace and options.capture == null) { - if (EnvVar.ZIG_DEBUG_CMD.isSet()) { + if (EnvVar.ZIG_DEBUG_CMD.isSet(env_map)) { const cmd = try std.mem.join(arena, " ", child_argv.items); std.debug.print("{s}\n", .{cmd}); } - const err = process.execv(gpa, child_argv.items); + const err = process.replace(io, .{ .argv = child_argv.items, .env_map = env_map }); const cmd = try std.mem.join(arena, " ", child_argv.items); fatal("the following command failed to execve with '{t}':\n{s}", .{ err, cmd }); } if (!process.can_spawn) { const cmd = try std.mem.join(arena, " ", child_argv.items); - fatal("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ - @tagName(native_os), cmd, + fatal("the following command cannot be executed ({t} does not support spawning a child process):\n{s}", .{ + native_os, cmd, }); } - var child = std.process.Child.init(child_argv.items, gpa); - child.stdin_behavior = .inherit; - child.stdout_behavior = if (options.capture == null) .inherit else .pipe; - child.stderr_behavior = .inherit; - - const term = t: { + switch (t: { _ = try io.lockStderr(&.{}, .no_color); defer io.unlockStderr(); - try child.spawn(io); + + var child = std.process.spawn(io, .{ + .argv = child_argv.items, + .stdin = .inherit, + .stdout = if (options.capture == null) .inherit else .pipe, + .stderr = .inherit, + }) catch |err| fatal("failed to spawn {s}: {t}", .{ child_argv.items[0], err }); + defer child.kill(io); if (options.capture) |ptr| { var stdout_reader = child.stdout.?.readerStreaming(io, &.{}); @@ -5671,9 +5703,8 @@ fn jitCmd( } break :t try child.wait(io); - }; - switch (term) { - .Exited => |code| { + }) { + .exited => |code| { if (code == 0) { if (options.capture != null) return; return cleanExit(io); @@ -5681,6 +5712,10 @@ fn jitCmd( const cmd = try std.mem.join(arena, " ", child_argv.items); fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd }); }, + .signal => |sig| { + const cmd = try std.mem.join(arena, " ", child_argv.items); + fatal("the following build command terminated with signal {t}:\n{s}", .{ sig, cmd }); + }, else => { const cmd = try std.mem.join(arena, " ", child_argv.items); fatal("the following build command crashed:\n{s}", .{cmd}); @@ -5796,7 +5831,7 @@ pub fn lldMain( return @intFromBool(!ok); } -const ArgIteratorResponseFile = process.ArgIteratorGeneral(.{ .comments = true, .single_quotes = true }); +const ArgIteratorResponseFile = process.Args.IteratorGeneral(.{ .comments = true, .single_quotes = true }); /// Initialize the arguments from a Response File. "*.rsp" fn initArgIteratorResponseFile(allocator: Allocator, io: Io, resp_file_path: []const u8) !ArgIteratorResponseFile { @@ -6872,14 +6907,15 @@ fn cmdFetch( arena: Allocator, io: Io, args: []const []const u8, + env_map: *process.Environ.Map, ) !void { dev.check(.fetch_command); const color: Color = .auto; const work_around_btrfs_bug = native_os == .linux and - EnvVar.ZIG_BTRFS_WORKAROUND.isSet(); + EnvVar.ZIG_BTRFS_WORKAROUND.isSet(env_map); 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 = EnvVar.ZIG_GLOBAL_CACHE_DIR.get(env_map); var debug_hash: bool = false; var save: union(enum) { no, @@ -6925,7 +6961,7 @@ fn cmdFetch( var http_client: std.http.Client = .{ .allocator = gpa, .io = io }; defer http_client.deinit(); - try http_client.initDefaultProxies(arena); + try http_client.initDefaultProxies(arena, env_map); var root_prog_node = std.Progress.start(io, .{ .root_name = "Fetch", @@ -6933,7 +6969,7 @@ fn cmdFetch( defer root_prog_node.end(); var global_cache_directory: Directory = l: { - const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena); + const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena, env_map); break :l .{ .handle = try Io.Dir.cwd().createDirPathOpen(io, p, .{}), .path = p, diff --git a/src/print_env.zig b/src/print_env.zig @@ -19,9 +19,10 @@ pub fn cmdEnv( else => void, }, host: *const std.Target, + env_map: *std.process.Environ.Map, ) !void { - const override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena); - const override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena); + const override_lib_dir: ?[]const u8 = EnvVar.ZIG_LIB_DIR.get(env_map); + const override_global_cache_dir: ?[]const u8 = EnvVar.ZIG_GLOBAL_CACHE_DIR.get(env_map); const self_exe_path = switch (builtin.target.os.tag) { .wasi => args[0], @@ -38,6 +39,7 @@ pub fn cmdEnv( .global, if (builtin.target.os.tag == .wasi) wasi_preopens, if (builtin.target.os.tag != .wasi) self_exe_path, + env_map, ); defer dirs.deinit(io); @@ -56,8 +58,8 @@ pub fn cmdEnv( try root.field("version", build_options.version, .{}); try root.field("target", triple, .{}); var env = try root.beginStructField("env", .{}); - inline for (@typeInfo(std.zig.EnvVar).@"enum".fields) |field| { - try env.field(field.name, try @field(std.zig.EnvVar, field.name).get(arena), .{}); + inline for (@typeInfo(EnvVar).@"enum".fields) |field| { + try env.field(field.name, @field(EnvVar, field.name).get(env_map), .{}); } try env.end(); try root.end();