Skip to content

Commit 965a344

Browse files
committed
feat(formatting): format generated diff corrections
Starting with Dune 3.25, format generated corrections consumed by optional text diffs before comparing or promoting them. Select Dune's built-in formatter or OCamlFormat according to the source's formatting configuration, and use the locked OCamlFormat dev tool when available. Construct formatted diffs directly during action expansion so formatter dependencies, environments, and sandbox requirements remain attached to the action. Apply the behavior to user diff? actions and Cinaps without exposing generated corrections as separate build targets. Projects using older language versions retain the previous behavior. Signed-off-by: Rudi Grinberg <me@rgrinberg.com>
1 parent fe11ea7 commit 965a344

20 files changed

Lines changed: 635 additions & 131 deletions

File tree

bin/print_rules.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ let rec encode_action : Action.For_shell.t -> Dune_lang.t =
7171
]
7272
| Ignore (outputs, r) ->
7373
List [ atom (sprintf "ignore-%s" (Outputs.to_string outputs)); encode_action r ]
74+
| If_file_exists (path, action) ->
75+
List [ atom "if-file-exists"; Encoder.string path; encode_action action ]
7476
| Progn l -> List (atom "progn" :: List.map l ~f:encode_action)
7577
| Concurrent l -> List (atom "concurrent" :: List.map l ~f:encode_action)
7678
| Echo xs -> List (atom "echo" :: List.map xs ~f:string)

doc/changes/added/16209.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
- Format generated corrections before optional text diffs compare or promote
2+
them, according to the source's formatting configuration, starting in Dune
3+
3.25. (#16209, @rgrinberg)

src/dune_engine/action.ml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ struct
4747
let ignore_stdout t = Ignore (Stdout, t)
4848
let ignore_stderr t = Ignore (Stderr, t)
4949
let ignore_outputs t = Ignore (Outputs, t)
50+
let if_file_exists path t = If_file_exists (path, t)
5051
let progn ts = Progn ts
5152
let concurrent ts = Concurrent ts
5253
let echo s = Echo s
@@ -308,6 +309,10 @@ let digest =
308309
| System command ->
309310
int d 22;
310311
string d command
312+
| If_file_exists (path, t) ->
313+
int d 23;
314+
digest_path d ~dir path;
315+
loop d t ~dir
311316
in
312317
fun d t -> loop d t ~dir:Path.root
313318
;;
@@ -319,6 +324,7 @@ let fold_one_step t ~init:acc ~f =
319324
| Redirect_out (_, _, _, t)
320325
| Redirect_in (_, _, t)
321326
| Ignore (_, t)
327+
| If_file_exists (_, t)
322328
| With_accepted_exit_codes (_, t) -> f acc t
323329
| Progn l | Pipe (_, l) | Concurrent l -> List.fold_left l ~init:acc ~f
324330
| Run _
@@ -366,6 +372,7 @@ let exists t ~leaf ~extension =
366372
| Redirect_out (_, _, _, t)
367373
| Redirect_in (_, _, t)
368374
| Ignore (_, t)
375+
| If_file_exists (_, t)
369376
| With_accepted_exit_codes (_, t) -> loop t
370377
| Progn l | Pipe (_, l) | Concurrent l -> List.exists l ~f:loop
371378
| Extension extension_ -> extension extension_
@@ -422,7 +429,7 @@ let is_useful_to memoize =
422429
| Setenv (_, _, t) -> loop t
423430
| Redirect_out (_, _, _, t) -> memoize || loop t
424431
| Redirect_in (_, _, t) -> loop t
425-
| Ignore (_, t) | With_accepted_exit_codes (_, t) -> loop t
432+
| Ignore (_, t) | If_file_exists (_, t) | With_accepted_exit_codes (_, t) -> loop t
426433
| Progn l | Pipe (_, l) | Concurrent l -> List.exists l ~f:loop
427434
| Echo _ -> false
428435
| Cat _ -> memoize

src/dune_engine/action_exec.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ let rec exec t ~ectx ~eenv : Done_or_more_deps.t Fiber.t =
128128
redirect_out t ~ectx ~eenv outputs ~perm fn
129129
| Redirect_in (inputs, fn, t) -> redirect_in t ~ectx ~eenv inputs fn
130130
| Ignore (outputs, t) -> redirect_out t ~ectx ~eenv ~perm:Normal outputs Dev_null.path
131+
| If_file_exists (path, t) ->
132+
if Fpath.exists (Path.to_string path) then exec t ~ectx ~eenv else Fiber.return Done
131133
| Progn ts -> exec_list ts ~ectx ~eenv
132134
| Concurrent ts ->
133135
Fiber.parallel_map ts ~f:(exec ~ectx ~eenv)

src/dune_engine/action_intf.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ module type Ast = sig
4747
| Redirect_out of Outputs.t * target * File_perm.t * t
4848
| Redirect_in of Inputs.t * path * t
4949
| Ignore of Outputs.t * t
50+
| If_file_exists of path * t
5051
| Progn of t list
5152
| Concurrent of t list
5253
| Echo of string list
@@ -82,6 +83,7 @@ module type Helpers = sig
8283
val ignore_stdout : t -> t
8384
val ignore_stderr : t -> t
8485
val ignore_outputs : t -> t
86+
val if_file_exists : path -> t -> t
8587
val progn : t list -> t
8688
val concurrent : t list -> t
8789
val echo : string list -> t

src/dune_engine/action_mapper.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ module Make (Src : Action_intf.Ast) (Dst : Action_intf.Ast) = struct
2929
Redirect_out (outputs, f_target ~dir fn, perm, f t ~dir)
3030
| Redirect_in (inputs, fn, t) -> Redirect_in (inputs, f_path ~dir fn, f t ~dir)
3131
| Ignore (outputs, t) -> Ignore (outputs, f t ~dir)
32+
| If_file_exists (path, t) -> If_file_exists (f_path ~dir path, f t ~dir)
3233
| Progn l -> Progn (List.map l ~f:(fun t -> f t ~dir))
3334
| Concurrent l -> Concurrent (List.map l ~f:(fun t -> f t ~dir))
3435
| Echo xs -> Echo (List.map xs ~f:(f_string ~dir))

src/dune_lang/format.ml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,5 +78,17 @@ let format_to_channel ~version ~src oc =
7878
;;
7979

8080
let format_action ~version ~src ~dst =
81-
Path.build dst |> Io.with_file_out ~f:(format_to_channel ~version ~src)
81+
let dst = Path.build dst in
82+
if Path.equal src dst
83+
then
84+
Temp.with_temp_file
85+
~dir:(Path.parent_exn dst)
86+
~prefix:"dune-format"
87+
~suffix:"output"
88+
~f:(function
89+
| Error exn -> raise exn
90+
| Ok temporary ->
91+
Io.with_file_out temporary ~f:(format_to_channel ~version ~src);
92+
Fpath.rename_exn (Path.to_string temporary) (Path.to_string dst))
93+
else Io.with_file_out dst ~f:(format_to_channel ~version ~src)
8294
;;

0 commit comments

Comments
 (0)