diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2018-11-09 11:45:02 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2018-11-09 11:45:02 +0200 |
commit | accbdae9b6e985d663d8af57375c7861ecd755a1 (patch) | |
tree | 6e6016a1a6ebdaccf2ef67b29c4236aa23914573 | |
parent | 6158e136e105aae2f032a159ce807db68abee281 (diff) |
Add support for relative to base scope command line variable overrides
Currently, if we say:
$ b dir/ ./foo=bar
The scope the foo=bar is set on is relative to CWD, not dir/. While this
may seem wrong at first, this is the least surprising behavior when we
take into account that there can be multiple dir/'s.
Sometimes, however, we do want the override directory to be treated relative
to (every) target's base scope that we are building. To support this we are
extending the '.' and '..' special directory names (which are still resolved
relative to CWD) with '...', which means "relative to the base scope of every
target in the buildspec". For example:
$ b dir/ .../foo=bar
Is equivalent to:
$ b dir/ dir/foo=bar
And:
$ b liba/ libb/ .../tests/foo=bar
Is equivalent to:
$ b liba/ libb/ liba/tests/foo=bar libb/tests/foo=bar
-rw-r--r-- | build2/b.cxx | 81 | ||||
-rw-r--r-- | build2/context.cxx | 36 | ||||
-rw-r--r-- | build2/file.cxx | 4 | ||||
-rw-r--r-- | build2/operation.cxx | 4 | ||||
-rw-r--r-- | build2/scope.hxx | 3 | ||||
-rw-r--r-- | build2/variable.hxx | 7 | ||||
-rwxr-xr-x | old-tests/variable/override/test.sh | 30 |
7 files changed, 113 insertions, 52 deletions
diff --git a/build2/b.cxx b/build2/b.cxx index aedda7f..c1bf07d 100644 --- a/build2/b.cxx +++ b/build2/b.cxx @@ -1192,50 +1192,67 @@ main (int argc, char* argv[]) // // This is further complicated by the project vs amalgamation logic // (we may have already done the amalgamation but not the project). + // So we split it into two passes. // - bool first_a (true); - for (const variable_override& o: var_ovs) { - if (o.ovr.visibility != variable_visibility::normal) - continue; - - auto p (rs.weak_scope ()->vars.insert (o.ovr)); + auto& sm (scope_map::instance); - if (!p.second) + bool first_a (true); + for (const variable_override& o: var_ovs) { - if (first_a) - break; + if (o.ovr.visibility != variable_visibility::normal) + continue; - fail << "multiple amalgamation overrides of variable " - << o.var.name; - } + // If we have a directory, enter the scope, similar to how we do + // it in the context's reset(). + // + scope& s (o.dir + ? sm.insert ((out_base / *o.dir).normalize ())->second + : *rs.weak_scope ()); - value& v (p.first); - v = o.val; - first_a = false; - } + auto p (s.vars.insert (o.ovr)); - bool first_p (true); - for (const variable_override& o: var_ovs) - { - // Ours is either project (%foo) or scope (/foo). - // - if (o.ovr.visibility == variable_visibility::normal) - continue; + if (!p.second) + { + if (first_a) + break; + + fail << "multiple " << (o.dir ? "scope" : "amalgamation") + << " overrides of variable " << o.var.name; + } - auto p (rs.vars.insert (o.ovr)); + value& v (p.first); + v = o.val; + first_a = false; + } - if (!p.second) + bool first_p (true); + for (const variable_override& o: var_ovs) { - if (first_p) - break; + // Ours is either project (%foo) or scope (/foo). + // + if (o.ovr.visibility == variable_visibility::normal) + continue; - fail << "multiple project overrides of variable " << o.var.name; - } + scope& s (o.dir + ? sm.insert ((out_base / *o.dir).normalize ())->second + : rs); + + auto p (s.vars.insert (o.ovr)); - value& v (p.first); - v = o.val; - first_p = false; + if (!p.second) + { + if (first_p) + break; + + fail << "multiple " << (o.dir ? "scope" : "project") + << " overrides of variable " << o.var.name; + } + + value& v (p.first); + v = o.val; + first_p = false; + } } ts.root_scope = &rs; diff --git a/build2/context.cxx b/build2/context.cxx index 5741489..10995e6 100644 --- a/build2/context.cxx +++ b/build2/context.cxx @@ -412,7 +412,7 @@ namespace build2 // auto make_global_scope = [] () -> scope& { - auto i (scope_map::instance.insert (dir_path (), false)); + auto i (scope_map::instance.insert (dir_path ())); scope& r (i->second); r.out_path_ = &i->first; global_scope = scope::global_ = &r; @@ -602,7 +602,7 @@ namespace build2 // token t (l.next ()); - dir_path dir; + optional<dir_path> dir; if (t.type == token_type::word) { string& v (t.value); @@ -638,10 +638,20 @@ namespace build2 t.value.erase (0, p + 1); // Erase the separator. } - if (dir.relative ()) - dir.complete (); + if (dir->relative ()) + { + // Handle the special relative to base scope case (.../). + // + auto i (dir->begin ()); + + if (*i == "...") + dir = dir_path (++i, dir->end ()); // Note: can become empty. + else + dir->complete (); // Relative to CWD. + } - dir.normalize (); + if (dir->absolute ()) + dir->normalize (); } } @@ -668,7 +678,7 @@ namespace build2 string n (t.value, c == '!' || c == '%' || c == '/' ? 1 : 0); - if (c == '!' && !dir.empty ()) + if (c == '!' && dir) fail << "scope-qualified global override of variable " << n; variable_visibility v (c == '/' ? variable_visibility::scope : @@ -713,12 +723,13 @@ namespace build2 if (r.first.type != nullptr) fail << "typed override of variable " << n; - // Global and scope overrides we can enter directly. Project ones will - // be entered by the caller for each amalgamation/project. + // Global and absolute scope overrides we can enter directly. Project + // and relative scope ones will be entered by the caller for each + // amalgamation/project. // - if (c == '!' || !dir.empty ()) + if (c == '!' || (dir && dir->absolute ())) { - scope& s (c == '!' ? gs : sm.insert (dir, false)->second); + scope& s (c == '!' ? gs : sm.insert (*dir)->second); auto p (s.vars.insert (*o)); if (!p.second) @@ -727,14 +738,15 @@ namespace build2 fail << "multiple global overrides of variable " << n; else fail << "multiple overrides of variable " << n - << " in scope " << dir; + << " in scope " << *dir; } value& v (p.first); v = move (r.first); } else - vos.emplace_back (variable_override {var, *o, move (r.first)}); + vos.push_back ( + variable_override {var, *o, move (dir), move (r.first)}); } // Enter builtin variables and patterns. diff --git a/build2/file.cxx b/build2/file.cxx index 05527f3..3f0e7b7 100644 --- a/build2/file.cxx +++ b/build2/file.cxx @@ -221,7 +221,7 @@ namespace build2 scope_map::iterator create_root (scope& l, const dir_path& out_root, const dir_path& src_root) { - auto i (scopes.rw (l).insert (out_root, true)); + auto i (scopes.rw (l).insert (out_root, true /* root */)); scope& rs (i->second); // Set out_path. Note that src_path is set in setup_root() below. @@ -348,7 +348,7 @@ namespace build2 // First, enter the scope into the map and see if it is in any project. If // it is not, then there is nothing else to do. // - auto i (scopes.rw (root).insert (p, false)); + auto i (scopes.rw (root).insert (p)); scope& base (i->second); scope* rs (base.root_scope ()); diff --git a/build2/operation.cxx b/build2/operation.cxx index 229ea4e..aa92756 100644 --- a/build2/operation.cxx +++ b/build2/operation.cxx @@ -81,7 +81,7 @@ namespace build2 // Create the base scope. Note that its existence doesn't mean it was // already setup as a base scope; it can be the same as root. // - auto i (scopes.rw (root).insert (out_base, false)); + auto i (scopes.rw (root).insert (out_base)); scope& base (setup_base (i, out_base, src_base)); // Load the buildfile unless it is implied. @@ -456,7 +456,7 @@ namespace build2 if (rs.out_path () != out_base || rs.src_path () != src_base) fail (l) << "meta-operation info target must be project root directory"; - setup_base (scopes.rw (rs).insert (out_base, false), out_base, src_base); + setup_base (scopes.rw (rs).insert (out_base), out_base, src_base); } void diff --git a/build2/scope.hxx b/build2/scope.hxx index 7929bce..a0c96f0 100644 --- a/build2/scope.hxx +++ b/build2/scope.hxx @@ -352,7 +352,7 @@ namespace build2 // global scope with empty key. // iterator - insert (const dir_path&, bool root); + insert (const dir_path&, bool root = false); // Find the most qualified scope that encompasses this path. // @@ -396,6 +396,7 @@ namespace build2 // Entities that can access bypassing the lock proof. // + friend int main (int, char*[]); friend variable_overrides reset (const strings&); scope& diff --git a/build2/variable.hxx b/build2/variable.hxx index 7dc3d1a..246fdf3 100644 --- a/build2/variable.hxx +++ b/build2/variable.hxx @@ -927,9 +927,10 @@ namespace build2 // struct variable_override { - const variable& var; // Original variable. - const variable& ovr; // Override variable. - value val; + const variable& var; // Original variable. + const variable& ovr; // Override variable. + optional<dir_path> dir; // Scope directory relative to base. + value val; }; using variable_overrides = vector<variable_override>; diff --git a/old-tests/variable/override/test.sh b/old-tests/variable/override/test.sh index 76409d8..baef1ca 100755 --- a/old-tests/variable/override/test.sh +++ b/old-tests/variable/override/test.sh @@ -93,6 +93,16 @@ 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 ./p/v=X <<EOF / : . : @@ -103,6 +113,16 @@ 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 --buildfile loader ./p/ <<EOF / : . : X @@ -113,6 +133,16 @@ p/d : X p/d/t : X EOF +test .../v=X --buildfile loader ./p/ <<EOF +/ : +. : +d : +d/t : +p : X +p/d : X +p/d/t : X +EOF + test /v=X <<EOF / : . : X |