aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2016-04-01 09:49:18 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2016-04-01 09:49:18 +0200
commit26146d391f179dd9e4e5e1f70a52ba99d6a0847d (patch)
treec79762ff1fd15bfa25fdeacf9f9e6efe338354bf
parent7253ffee27f6cae34e63a72b2d3d10db10571ecc (diff)
Get part of variable override implementation
-rw-r--r--build2/b.cxx7
-rw-r--r--build2/context.cxx17
-rw-r--r--build2/scope21
-rw-r--r--build2/scope.cxx214
-rw-r--r--build2/target.cxx33
-rw-r--r--build2/variable17
-rw-r--r--build2/variable.cxx4
7 files changed, 276 insertions, 37 deletions
diff --git a/build2/b.cxx b/build2/b.cxx
index 219f4eb..44fa032 100644
--- a/build2/b.cxx
+++ b/build2/b.cxx
@@ -820,13 +820,6 @@ main (int argc, char* argv[])
value& v (p.first);
v.assign (names (o.val), o.var); // Original var for diagnostics.
-
- // Also make sure the original variable itself is set (to at least
- // NULL) so that lookup finds something if nobody actually sets it
- // down the line.
- //
- rs.vars.assign (o.var);
-
first = false;
}
diff --git a/build2/context.cxx b/build2/context.cxx
index dbfffdb..21d683b 100644
--- a/build2/context.cxx
+++ b/build2/context.cxx
@@ -47,6 +47,8 @@ namespace build2
variable_overrides vos;
+ variable_override_cache.clear ();
+
targets.clear ();
scopes.clear ();
var_pool.clear ();
@@ -108,13 +110,6 @@ namespace build2
const variable& var (var_pool.find (t.value));
const string& n (var.name);
- // The first variable in the override list is always the cache. Note
- // that we might already be overridden by an earlier cmd line var.
- //
- if (var.override == nullptr)
- var.override.reset (new variable {
- n + ".__cache", nullptr, nullptr, variable_visibility::normal});
-
// Calculate visibility and kind.
//
variable_visibility v (c == '%'
@@ -125,7 +120,7 @@ namespace build2
// We might already have a variable for this kind of override.
//
- const variable* o (var.override.get ());
+ const variable* o (&var); // Step behind.
for (; o->override != nullptr; o = o->override.get ())
{
if (o->override->visibility == v &&
@@ -159,12 +154,6 @@ namespace build2
value& v (p.first);
v.assign (move (val), var); // Original var for diagnostics.
-
- // Also make sure the original variable itself is set (to at least
- // NULL) so that lookup finds something if nobody actually sets it
- // down the line.
- //
- gs.vars.assign (var);
}
else
vos.emplace_back (variable_override {var, *o, move (val)});
diff --git a/build2/scope b/build2/scope
index 6050bf8..b4c4e96 100644
--- a/build2/scope
+++ b/build2/scope
@@ -128,9 +128,24 @@ namespace build2
}
lookup
- find (const variable&,
- const target_type* tt, const string* tn,
- const target_type* gt = nullptr, const string* gn = nullptr) const;
+ find (const variable& var, const target_type* tt, const string* tn) const
+ {
+ lookup l (find_original (var, tt, tn));
+ return var.override == nullptr
+ ? l
+ : find_override (var, move (l), false);
+ }
+
+ // Implementation details (used by target lookup).
+ //
+ lookup
+ find_original (
+ const variable&,
+ const target_type* tt, const string* tn,
+ const target_type* gt = nullptr, const string* gn = nullptr) const;
+
+ lookup
+ find_override (const variable&, lookup&& original, bool orig_tspec) const;
// Return a value suitable for assignment (or append if you only
// want to append to the value from this scope). If the variable
diff --git a/build2/scope.cxx b/build2/scope.cxx
index 77efccd..5b1e3fd 100644
--- a/build2/scope.cxx
+++ b/build2/scope.cxx
@@ -13,9 +13,9 @@ namespace build2
// scope
//
lookup scope::
- find (const variable& var,
- const target_type* tt, const string* tn,
- const target_type* gt, const string* gn) const
+ find_original (const variable& var,
+ const target_type* tt, const string* tn,
+ const target_type* gt, const string* gn) const
{
for (const scope* s (this); s != nullptr; )
{
@@ -54,6 +54,214 @@ namespace build2
return lookup ();
}
+ lookup scope::
+ find_override (const variable& var, lookup&& orig, bool tspec) const
+ {
+ // Normally there would be no overrides and if there are, there will only
+ // be a few of them. As a result, here we concentrate on keeping the logic
+ // as straightforward as possible without trying to optimize anything.
+ //
+ assert (var.override != nullptr);
+
+ // The first step is to find out where our cache will reside. After some
+ // meditation it becomes clear it should be next to the innermost (scope-
+ // wise) value (override or original) that contributes to the end result.
+ //
+ // One special case is if the original is target-specific, which is the
+ // most innermost (or is it inermostest).
+ //
+ const variable_map* vars (tspec ? orig.vars : nullptr);
+
+ const scope* s;
+
+ // Return override value if it is present, applies, and ends with suffix.
+ //
+ auto find = [&vars, &s, this] (const variable* o, const char* sf = nullptr)
+ -> lookup
+ {
+ if (s != nullptr && o->name.rfind (sf) == string::npos)
+ return lookup ();
+
+ // Next see if it would apply. If there is nothing "inner", then any
+ // override will still be "visible".
+ //
+ if (vars != nullptr)
+ {
+ switch (o->visibility)
+ {
+ case variable_visibility::scope:
+ {
+ // Does not apply if the innermost value is not in this scope.
+ //
+ if (vars != &s->vars)
+ return lookup ();
+
+ break;
+ }
+ case variable_visibility::project:
+ {
+ // Does not apply if in a different project.
+ //
+ if (root_scope () != s->root_scope ())
+ return lookup ();
+
+ break;
+ }
+ case variable_visibility::normal:
+ break;
+ }
+ }
+
+ return lookup (s->vars.find (*o), &s->vars);
+ };
+
+ // Return true if a value is from this scope (either target-specific or
+ // normal).
+ //
+ auto test = [&s] (const lookup& l) -> bool
+ {
+ for (auto& p1: s->target_vars)
+ for (auto& p2: p1.second)
+ if (l.vars == &p2.second)
+ return true;
+
+ return l.vars == &s->vars;
+ };
+
+ // While looking for the cache we can also detect if none of the overrides
+ // apply. In this case the result is simply the original value (if any).
+ //
+ bool apply (false);
+
+ for (s = this; s != nullptr; s = s->parent_scope ())
+ {
+ // If we are still looking for the cache, see if the original comes from
+ // this scope. We check this before the overrides since it can come from
+ // the target type/patter-specific variables, which is "more inner" than
+ // normal scope variables (see find_original()).
+ //
+ if (vars == nullptr && orig && test (orig))
+ vars = orig.vars;
+
+ for (const variable* o (var.override.get ());
+ o != nullptr;
+ o = o->override.get ())
+ {
+ if (auto l = find (o))
+ {
+ if (vars == nullptr)
+ vars = l.vars;
+
+ apply = true;
+ break;
+ }
+ }
+
+ // If we found the cache and at least one override applies, then we can
+ // stop.
+ //
+ if (vars != nullptr && apply)
+ break;
+ }
+
+ if (!apply)
+ return move (orig);
+
+ assert (vars != nullptr);
+
+ // Implementing proper caching is tricky so for now we are going to re-
+ // calculate the value every time. Later, the plan is to use value
+ // versioning (incremented on every update) to detect stem value changes.
+ // We also need to watch out for the change of the stem itself in addition
+ // to its value (think of a new variable set since last lookup which is
+ // now a new stem).
+ //
+ // @@ MT
+ //
+ variable_override_value& cache (variable_override_cache[vars]);
+
+ // Now find our "stem", that is the value to which we will be appending
+ // suffixes and prepending prefixes. This is either the original or the
+ // __override, depending on which one is the innermost. We may also not
+ // have one at all.
+ //
+ lookup stem (tspec ? orig : lookup ());
+
+ for (s = this; s != nullptr; s = s->parent_scope ())
+ {
+ // First check if the original is from this scope.
+ //
+ if (orig && test (orig))
+ {
+ stem = orig;
+ break;
+ }
+
+ // Then look for an __override that applies.
+ //
+ for (const variable* o (var.override.get ());
+ o != nullptr;
+ o = o->override.get ())
+ {
+ if ((stem = find (o, ".__override")))
+ break;
+ }
+
+ if (stem)
+ break;
+ }
+
+ // If there is a stem, set it as the initial value of the cache.
+ // Otherwise, start with a NULL value.
+ //
+ if (stem)
+ {
+ cache.value = *stem;
+ cache.stem_vars = stem.vars;
+ }
+ else
+ {
+ cache.value = nullptr;
+ cache.stem_vars = nullptr; // No stem.
+ }
+
+ // Typify the cache value. If the stem is the original, then the type
+ // would get propagated automatically. But the stem could also be the
+ // override, which is kept untyped. Or the stem might not be there at all
+ // while we still need to apply prefixes/suffixes in the type-aware way.
+ //
+ if (cache.value.type != var.type)
+ typify (cache.value, *var.type, var);
+
+ // Now apply override prefixes and suffixes.
+ //
+ for (s = this; s != nullptr; s = s->parent_scope ())
+ {
+ for (const variable* o (var.override.get ());
+ o != nullptr;
+ o = o->override.get ())
+ {
+ // Note that we keep override values as untyped names even if the
+ // variable itself is typed. We also pass the original variable for
+ // diagnostics.
+ //
+ if (auto l = find (o, ".__prefix"))
+ {
+ cache.value.prepend (names (cast<names> (l)), var);
+ }
+ else if (auto l = find (o, ".__suffix"))
+ {
+ cache.value.append (names (cast<names> (l)), var);
+ }
+ }
+ }
+
+ // Use the location of the cache (innermost value that contributes) as
+ // the location of the result.
+ //
+ return lookup (&cache.value, vars);
+ }
+
value& scope::
append (const variable& var)
{
diff --git a/build2/target.cxx b/build2/target.cxx
index e945d6d..f1f0637 100644
--- a/build2/target.cxx
+++ b/build2/target.cxx
@@ -117,23 +117,38 @@ namespace build2
lookup target::
operator[] (const variable& var) const
{
+ lookup l;
+ bool tspec (false);
+
+ scope& s (base_scope ());
+
if (auto p = vars.find (var))
- return lookup (p, &vars);
+ {
+ tspec = true;
+ l = lookup (p, &vars);
+ }
- if (group != nullptr)
+ if (!l && group != nullptr)
{
if (auto p = group->vars.find (var))
- return lookup (p, &group->vars);
+ {
+ tspec = true;
+ l = lookup (p, &group->vars);
+ }
}
// Delegate to scope's find().
//
- return base_scope ().find (
- var,
- &type (),
- &name,
- group != nullptr ? &group->type () : nullptr,
- group != nullptr ? &group->name : nullptr);
+ if (!l)
+ l = s.find_original (var,
+ &type (),
+ &name,
+ group != nullptr ? &group->type () : nullptr,
+ group != nullptr ? &group->name : nullptr);
+
+ return var.override == nullptr
+ ? l
+ : s.find_override (var, move (l), tspec);
}
value& target::
diff --git a/build2/variable b/build2/variable
index 15f9c47..d22d6da 100644
--- a/build2/variable
+++ b/build2/variable
@@ -79,6 +79,10 @@ namespace build2
//
// @@ Document override semantics.
//
+ // Note that we don't propagate variable type to override variables and we
+ // keep override values as untyped names. So they get "typed" when they are
+ // applied.
+ //
struct variable
{
string name;
@@ -111,7 +115,7 @@ namespace build2
// Creation. A newly created value is NULL and can be reset back to NULL
// by assigning nullptr. Values can be copied and copy-assigned. Note that
- // for assignment, the values' types should be the same of LHS should be
+ // for assignment, the values' types should be the same or LHS should be
// untyped.
//
//
@@ -672,6 +676,17 @@ namespace build2
lookup
find (const target_type&, const string& tname, const variable&) const;
};
+
+ // Override cache.
+ //
+ struct variable_override_value
+ {
+ build2::value value;
+ const variable_map* stem_vars = nullptr; // NULL means there is no stem.
+ };
+
+ extern std::map<const variable_map*,
+ variable_override_value> variable_override_cache;
}
#include <build2/variable.ixx>
diff --git a/build2/variable.cxx b/build2/variable.cxx
index 0870000..2c2dbc2 100644
--- a/build2/variable.cxx
+++ b/build2/variable.cxx
@@ -703,4 +703,8 @@ namespace build2
return lookup ();
}
+
+ // variable_override
+ //
+ map<const variable_map*, variable_override_value> variable_override_cache;
}