From d262f63ce5a7c3810abde1f66ee3bb99d56acdd0 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 4 Jan 2018 15:35:39 +0200 Subject: Add support for variable aliases --- build2/config/utility.cxx | 2 +- build2/config/utility.txx | 2 +- build2/context.cxx | 2 +- build2/dump.cxx | 2 +- build2/file.cxx | 2 +- build2/prerequisite.cxx | 2 +- build2/scope.cxx | 18 ++++--- build2/target.cxx | 12 +++-- build2/test/script/script.cxx | 9 ++-- build2/variable.cxx | 107 ++++++++++++++++++++++++++++++++---------- build2/variable.hxx | 73 +++++++++++++++++++++------- 11 files changed, 168 insertions(+), 63 deletions(-) (limited to 'build2') 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 (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 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 (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 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 { - 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 ( + 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 { - auto* r (const_cast (find (var, typed))); + auto p (find (var, typed)); + auto* r (const_cast (p.first)); if (r != nullptr) r->version++; - return r; + return pair (r, p.second); } pair, 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 .{__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 override; + const variable* alias; // Circular linked list. + const value_type* type; // If NULL, then not (yet) typed. + unique_ptr 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 - 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::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 find (const variable&, bool typed = true) const; - value_data* + pair find_to_modify (const variable&, bool typed = true); // Convert a lookup pointing to a value belonging to this variable map -- cgit v1.1