From 88f0780e34116c0441a8d8c58b8a8fd9fde4b1f5 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 25 Jan 2017 15:41:44 +0200 Subject: Add model mutex, make var_pool const by default --- build2/algorithm | 19 +++---- build2/algorithm.cxx | 30 ++++++----- build2/algorithm.ixx | 49 +++++++++-------- build2/b.cxx | 30 ++++++----- build2/bin/init.cxx | 34 ++++++------ build2/bin/rule | 8 +-- build2/bin/rule.cxx | 16 +++--- build2/c/init.cxx | 26 ++++----- build2/cc/common | 4 +- build2/cc/common.cxx | 10 ++-- build2/cc/compile | 14 ++--- build2/cc/compile.cxx | 48 +++++++++-------- build2/cc/init.cxx | 67 +++++++++++++---------- build2/cc/install | 4 +- build2/cc/install.cxx | 10 ++-- build2/cc/link | 4 +- build2/cc/link.cxx | 22 ++++---- build2/cc/module.cxx | 8 +-- build2/cli/init.cxx | 4 +- build2/cli/rule | 4 +- build2/cli/rule.cxx | 16 +++--- build2/config/init.cxx | 8 +-- build2/config/operation.cxx | 37 +++++++++---- build2/config/utility | 10 +++- build2/config/utility.cxx | 19 +++---- build2/context | 33 ++++++++++-- build2/context.cxx | 60 ++++++++++++++------- build2/cxx/init.cxx | 6 +-- build2/dist/init.cxx | 32 +++++------ build2/dist/operation.cxx | 23 +++++--- build2/dist/rule | 4 +- build2/dist/rule.cxx | 8 +-- build2/file | 8 +-- build2/file.cxx | 83 ++++++++++++++-------------- build2/file.ixx | 4 +- build2/install/init.cxx | 66 ++++++++++++----------- build2/install/rule | 10 ++-- build2/install/rule.cxx | 20 +++---- build2/install/utility | 18 ++++--- build2/module | 10 ++-- build2/module.cxx | 17 +++--- build2/operation | 22 +++++--- build2/operation.cxx | 50 +++++++++++++---- build2/parser | 1 - build2/parser.cxx | 25 +++++---- build2/pkgconfig/init.cxx | 4 +- build2/rule | 23 ++++---- build2/rule.cxx | 22 ++++---- build2/scope | 59 +++++++++++--------- build2/target | 57 +++++++++----------- build2/target.cxx | 11 ++-- build2/target.ixx | 4 +- build2/target.txx | 4 +- build2/test/init.cxx | 2 +- build2/test/rule | 6 +-- build2/test/rule.cxx | 34 ++++++------ build2/types | 56 +++++++++++++++++-- build2/variable | 92 ++++++++++++++++++-------------- build2/variable.cxx | 25 +++++++-- build2/variable.ixx | 30 +++-------- unit-tests/function/buildfile | 3 ++ unit-tests/function/driver.cxx | 4 +- unit-tests/test/script/parser/driver.cxx | 9 ++-- 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 - 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 - 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 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 (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 - 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 - 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 (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 (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 (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 (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 ("bin.rc.path"); v.insert ("bin.ranlib.path"); @@ -528,7 +528,7 @@ namespace build2 if (ranlib != nullptr) { r.assign ("bin.ranlib.path") = move (ari.ranlib_path); - r.assign ("bin.ranlib.id") = move (ari.ranlib_id); + r.assign ("bin.ranlib.id") = move (ari.ranlib_id); r.assign ("bin.ranlib.signature") = move (ari.ranlib_signature); r.assign ("bin.ranlib.checksum") = @@ -554,10 +554,10 @@ namespace build2 // Make sure the bin core and ar.config are loaded. // if (!cast_false (b["bin.loaded"])) - load_module ("bin", r, b, loc, false, hints); + load_module (r, b, "bin", loc, false, hints); if (!cast_false (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 (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 ("bin.ld.path"); v.insert ("config.bin.ld", true); @@ -652,10 +652,10 @@ namespace build2 // Make sure the bin core and ld.config are loaded. // if (!cast_false (b["bin.loaded"])) - load_module ("bin", r, b, loc, false, hints); + load_module (r, b, "bin", loc, false, hints); if (!cast_false (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 (r["bin.ld.id"])); @@ -666,8 +666,8 @@ namespace build2 if (lid == "msvc") { const target_type& pdb (b.derive_target_type ("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 (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 ("bin.rc.path"); v.insert ("config.bin.rc", true); @@ -763,10 +763,10 @@ namespace build2 // Make sure the bin core and rc.config are loaded. // if (!cast_false (b["bin.loaded"])) - load_module ("bin", r, b, loc, false, hints); + load_module (r, b, "bin", loc, false, hints); if (!cast_false (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 (xt)); @@ -77,7 +77,7 @@ namespace build2 if (t.a == nullptr) t.a = &search (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 (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 (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 (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 ("c.loptions"), v.insert ("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 ("c.export.poptions"), v.insert ("c.export.coptions"), v.insert ("c.export.loptions"), v.insert> ("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 ("c.std", variable_visibility::project), @@ -224,7 +224,7 @@ namespace build2 // Load c.config. // if (!cast_false (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 ("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&) 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 -#include // import() +#include // import() #include #include #include @@ -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& 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 ? < : (p.is_a () ? static_cast (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 ) 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 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 (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 ("config.cc.poptions", true); v.insert ("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 (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 (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 (rs["cc.target"]).string (); if (auto l = hints["config.bin.pattern"]) - h.assign ("config.bin.pattern") = cast (l); + { + const variable& p (vp.insert ("config.bin.pattern")); + h.assign (p) = cast (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 (l) != "shared") { if (!cast_false (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 (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 (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 (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 (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 (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 (rs["bin.loaded"])) - load_module ("bin", rs, rs, loc); + load_module (rs, rs, "bin", loc); const string& cid (cast (rs["cc.id"])); const string& tsys (cast (rs["cc.target.system"])); @@ -277,7 +288,7 @@ namespace build2 if (cast (l) != "shared") { if (!cast_false (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 (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 (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 ()) { @@ -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 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 () || p.is_a () || p.is_a ()) { @@ -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 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 (rs[x_id]); h.assign ("config.cc.target") = cast (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 (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 (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 (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 ()) { @@ -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 ()) { @@ -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 ()) { @@ -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 (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 ("config.version")); + const variable& c_v (vp.insert ("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 (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 (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 (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 projects; + // Relock for shared access. + // + ml.unlock (); + for (void* v: ts) { target& t (*static_cast (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& 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 (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 inline pair required (scope& root, @@ -76,6 +78,8 @@ namespace build2 pair omitted (scope& root, const variable&); + // Note that the variable is expected to have already been registered. + // inline pair 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 (ns + ".configured")); + const variable& var (var_pool.rw (rs).insert (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 (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 (ns + ".configured")); + const variable& var (var_pool.rw (rs).insert (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 (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 (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 . @@ -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 ("src_root"); - v.insert ("out_root"); - v.insert ("src_base"); - v.insert ("out_base"); + var_src_root = &vp.insert ("src_root"); + var_out_root = &vp.insert ("out_root"); + var_src_base = &vp.insert ("src_base"); + var_out_base = &vp.insert ("out_base"); - v.insert ("project"); - v.insert ("amalgamation"); + // Note that subprojects is not typed since the value requires + // pre-processing (see file.cxx). + // + var_project = &vp.insert ("project"); + var_amalgamation = &vp.insert ("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 ("import.target"); - v.insert ("extension", variable_visibility::target); - } + // Target extension. + // + vp.insert ("extension", variable_visibility::target); gs.assign ("build.work") = work; gs.assign ("build.home") = home; @@ -240,8 +262,8 @@ namespace build2 // Build system version. // { - gs.assign ("build.version") = uint64_t (BUILD2_VERSION); - gs.assign ("build.version.string") = BUILD2_VERSION_STR; + gs.assign ("build.version") = uint64_t (BUILD2_VERSION); + gs.assign ("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 (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 (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 ("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&) + boot (scope& rs, const location&, unique_ptr&) { 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&, @@ -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(dist_id, test_id) // taking precedence. // - r.rules.insert (dist_id, 0, "dist", rule_); - r.rules.insert (dist_id, 0, "dist.alias", rule_); + rs.rules.insert (dist_id, 0, "dist", rule_); + rs.rules.insert (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 (l); // Strip abs_dir_path. } } @@ -120,11 +120,11 @@ namespace build2 // dist.cmd // { - value& v (r.assign ("dist.cmd")); + value& v (rs.assign ("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 (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 (l)); const process_path& dist_cmd (cast (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 (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 (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 - 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 (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 (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 - 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 (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 (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 (n, true)); + const variable& var ( + var_pool.rw (ibase).insert (n, true)); if (auto l = iroot[var]) { out_root = cast (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 (name, true)); + const variable& var (var_pool.rw (iroot).insert (name, true)); path r; if (auto l = iroot[var]) @@ -985,13 +984,13 @@ namespace build2 // First check the amalgamation itself. // - if (r != &iroot && cast (r->vars["project"]) == proj) + if (r != &iroot && cast (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 (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 (l)); @@ -1061,10 +1060,10 @@ namespace build2 // Now we know this project's name as well as all its subprojects. // - if (cast (root->vars["project"]) == proj) + if (cast (root->vars[var_project]) == proj) break; - if (auto l = root->vars["subprojects"]) + if (auto l = root->vars[var_subprojects]) { const auto& m (cast (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 (move (vn), true)); + const variable& vr (var_pool.rw (r).insert (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 (move (vn))); // Not overridable. + const variable& vr (var_pool.rw (r).insert (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 (string ("install.") + n + ".subdirs"); + var_pool.rw (r).insert (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&, 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 (perform_install_id, "install.alias", alias_); - b.rules.insert (perform_uninstall_id, "uninstall.alias", alias_); + bs.rules.insert (perform_install_id, "install.alias", alias_); + bs.rules.insert (perform_uninstall_id, "uninstall.alias", alias_); - b.rules.insert (perform_install_id, "install.file", file_); - b.rules.insert (perform_uninstall_id, "uinstall.file", file_); + bs.rules.insert (perform_install_id, "install.file", file_); + bs.rules.insert (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 (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 (b, dir_path ("bin")); // Install into install.bin. - install_path (b, dir_path ("doc")); // Install into install.doc. - install_path (b, dir_path ("man")); // Install into install.man. - install_path (b, dir_path ("man1")); // Install into install.man1. + install_path (bs, dir_path ("bin")); // Install into install.bin. + install_path (bs, dir_path ("doc")); // Install into install.doc. + install_path (bs, dir_path ("man")); // Install into install.man. + install_path (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 ())); 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 (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 (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&); + void (scope& root, + const location&, + unique_ptr&); // 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 (name + ".loaded", - variable_visibility::project)); - const variable& cv (var_pool.insert (name + ".configured", - variable_visibility::project)); + auto& vp (var_pool.rw (rs)); + + const variable& lv (vp.insert (name + ".loaded", + variable_visibility::project)); + + const variable& cv (vp.insert (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 (vt)); - l5 ([&]{trace << "matching " << t;}); - match (a, t); + for (void* vt: ts) + { + target& t (*static_cast (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> psp; - auto body = [a, quiet, &psp, &trace] (void* v) + auto body = [&ml, a, quiet, &psp, &trace] (void* v) { target& t (*static_cast (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 ("config.pkgconfig", true)); const variable& x_path (vp.insert ("pkgconfig.path")); @@ -142,7 +142,7 @@ namespace build2 // if (!cast_false (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 (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 find (const variable& var, const target_type* tt = nullptr, @@ -149,24 +145,35 @@ namespace build2 pair 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 value& - assign (string name) {return vars.assign (move (name));} + assign (string name) + { + return vars.assign (variable_pool::instance.insert (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; 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 - 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 - value& - assign (string name) {return vars.assign (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 inline prerequisite_members_range - prerequisite_members (action a, T&& x, bool members = true) + prerequisite_members (slock& ml, action a, T&& x, bool members = true) { - return prerequisite_members_range (a, forward (x), members); + return prerequisite_members_range (ml, a, forward (x), members); } template class prerequisite_members_range { public: - prerequisite_members_range (action a, T&& r, bool m) - : a_ (a), members_ (m), r_ (forward (r)), e_ (r_.end ()) {} + prerequisite_members_range (slock& l, action a, T&& r, bool m) + : l_ (l), a_ (a), members_ (m), r_ (forward (r)), e_ (r_.end ()) {} using base_iterator = decltype (declval ().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 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&); // + resolve_group_members (slock&, action, target&); // template inline auto prerequisite_members_range::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 ()) { @@ -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 (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 ())); 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 ()) 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& x, const char* xn, pair& 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 #include #include -#include #include // unique_ptr, shared_ptr #include // pair, move() #include // size_t, nullptr_t @@ -19,6 +18,11 @@ #include // function, reference_wrapper #include +#include +#include +#include +#include + #include // ios_base::failure #include // exception #include // logic_error, invalid_argument, runtime_error @@ -69,10 +73,56 @@ namespace build2 using std::istream; using std::ostream; + // Concurrency. + // using std::future; - // Exceptions. While 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; + using ulock = std::unique_lock; + + // 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 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 const variable& - insert (string name) - { - return insert ( - move (name), &value_traits::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 const variable& + insert (string name) + { + return insert (move (name), &value_traits::value_type); + } + + template + const variable& insert (string name, variable_visibility v) { - return insert (move (name), &value_traits::value_type, &v, nullptr); + return insert (move (name), &value_traits::value_type, &v); } template @@ -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 (*this);} + + variable_pool& + rw (scope&) const {return const_cast (*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; @@ -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 value& - assign (string name) - { - return assign (var_pool.insert (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, bool> insert (const variable&, bool typed = true); - pair, bool> - insert (const string& name, bool typed = true) - { - return insert (var_pool[name], typed); - } - pair - 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 ( + var_pool.rw (ml).insert ( "test.target", variable_visibility::project))); v = cast ((*global_scope)["build.host"]); -- cgit v1.1