From 7b01914ecd7647da1dce8d2e78f81c6010930b46 Mon Sep 17 00:00:00 2001 From: Etienne Millon Date: Tue, 11 Sep 2018 15:59:58 +0200 Subject: [PATCH] WIP: Ocamlformat integration --- src/dune_file.ml | 3 + src/dune_file.mli | 1 + src/lib_rules.ml | 1 + src/ocamlformat.ml | 72 +++++++++++++++++++ src/ocamlformat.mli | 9 +++ src/string_with_vars.ml | 10 ++- src/string_with_vars.mli | 1 + test/blackbox-tests/dune.inc | 10 +++ .../test-cases/ocamlformat/dune | 3 + .../test-cases/ocamlformat/dune-project | 1 + .../test-cases/ocamlformat/ignored.ml | 3 + .../test-cases/ocamlformat/ignored.mli | 0 .../test-cases/ocamlformat/lib/dune | 4 ++ .../test-cases/ocamlformat/lib/lib.ml.orig | 1 + .../test-cases/ocamlformat/lib/lib.mli | 1 + .../test-cases/ocamlformat/lib2/dune | 4 ++ .../test-cases/ocamlformat/lib2/lib2.ml | 1 + .../test-cases/ocamlformat/lib2/lib2.mli | 2 + .../test-cases/ocamlformat/run.t | 22 ++++++ 19 files changed, 147 insertions(+), 2 deletions(-) create mode 100644 src/ocamlformat.ml create mode 100644 src/ocamlformat.mli create mode 100644 test/blackbox-tests/test-cases/ocamlformat/dune create mode 100644 test/blackbox-tests/test-cases/ocamlformat/dune-project create mode 100644 test/blackbox-tests/test-cases/ocamlformat/ignored.ml create mode 100644 test/blackbox-tests/test-cases/ocamlformat/ignored.mli create mode 100644 test/blackbox-tests/test-cases/ocamlformat/lib/dune create mode 100644 test/blackbox-tests/test-cases/ocamlformat/lib/lib.ml.orig create mode 100644 test/blackbox-tests/test-cases/ocamlformat/lib/lib.mli create mode 100644 test/blackbox-tests/test-cases/ocamlformat/lib2/dune create mode 100644 test/blackbox-tests/test-cases/ocamlformat/lib2/lib2.ml create mode 100644 test/blackbox-tests/test-cases/ocamlformat/lib2/lib2.mli create mode 100644 test/blackbox-tests/test-cases/ocamlformat/run.t diff --git a/src/dune_file.ml b/src/dune_file.ml index 6cc2a22d9ffb..4ac9c3ea357d 100644 --- a/src/dune_file.ml +++ b/src/dune_file.ml @@ -680,6 +680,7 @@ module Buildable = struct ; ocamlopt_flags : Ordered_set_lang.Unexpanded.t ; js_of_ocaml : Js_of_ocaml.t ; allow_overlapping_dependencies : bool + ; ocamlformat : bool } let dparse = @@ -700,6 +701,7 @@ module Buildable = struct field "js_of_ocaml" Js_of_ocaml.dparse ~default:Js_of_ocaml.default and allow_overlapping_dependencies = field_b "allow_overlapping_dependencies" + and ocamlformat = field_b "ocamlformat" in { loc ; preprocess @@ -713,6 +715,7 @@ module Buildable = struct ; ocamlopt_flags ; js_of_ocaml ; allow_overlapping_dependencies + ; ocamlformat } let single_preprocess t = diff --git a/src/dune_file.mli b/src/dune_file.mli index 6277568e1dfc..35b6be618542 100644 --- a/src/dune_file.mli +++ b/src/dune_file.mli @@ -141,6 +141,7 @@ module Buildable : sig ; ocamlopt_flags : Ordered_set_lang.Unexpanded.t ; js_of_ocaml : Js_of_ocaml.t ; allow_overlapping_dependencies : bool + ; ocamlformat : bool } (** Preprocessing specification used by all modules or [No_preprocessing] *) diff --git a/src/lib_rules.ml b/src/lib_rules.ml index f92706328eb9..ff55e41d0dfa 100644 --- a/src/lib_rules.ml +++ b/src/lib_rules.ml @@ -375,6 +375,7 @@ module Gen (P : Install_rules.Params) = struct ~lib_name:(Some lib.name) ~dir_kind in + Ocamlformat.gen_rules sctx lib.buildable ~dir ~scope modules; let modules = Preprocessing.pp_modules pp modules in let modules = diff --git a/src/ocamlformat.ml b/src/ocamlformat.ml new file mode 100644 index 000000000000..8fdd620712fb --- /dev/null +++ b/src/ocamlformat.ml @@ -0,0 +1,72 @@ +open Import + +let flag_of_kind : Ml_kind.t -> _ = + function + | Impl -> "--impl" + | Intf -> "--intf" + +let add_alias_format sctx loc ~dir ~scope action = + let alias_conf = + { Dune_file.Alias_conf.name = "format" + ; deps = [] + ; action = Some (loc, action) + ; locks = [] + ; package = None + ; enabled_if = None + ; loc + } + in + Simple_rules.alias sctx ~dir ~scope alias_conf + +let sv = String_with_vars.virt_text __POS__ + +let target_var = String_with_vars.virt_var __POS__ "targets" + +let gen_rules_for_module sctx loc ocamlformat ~dir ~scope m = + Format.printf "dir = %a\n" Path.pp dir; + Module.iter m ~f:(fun kind src -> + let flag = flag_of_kind kind in + let path = Path.basename src.path in + let dep x = String_with_vars.make_macro loc "dep" x in + let formatted = path ^ ".formatted" in + let format_action = + let args = [sv flag; dep path] in + Action.Unexpanded.Redirect + ( Stdout + , target_var + , Run (dep @@ Path.to_string ocamlformat, args) + ) + in + let format_rule = + { Dune_file.Rule.targets = Static [formatted] + ; deps = [] + ; action = (loc, format_action) + ; mode = Standard + ; locks = [] + ; loc + } + in + let diff_action = + Action.Unexpanded.Diff + { optional = false + ; mode = Text + ; file1 = dep path + ; file2 = sv formatted + } + in + let _ : Path.t list = Simple_rules.user_rule sctx ~dir ~scope format_rule in + add_alias_format sctx loc ~dir ~scope diff_action + ) + +let gen_rules sctx (buildable:Dune_file.Buildable.t) + ~dir ~scope modules = + if buildable.ocamlformat then + let loc = buildable.loc in + match Bin.which "ocamlformat" with + | None -> + let msg = "Cannot find ocamlformat, skipping.\n" in + add_alias_format sctx loc ~dir ~scope (Echo [sv msg]) + | Some ocamlformat -> + Module.Name.Map.iter + modules + ~f:(gen_rules_for_module sctx loc ocamlformat ~dir ~scope) diff --git a/src/ocamlformat.mli b/src/ocamlformat.mli new file mode 100644 index 000000000000..ece42cc0cd88 --- /dev/null +++ b/src/ocamlformat.mli @@ -0,0 +1,9 @@ +open Import + +val gen_rules : + Super_context.t + -> Dune_file.Buildable.t + -> dir:Path.t + -> scope:Scope.t + -> Module.t Module.Name.Map.t + -> unit diff --git a/src/string_with_vars.ml b/src/string_with_vars.ml index 834a70fb00d0..957c566b0326 100644 --- a/src/string_with_vars.ml +++ b/src/string_with_vars.ml @@ -20,16 +20,22 @@ let make ?(quoted=false) loc part = let make_text ?quoted loc s = make ?quoted loc (Text s) -let make_var ?quoted loc name = +let make_var_args ?quoted loc name payload = let var = { loc ; name - ; payload = None + ; payload ; syntax = Percent } in make ?quoted loc (Var var) +let make_var ?quoted loc name = + make_var_args ?quoted loc name None + +let make_macro ?quoted loc macro param = + make_var_args ?quoted loc macro (Some param) + let literal ~quoted ~loc s = { parts = [Text s] ; quoted diff --git a/src/string_with_vars.mli b/src/string_with_vars.mli index 3e6dba580797..8fea8c9f675d 100644 --- a/src/string_with_vars.mli +++ b/src/string_with_vars.mli @@ -27,6 +27,7 @@ val virt_var : ?quoted: bool -> (string * int * int * int) -> string -> t val virt_text : (string * int * int * int) -> string -> t val make_var : ?quoted: bool -> Loc.t -> string -> t val make_text : ?quoted: bool -> Loc.t -> string -> t +val make_macro : ?quoted: bool -> Loc.t -> string -> string -> t val is_var : t -> name:string -> bool diff --git a/test/blackbox-tests/dune.inc b/test/blackbox-tests/dune.inc index 64c21d4df945..b848504f41c0 100644 --- a/test/blackbox-tests/dune.inc +++ b/test/blackbox-tests/dune.inc @@ -610,6 +610,14 @@ test-cases/ocamldep-multi-stanzas (progn (run %{exe:cram.exe} -test run.t) (diff? run.t run.t.corrected))))) +(alias + (name ocamlformat) + (deps (package dune) (source_tree test-cases/ocamlformat)) + (action + (chdir + test-cases/ocamlformat + (progn (run %{exe:cram.exe} -test run.t) (diff? run.t run.t.corrected))))) + (alias (name ocamllex-jbuild) (deps (package dune) (source_tree test-cases/ocamllex-jbuild)) @@ -960,6 +968,7 @@ (alias ocaml-config-macro) (alias ocaml-syntax) (alias ocamldep-multi-stanzas) + (alias ocamlformat) (alias ocamllex-jbuild) (alias odoc) (alias odoc-unique-mlds) @@ -1064,6 +1073,7 @@ (alias ocaml-config-macro) (alias ocaml-syntax) (alias ocamldep-multi-stanzas) + (alias ocamlformat) (alias ocamllex-jbuild) (alias output-obj) (alias package-dep) diff --git a/test/blackbox-tests/test-cases/ocamlformat/dune b/test/blackbox-tests/test-cases/ocamlformat/dune new file mode 100644 index 000000000000..18480e0518f3 --- /dev/null +++ b/test/blackbox-tests/test-cases/ocamlformat/dune @@ -0,0 +1,3 @@ +(executable + (name ignored) +) diff --git a/test/blackbox-tests/test-cases/ocamlformat/dune-project b/test/blackbox-tests/test-cases/ocamlformat/dune-project new file mode 100644 index 000000000000..f75713fb8c40 --- /dev/null +++ b/test/blackbox-tests/test-cases/ocamlformat/dune-project @@ -0,0 +1 @@ +(lang dune 1.2) diff --git a/test/blackbox-tests/test-cases/ocamlformat/ignored.ml b/test/blackbox-tests/test-cases/ocamlformat/ignored.ml new file mode 100644 index 000000000000..73d6fa2509d9 --- /dev/null +++ b/test/blackbox-tests/test-cases/ocamlformat/ignored.ml @@ -0,0 +1,3 @@ +let () = + print_endline + "hello" diff --git a/test/blackbox-tests/test-cases/ocamlformat/ignored.mli b/test/blackbox-tests/test-cases/ocamlformat/ignored.mli new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/test/blackbox-tests/test-cases/ocamlformat/lib/dune b/test/blackbox-tests/test-cases/ocamlformat/lib/dune new file mode 100644 index 000000000000..c0d8a0beb1d6 --- /dev/null +++ b/test/blackbox-tests/test-cases/ocamlformat/lib/dune @@ -0,0 +1,4 @@ +(library + (name lib) + (ocamlformat) +) diff --git a/test/blackbox-tests/test-cases/ocamlformat/lib/lib.ml.orig b/test/blackbox-tests/test-cases/ocamlformat/lib/lib.ml.orig new file mode 100644 index 000000000000..3fa824e29b3a --- /dev/null +++ b/test/blackbox-tests/test-cases/ocamlformat/lib/lib.ml.orig @@ -0,0 +1 @@ +let x = 1 diff --git a/test/blackbox-tests/test-cases/ocamlformat/lib/lib.mli b/test/blackbox-tests/test-cases/ocamlformat/lib/lib.mli new file mode 100644 index 000000000000..2055720bc2de --- /dev/null +++ b/test/blackbox-tests/test-cases/ocamlformat/lib/lib.mli @@ -0,0 +1 @@ +val x : int diff --git a/test/blackbox-tests/test-cases/ocamlformat/lib2/dune b/test/blackbox-tests/test-cases/ocamlformat/lib2/dune new file mode 100644 index 000000000000..301c9bd48c7e --- /dev/null +++ b/test/blackbox-tests/test-cases/ocamlformat/lib2/dune @@ -0,0 +1,4 @@ +(library + (name lib2) + (ocamlformat) +) diff --git a/test/blackbox-tests/test-cases/ocamlformat/lib2/lib2.ml b/test/blackbox-tests/test-cases/ocamlformat/lib2/lib2.ml new file mode 100644 index 000000000000..f4959323bb58 --- /dev/null +++ b/test/blackbox-tests/test-cases/ocamlformat/lib2/lib2.ml @@ -0,0 +1 @@ +let y=() diff --git a/test/blackbox-tests/test-cases/ocamlformat/lib2/lib2.mli b/test/blackbox-tests/test-cases/ocamlformat/lib2/lib2.mli new file mode 100644 index 000000000000..66947e21a8d9 --- /dev/null +++ b/test/blackbox-tests/test-cases/ocamlformat/lib2/lib2.mli @@ -0,0 +1,2 @@ +val y : + unit diff --git a/test/blackbox-tests/test-cases/ocamlformat/run.t b/test/blackbox-tests/test-cases/ocamlformat/run.t new file mode 100644 index 000000000000..7ca710276c60 --- /dev/null +++ b/test/blackbox-tests/test-cases/ocamlformat/run.t @@ -0,0 +1,22 @@ +Formatting can be checked using the @format target: + + $ cp lib/lib.ml.orig lib/lib.ml + $ dune build @format --diff-command false + sh (internal) (exit 1) + (cd _build/default && /bin/sh -c 'false lib/lib.ml lib/lib.ml.formatted') + sh (internal) (exit 1) + (cd _build/default && /bin/sh -c 'false lib/lib.mli lib/lib.mli.formatted') + sh (internal) (exit 1) + (cd _build/default && /bin/sh -c 'false lib2/lib2.mli lib2/lib2.mli.formatted') + sh (internal) (exit 1) + (cd _build/default && /bin/sh -c 'false lib2/lib2.ml lib2/lib2.ml.formatted') + dir = _build/default/lib2 + dir = _build/default/lib + [1] + +And fixable files can be promoted: + + $ dune promote lib/lib.ml + Promoting _build/default/lib/lib.ml.formatted to lib/lib.ml. + $ cat lib/lib.ml + let x = 1