Trigger an error for unquoted concatenations with list variables
Thus x${v} where v is a variable that returns several values must necessarily be quoted: "x${v}".
This commit is contained in:
parent
754aa59cc3
commit
a91cf637ae
|
@ -272,20 +272,29 @@ module Var_expansion = struct
|
||||||
| Paths of Path.t list * Concat_or_split.t
|
| Paths of Path.t list * Concat_or_split.t
|
||||||
| Strings of string list * Concat_or_split.t
|
| Strings of string list * Concat_or_split.t
|
||||||
|
|
||||||
|
let is_multivalued = function
|
||||||
|
| Paths (_, Split) | Strings (_, Split) -> true
|
||||||
|
| Paths (_, Concat) | Strings (_, Concat) -> false
|
||||||
|
|
||||||
let concat = function
|
let concat = function
|
||||||
| [s] -> s
|
| [s] -> s
|
||||||
| l -> String.concat ~sep:" " l
|
| l -> String.concat ~sep:" " l
|
||||||
|
|
||||||
|
let to_string = function
|
||||||
|
| Strings (l, _) -> concat l
|
||||||
|
| Paths (l, _) -> concat (List.map l ~f:Path.to_string)
|
||||||
|
|
||||||
|
(* Relative to [dir]. *)
|
||||||
let string_of_path ~dir p = Path.reach ~from:dir p
|
let string_of_path ~dir p = Path.reach ~from:dir p
|
||||||
let path_of_string ~dir s = Path.relative dir s
|
let path_of_string ~dir s = Path.relative dir s
|
||||||
|
|
||||||
let to_strings ~dir = function
|
let to_strings_rel ~dir = function
|
||||||
| Strings (l, Split ) -> l
|
| Strings (l, Split ) -> l
|
||||||
| Strings (l, Concat) -> [concat l]
|
| Strings (l, Concat) -> [concat l]
|
||||||
| Paths (l, Split ) -> List.map l ~f:(string_of_path ~dir)
|
| Paths (l, Split ) -> List.map l ~f:(string_of_path ~dir)
|
||||||
| Paths (l, Concat) -> [concat (List.map l ~f:(string_of_path ~dir))]
|
| Paths (l, Concat) -> [concat (List.map l ~f:(string_of_path ~dir))]
|
||||||
|
|
||||||
let to_string ~dir = function
|
let to_string_rel ~dir = function
|
||||||
| Strings (l, _) -> concat l
|
| Strings (l, _) -> concat l
|
||||||
| Paths (l, _) -> concat (List.map l ~f:(string_of_path ~dir))
|
| Paths (l, _) -> concat (List.map l ~f:(string_of_path ~dir))
|
||||||
|
|
||||||
|
@ -352,29 +361,27 @@ module Unexpanded = struct
|
||||||
include Past
|
include Past
|
||||||
|
|
||||||
module E = struct
|
module E = struct
|
||||||
let string ~dir ~f = function
|
|
||||||
| Inl x -> x
|
|
||||||
| Inr template ->
|
|
||||||
SW.expand template ~f:(fun loc var ->
|
|
||||||
match f loc var with
|
|
||||||
| None -> None
|
|
||||||
| Some e -> Some (VE.to_string ~dir e))
|
|
||||||
|
|
||||||
let expand ~generic ~special ~map ~dir ~f = function
|
let expand ~generic ~special ~map ~dir ~f = function
|
||||||
| Inl x -> map x
|
| Inl x -> map x
|
||||||
| Inr template as x ->
|
| Inr template ->
|
||||||
match SW.unquoted_var template with
|
match SW.expand_generic template ~f
|
||||||
| None -> generic ~dir (string ~dir ~f x)
|
~is_multivalued:VE.is_multivalued
|
||||||
| Some var ->
|
~to_string:(VE.to_string_rel ~dir)
|
||||||
match f (SW.loc template) var with
|
with
|
||||||
| None -> generic ~dir (SW.to_string template)
|
| Inl e -> special ~dir e
|
||||||
| Some e -> special ~dir e
|
| Inr s -> generic ~dir s
|
||||||
[@@inlined always]
|
[@@inlined always]
|
||||||
|
|
||||||
|
let string ~dir ~f x =
|
||||||
|
expand ~dir ~f x
|
||||||
|
~generic:(fun ~dir:_ x -> x)
|
||||||
|
~special:VE.to_string_rel
|
||||||
|
~map:(fun x -> x)
|
||||||
|
|
||||||
let strings ~dir ~f x =
|
let strings ~dir ~f x =
|
||||||
expand ~dir ~f x
|
expand ~dir ~f x
|
||||||
~generic:(fun ~dir:_ x -> [x])
|
~generic:(fun ~dir:_ x -> [x])
|
||||||
~special:VE.to_strings
|
~special:VE.to_strings_rel
|
||||||
~map:(fun x -> [x])
|
~map:(fun x -> [x])
|
||||||
|
|
||||||
let path ~dir ~f x =
|
let path ~dir ~f x =
|
||||||
|
@ -445,28 +452,23 @@ module Unexpanded = struct
|
||||||
end
|
end
|
||||||
|
|
||||||
module E = struct
|
module E = struct
|
||||||
let string ~dir ~f template =
|
|
||||||
SW.partial_expand template ~f:(fun loc var ->
|
|
||||||
match f loc var with
|
|
||||||
| None -> None
|
|
||||||
| Some e -> Some (VE.to_string ~dir e))
|
|
||||||
|
|
||||||
let expand ~generic ~special ~dir ~f template =
|
let expand ~generic ~special ~dir ~f template =
|
||||||
match SW.unquoted_var template with
|
match SW.partial_expand_generic template ~f
|
||||||
| None -> begin
|
~is_multivalued:VE.is_multivalued
|
||||||
match string ~dir ~f template with
|
~to_string:(VE.to_string_rel ~dir) with
|
||||||
| Inl x -> Inl (generic ~dir x)
|
| Inl (Inl e) -> Inl(special ~dir e)
|
||||||
| Inr _ as x -> x
|
| Inl (Inr s) -> Inl(generic ~dir s)
|
||||||
end
|
| Inr _ as x -> x
|
||||||
| Some var ->
|
|
||||||
match f (SW.loc template) var with
|
let string ~dir ~f x =
|
||||||
| None -> Inr template
|
expand ~dir ~f x
|
||||||
| Some e -> Inl (special ~dir e)
|
~generic:(fun ~dir:_ x -> x)
|
||||||
|
~special:VE.to_string_rel
|
||||||
|
|
||||||
let strings ~dir ~f x =
|
let strings ~dir ~f x =
|
||||||
expand ~dir ~f x
|
expand ~dir ~f x
|
||||||
~generic:(fun ~dir:_ x -> [x])
|
~generic:(fun ~dir:_ x -> [x])
|
||||||
~special:VE.to_strings
|
~special:VE.to_strings_rel
|
||||||
|
|
||||||
let path ~dir ~f x =
|
let path ~dir ~f x =
|
||||||
expand ~dir ~f x
|
expand ~dir ~f x
|
||||||
|
|
|
@ -3,13 +3,15 @@ open! Import
|
||||||
module Var_expansion : sig
|
module Var_expansion : sig
|
||||||
module Concat_or_split : sig
|
module Concat_or_split : sig
|
||||||
type t =
|
type t =
|
||||||
| Concat (* default *)
|
| Concat (** default *)
|
||||||
| Split (* the variable is a "split" list of items *)
|
| Split (** the variable is a "split" list of items *)
|
||||||
end
|
end
|
||||||
|
|
||||||
type t =
|
type t =
|
||||||
| Paths of Path.t list * Concat_or_split.t
|
| Paths of Path.t list * Concat_or_split.t
|
||||||
| Strings of string list * Concat_or_split.t
|
| Strings of string list * Concat_or_split.t
|
||||||
|
|
||||||
|
val to_string : t -> string
|
||||||
end
|
end
|
||||||
|
|
||||||
module Outputs : module type of struct include Action_intf.Outputs end
|
module Outputs : module type of struct include Action_intf.Outputs end
|
||||||
|
|
|
@ -59,11 +59,6 @@ let rec of_tokens : Token.t list -> item list = function
|
||||||
|
|
||||||
let items_of_string s = of_tokens (Token.tokenise s)
|
let items_of_string s = of_tokens (Token.tokenise s)
|
||||||
|
|
||||||
let unquoted_var t =
|
|
||||||
match t.quoted, t.items with
|
|
||||||
| true, [Var(_, s)] -> Some s
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let t : Sexp.Of_sexp.ast -> t = function
|
let t : Sexp.Of_sexp.ast -> t = function
|
||||||
| Atom(loc, s) -> { items = items_of_string s; loc; quoted = false }
|
| Atom(loc, s) -> { items = items_of_string s; loc; quoted = false }
|
||||||
| Quoted_string (loc, s) ->
|
| Quoted_string (loc, s) ->
|
||||||
|
@ -72,9 +67,9 @@ let t : Sexp.Of_sexp.ast -> t = function
|
||||||
|
|
||||||
let loc t = t.loc
|
let loc t = t.loc
|
||||||
|
|
||||||
let virt ?(quoted=true) pos s =
|
let virt ?(quoted=false) pos s =
|
||||||
{ items = items_of_string s; loc = Loc.of_pos pos; quoted }
|
{ items = items_of_string s; loc = Loc.of_pos pos; quoted }
|
||||||
let virt_var ?(quoted=true) pos s =
|
let virt_var ?(quoted=false) pos s =
|
||||||
{ items = [Var (Braces, s)]; loc = Loc.of_pos pos; quoted }
|
{ items = [Var (Braces, s)]; loc = Loc.of_pos pos; quoted }
|
||||||
let virt_text pos s =
|
let virt_text pos s =
|
||||||
{ items = [Text s]; loc = Loc.of_pos pos; quoted = true }
|
{ items = [Text s]; loc = Loc.of_pos pos; quoted = true }
|
||||||
|
@ -108,21 +103,41 @@ let string_of_var syntax v =
|
||||||
| Parens -> sprintf "$(%s)" v
|
| Parens -> sprintf "$(%s)" v
|
||||||
| Braces -> sprintf "${%s}" v
|
| Braces -> sprintf "${%s}" v
|
||||||
|
|
||||||
|
let expand_generic t ~f ~is_multivalued ~to_string =
|
||||||
|
match t.items with
|
||||||
|
| [Var (syntax, v)] when not t.quoted ->
|
||||||
|
(* Unquoted single var *)
|
||||||
|
(match f t.loc v with
|
||||||
|
| Some e -> Inl e
|
||||||
|
| None -> Inr(string_of_var syntax v))
|
||||||
|
| _ ->
|
||||||
|
Inr(List.map t.items ~f:(function
|
||||||
|
| Text s -> s
|
||||||
|
| Var (syntax, v) ->
|
||||||
|
match f t.loc v with
|
||||||
|
| Some x ->
|
||||||
|
if not t.quoted && is_multivalued x then
|
||||||
|
Loc.fail t.loc "please quote the string \
|
||||||
|
containing the list variable %s"
|
||||||
|
(string_of_var syntax v)
|
||||||
|
else to_string x
|
||||||
|
| None -> string_of_var syntax v)
|
||||||
|
|> String.concat ~sep:"")
|
||||||
|
|
||||||
|
let always_false _ = false
|
||||||
|
let identity_string (s: string) = s
|
||||||
|
|
||||||
let expand t ~f =
|
let expand t ~f =
|
||||||
List.map t.items ~f:(function
|
match expand_generic t ~f ~is_multivalued:always_false
|
||||||
| Text s -> s
|
~to_string:identity_string with
|
||||||
| Var (syntax, v) ->
|
| Inl s | Inr s -> s
|
||||||
match f t.loc v with
|
|
||||||
| Some x -> x
|
|
||||||
| None -> string_of_var syntax v)
|
|
||||||
|> String.concat ~sep:""
|
|
||||||
|
|
||||||
let concat_rev = function
|
let concat_rev = function
|
||||||
| [] -> ""
|
| [] -> ""
|
||||||
| [s] -> s
|
| [s] -> s
|
||||||
| l -> String.concat (List.rev l) ~sep:""
|
| l -> String.concat (List.rev l) ~sep:""
|
||||||
|
|
||||||
let partial_expand t ~f =
|
let partial_expand_generic t ~f ~is_multivalued ~to_string =
|
||||||
let commit_text acc_text acc =
|
let commit_text acc_text acc =
|
||||||
let s = concat_rev acc_text in
|
let s = concat_rev acc_text in
|
||||||
if s = "" then acc else Text s :: acc
|
if s = "" then acc else Text s :: acc
|
||||||
|
@ -131,17 +146,32 @@ let partial_expand t ~f =
|
||||||
match items with
|
match items with
|
||||||
| [] -> begin
|
| [] -> begin
|
||||||
match acc with
|
match acc with
|
||||||
| [] -> Inl (concat_rev acc_text)
|
| [] -> Inl (Inr(concat_rev acc_text))
|
||||||
| _ ->
|
| _ -> Inr { t with items = List.rev (commit_text acc_text acc) }
|
||||||
Inr { t with items = List.rev (commit_text acc_text acc) }
|
|
||||||
end
|
end
|
||||||
| Text s :: items -> loop (s :: acc_text) acc items
|
| Text s :: items -> loop (s :: acc_text) acc items
|
||||||
| Var (_, v) as it :: items ->
|
| Var (syntax, v) as it :: items ->
|
||||||
match f t.loc v with
|
match f t.loc v with
|
||||||
| None -> loop [] (it :: commit_text acc_text acc) items
|
| None -> loop [] (it :: commit_text acc_text acc) items
|
||||||
| Some s -> loop (s :: acc_text) acc items
|
| Some x ->
|
||||||
|
if not t.quoted && is_multivalued x then
|
||||||
|
Loc.fail t.loc "please quote the string containing the \
|
||||||
|
list variable %s" (string_of_var syntax v)
|
||||||
|
else loop (to_string x :: acc_text) acc items
|
||||||
in
|
in
|
||||||
loop [] [] t.items
|
match t.items with
|
||||||
|
| [Var (_, v)] when not t.quoted ->
|
||||||
|
(* Unquoted single var *)
|
||||||
|
(match f t.loc v with
|
||||||
|
| Some e -> Inl (Inl e)
|
||||||
|
| None -> Inr t)
|
||||||
|
| _ -> loop [] [] t.items
|
||||||
|
|
||||||
|
let partial_expand t ~f =
|
||||||
|
match partial_expand_generic t ~f ~is_multivalued:always_false
|
||||||
|
~to_string:identity_string with
|
||||||
|
| Inl(Inl s | Inr s) -> Inl s
|
||||||
|
| Inr _ as x -> x
|
||||||
|
|
||||||
let to_string t =
|
let to_string t =
|
||||||
match t.items with
|
match t.items with
|
||||||
|
|
|
@ -22,15 +22,12 @@ val to_string : t -> string
|
||||||
|
|
||||||
(** [t] generated by the OCaml code. The first argument should be
|
(** [t] generated by the OCaml code. The first argument should be
|
||||||
[__POS__]. The second is either a string to parse, a variable name
|
[__POS__]. The second is either a string to parse, a variable name
|
||||||
or plain text. *)
|
or plain text. [quoted] says whether the string is quoted ([false]
|
||||||
|
by default). *)
|
||||||
val virt : ?quoted: bool -> (string * int * int * int) -> string -> t
|
val virt : ?quoted: bool -> (string * int * int * int) -> string -> t
|
||||||
val virt_var : ?quoted: bool -> (string * int * int * int) -> string -> t
|
val virt_var : ?quoted: bool -> (string * int * int * int) -> string -> t
|
||||||
val virt_text : (string * int * int * int) -> string -> t
|
val virt_text : (string * int * int * int) -> string -> t
|
||||||
|
|
||||||
val unquoted_var : t -> string option
|
|
||||||
(** [unquoted_var t] return the [Some name] where [name] is the name of
|
|
||||||
the variable if [t] is solely made of a variable and [None] otherwise. *)
|
|
||||||
|
|
||||||
val vars : t -> String_set.t
|
val vars : t -> String_set.t
|
||||||
(** [vars t] returns the set of all variables in [t]. *)
|
(** [vars t] returns the set of all variables in [t]. *)
|
||||||
|
|
||||||
|
@ -42,18 +39,38 @@ val iter : t -> f:(Loc.t -> string -> unit) -> unit
|
||||||
(** [iter t ~f] iterates [f] over all variables of [t], the text
|
(** [iter t ~f] iterates [f] over all variables of [t], the text
|
||||||
portions being ignored. *)
|
portions being ignored. *)
|
||||||
|
|
||||||
val expand : t -> f:(Loc.t -> string -> string option) -> string
|
val expand_generic :
|
||||||
(** [expand t ~f] return [t] where all variables have been expanded
|
t -> f:(Loc.t -> string -> 'a option) -> is_multivalued:('a -> bool) ->
|
||||||
using [f]. If [f] returns [Some x], the variable is replaced by
|
to_string:('a -> string) -> ('a, string) either
|
||||||
[x]; if [f] returns [None], the variable is inserted as [${v}] or
|
(** [expand_generic t ~f] return [t] where all variables have been expanded
|
||||||
[$(v)] — depending on the original concrete syntax used — where [v]
|
using [f]. If [f loc var] return [Some x], the variable [var] is
|
||||||
is the name if the variable. *)
|
replaced by [x]; otherwise, the variable is inserted as [${var}] or
|
||||||
|
[$(var)] — depending on the original concrete syntax used.
|
||||||
|
|
||||||
val partial_expand : t -> f:(Loc.t -> string -> string option)
|
- [is_multivalued]: says whether the variables is a multivalued
|
||||||
-> (string, t) either
|
one (such as for example ${@}) which much be in quoted strings to
|
||||||
(** [partial_expand t ~f] is like [expand] where all variables that
|
be concatenated to text or other variables.
|
||||||
could be expanded (i.e., those for which [f] returns [Some _]) are.
|
- [to_string]: For single unquoted variables, the outcome of [f] is
|
||||||
If all the variables of [t] were expanded, a string is returned.
|
directly returned. For variables containing text portions or which
|
||||||
If [f] returns [None] on at least a variable of [t], it returns a
|
are quoted, the value returned by [f] is converted to a string
|
||||||
string-with-vars. *)
|
using [to_string]. *)
|
||||||
|
|
||||||
|
val expand :
|
||||||
|
t -> f:(Loc.t -> string -> string option) -> string
|
||||||
|
(** Specialized version [expand_generic] that returns a string (so
|
||||||
|
variables are assumed to expand to a single value). *)
|
||||||
|
|
||||||
|
val partial_expand_generic :
|
||||||
|
t -> f:(Loc.t -> string -> 'a option) -> is_multivalued:('a -> bool) ->
|
||||||
|
to_string:('a -> string) -> (('a, string) either, t) either
|
||||||
|
(** [partial_expand_generic t ~f] is like [expand_generic] where all
|
||||||
|
variables that could be expanded (i.e., those for which [f] returns
|
||||||
|
[Some _]) are. If all the variables of [t] were expanded, a string
|
||||||
|
is returned. If [f] returns [None] on at least a variable of [t],
|
||||||
|
it returns a string-with-vars. *)
|
||||||
|
|
||||||
|
val partial_expand :
|
||||||
|
t -> f:(Loc.t -> string -> string option) -> (string, t) either
|
||||||
|
(** [partial_expand] is a specialized version of
|
||||||
|
[partial_expand_generic] that returns a string. *)
|
||||||
|
|
||||||
|
|
|
@ -50,12 +50,8 @@ let expand_vars t ~(scope : Lib_db.Scope.t) ~dir s =
|
||||||
| "SCOPE_ROOT" ->
|
| "SCOPE_ROOT" ->
|
||||||
Some (Path.reach ~from:dir (Lib_db.Scope.root scope))
|
Some (Path.reach ~from:dir (Lib_db.Scope.root scope))
|
||||||
| var ->
|
| var ->
|
||||||
let open Action.Var_expansion in
|
|
||||||
expand_var_no_root t var
|
expand_var_no_root t var
|
||||||
|> Option.map ~f:(function
|
|> Option.map ~f:(fun e -> Action.Var_expansion.to_string e))
|
||||||
| Paths(p,_) -> let p = List.map p ~f:Path.to_string in
|
|
||||||
String.concat ~sep:" " p
|
|
||||||
| Strings(s,_) -> String.concat ~sep:" " s))
|
|
||||||
|
|
||||||
let resolve_program t ?hint bin =
|
let resolve_program t ?hint bin =
|
||||||
Artifacts.binary ?hint t.artifacts bin
|
Artifacts.binary ?hint t.artifacts bin
|
||||||
|
@ -139,7 +135,6 @@ let create
|
||||||
| Some p -> p
|
| Some p -> p
|
||||||
in
|
in
|
||||||
let open Action.Var_expansion in
|
let open Action.Var_expansion in
|
||||||
let open Action.Var_expansion.Concat_or_split in
|
|
||||||
let make =
|
let make =
|
||||||
match Bin.make with
|
match Bin.make with
|
||||||
| None -> Strings (["make"], Split)
|
| None -> Strings (["make"], Split)
|
||||||
|
@ -657,9 +652,9 @@ module Action = struct
|
||||||
| [] ->
|
| [] ->
|
||||||
Loc.warn loc "Variable '<' used with no explicit \
|
Loc.warn loc "Variable '<' used with no explicit \
|
||||||
dependencies@.";
|
dependencies@.";
|
||||||
Strings ([""], Split)
|
Strings ([""], Concat)
|
||||||
| dep :: _ ->
|
| dep :: _ ->
|
||||||
Paths ([dep], Split))
|
Paths ([dep], Concat))
|
||||||
| "^" -> Some (Paths (deps_written_by_user, Split))
|
| "^" -> Some (Paths (deps_written_by_user, Split))
|
||||||
| _ -> None)
|
| _ -> None)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue