@@ -384,6 +384,34 @@ module Files_of = struct
384384 }
385385end
386386
387+ module Trace = struct
388+ module Entry = struct
389+ type t =
390+ { rule_digest : Digest .t
391+ ; targets_digest : Digest .t
392+ }
393+ end
394+
395+ (* Keyed by the first target *)
396+ type t = Entry .t Path.Table .t
397+
398+ let file = Path. relative Path. build_dir " .db"
399+
400+ module P = Utils. Persistent (struct
401+ type nonrec t = t
402+ let name = " INCREMENTAL-DB"
403+ let version = 2
404+ end )
405+
406+ let dump t =
407+ if Path. build_dir_exists () then P. dump file t
408+
409+ let load () =
410+ match P. load file with
411+ | Some t -> t
412+ | None -> Path.Table. create 1024
413+ end
414+
387415type extra_sub_directories_to_keep =
388416 | All
389417 | These of String.Set .t
@@ -398,7 +426,7 @@ type t =
398426 ; contexts : Context .t String.Map .t
399427 ; (* Table from target to digest of
400428 [(deps (filename + contents), targets (filename only), action)] *)
401- trace : Digest .t Path.Table .t
429+ trace : Trace .t
402430 ; file_tree : File_tree .t
403431 ; mutable local_mkdirs : Path.Set .t
404432 ; mutable dirs : Dir_status .t Path.Table .t
@@ -617,18 +645,18 @@ let () =
617645 pending_targets := Path.Set. empty;
618646 Path.Set. iter fns ~f: Path. unlink_no_err)
619647
620- let clear_targets_digests_after_rule_execution targets =
621- let missing =
622- List. fold_left targets ~init: Path.Set. empty ~f: (fun acc fn ->
623- match Unix. lstat (Path. to_string fn) with
624- | exception _ -> Path.Set. add acc fn
625- | (_ : Unix.stats ) ->
626- Utils.Cached_digest. remove fn;
627- acc)
648+ let compute_targets_digest_after_rule_execution targets =
649+ let good, bad =
650+ List. partition_map targets ~f: (fun fn ->
651+ match Utils.Cached_digest. refresh fn with
652+ | digest -> Left digest
653+ | exception (Unix. Unix_error _ | Sys_error _ ) -> Right fn)
628654 in
629- if not (Path.Set. is_empty missing) then
655+ match bad with
656+ | [] -> Digest. string (Marshal. to_string good [] )
657+ | missing ->
630658 die " @{<error>Error@}: Rule failed to generate the following targets:\n %s"
631- (string_of_paths missing)
659+ (string_of_paths ( Path.Set. of_list missing) )
632660
633661let make_local_dir t fn =
634662 if not (Path.Set. mem t.local_mkdirs fn) then begin
@@ -762,7 +790,9 @@ let rec compile_rule t ?(copy_source=false) pre_rule =
762790 | None -> Env. empty
763791 | Some c -> c.env
764792 in
765- let hash =
793+ let head_target = List. hd targets_as_list in
794+ let prev_trace = Path.Table. find t.trace head_target in
795+ let rule_digest =
766796 let trace =
767797 ( Deps. trace all_deps env,
768798 List. map targets_as_list ~f: Path. to_string,
@@ -771,34 +801,30 @@ let rec compile_rule t ?(copy_source=false) pre_rule =
771801 in
772802 Digest. string (Marshal. to_string trace [] )
773803 in
804+ let targets_digest =
805+ match List. map targets_as_list ~f: Utils.Cached_digest. file with
806+ | l -> Some (Digest. string (Marshal. to_string l [] ))
807+ | exception (Unix. Unix_error _ | Sys_error _ ) -> None
808+ in
774809 let sandbox_dir =
775810 if sandbox then
776- Some (Path. relative sandbox_dir (Digest. to_hex hash ))
811+ Some (Path. relative sandbox_dir (Digest. to_hex rule_digest ))
777812 else
778813 None
779814 in
780- let deps_or_rule_changed =
781- List. fold_left targets_as_list ~init: false ~f: (fun acc fn ->
782- match Path.Table. find t.trace fn with
783- | None ->
784- Path.Table. add t.trace fn hash;
785- true
786- | Some prev_hash ->
787- Path.Table. replace t.trace ~key: fn ~data: hash;
788- acc || prev_hash <> hash)
789- in
790- let targets_missing =
791- List. exists targets_as_list ~f: (fun fn ->
792- match Unix. lstat (Path. to_string fn) with
793- | exception _ -> true
794- | (_ : Unix.stats ) -> false )
795- in
796815 let force =
797816 ! Clflags. force &&
798817 List. exists targets_as_list ~f: Path. is_alias_stamp_file
799818 in
819+ let something_changed =
820+ match prev_trace, targets_digest with
821+ | Some prev_trace , Some targets_digest ->
822+ prev_trace.rule_digest <> rule_digest ||
823+ prev_trace.targets_digest <> targets_digest
824+ | _ -> true
825+ in
800826 begin
801- if deps_or_rule_changed || targets_missing || force then begin
827+ if force || something_changed then begin
802828 List. iter targets_as_list ~f: Path. unlink_no_err;
803829 pending_targets := Path.Set. union targets ! pending_targets;
804830 let action =
@@ -821,7 +847,11 @@ let rec compile_rule t ?(copy_source=false) pre_rule =
821847 Option. iter sandbox_dir ~f: Path. rm_rf;
822848 (* All went well, these targets are no longer pending *)
823849 pending_targets := Path.Set. diff ! pending_targets targets;
824- clear_targets_digests_after_rule_execution targets_as_list
850+ let targets_digest =
851+ compute_targets_digest_after_rule_execution targets_as_list
852+ in
853+ Path.Table. replace t.trace ~key: head_target
854+ ~data: { rule_digest; targets_digest }
825855 end else
826856 Fiber. return ()
827857 end >> | fun () ->
@@ -1199,26 +1229,6 @@ let stamp_file_for_files_of t ~dir ~ext =
11991229 files_of_dir.stamps < - String.Map. add files_of_dir.stamps ext stamp_file;
12001230 stamp_file
12011231
1202- module Trace = struct
1203- type t = Digest .t Path.Table .t
1204-
1205- let file = Path. relative Path. build_dir " .db"
1206-
1207- module P = Utils. Persistent (struct
1208- type nonrec t = t
1209- let name = " INCREMENTAL-DB"
1210- let version = 1
1211- end )
1212-
1213- let dump t =
1214- if Path. build_dir_exists () then P. dump file t
1215-
1216- let load () =
1217- match P. load file with
1218- | Some t -> t
1219- | None -> Path.Table. create 1024
1220- end
1221-
12221232let all_targets t =
12231233 String.Map. iter t.contexts ~f: (fun ctx ->
12241234 File_tree. fold t.file_tree ~traverse_ignored_dirs: true ~init: ()
0 commit comments