| author | |
| committer | |
| log | 70d7d7e919d7f297e63ca421f6be5925259136e2 |
| tree | 1635b864f21a78a8d273fa9a946191a8b9796e8b |
| parent | 670e7d456c24a8597af6b3809bf1e9ea68746ade |
Normally when using LLD to link, Zig uses a file named "lld.id" in the
same directory as the output binary which contains the hash of the link
operation, allowing Zig to skip linking when the hash would be unchanged.
In the case that the output binary is being emitted into a directory which
is externally modified - essentially anything other than zig-cache - then
this flag would be set to disable this machinery to avoid false positives.
* Better defaults when using -fno-LLVM
* Fix compiler_rt and libc static libraries were getting a .zig
extension instead of .a extension.
* when using the stage1 backend, put the object file next to the
stage1.id file in the cache directory. this prevents an object file
from polluting the cwd when using zig from the CLI.5 files changed, 149 insertions(+), 120 deletions(-)
BRANCH_TODO+5-6| ... | @@ -1,7 +1,6 @@ | ... | @@ -1,7 +1,6 @@ |
| 1 | * make sure that `zig cc -o hello hello.c -target native-native-musl` and `zig build-exe hello.zig -lc -target native-native-musl` will share the same libc build. | ||
| 2 | * zig cc as a preprocessor (-E) | ||
| 1 | * tests passing with -Dskip-non-native | 3 | * tests passing with -Dskip-non-native |
| 2 | * make sure zig cc works | ||
| 3 | - using it as a preprocessor (-E) | ||
| 4 | - try building some software | ||
| 5 | * `-ftime-report` | 4 | * `-ftime-report` |
| 6 | * -fstack-report print stack size diagnostics\n" | 5 | * -fstack-report print stack size diagnostics\n" |
| 7 | * -fdump-analysis write analysis.json file with type information\n" | 6 | * -fdump-analysis write analysis.json file with type information\n" |
| ... | @@ -15,14 +14,12 @@ | ... | @@ -15,14 +14,12 @@ |
| 15 | * MachO LLD linking | 14 | * MachO LLD linking |
| 16 | * COFF LLD linking | 15 | * COFF LLD linking |
| 17 | * WASM LLD linking | 16 | * WASM LLD linking |
| 18 | * skip LLD caching when bin directory is not in the cache (so we don't put `id.txt` into the cwd) | ||
| 19 | (maybe make it an explicit option and have main.zig disable it) | ||
| 20 | - make sure that `zig cc -o hello hello.c -target native-native-musl` and `zig build-exe hello.zig -lc -target native-native-musl` will share the same libc build. | ||
| 21 | * audit the CLI options for stage2 | 17 | * audit the CLI options for stage2 |
| 22 | * audit the base cache hash | 18 | * audit the base cache hash |
| 23 | * On operating systems that support it, do an execve for `zig test` and `zig run` rather than child process. | 19 | * On operating systems that support it, do an execve for `zig test` and `zig run` rather than child process. |
| 24 | * restore error messages for stage2_add_link_lib | 20 | * restore error messages for stage2_add_link_lib |
| 25 | * windows CUSTOMBUILD : error : unable to build compiler_rt: FileNotFound [D:\a\1\s\build\zig_install_lib_files.vcxproj] | 21 | * windows CUSTOMBUILD : error : unable to build compiler_rt: FileNotFound [D:\a\1\s\build\zig_install_lib_files.vcxproj] |
| 22 | * try building some software with zig cc | ||
| 26 | 23 | ||
| 27 | * implement proper parsing of clang stderr/stdout and exposing compile errors with the Compilation API | 24 | * implement proper parsing of clang stderr/stdout and exposing compile errors with the Compilation API |
| 28 | * implement proper parsing of LLD stderr/stdout and exposing compile errors with the Compilation API | 25 | * implement proper parsing of LLD stderr/stdout and exposing compile errors with the Compilation API |
| ... | @@ -59,3 +56,5 @@ | ... | @@ -59,3 +56,5 @@ |
| 59 | * close the --pkg-begin --pkg-end Package directory handles | 56 | * close the --pkg-begin --pkg-end Package directory handles |
| 60 | * make std.Progress support multithreaded | 57 | * make std.Progress support multithreaded |
| 61 | * update musl.zig static data to use native path separator in static data rather than replacing '/' at runtime | 58 | * update musl.zig static data to use native path separator in static data rather than replacing '/' at runtime |
| 59 | * linking hello world with LLD, lld is silently calling exit(1) instead of reporting ok=false. when run standalone the error message is: ld.lld: error: section [index 3] has a sh_offset (0x57000) + sh_size (0x68) that is greater than the file size (0x57060) | ||
| 60 |
src/Compilation.zig+51-33| ... | @@ -3,10 +3,11 @@ const Compilation = @This(); | ... | @@ -3,10 +3,11 @@ const Compilation = @This(); |
| 3 | const std = @import("std"); | 3 | const std = @import("std"); |
| 4 | const mem = std.mem; | 4 | const mem = std.mem; |
| 5 | const Allocator = std.mem.Allocator; | 5 | const Allocator = std.mem.Allocator; |
| 6 | const Value = @import("value.zig").Value; | ||
| 7 | const assert = std.debug.assert; | 6 | const assert = std.debug.assert; |
| 8 | const log = std.log.scoped(.compilation); | 7 | const log = std.log.scoped(.compilation); |
| 9 | const Target = std.Target; | 8 | const Target = std.Target; |
| 9 | |||
| 10 | const Value = @import("value.zig").Value; | ||
| 10 | const target_util = @import("target.zig"); | 11 | const target_util = @import("target.zig"); |
| 11 | const Package = @import("Package.zig"); | 12 | const Package = @import("Package.zig"); |
| 12 | const link = @import("link.zig"); | 13 | const link = @import("link.zig"); |
| ... | @@ -286,6 +287,13 @@ pub const InitOptions = struct { | ... | @@ -286,6 +287,13 @@ pub const InitOptions = struct { |
| 286 | emit_h: ?EmitLoc = null, | 287 | emit_h: ?EmitLoc = null, |
| 287 | link_mode: ?std.builtin.LinkMode = null, | 288 | link_mode: ?std.builtin.LinkMode = null, |
| 288 | dll_export_fns: ?bool = false, | 289 | dll_export_fns: ?bool = false, |
| 290 | /// Normally when using LLD to link, Zig uses a file named "lld.id" in the | ||
| 291 | /// same directory as the output binary which contains the hash of the link | ||
| 292 | /// operation, allowing Zig to skip linking when the hash would be unchanged. | ||
| 293 | /// In the case that the output binary is being emitted into a directory which | ||
| 294 | /// is externally modified - essentially anything other than zig-cache - then | ||
| 295 | /// this flag would be set to disable this machinery to avoid false positives. | ||
| 296 | disable_lld_caching: bool = false, | ||
| 289 | object_format: ?std.builtin.ObjectFormat = null, | 297 | object_format: ?std.builtin.ObjectFormat = null, |
| 290 | optimize_mode: std.builtin.Mode = .Debug, | 298 | optimize_mode: std.builtin.Mode = .Debug, |
| 291 | keep_source_files_loaded: bool = false, | 299 | keep_source_files_loaded: bool = false, |
| ... | @@ -371,6 +379,26 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { | ... | @@ -371,6 +379,26 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 371 | 379 | ||
| 372 | const ofmt = options.object_format orelse options.target.getObjectFormat(); | 380 | const ofmt = options.object_format orelse options.target.getObjectFormat(); |
| 373 | 381 | ||
| 382 | // Make a decision on whether to use LLVM or our own backend. | ||
| 383 | const use_llvm = if (options.use_llvm) |explicit| explicit else blk: { | ||
| 384 | // If we have no zig code to compile, no need for LLVM. | ||
| 385 | if (options.root_pkg == null) | ||
| 386 | break :blk false; | ||
| 387 | |||
| 388 | // If we are the stage1 compiler, we depend on the stage1 c++ llvm backend | ||
| 389 | // to compile zig code. | ||
| 390 | if (build_options.is_stage1) | ||
| 391 | break :blk true; | ||
| 392 | |||
| 393 | // We would want to prefer LLVM for release builds when it is available, however | ||
| 394 | // we don't have an LLVM backend yet :) | ||
| 395 | // We would also want to prefer LLVM for architectures that we don't have self-hosted support for too. | ||
| 396 | break :blk false; | ||
| 397 | }; | ||
| 398 | if (!use_llvm and options.machine_code_model != .default) { | ||
| 399 | return error.MachineCodeModelNotSupported; | ||
| 400 | } | ||
| 401 | |||
| 374 | // Make a decision on whether to use LLD or our own linker. | 402 | // Make a decision on whether to use LLD or our own linker. |
| 375 | const use_lld = if (options.use_lld) |explicit| explicit else blk: { | 403 | const use_lld = if (options.use_lld) |explicit| explicit else blk: { |
| 376 | if (!build_options.have_llvm) | 404 | if (!build_options.have_llvm) |
| ... | @@ -393,7 +421,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { | ... | @@ -393,7 +421,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 393 | break :blk true; | 421 | break :blk true; |
| 394 | } | 422 | } |
| 395 | 423 | ||
| 396 | if (build_options.is_stage1) { | 424 | if (use_llvm) { |
| 397 | // If stage1 generates an object file, self-hosted linker is not | 425 | // If stage1 generates an object file, self-hosted linker is not |
| 398 | // yet sophisticated enough to handle that. | 426 | // yet sophisticated enough to handle that. |
| 399 | break :blk options.root_pkg != null; | 427 | break :blk options.root_pkg != null; |
| ... | @@ -402,25 +430,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { | ... | @@ -402,25 +430,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 402 | break :blk false; | 430 | break :blk false; |
| 403 | }; | 431 | }; |
| 404 | 432 | ||
| 405 | // Make a decision on whether to use LLVM or our own backend. | ||
| 406 | const use_llvm = if (options.use_llvm) |explicit| explicit else blk: { | ||
| 407 | // If we have no zig code to compile, no need for LLVM. | ||
| 408 | if (options.root_pkg == null) | ||
| 409 | break :blk false; | ||
| 410 | |||
| 411 | // If we are the stage1 compiler, we depend on the stage1 c++ llvm backend | ||
| 412 | // to compile zig code. | ||
| 413 | if (build_options.is_stage1) | ||
| 414 | break :blk true; | ||
| 415 | |||
| 416 | // We would want to prefer LLVM for release builds when it is available, however | ||
| 417 | // we don't have an LLVM backend yet :) | ||
| 418 | // We would also want to prefer LLVM for architectures that we don't have self-hosted support for too. | ||
| 419 | break :blk false; | ||
| 420 | }; | ||
| 421 | if (!use_llvm and options.machine_code_model != .default) { | ||
| 422 | return error.MachineCodeModelNotSupported; | ||
| 423 | } | ||
| 424 | 433 | ||
| 425 | const link_libc = options.link_libc or | 434 | const link_libc = options.link_libc or |
| 426 | (is_exe_or_dyn_lib and target_util.osRequiresLibC(options.target)); | 435 | (is_exe_or_dyn_lib and target_util.osRequiresLibC(options.target)); |
| ... | @@ -720,6 +729,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { | ... | @@ -720,6 +729,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 720 | .llvm_cpu_features = llvm_cpu_features, | 729 | .llvm_cpu_features = llvm_cpu_features, |
| 721 | .is_compiler_rt_or_libc = options.is_compiler_rt_or_libc, | 730 | .is_compiler_rt_or_libc = options.is_compiler_rt_or_libc, |
| 722 | .each_lib_rpath = options.each_lib_rpath orelse false, | 731 | .each_lib_rpath = options.each_lib_rpath orelse false, |
| 732 | .disable_lld_caching = options.disable_lld_caching, | ||
| 723 | }); | 733 | }); |
| 724 | errdefer bin_file.destroy(); | 734 | errdefer bin_file.destroy(); |
| 725 | comp.* = .{ | 735 | comp.* = .{ |
| ... | @@ -2288,7 +2298,7 @@ pub fn updateSubCompilation(sub_compilation: *Compilation) !void { | ... | @@ -2288,7 +2298,7 @@ pub fn updateSubCompilation(sub_compilation: *Compilation) !void { |
| 2288 | } | 2298 | } |
| 2289 | } | 2299 | } |
| 2290 | 2300 | ||
| 2291 | fn buildStaticLibFromZig(comp: *Compilation, basename: []const u8, out: *?CRTFile) !void { | 2301 | fn buildStaticLibFromZig(comp: *Compilation, src_basename: []const u8, out: *?CRTFile) !void { |
| 2292 | const tracy = trace(@src()); | 2302 | const tracy = trace(@src()); |
| 2293 | defer tracy.end(); | 2303 | defer tracy.end(); |
| 2294 | 2304 | ||
| ... | @@ -2304,12 +2314,20 @@ fn buildStaticLibFromZig(comp: *Compilation, basename: []const u8, out: *?CRTFil | ... | @@ -2304,12 +2314,20 @@ fn buildStaticLibFromZig(comp: *Compilation, basename: []const u8, out: *?CRTFil |
| 2304 | .path = special_path, | 2314 | .path = special_path, |
| 2305 | .handle = special_dir, | 2315 | .handle = special_dir, |
| 2306 | }, | 2316 | }, |
| 2307 | .root_src_path = basename, | 2317 | .root_src_path = src_basename, |
| 2308 | }; | 2318 | }; |
| 2319 | const root_name = mem.split(src_basename, ".").next().?; | ||
| 2320 | const target = comp.getTarget(); | ||
| 2321 | const bin_basename = try std.zig.binNameAlloc(comp.gpa, .{ | ||
| 2322 | .root_name = root_name, | ||
| 2323 | .target = target, | ||
| 2324 | .output_mode = .Lib, | ||
| 2325 | }); | ||
| 2326 | defer comp.gpa.free(bin_basename); | ||
| 2309 | 2327 | ||
| 2310 | const emit_bin = Compilation.EmitLoc{ | 2328 | const emit_bin = Compilation.EmitLoc{ |
| 2311 | .directory = null, // Put it in the cache directory. | 2329 | .directory = null, // Put it in the cache directory. |
| 2312 | .basename = basename, | 2330 | .basename = bin_basename, |
| 2313 | }; | 2331 | }; |
| 2314 | const optimize_mode: std.builtin.Mode = blk: { | 2332 | const optimize_mode: std.builtin.Mode = blk: { |
| 2315 | if (comp.is_test) | 2333 | if (comp.is_test) |
| ... | @@ -2323,8 +2341,8 @@ fn buildStaticLibFromZig(comp: *Compilation, basename: []const u8, out: *?CRTFil | ... | @@ -2323,8 +2341,8 @@ fn buildStaticLibFromZig(comp: *Compilation, basename: []const u8, out: *?CRTFil |
| 2323 | .global_cache_directory = comp.global_cache_directory, | 2341 | .global_cache_directory = comp.global_cache_directory, |
| 2324 | .local_cache_directory = comp.global_cache_directory, | 2342 | .local_cache_directory = comp.global_cache_directory, |
| 2325 | .zig_lib_directory = comp.zig_lib_directory, | 2343 | .zig_lib_directory = comp.zig_lib_directory, |
| 2326 | .target = comp.getTarget(), | 2344 | .target = target, |
| 2327 | .root_name = mem.split(basename, ".").next().?, | 2345 | .root_name = root_name, |
| 2328 | .root_pkg = &root_pkg, | 2346 | .root_pkg = &root_pkg, |
| 2329 | .output_mode = .Lib, | 2347 | .output_mode = .Lib, |
| 2330 | .rand = comp.rand, | 2348 | .rand = comp.rand, |
| ... | @@ -2358,7 +2376,9 @@ fn buildStaticLibFromZig(comp: *Compilation, basename: []const u8, out: *?CRTFil | ... | @@ -2358,7 +2376,9 @@ fn buildStaticLibFromZig(comp: *Compilation, basename: []const u8, out: *?CRTFil |
| 2358 | 2376 | ||
| 2359 | assert(out.* == null); | 2377 | assert(out.* == null); |
| 2360 | out.* = Compilation.CRTFile{ | 2378 | out.* = Compilation.CRTFile{ |
| 2361 | .full_object_path = try sub_compilation.bin_file.options.directory.join(comp.gpa, &[_][]const u8{basename}), | 2379 | .full_object_path = try sub_compilation.bin_file.options.directory.join(comp.gpa, &[_][]const u8{ |
| 2380 | sub_compilation.bin_file.options.sub_path, | ||
| 2381 | }), | ||
| 2362 | .lock = sub_compilation.bin_file.toOwnedLock(), | 2382 | .lock = sub_compilation.bin_file.toOwnedLock(), |
| 2363 | }; | 2383 | }; |
| 2364 | } | 2384 | } |
| ... | @@ -2461,7 +2481,7 @@ fn updateStage1Module(comp: *Compilation) !void { | ... | @@ -2461,7 +2481,7 @@ fn updateStage1Module(comp: *Compilation) !void { |
| 2461 | ) orelse return error.OutOfMemory; | 2481 | ) orelse return error.OutOfMemory; |
| 2462 | 2482 | ||
| 2463 | const stage1_pkg = try createStage1Pkg(arena, "root", mod.root_pkg, null); | 2483 | const stage1_pkg = try createStage1Pkg(arena, "root", mod.root_pkg, null); |
| 2464 | const output_dir = comp.bin_file.options.directory.path orelse "."; | 2484 | const output_dir = directory.path orelse "."; |
| 2465 | const test_filter = comp.test_filter orelse ""[0..0]; | 2485 | const test_filter = comp.test_filter orelse ""[0..0]; |
| 2466 | const test_name_prefix = comp.test_name_prefix orelse ""[0..0]; | 2486 | const test_name_prefix = comp.test_name_prefix orelse ""[0..0]; |
| 2467 | stage1_module.* = .{ | 2487 | stage1_module.* = .{ |
| ... | @@ -2617,13 +2637,11 @@ pub fn build_crt_file( | ... | @@ -2617,13 +2637,11 @@ pub fn build_crt_file( |
| 2617 | try sub_compilation.updateSubCompilation(); | 2637 | try sub_compilation.updateSubCompilation(); |
| 2618 | 2638 | ||
| 2619 | try comp.crt_files.ensureCapacity(comp.gpa, comp.crt_files.count() + 1); | 2639 | try comp.crt_files.ensureCapacity(comp.gpa, comp.crt_files.count() + 1); |
| 2620 | const artifact_path = if (sub_compilation.bin_file.options.directory.path) |p| | ||
| 2621 | try std.fs.path.join(comp.gpa, &[_][]const u8{ p, basename }) | ||
| 2622 | else | ||
| 2623 | try comp.gpa.dupe(u8, basename); | ||
| 2624 | 2640 | ||
| 2625 | comp.crt_files.putAssumeCapacityNoClobber(basename, .{ | 2641 | comp.crt_files.putAssumeCapacityNoClobber(basename, .{ |
| 2626 | .full_object_path = artifact_path, | 2642 | .full_object_path = try sub_compilation.bin_file.options.directory.join(comp.gpa, &[_][]const u8{ |
| 2643 | sub_compilation.bin_file.options.sub_path, | ||
| 2644 | }), | ||
| 2627 | .lock = sub_compilation.bin_file.toOwnedLock(), | 2645 | .lock = sub_compilation.bin_file.toOwnedLock(), |
| 2628 | }); | 2646 | }); |
| 2629 | } | 2647 | } |
src/link.zig+1| ... | @@ -66,6 +66,7 @@ pub const Options = struct { | ... | @@ -66,6 +66,7 @@ pub const Options = struct { |
| 66 | error_return_tracing: bool, | 66 | error_return_tracing: bool, |
| 67 | is_compiler_rt_or_libc: bool, | 67 | is_compiler_rt_or_libc: bool, |
| 68 | each_lib_rpath: bool, | 68 | each_lib_rpath: bool, |
| 69 | disable_lld_caching: bool, | ||
| 69 | gc_sections: ?bool = null, | 70 | gc_sections: ?bool = null, |
| 70 | allow_shlib_undefined: ?bool = null, | 71 | allow_shlib_undefined: ?bool = null, |
| 71 | linker_script: ?[]const u8 = null, | 72 | linker_script: ?[]const u8 = null, |
src/link/Elf.zig+91-81| ... | @@ -23,6 +23,7 @@ const File = link.File; | ... | @@ -23,6 +23,7 @@ const File = link.File; |
| 23 | const build_options = @import("build_options"); | 23 | const build_options = @import("build_options"); |
| 24 | const target_util = @import("../target.zig"); | 24 | const target_util = @import("../target.zig"); |
| 25 | const glibc = @import("../glibc.zig"); | 25 | const glibc = @import("../glibc.zig"); |
| 26 | const Cache = @import("../Cache.zig"); | ||
| 26 | 27 | ||
| 27 | const default_entry_addr = 0x8000000; | 28 | const default_entry_addr = 0x8000000; |
| 28 | 29 | ||
| ... | @@ -1225,7 +1226,8 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { | ... | @@ -1225,7 +1226,8 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { |
| 1225 | const use_stage1 = build_options.is_stage1 and self.base.options.use_llvm; | 1226 | const use_stage1 = build_options.is_stage1 and self.base.options.use_llvm; |
| 1226 | if (use_stage1) { | 1227 | if (use_stage1) { |
| 1227 | const obj_basename = try std.fmt.allocPrint(arena, "{}.o", .{self.base.options.root_name}); | 1228 | const obj_basename = try std.fmt.allocPrint(arena, "{}.o", .{self.base.options.root_name}); |
| 1228 | const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename}); | 1229 | const o_directory = self.base.options.module.?.zig_cache_artifact_directory; |
| 1230 | const full_obj_path = try o_directory.join(arena, &[_][]const u8{obj_basename}); | ||
| 1229 | break :blk full_obj_path; | 1231 | break :blk full_obj_path; |
| 1230 | } | 1232 | } |
| 1231 | 1233 | ||
| ... | @@ -1235,6 +1237,12 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { | ... | @@ -1235,6 +1237,12 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { |
| 1235 | break :blk full_obj_path; | 1237 | break :blk full_obj_path; |
| 1236 | } else null; | 1238 | } else null; |
| 1237 | 1239 | ||
| 1240 | const is_lib = self.base.options.output_mode == .Lib; | ||
| 1241 | const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib; | ||
| 1242 | const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe; | ||
| 1243 | const have_dynamic_linker = self.base.options.link_libc and | ||
| 1244 | self.base.options.link_mode == .Dynamic and is_exe_or_dyn_lib; | ||
| 1245 | |||
| 1238 | // Here we want to determine whether we can save time by not invoking LLD when the | 1246 | // Here we want to determine whether we can save time by not invoking LLD when the |
| 1239 | // output is unchanged. None of the linker options or the object files that are being | 1247 | // output is unchanged. None of the linker options or the object files that are being |
| 1240 | // linked are in the hash that namespaces the directory we are outputting to. Therefore, | 1248 | // linked are in the hash that namespaces the directory we are outputting to. Therefore, |
| ... | @@ -1245,78 +1253,78 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { | ... | @@ -1245,78 +1253,78 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { |
| 1245 | // our digest. If so, we can skip linking. Otherwise, we proceed with invoking LLD. | 1253 | // our digest. If so, we can skip linking. Otherwise, we proceed with invoking LLD. |
| 1246 | const id_symlink_basename = "lld.id"; | 1254 | const id_symlink_basename = "lld.id"; |
| 1247 | 1255 | ||
| 1248 | // We are about to obtain this lock, so here we give other processes a chance first. | 1256 | var man: Cache.Manifest = undefined; |
| 1249 | self.base.releaseLock(); | 1257 | defer if (!self.base.options.disable_lld_caching) man.deinit(); |
| 1250 | 1258 | ||
| 1251 | var ch = comp.cache_parent.obtain(); | 1259 | var digest: [Cache.hex_digest_len]u8 = undefined; |
| 1252 | defer ch.deinit(); | 1260 | |
| 1261 | if (!self.base.options.disable_lld_caching) { | ||
| 1262 | man = comp.cache_parent.obtain(); | ||
| 1263 | |||
| 1264 | // We are about to obtain this lock, so here we give other processes a chance first. | ||
| 1265 | self.base.releaseLock(); | ||
| 1266 | |||
| 1267 | try man.addOptionalFile(self.base.options.linker_script); | ||
| 1268 | try man.addOptionalFile(self.base.options.version_script); | ||
| 1269 | try man.addListOfFiles(self.base.options.objects); | ||
| 1270 | for (comp.c_object_table.items()) |entry| { | ||
| 1271 | _ = try man.addFile(entry.key.status.success.object_path, null); | ||
| 1272 | } | ||
| 1273 | try man.addOptionalFile(module_obj_path); | ||
| 1274 | // We can skip hashing libc and libc++ components that we are in charge of building from Zig | ||
| 1275 | // installation sources because they are always a product of the compiler version + target information. | ||
| 1276 | man.hash.addOptional(self.base.options.stack_size_override); | ||
| 1277 | man.hash.addOptional(self.base.options.gc_sections); | ||
| 1278 | man.hash.add(self.base.options.eh_frame_hdr); | ||
| 1279 | man.hash.add(self.base.options.rdynamic); | ||
| 1280 | man.hash.addListOfBytes(self.base.options.extra_lld_args); | ||
| 1281 | man.hash.addListOfBytes(self.base.options.lib_dirs); | ||
| 1282 | man.hash.addListOfBytes(self.base.options.rpath_list); | ||
| 1283 | man.hash.add(self.base.options.each_lib_rpath); | ||
| 1284 | man.hash.add(self.base.options.is_compiler_rt_or_libc); | ||
| 1285 | man.hash.add(self.base.options.z_nodelete); | ||
| 1286 | man.hash.add(self.base.options.z_defs); | ||
| 1287 | if (self.base.options.link_libc) { | ||
| 1288 | man.hash.add(self.base.options.libc_installation != null); | ||
| 1289 | if (self.base.options.libc_installation) |libc_installation| { | ||
| 1290 | man.hash.addBytes(libc_installation.crt_dir.?); | ||
| 1291 | } | ||
| 1292 | if (have_dynamic_linker) { | ||
| 1293 | man.hash.addOptionalBytes(self.base.options.dynamic_linker); | ||
| 1294 | } | ||
| 1295 | } | ||
| 1296 | if (is_dyn_lib) { | ||
| 1297 | man.hash.addOptionalBytes(self.base.options.override_soname); | ||
| 1298 | man.hash.addOptional(self.base.options.version); | ||
| 1299 | } | ||
| 1300 | man.hash.addListOfBytes(self.base.options.system_libs); | ||
| 1301 | man.hash.addOptional(self.base.options.allow_shlib_undefined); | ||
| 1302 | man.hash.add(self.base.options.bind_global_refs_locally); | ||
| 1253 | 1303 | ||
| 1254 | const is_lib = self.base.options.output_mode == .Lib; | 1304 | // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. |
| 1255 | const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib; | 1305 | _ = try man.hit(); |
| 1256 | const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe; | 1306 | digest = man.final(); |
| 1257 | const have_dynamic_linker = self.base.options.link_libc and | ||
| 1258 | self.base.options.link_mode == .Dynamic and is_exe_or_dyn_lib; | ||
| 1259 | 1307 | ||
| 1260 | try ch.addOptionalFile(self.base.options.linker_script); | 1308 | var prev_digest_buf: [digest.len]u8 = undefined; |
| 1261 | try ch.addOptionalFile(self.base.options.version_script); | 1309 | const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: { |
| 1262 | try ch.addListOfFiles(self.base.options.objects); | 1310 | log.debug("ELF LLD new_digest={} readlink error: {}", .{digest, @errorName(err)}); |
| 1263 | for (comp.c_object_table.items()) |entry| { | 1311 | // Handle this as a cache miss. |
| 1264 | _ = try ch.addFile(entry.key.status.success.object_path, null); | 1312 | break :blk prev_digest_buf[0..0]; |
| 1265 | } | 1313 | }; |
| 1266 | try ch.addOptionalFile(module_obj_path); | 1314 | if (mem.eql(u8, prev_digest, &digest)) { |
| 1267 | // We can skip hashing libc and libc++ components that we are in charge of building from Zig | 1315 | log.debug("ELF LLD digest={} match - skipping invocation", .{digest}); |
| 1268 | // installation sources because they are always a product of the compiler version + target information. | 1316 | // Hot diggity dog! The output binary is already there. |
| 1269 | ch.hash.addOptional(self.base.options.stack_size_override); | 1317 | self.base.lock = man.toOwnedLock(); |
| 1270 | ch.hash.addOptional(self.base.options.gc_sections); | 1318 | return; |
| 1271 | ch.hash.add(self.base.options.eh_frame_hdr); | ||
| 1272 | ch.hash.add(self.base.options.rdynamic); | ||
| 1273 | ch.hash.addListOfBytes(self.base.options.extra_lld_args); | ||
| 1274 | ch.hash.addListOfBytes(self.base.options.lib_dirs); | ||
| 1275 | ch.hash.addListOfBytes(self.base.options.rpath_list); | ||
| 1276 | ch.hash.add(self.base.options.each_lib_rpath); | ||
| 1277 | ch.hash.add(self.base.options.is_compiler_rt_or_libc); | ||
| 1278 | ch.hash.add(self.base.options.z_nodelete); | ||
| 1279 | ch.hash.add(self.base.options.z_defs); | ||
| 1280 | if (self.base.options.link_libc) { | ||
| 1281 | ch.hash.add(self.base.options.libc_installation != null); | ||
| 1282 | if (self.base.options.libc_installation) |libc_installation| { | ||
| 1283 | ch.hash.addBytes(libc_installation.crt_dir.?); | ||
| 1284 | } | ||
| 1285 | if (have_dynamic_linker) { | ||
| 1286 | ch.hash.addOptionalBytes(self.base.options.dynamic_linker); | ||
| 1287 | } | 1319 | } |
| 1320 | log.debug("ELF LLD prev_digest={} new_digest={}", .{prev_digest, digest}); | ||
| 1321 | |||
| 1322 | // We are about to change the output file to be different, so we invalidate the build hash now. | ||
| 1323 | directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { | ||
| 1324 | error.FileNotFound => {}, | ||
| 1325 | else => |e| return e, | ||
| 1326 | }; | ||
| 1288 | } | 1327 | } |
| 1289 | if (is_dyn_lib) { | ||
| 1290 | ch.hash.addOptionalBytes(self.base.options.override_soname); | ||
| 1291 | ch.hash.addOptional(self.base.options.version); | ||
| 1292 | } | ||
| 1293 | ch.hash.addListOfBytes(self.base.options.system_libs); | ||
| 1294 | ch.hash.addOptional(self.base.options.allow_shlib_undefined); | ||
| 1295 | ch.hash.add(self.base.options.bind_global_refs_locally); | ||
| 1296 | |||
| 1297 | // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. | ||
| 1298 | _ = try ch.hit(); | ||
| 1299 | const digest = ch.final(); | ||
| 1300 | |||
| 1301 | var prev_digest_buf: [digest.len]u8 = undefined; | ||
| 1302 | const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: { | ||
| 1303 | log.debug("ELF LLD new_digest={} readlink error: {}", .{digest, @errorName(err)}); | ||
| 1304 | // Handle this as a cache miss. | ||
| 1305 | break :blk prev_digest_buf[0..0]; | ||
| 1306 | }; | ||
| 1307 | if (mem.eql(u8, prev_digest, &digest)) { | ||
| 1308 | log.debug("ELF LLD digest={} match - skipping invocation", .{digest}); | ||
| 1309 | // Hot diggity dog! The output binary is already there. | ||
| 1310 | self.base.lock = ch.toOwnedLock(); | ||
| 1311 | return; | ||
| 1312 | } | ||
| 1313 | log.debug("ELF LLD prev_digest={} new_digest={}", .{prev_digest, digest}); | ||
| 1314 | |||
| 1315 | // We are about to change the output file to be different, so we invalidate the build hash now. | ||
| 1316 | directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { | ||
| 1317 | error.FileNotFound => {}, | ||
| 1318 | else => |e| return e, | ||
| 1319 | }; | ||
| 1320 | 1328 | ||
| 1321 | const target = self.base.options.target; | 1329 | const target = self.base.options.target; |
| 1322 | const is_obj = self.base.options.output_mode == .Obj; | 1330 | const is_obj = self.base.options.output_mode == .Obj; |
| ... | @@ -1620,18 +1628,20 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { | ... | @@ -1620,18 +1628,20 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { |
| 1620 | std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items}); | 1628 | std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items}); |
| 1621 | } | 1629 | } |
| 1622 | 1630 | ||
| 1623 | // Update the dangling symlink with the digest. If it fails we can continue; it only | 1631 | if (!self.base.options.disable_lld_caching) { |
| 1624 | // means that the next invocation will have an unnecessary cache miss. | 1632 | // Update the dangling symlink with the digest. If it fails we can continue; it only |
| 1625 | directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| { | 1633 | // means that the next invocation will have an unnecessary cache miss. |
| 1626 | std.log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)}); | 1634 | directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| { |
| 1627 | }; | 1635 | std.log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)}); |
| 1628 | // Again failure here only means an unnecessary cache miss. | 1636 | }; |
| 1629 | ch.writeManifest() catch |err| { | 1637 | // Again failure here only means an unnecessary cache miss. |
| 1630 | std.log.warn("failed to write cache manifest when linking: {}", .{ @errorName(err) }); | 1638 | man.writeManifest() catch |err| { |
| 1631 | }; | 1639 | std.log.warn("failed to write cache manifest when linking: {}", .{ @errorName(err) }); |
| 1632 | // We hang on to this lock so that the output file path can be used without | 1640 | }; |
| 1633 | // other processes clobbering it. | 1641 | // We hang on to this lock so that the output file path can be used without |
| 1634 | self.base.lock = ch.toOwnedLock(); | 1642 | // other processes clobbering it. |
| 1643 | self.base.lock = man.toOwnedLock(); | ||
| 1644 | } | ||
| 1635 | } | 1645 | } |
| 1636 | 1646 | ||
| 1637 | const LLDContext = struct { | 1647 | const LLDContext = struct { |
src/main.zig+1| ... | @@ -1425,6 +1425,7 @@ pub fn buildOutputType( | ... | @@ -1425,6 +1425,7 @@ pub fn buildOutputType( |
| 1425 | .test_evented_io = test_evented_io, | 1425 | .test_evented_io = test_evented_io, |
| 1426 | .test_filter = test_filter, | 1426 | .test_filter = test_filter, |
| 1427 | .test_name_prefix = test_name_prefix, | 1427 | .test_name_prefix = test_name_prefix, |
| 1428 | .disable_lld_caching = !have_enable_cache, | ||
| 1428 | }) catch |err| { | 1429 | }) catch |err| { |
| 1429 | fatal("unable to create compilation: {}", .{@errorName(err)}); | 1430 | fatal("unable to create compilation: {}", .{@errorName(err)}); |
| 1430 | }; | 1431 | }; |