aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2017-01-30 11:12:25 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2017-02-13 12:42:41 +0200
commitb262d2c9c56eed18d043dccefac02b54a6ae2f95 (patch)
tree010e5c6cce042e0fbf25817a62659d251c220acf
parentf93038fbee1631b95922b0742e0fd00fa8dae02e (diff)
Implement pattern-based variable typing, tighten variable type update
-rw-r--r--build2/cli/init.cxx6
-rw-r--r--build2/config/init.cxx4
-rw-r--r--build2/config/utility6
-rw-r--r--build2/config/utility.cxx18
-rw-r--r--build2/context.cxx17
-rw-r--r--build2/dist/init.cxx2
-rw-r--r--build2/file.cxx12
-rw-r--r--build2/install/init.cxx2
-rw-r--r--build2/module.cxx12
-rw-r--r--build2/variable69
-rw-r--r--build2/variable.cxx183
11 files changed, 246 insertions, 85 deletions
diff --git a/build2/cli/init.cxx b/build2/cli/init.cxx
index 2650c1b..1cf248d 100644
--- a/build2/cli/init.cxx
+++ b/build2/cli/init.cxx
@@ -81,13 +81,13 @@ namespace build2
// Otherwise we will only honor optional if the user didn't specify
// any cli configuration explicitly.
//
- optional = optional && !config::specified (rs, "config.cli");
+ optional = optional && !config::specified (rs, "cli");
// If the configuration says we are unconfigured, then we should't
// re-run tests, etc. But we may still need to print the config
// report.
//
- conf = !optional || !config::unconfigured (rs, "config.cli");
+ conf = !optional || !config::unconfigured (rs, "cli");
}
if (first)
@@ -214,7 +214,7 @@ namespace build2
// on each run.
//
if (!conf)
- nv = config::unconfigured (rs, "config.cli", true) || nv;
+ nv = config::unconfigured (rs, "cli", true) || nv;
// If this is a new value (e.g., we are configuring), then print the
// report at verbosity level 2 and up (-v).
diff --git a/build2/config/init.cxx b/build2/config/init.cxx
index 1f0b391..cfb69e0 100644
--- a/build2/config/init.cxx
+++ b/build2/config/init.cxx
@@ -40,6 +40,10 @@ namespace build2
auto& vp (var_pool.rw (rs));
+ // utility.cxx:unconfigured()
+ //
+ vp.insert_pattern<bool> ("config.*.configured");
+
// Load config.build if one exists.
//
// Note that we have to do this during bootstrap since the order in
diff --git a/build2/config/utility b/build2/config/utility
index e4e463d..041277a 100644
--- a/build2/config/utility
+++ b/build2/config/utility
@@ -116,21 +116,21 @@ namespace build2
// running the tests, etc).
//
bool
- specified (scope& root, const string& ns);
+ specified (scope& root, const string& name);
// Check if there is a false config.*.configured value. This mechanism can
// be used to "remember" that the module is left unconfigured in order to
// avoid re-running the tests, etc.
//
bool
- unconfigured (scope& root, const string& ns);
+ unconfigured (scope& root, const string& name);
// Set the config.*.configured value. Note that you only need to set it to
// false. It will be automatically ignored if there are any other config.*
// values for this module. Return true if this sets a new value.
//
bool
- unconfigured (scope& root, const string& ns, bool);
+ unconfigured (scope& root, const string& name, bool);
// Enter the variable so that it is saved during configuration. See
// config::module for details.
diff --git a/build2/config/utility.cxx b/build2/config/utility.cxx
index 7db4555..08faeeb 100644
--- a/build2/config/utility.cxx
+++ b/build2/config/utility.cxx
@@ -62,7 +62,7 @@ namespace build2
}
bool
- specified (scope& r, const string& ns)
+ specified (scope& r, const string& n)
{
// Search all outer scopes for any value in this namespace.
//
@@ -72,7 +72,7 @@ namespace build2
// any original values, they will be "visible"; see find_override() for
// details.
//
- const variable& vns (var_pool.rw (r).insert (ns));
+ const variable& vns (var_pool.rw (r).insert ("config." + n));
for (scope* s (&r); s != nullptr; s = s->parent_scope ())
{
for (auto p (s->vars.find_namespace (vns));
@@ -93,11 +93,12 @@ namespace build2
}
bool
- unconfigured (scope& rs, const string& ns)
+ unconfigured (scope& rs, const string& n)
{
- // Note: not overridable.
+ // Pattern-typed in boot() as bool.
//
- const variable& var (var_pool.rw (rs).insert<bool> (ns + ".configured"));
+ const variable& var (
+ var_pool.rw (rs).insert ("config." + n + ".configured"));
if (current_mif->id == configure_id)
save_variable (rs, var);
@@ -107,11 +108,12 @@ namespace build2
}
bool
- unconfigured (scope& rs, const string& ns, bool v)
+ unconfigured (scope& rs, const string& n, bool v)
{
- // Note: not overridable.
+ // Pattern-typed in boot() as bool.
//
- const variable& var (var_pool.rw (rs).insert<bool> (ns + ".configured"));
+ const variable& var (
+ var_pool.rw (rs).insert ("config." + n + ".configured"));
if (current_mif->id == configure_id)
save_variable (rs, var);
diff --git a/build2/context.cxx b/build2/context.cxx
index 0f13f27..a8fe2df 100644
--- a/build2/context.cxx
+++ b/build2/context.cxx
@@ -231,8 +231,23 @@ namespace build2
vos.emplace_back (variable_override {var, *o, move (r.first)});
}
- // Enter builtin variables.
+ // Enter builtin variables and patterns.
//
+
+ // file.cxx:import()
+ //
+ // Note that the order is important (reverse application).
+ //
+ vp.insert_pattern<path> ("config.import.**", true);
+ vp.insert_pattern<abs_dir_path> ("config.import.*", true);
+
+ // module.cxx:load_module().
+ //
+ vp.insert_pattern<bool> ("**.loaded", false, variable_visibility::project);
+ vp.insert_pattern<bool> ("**.configured",
+ false,
+ variable_visibility::project);
+
var_src_root = &vp.insert<dir_path> ("src_root");
var_out_root = &vp.insert<dir_path> ("out_root");
var_src_base = &vp.insert<dir_path> ("src_base");
diff --git a/build2/dist/init.cxx b/build2/dist/init.cxx
index 9ee2d46..98f8a9a 100644
--- a/build2/dist/init.cxx
+++ b/build2/dist/init.cxx
@@ -97,7 +97,7 @@ namespace build2
// must be explicitly specified or we will complain if and when
// we try to dist.
//
- bool s (config::specified (rs, "config.dist"));
+ bool s (config::specified (rs, "dist"));
// Adjust module priority so that the config.dist.* values are saved at
// the end of config.build.
diff --git a/build2/file.cxx b/build2/file.cxx
index 72d2807..755ff0f 100644
--- a/build2/file.cxx
+++ b/build2/file.cxx
@@ -900,10 +900,11 @@ namespace build2
// config.import.<proj>
//
- // Note: overridable variable with path auto-completion.
- //
{
- const variable& var (vp.insert<abs_dir_path> (n, true));
+ // Note: pattern-typed in context.cxx:reset() as an overridable
+ // variable of type abs_dir_path (path auto-completion).
+ //
+ const variable& var (vp.insert (n));
if (auto l = iroot[var])
{
@@ -934,7 +935,10 @@ namespace build2
{
auto lookup = [&iroot, &vp, &loc] (string name) -> path
{
- const variable& var (vp.insert<path> (name, true));
+ // Note: pattern-typed in context.cxx:reset() as an overridable
+ // variable of type path.
+ //
+ const variable& var (vp.insert (move (name)));
path r;
if (auto l = iroot[var])
diff --git a/build2/install/init.cxx b/build2/install/init.cxx
index 122eb54..0966d82 100644
--- a/build2/install/init.cxx
+++ b/build2/install/init.cxx
@@ -215,7 +215,7 @@ namespace build2
{
using build2::path;
- bool s (config::specified (rs, "config.install"));
+ bool s (config::specified (rs, "install"));
// Adjust module priority so that the (numerous) config.install.*
// values are saved at the end of config.build.
diff --git a/build2/module.cxx b/build2/module.cxx
index 031ae45..6fc33fa 100644
--- a/build2/module.cxx
+++ b/build2/module.cxx
@@ -102,13 +102,11 @@ namespace build2
auto& vp (var_pool.rw (rs));
- const variable& lv (vp.insert<bool> (name + ".loaded",
- variable_visibility::project));
-
- const variable& cv (vp.insert<bool> (name + ".configured",
- variable_visibility::project));
- bs.assign (lv) = l;
- bs.assign (cv) = c;
+ // Note: pattern-typed in context.cxx:reset() as project-visibility
+ // variables of type bool.
+ //
+ bs.assign (vp.insert (name + ".loaded")) = l;
+ bs.assign (vp.insert (name + ".configured")) = c;
return l && c;
}
diff --git a/build2/variable b/build2/variable
index d31d20f..876ae2b 100644
--- a/build2/variable
+++ b/build2/variable
@@ -797,10 +797,13 @@ namespace build2
find (const string& name) const;
// Find existing or insert new (untyped, non-overridable, normal
- // visibility).
+ // visibility; but may be overridden by a pattern).
//
const variable&
- insert (string name);
+ insert (string name)
+ {
+ return insert (move (name), nullptr, nullptr, nullptr);
+ }
// Insert or override (type/visibility). Note that by default the
// variable is not overridable.
@@ -853,6 +856,37 @@ namespace build2
move (name), &value_traits<T>::value_type, &v, &overridable);
}
+ // Insert a variable pattern. Any variable that matches this pattern
+ // will have the specified type, visibility, and overridability.
+ //
+ // The pattern must be in the form [<prefix>.](*|**)[.<suffix>] where
+ // '*' matches single component stems (i.e., 'foo' but not 'foo.bar')
+ // and '**' matches single and multi-component stems. Note that only
+ // multi-component variables are considered for pattern matching (so
+ // just '*' won't match anything).
+ //
+ // Note that patterns are matched in the reverse order of insertion (as
+ // opposed to, say, more specific first) with the first match used. A
+ // newly inserted pattern is also applied retrospectively to all the
+ // existing variables that match.
+ //
+ public:
+ void
+ insert_pattern (const string& pattern,
+ const build2::value_type* type,
+ bool overridable,
+ variable_visibility);
+
+ template <typename T>
+ void
+ insert_pattern (const string& p,
+ bool overridable = false,
+ variable_visibility v = variable_visibility::normal)
+ {
+ insert_pattern (p, &value_traits<T>::value_type, overridable, v);
+ }
+
+ public:
void
clear () {map_.clear ();}
@@ -875,6 +909,12 @@ namespace build2
const variable_visibility* = nullptr,
const bool* overridable = nullptr);
+ void
+ update (variable&,
+ const build2::value_type*,
+ const variable_visibility*,
+ const bool*) const;
+
// Entities that can access bypassing the lock.
//
friend class scope;
@@ -883,6 +923,8 @@ namespace build2
public:
static const variable_pool& cinstance; // For var_pool initialization.
+ // Variable map.
+ //
private:
using key = butl::map_key<string>;
using map = std::unordered_map<key, variable>;
@@ -904,11 +946,32 @@ namespace build2
return r;
}
+ map map_;
+
+ // Patterns.
+ //
+ public:
+ struct pattern
+ {
+ string prefix;
+ string suffix;
+ bool multi; // Match multi-component stems.
+
+ const build2::value_type* type;
+ variable_visibility visibility;
+ bool overridable;
+ };
+
+ private:
+ vector<pattern> patterns_;
+
+ // Global pool flag.
+ //
+ private:
explicit
variable_pool (bool global): global_ (global) {}
bool global_;
- map map_;
};
extern const variable_pool& var_pool;
diff --git a/build2/variable.cxx b/build2/variable.cxx
index bbe08d0..8695623 100644
--- a/build2/variable.cxx
+++ b/build2/variable.cxx
@@ -906,23 +906,82 @@ namespace build2
// variable_pool
//
- const variable& variable_pool::
- insert (string n)
+ void variable_pool::
+ update (variable& var,
+ const build2::value_type* t,
+ const variable_visibility* v,
+ const bool* o) const
{
- assert (!global_ || phase == run_phase::load);
+ // Check overridability (all overrides, if any, should already have
+ // been entered (see context.cxx:reset()).
+ //
+ if (var.override != nullptr && (o == nullptr || !*o))
+ fail << "variable " << var.name << " cannot be overridden";
- // We are not overriding anything so skip the insert_() checks.
+ bool ut (t != nullptr && var.type != t);
+ bool uv (v != nullptr && var.visibility != *v);
+
+ // In the global pool existing variables can only be updated during
+ // serial load.
//
- auto p (
- insert (
- variable {move (n), nullptr, nullptr, variable_visibility::normal}));
+ assert (!global_ || !(ut || uv) || model_lock == nullptr);
- const variable& r (p.first->second);
+ // Update type?
+ //
+ if (ut)
+ {
+ assert (var.type == nullptr);
+ var.type = t;
+ }
- if (r.override != nullptr)
- fail << "variable " << r.name << " cannot be overridden";
+ // 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 (uv)
+ {
+ assert (var.visibility == variable_visibility::normal); // Default.
+ var.visibility = *v;
+ }
+ }
- return r;
+ static bool
+ match_pattern (const string& n, const string& p, const string& s, bool multi)
+ {
+ size_t nn (n.size ()), pn (p.size ()), sn (s.size ());
+
+ if (nn < pn + sn + 1)
+ return false;
+
+ if (pn != 0)
+ {
+ if (n.compare (0, pn, p) != 0)
+ return false;
+ }
+
+ if (sn != 0)
+ {
+ if (n.compare (nn - sn, sn, s) != 0)
+ return false;
+ }
+
+ // Make sure the stem is a single name unless instructed otherwise.
+ //
+ return multi || string::traits_type::find (n.c_str () + pn,
+ nn - pn - sn,
+ '.') == nullptr;
+ }
+
+ static inline void
+ merge_pattern (const variable_pool::pattern& p,
+ const build2::value_type*& t,
+ const variable_visibility*& v,
+ const bool*& o)
+ {
+ if (t == nullptr) t = p.type; else assert ( t == p.type);
+ if (v == nullptr) v = &p.visibility; else assert (*v == p.visibility);
+ if (o == nullptr) o = &p.overridable; else assert (*o == p.overridable);
}
const variable& variable_pool::
@@ -933,6 +992,20 @@ namespace build2
{
assert (!global_ || phase == run_phase::load);
+ // Apply pattern.
+ //
+ if (n.find ('.') != string::npos)
+ {
+ for (const pattern& p: reverse_iterate (patterns_))
+ {
+ if (match_pattern (n, p.prefix, p.suffix, p.multi))
+ {
+ merge_pattern (p, t, v, o);
+ break;
+ }
+ }
+ }
+
auto p (
insert (
variable {
@@ -941,61 +1014,63 @@ namespace build2
nullptr,
v != nullptr ? *v : variable_visibility::normal}));
- const variable& r (p.first->second);
+ variable& r (p.first->second);
if (!p.second)
{
- // Check overridability (all overrides, if any, should already have
- // been entered (see context.cxx:reset()).
- //
- if (r.override != nullptr && (o == nullptr || !*o))
- fail << "variable " << r.name << " cannot be overridden";
+ if (t != nullptr || v != nullptr || o != nullptr)
+ update (r, t, v, o); // Not changing the key.
+ else
+ if (r.override != nullptr)
+ fail << "variable " << r.name << " cannot be overridden";
+ }
- bool ut (t != nullptr && r.type != t);
- bool uv (v != nullptr && r.visibility != *v);
+ return r;
+ }
- // In the global pool existing variables can only be updated during
- // serial load.
- //
- /*
- @@ MT
+ void variable_pool::
+ insert_pattern (const string& p,
+ const build2::value_type* t,
+ bool o,
+ variable_visibility v)
+ {
+ size_t pn (p.size ());
- if (global_)
- {
- //assert (!(ut || uv) || model_lock == nullptr);
+ size_t w (p.find ('*'));
+ assert (w != string::npos);
- if (model_lock != nullptr)
- {
- if (ut)
- text << r.name << " type update during exclusive load";
+ bool multi (w + 1 != pn && p[w + 1] == '*');
- if (uv)
- text << r.name << " visibility update during exclusive load";
- }
- }
- */
+ // Extract prefix and suffix.
+ //
+ string pfx, sfx;
- // Update type?
- //
- if (ut)
- {
- assert (r.type == nullptr);
- const_cast<variable&> (r).type = t; // Not changing the key.
- }
+ if (w != 0)
+ {
+ assert (p[w - 1] == '.' && w != 1);
+ pfx.assign (p, 0, w);
+ }
- // 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 (uv)
- {
- assert (r.visibility == variable_visibility::normal); // Default.
- const_cast<variable&> (r).visibility = *v; // Not changing the key.
- }
+ w += multi ? 2 : 1; // First suffix character.
+ size_t sn (pn - w); // Suffix length.
+
+ if (sn != 0)
+ {
+ assert (p[w] == '.' && sn != 1);
+ sfx.assign (p, w, sn);
}
- return r;
+ // Apply retrospectively to existing variables.
+ //
+ for (auto& p: map_)
+ {
+ variable& var (p.second);
+
+ if (match_pattern (var.name, pfx, sfx, multi))
+ update (var, t, &v, &o); // Not changing the key.
+ }
+
+ patterns_.push_back (pattern {move (pfx), move (sfx), multi, t, v, o});
}
variable_pool variable_pool::instance (true);