Skip to content

Commit

Permalink
Add (format-dune-file) action (#11166)
Browse files Browse the repository at this point in the history
Signed-off-by: Nicolás Ojeda Bär <[email protected]>
  • Loading branch information
nojb authored Dec 4, 2024
1 parent 41212a5 commit a09c51f
Show file tree
Hide file tree
Showing 12 changed files with 134 additions and 30 deletions.
1 change: 1 addition & 0 deletions doc/changes/11166.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Add `(format-dune-file <src> <dst>)` action. (#11166, @nojb)
14 changes: 14 additions & 0 deletions doc/reference/actions/format-dune-file.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
cat
---

.. highlight:: dune

.. describe:: (format-dune-file <src> <dst>)

Output the formatted contents of the file ``<src>`` to ``<dst>``. The source
file is assumed to contain S-expressions. Note that the precise formatting
can depend on the version of the Dune language used by containing project.

Example::

(format-dune-file file.sexp file.sexp.formatted)
1 change: 1 addition & 0 deletions doc/reference/actions/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ The following constructions are available:
copy#
write-file
pipe-outputs
format-dune-file

.. grid-item::

Expand Down
11 changes: 10 additions & 1 deletion src/dune_lang/action.ml
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ type t =
| Substitute of String_with_vars.t * String_with_vars.t
| Withenv of String_with_vars.t Env_update.t list * t
| When of Slang.blang * t
| Format_dune_file of String_with_vars.t * String_with_vars.t

let is_dev_null t = String_with_vars.is_pform t (Var Dev_null)

Expand Down Expand Up @@ -350,6 +351,11 @@ let cstrs_dune_file t =
, Syntax.since Stanza.syntax (2, 7)
>>> let+ script = sw in
Cram script )
; ( "format-dune-file"
, Syntax.since Stanza.syntax (3, 18)
>>> let+ src = sw
and+ dst = sw in
Format_dune_file (src, dst) )
]
;;

Expand Down Expand Up @@ -458,6 +464,7 @@ let rec encode =
List [ atom "withenv"; List (List.map ~f:Env_update.encode ops); encode t ]
| When (condition, action) ->
List [ atom "when"; Slang.encode_blang condition; encode action ]
| Format_dune_file (src, dst) -> List [ atom "format-dune-file"; sw src; sw dst ]
;;

(* In [Action_exec] we rely on one-to-one mapping between the cwd-relative paths
Expand Down Expand Up @@ -495,7 +502,8 @@ let ensure_at_most_one_dynamic_run ~loc action =
| Diff _
| Substitute _
| Patch _
| Cram _ -> false
| Cram _
| Format_dune_file _ -> false
| Pipe (_, ts) | Progn ts | Concurrent ts ->
List.fold_left ts ~init:false ~f:(fun acc t ->
let have_dyn = loop t in
Expand Down Expand Up @@ -597,6 +605,7 @@ let rec map_string_with_vars t ~f =
When
( blang_map_string_with_vars condition ~f:(slang_map_string_with_vars ~f)
, map_string_with_vars t ~f )
| Format_dune_file (src, dst) -> Format_dune_file (f src, f dst)
;;

let remove_locs = map_string_with_vars ~f:String_with_vars.remove_locs
Expand Down
1 change: 1 addition & 0 deletions src/dune_lang/action.mli
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ type t =
| Substitute of String_with_vars.t * String_with_vars.t
| Withenv of String_with_vars.t Env_update.t list * t
| When of Slang.blang * t
| Format_dune_file of String_with_vars.t * String_with_vars.t

val encode : t Encoder.t
val decode_dune_file : t Decoder.t
Expand Down
11 changes: 11 additions & 0 deletions src/dune_rules/action_unexpanded.ml
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,17 @@ let rec expand (t : Dune_lang.Action.t) : Action.t Action_expander.t =
| Cram script ->
let+ script = E.dep script in
Cram_exec.action script
| Format_dune_file (src, dst) ->
A.with_expander (fun expander ->
let open Memo.O in
let+ version =
let dir = Expander.dir expander in
Dune_load.find_project ~dir >>| Dune_project.dune_version
in
let open Action_expander.O in
let+ src = E.dep src
and+ dst = E.target dst in
Format_dune_file.action ~version src dst)
| Withenv _ | Substitute _ | Patch _ | When _ ->
(* these can only be provided by the package language which isn't expanded here *)
assert false
Expand Down
28 changes: 28 additions & 0 deletions src/dune_rules/format_dune_file.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
open Import

let action =
let module Spec = struct
type ('path, 'target) t = Dune_lang.Syntax.Version.t * 'path * 'target

let name = "format-dune-file"
let version = 1
let bimap (ver, src, dst) f g = ver, f src, g dst
let is_useful_to ~memoize = memoize

let encode (version, src, dst) path target : Sexp.t =
List
[ Dune_lang.Syntax.Version.encode version |> Dune_sexp.to_sexp
; path src
; target dst
]
;;

let action (version, src, dst) ~ectx:_ ~eenv:_ =
Dune_lang.Format.format_action ~version ~src ~dst;
Fiber.return ()
;;
end
in
let module A = Action_ext.Make (Spec) in
fun ~version (src : Path.t) (dst : Path.Build.t) -> A.action (version, src, dst)
;;
3 changes: 3 additions & 0 deletions src/dune_rules/format_dune_file.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
open Import

val action : version:Dune_lang.Syntax.Version.t -> Path.t -> Path.Build.t -> Action.t
29 changes: 1 addition & 28 deletions src/dune_rules/format_rules.ml
Original file line number Diff line number Diff line change
Expand Up @@ -27,33 +27,6 @@ let depend_on_files ~named dir =

let formatted_dir_basename = ".formatted"

let action =
let module Spec = struct
type ('path, 'target) t = Dune_lang.Syntax.Version.t * 'path * 'target

let name = "format-dune-file"
let version = 1
let bimap (ver, src, dst) f g = ver, f src, g dst
let is_useful_to ~memoize = memoize

let encode (version, src, dst) path target : Sexp.t =
List
[ Dune_lang.Syntax.Version.encode version |> Dune_sexp.to_sexp
; path src
; target dst
]
;;

let action (version, src, dst) ~ectx:_ ~eenv:_ =
Dune_lang.Format.format_action ~version ~src ~dst;
Fiber.return ()
;;
end
in
let module A = Action_ext.Make (Spec) in
fun ~version (src : Path.t) (dst : Path.Build.t) -> A.action (version, src, dst)
;;

module Alias = struct
let fmt ~dir = Alias.make Alias0.fmt ~dir
end
Expand Down Expand Up @@ -217,7 +190,7 @@ let gen_rules_output
let { Action_builder.With_targets.build; targets } =
(let open Action_builder.O in
let+ () = Action_builder.path input in
Action.Full.make (action ~version input output))
Action.Full.make (Format_dune_file.action ~version input output))
|> Action_builder.with_file_targets ~file_targets:[ output ]
in
let rule = Rule.make ~mode:Standard ~targets build in
Expand Down
1 change: 1 addition & 0 deletions src/dune_rules/stanzas/rule_conf.ml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ let atom_table =
; "aliases", Field
; "alias", Field
; "enabled_if", Field
; "format-dune-file", Since ((3, 18), Action)
; "package", Since ((3, 8), Field)
]
;;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ between them:

It is possible to pass a file name:

$ dune format-dune-file dune
$ dune format-dune-file dune.dune
(a b)

Parse errors are displayed:
Expand Down Expand Up @@ -188,3 +188,65 @@ Non 0 error code:
File "", line 2, characters 0-0:
Error: unclosed parenthesis at end of input
1

Using the built-in action.

$ cat >dune-project <<EOF
> (lang dune 3.18)
> EOF

$ cat >dune <<EOF
> (rule (with-stdout-to file (echo "( a c)")))
> (rule (format-dune-file file file.formatted))
> EOF

$ dune build file.formatted

$ cat _build/default/file.formatted
(a c)

Version check.

$ cat >dune-project <<EOF
> (lang dune 3.17)
> EOF

$ dune build file.out
File "dune", line 2, characters 0-45:
2 | (rule (format-dune-file file file.formatted))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: 'format-dune-file' in short-form 'rule' is only available since
version 3.18 of the dune language. Please update your dune-project file to
have (lang dune 3.18).
[1]

$ cat >dune <<EOF
> (rule (with-stdout-to file (echo "( a c)")))
> (rule (action (format-dune-file file file.formatted)))
> EOF

$ dune build file.out
File "dune", line 2, characters 14-52:
2 | (rule (action (format-dune-file file file.formatted)))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: 'format-dune-file' is only available since version 3.18 of the dune
language. Please update your dune-project file to have (lang dune 3.18).
[1]

Behaviour when the dune file is not syntactically valid.

$ cat >dune-project <<EOF
> (lang dune 3.18)
> EOF

$ cat >dune <<EOF
> (rule (with-stdout-to file (echo "xxx yyy (")))
> (rule (format-dune-file file file.formatted))
> EOF

$ dune build file.formatted
File "_build/default/file", line 1, characters 9-9:
1 | xxx yyy (

Error: unclosed parenthesis at end of input
[1]

0 comments on commit a09c51f

Please sign in to comment.