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 a variable %{target} #2341

Merged
merged 9 commits into from
Jul 5, 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
6 changes: 6 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@

- Set version in `META` and `dune-package` files to the one read from
the vcs when no other version is available (#2224, @diml)

- Add a variable `%{target}` to be used in situations where the context
requires at most one word, so `%{targets}` can be confusing; stdout
redirections and "-o" arguments of various tools are the main use
case; also, introduce a separate field `target` that must be used
instead of `targets` in those situations. (#2341, @aalekseyev)

- Fix dependency graph of wrapped_compat modules. Previously, the dependency on
the user written entry module was omitted. (#2305, @rgrinberg)
Expand Down
61 changes: 32 additions & 29 deletions doc/dune-files.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ Stanzas
(libraries base lwt))

(rule
(targets foo.ml)
(deps generator/gen.exe)
(action (run %{deps} -o %{targets})))
(target foo.ml)
(deps generator/gen.exe)
(action (run %{deps} -o %{target})))

The following sections describe the available stanzas and their meaning.

Expand Down Expand Up @@ -419,12 +419,14 @@ The syntax is as follows:
.. code:: scheme

(rule
(targets <filenames>)
(target[s] <filenames>)
(action <action>)
<optional-fields>)

``<filenames>`` is a list of file names. Note that currently dune only
support user rules with targets in the current directory.
``<filenames>`` is a list of file names (if defined with ``targets``)
or exactly one file name (if defined with ``target``). Note that
currently dune only supports user rules with targets in the current
directory.

``<action>`` is the action to run to produce the targets from the dependencies.
See the `User actions`_ section for more details.
Expand Down Expand Up @@ -509,9 +511,9 @@ For instance:
.. code:: scheme

(rule
(targets b)
(deps a)
(action (copy %{deps} %{targets})))
(target b)
(deps a)
(action (copy %{deps} %{target})))

In this example it is obvious by inspecting the action what the
dependencies and targets are. When this is the case you can use the
Expand Down Expand Up @@ -545,10 +547,10 @@ ocamllex
.. code:: scheme

(rule
(targets <name>.ml)
(deps <name>.mll)
(action (chdir %{workspace_root}
(run %{bin:ocamllex} -q -o %{targets} %{deps}))))
(target <name>.ml)
(deps <name>.mll)
(action (chdir %{workspace_root}
(run %{bin:ocamllex} -q -o %{target} %{deps}))))

To use a different rule mode, use the long form:

Expand Down Expand Up @@ -1076,6 +1078,7 @@ Dune supports the following variables:

In addition, ``(action ...)`` fields support the following special variables:

- ``target`` expands to the one target
- ``targets`` expands to the list of target
- ``deps`` expands to the list of dependencies
- ``^`` expands to the list of dependencies, separated by spaces
Expand Down Expand Up @@ -1173,9 +1176,9 @@ Here is another example:
.. code:: scheme

(rule
(targets foo.exe)
(deps foo.c)
(action (run %{cc} -o %{targets} %{deps} -lfoolib)))
(target foo.exe)
(deps foo.c)
(action (run %{cc} -o %{target} %{deps} -lfoolib)))


Library dependencies
Expand Down Expand Up @@ -1284,10 +1287,10 @@ you had setup a rule for every file of the form:
.. code:: scheme

(rule
(targets file.pp.ml)
(deps file.ml)
(action (with-stdout-to %{targets}
(chdir %{workspace_root} <action>))))
(target file.pp.ml)
(deps file.ml)
(action (with-stdout-to %{target}
(chdir %{workspace_root} <action>))))

The equivalent of a ``-pp <command>`` option passed to the OCaml compiler is
``(system "<command> %{input-file}")``.
Expand Down Expand Up @@ -1403,22 +1406,22 @@ Named Dependencies

dune allows a user to organize dependency lists by naming them. The user is
allowed to assign a group of dependencies a name that can later be referred to
in actions (like the ``%{deps}`` and ``%{targets}`` built in variables).
in actions (like the ``%{deps}``, ``%{target}`` and ``%{targets}`` built in variables).

One instance where this is useful is for naming globs. Here's an
example of an imaginary bundle command:

.. code:: scheme

(rule
(targets archive.tar)
(target archive.tar)
(deps
index.html
(:css (glob_files *.css))
(:js foo.js bar.js)
(:img (glob_files *.png) (glob_files *.jpg)))
(action
(run %{bin:bundle} index.html -css %{css} -js %{js} -img %{img} -o %{targets})))
(run %{bin:bundle} index.html -css %{css} -js %{js} -img %{img} -o %{target})))

Note that such named dependency list can also include unnamed
dependencies (like ``index.html`` in the example above). Also, such
Expand Down Expand Up @@ -1577,9 +1580,9 @@ To understand why this is important, let's consider this dune file living in
::

(rule
(targets blah.ml)
(deps blah.mll)
(action (run ocamllex -o %{targets} %{deps})))
(target blah.ml)
(deps blah.mll)
(action (run ocamllex -o %{target} %{deps})))

Here the command that will be executed is:

Expand All @@ -1601,9 +1604,9 @@ of your project. What you should write instead is:
::

(rule
(targets blah.ml)
(deps blah.mll)
(action (chdir %{workspace_root} (run ocamllex -o %{targets} %{deps}))))
(target blah.ml)
(deps blah.mll)
(action (chdir %{workspace_root} (run ocamllex -o %{target} %{deps}))))

Locks
-----
Expand Down
39 changes: 34 additions & 5 deletions src/dune_file.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1632,9 +1632,17 @@ end

module Rule = struct
module Targets = struct

module Multiplicity = struct
type t = One | Multiple
end

type static =
{ targets : String_with_vars.t list; multiplicity : Multiplicity.t }

type t =
(* List of files in the current directory *)
| Static of String_with_vars.t list
| Static of static
| Infer

let decode_static =
Expand All @@ -1648,7 +1656,20 @@ module Rule = struct
Stanza.syntax
(1, 3)
~what:"Using variables in the targets field");
Static targets
Static { targets; multiplicity = Multiple }

let decode_one_static =
let+ () = Syntax.since Stanza.syntax (1, 11)
and+ target = String_with_vars.decode
in
Static { targets = [target]; multiplicity = One }

let fields_parser =
fields_mutually_exclusive
[ "targets", decode_static
; "target", decode_one_static
]

end


Expand Down Expand Up @@ -1761,6 +1782,7 @@ module Rule = struct
; "diff" , Action
; "diff?" , Action
; "targets" , Field
; "target" , Field
; "deps" , Field
; "action" , Field
; "locks" , Field
Expand All @@ -1782,7 +1804,7 @@ module Rule = struct
let long_form =
let+ loc = loc
and+ action = field "action" (located Action_dune_lang.decode)
and+ targets = field "targets" Targets.decode_static
and+ targets = Targets.fields_parser
and+ deps =
field "deps" (Bindings.decode Dep_conf.decode) ~default:Bindings.empty
and+ locks = field "locks" (list String_with_vars.decode) ~default:[]
Expand Down Expand Up @@ -1904,7 +1926,11 @@ module Rule = struct
List.map modules ~f:(fun name ->
let src = name ^ ".mll" in
let dst = name ^ ".ml" in
{ targets = Static [S.make_text loc dst]
{ targets =
(* CR-someday aalekseyev: want to use [multiplicity = One] here, but
can't because this is might get parsed with old dune syntax where
[multiplicity = One] is not supported. *)
Static { targets = [S.make_text loc dst]; multiplicity = Multiple }
; deps = Bindings.singleton (Dep_conf.File (S.virt_text __POS__ src))
; action =
(loc,
Expand All @@ -1926,7 +1952,10 @@ module Rule = struct
let module S = String_with_vars in
List.map modules ~f:(fun name ->
let src = name ^ ".mly" in
{ targets = Static (List.map ~f:(S.make_text loc) [name ^ ".ml"; name ^ ".mli"])
{ targets = Static {
targets = List.map ~f:(S.make_text loc) [name ^ ".ml"; name ^ ".mli"];
multiplicity = Multiple;
}
; deps = Bindings.singleton (Dep_conf.File (S.virt_text __POS__ src))
; action =
(loc,
Expand Down
10 changes: 9 additions & 1 deletion src/dune_file.mli
Original file line number Diff line number Diff line change
Expand Up @@ -332,8 +332,16 @@ end

module Rule : sig
module Targets : sig

module Multiplicity : sig
type t = One | Multiple
end

type static =
{ targets : String_with_vars.t list; multiplicity : Multiplicity.t }

type t =
| Static of String_with_vars.t list
| Static of static
| Infer
end

Expand Down
49 changes: 46 additions & 3 deletions src/dune_lang/dune_lang.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1041,12 +1041,55 @@ module Decoder = struct
| Values (loc, cstr, _) -> (Values (loc, cstr), state)
| Fields (loc, cstr, _) -> (Fields (loc, cstr), state)

let traverse l ~f ctx state =
Tuple.T2.swap (
List.fold_map ~init:state l ~f:(fun state x ->
Tuple.T2.swap (f x ctx state)
))

let all = traverse ~f:(fun x -> x)

let fields_missing_need_exactly_one loc names =
User_error.raise ~loc [
Pp.textf "fields %s are all missing (exactly one is needed)"
(String.concat ~sep:", " names)
]
[@@inline never]

let fields_mutual_exclusion_violation loc names =
User_error.raise ~loc [
Pp.textf "fields %s are mutually exclusive"
(String.concat ~sep:", " names)
]
[@@inline never]

let fields_mutually_exclusive
?on_dup fields ((Fields (loc, _, _) : _ context) as ctx) state =
let res, state =
traverse ~f:(fun (name, parser) ->
field_o name ?on_dup parser
>>| fun res -> (name, res)
) fields ctx state
in
match
List.filter_map res
~f:(function (name, Some x) -> Some(name, x) | (_, None) -> None) with
| [] ->
let names = List.map fields ~f:fst in
fields_missing_need_exactly_one loc names
| [ (_name, res) ] ->
res, state
| (_ :: _ :: _ as results) ->
let names = List.map ~f:fst results in
fields_mutual_exclusion_violation loc names

let ( let* ) = ( >>= )
let ( let+ ) = ( >>| )
let ( and+ ) a b ctx state =
let a, state = a ctx state in
let b, state = b ctx state in
((a, b), state)
let a, state = a ctx state in
let b, state = b ctx state in
((a, b), state)

end

module type Conv = sig
Expand Down
7 changes: 7 additions & 0 deletions src/dune_lang/dune_lang.mli
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,8 @@ module Decoder : sig
val (>>>) : (unit, 'k) parser -> ('a, 'k) parser -> ('a, 'k) parser
val map : ('a, 'k) parser -> f:('a -> 'b) -> ('b, 'k) parser
val try_ : ('a, 'k) parser -> (exn -> ('a, 'k) parser) -> ('a, 'k) parser
val traverse : 'a list -> f:('a -> ('b, 'k) parser) -> ('b list, 'k) parser
val all : ('a, 'k) parser list -> ('a list, 'k) parser

(** Access to the context *)
val get : 'a Univ_map.Key.t -> ('a option, _) parser
Expand Down Expand Up @@ -427,6 +429,11 @@ module Decoder : sig
-> 'a t
-> 'a option fields_parser

val fields_mutually_exclusive
: ?on_dup:(Univ_map.t -> string -> Ast.t list -> unit)
-> (string * 'a t) list
-> 'a fields_parser

val field_b
: ?check:(unit t)
-> ?on_dup:(Univ_map.t -> string -> Ast.t list -> unit)
Expand Down
Loading