aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build2/algorithm19
-rw-r--r--build2/algorithm.cxx30
-rw-r--r--build2/algorithm.ixx49
-rw-r--r--build2/b.cxx30
-rw-r--r--build2/bin/init.cxx34
-rw-r--r--build2/bin/rule8
-rw-r--r--build2/bin/rule.cxx16
-rw-r--r--build2/c/init.cxx26
-rw-r--r--build2/cc/common4
-rw-r--r--build2/cc/common.cxx10
-rw-r--r--build2/cc/compile14
-rw-r--r--build2/cc/compile.cxx48
-rw-r--r--build2/cc/init.cxx67
-rw-r--r--build2/cc/install4
-rw-r--r--build2/cc/install.cxx10
-rw-r--r--build2/cc/link4
-rw-r--r--build2/cc/link.cxx22
-rw-r--r--build2/cc/module.cxx8
-rw-r--r--build2/cli/init.cxx4
-rw-r--r--build2/cli/rule4
-rw-r--r--build2/cli/rule.cxx16
-rw-r--r--build2/config/init.cxx8
-rw-r--r--build2/config/operation.cxx37
-rw-r--r--build2/config/utility10
-rw-r--r--build2/config/utility.cxx19
-rw-r--r--build2/context33
-rw-r--r--build2/context.cxx60
-rw-r--r--build2/cxx/init.cxx6
-rw-r--r--build2/dist/init.cxx32
-rw-r--r--build2/dist/operation.cxx23
-rw-r--r--build2/dist/rule4
-rw-r--r--build2/dist/rule.cxx8
-rw-r--r--build2/file8
-rw-r--r--build2/file.cxx83
-rw-r--r--build2/file.ixx4
-rw-r--r--build2/install/init.cxx66
-rw-r--r--build2/install/rule10
-rw-r--r--build2/install/rule.cxx20
-rw-r--r--build2/install/utility18
-rw-r--r--build2/module10
-rw-r--r--build2/module.cxx17
-rw-r--r--build2/operation22
-rw-r--r--build2/operation.cxx50
-rw-r--r--build2/parser1
-rw-r--r--build2/parser.cxx25
-rw-r--r--build2/pkgconfig/init.cxx4
-rw-r--r--build2/rule23
-rw-r--r--build2/rule.cxx22
-rw-r--r--build2/scope59
-rw-r--r--build2/target57
-rw-r--r--build2/target.cxx11
-rw-r--r--build2/target.ixx4
-rw-r--r--build2/target.txx4
-rw-r--r--build2/test/init.cxx2
-rw-r--r--build2/test/rule6
-rw-r--r--build2/test/rule.cxx34
-rw-r--r--build2/types56
-rw-r--r--build2/variable92
-rw-r--r--build2/variable.cxx25
-rw-r--r--build2/variable.ixx30
-rw-r--r--unit-tests/function/buildfile3
-rw-r--r--unit-tests/function/driver.cxx4
-rw-r--r--unit-tests/test/script/parser/driver.cxx9
63 files changed, 851 insertions, 595 deletions
diff --git a/build2/algorithm b/build2/algorithm
index ad52fe1..552ea7f 100644
--- a/build2/algorithm
+++ b/build2/algorithm
@@ -72,7 +72,7 @@ namespace build2
// indicate this.
//
void
- match (action, target&);
+ match (slock&, action, target&);
// Note that calling this function only makes sense if the target itself
// doesn't have its own dependents.
@@ -84,7 +84,7 @@ namespace build2
// detection. Note that this function does not touch the dependents count.
//
void
- match_only (action, target&);
+ match_only (slock&, action, target&);
// Match a "delegate rule" from withing another rules' apply() function
// skipping recursive matches (thus the third argument). Return recipe and
@@ -93,20 +93,20 @@ namespace build2
// execute_delegate().
//
pair<recipe, action>
- match_delegate (action, target&, const rule&);
+ match_delegate (slock&, action, target&, const rule&);
// The standard prerequisite search and match implementations. They call
// search_and_match_*() versions below passing non-empty directory for
// the clean operation.
//
void
- search_and_match_prerequisites (action, target&);
+ search_and_match_prerequisites (slock&, action, target&);
// If we are cleaning, this function doesn't go into group members,
// as an optimization (the group should clean everything up).
//
void
- search_and_match_prerequisite_members (action, target&);
+ search_and_match_prerequisite_members (slock&, action, target&);
// The actual prerequisite search and match implementations. They call
// search() and then match() for each prerequisite in a loop. If this
@@ -117,10 +117,11 @@ namespace build2
// match) prerequisites that are not in the same or its subdirectory.
//
void
- search_and_match_prerequisites (action, target&, const dir_path&);
+ search_and_match_prerequisites (slock&, action, target&, const dir_path&);
void
- search_and_match_prerequisite_members (action, target&, const dir_path&);
+ search_and_match_prerequisite_members (
+ slock&, action, target&, const dir_path&);
// Unless already available, match, and, if necessary, execute the group
// in order to obtain its members list. Note that even after that the
@@ -128,7 +129,7 @@ namespace build2
// fallback rule matched).
//
group_view
- resolve_group_members (action, target&);
+ resolve_group_members (slock&, action, target&);
// Inject dependency on the target's directory fsdir{}, unless it is in the
// src tree or is outside of any project (say, for example, an installation
@@ -138,7 +139,7 @@ namespace build2
// rule's apply() function.
//
fsdir*
- inject_fsdir (action, target&, bool parent = true);
+ inject_fsdir (slock&, action, target&, bool parent = true);
// Execute the action on target, assuming a rule has been matched
// and the recipe for this action has been set. This is the default
diff --git a/build2/algorithm.cxx b/build2/algorithm.cxx
index f3bb8a0..6c812f3 100644
--- a/build2/algorithm.cxx
+++ b/build2/algorithm.cxx
@@ -53,7 +53,7 @@ namespace build2
}
pair<const rule*, match_result>
- match_impl (action a, target& t, bool apply, const rule* skip)
+ match_impl (slock& ml, action a, target& t, bool apply, const rule* skip)
{
pair<const rule*, match_result> r (nullptr, false);
@@ -153,7 +153,7 @@ namespace build2
<< diag_do (ra, t);
}));
- if (!(m = ru.match (ra, t, hint)))
+ if (!(m = ru.match (ml, ra, t, hint)))
continue;
if (!m.recipe_action.valid ())
@@ -180,7 +180,7 @@ namespace build2
<< diag_do (ra, t);
}));
- if (!ru1.match (ra, t, hint))
+ if (!ru1.match (ml, ra, t, hint))
continue;
}
@@ -212,7 +212,7 @@ namespace build2
// @@ We could also allow the rule to change the recipe
// action in apply(). Could be useful with delegates.
//
- t.recipe (ra, ru.apply (ra, t));
+ t.recipe (ra, ru.apply (ml, ra, t));
}
else
{
@@ -240,7 +240,7 @@ namespace build2
}
group_view
- resolve_group_members_impl (action a, target& g)
+ resolve_group_members_impl (slock& ml, action a, target& g)
{
group_view r;
@@ -249,7 +249,7 @@ namespace build2
//
if (!g.recipe (a))
{
- auto rp (match_impl (a, g, false));
+ auto rp (match_impl (ml, a, g, false));
r = g.group_members (a);
if (r.members != nullptr)
@@ -259,7 +259,8 @@ namespace build2
// phase.
//
const match_result& mr (rp.second);
- g.recipe (mr.recipe_action, rp.first->apply (mr.recipe_action, g));
+ g.recipe (mr.recipe_action,
+ rp.first->apply (ml, mr.recipe_action, g));
}
// Note that we use execute_direct() rather than execute() here to
@@ -275,7 +276,7 @@ namespace build2
}
void
- search_and_match_prerequisites (action a, target& t, scope* s)
+ search_and_match_prerequisites (slock& ml, action a, target& t, scope* s)
{
for (prerequisite& p: group_prerequisites (t))
{
@@ -283,29 +284,30 @@ namespace build2
if (s == nullptr || pt.in (*s))
{
- match (a, pt);
+ match (ml, a, pt);
t.prerequisite_targets.push_back (&pt);
}
}
}
void
- search_and_match_prerequisite_members (action a, target& t, scope* s)
+ search_and_match_prerequisite_members (
+ slock& ml, action a, target& t, scope* s)
{
- for (prerequisite_member p: group_prerequisite_members (a, t))
+ for (prerequisite_member p: group_prerequisite_members (ml, a, t))
{
target& pt (p.search ());
if (s == nullptr || pt.in (*s))
{
- match (a, pt);
+ match (ml, a, pt);
t.prerequisite_targets.push_back (&pt);
}
}
}
fsdir*
- inject_fsdir (action a, target& t, bool parent)
+ inject_fsdir (slock& ml, action a, target& t, bool parent)
{
tracer trace ("inject_fsdir");
@@ -334,7 +336,7 @@ namespace build2
// Target is in the out tree, so out directory is empty.
//
fsdir* r (&search<fsdir> (d, dir_path (), string (), nullopt, nullptr));
- match (a, *r);
+ match (ml, a, *r);
t.prerequisite_targets.emplace_back (r);
return r;
}
diff --git a/build2/algorithm.ixx b/build2/algorithm.ixx
index 90a0789..7da189d 100644
--- a/build2/algorithm.ixx
+++ b/build2/algorithm.ixx
@@ -51,14 +51,16 @@ namespace build2
}
pair<const rule*, match_result>
- match_impl (action, target&, bool apply, const rule* skip = nullptr);
+ match_impl (slock&, action, target&, bool apply, const rule* skip = nullptr);
inline void
- match (action a, target& t)
+ match (slock& ml, action a, target& t)
{
if (!t.recipe (a))
- match_impl (a, t, true);
+ match_impl (ml, a, t, true);
+ //@@ MT
+ //
t.dependents++;
dependency_count++;
@@ -70,74 +72,79 @@ namespace build2
{
// text << "U " << t << ": " << t.dependents << " " << dependency_count;
+ //@@ MT
+ //
assert (t.dependents != 0 && dependency_count != 0);
t.dependents--;
dependency_count--;
}
inline void
- match_only (action a, target& t)
+ match_only (slock& ml, action a, target& t)
{
if (!t.recipe (a))
- match_impl (a, t, false);
+ match_impl (ml, a, t, false);
}
inline pair<recipe, action>
- match_delegate (action a, target& t, const rule& r)
+ match_delegate (slock& ml, action a, target& t, const rule& r)
{
- auto rp (match_impl (a, t, false, &r));
+ auto rp (match_impl (ml, a, t, false, &r));
const match_result& mr (rp.second);
- return make_pair (rp.first->apply (mr.recipe_action, t), mr.recipe_action);
+ return make_pair (rp.first->apply (ml, mr.recipe_action, t),
+ mr.recipe_action);
}
group_view
- resolve_group_members_impl (action, target&);
+ resolve_group_members_impl (slock& ml, action, target&);
inline group_view
- resolve_group_members (action a, target& g)
+ resolve_group_members (slock& ml, action a, target& g)
{
group_view r (g.group_members (a));
- return r.members != nullptr ? r : resolve_group_members_impl (a, g);
+ return r.members != nullptr ? r : resolve_group_members_impl (ml, a, g);
}
void
- search_and_match_prerequisites (action, target&, scope*);
+ search_and_match_prerequisites (slock&, action, target&, scope*);
void
- search_and_match_prerequisite_members (action, target&, scope*);
+ search_and_match_prerequisite_members (slock&, action, target&, scope*);
inline void
- search_and_match_prerequisites (action a, target& t)
+ search_and_match_prerequisites (slock& ml, action a, target& t)
{
search_and_match_prerequisites (
+ ml,
a,
t,
(a.operation () != clean_id ? nullptr : &t.root_scope ()));
}
inline void
- search_and_match_prerequisite_members (action a, target& t)
+ search_and_match_prerequisite_members (slock& ml, action a, target& t)
{
if (a.operation () != clean_id)
- search_and_match_prerequisite_members (a, t, nullptr);
+ search_and_match_prerequisite_members (ml, a, t, nullptr);
else
// Note that here we don't iterate over members even for see-
// through groups since the group target should clean eveything
// up. A bit of an optimization.
//
- search_and_match_prerequisites (a, t, &t.root_scope ());
+ search_and_match_prerequisites (ml, a, t, &t.root_scope ());
}
inline void
- search_and_match_prerequisites (action a, target& t, scope& s)
+ search_and_match_prerequisites (slock& ml, action a, target& t, scope& s)
{
- search_and_match_prerequisites (a, t, &s);
+ search_and_match_prerequisites (ml, a, t, &s);
}
inline void
- search_and_match_prerequisite_members (action a, target& t, scope& s)
+ search_and_match_prerequisite_members (
+ slock& ml, action a, target& t, scope& s)
{
- search_and_match_prerequisite_members (a, t, &s);
+ search_and_match_prerequisite_members (ml, a, t, &s);
}
target_state
diff --git a/build2/b.cxx b/build2/b.cxx
index b06459b..54defb3 100644
--- a/build2/b.cxx
+++ b/build2/b.cxx
@@ -96,6 +96,8 @@ main (int argc, char* argv[])
<< system_error (errno, system_category ()); // Sanitize.
#endif
+ ulock ml (model);
+
// Parse the command line. We want to be able to specify options, vars,
// and buildspecs in any order (it is really handy to just add -v at the
// end of the command line).
@@ -362,7 +364,7 @@ main (int argc, char* argv[])
//
if (dirty)
{
- var_ovs = reset (cmd_vars);
+ var_ovs = reset (ml, cmd_vars);
dirty = false;
}
@@ -572,7 +574,7 @@ main (int argc, char* argv[])
// See if the bootstrap process set/changed src_root.
//
- value& v (rs.assign ("src_root"));
+ value& v (rs.assign (var_src_root));
if (v)
{
@@ -650,7 +652,7 @@ main (int argc, char* argv[])
// Note that the subprojects variable has already been processed
// and converted to a map by the bootstrap_src() call above.
//
- if (auto l = rs.vars["subprojects"])
+ if (auto l = rs.vars[var_subprojects])
{
for (const auto& p: cast<subprojects> (l))
{
@@ -874,7 +876,7 @@ main (int argc, char* argv[])
trace << " out_root: " << out_root;
trace << " src_root: " << src_root;
- if (auto l = rs.vars["amalgamation"])
+ if (auto l = rs.vars[var_amalgamation])
trace << " amalgamat: " << cast<dir_path> (l);
}
@@ -973,7 +975,7 @@ main (int argc, char* argv[])
// Load the buildfile.
//
- mif->load (ts.buildfile, rs, ts.out_base, ts.src_base, l);
+ mif->load (ml, rs, ts.buildfile, 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
@@ -1007,7 +1009,11 @@ main (int argc, char* argv[])
? out_src (d, rs)
: dir_path ());
- mif->search (rs, target_key {ti, &d, &out, &tn.value, e}, l, tgs);
+ mif->search (ml,
+ rs,
+ target_key {ti, &d, &out, &tn.value, e},
+ l,
+ tgs);
}
} // target
@@ -1026,8 +1032,8 @@ main (int argc, char* argv[])
action a (mid, pre_oid, oid);
- mif->match (a, tgs);
- mif->execute (a, tgs, true); // Run quiet.
+ mif->match (ml, a, tgs);
+ mif->execute (ml, a, tgs, true); // Run quiet.
if (mif->operation_post != nullptr)
mif->operation_post (pre_oid);
@@ -1041,8 +1047,8 @@ main (int argc, char* argv[])
action a (mid, oid, 0);
- if (mif->match != nullptr) mif->match (a, tgs);
- if (mif->execute != nullptr) mif->execute (a, tgs, verb == 0);
+ if (mif->match != nullptr) mif->match (ml, a, tgs);
+ if (mif->execute != nullptr) mif->execute (ml, a, tgs, verb == 0);
if (post_oid != 0)
{
@@ -1057,8 +1063,8 @@ main (int argc, char* argv[])
action a (mid, post_oid, oid);
- mif->match (a, tgs);
- mif->execute (a, tgs, true); // Run quiet.
+ mif->match (ml, a, tgs);
+ mif->execute (ml, a, tgs, true); // Run quiet.
if (mif->operation_post != nullptr)
mif->operation_post (post_oid);
diff --git a/build2/bin/init.cxx b/build2/bin/init.cxx
index 913c887..6bd2636 100644
--- a/build2/bin/init.cxx
+++ b/build2/bin/init.cxx
@@ -49,7 +49,7 @@ namespace build2
//
if (first)
{
- auto& v (var_pool);
+ auto& v (var_pool.rw (r));
// Note: some overridable, some not.
//
@@ -325,7 +325,7 @@ namespace build2
// Load bin.config.
//
if (!cast_false<bool> (b["bin.config.loaded"]))
- load_module ("bin.config", r, b, loc, false, hints);
+ load_module (r, b, "bin.config", loc, false, hints);
// Cache some config values we will be needing below.
//
@@ -427,13 +427,13 @@ namespace build2
// Make sure bin.config is loaded.
//
if (!cast_false<bool> (b["bin.config.loaded"]))
- load_module ("bin.config", r, b, loc, false, hints);
+ load_module (r, b, "bin.config", loc, false, hints);
// Enter configuration variables.
//
if (first)
{
- auto& v (var_pool);
+ auto& v (var_pool.rw (r));
v.insert<process_path> ("bin.rc.path");
v.insert<process_path> ("bin.ranlib.path");
@@ -528,7 +528,7 @@ namespace build2
if (ranlib != nullptr)
{
r.assign<process_path> ("bin.ranlib.path") = move (ari.ranlib_path);
- r.assign<string> ("bin.ranlib.id") = move (ari.ranlib_id);
+ r.assign<string> ("bin.ranlib.id") = move (ari.ranlib_id);
r.assign<string> ("bin.ranlib.signature") =
move (ari.ranlib_signature);
r.assign<string> ("bin.ranlib.checksum") =
@@ -554,10 +554,10 @@ namespace build2
// Make sure the bin core and ar.config are loaded.
//
if (!cast_false<bool> (b["bin.loaded"]))
- load_module ("bin", r, b, loc, false, hints);
+ load_module (r, b, "bin", loc, false, hints);
if (!cast_false<bool> (b["bin.ar.config.loaded"]))
- load_module ("bin.ar.config", r, b, loc, false, hints);
+ load_module (r, b, "bin.ar.config", loc, false, hints);
return true;
}
@@ -577,13 +577,13 @@ namespace build2
// Make sure bin.config is loaded.
//
if (!cast_false<bool> (b["bin.config.loaded"]))
- load_module ("bin.config", r, b, loc, false, hints);
+ load_module (r, b, "bin.config", loc, false, hints);
// Enter configuration variables.
//
if (first)
{
- auto& v (var_pool);
+ auto& v (var_pool.rw (r));
v.insert<process_path> ("bin.ld.path");
v.insert<path> ("config.bin.ld", true);
@@ -652,10 +652,10 @@ namespace build2
// Make sure the bin core and ld.config are loaded.
//
if (!cast_false<bool> (b["bin.loaded"]))
- load_module ("bin", r, b, loc, false, hints);
+ load_module (r, b, "bin", loc, false, hints);
if (!cast_false<bool> (b["bin.ld.config.loaded"]))
- load_module ("bin.ld.config", r, b, loc, false, hints);
+ load_module (r, b, "bin.ld.config", loc, false, hints);
const string& lid (cast<string> (r["bin.ld.id"]));
@@ -666,8 +666,8 @@ namespace build2
if (lid == "msvc")
{
const target_type& pdb (b.derive_target_type<file> ("pdb").first);
- install_path (pdb, b, dir_path ("bin")); // Goes to install.bin
- install_mode (pdb, b, "644"); // But not executable.
+ install_path (b, pdb, dir_path ("bin")); // Goes to install.bin
+ install_mode (b, pdb, "644"); // But not executable.
}
return true;
@@ -688,13 +688,13 @@ namespace build2
// Make sure bin.config is loaded.
//
if (!cast_false<bool> (b["bin.config.loaded"]))
- load_module ("bin.config", r, b, loc, false, hints);
+ load_module (r, b, "bin.config", loc, false, hints);
// Enter configuration variables.
//
if (first)
{
- auto& v (var_pool);
+ auto& v (var_pool.rw (r));
v.insert<process_path> ("bin.rc.path");
v.insert<path> ("config.bin.rc", true);
@@ -763,10 +763,10 @@ namespace build2
// Make sure the bin core and rc.config are loaded.
//
if (!cast_false<bool> (b["bin.loaded"]))
- load_module ("bin", r, b, loc, false, hints);
+ load_module (r, b, "bin", loc, false, hints);
if (!cast_false<bool> (b["bin.rc.config.loaded"]))
- load_module ("bin.rc.config", r, b, loc, false, hints);
+ load_module (r, b, "bin.rc.config", loc, false, hints);
return true;
}
diff --git a/build2/bin/rule b/build2/bin/rule
index 5031be1..c903990 100644
--- a/build2/bin/rule
+++ b/build2/bin/rule
@@ -20,10 +20,10 @@ namespace build2
obj_rule () {}
virtual match_result
- match (action, target&, const string& hint) const override;
+ match (slock&, action, target&, const string& hint) const override;
virtual recipe
- apply (action, target&) const override;
+ apply (slock&, action, target&) const override;
};
class lib_rule: public rule
@@ -32,10 +32,10 @@ namespace build2
lib_rule () {}
virtual match_result
- match (action, target&, const string& hint) const override;
+ match (slock&, action, target&, const string& hint) const override;
virtual recipe
- apply (action, target&) const override;
+ apply (slock&, action, target&) const override;
static target_state
perform (action, target&);
diff --git a/build2/bin/rule.cxx b/build2/bin/rule.cxx
index ede2c85..648c8b6 100644
--- a/build2/bin/rule.cxx
+++ b/build2/bin/rule.cxx
@@ -20,7 +20,7 @@ namespace build2
// obj
//
match_result obj_rule::
- match (action a, target& t, const string&) const
+ match (slock&, action a, target& t, const string&) const
{
fail << diag_doing (a, t) << " target group" <<
info << "explicitly select obje{}, obja{}, or objs{} member";
@@ -29,7 +29,7 @@ namespace build2
}
recipe obj_rule::
- apply (action, target&) const {return empty_recipe;}
+ apply (slock&, action, target&) const {return empty_recipe;}
// lib
//
@@ -46,7 +46,7 @@ namespace build2
"insufficient space");
match_result lib_rule::
- match (action act, target& xt, const string&) const
+ match (slock& ml, action act, target& xt, const string&) const
{
lib& t (static_cast<lib&> (xt));
@@ -77,7 +77,7 @@ namespace build2
if (t.a == nullptr)
t.a = &search<liba> (t.dir, t.out, t.name, nullopt, nullptr);
- match_only (act, *t.a);
+ match_only (ml, act, *t.a);
}
if (s)
@@ -85,7 +85,7 @@ namespace build2
if (t.s == nullptr)
t.s = &search<libs> (t.dir, t.out, t.name, nullopt, nullptr);
- match_only (act, *t.s);
+ match_only (ml, act, *t.s);
}
t.data (match_data {type}); // Save in the target's auxilary storage.
@@ -102,7 +102,7 @@ namespace build2
}
recipe lib_rule::
- apply (action act, target& xt) const
+ apply (slock& ml, action act, target& xt) const
{
lib& t (static_cast<lib&> (xt));
@@ -115,10 +115,10 @@ namespace build2
// Now we do full match.
//
if (a)
- build2::match (act, *t.a);
+ build2::match (ml, act, *t.a);
if (s)
- build2::match (act, *t.s);
+ build2::match (ml, act, *t.s);
return &perform;
}
diff --git a/build2/c/init.cxx b/build2/c/init.cxx
index f33ba9e..c5b8ac8 100644
--- a/build2/c/init.cxx
+++ b/build2/c/init.cxx
@@ -112,11 +112,11 @@ namespace build2
// Load cc.core.vars so that we can cache all the cc.* variables.
//
if (!cast_false<bool> (rs["cc.core.vars.loaded"]))
- load_module ("cc.core.vars", rs, rs, loc);
+ load_module (rs, rs, "cc.core.vars", loc);
// Enter all the variables and initialize the module data.
//
- auto& v (var_pool);
+ auto& v (var_pool.rw (rs));
cc::config_data d {
cc::lang::c,
@@ -142,23 +142,23 @@ namespace build2
v.insert<strings> ("c.loptions"),
v.insert<strings> ("c.libs"),
- v["cc.poptions"],
- v["cc.coptions"],
- v["cc.loptions"],
- v["cc.libs"],
+ v.insert ("cc.poptions"),
+ v.insert ("cc.coptions"),
+ v.insert ("cc.loptions"),
+ v.insert ("cc.libs"),
v.insert<strings> ("c.export.poptions"),
v.insert<strings> ("c.export.coptions"),
v.insert<strings> ("c.export.loptions"),
v.insert<vector<name>> ("c.export.libs"),
- v["cc.export.poptions"],
- v["cc.export.coptions"],
- v["cc.export.loptions"],
- v["cc.export.libs"],
+ v.insert ("cc.export.poptions"),
+ v.insert ("cc.export.coptions"),
+ v.insert ("cc.export.loptions"),
+ v.insert ("cc.export.libs"),
- v["cc.type"],
- v["cc.system"],
+ v.insert ("cc.type"),
+ v.insert ("cc.system"),
v.insert<string> ("c.std", variable_visibility::project),
@@ -224,7 +224,7 @@ namespace build2
// Load c.config.
//
if (!cast_false<bool> (rs["c.config.loaded"]))
- load_module ("c.config", rs, rs, loc, false, hints);
+ load_module (rs, rs, "c.config", loc, false, hints);
config_module& cm (*rs.modules.lookup<config_module> ("c.config"));
diff --git a/build2/cc/common b/build2/cc/common
index f0476f9..c11e733 100644
--- a/build2/cc/common
+++ b/build2/cc/common
@@ -210,8 +210,8 @@ namespace build2
private:
file&
- resolve_library (name,
- scope&,
+ resolve_library (scope&,
+ name,
lorder,
const dir_paths&,
optional<dir_paths>&) const;
diff --git a/build2/cc/common.cxx b/build2/cc/common.cxx
index 6429a38..b941e6e 100644
--- a/build2/cc/common.cxx
+++ b/build2/cc/common.cxx
@@ -4,7 +4,7 @@
#include <build2/cc/common>
-#include <build2/file> // import()
+#include <build2/file> // import()
#include <build2/scope>
#include <build2/context>
#include <build2/variable>
@@ -287,7 +287,7 @@ namespace build2
if (sysd == nullptr) find_sysd ();
if (!lo) find_lo ();
- file& t (resolve_library (n, bs, *lo, *sysd, usrd));
+ file& t (resolve_library (bs, n, *lo, *sysd, usrd));
if (proc_lib)
{
@@ -386,8 +386,8 @@ namespace build2
// that's the only way to guarantee it will be up-to-date.
//
file& common::
- resolve_library (name n,
- scope& s,
+ resolve_library (scope& s,
+ name n,
lorder lo,
const dir_paths& sysd,
optional<dir_paths>& usrd) const
@@ -768,7 +768,7 @@ namespace build2
const char* bl (lt.a != nullptr
? (lt.s != nullptr ? "both" : "static")
: "shared");
- lt.assign ("bin.lib") = bl;
+ lt.assign (var_pool["bin.lib"]) = bl;
target* r (l ? &lt : (p.is_a<liba> () ? static_cast<target*> (a) : s));
diff --git a/build2/cc/compile b/build2/cc/compile
index 67897f2..11b3919 100644
--- a/build2/cc/compile
+++ b/build2/cc/compile
@@ -27,10 +27,10 @@ namespace build2
compile (data&&);
virtual match_result
- match (action, target&, const string& hint) const override;
+ match (slock&, action, target&, const string& hint) const override;
virtual recipe
- apply (action, target&) const override;
+ apply (slock&, action, target&) const override;
target_state
perform_update (action, target&) const;
@@ -40,10 +40,10 @@ namespace build2
private:
void
- append_lib_options (cstrings&, target&, scope&, lorder) const;
+ append_lib_options (scope&, cstrings&, target&, lorder) const;
void
- hash_lib_options (sha256&, target&, scope&, lorder) const;
+ hash_lib_options (scope&, sha256&, target&, lorder) const;
// Mapping of include prefixes (e.g., foo in <foo/bar>) for auto-
// generated headers to directories where they will be generated.
@@ -61,10 +61,10 @@ namespace build2
append_prefixes (prefix_map&, target&, const variable&) const;
void
- append_lib_prefixes (prefix_map&, target&, scope&, lorder) const;
+ append_lib_prefixes (scope&, prefix_map&, target&, lorder) const;
prefix_map
- build_prefix_map (target&, scope&, lorder) const;
+ build_prefix_map (scope&, target&, lorder) const;
// Reverse-lookup target type from extension.
//
@@ -74,7 +74,7 @@ namespace build2
// Header dependency injection.
//
void
- inject (action, target&, lorder, file&, depdb&) const;
+ inject (slock&, action, target&, lorder, file&, depdb&) const;
private:
const string rule_id;
diff --git a/build2/cc/compile.cxx b/build2/cc/compile.cxx
index f837765..bc35272 100644
--- a/build2/cc/compile.cxx
+++ b/build2/cc/compile.cxx
@@ -44,7 +44,7 @@ namespace build2
"insufficient space");
match_result compile::
- match (action a, target& t, const string&) const
+ match (slock& ml, action a, target& t, const string&) const
{
tracer trace (x, "compile::match");
@@ -58,7 +58,8 @@ namespace build2
// file specified for an obj*{} member overrides the one specified for
// the group. Also "see through" groups.
//
- for (prerequisite_member p: reverse_group_prerequisite_members (a, t))
+ for (prerequisite_member p:
+ reverse_group_prerequisite_members (ml, a, t))
{
if (p.is_a (x_src))
{
@@ -75,7 +76,7 @@ namespace build2
// (first one is cc.export.*) recursively, prerequisite libraries first.
//
void compile::
- append_lib_options (cstrings& args, target& t, scope& bs, lorder lo) const
+ append_lib_options (scope& bs, cstrings& args, target& t, lorder lo) const
{
auto opt = [&args, this] (file& l, const string& t, bool com, bool exp)
{
@@ -116,7 +117,7 @@ namespace build2
}
void compile::
- hash_lib_options (sha256& cs, target& t, scope& bs, lorder lo) const
+ hash_lib_options (scope& bs, sha256& cs, target& t, lorder lo) const
{
auto opt = [&cs, this] (file& l, const string& t, bool com, bool exp)
{
@@ -155,7 +156,7 @@ namespace build2
// recursively, prerequisite libraries first.
//
void compile::
- append_lib_prefixes (prefix_map& m, target& t, scope& bs, lorder lo) const
+ append_lib_prefixes (scope& bs, prefix_map& m, target& t, lorder lo) const
{
auto opt = [&m, this] (file& l, const string& t, bool com, bool exp)
{
@@ -191,7 +192,7 @@ namespace build2
}
recipe compile::
- apply (action a, target& xt) const
+ apply (slock& ml, action a, target& xt) const
{
tracer trace (x, "compile::apply");
@@ -250,7 +251,7 @@ namespace build2
// Inject dependency on the output directory.
//
- fsdir* dir (inject_fsdir (a, t));
+ fsdir* dir (inject_fsdir (ml, a, t));
// Search and match all the existing prerequisites. The injection code
// takes care of the ones it is adding.
@@ -260,7 +261,7 @@ namespace build2
//
optional<dir_paths> usr_lib_dirs; // Extract lazily.
- for (prerequisite_member p: group_prerequisite_members (a, t))
+ for (prerequisite_member p: group_prerequisite_members (ml, a, t))
{
// A dependency on a library is there so that we can get its
// *.export.poptions. In particular, making sure it is executed before
@@ -281,7 +282,7 @@ namespace build2
search_library (
sys_lib_dirs, usr_lib_dirs, p.prerequisite) == nullptr)
{
- match_only (a, p.search ());
+ match_only (ml, a, p.search ());
}
}
@@ -293,7 +294,7 @@ namespace build2
if (a.operation () == clean_id && !pt.dir.sub (rs.out_path ()))
continue;
- build2::match (a, pt);
+ build2::match (ml, a, pt);
t.prerequisite_targets.push_back (&pt);
}
@@ -351,7 +352,7 @@ namespace build2
// Hash *.export.poptions from prerequisite libraries.
//
- hash_lib_options (cs, t, bs, lo);
+ hash_lib_options (bs, cs, t, lo);
// Extra system header dirs (last).
//
@@ -385,7 +386,7 @@ namespace build2
if (dd.writing () || dd.mtime () > t.mtime ())
t.mtime (timestamp_nonexistent);
- inject (a, t, lo, src, dd);
+ inject (ml, a, t, lo, src, dd);
dd.close ();
}
@@ -525,7 +526,7 @@ namespace build2
}
auto compile::
- build_prefix_map (target& t, scope& bs, lorder lo) const -> prefix_map
+ build_prefix_map (scope& bs, target& t, lorder lo) const -> prefix_map
{
prefix_map m;
@@ -536,7 +537,7 @@ namespace build2
// Then process the include directories from prerequisite libraries.
//
- append_lib_prefixes (m, t, bs, lo);
+ append_lib_prefixes (bs, m, t, lo);
return m;
}
@@ -715,7 +716,12 @@ namespace build2
}
void compile::
- inject (action a, target& t, lorder lo, file& src, depdb& dd) const
+ inject (slock& ml,
+ action a,
+ target& t,
+ lorder lo,
+ file& src,
+ depdb& dd) const
{
tracer trace (x, "compile::inject");
@@ -739,14 +745,14 @@ namespace build2
const process_path* xc (nullptr);
cstrings args;
- auto init_args = [&t, lo, &src, &rs, &bs, &xc, &args, this] ()
+ auto init_args = [&ml, &t, lo, &src, &rs, &bs, &xc, &args, this] ()
{
xc = &cast<process_path> (rs[x_path]);
args.push_back (xc->recall_string ());
// Add *.export.poptions from prerequisite libraries.
//
- append_lib_options (args, t, bs, lo);
+ append_lib_options (bs, args, t, lo);
append_options (args, t, c_poptions);
append_options (args, t, x_poptions);
@@ -895,7 +901,7 @@ namespace build2
// from the depdb cache or from the compiler run. Return whether the
// extraction process should be restarted.
//
- auto add = [&trace, &update, &pm, a, &t, lo, &dd, &bs, this]
+ auto add = [&trace, &ml, &update, &pm, a, &t, lo, &dd, &bs, this]
(path f, bool cache) -> bool
{
// Find or maybe insert the target.
@@ -991,7 +997,7 @@ namespace build2
// then we would have failed below.
//
if (pm.empty ())
- pm = build_prefix_map (t, bs, lo);
+ pm = build_prefix_map (bs, t, lo);
// First try the whole file. Then just the directory.
//
@@ -1068,7 +1074,7 @@ namespace build2
// Match to a rule.
//
- build2::match (a, *pt);
+ build2::match (ml, a, *pt);
// Update.
//
@@ -1417,7 +1423,7 @@ namespace build2
// Add *.export.poptions from prerequisite libraries.
//
- append_lib_options (args, t, bs, lo);
+ append_lib_options (bs, args, t, lo);
// Extra system header dirs (last).
//
diff --git a/build2/cc/init.cxx b/build2/cc/init.cxx
index a162daa..ebe7653 100644
--- a/build2/cc/init.cxx
+++ b/build2/cc/init.cxx
@@ -35,7 +35,7 @@ namespace build2
// Enter variables. Note: some overridable, some not.
//
- auto& v (var_pool);
+ auto& v (var_pool.rw (r));
v.insert<strings> ("config.cc.poptions", true);
v.insert<strings> ("config.cc.coptions", true);
@@ -90,10 +90,12 @@ namespace build2
assert (first);
+ auto& vp (var_pool.rw (rs));
+
// Load cc.core.vars.
//
if (!cast_false<bool> (rs["cc.core.vars.loaded"]))
- load_module ("cc.core.vars", rs, rs, loc);
+ load_module (rs, rs, "cc.core.vars", loc);
// Configure.
//
@@ -169,14 +171,20 @@ namespace build2
variable_map h;
if (first)
{
- h.assign ("config.bin.target") =
- cast<target_triplet> (rs["cc.target"]).string ();
+ // Note that these variables have not yet been registered (we don't
+ // yet have the "bin.vars" module).
+ //
+ const variable& t (vp.insert ("config.bin.target"));
+ h.assign (t) = cast<target_triplet> (rs["cc.target"]).string ();
if (auto l = hints["config.bin.pattern"])
- h.assign ("config.bin.pattern") = cast<string> (l);
+ {
+ const variable& p (vp.insert ("config.bin.pattern"));
+ h.assign (p) = cast<string> (l);
+ }
}
- load_module ("bin.config", rs, rs, loc, false, h);
+ load_module (rs, rs, "bin.config", loc, false, h);
}
// Verify bin's target matches ours (we do it even if we loaded it
@@ -205,20 +213,20 @@ namespace build2
if (cast<string> (l) != "shared")
{
if (!cast_false<bool> (rs["bin.ar.config.loaded"]))
- load_module ("bin.ar.config", rs, rs, loc);
+ load_module (rs, rs, "bin.ar.config", loc);
}
}
if (cid == "msvc")
{
if (!cast_false<bool> (rs["bin.ld.config.loaded"]))
- load_module ("bin.ld.config", rs, rs, loc);
+ load_module (rs, rs, "bin.ld.config", loc);
}
if (tsys == "mingw32")
{
if (!cast_false<bool> (rs["bin.rc.config.loaded"]))
- load_module ("bin.rc.config", rs, rs, loc);
+ load_module (rs, rs, "bin.rc.config", loc);
}
// Load (optionally) the pkgconfig.config module.
@@ -233,10 +241,13 @@ namespace build2
// Prepare configuration hints.
//
variable_map h;
- h.assign ("config.pkgconfig.target") =
- cast<target_triplet> (rs["cc.target"]);
- load_module ("pkgconfig.config", rs, rs, loc, true, h);
+ // Note that this variable has not yet been registered.
+ //
+ const variable& t (vp.insert ("config.pkgconfig.target"));
+ h.assign (t) = cast<target_triplet> (rs["cc.target"]);
+
+ load_module (rs, rs, "pkgconfig.config", loc, true, h);
}
return true;
@@ -259,12 +270,12 @@ namespace build2
// Load cc.core.config.
//
if (!cast_false<bool> (rs["cc.core.config.loaded"]))
- load_module ("cc.core.config", rs, rs, loc, false, hints);
+ load_module (rs, rs, "cc.core.config", loc, false, hints);
// Load the bin module.
//
if (!cast_false<bool> (rs["bin.loaded"]))
- load_module ("bin", rs, rs, loc);
+ load_module (rs, rs, "bin", loc);
const string& cid (cast<string> (rs["cc.id"]));
const string& tsys (cast<string> (rs["cc.target.system"]));
@@ -277,7 +288,7 @@ namespace build2
if (cast<string> (l) != "shared")
{
if (!cast_false<bool> (rs["bin.ar.loaded"]))
- load_module ("bin.ar", rs, rs, loc);
+ load_module (rs, rs, "bin.ar", loc);
}
}
@@ -287,7 +298,7 @@ namespace build2
if (cid == "msvc")
{
if (!cast_false<bool> (rs["bin.ld.loaded"]))
- load_module ("bin.ld", rs, rs, loc);
+ load_module (rs, rs, "bin.ld", loc);
}
// If our target is MinGW, then we will need the resource compiler
@@ -296,7 +307,7 @@ namespace build2
if (tsys == "mingw32")
{
if (!cast_false<bool> (rs["bin.rc.loaded"]))
- load_module ("bin.rc", rs, rs, loc);
+ load_module (rs, rs, "bin.rc", loc);
}
return true;
@@ -308,13 +319,13 @@ namespace build2
//
static inline bool
init_alias (tracer& trace,
+ scope& rs,
+ scope& bs,
const char* m,
const char* c,
const char* c_loaded,
const char* cxx,
const char* cxx_loaded,
- scope& rs,
- scope& bs,
const location& loc,
const variable_map& hints)
{
@@ -338,13 +349,13 @@ namespace build2
//
if (lc && lp && rs["config.c"])
{
- load_module (c, rs, rs, loc, false, hints);
- load_module (cxx, rs, rs, loc, false, hints);
+ load_module (rs, rs, c, loc, false, hints);
+ load_module (rs, rs, cxx, loc, false, hints);
}
else
{
- if (lp) load_module (cxx, rs, rs, loc, false, hints);
- if (lc) load_module (c, rs, rs, loc, false, hints);
+ if (lp) load_module (rs, rs, cxx, loc, false, hints);
+ if (lc) load_module (rs, rs, c, loc, false, hints);
}
return true;
@@ -360,10 +371,11 @@ namespace build2
const variable_map& hints)
{
tracer trace ("cc::config_init");
- return init_alias (trace, "cc.config",
+ return init_alias (trace, rs, bs,
+ "cc.config",
"c.config", "c.config.loaded",
"cxx.config", "cxx.config.loaded",
- rs, bs, loc, hints);
+ loc, hints);
}
bool
@@ -376,10 +388,11 @@ namespace build2
const variable_map& hints)
{
tracer trace ("cc::init");
- return init_alias (trace, "cc",
+ return init_alias (trace, rs, bs,
+ "cc",
"c", "c.loaded",
"cxx", "cxx.loaded",
- rs, bs, loc, hints);
+ loc, hints);
}
}
}
diff --git a/build2/cc/install b/build2/cc/install
index 561ed87..ff7af4d 100644
--- a/build2/cc/install
+++ b/build2/cc/install
@@ -25,10 +25,10 @@ namespace build2
install (data&&, const link&);
virtual target*
- filter (action, target&, prerequisite_member) const override;
+ filter (slock&, action, target&, prerequisite_member) const override;
virtual match_result
- match (action, target&, const string&) const override;
+ match (slock&, action, target&, const string&) const override;
virtual void
install_extra (file&, const install_dir&) const override;
diff --git a/build2/cc/install.cxx b/build2/cc/install.cxx
index 28d7db2..074654b 100644
--- a/build2/cc/install.cxx
+++ b/build2/cc/install.cxx
@@ -23,7 +23,7 @@ namespace build2
install (data&& d, const link& l): common (move (d)), link_ (l) {}
target* install::
- filter (action a, target& t, prerequisite_member p) const
+ filter (slock& ml, action a, target& t, prerequisite_member p) const
{
if (t.is_a<exe> ())
{
@@ -53,11 +53,11 @@ namespace build2
return pt->in (t.weak_scope ()) ? pt : nullptr;
}
- return file_rule::filter (a, t, p);
+ return file_rule::filter (ml, a, t, p);
}
match_result install::
- match (action a, target& t, const string& hint) const
+ match (slock& ml, action a, target& t, const string& hint) const
{
// @@ How do we split the hint between the two?
//
@@ -65,8 +65,8 @@ namespace build2
// We only want to handle installation if we are also the
// ones building this target. So first run link's match().
//
- match_result r (link_.match (a, t, hint));
- return r ? install::file_rule::match (a, t, "") : r;
+ match_result r (link_.match (ml, a, t, hint));
+ return r ? install::file_rule::match (ml, a, t, "") : r;
}
void install::
diff --git a/build2/cc/link b/build2/cc/link
index c7d3c93..5f28204 100644
--- a/build2/cc/link
+++ b/build2/cc/link
@@ -25,10 +25,10 @@ namespace build2
link (data&&);
virtual match_result
- match (action, target&, const string& hint) const override;
+ match (slock&, action, target&, const string& hint) const override;
virtual recipe
- apply (action, target&) const override;
+ apply (slock&, action, target&) const override;
target_state
perform_update (action, target&) const;
diff --git a/build2/cc/link.cxx b/build2/cc/link.cxx
index c94eb26..6de294a 100644
--- a/build2/cc/link.cxx
+++ b/build2/cc/link.cxx
@@ -41,7 +41,7 @@ namespace build2
}
match_result link::
- match (action a, target& t, const string& hint) const
+ match (slock& ml, action a, target& t, const string& hint) const
{
tracer trace (x, "link::match");
@@ -64,7 +64,7 @@ namespace build2
//
bool seen_x (false), seen_c (false), seen_obj (false), seen_lib (false);
- for (prerequisite_member p: group_prerequisite_members (a, t))
+ for (prerequisite_member p: group_prerequisite_members (ml, a, t))
{
if (p.is_a (x_src))
{
@@ -145,7 +145,7 @@ namespace build2
optional<dir_paths> usr_lib_dirs; // Extract lazily.
- for (prerequisite_member p: group_prerequisite_members (a, t))
+ for (prerequisite_member p: group_prerequisite_members (ml, a, t))
{
if (p.is_a<lib> () || p.is_a<liba> () || p.is_a<libs> ())
{
@@ -159,7 +159,7 @@ namespace build2
if (pt == nullptr)
{
pt = &p.search ();
- match_only (a, *pt);
+ match_only (ml, a, *pt);
}
// If the prerequisite came from the lib{} group, then also
@@ -316,7 +316,7 @@ namespace build2
}
recipe link::
- apply (action a, target& xt) const
+ apply (slock& ml, action a, target& xt) const
{
tracer trace (x, "link::apply");
@@ -415,7 +415,7 @@ namespace build2
// Inject dependency on the output directory.
//
- inject_fsdir (a, t);
+ inject_fsdir (ml, a, t);
optional<dir_paths> usr_lib_dirs; // Extract lazily.
@@ -429,7 +429,7 @@ namespace build2
lt == otype::a ? obja::static_type :
objs::static_type);
- for (prerequisite_member p: group_prerequisite_members (a, t))
+ for (prerequisite_member p: group_prerequisite_members (ml, a, t))
{
target* pt (nullptr);
@@ -471,7 +471,7 @@ namespace build2
pt = &link_member (*l, lo);
}
- build2::match (a, *pt);
+ build2::match (ml, a, *pt);
t.prerequisite_targets.push_back (pt);
continue;
}
@@ -567,7 +567,7 @@ namespace build2
//
bool found (false);
for (prerequisite_member p1:
- reverse_group_prerequisite_members (a, *pt))
+ reverse_group_prerequisite_members (ml, a, *pt))
{
// Most of the time we will have just a single source so fast-path
// that case.
@@ -576,7 +576,7 @@ namespace build2
{
if (!found)
{
- build2::match (a, *pt); // Now p1 should be resolved.
+ build2::match (ml, a, *pt); // Now p1 should be resolved.
// Searching our own prerequisite is ok.
//
@@ -637,7 +637,7 @@ namespace build2
ot.prerequisites.emplace_back (p);
}
- build2::match (a, *pt);
+ build2::match (ml, a, *pt);
}
t.prerequisite_targets.push_back (pt);
diff --git a/build2/cc/module.cxx b/build2/cc/module.cxx
index 3e20694..c7c7c3c 100644
--- a/build2/cc/module.cxx
+++ b/build2/cc/module.cxx
@@ -279,6 +279,8 @@ namespace build2
//
variable_map h;
+ // Note that all these variables have already been registered.
+ //
h.assign ("config.cc.id") = cast<string> (rs[x_id]);
h.assign ("config.cc.target") = cast<target_triplet> (rs[x_target]);
@@ -288,7 +290,7 @@ namespace build2
if (!ci.bin_pattern.empty ())
h.assign ("config.bin.pattern") = move (ci.bin_pattern);
- load_module ("cc.core.config", rs, rs, loc, false, h);
+ load_module (rs, rs, "cc.core.config", loc, false, h);
}
else
{
@@ -331,7 +333,7 @@ namespace build2
// extra bin.* modules we may need.
//
if (!cast_false<bool> (rs["cc.core.loaded"]))
- load_module ("cc.core", rs, rs, loc);
+ load_module (rs, rs, "cc.core", loc);
// Register target types and configure their "installability".
//
@@ -347,7 +349,7 @@ namespace build2
for (const target_type* const* ht (x_hdr); *ht != nullptr; ++ht)
{
t.insert (**ht);
- install_path (**ht, rs, dir_path ("include"));
+ install_path (rs, **ht, dir_path ("include"));
}
}
diff --git a/build2/cli/init.cxx b/build2/cli/init.cxx
index b34711d..2650c1b 100644
--- a/build2/cli/init.cxx
+++ b/build2/cli/init.cxx
@@ -43,7 +43,7 @@ namespace build2
//
if (first)
{
- auto& v (var_pool);
+ auto& v (var_pool.rw (rs));
// Note: some overridable, some not.
//
@@ -274,7 +274,7 @@ namespace build2
//
if (!cast_false<bool> (bs["cli.config.loaded"]))
{
- if (!load_module ("cli.config", rs, bs, l, optional, hints))
+ if (!load_module (rs, bs, "cli.config", l, optional, hints))
return false;
}
else if (!cast_false<bool> (bs["cli.config.configured"]))
diff --git a/build2/cli/rule b/build2/cli/rule
index a46163b..fbacacf 100644
--- a/build2/cli/rule
+++ b/build2/cli/rule
@@ -20,10 +20,10 @@ namespace build2
compile () {}
virtual match_result
- match (action, target&, const string& hint) const override;
+ match (slock&, action, target&, const string& hint) const override;
virtual recipe
- apply (action, target&) const override;
+ apply (slock&, action, target&) const override;
static target_state
perform_update (action, target&);
diff --git a/build2/cli/rule.cxx b/build2/cli/rule.cxx
index 706830c..8cef1f5 100644
--- a/build2/cli/rule.cxx
+++ b/build2/cli/rule.cxx
@@ -44,7 +44,7 @@ namespace build2
}
match_result compile::
- match (action a, target& xt, const string&) const
+ match (slock& ml, action a, target& xt, const string&) const
{
tracer trace ("cli::compile::match");
@@ -57,7 +57,7 @@ namespace build2
// See if we have a .cli source file.
//
bool r (false);
- for (prerequisite_member p: group_prerequisite_members (a, t))
+ for (prerequisite_member p: group_prerequisite_members (ml, a, t))
{
if (p.is_a<cli> ())
{
@@ -125,7 +125,7 @@ namespace build2
//
if (g == nullptr || !g->has_prerequisites ())
{
- for (prerequisite_member p: group_prerequisite_members (a, t))
+ for (prerequisite_member p: group_prerequisite_members (ml, a, t))
{
if (p.is_a<cli> ())
{
@@ -151,7 +151,7 @@ namespace build2
// Resolve the group's members. This should link us up to the
// group.
//
- resolve_group_members (a, *g);
+ resolve_group_members (ml, a, *g);
// For ixx{}, verify it is part of the group.
//
@@ -169,7 +169,7 @@ namespace build2
}
recipe compile::
- apply (action a, target& xt) const
+ apply (slock& ml, action a, target& xt) const
{
if (cli_cxx* pt = xt.is_a<cli_cxx> ())
{
@@ -184,11 +184,11 @@ namespace build2
// Inject dependency on the output directory.
//
- inject_fsdir (a, t);
+ inject_fsdir (ml, a, t);
// Search and match prerequisite members.
//
- search_and_match_prerequisite_members (a, t);
+ search_and_match_prerequisite_members (ml, a, t);
switch (a)
{
@@ -200,7 +200,7 @@ namespace build2
else
{
cli_cxx& g (*static_cast<cli_cxx*> (xt.group));
- build2::match (a, g);
+ build2::match (ml, a, g);
return group_recipe; // Execute the group's recipe.
}
}
diff --git a/build2/config/init.cxx b/build2/config/init.cxx
index d6cbe74..1f0b391 100644
--- a/build2/config/init.cxx
+++ b/build2/config/init.cxx
@@ -38,6 +38,8 @@ namespace build2
rs.meta_operations.insert (configure_id, configure);
rs.meta_operations.insert (disfigure_id, disfigure);
+ auto& vp (var_pool.rw (rs));
+
// Load config.build if one exists.
//
// Note that we have to do this during bootstrap since the order in
@@ -45,7 +47,7 @@ namespace build2
// possible that some module which needs the configuration will get
// called first.
//
- const variable& c_v (var_pool.insert<uint64_t> ("config.version"));
+ const variable& c_v (vp.insert<uint64_t> ("config.version"));
// Don't load it if we are disfiguring. This is a bit tricky since the
// build2 core may not yet know it is disfiguring. But we know.
@@ -68,7 +70,7 @@ namespace build2
{
// Assume missing version is 0.
//
- auto p (extract_variable (f, c_v.name.c_str ()));
+ auto p (extract_variable (f, c_v));
uint64_t v (p.second ? cast<uint64_t> (p.first) : 0);
if (v != module::version)
@@ -80,7 +82,7 @@ namespace build2
<< out_root;
}
- source (f, rs, rs);
+ source (rs, rs, f);
}
}
}
diff --git a/build2/config/operation.cxx b/build2/config/operation.cxx
index f74136c..175338a 100644
--- a/build2/config/operation.cxx
+++ b/build2/config/operation.cxx
@@ -82,7 +82,7 @@ namespace build2
ofs << "config.version = " << module::version << endl;
- if (auto l = root.vars["amalgamation"])
+ if (auto l = root.vars[var_amalgamation])
{
const dir_path& d (cast<dir_path> (l));
@@ -325,7 +325,7 @@ namespace build2
// Configure subprojects that have been loaded.
//
- if (auto l = root.vars["subprojects"])
+ if (auto l = root.vars[var_subprojects])
{
for (auto p: cast<subprojects> (l))
{
@@ -345,13 +345,13 @@ namespace build2
}
static void
- configure_match (action, action_targets&)
+ configure_match (ulock&, action, action_targets&)
{
// Don't match anything -- see execute ().
}
static void
- configure_execute (action a, const action_targets& ts, bool)
+ configure_execute (ulock& ml, action a, const action_targets& ts, bool)
{
// Match rules to configure every operation supported by each
// project. Note that we are not calling operation_pre/post()
@@ -360,6 +360,10 @@ namespace build2
//
set<scope*> projects;
+ // Relock for shared access.
+ //
+ ml.unlock ();
+
for (void* v: ts)
{
target& t (*static_cast<target*> (v));
@@ -368,6 +372,9 @@ namespace build2
if (rs == nullptr)
fail << "out of project target " << t;
+ slock sl (*ml.mutex ());
+ model_lock = &sl; // @@ Guard?
+
for (operations::size_type id (default_id + 1); // Skip default_id
id < rs->operations.size ();
++id)
@@ -379,11 +386,15 @@ namespace build2
set_current_oif (*oif);
dependency_count = 0;
- match (action (configure_id, id), t);
+ match (sl, action (configure_id, id), t);
}
+ model_lock = nullptr;
+
configure_project (a, *rs, projects);
}
+
+ ml.lock ();
}
const meta_operation_info configure {
@@ -414,8 +425,9 @@ namespace build2
}
static void
- disfigure_load (const path& bf,
+ disfigure_load (ulock&,
scope&,
+ const path& bf,
const dir_path&,
const dir_path&,
const location&)
@@ -425,7 +437,8 @@ namespace build2
}
static void
- disfigure_search (scope& root,
+ disfigure_search (ulock&,
+ scope& root,
const target_key&,
const location&,
action_targets& ts)
@@ -436,7 +449,9 @@ namespace build2
}
static void
- disfigure_match (action, action_targets&) {}
+ disfigure_match (ulock&, action, action_targets&)
+ {
+ }
static bool
disfigure_project (action a, scope& root, set<scope*>& projects)
@@ -457,7 +472,7 @@ namespace build2
// Disfigure subprojects. Since we don't load buildfiles during
// disfigure, we do it for all known subprojects.
//
- if (auto l = root.vars["subprojects"])
+ if (auto l = root.vars[var_subprojects])
{
for (auto p: cast<subprojects> (l))
{
@@ -475,7 +490,7 @@ namespace build2
{
bootstrap_out (nroot);
- value& val (nroot.assign ("src_root"));
+ value& val (nroot.assign (var_src_root));
if (!val)
val = is_src_root (out_nroot) ? out_nroot : (src_root / pd);
@@ -550,7 +565,7 @@ namespace build2
}
static void
- disfigure_execute (action a, const action_targets& ts, bool quiet)
+ disfigure_execute (ulock&, action a, const action_targets& ts, bool quiet)
{
tracer trace ("disfigure_execute");
diff --git a/build2/config/utility b/build2/config/utility
index 6c18715..e4e463d 100644
--- a/build2/config/utility
+++ b/build2/config/utility
@@ -44,6 +44,8 @@ namespace build2
bool override = false,
uint64_t save_flags = 0);
+ // Note that the variable is expected to have already been registered.
+ //
template <typename T>
inline pair<lookup, bool>
required (scope& root,
@@ -76,6 +78,8 @@ namespace build2
pair<lookup, bool>
omitted (scope& root, const variable&);
+ // Note that the variable is expected to have already been registered.
+ //
inline pair<lookup, bool>
omitted (scope& root, const string& name)
{
@@ -91,10 +95,12 @@ namespace build2
lookup
optional (scope& root, const variable&);
+ // Note that the variable is expected to have already been registered.
+ //
inline lookup
- optional (scope& root, const string& var)
+ optional (scope& root, const string& name)
{
- return optional (root, var_pool[var]);
+ return optional (root, var_pool[name]);
}
// Check whether there are any variables specified from the config
diff --git a/build2/config/utility.cxx b/build2/config/utility.cxx
index 7a3fa9c..7db4555 100644
--- a/build2/config/utility.cxx
+++ b/build2/config/utility.cxx
@@ -72,9 +72,10 @@ namespace build2
// any original values, they will be "visible"; see find_override() for
// details.
//
+ const variable& vns (var_pool.rw (r).insert (ns));
for (scope* s (&r); s != nullptr; s = s->parent_scope ())
{
- for (auto p (s->vars.find_namespace (ns));
+ for (auto p (s->vars.find_namespace (vns));
p.first != p.second;
++p.first)
{
@@ -92,30 +93,30 @@ namespace build2
}
bool
- unconfigured (scope& root, const string& ns)
+ unconfigured (scope& rs, const string& ns)
{
// Note: not overridable.
//
- const variable& var (var_pool.insert<bool> (ns + ".configured"));
+ const variable& var (var_pool.rw (rs).insert<bool> (ns + ".configured"));
if (current_mif->id == configure_id)
- save_variable (root, var);
+ save_variable (rs, var);
- auto l (root[var]); // Include inherited values.
+ auto l (rs[var]); // Include inherited values.
return l && !cast<bool> (l);
}
bool
- unconfigured (scope& root, const string& ns, bool v)
+ unconfigured (scope& rs, const string& ns, bool v)
{
// Note: not overridable.
//
- const variable& var (var_pool.insert<bool> (ns + ".configured"));
+ const variable& var (var_pool.rw (rs).insert<bool> (ns + ".configured"));
if (current_mif->id == configure_id)
- save_variable (root, var);
+ save_variable (rs, var);
- value& x (root.assign (var));
+ value& x (rs.assign (var));
if (x.null || cast<bool> (x) != !v)
{
diff --git a/build2/context b/build2/context
index 1dd9daf..33b99a0 100644
--- a/build2/context
+++ b/build2/context
@@ -14,7 +14,32 @@
namespace build2
{
- class file;
+ // Top-level internal state mutex.
+ //
+ extern shared_mutex model;
+
+ // Thread's shared model lock. NULL in the serial stages.
+ //
+ extern
+#ifdef __cpp_thread_local
+ thread_local
+#else
+ __thread
+#endif
+ slock* model_lock;
+
+ // Cached variables.
+ //
+ extern const variable* var_src_root;
+ extern const variable* var_out_root;
+ extern const variable* var_src_base;
+ extern const variable* var_out_base;
+
+ extern const variable* var_project;
+ extern const variable* var_amalgamation;
+ extern const variable* var_subprojects;
+
+ extern const variable* var_import_target; // import.target
// Current action (meta/operation).
//
@@ -75,14 +100,14 @@ namespace build2
// scopes, and variables.
//
variable_overrides
- reset (const strings& cmd_vars);
+ reset (const ulock&, const strings& cmd_vars);
// Return the project name or empty string if unnamed.
//
inline const string&
- project (scope& root)
+ project (const scope& root)
{
- auto l (root["project"]);
+ auto l (root[var_project]);
return l ? cast<string> (l) : empty_string;
}
diff --git a/build2/context.cxx b/build2/context.cxx
index ba6629c..1cf3dd9 100644
--- a/build2/context.cxx
+++ b/build2/context.cxx
@@ -23,6 +23,26 @@ using namespace butl;
namespace build2
{
+ shared_mutex model;
+
+#ifdef __cpp_thread_local
+ thread_local
+#else
+ __thread
+#endif
+ slock* model_lock;
+
+ const variable* var_src_root;
+ const variable* var_out_root;
+ const variable* var_src_base;
+ const variable* var_out_base;
+
+ const variable* var_project;
+ const variable* var_amalgamation;
+ const variable* var_subprojects;
+
+ const variable* var_import_target;
+
const string* current_mname;
const string* current_oname;
@@ -37,7 +57,7 @@ namespace build2
variable_override_cache var_override_cache;
variable_overrides
- reset (const strings& cmd_vars)
+ reset (const ulock& ml, const strings& cmd_vars)
{
tracer trace ("reset");
@@ -46,13 +66,15 @@ namespace build2
l6 ([&]{trace << "resetting build state";});
+ auto& vp (var_pool.rw (ml));
+
variable_overrides vos;
var_override_cache.clear ();
targets.clear ();
scopes.clear ();
- var_pool.clear ();
+ vp.clear ();
// Reset meta/operation tables. Note that the order should match the id
// constants in <build2/operation>.
@@ -147,7 +169,7 @@ namespace build2
c == '%' ? variable_visibility::project :
variable_visibility::normal);
- const variable& var (var_pool[n]);
+ const variable& var (vp.insert (n));
const char* k (tt == token_type::assign ? ".__override" :
tt == token_type::append ? ".__suffix" : ".__prefix");
@@ -209,23 +231,23 @@ namespace build2
// Enter builtin variables.
//
- {
- auto& v (var_pool);
-
- v.insert<dir_path> ("src_root");
- v.insert<dir_path> ("out_root");
- v.insert<dir_path> ("src_base");
- v.insert<dir_path> ("out_base");
+ 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");
+ var_out_base = &vp.insert<dir_path> ("out_base");
- v.insert<string> ("project");
- v.insert<dir_path> ("amalgamation");
+ // Note that subprojects is not typed since the value requires
+ // pre-processing (see file.cxx).
+ //
+ var_project = &vp.insert<string> ("project");
+ var_amalgamation = &vp.insert<dir_path> ("amalgamation");
+ var_subprojects = &vp.insert ("subprojects");
- // Not typed since the value requires pre-processing (see file.cxx).
- //
- v.insert ("subprojects");
+ var_import_target = &vp.insert<name> ("import.target");
- v.insert<string> ("extension", variable_visibility::target);
- }
+ // Target extension.
+ //
+ vp.insert<string> ("extension", variable_visibility::target);
gs.assign<dir_path> ("build.work") = work;
gs.assign<dir_path> ("build.home") = home;
@@ -240,8 +262,8 @@ namespace build2
// Build system version.
//
{
- gs.assign<uint64_t> ("build.version") = uint64_t (BUILD2_VERSION);
- gs.assign<string> ("build.version.string") = BUILD2_VERSION_STR;
+ gs.assign<uint64_t> ("build.version") = uint64_t (BUILD2_VERSION);
+ gs.assign<string> ("build.version.string") = BUILD2_VERSION_STR;
// AABBCCDD
//
diff --git a/build2/cxx/init.cxx b/build2/cxx/init.cxx
index 477056c..f76779b 100644
--- a/build2/cxx/init.cxx
+++ b/build2/cxx/init.cxx
@@ -175,11 +175,11 @@ namespace build2
// Load cc.core.vars so that we can cache all the cc.* variables.
//
if (!cast_false<bool> (rs["cc.core.vars.loaded"]))
- load_module ("cc.core.vars", rs, rs, loc);
+ load_module (rs, rs, "cc.core.vars", loc);
// Enter all the variables and initialize the module data.
//
- auto& v (var_pool);
+ auto& v (var_pool.rw (rs));
cc::config_data d {
cc::lang::cxx,
@@ -294,7 +294,7 @@ namespace build2
// Load cxx.config.
//
if (!cast_false<bool> (rs["cxx.config.loaded"]))
- load_module ("cxx.config", rs, rs, loc, false, hints);
+ load_module (rs, rs, "cxx.config", loc, false, hints);
config_module& cm (*rs.modules.lookup<config_module> ("cxx.config"));
diff --git a/build2/dist/init.cxx b/build2/dist/init.cxx
index 901e9c2..9ee2d46 100644
--- a/build2/dist/init.cxx
+++ b/build2/dist/init.cxx
@@ -23,21 +23,21 @@ namespace build2
static const rule rule_;
void
- boot (scope& r, const location&, unique_ptr<module_base>&)
+ boot (scope& rs, const location&, unique_ptr<module_base>&)
{
tracer trace ("dist::boot");
- l5 ([&]{trace << "for " << r.out_path ();});
+ l5 ([&]{trace << "for " << rs.out_path ();});
// Register meta-operation.
//
- r.meta_operations.insert (dist_id, dist);
+ rs.meta_operations.insert (dist_id, dist);
// Enter module variables. Do it during boot in case they get assigned
// in bootstrap.build (which is customary for, e.g., dist.package).
//
{
- auto& v (var_pool);
+ auto& v (var_pool.rw (rs));
// Note: some overridable, some not.
//
@@ -63,7 +63,7 @@ namespace build2
}
bool
- init (scope& r,
+ init (scope& rs,
scope&,
const location& l,
unique_ptr<module_base>&,
@@ -79,7 +79,7 @@ namespace build2
return true;
}
- const dir_path& out_root (r.out_path ());
+ const dir_path& out_root (rs.out_path ());
l5 ([&]{trace << "for " << out_root;});
assert (config_hints.empty ()); // We don't known any hints.
@@ -88,8 +88,8 @@ namespace build2
// to prevent something like insert<target>(dist_id, test_id)
// taking precedence.
//
- r.rules.insert<target> (dist_id, 0, "dist", rule_);
- r.rules.insert<alias> (dist_id, 0, "dist.alias", rule_);
+ rs.rules.insert<target> (dist_id, 0, "dist", rule_);
+ rs.rules.insert<alias> (dist_id, 0, "dist.alias", rule_);
// Configuration.
//
@@ -97,22 +97,22 @@ namespace build2
// must be explicitly specified or we will complain if and when
// we try to dist.
//
- bool s (config::specified (r, "config.dist"));
+ bool s (config::specified (rs, "config.dist"));
// Adjust module priority so that the config.dist.* values are saved at
// the end of config.build.
//
if (s)
- config::save_module (r, "dist", INT32_MAX);
+ config::save_module (rs, "dist", INT32_MAX);
// dist.root
//
{
- value& v (r.assign ("dist.root"));
+ value& v (rs.assign ("dist.root"));
if (s)
{
- if (lookup l = config::optional (r, "config.dist.root"))
+ if (lookup l = config::optional (rs, "config.dist.root"))
v = cast<dir_path> (l); // Strip abs_dir_path.
}
}
@@ -120,11 +120,11 @@ namespace build2
// dist.cmd
//
{
- value& v (r.assign<process_path> ("dist.cmd"));
+ value& v (rs.assign<process_path> ("dist.cmd"));
if (s)
{
- if (lookup l = config::required (r,
+ if (lookup l = config::required (rs,
"config.dist.cmd",
path ("install")).first)
v = run_search (cast<path> (l), true);
@@ -134,11 +134,11 @@ namespace build2
// dist.archives
//
{
- value& v (r.assign ("dist.archives"));
+ value& v (rs.assign ("dist.archives"));
if (s)
{
- if (lookup l = config::optional (r, "config.dist.archives"))
+ if (lookup l = config::optional (rs, "config.dist.archives"))
v = *l;
}
}
diff --git a/build2/dist/operation.cxx b/build2/dist/operation.cxx
index 1b96282..83a5b01 100644
--- a/build2/dist/operation.cxx
+++ b/build2/dist/operation.cxx
@@ -30,7 +30,7 @@ namespace build2
}
static void
- dist_match (action, action_targets&)
+ dist_match (ulock&, action, action_targets&)
{
// Don't match anything -- see execute ().
}
@@ -54,7 +54,7 @@ namespace build2
const string& ext);
static void
- dist_execute (action, const action_targets& ts, bool)
+ dist_execute (ulock& ml, action, const action_targets& ts, bool)
{
tracer trace ("dist_execute");
@@ -97,6 +97,10 @@ namespace build2
const string& dist_package (cast<string> (l));
const process_path& dist_cmd (cast<process_path> (rs->vars["dist.cmd"]));
+ // Relock for shared access. @@ BOGUS
+ //
+ ml.unlock ();
+
// Get the list of operations supported by this project. Skip
// default_id.
//
@@ -120,6 +124,9 @@ namespace build2
if (verb >= 6)
dump (a);
+ slock sl (*ml.mutex ());
+ model_lock = &sl; // @@ Guard?
+
for (void* v: ts)
{
target& t (*static_cast<target*> (v));
@@ -131,13 +138,17 @@ namespace build2
l5 ([&]{trace << diag_doing (a, t);});
- match (a, t);
+ match (sl, a, t);
}
+ model_lock = nullptr;
+
if (verb >= 6)
dump (a);
}
+ ml.lock ();
+
// Add buildfiles that are not normally loaded as part of the
// project, for example, the export stub. They will still be
// ignored on the next step if the user explicitly marked them
@@ -169,7 +180,7 @@ namespace build2
// The same for subprojects that have been loaded.
//
- if (auto l = rs->vars["subprojects"])
+ if (auto l = rs->vars[var_subprojects])
{
for (auto p: cast<subprojects> (l))
{
@@ -247,8 +258,8 @@ namespace build2
action a (perform_id, update_id);
- perform.match (a, files);
- perform.execute (a, files, true); // Run quiet.
+ perform.match (ml, a, files);
+ perform.execute (ml, a, files, true); // Run quiet.
if (perform.operation_post != nullptr)
perform.operation_post (update_id);
diff --git a/build2/dist/rule b/build2/dist/rule
index db8e731..61be24f 100644
--- a/build2/dist/rule
+++ b/build2/dist/rule
@@ -22,10 +22,10 @@ namespace build2
rule () {}
virtual match_result
- match (action, target&, const string&) const override;
+ match (slock&, action, target&, const string&) const override;
virtual recipe
- apply (action, target&) const override;
+ apply (slock&, action, target&) const override;
};
}
}
diff --git a/build2/dist/rule.cxx b/build2/dist/rule.cxx
index 6cbd7df..f5c4018 100644
--- a/build2/dist/rule.cxx
+++ b/build2/dist/rule.cxx
@@ -16,17 +16,17 @@ namespace build2
namespace dist
{
match_result rule::
- match (action, target&, const string&) const
+ match (slock&, action, target&, const string&) const
{
return true; // We always match.
}
recipe rule::
- apply (action a, target& t) const
+ apply (slock& ml, action a, target& t) const
{
const dir_path& out_root (t.root_scope ().out_path ());
- auto r (group_prerequisite_members (a, t, false));
+ auto r (group_prerequisite_members (ml, a, t, false));
for (auto i (r.begin ()); i != r.end (); ++i)
{
prerequisite_member p (*i);
@@ -48,7 +48,7 @@ namespace build2
// Don't match targets that are outside of our project.
//
if (pt.dir.sub (out_root))
- build2::match (a, pt);
+ build2::match (ml, a, pt);
}
return noop_recipe; // We will never be executed.
diff --git a/build2/file b/build2/file
index c8d8f6f..adfd884 100644
--- a/build2/file
+++ b/build2/file
@@ -56,18 +56,18 @@ namespace build2
// If buildfile is '-', then read from STDIN.
//
void
- source (const path& buildfile, scope& root, scope& base);
+ source (scope& root, scope& base, const path&);
// As above but first check if this buildfile has already been sourced for
// the base scope. Return false if the file has already been sourced.
//
bool
- source_once (const path& buildfile, scope& root, scope& base);
+ source_once (scope& root, scope& base, const path&);
// As above but checks against the specified scope rather than base.
//
bool
- source_once (const path& buildfile, scope& root, scope& base, scope& once);
+ source_once (scope& root, scope& base, const path&, scope& once);
// Create project's root scope. Only set the src_root variable if the
// passed src_root value is not empty.
@@ -143,7 +143,7 @@ namespace build2
// an indication of whether the variable was found.
//
pair<value, bool>
- extract_variable (const path&, const char* var);
+ extract_variable (const path&, const variable&);
// Import has two phases: the first is triggered by the import
// directive in the buildfile. It will try to find and load the
diff --git a/build2/file.cxx b/build2/file.cxx
index 527f752..9440738 100644
--- a/build2/file.cxx
+++ b/build2/file.cxx
@@ -83,7 +83,7 @@ namespace build2
}
static void
- source (const path& bf, scope& root, scope& base, bool boot)
+ source (scope& root, scope& base, const path& bf, bool boot)
{
tracer trace ("source");
@@ -112,13 +112,13 @@ namespace build2
}
void
- source (const path& bf, scope& root, scope& base)
+ source (scope& root, scope& base, const path& bf)
{
- source (bf, root, base, false);
+ source (root, base, bf, false);
}
bool
- source_once (const path& bf, scope& root, scope& base, scope& once)
+ source_once (scope& root, scope& base, const path& bf, scope& once)
{
tracer trace ("source_once");
@@ -128,7 +128,7 @@ namespace build2
return false;
}
- source (bf, root, base);
+ source (root, base, bf);
return true;
}
@@ -168,7 +168,7 @@ namespace build2
// consistent.
//
{
- value& v (rs.assign ("out_root"));
+ value& v (rs.assign (var_out_root));
if (!v)
v = out_root;
@@ -184,7 +184,7 @@ namespace build2
if (!src_root.empty ())
{
- value& v (rs.assign ("src_root"));
+ value& v (rs.assign (var_src_root));
if (!v)
v = src_root;
@@ -206,7 +206,7 @@ namespace build2
{
// The caller must have made sure src_root is set on this scope.
//
- value& v (s.assign ("src_root"));
+ value& v (s.assign (var_src_root));
assert (v);
const dir_path& d (cast<dir_path> (v));
@@ -225,14 +225,14 @@ namespace build2
// Set src/out_base variables.
//
- value& ov (s.assign ("out_base"));
+ value& ov (s.assign (var_out_base));
if (!ov)
ov = out_base;
else
assert (cast<dir_path> (ov) == out_base);
- value& sv (s.assign ("src_base"));
+ value& sv (s.assign (var_src_base));
if (!sv)
sv = src_base;
@@ -309,7 +309,7 @@ namespace build2
// prevent multiple sourcing. We handle it here but we still
// need something like source_once (once [scope] source).
//
- source_once (bf, root, root);
+ source_once (root, root, bf);
}
// Extract the specified variable value from a buildfile. It is expected to
@@ -317,7 +317,7 @@ namespace build2
// other than those from the global scope or any variable overrides.
//
pair<value, bool>
- extract_variable (const path& bf, const char* name)
+ extract_variable (const path& bf, const variable& var)
{
try
{
@@ -327,7 +327,7 @@ namespace build2
token t (lex.next ());
token_type tt;
- if (t.type != token_type::word || t.value != name ||
+ if (t.type != token_type::word || t.value != var.name ||
((tt = lex.next ().type) != token_type::assign &&
tt != token_type::prepend &&
tt != token_type::append))
@@ -335,8 +335,6 @@ namespace build2
return make_pair (value (), false);
}
- const variable& var (var_pool[t.value]);
-
parser p;
temp_scope tmp (*global_scope);
p.parse_variable (lex, tmp, var, tt);
@@ -382,7 +380,7 @@ namespace build2
src_root = &fallback_src_root;
else
{
- auto p (extract_variable (f, "src_root"));
+ auto p (extract_variable (f, *var_src_root));
if (!p.second)
fail << "variable 'src_root' expected as first line in " << f;
@@ -397,10 +395,11 @@ namespace build2
string name;
{
path f (*src_root / bootstrap_file);
- auto p (extract_variable (f, "project"));
+ auto p (extract_variable (f, *var_project));
if (!p.second)
- fail << "variable 'project' expected as first line in " << f;
+ fail << "variable '" << var_project->name << "' expected "
+ << "as a first line in " << f;
name = cast<string> (move (p.first));
}
@@ -513,7 +512,7 @@ namespace build2
// same root scope multiple time.
//
if (root.buildfiles.insert (bf).second)
- source (bf, root, root, true);
+ source (root, root, bf, true);
else
l5 ([&]{trace << "skipping already sourced " << bf;});
@@ -532,7 +531,7 @@ namespace build2
// amalgamated.
//
{
- auto rp (root.vars.insert ("amalgamation")); // Set NULL by default.
+ auto rp (root.vars.insert (*var_amalgamation)); // Set NULL by default.
value& v (rp.first);
if (v && v.empty ()) // Convert empty to NULL.
@@ -603,8 +602,7 @@ namespace build2
// NULL value indicates that we found no subprojects.
//
{
- const variable& var (var_pool["subprojects"]);
- auto rp (root.vars.insert (var)); // Set NULL by default.
+ auto rp (root.vars.insert (*var_subprojects)); // Set NULL by default.
value& v (rp.first);
if (rp.second)
@@ -725,14 +723,14 @@ namespace build2
// It should either be NULL or typed (so we assume that the user will
// never set it to NULL).
//
- auto l (root.vars["subprojects"]);
+ auto l (root.vars[var_subprojects]);
return l.defined () && (l->null || l->type != nullptr);
}
void
create_bootstrap_outer (scope& root)
{
- auto l (root.vars["amalgamation"]);
+ auto l (root.vars[var_amalgamation]);
if (!l)
return;
@@ -758,7 +756,7 @@ namespace build2
{
bootstrap_out (rs); // #3 happens here, if at all.
- value& v (rs.assign ("src_root"));
+ value& v (rs.assign (var_src_root));
if (!v)
{
@@ -787,7 +785,7 @@ namespace build2
scope&
create_bootstrap_inner (scope& root, const dir_path& out_base)
{
- if (auto l = root.vars["subprojects"])
+ if (auto l = root.vars[var_subprojects])
{
for (const auto& p: cast<subprojects> (l))
{
@@ -804,7 +802,7 @@ namespace build2
{
bootstrap_out (rs);
- value& v (rs.assign ("src_root"));
+ value& v (rs.assign (var_src_root));
if (!v)
v = is_src_root (out_root)
@@ -848,7 +846,7 @@ namespace build2
if (s.boot)
{
- load_module (n, root, root, s.loc);
+ load_module (root, root, n, s.loc);
assert (!s.boot);
}
}
@@ -858,7 +856,7 @@ namespace build2
path bf (root.src_path () / root_file);
if (exists (bf))
- source_once (bf, root, root);
+ source_once (root, root, bf);
}
names
@@ -903,12 +901,13 @@ namespace build2
// Note: overridable variable with path auto-completion.
//
{
- const variable& var (var_pool.insert<abs_dir_path> (n, true));
+ const variable& var (
+ var_pool.rw (ibase).insert<abs_dir_path> (n, true));
if (auto l = iroot[var])
{
out_root = cast<dir_path> (l); // Normalized and actualized.
- config::save_variable (iroot, var); // Mark as part of configuration.
+ config::save_variable (iroot, var); // Mark as part of config.
// Empty config.import.* value means don't look in subprojects or
// amalgamations and go straight to the rule-specific import (e.g.,
@@ -934,7 +933,7 @@ namespace build2
{
auto lookup = [&iroot, &loc] (string name) -> path
{
- const variable& var (var_pool.insert<path> (name, true));
+ const variable& var (var_pool.rw (iroot).insert<path> (name, true));
path r;
if (auto l = iroot[var])
@@ -985,13 +984,13 @@ namespace build2
// First check the amalgamation itself.
//
- if (r != &iroot && cast<string> (r->vars["project"]) == proj)
+ if (r != &iroot && cast<string> (r->vars[var_project]) == proj)
{
out_root = r->out_path ();
break;
}
- if (auto l = r->vars["subprojects"])
+ if (auto l = r->vars[var_subprojects])
{
const auto& m (cast<subprojects> (l));
auto i (m.find (proj));
@@ -1004,7 +1003,7 @@ namespace build2
}
}
- if (!r->vars["amalgamation"])
+ if (!r->vars[var_amalgamation])
break;
}
@@ -1041,7 +1040,7 @@ namespace build2
// Check that the bootstrap process set src_root.
//
- if (auto l = root->vars["src_root"])
+ if (auto l = root->vars[*var_src_root])
{
const dir_path& p (cast<dir_path> (l));
@@ -1061,10 +1060,10 @@ namespace build2
// Now we know this project's name as well as all its subprojects.
//
- if (cast<string> (root->vars["project"]) == proj)
+ if (cast<string> (root->vars[var_project]) == proj)
break;
- if (auto l = root->vars["subprojects"])
+ if (auto l = root->vars[var_subprojects])
{
const auto& m (cast<subprojects> (l));
auto i (m.find (proj));
@@ -1096,13 +1095,13 @@ namespace build2
// "Pass" the imported project's roots to the stub.
//
- ts.assign ("out_root") = move (out_root);
- ts.assign ("src_root") = move (src_root);
+ ts.assign (var_out_root) = move (out_root);
+ ts.assign (var_src_root) = move (src_root);
- // Also pass the target being imported.
+ // Also pass the target being imported in the import.target variable.
//
{
- value& v (ts.assign ("target"));
+ value& v (ts.assign (var_import_target));
if (!target.empty ()) // Otherwise leave NULL.
v = move (target);
diff --git a/build2/file.ixx b/build2/file.ixx
index 050a3e9..e12654c 100644
--- a/build2/file.ixx
+++ b/build2/file.ixx
@@ -5,8 +5,8 @@
namespace build2
{
inline bool
- source_once (const path& bf, scope& root, scope& base)
+ source_once (scope& root, scope& base, const path& bf)
{
- return source_once (bf, root, base, base);
+ return source_once (root, base, bf, base);
}
}
diff --git a/build2/install/init.cxx b/build2/install/init.cxx
index 8c28dad..122eb54 100644
--- a/build2/install/init.cxx
+++ b/build2/install/init.cxx
@@ -52,6 +52,8 @@ namespace build2
if (spec)
{
+ // Note: overridable.
+ //
vn = "config.install";
if (!global)
{
@@ -59,7 +61,7 @@ namespace build2
vn += name;
}
vn += var;
- const variable& vr (var_pool.insert<CT> (move (vn), true));
+ const variable& vr (var_pool.rw (r).insert<CT> (move (vn), true));
l = dv != nullptr
? config::required (r, vr, *dv, override).first
@@ -71,10 +73,12 @@ namespace build2
if (global)
return;
+ // Note: not overridable.
+ //
vn = "install.";
vn += name;
vn += var;
- const variable& vr (var_pool.insert<T> (move (vn))); // Not overridable.
+ const variable& vr (var_pool.rw (r).insert<T> (move (vn)));
value& v (r.assign (vr));
@@ -117,7 +121,7 @@ namespace build2
// This one doesn't have config.* value (only set in a buildfile).
//
if (!global)
- var_pool.insert<bool> (string ("install.") + n + ".subdirs");
+ var_pool.rw (r).insert<bool> (string ("install.") + n + ".subdirs");
}
static const alias_rule alias_;
@@ -153,8 +157,8 @@ namespace build2
static const dir_path dir_man1 (dir_path ("man") /= "man1");
bool
- init (scope& r,
- scope& b,
+ init (scope& rs,
+ scope& bs,
const location& l,
unique_ptr<module_base>&,
bool first,
@@ -169,7 +173,7 @@ namespace build2
return true;
}
- const dir_path& out_root (r.out_path ());
+ const dir_path& out_root (rs.out_path ());
l5 ([&]{trace << "for " << out_root;});
assert (config_hints.empty ()); // We don't known any hints.
@@ -179,7 +183,7 @@ namespace build2
// Note that the set_dir() calls below enter some more.
//
{
- auto& v (var_pool);
+ auto& v (var_pool.rw (rs));
// Note: not overridable.
//
@@ -196,11 +200,11 @@ namespace build2
// Register our alias and file rules.
//
- b.rules.insert<alias> (perform_install_id, "install.alias", alias_);
- b.rules.insert<alias> (perform_uninstall_id, "uninstall.alias", alias_);
+ bs.rules.insert<alias> (perform_install_id, "install.alias", alias_);
+ bs.rules.insert<alias> (perform_uninstall_id, "uninstall.alias", alias_);
- b.rules.insert<file> (perform_install_id, "install.file", file_);
- b.rules.insert<file> (perform_uninstall_id, "uinstall.file", file_);
+ bs.rules.insert<file> (perform_install_id, "install.file", file_);
+ bs.rules.insert<file> (perform_uninstall_id, "uinstall.file", file_);
// Configuration.
//
@@ -211,44 +215,44 @@ namespace build2
{
using build2::path;
- bool s (config::specified (r, "config.install"));
+ bool s (config::specified (rs, "config.install"));
// Adjust module priority so that the (numerous) config.install.*
// values are saved at the end of config.build.
//
if (s)
- config::save_module (r, "install", INT32_MAX);
+ config::save_module (rs, "install", INT32_MAX);
- const string& n (cast<string> (r["project"]));
+ const string& n (project (rs));
// Global config.install.* values.
//
- set_dir (s, r, "", abs_dir_path (), false, "644", "755", cmd);
+ set_dir (s, rs, "", abs_dir_path (), false, "644", "755", cmd);
- set_dir (s, r, "root", abs_dir_path ());
+ set_dir (s, rs, "root", abs_dir_path ());
- set_dir (s, r, "data_root", dir_root);
- set_dir (s, r, "exec_root", dir_root, false, "755");
+ set_dir (s, rs, "data_root", dir_root);
+ set_dir (s, rs, "exec_root", dir_root, false, "755");
- set_dir (s, r, "sbin", dir_sbin);
- set_dir (s, r, "bin", dir_bin);
- set_dir (s, r, "lib", dir_lib);
- set_dir (s, r, "libexec", dir_path (dir_libexec) /= n, true);
+ set_dir (s, rs, "sbin", dir_sbin);
+ set_dir (s, rs, "bin", dir_bin);
+ set_dir (s, rs, "lib", dir_lib);
+ set_dir (s, rs, "libexec", dir_path (dir_libexec) /= n, true);
- set_dir (s, r, "data", dir_path (dir_data) /= n, true);
- set_dir (s, r, "include", dir_include);
+ set_dir (s, rs, "data", dir_path (dir_data) /= n, true);
+ set_dir (s, rs, "include", dir_include);
- set_dir (s, r, "doc", dir_path (dir_doc) /= n, true);
- set_dir (s, r, "man", dir_man);
- set_dir (s, r, "man1", dir_man1);
+ set_dir (s, rs, "doc", dir_path (dir_doc) /= n, true);
+ set_dir (s, rs, "man", dir_man);
+ set_dir (s, rs, "man1", dir_man1);
}
// Configure "installability" for built-in target types.
//
- install_path<exe> (b, dir_path ("bin")); // Install into install.bin.
- install_path<doc> (b, dir_path ("doc")); // Install into install.doc.
- install_path<man> (b, dir_path ("man")); // Install into install.man.
- install_path<man1> (b, dir_path ("man1")); // Install into install.man1.
+ install_path<exe> (bs, dir_path ("bin")); // Install into install.bin.
+ install_path<doc> (bs, dir_path ("doc")); // Install into install.doc.
+ install_path<man> (bs, dir_path ("man")); // Install into install.man.
+ install_path<man1> (bs, dir_path ("man1")); // Install into install.man1.
return true;
}
diff --git a/build2/install/rule b/build2/install/rule
index aad5ee4..169c43a 100644
--- a/build2/install/rule
+++ b/build2/install/rule
@@ -22,10 +22,10 @@ namespace build2
alias_rule () {}
virtual match_result
- match (action, target&, const string&) const override;
+ match (slock&, action, target&, const string&) const override;
virtual recipe
- apply (action, target&) const override;
+ apply (slock&, action, target&) const override;
};
struct install_dir;
@@ -36,17 +36,17 @@ namespace build2
file_rule () {}
virtual match_result
- match (action, target&, const string&) const override;
+ match (slock&, action, target&, const string&) const override;
virtual recipe
- apply (action, target&) const override;
+ apply (slock&, action, target&) const override;
// Return NULL if this prerequisite should be ignored and pointer to its
// target otherwise. The default implementation ignores prerequsites that
// are outside of this target's project.
//
virtual target*
- filter (action, target&, prerequisite_member) const;
+ filter (slock&, action, target&, prerequisite_member) const;
// Extra installation hooks.
//
diff --git a/build2/install/rule.cxx b/build2/install/rule.cxx
index a727acc..13c93a7 100644
--- a/build2/install/rule.cxx
+++ b/build2/install/rule.cxx
@@ -39,13 +39,13 @@ namespace build2
// alias_rule
//
match_result alias_rule::
- match (action, target&, const string&) const
+ match (slock&, action, target&, const string&) const
{
return true;
}
recipe alias_rule::
- apply (action a, target& t) const
+ apply (slock& ml, action a, target& t) const
{
tracer trace ("install::alias_rule::apply");
@@ -73,7 +73,7 @@ namespace build2
continue;
}
- build2::match (a, pt);
+ build2::match (ml, a, pt);
t.prerequisite_targets.push_back (&pt);
}
@@ -91,7 +91,7 @@ namespace build2
"insufficient space");
match_result file_rule::
- match (action a, target& t, const string&) const
+ match (slock&, action a, target& t, const string&) const
{
// First determine if this target should be installed (called
// "installable" for short).
@@ -119,14 +119,14 @@ namespace build2
}
target* file_rule::
- filter (action, target& t, prerequisite_member p) const
+ filter (slock&, action, target& t, prerequisite_member p) const
{
target& pt (p.search ());
return pt.in (t.root_scope ()) ? &pt : nullptr;
}
recipe file_rule::
- apply (action a, target& t) const
+ apply (slock& ml, action a, target& t) const
{
match_data md (move (t.data<match_data> ()));
t.clear_data (); // In case delegated-to rule also uses aux storage.
@@ -149,7 +149,7 @@ namespace build2
// run standard search_and_match()? Will need an indicator
// that it was forced (e.g., [install]) for filter() below.
//
- auto r (group_prerequisite_members (a, t));
+ auto r (group_prerequisite_members (ml, a, t));
for (auto i (r.begin ()); i != r.end (); ++i)
{
prerequisite_member p (*i);
@@ -162,7 +162,7 @@ namespace build2
// Let a customized rule have its say.
//
- target* pt (filter (a, t, p));
+ target* pt (filter (ml, a, t, p));
if (pt == nullptr)
continue;
@@ -172,7 +172,7 @@ namespace build2
if (l && cast<path> (l).string () == "false")
continue;
- build2::match (a, *pt);
+ build2::match (ml, a, *pt);
// If the matched rule returned noop_recipe, then the target
// state will be set to unchanged as an optimization. Use this
@@ -209,7 +209,7 @@ namespace build2
// have been found if we signalled that we do not match from
// match() above.
//
- recipe d (match_delegate (a, t, *this).first);
+ recipe d (match_delegate (ml, a, t, *this).first);
// If we have no installable prerequisites, then simply redirect
// to it.
diff --git a/build2/install/utility b/build2/install/utility
index 713c3e8..239447e 100644
--- a/build2/install/utility
+++ b/build2/install/utility
@@ -17,9 +17,12 @@ namespace build2
// Set install path, mode for a target type.
//
inline void
- install_path (const target_type& tt, scope& s, dir_path d)
+ install_path (scope& s, const target_type& tt, dir_path d)
{
- auto r (s.target_vars[tt]["*"].insert ("install"));
+ auto r (
+ s.target_vars[tt]["*"].insert (
+ var_pool.rw (s).insert ("install")));
+
if (r.second) // Already set by the user?
r.first.get () = path_cast<path> (move (d));
}
@@ -28,13 +31,16 @@ namespace build2
inline void
install_path (scope& s, dir_path d)
{
- return install_path (T::static_type, s, move (d));
+ return install_path (s, T::static_type, move (d));
}
inline void
- install_mode (const target_type& tt, scope& s, string m)
+ install_mode (scope& s, const target_type& tt, string m)
{
- auto r (s.target_vars[tt]["*"].insert ("install.mode"));
+ auto r (
+ s.target_vars[tt]["*"].insert (
+ var_pool.rw (s).insert ("install.mode")));
+
if (r.second) // Already set by the user?
r.first.get () = move (m);
}
@@ -43,7 +49,7 @@ namespace build2
inline void
install_mode (scope& s, string m)
{
- return install_mode (T::static_type, s, move (m));
+ return install_mode (s, T::static_type, move (m));
}
}
}
diff --git a/build2/module b/build2/module
index 7d6439f..7c4672f 100644
--- a/build2/module
+++ b/build2/module
@@ -26,7 +26,9 @@ namespace build2
};
using module_boot_function =
- void (scope& root, const location&, unique_ptr<module_base>&);
+ void (scope& root,
+ const location&,
+ unique_ptr<module_base>&);
// Return false if the module configuration (normally based on the default
// values) was unsuccessful but this is not (yet) an error. One example
@@ -82,7 +84,7 @@ namespace build2
// Load and boot the specified module.
//
void
- boot_module (const string& name, scope& root, const location&);
+ boot_module (scope& root, const string& name, const location&);
// Load (if not already loaded) and initialize the specified module. Used
// by the parser but also by some modules to load prerequisite modules.
@@ -96,9 +98,9 @@ namespace build2
// its tools).
//
bool
- load_module (const string& name,
- scope& root,
+ load_module (scope& root,
scope& base,
+ const string& name,
const location&,
bool optional = false,
const variable_map& config_hints = variable_map ());
diff --git a/build2/module.cxx b/build2/module.cxx
index c2f907f..031ae45 100644
--- a/build2/module.cxx
+++ b/build2/module.cxx
@@ -15,7 +15,7 @@ namespace build2
available_module_map builtin_modules;
void
- boot_module (const string& name, scope& rs, const location& loc)
+ boot_module (scope& rs, const string& name, const location& loc)
{
// First see if this modules has already been loaded for this project.
//
@@ -50,9 +50,9 @@ namespace build2
}
bool
- load_module (const string& name,
- scope& rs,
+ load_module (scope& rs,
scope& bs,
+ const string& name,
const location& loc,
bool opt,
const variable_map& hints)
@@ -100,10 +100,13 @@ namespace build2
bool c (l &&
i->second.init (rs, bs, loc, i->second.module, f, opt, hints));
- const variable& lv (var_pool.insert<bool> (name + ".loaded",
- variable_visibility::project));
- const variable& cv (var_pool.insert<bool> (name + ".configured",
- variable_visibility::project));
+ 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;
diff --git a/build2/operation b/build2/operation
index 0daf413..d52c748 100644
--- a/build2/operation
+++ b/build2/operation
@@ -198,20 +198,25 @@ namespace build2
// Meta-operation-specific logic to load the buildfile, search and match
// the targets, and execute the action on the targets.
//
- void (*load) (const path& buildfile,
+ // Note that the model lock is passed locked and is expected to also be
+ // locked on return (but it can be released and re-acquired inside).
+ //
+ void (*load) (ulock&,
scope& root,
+ const path& buildfile,
const dir_path& out_base,
const dir_path& src_base,
const location&);
- void (*search) (scope& root,
+ void (*search) (ulock&,
+ scope& root,
const target_key&,
const location&,
action_targets&);
- void (*match) (action, action_targets&);
+ void (*match) (ulock&, action, action_targets&);
- void (*execute) (action, const action_targets&, bool quiet);
+ void (*execute) (ulock&, action, const action_targets&, bool quiet);
void (*operation_post) (operation_id); // End of operation batch.
void (*meta_operation_post) (); // End of meta-operation batch.
@@ -229,8 +234,9 @@ namespace build2
// scope.
//
void
- load (const path& buildfile,
+ load (ulock&,
scope& root,
+ const path& buildfile,
const dir_path& out_base,
const dir_path& src_base,
const location&);
@@ -239,17 +245,17 @@ namespace build2
// that does just that and adds a pointer to the target to the list.
//
void
- search (scope&, const target_key&, const location&, action_targets&);
+ search (ulock&, scope&, const target_key&, const location&, action_targets&);
void
- match (action, action_targets&);
+ match (ulock&, action, action_targets&);
// Execute the action on the list of targets. This is the default
// implementation that does just that while issuing appropriate
// diagnostics (unless quiet).
//
void
- execute (action, const action_targets&, bool quiet);
+ execute (ulock&, action, const action_targets&, bool quiet);
extern const meta_operation_info noop;
extern const meta_operation_info perform;
diff --git a/build2/operation.cxx b/build2/operation.cxx
index 545bc9e..3871970 100644
--- a/build2/operation.cxx
+++ b/build2/operation.cxx
@@ -44,8 +44,9 @@ namespace build2
// perform
//
void
- load (const path& bf,
+ load (ulock&,
scope& root,
+ const path& bf,
const dir_path& out_base,
const dir_path& src_base,
const location&)
@@ -62,11 +63,12 @@ namespace build2
// Load the buildfile unless it has already been loaded.
//
- source_once (bf, root, base, root);
+ source_once (root, base, bf, root);
}
void
- search (scope&,
+ search (ulock&,
+ scope&,
const target_key& tk,
const location& l,
action_targets& ts)
@@ -81,26 +83,38 @@ namespace build2
}
void
- match (action a, action_targets& ts)
+ match (ulock& ml, action a, action_targets& ts)
{
tracer trace ("match");
if (verb >= 6)
dump (a);
- for (void* vt: ts)
+ // Relock for shared access.
+ //
+ ml.unlock ();
+
{
- target& t (*static_cast<target*> (vt));
- l5 ([&]{trace << "matching " << t;});
- match (a, t);
+ for (void* vt: ts)
+ {
+ target& t (*static_cast<target*> (vt));
+ l5 ([&]{trace << "matching " << t;});
+
+ slock sl (*ml.mutex ());
+ model_lock = &sl; // @@ Guard?
+ match (sl, a, t);
+ model_lock = nullptr;
+ }
}
+ ml.lock ();
+
if (verb >= 6)
dump (a);
}
void
- execute (action a, const action_targets& ts, bool quiet)
+ execute (ulock& ml, action a, const action_targets& ts, bool quiet)
{
tracer trace ("execute");
@@ -109,13 +123,21 @@ namespace build2
//
vector<reference_wrapper<target>> psp;
- auto body = [a, quiet, &psp, &trace] (void* v)
+ auto body = [&ml, a, quiet, &psp, &trace] (void* v)
{
target& t (*static_cast<target*> (v));
l5 ([&]{trace << diag_doing (a, t);});
- switch (execute (a, t))
+ target_state ts;
+ {
+ slock sl (*ml.mutex ());
+ model_lock = &sl; // @@ Guard?
+ ts = execute (a, t);
+ model_lock = nullptr;
+ }
+
+ switch (ts)
{
case target_state::unchanged:
{
@@ -135,11 +157,17 @@ namespace build2
}
};
+ // Relock for shared access.
+ //
+ ml.unlock ();
+
if (current_mode == execution_mode::first)
for (void* v: ts) body (v);
else
for (void* v: reverse_iterate (ts)) body (v);
+ ml.lock ();
+
// We should have executed every target that we matched.
//
assert (dependency_count == 0);
diff --git a/build2/parser b/build2/parser
index f91bbb6..e589948 100644
--- a/build2/parser
+++ b/build2/parser
@@ -521,7 +521,6 @@ namespace build2
protected:
bool pre_parse_ = false;
-
bool boot_;
const path* path_; // Current path.
diff --git a/build2/parser.cxx b/build2/parser.cxx
index ec9d9e4..fe12b98 100644
--- a/build2/parser.cxx
+++ b/build2/parser.cxx
@@ -457,7 +457,8 @@ namespace build2
type att (tt);
const variable& var (
- var_pool.insert (parse_variable_name (move (pns), ploc)));
+ var_pool.rw (*scope_).insert (
+ parse_variable_name (move (pns), ploc)));
// Apply variable attributes.
//
@@ -725,7 +726,8 @@ namespace build2
if (tt == type::assign || tt == type::prepend || tt == type::append)
{
const variable& var (
- var_pool.insert (parse_variable_name (move (ns), nloc)));
+ var_pool.rw (*scope_).insert (
+ parse_variable_name (move (ns), nloc)));
// Apply variable attributes.
//
@@ -1049,9 +1051,10 @@ namespace build2
// Is this the 'foo=...' case?
//
size_t p (t.value.find ('='));
+ auto& vp (var_pool.rw (*scope_));
if (p != string::npos)
- var = &var_pool.insert (split (p));
+ var = &vp.insert (split (p));
//
// This could still be the 'foo =...' case.
//
@@ -1066,7 +1069,7 @@ namespace build2
(v[p = 0] == '=' ||
(n > 1 && v[0] == '+' && v[p = 1] == '=')))
{
- var = &var_pool[t.value];
+ var = &vp.insert (move (t.value));
next (t, tt); // Get the peeked token.
split (p); // Returned name should be empty.
}
@@ -1235,9 +1238,9 @@ namespace build2
assert (v.empty ()); // Module versioning not yet implemented.
if (boot_)
- boot_module (n, *root_, l);
+ boot_module (*root_, n, l);
else
- load_module (n, *root_, *scope_, l, optional);
+ load_module (*root_, *scope_, n, l, optional);
}
}
@@ -1503,8 +1506,12 @@ namespace build2
value& lhs (
kind == type::assign
- ? target_ != nullptr ? target_->assign (var) : scope_->assign (var)
- : target_ != nullptr ? target_->append (var) : scope_->append (var));
+ ? (target_ != nullptr
+ ? target_->assign (var)
+ : scope_->assign (var))
+ : (target_ != nullptr
+ ? target_->append (var)
+ : scope_->append (var)));
apply_value_attributes (&var, lhs, move (rhs), kind);
}
@@ -3491,7 +3498,7 @@ namespace build2
// Lookup.
//
- const auto& var (var_pool.insert (move (name)));
+ const auto& var (var_pool.rw (*scope_).insert (move (name)));
return target_ != nullptr ? (*target_)[var] : (*scope_)[var];
// Undefined/NULL namespace variables are not allowed.
diff --git a/build2/pkgconfig/init.cxx b/build2/pkgconfig/init.cxx
index ff386bb..352e845 100644
--- a/build2/pkgconfig/init.cxx
+++ b/build2/pkgconfig/init.cxx
@@ -39,7 +39,7 @@ namespace build2
//
// config.pkgconfig.target is a hint.
//
- auto& vp (var_pool);
+ auto& vp (var_pool.rw (rs));
const variable& c_x (vp.insert<path> ("config.pkgconfig", true));
const variable& x_path (vp.insert<process_path> ("pkgconfig.path"));
@@ -142,7 +142,7 @@ namespace build2
//
if (!cast_false<bool> (rs["pkgconfig.config.loaded"]))
{
- if (!load_module ("pkgconfig.config", rs, rs, loc, optional, hints))
+ if (!load_module (rs, rs, "pkgconfig.config", loc, optional, hints))
return false;
}
else if (!cast_false<bool> (rs["pkgconfig.config.configured"]))
diff --git a/build2/rule b/build2/rule
index d01df6b..cc98bfa 100644
--- a/build2/rule
+++ b/build2/rule
@@ -37,10 +37,10 @@ namespace build2
{
public:
virtual match_result
- match (action, target&, const string& hint) const = 0;
+ match (slock&, action, target&, const string& hint) const = 0;
virtual recipe
- apply (action, target&) const = 0;
+ apply (slock&, action, target&) const = 0;
};
// Fallback rule that only matches if the file exists.
@@ -51,10 +51,10 @@ namespace build2
file_rule () {}
virtual match_result
- match (action, target&, const string& hint) const override;
+ match (slock&, action, target&, const string&) const override;
virtual recipe
- apply (action, target&) const override;
+ apply (slock&, action, target&) const override;
static const file_rule instance;
};
@@ -65,10 +65,10 @@ namespace build2
alias_rule () {}
virtual match_result
- match (action, target&, const string& hint) const override;
+ match (slock&, action, target&, const string&) const override;
virtual recipe
- apply (action, target&) const override;
+ apply (slock&, action, target&) const override;
static const alias_rule instance;
};
@@ -79,10 +79,10 @@ namespace build2
fsdir_rule () {}
virtual match_result
- match (action, target&, const string& hint) const override;
+ match (slock&, action, target&, const string&) const override;
virtual recipe
- apply (action, target&) const override;
+ apply (slock&, action, target&) const override;
static target_state
perform_update (action, target&);
@@ -101,10 +101,13 @@ namespace build2
fallback_rule () {}
virtual match_result
- match (action, target&, const string&) const override {return true;}
+ match (slock&, action, target&, const string&) const override
+ {
+ return true;
+ }
virtual recipe
- apply (action, target&) const override {return noop_recipe;}
+ apply (slock&, action, target&) const override {return noop_recipe;}
static const fallback_rule instance;
};
diff --git a/build2/rule.cxx b/build2/rule.cxx
index 42d5eb8..88c7941 100644
--- a/build2/rule.cxx
+++ b/build2/rule.cxx
@@ -26,7 +26,7 @@ namespace build2
// use it as a guide to implement your own, normal, rules.
//
match_result file_rule::
- match (action a, target& t, const string&) const
+ match (slock&, action a, target& t, const string&) const
{
tracer trace ("file_rule::match");
@@ -79,7 +79,7 @@ namespace build2
}
recipe file_rule::
- apply (action a, target& t) const
+ apply (slock& ml, action a, target& t) const
{
// Update triggers the update of this target's prerequisites so it would
// seem natural that we should also trigger their cleanup. However, this
@@ -99,7 +99,7 @@ namespace build2
// Search and match all the prerequisites.
//
- search_and_match_prerequisites (a, t);
+ search_and_match_prerequisites (ml, a, t);
// Note that we used to provide perform_update() which checked that this
// target is not older than any of its prerequisites. However, later we
@@ -115,20 +115,20 @@ namespace build2
// alias_rule
//
match_result alias_rule::
- match (action, target&, const string&) const
+ match (slock&, action, target&, const string&) const
{
return true;
}
recipe alias_rule::
- apply (action a, target& t) const
+ apply (slock& ml, action a, target& t) const
{
// Inject dependency on our directory (note: not parent) so that it is
// automatically created on update and removed on clean.
//
- inject_fsdir (a, t, false);
+ inject_fsdir (ml, a, t, false);
- search_and_match_prerequisites (a, t);
+ search_and_match_prerequisites (ml, a, t);
return default_recipe;
}
@@ -137,22 +137,22 @@ namespace build2
// fsdir_rule
//
match_result fsdir_rule::
- match (action, target&, const string&) const
+ match (slock&, action, target&, const string&) const
{
return true;
}
recipe fsdir_rule::
- apply (action a, target& t) const
+ apply (slock& ml, action a, target& t) const
{
// Inject dependency on the parent directory. Note that we don't do it for
// clean since we shouldn't (and can't possibly, since it's our parent) be
// removing it.
//
if (a.operation () != clean_id)
- inject_fsdir (a, t);
+ inject_fsdir (ml, a, t);
- search_and_match_prerequisites (a, t);
+ search_and_match_prerequisites (ml, a, t);
switch (a)
{
diff --git a/build2/scope b/build2/scope
index eff60b0..16ddfbb 100644
--- a/build2/scope
+++ b/build2/scope
@@ -94,9 +94,17 @@ namespace build2
}
lookup
+ operator[] (const variable* var) const // For cached variables.
+ {
+ assert (var != nullptr);
+ return operator[] (*var);
+ }
+
+ lookup
operator[] (const string& name) const
{
- return operator[] (var_pool[name]);
+ const variable* var (var_pool.find (name));
+ return var != nullptr ? operator[] (*var) : lookup ();
}
// As above, but include target type/pattern-specific variables.
@@ -108,23 +116,11 @@ namespace build2
}
lookup
- find (const string& var, const target_key& tk) const
- {
- return find (var_pool[var], tk);
- }
-
- lookup
find (const variable& var, const target_type& tt, const string& tn) const
{
return find (var, &tt, &tn).first;
}
- lookup
- find (const string& var, const target_type& tt, const string& tn) const
- {
- return find (var_pool[var], tt, tn);
- }
-
pair<lookup, size_t>
find (const variable& var,
const target_type* tt = nullptr,
@@ -149,24 +145,35 @@ namespace build2
pair<lookup, size_t> original,
bool target = false) const;
- // Return a value suitable for assignment (or append if you only
- // want to append to the value from this scope). If the variable
- // does not exist in this scope's map, then a new one with the
- // NULL value is added and returned. Otherwise the existing value
- // is returned.
+ // Return a value suitable for assignment (or append if you only want to
+ // append to the value from this scope). If the value does not exist in
+ // this scope's map, then a new one with the NULL value is added and
+ // returned. Otherwise the existing value is returned.
//
value&
assign (const variable& var) {return vars.assign (var);}
value&
- assign (const string& name) {return vars.assign (name);}
+ assign (const variable* var) // For cached variables.
+ {
+ assert (var != nullptr);
+ return vars.assign (*var);
+ }
+
+ value&
+ assign (string name)
+ {
+ return assign (variable_pool::instance.insert (move (name)));
+ }
- // Unlike the two above, assign a typed non-overridable variable with
- // normal visibility.
+ // Assign a typed non-overridable variable with normal visibility.
//
template <typename T>
value&
- assign (string name) {return vars.assign<T> (move (name));}
+ assign (string name)
+ {
+ return vars.assign (variable_pool::instance.insert<T> (move (name)));
+ }
// Return a value suitable for appending. If the variable does not
// exist in this scope's map, then outer scopes are searched for
@@ -177,9 +184,6 @@ namespace build2
value&
append (const variable&);
- value&
- append (const string& name) {return append (var_pool[name]);}
-
// Target type/pattern-specific variables.
//
variable_type_map target_vars;
@@ -278,7 +282,10 @@ namespace build2
}
};
- // Note that the scope map is only for paths from the out tree.
+ // Scope map.
+ //
+ // Protected by the model mutex. Note that the scope map is only for paths
+ // from the out tree.
//
using scope_map_base = butl::dir_path_map<scope>;
diff --git a/build2/target b/build2/target
index 339ff90..0dec5d4 100644
--- a/build2/target
+++ b/build2/target
@@ -331,9 +331,17 @@ namespace build2
}
lookup
+ operator[] (const variable* var) const // For cached variables.
+ {
+ assert (var != nullptr);
+ return operator[] (*var);
+ }
+
+ lookup
operator[] (const string& name) const
{
- return operator[] (var_pool[name]);
+ const variable* var (var_pool.find (name));
+ return var != nullptr ? operator[] (*var) : lookup ();
}
// As above but also return the depth at which the value is found. The
@@ -354,9 +362,6 @@ namespace build2
: base_scope ().find_override (var, move (p), true);
}
- pair<lookup, size_t>
- find (const string& name) const {return find (var_pool[name]);}
-
// If target_only is true, then only look in target and its target group
// without continuing in scopes.
//
@@ -368,27 +373,11 @@ namespace build2
value&
assign (const variable& var) {return vars.assign (var);}
- value&
- assign (const string& name) {return vars.assign (name);}
-
- // Unlike the two above, assign a typed non-overridable variable with
- // normal visibility.
- //
- template <typename T>
- value&
- assign (string name) {return vars.assign<T> (move (name)).first.get ();}
-
// Return a value suitable for appending. See class scope for details.
//
value&
append (const variable&);
- value&
- append (const string& name)
- {
- return append (var_pool[name]);
- }
-
// A target that is not (yet) entered as part of a real dependency
// declaration (for example, that is entered as part of a target-specific
// variable assignment) is called implied.
@@ -810,17 +799,17 @@ namespace build2
template <typename T>
inline prerequisite_members_range<T>
- prerequisite_members (action a, T&& x, bool members = true)
+ prerequisite_members (slock& ml, action a, T&& x, bool members = true)
{
- return prerequisite_members_range<T> (a, forward<T> (x), members);
+ return prerequisite_members_range<T> (ml, a, forward<T> (x), members);
}
template <typename T>
class prerequisite_members_range
{
public:
- prerequisite_members_range (action a, T&& r, bool m)
- : a_ (a), members_ (m), r_ (forward<T> (r)), e_ (r_.end ()) {}
+ prerequisite_members_range (slock& l, action a, T&& r, bool m)
+ : l_ (l), a_ (a), members_ (m), r_ (forward<T> (r)), e_ (r_.end ()) {}
using base_iterator = decltype (declval<T> ().begin ());
@@ -925,6 +914,7 @@ namespace build2
end () const {return iterator (this, e_);}
private:
+ slock& l_;
action a_;
bool members_; // Go into group members by default?
T r_;
@@ -934,35 +924,38 @@ namespace build2
// prerequisite_members(t.prerequisites)
//
inline auto
- prerequisite_members (action a, target& t, bool members = true)
+ prerequisite_members (slock& ml, action a, target& t, bool members = true)
{
- return prerequisite_members (a, t.prerequisites, members);
+ return prerequisite_members (ml, a, t.prerequisites, members);
}
// prerequisite_members(reverse_iterate(t.prerequisites))
//
inline auto
- reverse_prerequisite_members (action a, target& t, bool members = true)
+ reverse_prerequisite_members (
+ slock& ml, action a, target& t, bool members = true)
{
return prerequisite_members (
- a, reverse_iterate (t.prerequisites), members);
+ ml, a, reverse_iterate (t.prerequisites), members);
}
// prerequisite_members(group_prerequisites (t))
//
inline auto
- group_prerequisite_members (action a, target& t, bool members = true)
+ group_prerequisite_members (
+ slock& ml, action a, target& t, bool members = true)
{
- return prerequisite_members (a, group_prerequisites (t), members);
+ return prerequisite_members (ml, a, group_prerequisites (t), members);
}
// prerequisite_members(reverse_iterate (group_prerequisites (t)))
//
inline auto
- reverse_group_prerequisite_members (action a, target& t, bool members = true)
+ reverse_group_prerequisite_members (
+ slock& ml, action a, target& t, bool members = true)
{
return prerequisite_members (
- a, reverse_iterate (group_prerequisites (t)), members);
+ ml, a, reverse_iterate (group_prerequisites (t)), members);
}
// A target with an unspecified extension is considered equal to the one
diff --git a/build2/target.cxx b/build2/target.cxx
index 64b376c..81d613f 100644
--- a/build2/target.cxx
+++ b/build2/target.cxx
@@ -613,8 +613,9 @@ namespace build2
out_base.normalize ();
// In our world modifications to the scope structure during search &
- // match should be "pure" in the sense that they should not affect any
- // existing targets that have already been searched & matched.
+ // match should be "pure append" in the sense that they should not
+ // affect any existing targets that have already been searched &
+ // matched.
//
// A straightforward way to enforce this is to not allow any existing
// targets to be inside any newly created scopes (except, perhaps for
@@ -625,6 +626,10 @@ namespace build2
// not a subdirectory of out_base. So for now we just assume that this
// is so. And so it is.
+ // Relock for exclusive access.
+ //
+ rlock rl (model_lock);
+
pair<scope&, scope*> sp (switch_scope (*s.root_scope (), out_base));
if (sp.second != nullptr) // Ignore scopes out of any project.
@@ -638,7 +643,7 @@ namespace build2
{
l5 ([&]{trace << "loading buildfile " << bf << " for " << pk;});
- if (source_once (bf, root, base, root))
+ if (source_once (root, base, bf, root))
{
// If we loaded the buildfile, examine the target again.
//
diff --git a/build2/target.ixx b/build2/target.ixx
index e2ac111..f3417aa 100644
--- a/build2/target.ixx
+++ b/build2/target.ixx
@@ -23,7 +23,7 @@ namespace build2
// prerequisite_members
//
group_view
- resolve_group_members (action, target&); // <build2/algorithm>
+ resolve_group_members (slock&, action, target&); // <build2/algorithm>
template <typename T>
inline auto prerequisite_members_range<T>::iterator::
@@ -78,7 +78,7 @@ namespace build2
{
// Otherwise assume it is a normal group.
//
- g_ = resolve_group_members (r_->a_, search (*i_));
+ g_ = resolve_group_members (r_->l_, r_->a_, search (*i_));
if (g_.members == nullptr) // Members are not know.
{
diff --git a/build2/target.txx b/build2/target.txx
index b72af7d..5519126 100644
--- a/build2/target.txx
+++ b/build2/target.txx
@@ -18,7 +18,7 @@ namespace build2
//
do
{
- g_ = resolve_group_members (r_->a_, search (*i_));
+ g_ = resolve_group_members (r_->l_, r_->a_, search (*i_));
assert (g_.members != nullptr); // Group could not be resolved.
if (g_.count != 0) // Skip empty see through groups.
@@ -45,7 +45,7 @@ namespace build2
{
// Include target type/pattern-specific variables.
//
- if (auto l = s.find (var, tk))
+ if (auto l = s.find (var_pool[var], tk))
{
// Help the user here and strip leading '.' from the extension.
//
diff --git a/build2/test/init.cxx b/build2/test/init.cxx
index 94e4073..fd8db99 100644
--- a/build2/test/init.cxx
+++ b/build2/test/init.cxx
@@ -36,7 +36,7 @@ namespace build2
// Enter module variables. Do it during boot in case they get assigned
// in bootstrap.build.
//
- auto& vp (var_pool);
+ auto& vp (var_pool.rw (rs));
// Tests to execute.
//
diff --git a/build2/test/rule b/build2/test/rule
index 20ad905..0e208ff 100644
--- a/build2/test/rule
+++ b/build2/test/rule
@@ -21,7 +21,7 @@ namespace build2
{
public:
virtual match_result
- match (action, target&, const string&) const override;
+ match (slock&, action, target&, const string&) const override;
target_state
perform_script (action, target&) const;
@@ -31,7 +31,7 @@ namespace build2
{
public:
virtual recipe
- apply (action, target&) const override;
+ apply (slock&, action, target&) const override;
static target_state
perform_test (action, target&);
@@ -41,7 +41,7 @@ namespace build2
{
public:
virtual recipe
- apply (action, target&) const override;
+ apply (slock&, action, target&) const override;
target_state
perform_test (action, target&) const;
diff --git a/build2/test/rule.cxx b/build2/test/rule.cxx
index 1f27979..17821a4 100644
--- a/build2/test/rule.cxx
+++ b/build2/test/rule.cxx
@@ -35,7 +35,7 @@ namespace build2
"insufficient space");
match_result rule_common::
- match (action a, target& t, const string&) const
+ match (slock& ml, action a, target& t, const string&) const
{
// The (admittedly twisted) logic of this rule tries to achieve the
// following: If the target is testable, then we want both first update
@@ -63,7 +63,7 @@ namespace build2
// If we have any prerequisites of the test{} type, then this is the
// testscript case.
//
- for (prerequisite_member p: group_prerequisite_members (a, t))
+ for (prerequisite_member p: group_prerequisite_members (ml, a, t))
{
if (p.is_a<testscript> ())
{
@@ -91,7 +91,7 @@ namespace build2
// Use lookup depths to figure out who "overrides" whom.
//
- auto p (t.find ("test"));
+ auto p (t.find (var_pool["test"]));
const name* n (cast_null<name> (p.first));
// Note that test can be set to an "override" target.
@@ -102,7 +102,7 @@ namespace build2
{
auto test = [&t, &p] (const char* var)
{
- return t.find (var).second < p.second;
+ return t.find (var_pool[var]).second < p.second;
};
md.test =
@@ -155,7 +155,7 @@ namespace build2
}
recipe alias_rule::
- apply (action a, target& t) const
+ apply (slock& ml, action a, target& t) const
{
match_data md (move (t.data<match_data> ()));
t.clear_data (); // In case delegated-to rule also uses aux storage.
@@ -171,7 +171,7 @@ namespace build2
// standard alias rule.
//
if (a.operation () == update_id)
- return match_delegate (a, t, *this).first;
+ return match_delegate (ml, a, t, *this).first;
// For the test operation we have to implement our own search and match
// because we need to ignore prerequisites that are outside of our
@@ -181,7 +181,7 @@ namespace build2
// not ours seems right. Note that we still want to make sure they are
// up to date (via the above delegate) since our tests might use them.
//
- search_and_match_prerequisites (a, t, t.root_scope ());
+ search_and_match_prerequisites (ml, a, t, t.root_scope ());
// If not a test then also redirect to the alias rule.
//
@@ -191,7 +191,7 @@ namespace build2
}
recipe rule::
- apply (action a, target& t) const
+ apply (slock& ml, action a, target& t) const
{
tracer trace ("test::rule::apply");
@@ -208,11 +208,11 @@ namespace build2
if (md.script)
{
if (a.operation () == update_id)
- return match_delegate (a, t, *this).first;
+ return match_delegate (ml, a, t, *this).first;
// Collect all the testscript targets in prerequisite_targets.
//
- for (prerequisite_member p: group_prerequisite_members (a, t))
+ for (prerequisite_member p: group_prerequisite_members (ml, a, t))
{
if (p.is_a<testscript> ())
t.prerequisite_targets.push_back (&p.search ());
@@ -229,10 +229,10 @@ namespace build2
// We should have either arguments or input/roundtrip. Again, use
// lookup depth to figure out who takes precedence.
//
- auto ip (t.find ("test.input"));
- auto op (t.find ("test.output"));
- auto rp (t.find ("test.roundtrip"));
- auto ap (t.find ("test.arguments"));
+ auto ip (t.find (var_pool["test.input"]));
+ auto op (t.find (var_pool["test.output"]));
+ auto rp (t.find (var_pool["test.roundtrip"]));
+ auto ap (t.find (var_pool["test.arguments"]));
auto test = [&t] (pair<lookup, size_t>& x, const char* xn,
pair<lookup, size_t>& y, const char* yn)
@@ -286,7 +286,7 @@ namespace build2
//
if (it != nullptr)
{
- build2::match (a, *it);
+ build2::match (ml, a, *it);
if (it->state () == target_state::unchanged)
{
@@ -299,7 +299,7 @@ namespace build2
{
if (in != on)
{
- build2::match (a, *ot);
+ build2::match (ml, a, *ot);
if (ot->state () == target_state::unchanged)
{
@@ -315,7 +315,7 @@ namespace build2
// been found if we signalled that we do not match from match()
// above.
//
- recipe d (match_delegate (a, t, *this).first);
+ recipe d (match_delegate (ml, a, t, *this).first);
// If we have no input/output that needs updating, then simply
// redirect to it.
diff --git a/build2/types b/build2/types
index f8abef2..1e7a347 100644
--- a/build2/types
+++ b/build2/types
@@ -9,7 +9,6 @@
#include <tuple>
#include <vector>
#include <string>
-#include <future>
#include <memory> // unique_ptr, shared_ptr
#include <utility> // pair, move()
#include <cstddef> // size_t, nullptr_t
@@ -19,6 +18,11 @@
#include <functional> // function, reference_wrapper
#include <initializer_list>
+#include <mutex>
+#include <future>
+#include <butl/ft/shared_mutex>
+#include <shared_mutex>
+
#include <ios> // ios_base::failure
#include <exception> // exception
#include <stdexcept> // logic_error, invalid_argument, runtime_error
@@ -69,10 +73,56 @@ namespace build2
using std::istream;
using std::ostream;
+ // Concurrency.
+ //
using std::future;
- // Exceptions. While <exception> is included, there is no using for
- // std::exception -- use qualified.
+#ifdef __cpp_lib_shared_mutex
+ using shared_mutex = std::shared_mutex;
+#else
+ using shared_mutex = std::shared_timed_mutex;
+#endif
+
+ using slock = std::shared_lock<shared_mutex>;
+ using ulock = std::unique_lock<shared_mutex>;
+
+ // Re-lock shared to exclusive for the lifetime or rlock.
+ //
+ struct rlock
+ {
+ explicit
+ rlock (slock* sl)
+ : sl_ (sl)
+ {
+ if (sl_ != nullptr)
+ {
+ sl_->unlock ();
+ ul_ = ulock (*sl_->mutex ());
+ }
+ }
+
+ ~rlock ()
+ {
+ if (sl_ != nullptr)
+ {
+ ul_.unlock ();
+ sl_->lock ();
+ }
+ }
+
+ // Can be treated as const ulock.
+ //
+ operator const ulock& () const {return ul_;}
+
+ private:
+ slock* sl_;
+ ulock ul_;
+ };
+
+ // Exceptions.
+ //
+ // While <exception> is included, there is no using for std::exception --
+ // use qualified.
//
using std::logic_error;
using std::invalid_argument;
diff --git a/build2/variable b/build2/variable
index 2206fe0..4377d7b 100644
--- a/build2/variable
+++ b/build2/variable
@@ -755,36 +755,32 @@ namespace build2
static const value_type_ex value_type;
};
- // variable_pool
+ // Variable pool.
+ //
+ // Protected by the model mutex.
//
class variable_pool
{
public:
- // Find existing or insert new. Find bias.
+ // Find existing or insert new.
//
const variable&
- operator[] (const string& name);
-
- // Find existing or insert new. Insert bias.
- //
- const variable&
- insert (string name);
+ operator[] (const string& name) const;
// Return NULL if there is no variable with this name.
//
const variable*
- find (const string& name);
+ find (const string& name) const;
- // Insert or override.
+ // Find existing or insert new (untyped, non-overridable, normal
+ // visibility).
//
- template <typename T>
const variable&
- insert (string name)
- {
- return insert (
- move (name), &value_traits<T>::value_type, nullptr, nullptr);
- }
+ insert (string name);
+ // Insert or override (type/visibility). Note that by default the
+ // variable is not overridable.
+ //
const variable&
insert (string name, variable_visibility v)
{
@@ -805,9 +801,16 @@ namespace build2
template <typename T>
const variable&
+ insert (string name)
+ {
+ return insert (move (name), &value_traits<T>::value_type);
+ }
+
+ template <typename T>
+ const variable&
insert (string name, variable_visibility v)
{
- return insert (move (name), &value_traits<T>::value_type, &v, nullptr);
+ return insert (move (name), &value_traits<T>::value_type, &v);
}
template <typename T>
@@ -829,12 +832,29 @@ namespace build2
void
clear () {map_.clear ();}
+ // Proof of lock for RW access.
+ //
+ variable_pool&
+ rw (const ulock&) const {return const_cast<variable_pool&> (*this);}
+
+ variable_pool&
+ rw (scope&) const {return const_cast<variable_pool&> (*this);}
+
private:
+ // Classes that can access bypassing the lock.
+ //
+ friend class scope;
+
+ static variable_pool instance;
+
const variable&
insert (string name,
const build2::value_type*,
- const variable_visibility*,
- const bool* overridable);
+ const variable_visibility* = nullptr,
+ const bool* overridable = nullptr);
+
+ public:
+ static const variable_pool& cinstance; // For var_pool initialization.
private:
using key = butl::map_key<string>;
@@ -860,7 +880,7 @@ namespace build2
map map_;
};
- extern variable_pool var_pool;
+ extern const variable_pool& var_pool;
}
// variable_map
@@ -929,9 +949,17 @@ namespace build2
}
lookup
+ operator[] (const variable* var) const // For cached variables.
+ {
+ assert (var != nullptr);
+ return operator[] (*var);
+ }
+
+ lookup
operator[] (const string& name) const
{
- return operator[] (var_pool[name]);
+ const variable* var (var_pool.find (name));
+ return var != nullptr ? operator[] (*var) : lookup ();
}
// If typed is false, leave the value untyped even if the variable is.
@@ -947,18 +975,10 @@ namespace build2
value&
assign (const variable& var) {return insert (var).first;}
- value&
- assign (const string& name) {return insert (name).first;}
-
- // Unlike the two above, assign a typed, non-overridable variable with
- // normal visibility.
+ // Note that the variable is expected to have already been registered.
//
- template <typename T>
value&
- assign (string name)
- {
- return assign (var_pool.insert<T> (move (name)));
- }
+ assign (const string& name) {return insert (var_pool[name]).first;}
// As above but also return an indication of whether the new value (which
// will be NULL) was actually inserted. Similar to find(), if typed is
@@ -967,16 +987,10 @@ namespace build2
pair<reference_wrapper<value>, bool>
insert (const variable&, bool typed = true);
- pair<reference_wrapper<value>, bool>
- insert (const string& name, bool typed = true)
- {
- return insert (var_pool[name], typed);
- }
-
pair<const_iterator, const_iterator>
- find_namespace (const string& ns) const
+ find_namespace (const variable& ns) const
{
- auto r (m_.find_prefix (var_pool[ns]));
+ auto r (m_.find_prefix (ns));
return make_pair (const_iterator (r.first), const_iterator (r.second));
}
diff --git a/build2/variable.cxx b/build2/variable.cxx
index 48ea8aa..ec72fdc 100644
--- a/build2/variable.cxx
+++ b/build2/variable.cxx
@@ -906,6 +906,23 @@ namespace build2
// variable_pool
//
const variable& variable_pool::
+ insert (string n)
+ {
+ // We are not overriding anything so skip the insert_() checks.
+ //
+ auto p (
+ insert (
+ variable {move (n), nullptr, nullptr, variable_visibility::normal}));
+
+ const variable& r (p.first->second);
+
+ if (r.override != nullptr)
+ fail << "variable " << r.name << " cannot be overridden";
+
+ return r;
+ }
+
+ const variable& variable_pool::
insert (string n,
const build2::value_type* t,
const variable_visibility* v,
@@ -943,16 +960,18 @@ namespace build2
}
// Check overridability (all overrides, if any, should already have
- // been enetered (see context.cxx:reset()).
+ // been entered (see context.cxx:reset()).
//
- if (o != nullptr && r.override != nullptr && !*o)
+ if (r.override != nullptr && (o == nullptr || !*o))
fail << "variable " << r.name << " cannot be overridden";
}
return r;
}
- variable_pool var_pool;
+ variable_pool variable_pool::instance;
+ const variable_pool& variable_pool::cinstance = variable_pool::instance;
+ const variable_pool& var_pool = variable_pool::cinstance;
// variable_map
//
diff --git a/build2/variable.ixx b/build2/variable.ixx
index bb97750..816f4bf 100644
--- a/build2/variable.ixx
+++ b/build2/variable.ixx
@@ -622,33 +622,19 @@ namespace build2
// variable_pool
//
- inline const variable* variable_pool::
- find (const string& n)
- {
- auto i (map_.find (&n));
- return i != map_.end () ? &i->second : nullptr;
- }
-
inline const variable& variable_pool::
- insert (string n)
+ operator[] (const string& n) const
{
- // We are not overriding anything so skip the custom insert() checks.
- //
- auto p (
- insert (
- variable {move (n), nullptr, nullptr, variable_visibility::normal}));
-
- return p.first->second;
+ const variable* r (find (n));
+ assert (r != nullptr);
+ return *r;
}
-
- inline const variable& variable_pool::
- operator[] (const string& n)
+ inline const variable* variable_pool::
+ find (const string& n) const
{
- if (const variable* v = find (n))
- return *v;
- else
- return insert (n);
+ auto i (map_.find (&n));
+ return i != map_.end () ? &i->second : nullptr;
}
// variable_map::iterator_adapter
diff --git a/unit-tests/function/buildfile b/unit-tests/function/buildfile
index 5c326b8..27c73d1 100644
--- a/unit-tests/function/buildfile
+++ b/unit-tests/function/buildfile
@@ -4,6 +4,8 @@
#@@ Temporary until we get utility library support.
#
+if ($cxx.target.class != "windows")
+ cxx.libs += -lpthread
import libs = libbutl%lib{butl}
src = token lexer diagnostics utility variable name b-options types-parsers \
context scope parser target operation rule prerequisite file module function \
@@ -11,6 +13,7 @@ functions-builtin functions-path functions-process-path functions-string \
functions-target-triplet algorithm search dump filesystem \
config/{utility init operation}
+
exe{driver}: cxx{driver} ../../build2/cxx{$src} $libs test{call syntax}
include ../../build2/
diff --git a/unit-tests/function/driver.cxx b/unit-tests/function/driver.cxx
index 2f605b0..4fbbdb6 100644
--- a/unit-tests/function/driver.cxx
+++ b/unit-tests/function/driver.cxx
@@ -26,7 +26,9 @@ namespace build2
main (int, char* argv[])
{
init (argv[0], 1); // Fake build system driver, default verbosity.
- reset (strings ()); // No command line variables.
+
+ ulock ml (model);
+ reset (ml, strings ()); // No command line variables.
function_family f ("dummy");
diff --git a/unit-tests/test/script/parser/driver.cxx b/unit-tests/test/script/parser/driver.cxx
index f641dc4..1e959c1 100644
--- a/unit-tests/test/script/parser/driver.cxx
+++ b/unit-tests/test/script/parser/driver.cxx
@@ -142,9 +142,10 @@ namespace build2
{
tracer trace ("main");
- init (argv[0], 1); // Fake build system driver, default verbosity.
- sched.startup (1); // Serial execution.
- reset (strings ()); // No command line variables.
+ init (argv[0], 1); // Fake build system driver, default verbosity.
+ ulock ml (model);
+ sched.startup (1); // Serial execution.
+ reset (ml, strings ()); // No command line variables.
bool scope (false);
bool id (false);
@@ -191,7 +192,7 @@ namespace build2
value& v (
tt.assign (
- var_pool.insert<target_triplet> (
+ var_pool.rw (ml).insert<target_triplet> (
"test.target", variable_visibility::project)));
v = cast<target_triplet> ((*global_scope)["build.host"]);