Skip to content

Commit cc1a16c

Browse files
authored
fix: allow rename to overwrite read-only destination on Windows when deduplicating Dune cache (#13713)
In the Dune cache, when a temp artifact already exists in the cache, Dune attempts deduplication by deleting the temp artifact, linking it to the file already in the cache, and then moving the temp artifact to the build directory. The last step fails on Windows because Dune sets a read-only attribute on the destination file, causing `Unix.rename` to fail with `EACCES` in this case. Since the operation fails, Dune does not consider the file cached. Combined with the fact that this seems to happen quite often [1] on Windows, this effectively nullifies the cache speedup. This PR creates a helper function that removes the read-only attribute on the destination file before trying to overwrite it (similar to the existing `win32_unlink`) and use it in the deduplication logic of the cache, fixing the issue. _Note: This issue does not exist on Unix. This discrepancy is also being addressed directly on OCaml's side: ocaml/ocaml#14602 The test dedup.t already exhibits the issue on Windows. The `dune_cmd stat hardlinks _build/default/target` command returns 1 instead of the expected 3. This PR bumps the result to 2. To make the test completely pass, another issue around `Io.portable_hardlink` on Windows still needs to be addressed [1]. [1] It seems that the fact that on windows dune always copies files instead of trying to create a hardlink is linked to the fact that this deduplication happen more often on windows. See #13714. --------- Signed-off-by: Roven Lamar Gabriel <nevor@nevor.net>
1 parent 2419738 commit cc1a16c

File tree

4 files changed

+22
-1
lines changed

4 files changed

+22
-1
lines changed

doc/changes/fixed/13713.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
- Fix the Dune cache on Windows by correctly handling renames onto read-only
2+
files. Before this change, the Dune cache would be filled but the stored
3+
artifacts would not generally be usable by Dune. (#13713, @Nevor)

otherlibs/stdune/src/fpath.ml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,23 @@ let unlink_exn ~chmod dir fn =
204204
unlink_exn_ignore_missing fn
205205
;;
206206

207+
let win32_rename_exn src dst =
208+
match Unix.rename src dst with
209+
| () -> ()
210+
| exception Unix.Unix_error (Unix.EACCES, _, _) ->
211+
(* Try removing the read-only attribute.
212+
Workaround a behavior discrepancy between Unix and Windows of the
213+
[Unix.rename] function. It accepts readonly override on Unix but not
214+
on Windows. This discrepancy will be fixed in OCaml 5.6 which will
215+
include https://github.com/ocaml/ocaml/pull/14602 *)
216+
Unix.chmod dst 0o666;
217+
Unix.rename src dst
218+
;;
219+
220+
let rename_exn =
221+
if Stdlib.Sys.win32 then fun x y -> win32_rename_exn x y else fun x y -> Unix.rename x y
222+
;;
223+
207224
let rec clear_dir ?(chmod = false) dir =
208225
match
209226
match Readdir.read_directory_with_kinds dir with

otherlibs/stdune/src/fpath.mli

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ type unlink_status =
4646
(** Unlink and return error, if any. *)
4747
val unlink : string -> unlink_status
4848

49+
val rename_exn : string -> string -> unit
4950
val initial_cwd : string
5051

5152
type clear_dir_result =

src/dune_cache/local.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ module Artifacts = struct
191191
[rename] operation has a quirk where [path_in_temp_dir] can
192192
remain on disk. This is not a problem because we clean the
193193
temporary directory later. *)
194-
Unix.rename
194+
Fpath.rename_exn
195195
(Path.to_string path_in_temp_dir)
196196
(Path.to_string path_in_build_dir)
197197
with

0 commit comments

Comments
 (0)