aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build2/context.cxx68
-rwxr-xr-xtests/variable/override/test.sh44
2 files changed, 93 insertions, 19 deletions
diff --git a/build2/context.cxx b/build2/context.cxx
index 02faee0..bb946ff 100644
--- a/build2/context.cxx
+++ b/build2/context.cxx
@@ -92,19 +92,37 @@ namespace build2
//
for (const string& s: cmd_vars)
{
- char c (s[0]); // Should at least have '='.
- string a (s, c == '!' || c == '%' ? 1 : 0);
-
- istringstream is (a);
+ istringstream is (s);
is.exceptions (istringstream::failbit | istringstream::badbit);
lexer l (is, path ("<cmdline>"));
- // This should be a name followed by =, +=, or =+.
+ // The first token should be a name, either the variable name or the
+ // scope qualification.
//
token t (l.next ());
token_type tt (l.next ().type);
- if (t.type != token_type::name ||
+ dir_path dir;
+ if (t.type == token_type::name && tt == token_type::colon)
+ {
+ if (!path::traits::is_separator (t.value.back ()))
+ fail << "expected directory (with trailing slash) instead of "
+ << "'" << t.value << "'";
+
+ dir = dir_path (move (t.value));
+
+ if (dir.relative ())
+ dir.complete ();
+
+ dir.normalize ();
+
+ t = l.next ();
+ tt = l.next ().type;
+ }
+
+ // This should be the variable name followed by =, +=, or =+.
+ //
+ if (t.type != token_type::name || t.value.empty () ||
(tt != token_type::assign &&
tt != token_type::prepend &&
tt != token_type::append))
@@ -113,14 +131,20 @@ namespace build2
info << "use double '--' to treat this argument as buildspec";
}
- const variable& var (var_pool.find (t.value));
- const string& n (var.name);
-
- // Calculate visibility and kind.
+ // Take care of the visibility. Note that here we rely on the fact that
+ // none of these characters are lexer's name separators.
//
- variable_visibility v (c == '%'
- ? variable_visibility::project
- : variable_visibility::normal);
+ char c (t.value[0]);
+ string n (t.value, c == '!' || c == '%' || c == '/' ? 1 : 0);
+
+ if (c == '!' && !dir.empty ())
+ fail << "scope-qualified global override of variable " << n;
+
+ variable_visibility v (c == '/' ? variable_visibility::scope :
+ c == '%' ? variable_visibility::project :
+ variable_visibility::normal);
+
+ const variable& var (var_pool.find (n));
const char* k (tt == token_type::assign ? ".__override" :
tt == token_type::append ? ".__suffix" : ".__prefix");
@@ -154,14 +178,24 @@ namespace build2
// Make sure the value is not typed.
//
if (r.first.type != nullptr)
- fail << "typed override of variable " << var.name;
+ fail << "typed override of variable " << n;
- if (c == '!')
+ if (c == '!' || !dir.empty ())
{
- auto p (gs.vars.assign (*o));
+ scope& s (c == '!'
+ ? gs
+ : *scopes.insert (dir, nullptr, true, false)->second);
+
+ auto p (s.vars.assign (*o));
if (!p.second)
- fail << "multiple global overrides of variable " << var.name;
+ {
+ if (c == '!')
+ fail << "multiple global overrides of variable " << n;
+ else
+ fail << "multiple overrides of variable " << n
+ << " in scope " << dir;
+ }
value& v (p.first);
v = move (r.first);
diff --git a/tests/variable/override/test.sh b/tests/variable/override/test.sh
index a195766..63a792f 100755
--- a/tests/variable/override/test.sh
+++ b/tests/variable/override/test.sh
@@ -39,7 +39,7 @@ fail "%foo=bar" "%foo=BAR" # error: multiple project overrides of variable foo
test --buildfile simple foo=bar ./ ./ <<< "bar" # Multiple bootstraps of the same project.
-# Visibility.
+# Visibility/qualification.
#
test !v=X <<EOF
/ : X
@@ -61,7 +61,37 @@ p/d : X
p/d/t : X
EOF
-test v=X p_a=assing <<EOF
+test ./:v=X <<EOF
+/ :
+. : X
+d : X
+d/t : X
+p : X
+p/d : X
+p/d/t : X
+EOF
+
+test ./p/:v=X <<EOF
+/ :
+. :
+d :
+d/t :
+p : X
+p/d : X
+p/d/t : X
+EOF
+
+test /v=X <<EOF
+/ :
+. : X
+d : X
+d/t : X
+p : X
+p/d : X
+p/d/t : X
+EOF
+
+test v=X p_a=as <<EOF
/ :
. : X
d : X
@@ -91,6 +121,16 @@ p/d : x
p/d/t : x
EOF
+test /v=X d_a=as p_d_a=as <<EOF
+/ :
+. : X
+d : x
+d/t : x
+p : X
+p/d : x
+p/d/t : x
+EOF
+
test %v+=S %v=+P a=as <<EOF
/ :
. : P x S