aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build2/scope.cxx160
1 files changed, 91 insertions, 69 deletions
diff --git a/build2/scope.cxx b/build2/scope.cxx
index f0d28e0..b22cb0c 100644
--- a/build2/scope.cxx
+++ b/build2/scope.cxx
@@ -76,24 +76,23 @@ namespace build2
//
// Note also that we rely (e.g., in the config module) on the fact that if
// no overrides apply, then we return the original value and not its copy
- // in the cache (this can be used to detect if the value was overriden).
- //
+ // in the cache (this is used to detect if the value was overriden).
//
assert (var.override != nullptr);
- lookup& origl (original.first);
- size_t origd (original.second);
+ const lookup& orig (original.first);
+ size_t orig_depth (original.second);
// 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 could contribute to the end
- // result.
+ // meditation you will see it should be next to the innermost (scope-wise)
+ // value of this variable (override or original).
//
- const variable_map* vars (nullptr);
-
- // Root scope of a project from which our initial value comes. See below.
+ // We also keep track of the root scope of the project from which this
+ // innermost value comes. This is used to decide whether a non-recursive
+ // project-wise override applies.
//
- const scope* proj (nullptr);
+ const variable_map* inner_vars (nullptr);
+ const scope* inner_proj (nullptr);
// One special case is if the original is target-specific, which is the
// most innermost. Or is it innermostest?
@@ -101,28 +100,30 @@ namespace build2
bool targetspec (false);
if (target)
{
- targetspec = origl.defined () && (origd == 1 || origd == 2);
+ targetspec = orig.defined () && (orig_depth == 1 || orig_depth == 2);
if (targetspec)
{
- vars = origl.vars;
- proj = root_scope ();
+ inner_vars = orig.vars;
+ inner_proj = root_scope ();
}
}
const scope* s;
// Return true if the override applies. Note that it expects vars and proj
- // to be not NULL; if there is nothing "inner", then any override will
- // still be "visible".
+ // to be not NULL; if there is nothing "more inner", then any override
+ // will still be "visible".
//
- auto applies = [&vars, &s] (const variable* o, const scope* proj) -> bool
+ auto applies = [&s] (const variable* o,
+ const variable_map* vars,
+ const scope* proj) -> bool
{
switch (o->visibility)
{
case variable_visibility::scope:
{
- // Does not apply if the innermost value is not in this scope.
+ // Does not apply if in a different scope.
//
if (vars != &s->vars)
return false;
@@ -145,7 +146,8 @@ namespace build2
return true;
};
- // Return override value if it is present and optionally ends with suffix.
+ // Return the override value if it is present and (optionally) ends with
+ // a suffix.
//
auto find = [&s] (const variable* o, const char* sf = nullptr) -> lookup
{
@@ -158,17 +160,20 @@ namespace build2
// Return true if a value is from this scope (either target type/pattern-
// specific or ordinary).
//
- auto belongs = [&s] (const lookup& l) -> bool
+ auto belongs = [&s, target] (const lookup& l) -> bool
{
- for (auto& p1: s->target_vars)
- for (auto& p2: p1.second)
- if (l.vars == &p2.second)
- return true;
+ if (target)
+ {
+ 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
+ // While looking for the cache we also detect if none of the overrides
// apply. In this case the result is simply the original value (if any).
//
bool apply (false);
@@ -180,43 +185,44 @@ namespace build2
// the target type/patter-specific variables, which is "more inner" than
// normal scope variables (see find_original()).
//
- if (vars == nullptr && origl.defined () && belongs (origl))
+ if (inner_vars == nullptr && orig.defined () && belongs (orig))
{
- vars = origl.vars;
- proj = s->root_scope (); // This is so we skip non-recursive overrides
- // that would not apply. We reset it later.
+ inner_vars = orig.vars;
+ inner_proj = s->root_scope ();
}
for (const variable* o (var.override.get ());
o != nullptr;
o = o->override.get ())
{
- if (vars != 0 && !applies (o, proj))
+ if (inner_vars != nullptr && !applies (o, inner_vars, inner_proj))
continue;
auto l (find (o));
if (l.defined ())
{
- if (vars == nullptr)
- vars = l.vars;
+ if (inner_vars == nullptr)
+ {
+ inner_vars = l.vars;
+ inner_proj = s->root_scope (); // Not actually used.
+ }
apply = true;
break;
}
}
- // If we found the cache and at least one override applies, then we can
- // stop.
+ // We can stop if we found the cache and at least one override applies.
//
- if (vars != nullptr && apply)
+ if (inner_vars != nullptr && apply)
break;
}
if (!apply)
return move (original);
- assert (vars != nullptr);
+ assert (inner_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
@@ -228,15 +234,26 @@ namespace build2
// @@ MT
//
variable_override_value& cache (
- variable_override_cache[make_pair (vars, &var)]);
+ variable_override_cache[make_pair (inner_vars, &var)]);
// 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 provided it applies. We may also not have either.
+ // __override, provided it applies. We may also not have either.
//
- lookup stem (targetspec ? origl : lookup ());
- size_t depth (targetspec ? origd : 0);
- size_t ovrd (target ? 2 : 0); // For implied target-specific lookup.
+ lookup stem;
+ size_t stem_depth (0);
+ const scope* stem_proj (nullptr);
+
+ // Again the special case of a target-specific variable.
+ //
+ if (targetspec)
+ {
+ stem = orig;
+ stem_depth = orig_depth;
+ stem_proj = root_scope ();
+ }
+
+ size_t ovr_depth (target ? 2 : 0); // For implied target-specific lookup.
for (s = this; s != nullptr; s = s->parent_scope ())
{
@@ -244,15 +261,15 @@ namespace build2
// First check if the original is from this scope.
//
- if (origl.defined () && belongs (origl))
+ if (orig.defined () && belongs (orig))
{
- stem = origl;
- depth = origd;
- proj = s->root_scope ();
+ stem = orig;
+ stem_depth = orig_depth;
+ stem_proj = s->root_scope ();
// Keep searching.
}
- ++ovrd;
+ ++ovr_depth;
// Then look for an __override that applies.
//
@@ -263,16 +280,16 @@ namespace build2
// If we haven't yet found anything, then any override will still be
// "visible" even if it doesn't apply.
//
- if (stem.defined () && !applies (o, root_scope ()))
+ if (stem.defined () && !applies (o, stem.vars, stem_proj))
continue;
auto l (find (o, ".__override"));
if (l.defined ())
{
- depth = ovrd;
stem = move (l);
- proj = s->root_scope ();
+ stem_depth = ovr_depth;
+ stem_proj = s->root_scope ();
done = true;
break;
}
@@ -311,26 +328,30 @@ namespace build2
if (cache.value.type == nullptr && var.type != nullptr)
typify (cache.value, *var.type, var);
- // Now apply override prefixes and suffixes.
+ // Now apply override prefixes and suffixes. Also calculate the vars and
+ // depth of the result, which will be those of the stem or prefix/suffix
+ // that applies, whichever is the innermost.
//
- ovrd = target ? 2 : 0;
- const variable_map* ovrv (cache.stem_vars);
+ size_t depth (stem_depth);
+ const variable_map* vars (stem.vars);
+ const scope* proj (stem_proj);
+
+ ovr_depth = target ? 2 : 0;
for (s = this; s != nullptr; s = s->parent_scope ())
{
- ++ovrd;
+ ++ovr_depth;
for (const variable* o (var.override.get ());
o != nullptr;
o = o->override.get ())
{
- // First see if this override applies. This is actually tricky: what
- // if the stem is a "visible" override from an outer project?
- // Shouldn't its overrides apply? Sure sounds logical. So it seems
- // we should use the project of the stem's scope and not the project
- // of this scope.
+ // First see if this override applies. This is tricky: what if the
+ // stem is a "visible" override from an outer project? Shouldn't its
+ // overrides apply? Sure sounds logical. So we use the project of the
+ // stem's scope.
//
- if (proj != nullptr && !applies (o, proj))
+ if (vars != nullptr && !applies (o, vars, proj))
continue;
// Note that we keep override values as untyped names even if the
@@ -350,20 +371,21 @@ namespace build2
if (l.defined ())
{
- // If we had no stem, use the scope of the first override that
- // applies as the project. For vars/depth we need to pick the
- // innermost.
+ // If we had no stem, use the first override as a surrogate stem.
//
- if (proj == nullptr)
+ if (vars == nullptr)
{
+ depth = ovr_depth;
+ vars = &s->vars;
proj = s->root_scope ();
- depth = ovrd;
- ovrv = &s->vars;
}
- else if (ovrd < depth)
+ // Otherwise, pick the innermost location between the stem and
+ // prefix/suffix.
+ //
+ else if (ovr_depth < depth)
{
- depth = ovrd;
- ovrv = &s->vars;
+ depth = ovr_depth;
+ vars = &s->vars;
}
}
}
@@ -372,7 +394,7 @@ namespace build2
// Use the location of the innermost value that contributed as the
// location of the result.
//
- return make_pair (lookup (&cache.value, ovrv), depth);
+ return make_pair (lookup (&cache.value, vars), depth);
}
value& scope::