Skip to content

Commit

Permalink
Add dune init command
Browse files Browse the repository at this point in the history
Signed-off-by: Shon Feder <[email protected]>
Signed-off-by: Rudi Grinberg <[email protected]>
  • Loading branch information
shonfeder authored and rgrinberg committed Mar 27, 2019
1 parent e4b407e commit 5daa555
Show file tree
Hide file tree
Showing 23 changed files with 981 additions and 3 deletions.
3 changes: 3 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ unreleased
- Add support for library variants and default implementations. (#1900,
@TheLortex)

- Add experimental `$ dune init` command. This command is used to create or
update project boilerplate. (#1448, fixes #159, @shonfeder)

1.8.2 (10/03/2019)
------------------

Expand Down
9 changes: 9 additions & 0 deletions bin/common.mli
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,19 @@ type t =

val prefix_target : t -> string -> string

(** [set_common common ~targets] is [set_dirs common] followed by
[set_common_other common ~targets]. In general, [set_common] executes
sequence of side-effecting actions to initialize Dune's working
environment based on the options determined in a [Common.t] record *)
val set_common : t -> targets:string list -> unit

(** [set_common_other common ~targets] sets all stateful values dictated by
[common], except those accounted for by [set_dirs]. [targets] are
used to obtain external library dependency hints, if needed. *)
val set_common_other : t -> targets:string list -> unit

(** [set_dirs common] sets the workspace root and build directories, and makes
the root the current working directory *)
val set_dirs : t -> unit

val help_secs
Expand Down
115 changes: 115 additions & 0 deletions bin/init.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
open Stdune
open Import

open Dune.Dune_init

(* 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 ->
die "The %s component does not support the %s option"
(Kind.to_string kind) option_name
in
List.iter ~f:report_invalid_option unsupported_options

let doc = "Initialize dune components"
let man =
[ `S "DESCRIPTION"
; `P {|$(b,dune init {lib,exe,test} NAME [PATH]) initialize a new dune
component of the specified kind, named $(b,NAME), with fields
determined by the supplied options.|}
; `P {|If the optional $(b,PATH) is provided, the project 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 as
well as for creating new dune files and basic component templates.|}
; `S "EXAMPLES"
; `Pre {|
Define an executable component named 'myexe' in a dune file in the
current directory:
dune init exe myexe
Define 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
Define a library component named mytest in a dune file in the ./test
directory that depends on mylib:
dune init test myexe test --libs mylib|}
]

let info = Term.info "init" ~doc ~man

let term =
let+ common_term = Common.term
and+ kind =
(* TODO(shonfeder): Replace with nested subcommand once we have support for that *)
Arg.(required & pos 0 (some (enum Kind.commands)) None & info [] ~docv:"INIT_KIND")
and+ name =
Arg.(required & pos 1 (some string) None & info [] ~docv:"NAME")
and+ path =
Arg.(value & pos 2 (some string) None & info [] ~docv:"PATH" )
and+ libraries =
Arg.(value
& opt (list string) []
& info ["libs"]
~docv:"LIBRARIES"
~doc:"Libraries on which the component depends")
and+ pps =
Arg.(value
& opt (list string) []
& info ["ppx"]
~docv:"PREPROCESSORS"
~doc:"ppx preprocessors used by the component")
and+ public =
(* TODO(shonfeder): Move to subcommands {lib, exe} once implemented *)
Arg.(value
& opt ~vopt:(Some "") (some string) None
& info ["public"]
~docv:"PUBLIC_NAME"
~doc:"If called with an argument, make the component public \
under the given PUBLIC_NAME. If supplied without an \
argument, use NAME.")
and+ inline_tests =
(* TODO Move to subcommand lib once implemented *)
Arg.(value
& flag
& info ["inline-tests"]
~docv:"USE_INLINE_TESTS"
~doc:"Whether to use inline tests. \
Only applicable for lib components.")
in

validate_component_name name;

Common.set_common common_term ~targets:[];
let open Component in
let context = Init_context.make path in
let common : Options.common = { name; libraries; pps } in
let given_public = Option.is_some public in
begin match kind with
| Kind.Library ->
init @@ Library { context; common; options = {public; inline_tests} }
| Kind.Executable ->
let unsupported_options =
["inline-tests", inline_tests]
in
validate_component_options kind ~unsupported_options;
init @@ Executable { context; common; options = {public} }
| Kind.Test ->
let unsupported_options =
[ "public", given_public
; "inline-tests", inline_tests]
in
validate_component_options kind ~unsupported_options;
init @@ Test { context; common; options = () }
end;

print_completion kind name

let command = term, info

1 change: 1 addition & 0 deletions bin/init.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
val command : unit Cmdliner.Term.t * Cmdliner.Term.info
1 change: 1 addition & 0 deletions bin/main.ml
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ let all =
; Subst.command
; Print_rules.command
; Utop.command
; Init.command
; promote
; Printenv.command
; Help.command
Expand Down
9 changes: 9 additions & 0 deletions doc/dune.inc
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@
(package dune)
(files dune-help.1))

(rule
(with-stdout-to dune-init.1
(run dune init --help=groff)))

(install
(section man)
(package dune)
(files dune-init.1))

(rule
(with-stdout-to dune-install.1
(run dune install --help=groff)))
Expand Down
2 changes: 1 addition & 1 deletion doc/update-jbuild.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
set -e -o pipefail

CMDS=$(dune --help=plain | \
sed -n '/COMMANDS/,/OPTIONS/p' | sed -En 's/^ ([a-z-]+)/\1/p')
sed -n '/COMMANDS/,/OPTIONS/p' | sed -En 's/^ ([a-z-]+) ?.*/\1/p')

for cmd in $CMDS; do
cat <<EOF
Expand Down
54 changes: 53 additions & 1 deletion doc/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,58 @@ Usage

This section describe usage of dune from the shell.

.. _initializing_components:

Initializing components
=======================

NOTE: The ``dune init`` command is still under development and subject to
change.

Dune's ``init`` subcommand provides limited support for generating dune file
stanzas and folder structures to define components. ``dune init`` can be used to
quickly add new libraries, tests, or executables without having to manually edit
a dune file, or it can be composed to programmatically generate parts of a
multi-component project.

For example, to add a new executable to a ``dune`` file in the current directory
(creating the file if necessary), you can run

.. code:: bash
$ dune init exe myexe --libs base,containers,notty --ppx ppx_deriving
This will add the following stanza to the ``dune`` file:

.. code:: scheme
(executable
(name main)
(libraries base containers notty)
(preprocess
(pps ppx_deriving)))
Or, to create a new directory ``src``, initialized as a library, you can run:

.. code:: bash
$ dune init lib mylib src --libs core --inline-tests --public
This will ensure the file ``./src/dune`` contains the following stanza (creating
the file and directory, if needed):

.. code:: scheme
(library
(public_name mylib)
(inline_tests)
(name mylib)
(libraries core)
(preprocess
(pps ppx_inline_tests)))
Consult the manual page ``dune init --help`` for more details.

.. _finding-root:

Finding the root
Expand Down Expand Up @@ -505,7 +557,7 @@ must be prefixed by the shortest one.
Watermarking
============

One of the feature dune-release provides is watermarking; it replaces
One of the features dune-release provides is watermarking; it replaces
various strings of the form ``%%ID%%`` in all files of your project
before creating a release tarball or when the package is pinned by the
user using opam.
Expand Down
9 changes: 9 additions & 0 deletions src/dune_file.mli
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,15 @@ module Stanzas : sig

type syntax = OCaml | Plain

(** [parse ~file ~kind project stanza_exprs] is a list of [Stanza.t]s derived
from decoding the [stanza_exprs] from [Dune_lang.Ast.t]s to [Stanza.t]s
and combining those with the stanzas parsed from the supplied dune [file].
The stanzas are parsed in the context of the dune [project].
The syntax [kind] determines whether the expected syntax is either the
depreciated jbuilder syntax or the version of dune syntax specified in the
[project]. *)
val parse
: file:Path.t
-> kind:Dune_lang.Syntax.t
Expand Down
Loading

0 comments on commit 5daa555

Please sign in to comment.