aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2018-01-04 15:35:39 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2018-01-05 15:10:01 +0200
commitd262f63ce5a7c3810abde1f66ee3bb99d56acdd0 (patch)
tree7a7114ceb95d17ebadc56f983309fb6ac5864a39
parent759e714238438cccf74f035e1ba11c925b27cd55 (diff)
Add support for variable aliases
-rw-r--r--build2/config/utility.cxx2
-rw-r--r--build2/config/utility.txx2
-rw-r--r--build2/context.cxx2
-rw-r--r--build2/dump.cxx2
-rw-r--r--build2/file.cxx2
-rw-r--r--build2/prerequisite.cxx2
-rw-r--r--build2/scope.cxx18
-rw-r--r--build2/target.cxx12
-rw-r--r--build2/test/script/script.cxx9
-rw-r--r--build2/variable.cxx107
-rw-r--r--build2/variable.hxx73
11 files changed, 168 insertions, 63 deletions
diff --git a/build2/config/utility.cxx b/build2/config/utility.cxx
index 1c80d13..08795d7 100644
--- a/build2/config/utility.cxx
+++ b/build2/config/utility.cxx
@@ -61,7 +61,7 @@ namespace build2
auto l (r[var]);
return l.defined ()
? l
- : lookup (r.assign (var), r); // NULL.
+ : lookup (r.assign (var), var, r); // NULL.
}
bool
diff --git a/build2/config/utility.txx b/build2/config/utility.txx
index fc21710..4336955 100644
--- a/build2/config/utility.txx
+++ b/build2/config/utility.txx
@@ -39,7 +39,7 @@ namespace build2
v.extra = true; // Default value flag.
n = (save_flags & save_commented) == 0; // Absence means default.
- l = lookup (v, root);
+ l = lookup (v, var, root);
org = make_pair (l, 1); // Lookup depth is 1 since it's in root.vars.
}
// Treat an inherited value that was set to default as new.
diff --git a/build2/context.cxx b/build2/context.cxx
index 73717be..3bb07a8 100644
--- a/build2/context.cxx
+++ b/build2/context.cxx
@@ -605,7 +605,7 @@ namespace build2
//
if (o->override == nullptr)
const_cast<variable*> (o)->override.reset (
- new variable {n + k, nullptr, nullptr, v});
+ new variable {n + k, nullptr , nullptr, nullptr, v});
o = o->override.get ();
diff --git a/build2/dump.cxx b/build2/dump.cxx
index 3f5aced..f88dcfd 100644
--- a/build2/dump.cxx
+++ b/build2/dump.cxx
@@ -101,7 +101,7 @@ namespace build2
var.name.rfind (".__suffix") == string::npos &&
var.name.rfind (".__prefix") == string::npos)
{
- lookup org (v, vm);
+ lookup org (v, var, vm);
// The original is always from this scope/target, so depth is 1.
//
diff --git a/build2/file.cxx b/build2/file.cxx
index e18ae5e..5ffa59a 100644
--- a/build2/file.cxx
+++ b/build2/file.cxx
@@ -376,7 +376,7 @@ namespace build2
temp_scope tmp (s.global ());
p.parse_variable (lex, tmp, var, tt);
- value* v (tmp.vars.find_to_modify (var));
+ value* v (tmp.vars.find_to_modify (var).first);
assert (v != nullptr);
// Steal the value, the scope is going away.
diff --git a/build2/prerequisite.cxx b/build2/prerequisite.cxx
index b8c1791..7a74f76 100644
--- a/build2/prerequisite.cxx
+++ b/build2/prerequisite.cxx
@@ -80,7 +80,7 @@ namespace build2
value& prerequisite::
append (const variable& var, const target_type& t)
{
- if (value* r = vars.find_to_modify (var))
+ if (value* r = vars.find_to_modify (var).first)
return *r;
value& r (assign (var)); // NULL.
diff --git a/build2/scope.cxx b/build2/scope.cxx
index f10215e..01a60a0 100644
--- a/build2/scope.cxx
+++ b/build2/scope.cxx
@@ -93,8 +93,8 @@ namespace build2
}
}
- // Return cache as the resulting value but retain l.vars, so it looks as
- // if the value came from s->target_vars.
+ // Return cache as the resulting value but retain l.var/vars, so it
+ // looks as if the value came from s->target_vars.
//
l.value = &cv;
};
@@ -147,8 +147,9 @@ namespace build2
//
if (++d >= start_d && var.visibility != variable_visibility::target)
{
- if (const value* v = s->vars.find (var))
- return make_pair (lookup (v, &s->vars), d);
+ auto p (s->vars.find (var));
+ if (p.first != nullptr)
+ return make_pair (lookup (*p.first, p.second, s->vars), d);
}
switch (var.visibility)
@@ -255,12 +256,15 @@ namespace build2
// 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
+ auto find = [&s, &var] (const variable* o,
+ const char* sf = nullptr) -> lookup
{
if (sf != nullptr && o->name.rfind (sf) == string::npos)
return lookup ();
- return lookup (s->vars.find (*o), &s->vars);
+ // Note: using the original as storage variable.
+ //
+ return lookup (s->vars.find (*o).first, &var, &s->vars);
};
// Return true if a value is from this scope (either target type/pattern-
@@ -513,7 +517,7 @@ namespace build2
// Use the location of the innermost value that contributed as the
// location of the result.
//
- return make_pair (lookup (&cv, vars), depth);
+ return make_pair (lookup (&cv, &var, vars), depth);
}
value& scope::
diff --git a/build2/target.cxx b/build2/target.cxx
index 90b2511..a5316ce 100644
--- a/build2/target.cxx
+++ b/build2/target.cxx
@@ -200,8 +200,11 @@ namespace build2
pair<lookup, size_t> r (lookup (), 0);
++r.second;
- if (auto p = vars.find (var))
- r.first = lookup (p, &vars);
+ {
+ auto p (vars.find (var));
+ if (p.first != nullptr)
+ r.first = lookup (*p.first, p.second, vars);
+ }
const target* g (nullptr);
@@ -216,8 +219,9 @@ namespace build2
? nullptr
: group->adhoc_group () ? group->group : group))
{
- if (auto p = g->vars.find (var))
- r.first = lookup (p, &g->vars);
+ auto p (g->vars.find (var));
+ if (p.first != nullptr)
+ r.first = lookup (*p.first, p.second, g->vars);
}
}
diff --git a/build2/test/script/script.cxx b/build2/test/script/script.cxx
index 99ace23..51c08cb 100644
--- a/build2/test/script/script.cxx
+++ b/build2/test/script/script.cxx
@@ -624,14 +624,15 @@ namespace build2
{
// Search script scopes until we hit the root.
//
- const scope* p (this);
+ const scope* s (this);
do
{
- if (const value* v = p->vars.find (var))
- return lookup (v, &p->vars);
+ auto p (s->vars.find (var));
+ if (p.first != nullptr)
+ return lookup (*p.first, p.second, s->vars);
}
- while ((p->parent != nullptr ? (p = p->parent) : nullptr) != nullptr);
+ while ((s->parent != nullptr ? (s = s->parent) : nullptr) != nullptr);
return find_in_buildfile (var.name);
}
diff --git a/build2/variable.cxx b/build2/variable.cxx
index d8b48d0..d1f95c5 100644
--- a/build2/variable.cxx
+++ b/build2/variable.cxx
@@ -1039,6 +1039,10 @@ namespace build2
bool ut (t != nullptr && var.type != t);
bool uv (v != nullptr && var.visibility != *v);
+ // Variable should not be updated post-aliasing.
+ //
+ assert (var.alias == &var || (!ut && !uv));
+
// Update type?
//
if (ut)
@@ -1124,26 +1128,30 @@ namespace build2
}
}
- const variable& variable_pool::
+ variable& variable_pool::
insert (string n,
const build2::value_type* t,
const variable_visibility* v,
- const bool* o)
+ const bool* o,
+ bool pat)
{
assert (!global_ || phase == run_phase::load);
// Apply pattern.
//
- if (n.find ('.') != string::npos)
+ if (pat)
{
- // Reverse means from the "largest" (most specific).
- //
- for (const pattern& p: reverse_iterate (patterns_))
+ if (n.find ('.') != string::npos)
{
- if (match_pattern (n, p.prefix, p.suffix, p.multi))
+ // Reverse means from the "largest" (most specific).
+ //
+ for (const pattern& p: reverse_iterate (patterns_))
{
- merge_pattern (p, t, v, o);
- break;
+ if (match_pattern (n, p.prefix, p.suffix, p.multi))
+ {
+ merge_pattern (p, t, v, o);
+ break;
+ }
}
}
}
@@ -1152,13 +1160,16 @@ namespace build2
insert (
variable {
move (n),
+ nullptr,
t,
nullptr,
v != nullptr ? *v : variable_visibility::normal}));
variable& r (p.first->second);
- if (!p.second) // Note: overridden variable will always exist.
+ if (p.second)
+ r.alias = &r;
+ else // Note: overridden variable will always exist.
{
if (t != nullptr || v != nullptr || o != nullptr)
update (r, t, v, o); // Not changing the key.
@@ -1169,6 +1180,28 @@ namespace build2
return r;
}
+ const variable& variable_pool::
+ insert_alias (const variable& var, string n)
+ {
+ assert (var.alias != nullptr && var.override == nullptr);
+
+ variable& a (insert (move (n),
+ var.type,
+ &var.visibility,
+ nullptr /* override */,
+ false /* pattern */));
+
+ if (a.alias == &a) // Not aliased yet.
+ {
+ a.alias = var.alias;
+ const_cast<variable&> (var).alias = &a;
+ }
+ else
+ assert (a.aliases (var)); // Make sure it is already an alias of var.
+
+ return a;
+ }
+
void variable_pool::
insert_pattern (const string& p,
optional<const value_type*> t,
@@ -1246,28 +1279,48 @@ namespace build2
// variable_map
//
auto variable_map::
- find (const variable& var, bool typed) const -> const value_data*
+ find (const variable& var, bool typed) const ->
+ pair<const value_data*, const variable&>
{
- auto i (m_.find (var));
- const value_data* r (i != m_.end () ? &i->second : nullptr);
+ const variable* v (&var);
+ const value_data* r (nullptr);
+ do
+ {
+ // @@ Should we verify that there are no distinct values for aliases?
+ // This can happen if the values were entered before the variables
+ // were aliased. Possible but probably highly unlikely.
+ //
+ auto i (m_.find (*v));
+ if (i != m_.end ())
+ {
+ r = &i->second;
+ break;
+ }
+
+ v = v->alias;
+
+ } while (v != &var && v != nullptr);
// Check if this is the first access after being assigned a type.
//
- if (r != nullptr && typed && var.type != nullptr)
- typify (*r, var);
+ if (r != nullptr && typed && v->type != nullptr)
+ typify (*r, *v);
- return r;
+ return pair<const value_data*, const variable&> (
+ r, r != nullptr ? *v : var);
}
auto variable_map::
- find_to_modify (const variable& var, bool typed) -> value_data*
+ find_to_modify (const variable& var, bool typed) ->
+ pair<value_data*, const variable&>
{
- auto* r (const_cast<value_data*> (find (var, typed)));
+ auto p (find (var, typed));
+ auto* r (const_cast<value_data*> (p.first));
if (r != nullptr)
r->version++;
- return r;
+ return pair<value_data*, const variable&> (r, p.second);
}
pair<reference_wrapper<value>, bool> variable_map::
@@ -1354,15 +1407,17 @@ namespace build2
// ourselves.
//
const variable_map& vm (j->second);
-
- if (const variable_map::value_data* v = vm.find (var, false))
{
- // Check if this is the first access after being assigned a type.
- //
- if (v->extra == 0 && var.type != nullptr)
- vm.typify (*v, var);
+ auto p (vm.find (var, false));
+ if (const variable_map::value_data* v = p.first)
+ {
+ // Check if this is the first access after being assigned a type.
+ //
+ if (v->extra == 0 && var.type != nullptr)
+ vm.typify (*v, var);
- return lookup (*v, vm);
+ return lookup (*v, p.second, vm);
+ }
}
}
}
diff --git a/build2/variable.hxx b/build2/variable.hxx
index 51cc350..61485b5 100644
--- a/build2/variable.hxx
+++ b/build2/variable.hxx
@@ -109,11 +109,15 @@ namespace build2
//
// The two variables are considered the same if they have the same name.
//
+ // Variables can be aliases of each other in which case they form a circular
+ // linked list (alias for variable without any aliases points to the
+ // variable itself).
+ //
// If the variable is overridden on the command line, then override is the
// chain of the special override variables. Their names are derived from the
// main variable name as <name>.{__override,__prefix,__suffix} and they are
// not entered into the var_pool. The override variables only vary in their
- // names and visibility.
+ // names and visibility. Their alias pointer is always NULL.
//
// Note also that we don't propagate the variable type to override variables
// and we keep override values as untyped names. They get "typed" when they
@@ -134,9 +138,20 @@ namespace build2
struct variable
{
string name;
- const value_type* type; // If NULL, then not (yet) typed.
- unique_ptr<variable> override;
+ const variable* alias; // Circular linked list.
+ const value_type* type; // If NULL, then not (yet) typed.
+ unique_ptr<const variable> override;
variable_visibility visibility;
+
+ // Return true if this variable is an alias of the specified variable.
+ //
+ bool
+ aliases (const variable& var) const
+ {
+ const variable* v (alias);
+ for (; v != &var && v != this; v = v->alias) ;
+ return v == &var;
+ }
};
inline bool
@@ -359,8 +374,11 @@ namespace build2
{
using value_type = build2::value;
- const value_type* value; // NULL if undefined.
- const variable_map* vars; // value is variable_map::value_data if not NULL.
+ // If vars is not NULL, then value is variable_map::value_data.
+ //
+ const value_type* value; // NULL if undefined.
+ const variable* var; // Storage variable.
+ const variable_map* vars; // Storage map.
bool
defined () const {return value != nullptr;}
@@ -385,16 +403,19 @@ namespace build2
bool
belongs (const T& x, bool target_type_pattern) const;
- lookup (): value (nullptr), vars (nullptr) {}
+ lookup (): value (nullptr), var (nullptr), vars (nullptr) {}
template <typename T>
- lookup (const value_type& v, const T& x): lookup (&v, &x.vars) {}
+ lookup (const value_type& v, const variable& r, const T& x)
+ : lookup (&v, &r, &x.vars) {}
- lookup (const value_type& v, const variable_map& vm)
- : value (&v), vars (&vm) {}
+ lookup (const value_type& v, const variable& r, const variable_map& m)
+ : lookup (&v, &r, &m) {}
- lookup (const value_type* v, const variable_map* vm)
- : value (v), vars (v != nullptr ? vm : nullptr) {}
+ lookup (const value_type* v, const variable* r, const variable_map* m)
+ : value (v),
+ var (v != nullptr ? r : nullptr),
+ vars (v != nullptr ? m : nullptr) {}
};
// Two lookups are equal if they point to the same variable.
@@ -921,6 +942,23 @@ namespace build2
move (name), &value_traits<T>::value_type, &v, &overridable);
}
+ // Alias an existing variable with a new name.
+ //
+ // Aliasing is purely a lookup-level mechanism. That is, when variable_map
+ // looks for a value, it tries all the aliases (and returns the storage
+ // variable in lookup).
+ //
+ // The existing variable should already have final type and visibility
+ // values which are copied over to the alias.
+ //
+ // Overridable aliased variables are most likely a bad idea: without a
+ // significant effort, the overrides will only be applied along the alias
+ // names (i.e., there would be no cross-alias overriding). So for now we
+ // don't allow this (use the common variable mechanism instead).
+ //
+ const variable&
+ insert_alias (const variable& var, string name);
+
// Insert a variable pattern. Any variable that matches this pattern
// will have the specified type, visibility, and overridability. If
// match is true, then individual insertions of the matching variable
@@ -989,11 +1027,12 @@ namespace build2
private:
static variable_pool instance;
- const variable&
+ variable&
insert (string name,
const value_type*,
const variable_visibility* = nullptr,
- const bool* overridable = nullptr);
+ const bool* overridable = nullptr,
+ bool pattern = true);
void
update (variable&,
@@ -1152,7 +1191,8 @@ namespace build2
lookup
operator[] (const variable& var) const
{
- return lookup (find (var), this);
+ auto p (find (var));
+ return lookup (p.first, &p.second, this);
}
lookup
@@ -1170,11 +1210,12 @@ namespace build2
}
// If typed is false, leave the value untyped even if the variable is.
+ // The second half of the pair is the storage variable.
//
- const value_data*
+ pair<const value_data*, const variable&>
find (const variable&, bool typed = true) const;
- value_data*
+ pair<value_data*, const variable&>
find_to_modify (const variable&, bool typed = true);
// Convert a lookup pointing to a value belonging to this variable map