diff options
-rw-r--r-- | build2/b.cxx | 180 | ||||
-rw-r--r-- | build2/bin/module.cxx | 35 | ||||
-rw-r--r-- | build2/cli/module.cxx | 10 | ||||
-rw-r--r-- | build2/context | 15 | ||||
-rw-r--r-- | build2/context.cxx | 93 | ||||
-rw-r--r-- | build2/cxx/module.cxx | 30 | ||||
-rw-r--r-- | build2/dist/module.cxx | 18 | ||||
-rw-r--r-- | build2/file.cxx | 14 | ||||
-rw-r--r-- | build2/install/module.cxx | 8 | ||||
-rw-r--r-- | build2/module.cxx | 10 | ||||
-rw-r--r-- | build2/parser | 3 | ||||
-rw-r--r-- | build2/parser.cxx | 14 | ||||
-rw-r--r-- | build2/scope | 5 | ||||
-rw-r--r-- | build2/spec | 8 | ||||
-rw-r--r-- | build2/target | 5 | ||||
-rw-r--r-- | build2/test/module.cxx | 14 | ||||
-rw-r--r-- | build2/test/rule.cxx | 17 | ||||
-rw-r--r-- | build2/variable | 40 | ||||
-rw-r--r-- | build2/variable.cxx | 40 | ||||
-rw-r--r-- | build2/variable.ixx | 35 | ||||
-rwxr-xr-x | tests/variable/expansion/test.sh | 2 | ||||
-rwxr-xr-x | tests/variable/prepend/test.sh | 2 |
22 files changed, 404 insertions, 194 deletions
diff --git a/build2/b.cxx b/build2/b.cxx index 1770d2d..219f4eb 100644 --- a/build2/b.cxx +++ b/build2/b.cxx @@ -253,6 +253,7 @@ main (int argc, char* argv[]) opspec* lifted (nullptr); size_t skip (0); bool dirty (true); + variable_overrides var_ovs; for (auto mit (bspec.begin ()); mit != bspec.end (); ) { @@ -292,7 +293,7 @@ main (int argc, char* argv[]) // if (dirty) { - reset (cmd_vars); + var_ovs = reset (cmd_vars); dirty = false; } @@ -326,12 +327,14 @@ main (int argc, char* argv[]) const operation_info* post_oif (nullptr); // We do meta-operation and operation batches sequentially (no - // parallelism). But multiple targets in an operation batch - // can be done in parallel. - // - action_targets tgs; - tgs.reserve (os.size ()); + // parallelism). But multiple targets in an operation batch can be + // done in parallel. + // First bootstrap projects for all the target so that all the + // variable overrides are set (if we also load/search/match in the + // same loop then we may end up loading a project (via import) before + // this happends. + // for (targetspec& ts: os) { name& tn (ts.name); @@ -776,7 +779,7 @@ main (int argc, char* argv[]) if (verb >= 5) { - trace << "target " << tn << ':'; + trace << "bootstrapped " << tn << ':'; trace << " out_base: " << out_base; trace << " src_base: " << src_base; trace << " out_root: " << out_root; @@ -793,16 +796,75 @@ main (int argc, char* argv[]) << info << "consider explicitly specifying src_base " << "for " << tn; + // Enter project-wide (as opposed to global) variable overrides. + // + // The mildly tricky part here is to distinguish the situation where + // we are bootstrapping the same project multiple times (which is + // ok) vs overriding the same variable multiple times (which is not + // ok). The first override that we set cannot possibly end up in the + // second sitution so if it is already set, then it can only be the + // first case. + // + bool first (true); + for (const variable_override& o: var_ovs) + { + auto p (rs.vars.assign (o.ovr)); + + if (!p.second) + { + if (first) + break; + + fail << "multiple project overrides of variable " << o.var.name; + } + + 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; + } + + ts.root_scope = &rs; + ts.out_base = move (out_base); + ts.buildfile = move (bf); + } + + // If this operation has been lifted, break out. + // + if (lifted == &os) + { + assert (oid == 0); // Should happend on the first target. + break; + } + + // Now load/search/match the targets. + // + action_targets tgs; + tgs.reserve (os.size ()); + + for (targetspec& ts: os) + { + name& tn (ts.name); + scope& rs (*ts.root_scope); + + l5 ([&]{trace << "loading " << tn;}); + // Load the buildfile. // - mif->load (bf, rs, out_base, src_base, l); + mif->load (ts.buildfile, rs, ts.out_base, ts.src_base, l); // Next search and match the targets. We don't want to start // building before we know how to for all the targets in this // operation batch. // { - scope& bs (scopes.find (out_base)); + scope& bs (scopes.find (ts.out_base)); const string* e; const target_type* ti (bs.find_target_type (tn, e)); @@ -824,81 +886,73 @@ main (int argc, char* argv[]) } } - // We don't do anything if we lifted the first operation. + // Finally perform the operation. // - if (oid != 0) + if (pre_oid != 0) { - if (pre_oid != 0) - { - l5 ([&]{trace << "start pre-operation batch " << pre_oif->name - << ", id " << static_cast<uint16_t> (pre_oid);}); - - if (mif->operation_pre != nullptr) - mif->operation_pre (pre_oid); // Cannot be translated. + l5 ([&]{trace << "start pre-operation batch " << pre_oif->name + << ", id " << static_cast<uint16_t> (pre_oid);}); - current_inner_oif = pre_oif; - current_outer_oif = oif; - current_mode = pre_oif->mode; - dependency_count = 0; + if (mif->operation_pre != nullptr) + mif->operation_pre (pre_oid); // Cannot be translated. - action a (mid, pre_oid, oid); - - mif->match (a, tgs); - mif->execute (a, tgs, true); // Run quiet. + current_inner_oif = pre_oif; + current_outer_oif = oif; + current_mode = pre_oif->mode; + dependency_count = 0; - if (mif->operation_post != nullptr) - mif->operation_post (pre_oid); + action a (mid, pre_oid, oid); - l5 ([&]{trace << "end pre-operation batch " << pre_oif->name - << ", id " << static_cast<uint16_t> (pre_oid);}); - } + mif->match (a, tgs); + mif->execute (a, tgs, true); // Run quiet. - current_inner_oif = oif; - current_outer_oif = nullptr; - current_mode = oif->mode; - dependency_count = 0; + if (mif->operation_post != nullptr) + mif->operation_post (pre_oid); - action a (mid, oid, 0); + l5 ([&]{trace << "end pre-operation batch " << pre_oif->name + << ", id " << static_cast<uint16_t> (pre_oid);}); + } - mif->match (a, tgs); - mif->execute (a, tgs, verb == 0); + current_inner_oif = oif; + current_outer_oif = nullptr; + current_mode = oif->mode; + dependency_count = 0; - if (post_oid != 0) - { - l5 ([&]{trace << "start post-operation batch " << post_oif->name - << ", id " << static_cast<uint16_t> (post_oid);}); + action a (mid, oid, 0); - if (mif->operation_pre != nullptr) - mif->operation_pre (post_oid); // Cannot be translated. + mif->match (a, tgs); + mif->execute (a, tgs, verb == 0); - current_inner_oif = post_oif; - current_outer_oif = oif; - current_mode = post_oif->mode; - dependency_count = 0; + if (post_oid != 0) + { + l5 ([&]{trace << "start post-operation batch " << post_oif->name + << ", id " << static_cast<uint16_t> (post_oid);}); - action a (mid, post_oid, oid); + if (mif->operation_pre != nullptr) + mif->operation_pre (post_oid); // Cannot be translated. - mif->match (a, tgs); - mif->execute (a, tgs, true); // Run quiet. + current_inner_oif = post_oif; + current_outer_oif = oif; + current_mode = post_oif->mode; + dependency_count = 0; - if (mif->operation_post != nullptr) - mif->operation_post (post_oid); + action a (mid, post_oid, oid); - l5 ([&]{trace << "end post-operation batch " << post_oif->name - << ", id " << static_cast<uint16_t> (post_oid);}); - } + mif->match (a, tgs); + mif->execute (a, tgs, true); // Run quiet. if (mif->operation_post != nullptr) - mif->operation_post (oid); + mif->operation_post (post_oid); - l5 ([&]{trace << "end operation batch " << oif->name - << ", id " << static_cast<uint16_t> (oid);}); + l5 ([&]{trace << "end post-operation batch " << post_oif->name + << ", id " << static_cast<uint16_t> (post_oid);}); } - // If this operation has been lifted, break out. - // - if (lifted == &os) - break; + if (mif->operation_post != nullptr) + mif->operation_post (oid); + + l5 ([&]{trace << "end operation batch " << oif->name + << ", id " << static_cast<uint16_t> (oid);}); } if (mid != 0) diff --git a/build2/bin/module.cxx b/build2/bin/module.cxx index 13bf2a8..5b1d8ca 100644 --- a/build2/bin/module.cxx +++ b/build2/bin/module.cxx @@ -47,22 +47,25 @@ namespace build2 { auto& v (var_pool); - v.find<path> ("config.bin.ar"); - v.find<path> ("config.bin.ranlib"); - - v.find<string> ("config.bin.lib"); - v.find<strings> ("config.bin.exe.lib"); - v.find<strings> ("config.bin.liba.lib"); - v.find<strings> ("config.bin.libso.lib"); - v.find<dir_paths> ("config.bin.rpath"); - - v.find<string> ("bin.lib"); - v.find<strings> ("bin.exe.lib"); - v.find<strings> ("bin.liba.lib"); - v.find<strings> ("bin.libso.lib"); - v.find<dir_paths> ("bin.rpath"); - - v.find<string> ("bin.libprefix"); + // @@ OVR + // + + v.insert<path> ("config.bin.ar"); + v.insert<path> ("config.bin.ranlib"); + + v.insert<string> ("config.bin.lib"); + v.insert<strings> ("config.bin.exe.lib"); + v.insert<strings> ("config.bin.liba.lib"); + v.insert<strings> ("config.bin.libso.lib"); + v.insert<dir_paths> ("config.bin.rpath"); + + v.insert<string> ("bin.lib"); + v.insert<strings> ("bin.exe.lib"); + v.insert<strings> ("bin.liba.lib"); + v.insert<strings> ("bin.libso.lib"); + v.insert<dir_paths> ("bin.rpath"); + + v.insert<string> ("bin.libprefix"); } // Register target types. diff --git a/build2/cli/module.cxx b/build2/cli/module.cxx index 15c8b90..bf975e7 100644 --- a/build2/cli/module.cxx +++ b/build2/cli/module.cxx @@ -55,12 +55,14 @@ namespace build2 { auto& v (var_pool); - v.find<bool> ("config.cli.configured"); + // @@ OVR - v.find<path> ("config.cli"); + v.insert<bool> ("config.cli.configured"); - v.find<strings> ("config.cli.options"); - v.find<strings> ("cli.options"); + v.insert<path> ("config.cli"); + + v.insert<strings> ("config.cli.options"); + v.insert<strings> ("cli.options"); } // Register target types. diff --git a/build2/context b/build2/context index 661d9ff..bd56dbb 100644 --- a/build2/context +++ b/build2/context @@ -17,6 +17,7 @@ namespace build2 { class scope; class file; + struct variable; extern dir_path work; extern dir_path home; @@ -41,10 +42,22 @@ namespace build2 // extern uint64_t dependency_count; + // Project-wide (as opposed to global) variable overrides. Returned by + // reset(). + // + struct variable_override + { + const variable& var; // Original variable. + const variable& ovr; // Override variable. + names val; + }; + + using variable_overrides = vector<variable_override>; + // Reset the build state. In particular, this removes all the targets, // scopes, and variables. // - void + variable_overrides reset (const strings& cmd_vars); // The dual interface wrapper for the {mk,rm}{file,dir}() functions diff --git a/build2/context.cxx b/build2/context.cxx index 0988f79..dbfffdb 100644 --- a/build2/context.cxx +++ b/build2/context.cxx @@ -38,13 +38,15 @@ namespace build2 execution_mode current_mode; uint64_t dependency_count; - void + variable_overrides reset (const strings& cmd_vars) { tracer trace ("reset"); l6 ([&]{trace << "resetting build state";}); + variable_overrides vos; + targets.clear (); scopes.clear (); var_pool.clear (); @@ -80,9 +82,12 @@ namespace build2 // marked as such first. Then, as we enter variables, we can verify that // the override is alowed. // - for (const string& v: cmd_vars) + for (const string& s: cmd_vars) { - istringstream is (v); + char c (s[0]); // Should at least have '='. + string a (s, c == '!' || c == '%' ? 1 : 0); + + istringstream is (a); is.exceptions (istringstream::failbit | istringstream::badbit); lexer l (is, path ("<cmdline>")); @@ -96,15 +101,73 @@ namespace build2 tt != token_type::prepend && tt != token_type::append)) { - fail << "expected variable assignment instead of '" << v << "'" << + fail << "expected variable assignment instead of '" << s << "'" << info << "use double '--' to treat this argument as buildspec"; } + 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 == '%' + ? variable_visibility::project + : variable_visibility::normal); + const char* k (tt == token_type::assign ? ".__override" : + tt == token_type::append ? ".__suffix" : ".__prefix"); + + // We might already have a variable for this kind of override. + // + const variable* o (var.override.get ()); + for (; o->override != nullptr; o = o->override.get ()) + { + if (o->override->visibility == v && + o->override->name.rfind (k) != string::npos) + break; + } + + // Add it if not found. + // + if (o->override == nullptr) + o->override.reset (new variable {n + k, nullptr, nullptr, v}); + + o = o->override.get (); + + // Currently we expand project overrides in the global scope to keep + // things simple. + // parser p; - t = p.parse_variable (l, gs, var_pool.find (t.value), tt); + names val; + t = p.parse_variable_value (l, gs, val); if (t.type != token_type::eos) - fail << "unexpected " << t << " in variable assignment '" << v << "'"; + fail << "unexpected " << t << " in variable assignment '" << s << "'"; + + if (c == '!') + { + auto p (gs.vars.assign (*o)); + + if (!p.second) + fail << "multiple global overrides of variable " << var.name; + + 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)}); } // Enter builtin variables. @@ -112,19 +175,19 @@ namespace build2 { auto& v (var_pool); - v.find<dir_path> ("src_root"); - v.find<dir_path> ("out_root"); - v.find<dir_path> ("src_base"); - v.find<dir_path> ("out_base"); + v.insert<dir_path> ("src_root"); + v.insert<dir_path> ("out_root"); + v.insert<dir_path> ("src_base"); + v.insert<dir_path> ("out_base"); - v.find<string> ("project"); - v.find<dir_path> ("amalgamation"); + v.insert<string> ("project"); + v.insert<dir_path> ("amalgamation"); // Not typed since the value requires pre-processing (see file.cxx). // - v.find ("subprojects"); + v.insert ("subprojects"); - v.find<string> ("extension"); + v.insert<string> ("extension"); } gs.assign<dir_path> ("build.work") = work; @@ -224,6 +287,8 @@ namespace build2 r.insert<file> (perform_update_id, "file", file_rule::instance); r.insert<file> (perform_clean_id, "file", file_rule::instance); } + + return vos; } fs_status<mkdir_status> diff --git a/build2/cxx/module.cxx b/build2/cxx/module.cxx index 7b0f04e..aac2af0 100644 --- a/build2/cxx/module.cxx +++ b/build2/cxx/module.cxx @@ -62,24 +62,26 @@ namespace build2 { auto& v (var_pool); - v.find<path> ("config.cxx"); + // @@ OVR - v.find<strings> ("config.cxx.poptions"); - v.find<strings> ("config.cxx.coptions"); - v.find<strings> ("config.cxx.loptions"); - v.find<strings> ("config.cxx.libs"); + v.insert<path> ("config.cxx", true); - v.find<strings> ("cxx.poptions"); - v.find<strings> ("cxx.coptions"); - v.find<strings> ("cxx.loptions"); - v.find<strings> ("cxx.libs"); + v.insert<strings> ("config.cxx.poptions"); + v.insert<strings> ("config.cxx.coptions"); + v.insert<strings> ("config.cxx.loptions"); + v.insert<strings> ("config.cxx.libs"); - v.find<strings> ("cxx.export.poptions"); - v.find<strings> ("cxx.export.coptions"); - v.find<strings> ("cxx.export.loptions"); - v.find<strings> ("cxx.export.libs"); + v.insert<strings> ("cxx.poptions"); + v.insert<strings> ("cxx.coptions"); + v.insert<strings> ("cxx.loptions"); + v.insert<strings> ("cxx.libs"); - v.find<string> ("cxx.std"); + v.insert<strings> ("cxx.export.poptions"); + v.insert<strings> ("cxx.export.coptions"); + v.insert<strings> ("cxx.export.loptions"); + v.insert<strings> ("cxx.export.libs"); + + v.insert<string> ("cxx.std"); } // Register target types. diff --git a/build2/dist/module.cxx b/build2/dist/module.cxx index ab8c5a3..4b68bb8 100644 --- a/build2/dist/module.cxx +++ b/build2/dist/module.cxx @@ -39,18 +39,20 @@ namespace build2 { auto& v (var_pool); - v.find<bool> ("dist"); + // @@ OVR - v.find<string> ("dist.package"); + v.insert<bool> ("dist"); - v.find<dir_path> ("dist.root"); - v.find<dir_path> ("config.dist.root"); + v.insert<string> ("dist.package"); - v.find<path> ("dist.cmd"); - v.find<path> ("config.dist.cmd"); + v.insert<dir_path> ("dist.root"); + v.insert<dir_path> ("config.dist.root"); - v.find<strings> ("dist.archives"); - v.find<strings> ("config.dist.archives"); + v.insert<path> ("dist.cmd"); + v.insert<path> ("config.dist.cmd"); + + v.insert<strings> ("dist.archives"); + v.insert<strings> ("config.dist.archives"); } } diff --git a/build2/file.cxx b/build2/file.cxx index 90d584d..1f4e517 100644 --- a/build2/file.cxx +++ b/build2/file.cxx @@ -10,12 +10,12 @@ #include <build2/scope> #include <build2/context> -#include <build2/parser> #include <build2/prerequisite> #include <build2/diagnostics> #include <build2/token> #include <build2/lexer> +#include <build2/parser> using namespace std; using namespace butl; @@ -131,11 +131,15 @@ namespace build2 rs.out_path_ = &i->first; } + // First time create_root() is called on this scope. + // + bool first (rs.meta_operations.empty ()); + // Enter built-in meta-operation and operation names. Loading of // modules (via the src bootstrap; see below) can result in // additional meta/operations being added. // - if (rs.meta_operations.empty ()) + if (first) { rs.meta_operations.insert (perform_id, perform); @@ -844,12 +848,14 @@ namespace build2 break; } - // Then try the config.import.* mechanism. + // Then try the config.import.* mechanism (overridable variable). // if (out_root.empty ()) { + // @@ OVR + // const variable& var ( - var_pool.find<dir_path> ("config.import." + project)); + var_pool.insert<dir_path> ("config.import." + project, true)); if (auto l = iroot[var]) { diff --git a/build2/install/module.cxx b/build2/install/module.cxx index 0c17c05..7838a65 100644 --- a/build2/install/module.cxx +++ b/build2/install/module.cxx @@ -50,7 +50,7 @@ namespace build2 vn = "config.install."; vn += name; vn += var; - const variable& vr (var_pool.find<T> (move (vn))); + const variable& vr (var_pool.insert<T> (move (vn))); // @@ OVR cv = dv != nullptr ? &config::required (r, vr, *dv, override).first.get () @@ -60,7 +60,7 @@ namespace build2 vn = "install."; vn += name; vn += var; - const variable& vr (var_pool.find<T> (move (vn))); + const variable& vr (var_pool.insert<T> (move (vn))); // @@ OVR value& v (r.assign (vr)); @@ -139,7 +139,9 @@ namespace build2 { auto& v (var_pool); - v.find<dir_path> ("install"); + // @@ OVR + // + v.insert<dir_path> ("install"); } // Register our alias and file installer rule. diff --git a/build2/module.cxx b/build2/module.cxx index b090173..cfab057 100644 --- a/build2/module.cxx +++ b/build2/module.cxx @@ -98,10 +98,12 @@ namespace build2 bool l (i != lm.end ()); bool c (l && i->second.init (rs, bs, loc, i->second.module, f, opt)); - const variable& lv (var_pool.find<bool> (name + ".loaded", - variable_visibility::project)); - const variable& cv (var_pool.find<bool> (name + ".configured", - variable_visibility::project)); + const variable& lv (var_pool.insert<bool> (name + ".loaded", + false, + variable_visibility::project)); + const variable& cv (var_pool.insert<bool> (name + ".configured", + false, + variable_visibility::project)); bs.assign (lv) = l; bs.assign (cv) = c; diff --git a/build2/parser b/build2/parser index f3413a3..0e15e4c 100644 --- a/build2/parser +++ b/build2/parser @@ -41,6 +41,9 @@ namespace build2 token parse_variable (lexer&, scope&, const variable_type&, token_type kind); + token + parse_variable_value (lexer&, scope&, names_type& result); + names_type parse_export_stub (istream& is, const path& p, scope& r, scope& b) { diff --git a/build2/parser.cxx b/build2/parser.cxx index f9391f8..d42b266 100644 --- a/build2/parser.cxx +++ b/build2/parser.cxx @@ -68,6 +68,20 @@ namespace build2 return t; } + token parser:: + parse_variable_value (lexer& l, scope& s, names_type& result) + { + path_ = &l.name (); + lexer_ = &l; + target_ = nullptr; + scope_ = &s; + + type tt; + token t (type::eos, false, 0, 0); + result = variable_value (t, tt); + return t; + } + void parser:: clause (token& t, type& tt) { diff --git a/build2/scope b/build2/scope index 9878e7f..7ec8c3b 100644 --- a/build2/scope +++ b/build2/scope @@ -143,9 +143,12 @@ namespace build2 value& assign (const string& name) {return vars.assign (name).first.get ();} + // Unlike the two above, assign a non-overridable variable with normal + // visibility. + // template <typename T> value& - assign (const string& name) {return vars.assign<T> (name).first.get ();} + assign (string name) {return vars.assign<T> (move (name)).first.get ();} // Return a value suitable for appending. If the variable does not // exist in this scope's map, then outer scopes are searched for diff --git a/build2/spec b/build2/spec index 5e02f46..4602b6e 100644 --- a/build2/spec +++ b/build2/spec @@ -10,6 +10,8 @@ namespace build2 { + class scope; + struct targetspec { typedef build2::name name_type; @@ -21,6 +23,12 @@ namespace build2 dir_path src_base; name_type name; + + // The rest is calculated and cached. + // + scope* root_scope = nullptr; + dir_path out_base; + path buildfile; }; struct opspec: vector<targetspec> diff --git a/build2/target b/build2/target index 4c5d046..bf5ff04 100644 --- a/build2/target +++ b/build2/target @@ -294,9 +294,12 @@ namespace build2 value& assign (const string& name) {return vars.assign (name).first;} + // Unlike the two above, assign a non-overridable variable with normal + // visibility. + // template <typename T> value& - assign (const string& name) {return vars.assign<T> (name).first.get ();} + assign (string name) {return vars.assign<T> (move (name)).first.get ();} // Return a value suitable for appending. See class scope for // details. diff --git a/build2/test/module.cxx b/build2/test/module.cxx index 10948ab..d5f6430 100644 --- a/build2/test/module.cxx +++ b/build2/test/module.cxx @@ -38,12 +38,14 @@ namespace build2 { auto& v (var_pool); - v.find<bool> ("test"); - v.find<name> ("test.input"); - v.find<name> ("test.output"); - v.find<name> ("test.roundtrip"); - v.find<strings> ("test.options"); - v.find<strings> ("test.arguments"); + // @@ OVR + + v.insert<bool> ("test"); + v.insert<name> ("test.input"); + v.insert<name> ("test.output"); + v.insert<name> ("test.roundtrip"); + v.insert<strings> ("test.options"); + v.insert<strings> ("test.arguments"); } } diff --git a/build2/test/rule.cxx b/build2/test/rule.cxx index 8c89bc7..063d148 100644 --- a/build2/test/rule.cxx +++ b/build2/test/rule.cxx @@ -66,9 +66,12 @@ namespace build2 { // See if there is a scope variable. // + // @@ I don't think we use this (e.g., test.exe = true) anymore. + // We now do exe{*}: test = true. + // if (!l.defined ()) l = t.base_scope ()[ - var_pool.find<bool> (string("test.") + t.type ().name)]; + var_pool.insert<bool> (string("test.") + t.type ().name)]; r = l && cast<bool> (l); } @@ -143,12 +146,14 @@ namespace build2 if (!il && !ol && !rl) { + // @@ Again, don't think we use this anymore. + // string n ("test."); n += t.type ().name; - const variable& in (var_pool.find<name> (n + ".input")); - const variable& on (var_pool.find<name> (n + ".output")); - const variable& rn (var_pool.find<name> (n + ".roundtrip")); + const variable& in (var_pool.insert<name> (n + ".input")); + const variable& on (var_pool.insert<name> (n + ".output")); + const variable& rn (var_pool.insert<name> (n + ".roundtrip")); // We should only keep value(s) that were specified together // in the innermost scope. @@ -288,11 +293,13 @@ namespace build2 if (!l) { + // @@ Again, don't think we do it. + // var.resize (5); var += t.type ().name; var += '.'; var += n; - l = t.base_scope ()[var_pool.find<strings> (var)]; + l = t.base_scope ()[var_pool.insert<strings> (var)]; } if (l) diff --git a/build2/variable b/build2/variable index 931b4f7..7b137aa 100644 --- a/build2/variable +++ b/build2/variable @@ -80,7 +80,8 @@ namespace build2 struct variable { string name; - const value_type* type; // If NULL, then not (yet) typed. + const value_type* type; // If NULL, then not (yet) typed. + mutable unique_ptr<variable> override; variable_visibility visibility; }; @@ -539,36 +540,34 @@ namespace build2 struct variable_pool: private variable_pool_base { const variable& - find (string name) + insert (string name, + bool overridable = false, + variable_visibility v = variable_visibility::normal) { - return find (name, nullptr, nullptr); + return insert (move (name), nullptr, v, overridable); } template <typename T> const variable& - find (string name) + insert (string name, + bool overridable = false, + variable_visibility v = variable_visibility::normal) { - return find (name, nullptr, &value_traits<T>::value_type); + return insert ( + move (name), &value_traits<T>::value_type, v, overridable); } const variable& - find (string name, variable_visibility v) - { - return find (name, &v, nullptr); - } - - template <typename T> - const variable& - find (string name, variable_visibility v) - { - return find (name, &v, &value_traits<T>::value_type); - } + find (const string& name); using variable_pool_base::clear; private: const variable& - find (string name, const variable_visibility*, const build2::value_type*); + insert (string name, + const build2::value_type*, + variable_visibility, + bool overridable); }; extern variable_pool var_pool; @@ -635,11 +634,14 @@ namespace build2 return assign (var_pool.find (name)); } + // Unlike the two above, assign a non-overridable variable with normal + // visibility. + // template <typename T> pair<reference_wrapper<value>, bool> - assign (const string& name) + assign (string name) { - return assign (var_pool.find<T> (name)); + return assign (var_pool.insert<T> (move (name))); } pair<const_iterator, const_iterator> diff --git a/build2/variable.cxx b/build2/variable.cxx index 7b7f16d..4597e9e 100644 --- a/build2/variable.cxx +++ b/build2/variable.cxx @@ -552,6 +552,46 @@ namespace build2 // variable_pool // + const variable& variable_pool:: + insert (string n, + const build2::value_type* t, + variable_visibility v, + bool o) + { + auto p (variable_pool_base::insert (variable {move (n), t, nullptr, v})); + const variable& r (*p.first); + + if (!p.second) + { + // Update type? + // + if (t != nullptr && r.type != t) + { + assert (r.type == nullptr); + const_cast<variable&> (r).type = t; // Not changing the key. + } + + // Change visibility? While this might at first seem like a bad idea, + // it can happen that the variable lookup happens before any values + // were set, in which case the variable will be entered with the + // default visibility. + // + if (r.visibility != v) + { + assert (r.visibility == variable_visibility::normal); // Default. + const_cast<variable&> (r).visibility = v; // Not changing the key. + } + + // Check overridability (all overrides, if any, should already have + // been enetered (see context.cxx:reset()). + // + if (r.override != nullptr && !o) + fail << "variable " << r.name << " cannot be overridden"; + } + + return r; + } + variable_pool var_pool; // variable_map diff --git a/build2/variable.ixx b/build2/variable.ixx index 67f8c56..a4d7dc3 100644 --- a/build2/variable.ixx +++ b/build2/variable.ixx @@ -552,37 +552,14 @@ namespace build2 return !p->empty (); } + // variable_pool + // inline const variable& variable_pool:: - find (string n, const variable_visibility* vv, const build2::value_type* t) + find (const string& n) { - auto r ( - insert ( - variable { - move (n), - t, - vv != nullptr ? *vv : variable_visibility::normal})); - const variable& v (*r.first); - - // Update type? - // - if (!r.second && t != nullptr && v.type != t) - { - assert (v.type == nullptr); - const_cast<variable&> (v).type = t; // Not changing the key. - } - - // Change visibility? While this might at first seem like a bad idea, - // it can happen that the variable lookup happens before any values - // were set, in which case the variable will be entered with the - // default visibility. - // - if (!r.second && vv != nullptr && v.visibility != *vv) - { - assert (v.visibility == variable_visibility::normal); // Default. - const_cast<variable&> (v).visibility = *vv; // Not changing the key. - } - - return v; + auto p (variable_pool_base::insert ( + variable {n, nullptr, nullptr, variable_visibility::normal})); + return *p.first; } // variable_map::iterator_adapter diff --git a/tests/variable/expansion/test.sh b/tests/variable/expansion/test.sh index b898b3c..afcb3bd 100755 --- a/tests/variable/expansion/test.sh +++ b/tests/variable/expansion/test.sh @@ -1,3 +1,3 @@ #!/bin/sh -valgrind -q b -q | diff -u test.out - +b -q | diff -u test.out - diff --git a/tests/variable/prepend/test.sh b/tests/variable/prepend/test.sh index b898b3c..afcb3bd 100755 --- a/tests/variable/prepend/test.sh +++ b/tests/variable/prepend/test.sh @@ -1,3 +1,3 @@ #!/bin/sh -valgrind -q b -q | diff -u test.out - +b -q | diff -u test.out - |