aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2016-03-31 09:01:50 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2016-03-31 09:01:50 +0200
commit6417a4e6af2b7732ec0da6af24f1a56f7cdada3f (patch)
tree58ffae7ef0cdac55acd6d498dfc66ce0967f4e9a
parent31bd69c56bc29ec1c154a7c0623b6f0ccce78af1 (diff)
Set part of variable override implementation
-rw-r--r--build2/b.cxx180
-rw-r--r--build2/bin/module.cxx35
-rw-r--r--build2/cli/module.cxx10
-rw-r--r--build2/context15
-rw-r--r--build2/context.cxx93
-rw-r--r--build2/cxx/module.cxx30
-rw-r--r--build2/dist/module.cxx18
-rw-r--r--build2/file.cxx14
-rw-r--r--build2/install/module.cxx8
-rw-r--r--build2/module.cxx10
-rw-r--r--build2/parser3
-rw-r--r--build2/parser.cxx14
-rw-r--r--build2/scope5
-rw-r--r--build2/spec8
-rw-r--r--build2/target5
-rw-r--r--build2/test/module.cxx14
-rw-r--r--build2/test/rule.cxx17
-rw-r--r--build2/variable40
-rw-r--r--build2/variable.cxx40
-rw-r--r--build2/variable.ixx35
-rwxr-xr-xtests/variable/expansion/test.sh2
-rwxr-xr-xtests/variable/prepend/test.sh2
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 -