@@ -3125,50 +3125,54 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE
31253125 const bin_digest = man .finalBin ();
31263126 const hex_digest = Cache .binToHex (bin_digest );
31273127
3128- // Work around windows `AccessDenied` if any files within this
3129- // directory are open by closing and reopening the file handles.
3130- // While this need is only documented on windows, there are some
3131- // niche scenarios, such as WSL on ReFS, where it may be required
3132- // on other platforms. As the workaround is low-cost, just
3133- // use it on all platforms rather than trying to isolate every
3134- // specific case where it's needed.
3135- const need_writable_dance : enum { no , lf_only , lf_and_debug } = w : {
3136- if (comp .bin_file ) | lf | {
3137- // We cannot just call `makeExecutable` as it makes a false
3138- // assumption that we have a file handle open only when linking
3139- // an executable file. This used to be true when our linkers
3140- // were incapable of emitting relocatables and static archive.
3141- // Now that they are capable, we need to unconditionally close
3142- // the file handle and re-open it in the follow up call to
3143- // `makeWritable`.
3144- if (lf .file ) | f | {
3145- f .close ();
3146- lf .file = null ;
3147-
3148- if (lf .closeDebugInfo ()) break :w .lf_and_debug ;
3149- break :w .lf_only ;
3150- }
3151- }
3152- break :w .no ;
3153- };
3154-
3155- // Rename the temporary directory into place.
3156- // Close tmp dir and link.File to avoid open handle during rename.
31573128 whole .tmp_artifact_directory .? .handle .close ();
31583129 whole .tmp_artifact_directory = null ;
31593130 const s = fs .path .sep_str ;
31603131 const tmp_dir_sub_path = "tmp" ++ s ++ std .fmt .hex (tmp_dir_rand_int );
31613132 const o_sub_path = "o" ++ s ++ hex_digest ;
3162- renameTmpIntoCache (comp .dirs .local_cache , tmp_dir_sub_path , o_sub_path ) catch | err | {
3163- return comp .setMiscFailure (
3164- .rename_results ,
3165- "failed to rename compilation results ('{f}{s}') into local cache ('{f}{s}'): {t}" ,
3166- .{
3167- comp .dirs .local_cache , tmp_dir_sub_path ,
3168- comp .dirs .local_cache , o_sub_path ,
3169- err ,
3170- },
3171- );
3133+
3134+ // Work around situations (host OS or filesystem) that disallow
3135+ // renaming a directory if any files within have open handles.
3136+ // This is only guaranteed required on windows, but has been
3137+ // observed in one other niche environment (ReFS on WSL).
3138+ const need_writable_dance : link.File.ClosedFiles = dance : {
3139+ // The workaround is only guaranteed required on windows, so try
3140+ // just doing the rename straight up first on other platforms.
3141+ if (builtin .os .tag != .windows ) b : {
3142+ renameTmpIntoCache (comp .dirs .local_cache , tmp_dir_sub_path , o_sub_path ) catch | err | {
3143+ // If we get AccessDenied, assume the host needs the close/open dance and try again.
3144+ if (err == error .AccessDenied ) {
3145+ break :b ;
3146+ }
3147+ return comp .setMiscFailure (
3148+ .rename_results ,
3149+ "failed to rename compilation results ('{f}{s}') into local cache ('{f}{s}'): {t}" ,
3150+ .{
3151+ comp .dirs .local_cache , tmp_dir_sub_path ,
3152+ comp .dirs .local_cache , o_sub_path ,
3153+ err ,
3154+ },
3155+ );
3156+ };
3157+ break :dance .no ;
3158+ }
3159+
3160+ std .debug .print ("trying windows AccessDenied workaround\n " , .{});
3161+
3162+ const d = if (comp .bin_file ) | f | f .closeBin () else .no ;
3163+ renameTmpIntoCache (comp .dirs .local_cache , tmp_dir_sub_path , o_sub_path ) catch | err | {
3164+ // If we get AccessDenied here, its not the issue we're working around, so we abort.
3165+ return comp .setMiscFailure (
3166+ .rename_results ,
3167+ "failed to rename compilation results ('{f}{s}') into local cache ('{f}{s}'): {t}" ,
3168+ .{
3169+ comp .dirs .local_cache , tmp_dir_sub_path ,
3170+ comp .dirs .local_cache , o_sub_path ,
3171+ err ,
3172+ },
3173+ );
3174+ };
3175+ break :dance d ;
31723176 };
31733177 comp .digest = bin_digest ;
31743178
@@ -3180,15 +3184,9 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE
31803184 .root_dir = comp .dirs .local_cache ,
31813185 .sub_path = try fs .path .join (arena , &.{ o_sub_path , comp .emit_bin .? }),
31823186 };
3183- const result : (link .File .OpenError || error { HotSwapUnavailableOnHostOperatingSystem , RenameAcrossMountPoints , InvalidFileName })! void = switch (need_writable_dance ) {
3184- .no = > {},
3185- .lf_only = > lf .makeWritable (),
3186- .lf_and_debug = > res : {
3187- lf .makeWritable () catch | err | break :res err ;
3188- lf .reopenDebugInfo () catch | err | break :res err ;
3189- },
3190- };
3191- result catch | err | {
3187+ // reopenBin will call makeWritable for us, as well as any
3188+ // extra work needed beyond that function for certain backends
3189+ lf .reopenBin (need_writable_dance ) catch | err | {
31923190 return comp .setMiscFailure (
31933191 .rename_results ,
31943192 "failed to re-open renamed compilation results ('{f}{s}'): {t}" ,
0 commit comments