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 dune init command #1448

Merged
merged 1 commit into from
Mar 27, 2019
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
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