Commit cb635e084b

Andrew Kelley <andrew@ziglang.org>
2022-10-18 01:54:52
stage2: better handling of CacheMode.whole on Windows
Windows gives AccessDenied if you delete a directory which contains open file handles. This could be triggered when using CacheMode.whole when cross compiling macho test binaries.
1 parent 9ee4530
Changed files (2)
src/Compilation.zig
@@ -2390,9 +2390,21 @@ pub fn update(comp: *Compilation) !void {
         const o_sub_path = try std.fs.path.join(comp.gpa, &[_][]const u8{ "o", &digest });
         defer comp.gpa.free(o_sub_path);
 
+        // Work around windows `AccessDenied` if any files within this directory are open
+        // by doing the makeExecutable/makeWritable dance.
+        const need_writable_dance = builtin.os.tag == .windows and comp.bin_file.file != null;
+        if (need_writable_dance) {
+            try comp.bin_file.makeExecutable();
+        }
+
         try comp.bin_file.renameTmpIntoCache(comp.local_cache_directory, tmp_dir_sub_path, o_sub_path);
         comp.wholeCacheModeSetBinFilePath(&digest);
 
+        // Has to be after the `wholeCacheModeSetBinFilePath` above.
+        if (need_writable_dance) {
+            try comp.bin_file.makeWritable();
+        }
+
         // This is intentionally sandwiched between renameTmpIntoCache() and writeManifest().
         if (comp.bin_file.options.module) |module| {
             // We need to set the zig_cache_artifact_directory for -femit-asm, -femit-llvm-ir,
@@ -3207,8 +3219,8 @@ fn processOneJob(comp: *Compilation, job: Job) !void {
                 // TODO Surface more error details.
                 comp.lockAndSetMiscFailure(
                     .mingw_crt_file,
-                    "unable to build mingw-w64 CRT file: {s}",
-                    .{@errorName(err)},
+                    "unable to build mingw-w64 CRT file {s}: {s}",
+                    .{ @tagName(crt_file), @errorName(err) },
                 );
             };
         },
src/link.zig
@@ -403,10 +403,8 @@ pub const File = struct {
                         try emit.directory.handle.copyFile(emit.sub_path, emit.directory.handle, emit.sub_path, .{});
                     }
                 }
-                if (base.intermediary_basename == null) {
-                    f.close();
-                    base.file = null;
-                }
+                f.close();
+                base.file = null;
             },
             .coff, .elf, .plan9, .wasm => if (base.file) |f| {
                 if (base.intermediary_basename != null) {
@@ -777,7 +775,7 @@ pub const File = struct {
         _ = base;
         while (true) {
             if (builtin.os.tag == .windows) {
-                // workaround windows `renameW` can't fail with `PathAlreadyExists`
+                // Work around windows `renameW` can't fail with `PathAlreadyExists`
                 // See https://github.com/ziglang/zig/issues/8362
                 if (cache_directory.handle.access(o_sub_path, .{})) |_| {
                     try cache_directory.handle.deleteTree(o_sub_path);
@@ -791,9 +789,9 @@ pub const File = struct {
                     tmp_dir_sub_path,
                     cache_directory.handle,
                     o_sub_path,
-                ) catch |err| switch (err) {
-                    error.AccessDenied => unreachable, // We are most likely trying to move a dir with open handles to its resources
-                    else => |e| return e,
+                ) catch |err| {
+                    log.err("unable to rename cache dir {s} to {s}: {s}", .{ tmp_dir_sub_path, o_sub_path, @errorName(err) });
+                    return err;
                 };
                 break;
             } else {