Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add (format-dune-file) action #11166

Merged
merged 8 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a test demonstraating how the action fails when given an invalid dune file?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, thanks.


$ 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]
Loading