From 3f7b77efa01576387b06bce0131547495a383b6f Mon Sep 17 00:00:00 2001 From: Shon Feder Date: Mon, 26 Sep 2022 16:40:35 -0400 Subject: [PATCH 1/2] Link to hacking.rst in CONTRIBUTING.md Signed-off-by: Shon Feder --- CONTRIBUTING.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7a52d78969b..c23e7a32f18 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,10 +4,13 @@ developed at [Jane Street][js] and is now maintained by Jane Street, community. Contributions to Dune are welcome and should be submitted via GitHub -pull requests against the `main` branch. Dune is distributed under -the MIT license and contributors are required to sign their work in -order to certify that they have the right to submit it under this -license. See the following section for more details. +pull requests against the `main` branch. See [./doc/hacking.rst][hack] +for a guide to getting started on the code base. + +Dune is distributed under the MIT license and contributors are +required to sign their work in order to certify that they have the +right to submit it under this license. See the following section for +more details. Signing contributions --------------------- @@ -71,6 +74,7 @@ your commit automatically with `git commit -s`. [dco]: http://developercertificate.org/ [js]: https://www.janestreet.com/ [ocl]: http://ocamllabs.io/ +[hack]: ./doc/hacking.rst Coding style ------------ From 8060a64ae33571852cede275c2ccf0f7700b569a Mon Sep 17 00:00:00 2001 From: Shon Feder Date: Fri, 30 Sep 2022 17:02:47 -0400 Subject: [PATCH 2/2] Migrate init to cmdliner nested subcommands Closes #6161 Signed-off-by: Shon Feder --- CHANGES.md | 3 +- bin/dune_init.ml | 21 -- bin/dune_init.mli | 13 - bin/init.ml | 339 +++++++++--------- bin/init.mli | 2 +- bin/main.ml | 5 +- .../test-cases/dune-init.t/run.t | 23 +- test/blackbox-tests/test-cases/github3046.t | 12 +- 8 files changed, 179 insertions(+), 239 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index f972e9c8b0c..72ffb526be5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -67,7 +67,8 @@ For instance `%{coq:version}` (#6049, @Alizter) - update vendored copy of cmdliner to 1.1.1. This improves the built-in - documentation for command groups such as `dune ocaml`. (#6038, @emillon) + documentation for command groups such as `dune ocaml`. (#6038, @emillon, + #6169, @shonfeder) - The test suite for Coq now requires Coq >= 8.16 due to changes in the plugin loading mechanism upstream (which now uses findlib). diff --git a/bin/dune_init.ml b/bin/dune_init.ml index 05074582a90..37efa0cf14c 100644 --- a/bin/dune_init.ml +++ b/bin/dune_init.ml @@ -10,27 +10,6 @@ module Dialect = Dune_engine.Dialect syntax tree (CST) a good deal. *) module Cst = Dune_lang.Cst -module Kind = struct - type t = - | Executable - | Library - | Project - | Test - - let to_string = function - | Executable -> "executable" - | Library -> "library" - | Project -> "project" - | Test -> "test" - - let commands = - [ ("executable", Executable) - ; ("library", Library) - ; ("project", Project) - ; ("test", Test) - ] -end - (** Abstractions around the kinds of files handled during initialization *) module File = struct type dune = diff --git a/bin/dune_init.mli b/bin/dune_init.mli index 746e6bcfff4..142a15d9ab3 100644 --- a/bin/dune_init.mli +++ b/bin/dune_init.mli @@ -2,19 +2,6 @@ open! Stdune -(** Supported kinds of components for initialization *) -module Kind : sig - type t = - | Executable - | Library - | Project - | Test - - val to_string : t -> string - - val commands : (string * t) list -end - (** The context in which the initialization is executed *) module Init_context : sig type t = diff --git a/bin/init.ml b/bin/init.ml index 3ed0fb3e4ab..cf62a9f6402 100644 --- a/bin/init.ml +++ b/bin/init.ml @@ -4,20 +4,6 @@ open Dune_init (** {1 Helper functions} *) -(** {2 Validation} *) - -(* TODO(shonfeder): Remove when nested subcommands are available *) -let validate_component_options kind unsupported_options = - let report_invalid_option = function - | _, false -> () (* The option wasn't supplied *) - | option_name, true -> - User_error.raise - [ Pp.textf "The `%s' component does not support the `--%s' option" - (Kind.to_string kind) option_name - ] - in - List.iter ~f:report_invalid_option unsupported_options - (** {2 Cmdliner Argument Converters} *) let atom_parser s = @@ -68,93 +54,17 @@ let print_completion kind name = Console.print_user_message (User_message.make [ Pp.tag User_message.Style.Ok (Pp.verbatim "Success") - ++ Pp.textf ": initialized %s component named " (Kind.to_string kind) + ++ Pp.textf ": initialized %s component named " kind ++ Pp.tag User_message.Style.Kwd (Pp.verbatim (Dune_lang.Atom.to_string name)) ]) (** {1 CLI} *) -let doc = "Initialize dune components" - -let synopsis = - Common.command_synopsis - [ "init proj NAME [PATH] [OPTION]... " - ; "init exec NAME [PATH] [OPTION]... " - ; "init lib NAME [PATH] [OPTION]... " - ; "init test NAME [PATH] [OPTION]... " - ] - -let man = - [ `Blocks synopsis - ; `S "DESCRIPTION" - ; `P - {|$(b,dune init COMPONENT NAME [PATH] [OPTION]...) initializes a new dune - configuration for a component of the kind specified by $(b,COMPONENT), - named $(b,NAME), with fields determined by the supplied $(b,OPTION)s.|} - ; `P - {|If the optional $(b,PATH) is provided, the component will be created - there. Otherwise, it is created in the current working directory.|} - ; `P - {|The command can be used to add stanzas to existing dune files and for - creating new dune files and composing basic component templates.|} - ; `P {|Supported $(b,COMPONENT)s:|} - ; `I - ( "$(b,project)" - , {|A project is a predefined composition of components arranged in a - standard directory structure. The kind of project initialized is - determined by the value of the $(b,--kind) flag and defaults to an - executable project, composed of a library, an executable, and a test - component.|} - ) - ; `I ("$(b,executable)", {|A binary executable.|}) - ; `I ("$(b,library)", {|An OCaml library.|}) - ; `I - ( "$(b,test)" - , {|A separate test harness. (For inline tests, use the - $(b,--inline-tests) flag along with the other component kinds.)|} - ) - ; `P - {|Any prefix of the $(b,COMPONENT) kind names can be supplied in place of - full name (as illustrated in the synopsis).|} - ; `P - {|For more details, see https://dune.readthedocs.io/en/stable/usage.html#initializing-components|} - ; Common.examples - [ ( {|Generate a project skeleton for an executable named `myproj' in a - new directory named `myproj', depending on the bos library and - using inline tests along with ppx_inline_test |} - , {|dune init proj myproj --libs bos --ppx ppx_inline_test --inline-tests|} - ) - ; ( {|Configure an executable component named `myexe' in a dune file in the - current directory|} - , {|dune init exe myexe|} ) - ; ( {|Configure a library component named `mylib' in a dune file in the ./src - directory depending on the core and cmdliner libraries, the ppx_let - and ppx_inline_test preprocessors, and declared as using inline - tests|} - , {|dune init lib mylib src --libs core,cmdliner --ppx ppx_let,ppx_inline_test --inline-tests|} - ) - ; ( {|Configure a test component named `mytest' in a dune file in the - ./test directory that depends on `mylib'|} - , {|dune init test mytest test --libs mylib|} ) - ] - ] - -let info = Cmd.info "init" ~doc ~man - -let term = - let+ common_term = Common.term_with_default_root_is_cwd - and+ kind = - (* TODO(shonfeder): Replace with nested subcommand once we have support for - that *) - let docv = "COMPONENT" in - Arg.(required & pos 0 (some (enum Kind.commands)) None & info [] ~docv) - and+ name = +let common : Component.Options.Common.t Term.t = + let+ name = let docv = "NAME" in - Arg.(required & pos 1 (some component_name_conv) None & info [] ~docv) - and+ path = - let docv = "PATH" in - Arg.(value & pos 2 (some string) None & info [] ~docv) + Arg.(required & pos 0 (some component_name_conv) None & info [] ~docv) and+ libraries = let docv = "LIBRARIES" in let doc = @@ -167,86 +77,167 @@ let term = "A comma separated list of ppx preprocessors used by the component" in Arg.(value & opt (list atom_conv) [] & info [ "ppx" ] ~docv ~doc) - and+ public = - (* TODO(shonfeder): Move to subcommands {lib, exe} once implemented *) - let docv = "PUBLIC_NAME" in - let doc = - "If called with an argument, make the component public under the given \ - PUBLIC_NAME. If supplied without an argument, use NAME." - in - Arg.( - value - & opt ~vopt:(Some Component.Options.Use_name) (some public_name_conv) None - & info [ "public" ] ~docv ~doc) - and+ inline_tests = - (* TODO(shonfeder): Move to subcommand [lib] once implemented *) - let docv = "USE_INLINE_TESTS" in - let doc = - "Whether to use inline tests. Only applicable for $(b,library) and \ - $(b,project) components." - in - Arg.(value & flag & info [ "inline-tests" ] ~docv ~doc) - and+ template = - let docv = "PROJECT_KIND" in - let doc = - "The kind of project to initialize. Valid options are $(b,e[xecutable]) \ - or $(b,l[ibrary]). Defaults to $(b,executable). Only applicable for \ - $(b,project) components." - in - Arg.( - value - & opt (some (enum Component.Options.Project.Template.commands)) None - & info [ "kind" ] ~docv ~doc) - and+ pkg = - let docv = "PACKAGE_MANAGER" in - let doc = - "Which package manager to use. Valid options are $(b,o[pam]) or \ - $(b,e[sy]). Defaults to $(b,opam). Only applicable for $(b,project) \ - components." - in - Arg.( - value - & opt (some (enum Component.Options.Project.Pkg.commands)) None - & info [ "pkg" ] ~docv ~doc) + in + { Component.Options.Common.name; libraries; pps } + +let context : Init_context.t Term.t = + let+ common_term = Common.term_with_default_root_is_cwd + and+ path = + let docv = "PATH" in + Arg.(value & pos 1 (some string) None & info [] ~docv) in let config = Common.init common_term in - Dune_engine.Clflags.on_missing_dune_project_file := Dune_engine.Clflags.Ignore; - let open Component in - let context = - Scheduler.go ~common:common_term ~config (fun () -> - Memo.run (Init_context.make path)) + Scheduler.go ~common:common_term ~config (fun () -> + Memo.run (Init_context.make path)) + +let public : Component.Options.public_name option Term.t = + let docv = "PUBLIC_NAME" in + let doc = + "If called with an argument, make the component public under the given \ + PUBLIC_NAME. If supplied without an argument, use NAME." + in + Arg.( + value + & opt ~vopt:(Some Component.Options.Use_name) (some public_name_conv) None + & info [ "public" ] ~docv ~doc) + +let inline_tests : bool Term.t = + let docv = "USE_INLINE_TESTS" in + let doc = + "Whether to use inline tests. Only applicable for $(b,library) and \ + $(b,project) components." + in + Arg.(value & flag & info [ "inline-tests" ] ~docv ~doc) + +let opt_default ~default term = Term.(const (Option.value ~default) $ term) + +let executable = + let doc = "A binary executable." in + let man = [] in + let kind = "executable" in + Cmd.v (Cmd.info kind ~doc ~man) + @@ let+ context = context + and+ common = common + and+ public = public in + Component.init (Executable { context; common; options = { public } }); + print_completion kind common.name + +let library = + let doc = "An OCaml library." in + let man = [] in + let kind = "library" in + Cmd.v (Cmd.info kind ~doc ~man) + @@ let+ context = context + and+ common = common + and+ public = public + and+ inline_tests = inline_tests in + Component.init + (Library { context; common; options = { public; inline_tests } }); + print_completion kind common.name + +let test = + let doc = + "A test harness. (For inline tests, use the $(b,--inline-tests) flag along \ + with the other component kinds.)" + in + let man = [] in + let kind = "test" in + Cmd.v (Cmd.info kind ~doc ~man) + @@ let+ context = context + and+ common = common in + Component.init (Test { context; common; options = () }); + print_completion kind common.name + +let project = + let open Component.Options in + let doc = + "A project is a predefined composition of components arranged in a \ + standard directory structure. The kind of project initialized is \ + determined by the value of the $(b,--kind) flag and defaults to an \ + executable project, composed of a library, an executable, and a test \ + component." + in + let man = [] in + Cmd.v (Cmd.info "project" ~doc ~man) + @@ let+ context = context + and+ common = common + and+ inline_tests = inline_tests + and+ template = + let docv = "PROJECT_KIND" in + let doc = + "The kind of project to initialize. Valid options are \ + $(b,e[xecutable]) or $(b,l[ibrary]). Defaults to $(b,executable). \ + Only applicable for $(b,project) components." + in + opt_default ~default:Project.Template.Exec + Arg.( + value + & opt (some (enum Project.Template.commands)) None + & info [ "kind" ] ~docv ~doc) + and+ pkg = + let docv = "PACKAGE_MANAGER" in + let doc = + "Which package manager to use. Valid options are $(b,o[pam]) or \ + $(b,e[sy]). Defaults to $(b,opam). Only applicable for $(b,project) \ + components." + in + opt_default ~default:Project.Pkg.Opam + Arg.( + value + & opt (some (enum Project.Pkg.commands)) None + & info [ "pkg" ] ~docv ~doc) + in + Component.init + (Project { context; common; options = { template; inline_tests; pkg } }); + print_completion "project" common.name + +let group = + let doc = "Command group for initializing dune components" in + let synopsis = + Common.command_synopsis + [ "init proj NAME [PATH] [OPTION]... " + ; "init exec NAME [PATH] [OPTION]... " + ; "init lib NAME [PATH] [OPTION]... " + ; "init test NAME [PATH] [OPTION]... " + ] + in + let man = + [ `Blocks synopsis + ; `S "DESCRIPTION" + ; `P + {|$(b,dune init COMPONENT NAME [PATH] [OPTION]...) initializes a new dune + configuration for a component of the kind specified by the subcommand + $(b,COMPONENT), named $(b,NAME), with fields determined by the supplied + $(b,OPTION)s.|} + ; `P + {|Run a subcommand with $(b, --help) for for details on it's supported arguments|} + ; `P + {|If the optional $(b,PATH) is provided, the component will be created + there. Otherwise, it is created in the current working directory.|} + ; `P + {|Any prefix of a $(b,COMMAND)'s name can be supplied in place of + full name (as illustrated in the synopsis).|} + ; `P + {|For more details, see https://dune.readthedocs.io/en/stable/usage.html#initializing-components|} + ; Common.examples + [ ( {|Generate a project skeleton for an executable named `myproj' in a + new directory named `myproj', depending on the bos library and + using inline tests along with ppx_inline_test |} + , {|dune init proj myproj --libs bos --ppx ppx_inline_test --inline-tests|} + ) + ; ( {|Configure an executable component named `myexe' in a dune file in the + current directory|} + , {|dune init exe myexe|} ) + ; ( {|Configure a library component named `mylib' in a dune file in the ./src + directory depending on the core and cmdliner libraries, the ppx_let + and ppx_inline_test preprocessors, and declared as using inline + tests|} + , {|dune init lib mylib src --libs core,cmdliner --ppx ppx_let,ppx_inline_test --inline-tests|} + ) + ; ( {|Configure a test component named `mytest' in a dune file in the + ./test directory that depends on `mylib'|} + , {|dune init test mytest test --libs mylib|} ) + ] + ] in - let common : Options.Common.t = { name; libraries; pps } in - let given_public = Option.is_some public in - let given_pkg = Option.is_some pkg in - let given_template = Option.is_some template in - let pkg = Option.value pkg ~default:Options.Project.Pkg.Opam in - let template = Option.value template ~default:Options.Project.Template.Exec in - (* for the [kind] of initialization *) - let check_unsupported_options = validate_component_options kind in - (match kind with - | Kind.Executable -> - check_unsupported_options - [ ("inline-tests", inline_tests) - ; ("kind", given_template) - ; ("pkg", given_pkg) - ]; - init @@ Executable { context; common; options = { public } } - | Kind.Library -> - check_unsupported_options [ ("kind", given_template); ("pkg", given_pkg) ]; - init @@ Library { context; common; options = { public; inline_tests } } - | Kind.Project -> - check_unsupported_options [ ("public", given_public) ]; - init - @@ Project { context; common; options = { inline_tests; pkg; template } } - | Kind.Test -> - check_unsupported_options - [ ("public", given_public) - ; ("inline-tests", inline_tests) - ; ("kind", given_template) - ; ("pkg", given_pkg) - ]; - init @@ Test { context; common; options = () }); - print_completion kind name - -let command = Cmd.v info term + Cmd.group (Cmd.info "init" ~doc ~man) [ executable; project; library; test ] diff --git a/bin/init.mli b/bin/init.mli index 8c78dc310b9..d4c5902fcd6 100644 --- a/bin/init.mli +++ b/bin/init.mli @@ -1,3 +1,3 @@ open Import -val command : unit Cmd.t +val group : unit Cmd.t diff --git a/bin/main.ml b/bin/main.ml index bfe0123a274..cd0648a4dd6 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -16,7 +16,6 @@ let all : _ Cmdliner.Cmd.t list = ; Subst.command ; Print_rules.command ; Utop.command - ; Init.command ; Promote.command ; Printenv.command ; Help.command @@ -30,7 +29,9 @@ let all : _ Cmdliner.Cmd.t list = ; Diagnostics.command ] in - let groups = [ Ocaml_cmd.group; Coq.group; Rpc.group; Internal.group ] in + let groups = + [ Ocaml_cmd.group; Coq.group; Rpc.group; Internal.group; Init.group ] + in terms @ groups (* Short reminders for the most used and useful commands *) diff --git a/test/blackbox-tests/test-cases/dune-init.t/run.t b/test/blackbox-tests/test-cases/dune-init.t/run.t index eb82ed5ea6e..6f100957028 100644 --- a/test/blackbox-tests/test-cases/dune-init.t/run.t +++ b/test/blackbox-tests/test-cases/dune-init.t/run.t @@ -255,31 +255,12 @@ Will not create components with invalid names Library names must be non-empty and composed only of the following characters: 'A'..'Z', 'a'..'z', '_' or '0'..'9'. - Usage: dune init [OPTION]… COMPONENT NAME [PATH] - Try 'dune init --help' or 'dune --help' for more information. + Usage: dune init library [OPTION]… NAME [PATH] + Try 'dune init library --help' or 'dune --help' for more information. [1] $ test -f ./_test_lib [1] -Will fail and inform user when invalid component command is given - - $ dune init foo blah - dune: COMPONENT argument: invalid value 'foo', expected one of 'executable', - 'library', 'project' or 'test' - Usage: dune init [OPTION]… COMPONENT NAME [PATH] - Try 'dune init --help' or 'dune --help' for more information. - [1] - -Will fail and inform user when an invalid option is given to a component - - $ dune init test test_foo --public - Error: The `test' component does not support the `--public' option - [1] - $ dune init exe test_exe --inline-tests - Error: The `executable' component does not support the `--inline-tests' - option - [1] - Adding fields to existing stanzas --------------------------------- diff --git a/test/blackbox-tests/test-cases/github3046.t b/test/blackbox-tests/test-cases/github3046.t index 3e69b68328c..cfb9536e1f0 100644 --- a/test/blackbox-tests/test-cases/github3046.t +++ b/test/blackbox-tests/test-cases/github3046.t @@ -9,8 +9,8 @@ are given as parameters $ dune init exe main --libs="str gsl" dune: option '--libs': invalid element in list ('str gsl'): expected a valid dune atom - Usage: dune init [OPTION]… COMPONENT NAME [PATH] - Try 'dune init --help' or 'dune --help' for more information. + Usage: dune init executable [OPTION]… NAME [PATH] + Try 'dune init executable --help' or 'dune --help' for more information. [1] `dune init lib foo --ppx="foo bar"` returns an informative parsing error @@ -18,8 +18,8 @@ are given as parameters $ dune init lib foo --ppx="foo bar" dune: option '--ppx': invalid element in list ('foo bar'): expected a valid dune atom - Usage: dune init [OPTION]… COMPONENT NAME [PATH] - Try 'dune init --help' or 'dune --help' for more information. + Usage: dune init library [OPTION]… NAME [PATH] + Try 'dune init library --help' or 'dune --help' for more information. [1] `dune init lib foo --public="some/invalid&name!"` returns an informative parsing error @@ -29,6 +29,6 @@ are given as parameters Library names must be non-empty and composed only of the following characters: 'A'..'Z', 'a'..'z', '_' or '0'..'9'. - Usage: dune init [OPTION]… COMPONENT NAME [PATH] - Try 'dune init --help' or 'dune --help' for more information. + Usage: dune init library [OPTION]… NAME [PATH] + Try 'dune init library --help' or 'dune --help' for more information. [1]