Add ${!^} and ${!@} and document them

This commit is contained in:
Jeremie Dimino 2017-05-29 19:12:37 +01:00
parent 3241026fff
commit a8a43e4b22
4 changed files with 74 additions and 19 deletions

View File

@ -523,9 +523,11 @@ Jbuilder supports the following variables:
In addition, ``(action ...)`` fields support the following special variables: In addition, ``(action ...)`` fields support the following special variables:
- ``@`` expands to the list of target, separated by spaces - ``@`` expands to the list of target, separated by spaces
- ``!@`` same as ``@`` but with a split semantic (see below)
- ``<`` expands to the first dependency, or the empty string if there are no - ``<`` expands to the first dependency, or the empty string if there are no
dependencies dependencies
- ``^`` expands to the list of dependencies, separated by spaces - ``^`` expands to the list of dependencies, separated by spaces
- ``!^`` same as ``^`` but with a split semantic (see below)
- ``path:<path>`` expands to ``<path>`` - ``path:<path>`` expands to ``<path>``
- ``exe:<path>`` is the same as ``<path>``, except when cross-compiling, in - ``exe:<path>`` is the same as ``<path>``, except when cross-compiling, in
which case it will expand to ``<path>`` from the host build context which case it will expand to ``<path>`` from the host build context
@ -558,6 +560,39 @@ transparently whether things are installed or not.
Note that aliases are ignored by both ``${<}`` and ``${^}``. Note that aliases are ignored by both ``${<}`` and ``${^}``.
Moreover ``${^}`` and ``${@}`` will always expand to a single
string. For instance in:
.. code:: scheme
(run foo ${^})
even if there are two dependencies ``a`` and ``b``, the produced
command will be equivalent to the shell command:
.. code:: shell
$ foo "a b"
In order to *split* them, you can use ``${!^}`` and ``${!@}``. These
two forms are only available in ``(run ...)`` forms and can only be
used as a whole atom, i.e. they can't be used inside a quoted
atom. Replacing ``${^}`` by ``${!^}`` in the previous example would
produce a command equivalent to this shell command:
.. code:: shell
$ foo "a" "b"
You can also use ``${!^}`` as program name, for instance:
.. code:: scheme
(rule
((targets (result.txt))
(deps (foo.exe (glob_files *.txt)))
(action (run ${!^}))))
Library dependencies Library dependencies
-------------------- --------------------

View File

@ -3,10 +3,12 @@ open Sexp.Of_sexp
module Env_var_map = Context.Env_var_map module Env_var_map = Context.Env_var_map
type split_or_concat = Split | Concat
type var_expansion = type var_expansion =
| Not_found | Not_found
| Path of Path.t | Path of Path.t
| Paths of Path.t list | Paths of Path.t list * split_or_concat
| Str of string | Str of string
let expand_str ~dir ~f template = let expand_str ~dir ~f template =
@ -14,9 +16,21 @@ let expand_str ~dir ~f template =
match f var with match f var with
| Not_found -> None | Not_found -> None
| Path path -> Some (Path.reach ~from:dir path) | Path path -> Some (Path.reach ~from:dir path)
| Paths l -> Some (List.map l ~f:(Path.reach ~from:dir) |> String.concat ~sep:" ") | Paths (l, _) -> Some (List.map l ~f:(Path.reach ~from:dir) |> String.concat ~sep:" ")
| Str s -> Some s) | Str s -> Some s)
let expand_str_split ~dir ~f template =
match String_with_vars.just_a_var template with
| None -> [expand_str ~dir ~f template]
| Some var ->
match f var with
| Not_found -> [expand_str ~dir ~f template]
| Path path -> [Path.reach ~from:dir path]
| Str s -> [s]
| Paths (l, Concat) ->
[List.map l ~f:(Path.reach ~from:dir) |> String.concat ~sep:" "]
| Paths (l, Split) -> List.map l ~f:(Path.reach ~from:dir)
let expand_path ~dir ~f template = let expand_path ~dir ~f template =
match String_with_vars.just_a_var template with match String_with_vars.just_a_var template with
| None -> expand_str ~dir ~f template |> Path.relative dir | None -> expand_str ~dir ~f template |> Path.relative dir
@ -24,9 +38,9 @@ let expand_path ~dir ~f template =
match f v with match f v with
| Not_found -> expand_str ~dir ~f template |> Path.relative dir | Not_found -> expand_str ~dir ~f template |> Path.relative dir
| Path p | Path p
| Paths [p] -> p | Paths ([p], _) -> p
| Str s -> Path.relative dir s | Str s -> Path.relative dir s
| Paths l -> | Paths (l, _) ->
List.map l ~f:(Path.reach ~from:dir) List.map l ~f:(Path.reach ~from:dir)
|> String.concat ~sep:" " |> String.concat ~sep:" "
|> Path.relative dir |> Path.relative dir
@ -41,17 +55,19 @@ let expand_prog ctx ~dir ~f template =
| None -> Utils.program_not_found ~context:ctx.name s | None -> Utils.program_not_found ~context:ctx.name s
in in
match String_with_vars.just_a_var template with match String_with_vars.just_a_var template with
| None -> resolve (expand_str ~dir ~f template) | None -> (resolve (expand_str ~dir ~f template), [])
| Some v -> | Some v ->
match f v with match f v with
| Not_found -> resolve (expand_str ~dir ~f template) | Not_found -> (resolve (expand_str ~dir ~f template), [])
| Path p | Path p
| Paths [p] -> p | Paths ([p], _) -> (p, [])
| Str s -> resolve s | Str s -> (resolve s, [])
| Paths l -> | Paths (p :: args, Split) -> (p, List.map args ~f:(Path.reach ~from:dir))
List.map l ~f:(Path.reach ~from:dir) | Paths (l, _) ->
|> String.concat ~sep:" " (List.map l ~f:(Path.reach ~from:dir)
|> resolve |> String.concat ~sep:" "
|> resolve,
[])
module Outputs = struct module Outputs = struct
include Action_intf.Outputs include Action_intf.Outputs
@ -197,8 +213,9 @@ module Unexpanded = struct
let rec expand ctx dir t ~f : action = let rec expand ctx dir t ~f : action =
match t with match t with
| Run (prog, args) -> | Run (prog, args) ->
Run (expand_prog ctx ~dir ~f prog, let prog, more_args = expand_prog ctx ~dir ~f prog in
List.map args ~f:(fun arg -> expand_str ~dir ~f arg)) Run (prog,
more_args @ List.concat_map args ~f:(expand_str_split ~dir ~f))
| Chdir (fn, t) -> | Chdir (fn, t) ->
let fn = expand_path ~dir ~f fn in let fn = expand_path ~dir ~f fn in
Chdir (fn, expand ctx fn t ~f) Chdir (fn, expand ctx fn t ~f)

View File

@ -1,9 +1,11 @@
open! Import open! Import
type split_or_concat = Split | Concat
type var_expansion = type var_expansion =
| Not_found | Not_found
| Path of Path.t | Path of Path.t
| Paths of Path.t list | Paths of Path.t list * split_or_concat
| Str of string | Str of string
module Outputs : module type of struct include Action_intf.Outputs end module Outputs : module type of struct include Action_intf.Outputs end

View File

@ -550,13 +550,14 @@ module Action = struct
| Some exp -> exp | Some exp -> exp
| None -> | None ->
match var_name with match var_name with
| "@" -> Action.Paths targets | "@" -> Action.Paths (targets, Concat)
| "!@" -> Action.Paths (targets, Split)
| "<" -> | "<" ->
(match deps with (match deps with
| [] -> Str "" (* CR-someday jdimino: this should be an error *) | [] -> Str "" (* CR-someday jdimino: this should be an error *)
| dep :: _ -> Path dep) | dep :: _ -> Path dep)
| "^" -> | "^" -> Paths (deps, Concat)
Paths deps | "!^" -> Paths (deps, Split)
| "ROOT" -> Path sctx.context.build_dir | "ROOT" -> Path sctx.context.build_dir
| var -> | var ->
match expand_var_no_root sctx var with match expand_var_no_root sctx var with
@ -574,7 +575,7 @@ module Action = struct
~f:(fun ~key:_ ~data:exp acc -> ~f:(fun ~key:_ ~data:exp acc ->
match exp with match exp with
| Action.Path p -> Path.Set.add p acc | Action.Path p -> Path.Set.add p acc
| Paths ps -> Path.Set.union acc (Path.Set.of_list ps) | Paths (ps, _) -> Path.Set.union acc (Path.Set.of_list ps)
| Not_found | Str _ -> acc)) | Not_found | Str _ -> acc))
>>> >>>
Build.arr (fun paths -> ((), paths)) Build.arr (fun paths -> ((), paths))