Skip to content

Commit 6a62109

Browse files
authored
refactor: pull [Cached_digest] move (ocaml#10313)
It was moved to [Dune_digest] Signed-off-by: Rudi Grinberg <[email protected]>
1 parent 77c5419 commit 6a62109

File tree

18 files changed

+198
-193
lines changed

18 files changed

+198
-193
lines changed

bin/common.ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1258,9 +1258,9 @@ let init (builder : Builder.t) =
12581258
Dune_engine.Clflags.debug_backtraces c.builder.debug_backtraces;
12591259
Dune_rules.Clflags.debug_artifact_substitution := c.builder.debug_artifact_substitution;
12601260
Dune_engine.Clflags.debug_load_dir := c.builder.debug_load_dir;
1261-
Dune_engine.Clflags.debug_digests := c.builder.debug_digests;
12621261
Dune_engine.Clflags.debug_fs_cache := c.builder.cache_debug_flags.fs_cache;
1263-
Dune_engine.Clflags.wait_for_filesystem_clock := c.builder.wait_for_filesystem_clock;
1262+
Dune_digest.Clflags.debug_digests := c.builder.debug_digests;
1263+
Dune_digest.Clflags.wait_for_filesystem_clock := c.builder.wait_for_filesystem_clock;
12641264
Dune_engine.Clflags.capture_outputs := c.builder.capture_outputs;
12651265
Dune_engine.Clflags.diff_command := c.builder.diff_command;
12661266
Dune_engine.Clflags.promote := c.builder.promote;

bin/import.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ include struct
1616
module Dpath = Dpath
1717
module Findlib = Dune_rules.Findlib
1818
module Diff_promotion = Diff_promotion
19-
module Cached_digest = Cached_digest
2019
module Targets = Targets
2120
module Context_name = Context_name
2221
end
2322

23+
module Cached_digest = Dune_digest.Cached_digest
2424
module Execution_env = Dune_util.Execution_env
2525

2626
include struct
Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ let cache =
9292
let get_current_filesystem_time () =
9393
let special_path = Path.relative Path.build_dir ".filesystem-clock" in
9494
Io.write_file special_path "<dummy>";
95-
(Path.Untracked.stat_exn special_path).st_mtime
95+
(Path.stat_exn special_path).st_mtime
9696
;;
9797

9898
let wait_for_fs_clock_to_advance () =
@@ -176,7 +176,7 @@ let set_with_stat path digest stat =
176176
let set path digest =
177177
(* the caller of [set] ensures that the files exist *)
178178
let path = Path.build path in
179-
let stat = Path.Untracked.stat_exn path in
179+
let stat = Path.stat_exn path in
180180
set_with_stat path digest stat
181181
;;
182182

@@ -260,12 +260,12 @@ let catch_fs_errors f =
260260
(* Here we make only one [stat] call on the happy path. *)
261261
let refresh_without_removing_write_permissions ~allow_dirs path =
262262
catch_fs_errors (fun () ->
263-
match Path.Untracked.stat_exn path with
263+
match Path.stat_exn path with
264264
| stats -> refresh stats ~allow_dirs path
265265
| exception Unix.Unix_error (ELOOP, _, _) -> Error Cyclic_symlink
266266
| exception Unix.Unix_error (ENOENT, _, _) ->
267267
(* Test if this is a broken symlink for better error messages. *)
268-
(match Path.Untracked.lstat_exn path with
268+
(match Path.lstat_exn path with
269269
| exception Unix.Unix_error (ENOENT, _, _) -> Error No_such_file
270270
| _stats_so_must_be_a_symlink -> Error Broken_symlink))
271271
;;
@@ -277,12 +277,12 @@ let refresh_without_removing_write_permissions ~allow_dirs path =
277277
here, e.g., by telling the subsequent [chmod] to not follow symlinks. *)
278278
let refresh_and_remove_write_permissions ~allow_dirs path =
279279
catch_fs_errors (fun () ->
280-
match Path.Untracked.lstat_exn path with
280+
match Path.lstat_exn path with
281281
| exception Unix.Unix_error (ENOENT, _, _) -> Error No_such_file
282282
| stats ->
283283
(match stats.st_kind with
284284
| S_LNK ->
285-
(match Path.Untracked.stat_exn path with
285+
(match Path.stat_exn path with
286286
| stats -> refresh stats ~allow_dirs:false path
287287
| exception Unix.Unix_error (ELOOP, _, _) -> Error Cyclic_symlink
288288
| exception Unix.Unix_error (ENOENT, _, _) -> Error Broken_symlink)
@@ -314,7 +314,7 @@ let peek_file ~allow_dirs path =
314314
then Ok x.digest
315315
else (
316316
(* The [stat_exn] below follows symlinks. *)
317-
match Path.Untracked.stat_exn path with
317+
match Path.stat_exn path with
318318
| exception Unix.Unix_error (ELOOP, _, _) ->
319319
Error Digest_result.Error.Cyclic_symlink
320320
| exception Unix.Unix_error (ENOENT, _, _) -> Error No_such_file
@@ -374,9 +374,12 @@ let remove path =
374374
;;
375375

376376
module Untracked = struct
377-
let source_or_external_file = peek_or_refresh_file ~allow_dirs:false
377+
let source_or_external_file path =
378+
peek_or_refresh_file ~allow_dirs:false (Path.outside_build_dir path)
379+
;;
378380

379381
let invalidate_cached_timestamp path =
382+
let path = Path.outside_build_dir path in
380383
let cache = Lazy.force cache in
381384
match Path.Table.find cache.table path with
382385
| None -> ()
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,11 @@ val refresh
4141
module Untracked : sig
4242
(** Digest the contents of a source or external file. This function doesn't
4343
track the source file. For a tracked version, see [fs_memo.mli]. *)
44-
val source_or_external_file : Path.t -> Digest_result.t
44+
val source_or_external_file : Path.Outside_build_dir.t -> Digest_result.t
4545

4646
(** Invalidate the cached [stat] value. This causes the subsequent call to
4747
[source_or_external_file] to incur an additional [stat] call. *)
48-
val invalidate_cached_timestamp : Path.t -> unit
48+
val invalidate_cached_timestamp : Path.Outside_build_dir.t -> unit
4949
end
5050

5151
(** {1 Managing the cache} *)

src/dune_digest/clflags.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
let wait_for_filesystem_clock = ref false
2+
let debug_digests = ref false

src/dune_digest/clflags.mli

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
(** Wait for the filesystem clock to advance rather than dropping cached digest
2+
entries *)
3+
val wait_for_filesystem_clock : bool ref
4+
5+
(** Print debug info for cached digests *)
6+
val debug_digests : bool ref

src/dune_digest/digest.ml

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
open Stdune
2+
3+
type t = string
4+
5+
external md5_fd : Unix.file_descr -> string = "dune_md5_fd"
6+
7+
module D = Stdlib.Digest
8+
module Set = String.Set
9+
module Map = String.Map
10+
module Metrics = Dune_metrics
11+
12+
module type Digest_impl = sig
13+
val file : string -> t
14+
val string : string -> t
15+
end
16+
17+
module Direct_impl : Digest_impl = struct
18+
let file file =
19+
(* On Windows, if this function is invoked in a background thread,
20+
if can happen that the file is not properly closed.
21+
[O_SHARE_DELETE] ensures that the main thread can delete it even if it
22+
is still open. See #8243. *)
23+
let fd =
24+
match Unix.openfile file [ Unix.O_RDONLY; O_SHARE_DELETE; O_CLOEXEC ] 0 with
25+
| fd -> fd
26+
| exception Unix.Unix_error (Unix.EACCES, _, _) ->
27+
raise (Sys_error (sprintf "%s: Permission denied" file))
28+
| exception exn -> reraise exn
29+
in
30+
Exn.protectx fd ~f:md5_fd ~finally:Unix.close
31+
;;
32+
33+
let string = D.string
34+
end
35+
36+
module Mutable_impl = struct
37+
let file_ref = ref Direct_impl.file
38+
let string_ref = ref D.string
39+
let file f = !file_ref f
40+
let string s = !string_ref s
41+
end
42+
43+
let override_impl ~file ~string =
44+
Mutable_impl.file_ref := file;
45+
Mutable_impl.string_ref := string
46+
;;
47+
48+
module Impl : Digest_impl = Mutable_impl
49+
50+
let hash = Poly.hash
51+
let equal = String.equal
52+
let file p = Impl.file (Path.to_string p)
53+
let compare x y = Ordering.of_int (D.compare x y)
54+
let to_string = D.to_hex
55+
let to_dyn s = Dyn.variant "digest" [ String (to_string s) ]
56+
57+
let from_hex s =
58+
match D.from_hex s with
59+
| s -> Some s
60+
| exception Invalid_argument _ -> None
61+
;;
62+
63+
let string = Impl.string
64+
let to_string_raw s = s
65+
66+
(* We use [No_sharing] to avoid generating different digests for inputs that
67+
differ only in how they share internal values. Without [No_sharing], if a
68+
command line contains duplicate flags, such as multiple occurrences of the
69+
flag [-I], then [Marshal.to_string] will produce different digests depending
70+
on whether the corresponding strings ["-I"] point to the same memory location
71+
or to different memory locations. *)
72+
let generic a =
73+
Metrics.Timer.record "generic_digest" ~f:(fun () ->
74+
string (Marshal.to_string a [ No_sharing ]))
75+
;;
76+
77+
let path_with_executable_bit =
78+
(* We follow the digest scheme used by Jenga. *)
79+
let string_and_bool ~digest_hex ~bool =
80+
Impl.string (digest_hex ^ if bool then "\001" else "\000")
81+
in
82+
fun ~executable ~content_digest ->
83+
string_and_bool ~digest_hex:content_digest ~bool:executable
84+
;;
85+
86+
let file_with_executable_bit ~executable path =
87+
let content_digest = file path in
88+
path_with_executable_bit ~content_digest ~executable
89+
;;
90+
91+
module Stats_for_digest = struct
92+
type t =
93+
{ st_kind : Unix.file_kind
94+
; st_perm : Unix.file_perm
95+
}
96+
97+
let of_unix_stats (stats : Unix.stats) =
98+
{ st_kind = stats.st_kind; st_perm = stats.st_perm }
99+
;;
100+
end
101+
102+
module Path_digest_error = struct
103+
type nonrec t =
104+
| Unexpected_kind
105+
| Unix_error of Dune_filesystem_stubs.Unix_error.Detailed.t
106+
end
107+
108+
exception E of Path_digest_error.t
109+
110+
let directory_digest_version = 2
111+
112+
let path_with_stats ~allow_dirs path (stats : Stats_for_digest.t) =
113+
let rec loop path (stats : Stats_for_digest.t) =
114+
match stats.st_kind with
115+
| S_LNK ->
116+
let executable = Path.Permissions.test Path.Permissions.execute stats.st_perm in
117+
Dune_filesystem_stubs.Unix_error.Detailed.catch
118+
(fun path ->
119+
let contents = Unix.readlink (Path.to_string path) in
120+
path_with_executable_bit ~executable ~content_digest:contents)
121+
path
122+
|> Result.map_error ~f:(fun x -> Path_digest_error.Unix_error x)
123+
| S_REG ->
124+
let executable = Path.Permissions.test Path.Permissions.execute stats.st_perm in
125+
Dune_filesystem_stubs.Unix_error.Detailed.catch
126+
(file_with_executable_bit ~executable)
127+
path
128+
|> Result.map_error ~f:(fun x -> Path_digest_error.Unix_error x)
129+
| S_DIR when allow_dirs ->
130+
(* CR-someday amokhov: The current digesting scheme has collisions for files
131+
and directories. It's unclear if this is actually a problem. If it turns
132+
out to be a problem, we should include [st_kind] into both digests. *)
133+
(match Path.readdir_unsorted path with
134+
| Error e -> Error (Path_digest_error.Unix_error e)
135+
| Ok listing ->
136+
(match
137+
List.rev_map listing ~f:(fun name ->
138+
let path = Path.relative path name in
139+
let stats =
140+
match Path.lstat path with
141+
| Error e -> raise_notrace (E (Unix_error e))
142+
| Ok stat -> Stats_for_digest.of_unix_stats stat
143+
in
144+
let digest =
145+
match loop path stats with
146+
| Ok s -> s
147+
| Error e -> raise_notrace (E e)
148+
in
149+
name, digest)
150+
|> List.sort ~compare:(fun (x, _) (y, _) -> String.compare x y)
151+
with
152+
| exception E e -> Error e
153+
| contents -> Ok (generic (directory_digest_version, contents, stats.st_perm))))
154+
| S_DIR | S_BLK | S_CHR | S_FIFO | S_SOCK -> Error Unexpected_kind
155+
in
156+
match stats.st_kind with
157+
| S_DIR when not allow_dirs -> Error Path_digest_error.Unexpected_kind
158+
| S_BLK | S_CHR | S_LNK | S_FIFO | S_SOCK -> Error Unexpected_kind
159+
| _ -> loop path stats
160+
;;

src/dune_digest/dune

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
(library
22
(name dune_digest)
3-
(libraries dune_metrics stdune unix)
3+
(libraries dune_metrics dune_stats dune_console dune_util stdune unix)
44
(foreign_stubs
55
(names dune_digest_stubs)
66
(language c))

0 commit comments

Comments
 (0)