Skip to content

Commit c3575ce

Browse files
committed
fix: patch back fail on promotions
Signed-off-by: Rudi Grinberg <me@rgrinberg.com>
1 parent 0aa2277 commit c3575ce

2 files changed

Lines changed: 161 additions & 64 deletions

File tree

src/dune_engine/sandbox.ml

Lines changed: 94 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -225,41 +225,67 @@ let register_snapshot_promotion t (targets : Targets.Validated.t) ~old_snapshot
225225
let in_source_tree p =
226226
Path.extract_build_context_dir_maybe_sandboxed p |> Option.value_exn |> snd
227227
in
228-
let copy_file p =
229-
let source_file = in_source_tree p in
230-
let correction_file = Path.as_in_build_dir_exn p in
231-
Diff_promotion.register_intermediate `Move ~source_file ~correction_file
232-
in
233-
let delete what file = in_source_tree file |> Diff_promotion.register_delete what in
228+
let diffs = ref [] in
229+
let add_copy_file p = diffs := p :: !diffs in
230+
let deletes = ref [] in
231+
let add_delete what file = deletes := (what, in_source_tree file) :: !deletes in
234232
let target_root_in_sandbox = map_path t targets.root in
235-
Path.Map.iter2 old_snapshot new_snapshot ~f:(fun p before after ->
236-
if
237-
not
238-
(let dir = Path.as_in_build_dir_exn (Path.parent_exn p) in
239-
Path.Build.equal dir target_root_in_sandbox
240-
&&
241-
let basename = Path.basename p in
242-
Filename.Set.mem targets.files basename || Filename.Set.mem targets.dirs basename)
243-
then (
244-
match before, after with
245-
| None, None -> assert false
246-
| None, Some (`File _) -> copy_file p
247-
| Some (`File _), None -> delete `File p
248-
| Some `Dir, None -> delete `Directory p
249-
| Some `Dir, Some `Dir -> ()
250-
| None, Some `Dir ->
251-
(* We don't create empty dirs and rely on the traversal of this dir to
233+
let () =
234+
Path.Map.iter2 old_snapshot new_snapshot ~f:(fun p before after ->
235+
if
236+
not
237+
(let dir = Path.as_in_build_dir_exn (Path.parent_exn p) in
238+
Path.Build.equal dir target_root_in_sandbox
239+
&&
240+
let basename = Path.basename p in
241+
Filename.Set.mem targets.files basename
242+
|| Filename.Set.mem targets.dirs basename)
243+
then (
244+
match before, after with
245+
| None, None -> assert false
246+
| None, Some (`File _) -> add_copy_file p
247+
| Some (`File _), None -> add_delete `File p
248+
| Some `Dir, None -> add_delete `Directory p
249+
| Some `Dir, Some `Dir -> ()
250+
| None, Some `Dir ->
251+
(* We don't create empty dirs and rely on the traversal of this dir to
252252
create the underlying files. Mayb e we should try harder *)
253-
()
254-
| Some (`File _), Some `Dir ->
255-
(* We are going to traverse the target directory here, but we should
253+
()
254+
| Some (`File _), Some `Dir ->
255+
(* We are going to traverse the target directory here, but we should
256256
really treat this as a deletion *)
257-
()
258-
| Some `Dir, Some (`File _) -> copy_file p
259-
| Some (`File before), Some (`File after) ->
260-
(match Cached_digest.Reduced_stats.compare before after with
261-
| Eq -> ()
262-
| Lt | Gt -> copy_file p)))
257+
()
258+
| Some `Dir, Some (`File _) -> add_copy_file p
259+
| Some (`File before), Some (`File after) ->
260+
(match Cached_digest.Reduced_stats.compare before after with
261+
| Eq -> ()
262+
| Lt | Gt -> add_copy_file p)))
263+
in
264+
Fiber.fork_and_join_unit
265+
(fun () ->
266+
Fiber.parallel_iter !diffs ~f:(fun path ->
267+
let source = Path.drop_optional_sandbox_root path in
268+
Diff_action.exec
269+
t.loc
270+
{ Dune_util.Action.Diff.file1 = source
271+
; file2 = Path.as_in_build_dir_exn path
272+
; optional = true
273+
; mode = Text
274+
}))
275+
(fun () ->
276+
Fiber.parallel_iter !deletes ~f:(fun (what, path) ->
277+
Diff_promotion.register_delete what path;
278+
let what =
279+
match what with
280+
| `File -> "File"
281+
| `Directory -> "Directory"
282+
in
283+
User_error.raise
284+
[ Pp.textf
285+
"%s %s should be deleted"
286+
what
287+
(Path.Source.to_string_maybe_quoted path)
288+
]))
263289
;;
264290

265291
let hint_delete_dir =
@@ -273,42 +299,46 @@ let move_targets_to_build_dir t ~should_be_skipped ~(targets : Targets.Validated
273299
: unit Fiber.t
274300
=
275301
let open Fiber.O in
276-
let+ start, stop, queued =
277-
maybe_async (fun () ->
278-
Option.iter t.snapshot ~f:(fun old_snapshot ->
279-
register_snapshot_promotion t targets ~old_snapshot);
280-
Targets.Validated.iter
281-
targets
282-
~file:(fun target ->
283-
if not (should_be_skipped target)
284-
then rename_optional_file ~src:(map_path t target) ~dst:target)
285-
~dir:(fun target ->
286-
let src_dir = map_path t target in
287-
(match Path.Untracked.stat (Path.build target) with
288-
| Error (Unix.ENOENT, _, _) -> ()
289-
| Error e ->
290-
User_error.raise
291-
~hints:hint_delete_dir
292-
[ Pp.textf "unable to stat %s" (Path.Build.to_string_maybe_quoted target)
293-
; Pp.text "reason:"
294-
; Pp.text (Unix_error.Detailed.to_string_hum e)
295-
]
296-
| Ok { Unix.st_kind; _ } ->
297-
(* We clean up all targets (including directory targets) before
302+
let start = Time.now () in
303+
let+ () =
304+
match t.snapshot with
305+
| None -> Fiber.return ()
306+
| Some old_snapshot -> register_snapshot_promotion t targets ~old_snapshot
307+
in
308+
let () =
309+
Targets.Validated.iter
310+
targets
311+
~file:(fun target ->
312+
if not (should_be_skipped target)
313+
then rename_optional_file ~src:(map_path t target) ~dst:target)
314+
~dir:(fun target ->
315+
let src_dir = map_path t target in
316+
(match Path.Untracked.stat (Path.build target) with
317+
| Error (Unix.ENOENT, _, _) -> ()
318+
| Error e ->
319+
User_error.raise
320+
~hints:hint_delete_dir
321+
[ Pp.textf "unable to stat %s" (Path.Build.to_string_maybe_quoted target)
322+
; Pp.text "reason:"
323+
; Pp.text (Unix_error.Detailed.to_string_hum e)
324+
]
325+
| Ok { Unix.st_kind; _ } ->
326+
(* We clean up all targets (including directory targets) before
298327
running an action, so this branch should be unreachable unless
299328
the rule somehow escaped the sandbox *)
300-
User_error.raise
301-
~hints:hint_delete_dir
302-
[ Pp.textf
303-
"Target %s of kind %S already exists in the build directory"
304-
(Path.Build.to_string_maybe_quoted target)
305-
(File_kind.to_string_hum st_kind)
306-
]);
307-
if Fpath.exists (Path.Build.to_string src_dir)
308-
then Unix.rename (Path.Build.to_string src_dir) (Path.Build.to_string target)))
329+
User_error.raise
330+
~hints:hint_delete_dir
331+
[ Pp.textf
332+
"Target %s of kind %S already exists in the build directory"
333+
(Path.Build.to_string_maybe_quoted target)
334+
(File_kind.to_string_hum st_kind)
335+
]);
336+
if Fpath.exists (Path.Build.to_string src_dir)
337+
then Unix.rename (Path.Build.to_string src_dir) (Path.Build.to_string target))
309338
in
339+
let stop = Time.now () in
310340
Dune_trace.emit ~buffered:true Sandbox (fun () ->
311-
Dune_trace.Event.sandbox `Extract ~start ~stop ~queued t.loc ~dir:t.dir)
341+
Dune_trace.Event.sandbox `Extract ~start ~stop ~queued:None t.loc ~dir:t.dir)
312342
;;
313343

314344
let failed_to_delete_sandbox dir reason =

test/blackbox-tests/test-cases/sandbox/patch-back-source-tree.t

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ All modified dependencies are promoted
3434

3535
$ echo blah > x
3636
$ dune build
37+
File "_build/default/x", line 1, characters 0-0:
38+
--- _build/default/x
39+
+++ _build/.sandbox/86f2c40130a800b8151676d70b10262e/default/x
40+
@@ -1 +1 @@
41+
-blah
42+
+Hello, world!
43+
[1]
3744

3845
$ dune trace cat | jq '
3946
> include "dune";
@@ -82,6 +89,12 @@ All other new files are copied
8289
> EOF
8390

8491
$ dune build
92+
File "_build/default/y", line 1, characters 0-0:
93+
--- _build/default/y
94+
+++ _build/.sandbox/ba441369ce7d90755f539d06c1f02c22/default/y
95+
@@ -0,0 +1 @@
96+
+Hello, world!
97+
[1]
8598
$ dune promote
8699
Promoting _build/default/y to y.
87100
$ cat y
@@ -98,6 +111,12 @@ Directories are created if needed
98111
> EOF
99112

100113
$ dune build
114+
File "_build/default/z/z", line 1, characters 0-0:
115+
--- _build/default/z/z
116+
+++ _build/.sandbox/99313b6fe2aa282ac731133ff51ddc94/default/z/z
117+
@@ -0,0 +1 @@
118+
+Hello, world!
119+
[1]
101120
$ dune promote
102121
Promoting _build/default/z/z to z/z.
103122
$ cat z/z
@@ -116,6 +135,13 @@ Actions are allowed to delete files
116135
> EOF
117136

118137
$ dune build
138+
File "dune", lines 1-4, characters 0-96:
139+
1 | (rule
140+
2 | (deps foo (sandbox patch_back_source_tree))
141+
3 | (alias default)
142+
4 | (action (system "rm foo")))
143+
Error: File foo should be deleted
144+
[1]
119145
$ dune promote
120146
$ [[ ! -f foo ]] && echo foo has been deleted
121147
foo has been deleted
@@ -134,6 +160,25 @@ Actions are allowed to delete directories
134160
> EOF
135161

136162
$ dune build
163+
File "dune", lines 1-4, characters 0-116:
164+
1 | (rule
165+
2 | (deps todelete/y/foo (sandbox patch_back_source_tree))
166+
3 | (alias default)
167+
4 | (action (system "rm -rf todelete")))
168+
Error: Directory todelete should be deleted
169+
File "dune", lines 1-4, characters 0-116:
170+
1 | (rule
171+
2 | (deps todelete/y/foo (sandbox patch_back_source_tree))
172+
3 | (alias default)
173+
4 | (action (system "rm -rf todelete")))
174+
Error: Directory todelete/y should be deleted
175+
File "dune", lines 1-4, characters 0-116:
176+
1 | (rule
177+
2 | (deps todelete/y/foo (sandbox patch_back_source_tree))
178+
3 | (alias default)
179+
4 | (action (system "rm -rf todelete")))
180+
Error: File todelete/y/foo should be deleted
181+
[1]
137182

138183
$ dune promote
139184
$ [[ ! -d todelete ]] && echo todelete has been deleted
@@ -198,12 +243,27 @@ the rule:
198243
> }
199244

200245
$ test_with copy
246+
File "_build/default/x", line 1, characters 0-0:
247+
--- _build/default/x
248+
+++ _build/.sandbox/e9782accaad816d62e1b8df11fc7ba71/default/x
249+
@@ -0,0 +1 @@
250+
+Hello, world!
201251
Promoting _build/default/x to x.
202252
Hello, world!
203253
$ test_with hardlink
254+
File "_build/default/x", line 1, characters 0-0:
255+
--- _build/default/x
256+
+++ _build/.sandbox/e9782accaad816d62e1b8df11fc7ba71/default/x
257+
@@ -0,0 +1 @@
258+
+Hello, world!
204259
Promoting _build/default/x to x.
205260
Hello, world!
206261
$ test_with symlink
262+
File "_build/default/x", line 1, characters 0-0:
263+
--- _build/default/x
264+
+++ _build/.sandbox/e9782accaad816d62e1b8df11fc7ba71/default/x
265+
@@ -0,0 +1 @@
266+
+Hello, world!
207267
Promoting _build/default/x to x.
208268
Hello, world!
209269

@@ -227,6 +287,13 @@ If a source file is read-only, the action sees it as writable:
227287

228288
$ dune build
229289
writable
290+
File "_build/default/x", line 1, characters 0-0:
291+
--- _build/default/x
292+
+++ _build/.sandbox/f7a07be918688b38bde181bb8be9d91d/default/x
293+
@@ -1 +1 @@
294+
-xx
295+
+blah
296+
[1]
230297

231298
And as the action modified `x`, its permissions have now changed
232299
inside the source tree:

0 commit comments

Comments
 (0)