diff --git a/doc/changes/fixed/13713.md b/doc/changes/fixed/13713.md new file mode 100644 index 00000000000..23812ed582e --- /dev/null +++ b/doc/changes/fixed/13713.md @@ -0,0 +1,3 @@ +- Fix the Dune cache on Windows by correctly handling renames onto read-only + files. Before this change, the Dune cache would be filled but the stored + artifacts would not generally be usable by Dune. (#13713, @Nevor) diff --git a/otherlibs/stdune/src/fpath.ml b/otherlibs/stdune/src/fpath.ml index 1ccb454d21d..9f25579a587 100644 --- a/otherlibs/stdune/src/fpath.ml +++ b/otherlibs/stdune/src/fpath.ml @@ -204,6 +204,23 @@ let unlink_exn ~chmod dir fn = unlink_exn_ignore_missing fn ;; +let win32_rename_exn src dst = + match Unix.rename src dst with + | () -> () + | exception Unix.Unix_error (Unix.EACCES, _, _) -> + (* Try removing the read-only attribute. + Workaround a behavior discrepancy between Unix and Windows of the + [Unix.rename] function. It accepts readonly override on Unix but not + on Windows. This discrepancy will be fixed in OCaml 5.6 which will + include https://github.com/ocaml/ocaml/pull/14602 *) + Unix.chmod dst 0o666; + Unix.rename src dst +;; + +let rename_exn = + if Stdlib.Sys.win32 then fun x y -> win32_rename_exn x y else fun x y -> Unix.rename x y +;; + let rec clear_dir ?(chmod = false) dir = match match Readdir.read_directory_with_kinds dir with diff --git a/otherlibs/stdune/src/fpath.mli b/otherlibs/stdune/src/fpath.mli index a7b23251c49..47bd93e46b3 100644 --- a/otherlibs/stdune/src/fpath.mli +++ b/otherlibs/stdune/src/fpath.mli @@ -46,6 +46,7 @@ type unlink_status = (** Unlink and return error, if any. *) val unlink : string -> unlink_status +val rename_exn : string -> string -> unit val initial_cwd : string type clear_dir_result = diff --git a/src/dune_cache/local.ml b/src/dune_cache/local.ml index d7ff859bcfa..77d9a437411 100644 --- a/src/dune_cache/local.ml +++ b/src/dune_cache/local.ml @@ -191,7 +191,7 @@ module Artifacts = struct [rename] operation has a quirk where [path_in_temp_dir] can remain on disk. This is not a problem because we clean the temporary directory later. *) - Unix.rename + Fpath.rename_exn (Path.to_string path_in_temp_dir) (Path.to_string path_in_build_dir) with