From 4724774433dd7b1a80b572933556b7452c867b08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Fri, 28 Nov 2025 22:06:46 +0100 Subject: [PATCH 1/4] ci: apply workaround for #22213 to x86_64-linux scripts This should be reverted on the next CI tarballs update. --- ci/x86_64-linux-debug-llvm.sh | 5 ++++- ci/x86_64-linux-debug.sh | 5 ++++- ci/x86_64-linux-release.sh | 10 ++++++++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/ci/x86_64-linux-debug-llvm.sh b/ci/x86_64-linux-debug-llvm.sh index f6ac310ef72b2a486888b2a6955e662029e29d21..3a69718c1be6ebcb0c95616f190abdbc9f7bf2f8 100755 --- a/ci/x86_64-linux-debug-llvm.sh +++ b/ci/x86_64-linux-debug-llvm.sh @@ -34,7 +34,10 @@ cmake .. \ -DZIG_STATIC=ON \ -DZIG_NO_LIB=ON \ -DZIG_EXTRA_BUILD_ARGS="-Duse-llvm=true" \ - -GNinja + -GNinja \ + -DCMAKE_C_LINKER_DEPFILE_SUPPORTED=FALSE \ + -DCMAKE_CXX_LINKER_DEPFILE_SUPPORTED=FALSE +# https://github.com/ziglang/zig/issues/22213 # Now cmake will use zig as the C/C++ compiler. We reset the environment variables # so that installation and testing do not get affected by them. diff --git a/ci/x86_64-linux-debug.sh b/ci/x86_64-linux-debug.sh index 98d2ff2ee897e50d9d2b9d3359680261dab9748b..0fc6605e59938d9d23068c0ef1e781699b51a865 100755 --- a/ci/x86_64-linux-debug.sh +++ b/ci/x86_64-linux-debug.sh @@ -33,7 +33,10 @@ cmake .. \ -DZIG_TARGET_MCPU="$MCPU" \ -DZIG_STATIC=ON \ -DZIG_NO_LIB=ON \ - -GNinja + -GNinja \ + -DCMAKE_C_LINKER_DEPFILE_SUPPORTED=FALSE \ + -DCMAKE_CXX_LINKER_DEPFILE_SUPPORTED=FALSE +# https://github.com/ziglang/zig/issues/22213 # Now cmake will use zig as the C/C++ compiler. We reset the environment variables # so that installation and testing do not get affected by them. diff --git a/ci/x86_64-linux-release.sh b/ci/x86_64-linux-release.sh index 9f89d4370228b61c55218fd100feaccc818c0510..f7b1b502b74a360f421aab31179174462f82c284 100755 --- a/ci/x86_64-linux-release.sh +++ b/ci/x86_64-linux-release.sh @@ -39,7 +39,10 @@ cmake .. \ -DZIG_TARGET_MCPU="$MCPU" \ -DZIG_STATIC=ON \ -DZIG_NO_LIB=ON \ - -GNinja + -GNinja \ + -DCMAKE_C_LINKER_DEPFILE_SUPPORTED=FALSE \ + -DCMAKE_CXX_LINKER_DEPFILE_SUPPORTED=FALSE +# https://github.com/ziglang/zig/issues/22213 # Now cmake will use zig as the C/C++ compiler. We reset the environment variables # so that installation and testing do not get affected by them. @@ -97,7 +100,10 @@ cmake .. \ -DZIG_TARGET_MCPU="$MCPU" \ -DZIG_STATIC=ON \ -DZIG_NO_LIB=ON \ - -GNinja + -GNinja \ + -DCMAKE_C_LINKER_DEPFILE_SUPPORTED=FALSE \ + -DCMAKE_CXX_LINKER_DEPFILE_SUPPORTED=FALSE +# https://github.com/ziglang/zig/issues/22213 unset CC unset CXX -- 2.54.0 From 34f88722cd6c206cbbc312377563340f3c3ab2b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Fri, 5 Dec 2025 07:25:51 +0100 Subject: [PATCH 2/4] compiler: support --dependency-file linker option closes https://github.com/ziglang/zig/issues/22213 --- src/Compilation.zig | 50 +++++++++++++++++++++++++++++++++++++++++++++ src/main.zig | 4 ++++ 2 files changed, 54 insertions(+) diff --git a/src/Compilation.zig b/src/Compilation.zig index 687525cbf3813560af683c80303df3b64f775098..01ab281c14e1fb1f1215a0ead39c9f6861ead781 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -173,6 +173,7 @@ verbose_llvm_bc: ?[]const u8, verbose_cimport: bool, verbose_llvm_cpu_features: bool, verbose_link: bool, +link_depfile: ?[]const u8, disable_c_depfile: bool, stack_report: bool, debug_compiler_runtime_libs: bool, @@ -1403,6 +1404,7 @@ pub const MiscTask = enum { compiler_rt, libzigc, analyze_mod, + link_depfile, docs_copy, docs_wasm, @@ -1732,6 +1734,7 @@ pub const CreateOptions = struct { verbose_generic_instances: bool = false, verbose_llvm_ir: ?[]const u8 = null, verbose_llvm_bc: ?[]const u8 = null, + link_depfile: ?[]const u8 = null, verbose_cimport: bool = false, verbose_llvm_cpu_features: bool = false, debug_compiler_runtime_libs: bool = false, @@ -2247,6 +2250,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, .verbose_generic_instances = options.verbose_generic_instances, .verbose_llvm_ir = options.verbose_llvm_ir, .verbose_llvm_bc = options.verbose_llvm_bc, + .link_depfile = options.link_depfile, .verbose_cimport = options.verbose_cimport, .verbose_llvm_cpu_features = options.verbose_llvm_cpu_features, .verbose_link = options.verbose_link, @@ -3099,6 +3103,15 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE } } + if (comp.link_depfile) |depfile_path| if (comp.bin_file) |lf| { + assert(comp.file_system_inputs != null); + comp.createDepFile(depfile_path, lf.emit) catch |err| comp.setMiscFailure( + .link_depfile, + "unable to write linker dependency file: {t}", + .{err}, + ); + }; + if (anyErrors(comp)) { // Skip flushing and keep source files loaded for error reporting. return; @@ -5208,6 +5221,43 @@ pub fn separateCodegenThreadOk(comp: *const Compilation) bool { return zcu.backendSupportsFeature(.separate_thread); } +fn createDepFile( + comp: *Compilation, + depfile: []const u8, + binfile: Cache.Path, +) anyerror!void { + var buf: [4096]u8 = undefined; + var af = try std.fs.cwd().atomicFile(depfile, .{ .write_buffer = &buf }); + defer af.deinit(); + + comp.writeDepFile(binfile, &af.file_writer.interface) catch return af.file_writer.err.?; + + try af.finish(); +} + +fn writeDepFile( + comp: *Compilation, + binfile: Cache.Path, + w: *std.Io.Writer, +) std.Io.Writer.Error!void { + const prefixes = comp.cache_parent.prefixes(); + const fsi = comp.file_system_inputs.?.items; + + try w.print("{f}:", .{binfile}); + + { + var it = std.mem.splitScalar(u8, fsi, 0); + while (it.next()) |input| try w.print(" \\\n {f}{s}", .{ prefixes[input[0] - 1], input[1..] }); + } + + { + var it = std.mem.splitScalar(u8, fsi, 0); + while (it.next()) |input| try w.print("\n\n{f}{s}:", .{ prefixes[input[0] - 1], input[1..] }); + } + + try w.writeByte('\n'); +} + fn workerDocsCopy(comp: *Compilation) void { docsCopyFallible(comp) catch |err| return comp.lockAndSetMiscFailure( .docs_copy, diff --git a/src/main.zig b/src/main.zig index c08e9da4495114915917e1c0fa49ebaee7694a3f..8112e760df76518bcd482453d974a4e39a02634a 100644 --- a/src/main.zig +++ b/src/main.zig @@ -821,6 +821,7 @@ fn buildOutputType( var verbose_generic_instances = false; var verbose_llvm_ir: ?[]const u8 = null; var verbose_llvm_bc: ?[]const u8 = null; + var link_depfile: ?[]const u8 = null; var verbose_cimport = false; var verbose_llvm_cpu_features = false; var time_report = false; @@ -2704,6 +2705,8 @@ fn buildOutputType( { emit_implib = .{ .yes = linker_args_it.nextOrFatal() }; emit_implib_arg_provided = true; + } else if (mem.eql(u8, arg, "--dependency-file")) { + link_depfile = linker_args_it.nextOrFatal(); } else if (mem.eql(u8, arg, "-Brepro") or mem.eql(u8, arg, "/Brepro")) { linker_repro = true; } else if (mem.eql(u8, arg, "-undefined")) { @@ -3479,6 +3482,7 @@ fn buildOutputType( .verbose_generic_instances = verbose_generic_instances, .verbose_llvm_ir = verbose_llvm_ir, .verbose_llvm_bc = verbose_llvm_bc, + .link_depfile = link_depfile, .verbose_cimport = verbose_cimport, .verbose_llvm_cpu_features = verbose_llvm_cpu_features, .time_report = time_report, -- 2.54.0 From 38415911c10055b67d6932d7cbafd14e0b8b6775 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Fri, 5 Dec 2025 08:47:09 +0100 Subject: [PATCH 3/4] compiler: handle -Xlinker args similarly to -Wl args --- src/main.zig | 95 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 86 insertions(+), 9 deletions(-) diff --git a/src/main.zig b/src/main.zig index 8112e760df76518bcd482453d974a4e39a02634a..ce4bce699fc05d8d72c0b4ac2e790589847d5e50 100644 --- a/src/main.zig +++ b/src/main.zig @@ -2071,6 +2071,8 @@ fn buildOutputType( .wl => { var split_it = mem.splitScalar(u8, it.only_arg, ','); while (split_it.next()) |linker_arg| { + // Unfortunately duplicated with the `for_linker` handling below. + // Handle nested-joined args like `-Wl,-rpath=foo`. // Must be prefixed with 1 or 2 dashes. if (linker_arg.len >= 3 and @@ -2080,6 +2082,10 @@ fn buildOutputType( if (mem.indexOfScalar(u8, linker_arg, '=')) |equals_pos| { const key = linker_arg[0..equals_pos]; const value = linker_arg[equals_pos + 1 ..]; + + // We have to handle these here because they would be ambiguous + // if split and added to `linker_args`, as there are argument-less + // variants of them. if (mem.eql(u8, key, "--build-id")) { build_id = std.zig.BuildId.parse(value) catch |err| { fatal("unable to parse --build-id style '{s}': {s}", .{ @@ -2092,22 +2098,19 @@ fn buildOutputType( // is done below. continue; } + try linker_args.append(key); try linker_args.append(value); continue; } } - if (mem.eql(u8, linker_arg, "--build-id")) { - build_id = .fast; - } else if (mem.eql(u8, linker_arg, "--as-needed")) { + + // These options are handled inline because their order matters for + // other non-linker options. + if (mem.eql(u8, linker_arg, "--as-needed")) { needed = false; } else if (mem.eql(u8, linker_arg, "--no-as-needed")) { needed = true; - } else if (mem.eql(u8, linker_arg, "-no-pie")) { - create_module.opts.pie = false; - } else if (mem.eql(u8, linker_arg, "--sort-common")) { - // from ld.lld(1): --sort-common is ignored for GNU compatibility, - // this ignores plain --sort-common } else if (mem.eql(u8, linker_arg, "--whole-archive") or mem.eql(u8, linker_arg, "-whole-archive")) { @@ -2280,7 +2283,74 @@ fn buildOutputType( disable_c_depfile = true; try cc_argv.append(arena, "-###"); }, - .for_linker => try linker_args.append(it.only_arg), + .for_linker => blk: { + // Unfortunately duplicated with the `wl` handling above. + + // Handle joined args like `--dependency-file=foo.d`. + // Must be prefixed with 1 or 2 dashes. + if (it.only_arg.len >= 3 and it.only_arg[0] == '-' and it.only_arg[2] != '-') { + if (mem.indexOfScalar(u8, it.only_arg, '=')) |equals_pos| { + const key = it.only_arg[0..equals_pos]; + const value = it.only_arg[equals_pos + 1 ..]; + + // We have to handle these here because they would be ambiguous + // if split and added to `linker_args`, as there are argument-less + // variants of them. + if (mem.eql(u8, key, "--build-id")) { + build_id = std.zig.BuildId.parse(value) catch |err| { + fatal("unable to parse --build-id style '{s}': {s}", .{ + value, @errorName(err), + }); + }; + continue; + } else if (mem.eql(u8, key, "--sort-common")) { + // this ignores --sort-common= + continue; + } + + try linker_args.append(key); + try linker_args.append(value); + break :blk; + } + } + + // These options are handled inline because their order matters for + // other non-linker options. + if (mem.eql(u8, it.only_arg, "--as-needed")) { + needed = false; + } else if (mem.eql(u8, it.only_arg, "--no-as-needed")) { + needed = true; + } else if (mem.eql(u8, it.only_arg, "--whole-archive") or + mem.eql(u8, it.only_arg, "-whole-archive")) + { + must_link = true; + } else if (mem.eql(u8, it.only_arg, "--no-whole-archive") or + mem.eql(u8, it.only_arg, "-no-whole-archive")) + { + must_link = false; + } else if (mem.eql(u8, it.only_arg, "-Bdynamic") or + mem.eql(u8, it.only_arg, "-dy") or + mem.eql(u8, it.only_arg, "-call_shared")) + { + lib_search_strategy = .no_fallback; + lib_preferred_mode = .dynamic; + } else if (mem.eql(u8, it.only_arg, "-Bstatic") or + mem.eql(u8, it.only_arg, "-dn") or + mem.eql(u8, it.only_arg, "-non_shared") or + mem.eql(u8, it.only_arg, "-static")) + { + lib_search_strategy = .no_fallback; + lib_preferred_mode = .static; + } else if (mem.eql(u8, it.only_arg, "-search_paths_first")) { + lib_search_strategy = .paths_first; + lib_preferred_mode = .dynamic; + } else if (mem.eql(u8, it.only_arg, "-search_dylibs_first")) { + lib_search_strategy = .mode_first; + lib_preferred_mode = .dynamic; + } else { + try linker_args.append(it.only_arg); + } + }, .linker_input_z => { try linker_args.append("-z"); try linker_args.append(it.only_arg); @@ -2410,6 +2480,13 @@ fn buildOutputType( } } provided_name = name[prefix..end]; + } else if (mem.eql(u8, arg, "--build-id")) { + build_id = .fast; + } else if (mem.eql(u8, arg, "-no-pie")) { + create_module.opts.pie = false; + } else if (mem.eql(u8, arg, "--sort-common")) { + // from ld.lld(1): --sort-common is ignored for GNU compatibility, + // this ignores plain --sort-common } else if (mem.eql(u8, arg, "-rpath") or mem.eql(u8, arg, "--rpath") or mem.eql(u8, arg, "-R")) { try create_module.rpath_list.append(arena, linker_args_it.nextOrFatal()); } else if (mem.eql(u8, arg, "--subsystem")) { -- 2.54.0 From d22231c039ce8f087b2499a97eb769fb800f2000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Fri, 5 Dec 2025 10:18:05 +0100 Subject: [PATCH 4/4] Compilation: track indirect file system inputs from clang's depfile Co-authored-by: Matthew Lugg --- lib/std/Build/Cache.zig | 2 +- src/Compilation.zig | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/lib/std/Build/Cache.zig b/lib/std/Build/Cache.zig index e68c3e789220cee483bda66cc2b6e6120331bf97..5e8412cfcf0a24320cd8b48dd38f2a8b96df6dbc 100644 --- a/lib/std/Build/Cache.zig +++ b/lib/std/Build/Cache.zig @@ -123,7 +123,7 @@ pub const HexDigest = [hex_digest_len]u8; /// This is currently just an arbitrary non-empty string that can't match another manifest line. const manifest_header = "0"; -const manifest_file_size_max = 100 * 1024 * 1024; +pub const manifest_file_size_max = 100 * 1024 * 1024; /// The type used for hashing file contents. Currently, this is SipHash128(1, 3), because it /// provides enough collision resistance for the Manifest use cases, while being one of our diff --git a/src/Compilation.zig b/src/Compilation.zig index 01ab281c14e1fb1f1215a0ead39c9f6861ead781..138755773877254786b45dcb5a9cbc4bbaba6805 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -6419,6 +6419,38 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr if (out_dep_path) |dep_file_path| { const dep_basename = fs.path.basename(dep_file_path); + + if (comp.file_system_inputs != null) { + // Use the same file size limit as the cache code does for dependency files. + const dep_file_contents = try zig_cache_tmp_dir.readFileAlloc(dep_basename, gpa, .limited(Cache.manifest_file_size_max)); + defer gpa.free(dep_file_contents); + + var str_buf: std.ArrayList(u8) = .empty; + defer str_buf.deinit(gpa); + + var it: std.Build.Cache.DepTokenizer = .{ .bytes = dep_file_contents }; + while (it.next()) |token| { + const input_path: Compilation.Path = switch (token) { + .target, .target_must_resolve => continue, + .prereq => |file_path| try .fromUnresolved(arena, comp.dirs, &.{file_path}), + .prereq_must_resolve => p: { + try token.resolve(gpa, &str_buf); + break :p try .fromUnresolved(arena, comp.dirs, &.{str_buf.items}); + }, + else => |err| { + try err.printError(gpa, &str_buf); + log.err("failed parsing {s}: {s}", .{ dep_basename, str_buf.items }); + return error.InvalidDepFile; + }, + }; + + // There may be concurrent calls to `appendFileSystemInput` from other C objects. + comp.mutex.lock(); + defer comp.mutex.unlock(); + try comp.appendFileSystemInput(input_path); + } + } + // Add the files depended on to the cache system. try man.addDepFilePost(zig_cache_tmp_dir, dep_basename); switch (comp.cache_use) { -- 2.54.0