| author | |
| committer | |
| log | 725c82582935d2bb84d52d906edb2975827b01ec |
| tree | 4a46d9a3f26ec99e4da17e61eeec7c5b5a601fb3 |
| parent | c2983a3f88411c1c9c20582c43054c07cbbd88b3 |
Windows is a ridiculous operating system designed by toddlers, and so
requires us to close all file handles in the `tmp/xxxxxxx` cache dir
before renaming it into `o/xxxxxxx`. We have a hack in place to handle
this for the main output file, but the MachO linker also outputs a file
with debug symbols, and we weren't closing it! This led to a fuckton of
CI failures when we enabled `.whole` cache mode by default for
self-hosted backends.
thanks jacob for figuring this out while i sat there3 files changed, 61 insertions(+), 25 deletions(-)
src/Compilation.zig+12-5| ... | @@ -2397,7 +2397,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { | ... | @@ -2397,7 +2397,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { |
| 2397 | 2397 | ||
| 2398 | // Work around windows `AccessDenied` if any files within this | 2398 | // Work around windows `AccessDenied` if any files within this |
| 2399 | // directory are open by closing and reopening the file handles. | 2399 | // directory are open by closing and reopening the file handles. |
| 2400 | const need_writable_dance = w: { | 2400 | const need_writable_dance: enum { no, lf_only, lf_and_debug } = w: { |
| 2401 | if (builtin.os.tag == .windows) { | 2401 | if (builtin.os.tag == .windows) { |
| 2402 | if (comp.bin_file) |lf| { | 2402 | if (comp.bin_file) |lf| { |
| 2403 | // We cannot just call `makeExecutable` as it makes a false | 2403 | // We cannot just call `makeExecutable` as it makes a false |
| ... | @@ -2410,11 +2410,13 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { | ... | @@ -2410,11 +2410,13 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { |
| 2410 | if (lf.file) |f| { | 2410 | if (lf.file) |f| { |
| 2411 | f.close(); | 2411 | f.close(); |
| 2412 | lf.file = null; | 2412 | lf.file = null; |
| 2413 | break :w true; | 2413 | |
| 2414 | if (lf.closeDebugInfo()) break :w .lf_and_debug; | ||
| 2415 | break :w .lf_only; | ||
| 2414 | } | 2416 | } |
| 2415 | } | 2417 | } |
| 2416 | } | 2418 | } |
| 2417 | break :w false; | 2419 | break :w .no; |
| 2418 | }; | 2420 | }; |
| 2419 | 2421 | ||
| 2420 | renameTmpIntoCache(comp.local_cache_directory, tmp_dir_sub_path, o_sub_path) catch |err| { | 2422 | renameTmpIntoCache(comp.local_cache_directory, tmp_dir_sub_path, o_sub_path) catch |err| { |
| ... | @@ -2441,8 +2443,13 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { | ... | @@ -2441,8 +2443,13 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { |
| 2441 | }; | 2443 | }; |
| 2442 | 2444 | ||
| 2443 | // Has to be after the `wholeCacheModeSetBinFilePath` above. | 2445 | // Has to be after the `wholeCacheModeSetBinFilePath` above. |
| 2444 | if (need_writable_dance) { | 2446 | switch (need_writable_dance) { |
| 2445 | try lf.makeWritable(); | 2447 | .no => {}, |
| 2448 | .lf_only => try lf.makeWritable(), | ||
| 2449 | .lf_and_debug => { | ||
| 2450 | try lf.makeWritable(); | ||
| 2451 | try lf.reopenDebugInfo(); | ||
| 2452 | }, | ||
| 2446 | } | 2453 | } |
| 2447 | } | 2454 | } |
| 2448 | 2455 |
src/link.zig+14| ... | @@ -600,6 +600,20 @@ pub const File = struct { | ... | @@ -600,6 +600,20 @@ pub const File = struct { |
| 600 | } | 600 | } |
| 601 | } | 601 | } |
| 602 | 602 | ||
| 603 | /// Some linkers create a separate file for debug info, which we might need to temporarily close | ||
| 604 | /// when moving the compilation result directory due to the host OS not allowing moving a | ||
| 605 | /// file/directory while a handle remains open. | ||
| 606 | /// Returns `true` if a debug info file was closed. In that case, `reopenDebugInfo` may be called. | ||
| 607 | pub fn closeDebugInfo(base: *File) bool { | ||
| 608 | const macho = base.cast(.macho) orelse return false; | ||
| 609 | return macho.closeDebugInfo(); | ||
| 610 | } | ||
| 611 | |||
| 612 | pub fn reopenDebugInfo(base: *File) !void { | ||
| 613 | const macho = base.cast(.macho).?; | ||
| 614 | return macho.reopenDebugInfo(); | ||
| 615 | } | ||
| 616 | |||
| 603 | pub fn makeExecutable(base: *File) !void { | 617 | pub fn makeExecutable(base: *File) !void { |
| 604 | dev.check(.make_executable); | 618 | dev.check(.make_executable); |
| 605 | const comp = base.comp; | 619 | const comp = base.comp; |
src/link/MachO.zig+35-20| ... | @@ -3277,6 +3277,40 @@ const InitMetadataOptions = struct { | ... | @@ -3277,6 +3277,40 @@ const InitMetadataOptions = struct { |
| 3277 | program_code_size_hint: u64, | 3277 | program_code_size_hint: u64, |
| 3278 | }; | 3278 | }; |
| 3279 | 3279 | ||
| 3280 | pub fn closeDebugInfo(self: *MachO) bool { | ||
| 3281 | const d_sym = &(self.d_sym orelse return false); | ||
| 3282 | d_sym.deinit(); | ||
| 3283 | self.d_sym = null; | ||
| 3284 | return true; | ||
| 3285 | } | ||
| 3286 | |||
| 3287 | pub fn reopenDebugInfo(self: *MachO) !void { | ||
| 3288 | assert(self.d_sym == null); | ||
| 3289 | |||
| 3290 | assert(!self.base.comp.config.use_llvm); | ||
| 3291 | assert(self.base.comp.config.debug_format == .dwarf); | ||
| 3292 | |||
| 3293 | const gpa = self.base.comp.gpa; | ||
| 3294 | const sep = fs.path.sep_str; | ||
| 3295 | const d_sym_path = try std.fmt.allocPrint( | ||
| 3296 | gpa, | ||
| 3297 | "{s}.dSYM" ++ sep ++ "Contents" ++ sep ++ "Resources" ++ sep ++ "DWARF", | ||
| 3298 | .{self.base.emit.sub_path}, | ||
| 3299 | ); | ||
| 3300 | defer gpa.free(d_sym_path); | ||
| 3301 | |||
| 3302 | var d_sym_bundle = try self.base.emit.root_dir.handle.makeOpenPath(d_sym_path, .{}); | ||
| 3303 | defer d_sym_bundle.close(); | ||
| 3304 | |||
| 3305 | const d_sym_file = try d_sym_bundle.createFile(self.base.emit.sub_path, .{ | ||
| 3306 | .truncate = false, | ||
| 3307 | .read = true, | ||
| 3308 | }); | ||
| 3309 | |||
| 3310 | self.d_sym = .{ .allocator = gpa, .file = d_sym_file }; | ||
| 3311 | try self.d_sym.?.initMetadata(self); | ||
| 3312 | } | ||
| 3313 | |||
| 3280 | // TODO: move to ZigObject | 3314 | // TODO: move to ZigObject |
| 3281 | fn initMetadata(self: *MachO, options: InitMetadataOptions) !void { | 3315 | fn initMetadata(self: *MachO, options: InitMetadataOptions) !void { |
| 3282 | if (!self.base.isRelocatable()) { | 3316 | if (!self.base.isRelocatable()) { |
| ... | @@ -3333,26 +3367,7 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void { | ... | @@ -3333,26 +3367,7 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void { |
| 3333 | if (options.zo.dwarf) |*dwarf| { | 3367 | if (options.zo.dwarf) |*dwarf| { |
| 3334 | // Create dSYM bundle. | 3368 | // Create dSYM bundle. |
| 3335 | log.debug("creating {s}.dSYM bundle", .{options.emit.sub_path}); | 3369 | log.debug("creating {s}.dSYM bundle", .{options.emit.sub_path}); |
| 3336 | 3370 | try self.reopenDebugInfo(); | |
| 3337 | const gpa = self.base.comp.gpa; | ||
| 3338 | const sep = fs.path.sep_str; | ||
| 3339 | const d_sym_path = try std.fmt.allocPrint( | ||
| 3340 | gpa, | ||
| 3341 | "{s}.dSYM" ++ sep ++ "Contents" ++ sep ++ "Resources" ++ sep ++ "DWARF", | ||
| 3342 | .{options.emit.sub_path}, | ||
| 3343 | ); | ||
| 3344 | defer gpa.free(d_sym_path); | ||
| 3345 | |||
| 3346 | var d_sym_bundle = try options.emit.root_dir.handle.makeOpenPath(d_sym_path, .{}); | ||
| 3347 | defer d_sym_bundle.close(); | ||
| 3348 | |||
| 3349 | const d_sym_file = try d_sym_bundle.createFile(options.emit.sub_path, .{ | ||
| 3350 | .truncate = false, | ||
| 3351 | .read = true, | ||
| 3352 | }); | ||
| 3353 | |||
| 3354 | self.d_sym = .{ .allocator = gpa, .file = d_sym_file }; | ||
| 3355 | try self.d_sym.?.initMetadata(self); | ||
| 3356 | try dwarf.initMetadata(); | 3371 | try dwarf.initMetadata(); |
| 3357 | } | 3372 | } |
| 3358 | } | 3373 | } |