Skip to content

Commit 8cc612a

Browse files
committed
Re-execute actions when a target is modified
Fixes #1342 Signed-off-by: Jeremie Dimino <[email protected]>
1 parent d713cb0 commit 8cc612a

File tree

6 files changed

+81
-65
lines changed

6 files changed

+81
-65
lines changed

CHANGES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ next
77
- Let `Configurator.V1.C_define.import` handle negative integers
88
(#1334, @Chris00)
99

10+
- Re-execute actions when a target is modified by the user inside
11+
`_build` (#1343, fix #1342, @diml)
12+
1013
- Pass `--set-switch` to opam (#1341, fix #1337, @diml)
1114

1215
1.3.0 (23/09/2018)

src/build_system.ml

Lines changed: 61 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,34 @@ module Files_of = struct
384384
}
385385
end
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+
387415
type 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

633661
let 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-
12221232
let all_targets t =
12231233
String.Map.iter t.contexts ~f:(fun ctx ->
12241234
File_tree.fold t.file_tree ~traverse_ignored_dirs:true ~init:()

src/utils.ml

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,15 @@ module Cached_digest = struct
207207
; table = Hashtbl.create 1024
208208
}
209209

210+
let refresh fn =
211+
let digest = Digest.file (Path.to_string fn) in
212+
Hashtbl.replace cache.table ~key:fn
213+
~data:{ digest
214+
; timestamp = (Unix.stat (Path.to_string fn)).st_mtime
215+
; timestamp_checked = cache.checked_key
216+
};
217+
digest
218+
210219
let file fn =
211220
match Hashtbl.find cache.table fn with
212221
| Some x ->
@@ -223,13 +232,7 @@ module Cached_digest = struct
223232
x.digest
224233
end
225234
| None ->
226-
let digest = Digest.file (Path.to_string fn) in
227-
Hashtbl.add cache.table fn
228-
{ digest
229-
; timestamp = (Unix.stat (Path.to_string fn)).st_mtime
230-
; timestamp_checked = cache.checked_key
231-
};
232-
digest
235+
refresh fn
233236

234237
let remove fn = Hashtbl.remove cache.table fn
235238

src/utils.mli

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ module Cached_digest : sig
8282
(** Clear the following digest from the cache *)
8383
val remove : Path.t -> unit
8484

85+
(** Same as {!file} but forces the digest to be recomputed *)
86+
val refresh : Path.t -> Digest.t
87+
8588
(** Dump/load the cache to/from the disk *)
8689
val dump : unit -> unit
8790
val load : unit -> unit
Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
In this test the "x" alias depends on the file "data" but the action
2-
associated to "x" appends a line to "data". The current behavior is
3-
that the file in _build just keeps growing. The expected behavior is
4-
an error from Dune telling the user that this is not allowed.
2+
associated to "x" appends a line to "data". The expected behavior is
3+
an error from Dune telling the user that this is not allowed, however
4+
Dune currently silently ignores this.
55

66
$ dune build @x
77
$ cat _build/default/data
@@ -12,11 +12,8 @@ an error from Dune telling the user that this is not allowed.
1212
$ cat _build/default/data
1313
hello
1414
hello
15-
hello
1615

1716
$ dune build @x
1817
$ cat _build/default/data
1918
hello
2019
hello
21-
hello
22-
hello

test/blackbox-tests/test-cases/github1342/run.t

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ _build, things are rebuild as expected.
88
$ echo 0 > _build/default/x
99
$ dune build x
1010
$ cat _build/default/x
11-
0
11+
42

0 commit comments

Comments
 (0)