From 85fe35d246d0076d403fc5ee90f4434da7206fd5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 1 Jan 2026 20:39:16 -0800 Subject: [PATCH] compiler: fix -Denable-llvm compilation failures --- lib/std/Io/Threaded.zig | 11 ----- src/Compilation.zig | 93 +++++++++++++++++++++++------------------ src/introspect.zig | 2 +- src/libs/freebsd.zig | 2 + src/libs/glibc.zig | 2 + src/libs/libcxx.zig | 2 + src/libs/libtsan.zig | 1 + src/libs/libunwind.zig | 1 + src/libs/musl.zig | 1 + src/libs/netbsd.zig | 2 + src/main.zig | 4 +- 11 files changed, 67 insertions(+), 54 deletions(-) diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index 965266aee9..b6715d03b5 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -14279,17 +14279,6 @@ fn testArgvToCommandLineWindows(argv: []const []const u8, expected_cmd_line: []c try std.testing.expectEqualStrings(expected_cmd_line, cmd_line); } -/// Replaces the current process image with the executed process. If this -/// function succeeds, it does not return. -/// -/// This operation is not available on all targets. `can_execv` -/// -/// This function also uses the PATH environment variable to get the full path to the executable. -/// If `file` is an absolute path, this is the same as `execveZ`. -/// -/// Like `execvpeZ` except if `arg0_expand` is `.expand`, then `argv` is mutable, -/// and `argv[0]` is expanded to be the same absolute path that is passed to the execve syscall. -/// If this function returns with an error, `argv[0]` will be restored to the value it was when it was passed in. fn execvpeZ_expandArg0( arg0_expand: process.ArgExpansion, file: [*:0]const u8, diff --git a/src/Compilation.zig b/src/Compilation.zig index ac5df44316..d55f5ad48f 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -54,7 +54,7 @@ gpa: Allocator, /// threads at once. arena: Allocator, io: Io, -environ_map: *std.process.Environ.Map, +environ_map: *const 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, @@ -762,12 +762,12 @@ pub const Directories = struct { .wasi => void, else => []const u8, }, - env_map: *std.process.Environ.Map, + env_map: *const std.process.Environ.Map, ) Directories { const wasi = builtin.target.os.tag == .wasi; const cwd = introspect.getResolvedCwd(arena) catch |err| { - fatal("unable to get cwd: {s}", .{@errorName(err)}); + fatal("unable to get cwd: {t}", .{err}); }; const zig_lib: Cache.Directory = d: { @@ -1799,7 +1799,7 @@ pub const CreateOptions = struct { parent_whole_cache: ?ParentWholeCache = null, - environ_map: *std.process.Environ.Map, + environ_map: *const std.process.Environ.Map, pub const Entry = link.File.OpenOptions.Entry; @@ -5713,7 +5713,7 @@ pub fn translateC( translated_basename: []const u8, owner_mod: *Package.Module, prog_node: std.Progress.Node, - env_map: *std.process.Environ.Map, + env_map: *const std.process.Environ.Map, ) !CImportResult { dev.check(.translate_c_command); @@ -6260,7 +6260,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr // that we could "tail call" clang by doing an execve, and any use of // the caching system would actually be problematic since the user is // presumably doing their own caching by using dep file flags. - if (std.process.can_execv and direct_o and + if (std.process.can_replace and direct_o and comp.disable_c_depfile and comp.clang_passthrough_mode) { try comp.addCCArgs(arena, &argv, ext, null, c_object.src.owner); @@ -6292,8 +6292,8 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr try dumpArgv(io, argv.items); } - const err = std.process.execv(arena, argv.items); - fatal("unable to execv clang: {s}", .{@errorName(err)}); + const err = std.process.replace(io, .{ .argv = argv.items }); + fatal("unable to replace process with clang: {t}", .{err}); } // We can't know the digest until we do the C compiler invocation, @@ -6348,14 +6348,21 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr else => log.warn("failed to delete '{s}': {s}", .{ dep_file_path, @errorName(err) }), }; if (std.process.can_spawn) { - var child = std.process.Child.init(argv.items, arena); if (comp.clang_passthrough_mode) { - child.stdin_behavior = .inherit; - child.stdout_behavior = .inherit; - child.stderr_behavior = .inherit; - - const term = child.spawnAndWait(io) catch |err| { - return comp.failCObj(c_object, "failed to spawn zig clang (passthrough mode) {s}: {s}", .{ argv.items[0], @errorName(err) }); + var child = std.process.spawn(io, .{ + .argv = argv.items, + .stdin = .inherit, + .stdout = .inherit, + .stderr = .inherit, + }) catch |err| { + return comp.failCObj(c_object, "failed to spawn zig clang (passthrough mode) {s}: {t}", .{ + argv.items[0], err, + }); + }; + const term = child.wait(io) catch |err| { + return comp.failCObj(c_object, "failed to wait zig clang (passthrough mode) {s}: {t}", .{ + argv.items[0], err, + }); }; switch (term) { .exited => |code| { @@ -6367,36 +6374,41 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr }, else => std.process.abort(), } - } else { - child.stdin_behavior = .ignore; - child.stdout_behavior = .ignore; - child.stderr_behavior = .pipe; + unreachable; + } - try child.spawn(io); + var child = try std.process.spawn(io, .{ + .argv = argv.items, + .stdin = .ignore, + .stdout = .ignore, + .stderr = .pipe, + }); - var stderr_reader = child.stderr.?.readerStreaming(io, &.{}); - const stderr = try stderr_reader.interface.allocRemaining(arena, .limited(std.math.maxInt(u32))); + var stderr_reader = child.stderr.?.readerStreaming(io, &.{}); + const stderr = try stderr_reader.interface.allocRemaining(arena, .limited(std.math.maxInt(u32))); - const term = child.wait(io) catch |err| { - return comp.failCObj(c_object, "failed to spawn zig clang {s}: {s}", .{ argv.items[0], @errorName(err) }); - }; + const term = child.wait(io) catch |err| + return comp.failCObj(c_object, "failed to spawn zig clang {s}: {t}", .{ argv.items[0], err }); - switch (term) { - .exited => |code| if (code != 0) if (out_diag_path) |diag_file_path| { - const bundle = CObject.Diag.Bundle.parse(gpa, io, diag_file_path) catch |err| { - log.err("{}: failed to parse clang diagnostics: {s}", .{ err, stderr }); - return comp.failCObj(c_object, "clang exited with code {d}", .{code}); - }; - return comp.failCObjWithOwnedDiagBundle(c_object, bundle); - } else { - log.err("clang failed with stderr: {s}", .{stderr}); + switch (term) { + .exited => |code| if (code != 0) if (out_diag_path) |diag_file_path| { + const bundle = CObject.Diag.Bundle.parse(gpa, io, diag_file_path) catch |err| { + log.err("{}: failed to parse clang diagnostics: {s}", .{ err, stderr }); return comp.failCObj(c_object, "clang exited with code {d}", .{code}); - }, - else => { - log.err("clang terminated with stderr: {s}", .{stderr}); - return comp.failCObj(c_object, "clang terminated unexpectedly", .{}); - }, - } + }; + return comp.failCObjWithOwnedDiagBundle(c_object, bundle); + } else { + log.err("clang failed with stderr: {s}", .{stderr}); + return comp.failCObj(c_object, "clang exited with code {d}", .{code}); + }, + .signal => |sig| { + log.err("clang failed with stderr: {s}", .{stderr}); + return comp.failCObj(c_object, "clang terminated with signal {t}", .{sig}); + }, + else => { + log.err("clang terminated with stderr: {s}", .{stderr}); + return comp.failCObj(c_object, "clang terminated unexpectedly", .{}); + }, } } else { const exit_code = try clangMain(arena, argv.items); @@ -8113,6 +8125,7 @@ pub fn build_crt_file( .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 index 574bf9628e..7c5a2e17e7 100644 --- a/src/introspect.zig +++ b/src/introspect.zig @@ -102,7 +102,7 @@ pub fn findZigLibDirFromSelfExe( return error.FileNotFound; } -pub fn resolveGlobalCacheDir(arena: Allocator, env_map: *std.process.Environ.Map) ![]const u8 { +pub fn resolveGlobalCacheDir(arena: Allocator, env_map: *const std.process.Environ.Map) ![]const u8 { if (std.zig.EnvVar.ZIG_GLOBAL_CACHE_DIR.get(env_map)) |value| return value; const app_name = "zig"; diff --git a/src/libs/freebsd.zig b/src/libs/freebsd.zig index ba85f45830..9ad7eb7e1a 100644 --- a/src/libs/freebsd.zig +++ b/src/libs/freebsd.zig @@ -445,6 +445,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye .gpa = gpa, .io = io, .manifest_dir = try comp.dirs.global_cache.handle.createDirPathOpen(io, "h", .{}), + .cwd = comp.dirs.cwd, }; cache.addPrefix(.{ .path = null, .handle = Io.Dir.cwd() }); cache.addPrefix(comp.dirs.zig_lib); @@ -1119,6 +1120,7 @@ fn buildSharedLib( .soname = soname, .c_source_files = &c_source_files, .skip_linker_dependencies = true, + .environ_map = comp.environ_map, }) catch |err| switch (err) { error.CreateFail => { comp.lockAndSetMiscFailure(misc_task, "sub-compilation of {t} failed: {f}", .{ misc_task, sub_create_diag }); diff --git a/src/libs/glibc.zig b/src/libs/glibc.zig index e9b6ce1882..10e8446fa4 100644 --- a/src/libs/glibc.zig +++ b/src/libs/glibc.zig @@ -680,6 +680,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye .gpa = gpa, .io = io, .manifest_dir = try comp.dirs.global_cache.handle.createDirPathOpen(io, "h", .{}), + .cwd = comp.dirs.cwd, }; cache.addPrefix(.{ .path = null, .handle = Io.Dir.cwd() }); cache.addPrefix(comp.dirs.zig_lib); @@ -1258,6 +1259,7 @@ fn buildSharedLib( .soname = soname, .c_source_files = &c_source_files, .skip_linker_dependencies = true, + .environ_map = comp.environ_map, }) catch |err| switch (err) { error.CreateFail => { comp.lockAndSetMiscFailure(misc_task, "sub-compilation of {t} failed: {f}", .{ misc_task, sub_create_diag }); diff --git a/src/libs/libcxx.zig b/src/libs/libcxx.zig index d293a3b899..bd8863991d 100644 --- a/src/libs/libcxx.zig +++ b/src/libs/libcxx.zig @@ -275,6 +275,7 @@ pub fn buildLibCxx(comp: *Compilation, prog_node: std.Progress.Node) BuildError! .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) { else => comp.lockAndSetMiscFailure(misc_task, "unable to build libc++: create compilation failed: {t}", .{err}), @@ -468,6 +469,7 @@ pub fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) BuildErr .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) { else => comp.lockAndSetMiscFailure(misc_task, "unable to build libc++abi: create compilation failed: {t}", .{err}), diff --git a/src/libs/libtsan.zig b/src/libs/libtsan.zig index 3dcbc132b1..2f9574c473 100644 --- a/src/libs/libtsan.zig +++ b/src/libs/libtsan.zig @@ -301,6 +301,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo .linker_allow_shlib_undefined = linker_allow_shlib_undefined, .install_name = install_name, .headerpad_size = headerpad_size, + .environ_map = comp.environ_map, }) catch |err| { switch (err) { else => comp.lockAndSetMiscFailure(misc_task, "unable to build {t}: create compilation failed: {t}", .{ misc_task, err }), diff --git a/src/libs/libunwind.zig b/src/libs/libunwind.zig index 7ab079b205..787a87f951 100644 --- a/src/libs/libunwind.zig +++ b/src/libs/libunwind.zig @@ -166,6 +166,7 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr .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) { else => comp.lockAndSetMiscFailure(misc_task, "unable to build {t}: create compilation failed: {t}", .{ misc_task, err }), diff --git a/src/libs/musl.zig b/src/libs/musl.zig index 1a1807f250..b847c279be 100644 --- a/src/libs/musl.zig +++ b/src/libs/musl.zig @@ -272,6 +272,7 @@ pub fn buildCrtFile(comp: *Compilation, in_crt_file: CrtFile, prog_node: std.Pro }, .skip_linker_dependencies = true, .soname = "libc.so", + .environ_map = comp.environ_map, }) catch |err| switch (err) { error.CreateFail => { comp.lockAndSetMiscFailure(misc_task, "sub-compilation of {t} failed: {f}", .{ misc_task, sub_create_diag }); diff --git a/src/libs/netbsd.zig b/src/libs/netbsd.zig index 9e4213d237..87d2909e07 100644 --- a/src/libs/netbsd.zig +++ b/src/libs/netbsd.zig @@ -386,6 +386,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye .gpa = gpa, .io = io, .manifest_dir = try comp.dirs.global_cache.handle.createDirPathOpen(io, "h", .{}), + .cwd = comp.dirs.cwd, }; cache.addPrefix(.{ .path = null, .handle = Io.Dir.cwd() }); cache.addPrefix(comp.dirs.zig_lib); @@ -761,6 +762,7 @@ fn buildSharedLib( .soname = soname, .c_source_files = &c_source_files, .skip_linker_dependencies = true, + .environ_map = comp.environ_map, }) catch |err| switch (err) { error.CreateFail => { comp.lockAndSetMiscFailure(misc_task, "sub-compilation of {t} failed: {f}", .{ misc_task, sub_create_diag }); diff --git a/src/main.zig b/src/main.zig index a7b6efa33f..3d90e031d1 100644 --- a/src/main.zig +++ b/src/main.zig @@ -4708,7 +4708,7 @@ pub fn translateC( arena: Allocator, io: Io, argv: []const []const u8, - env_map: *process.Environ.Map, + env_map: *const process.Environ.Map, prog_node: std.Progress.Node, capture: ?*[]u8, ) !void { @@ -5516,7 +5516,7 @@ fn jitCmd( arena: Allocator, io: Io, args: []const []const u8, - env_map: *process.Environ.Map, + env_map: *const process.Environ.Map, options: JitCmdOptions, ) !void { dev.check(.jit_command);