diff options
Diffstat (limited to 'libbuild2')
53 files changed, 985 insertions, 877 deletions
diff --git a/libbuild2/algorithm.cxx b/libbuild2/algorithm.cxx index 50db5d3..298f945 100644 --- a/libbuild2/algorithm.cxx +++ b/libbuild2/algorithm.cxx @@ -41,7 +41,7 @@ namespace build2 if (r == nullptr) { - r = search_existing (p.key ()); + r = search_existing (p.scope.ctx, p.key ()); if (r != nullptr) search_custom (p, *r); @@ -59,20 +59,22 @@ namespace build2 // business. // if (pk.proj) - return import (pk); + return import (t.ctx, pk); if (const target* pt = pk.tk.type->search (t, pk)) return *pt; - return create_new_target (pk); + return create_new_target (t.ctx, pk); } const target* - search_existing (const prerequisite_key& pk) + search_existing (context& ctx, const prerequisite_key& pk) { assert (phase == run_phase::match || phase == run_phase::execute); - return pk.proj ? import_existing (pk) : search_existing_target (pk); + return pk.proj + ? import_existing (ctx, pk) + : search_existing_target (ctx, pk); } const target& @@ -130,7 +132,9 @@ namespace build2 prerequisite_key pk { n.proj, {tt, &n.dir, q ? &empty_dir_path : &out, &n.value, ext}, &s}; - return q ? import_existing (pk) : search_existing_target (pk); + return q + ? import_existing (s.ctx, pk) + : search_existing_target (s.ctx, pk); } // target_lock @@ -266,13 +270,13 @@ namespace build2 target& m (*mp != nullptr // Might already be there. ? **mp - : targets.insert (tt, - dir, - out, - move (n), - nullopt /* ext */, - true /* implied */, - trace).first); + : t.ctx.targets.insert (tt, + dir, + out, + move (n), + nullopt /* ext */, + true /* implied */, + trace).first); if (*mp == nullptr) { *mp = &m; @@ -303,7 +307,7 @@ namespace build2 // for (const scope* s (&bs); s != nullptr; - s = s->root () ? global_scope : s->parent_scope ()) + s = s->root () ? &s->global_scope () : s->parent_scope ()) { const operation_rule_map* om (s->rules[mo]); @@ -897,7 +901,7 @@ namespace build2 // const dir_path& d (parent && t.name.empty () ? t.dir.directory () : t.dir); - const scope& bs (scopes.find (d)); + const scope& bs (t.ctx.scopes.find (d)); const scope* rs (bs.root_scope ()); // If root scope is NULL, then this can mean that we are out of any @@ -973,7 +977,7 @@ namespace build2 if (op_t != nullptr) { - op_s = &scopes.find (t.dir); + op_s = &t.ctx.scopes.find (t.dir); if (op_s->out_path () == t.dir && !op_s->operation_callbacks.empty ()) { @@ -1401,7 +1405,7 @@ namespace build2 // stops at the project boundary). // if (!l.defined ()) - l = global_scope->find (*var_backlink, t.key ()); + l = t.ctx.global_scope.find (*var_backlink, t.key ()); return l ? backlink_test (t, l) : nullopt; } diff --git a/libbuild2/algorithm.hxx b/libbuild2/algorithm.hxx index 4707ae7..2ca83e3 100644 --- a/libbuild2/algorithm.hxx +++ b/libbuild2/algorithm.hxx @@ -16,6 +16,7 @@ namespace build2 { class scope; + class context; class prerequisite; class prerequisite_key; @@ -42,7 +43,7 @@ namespace build2 search (const target&, const prerequisite_key&); LIBBUILD2_SYMEXPORT const target* - search_existing (const prerequisite_key&); + search_existing (context&, const prerequisite_key&); // Uniform search interface for prerequisite/prerequisite_member. // @@ -61,7 +62,7 @@ namespace build2 // const target& search (const target&, - const target_type& type, + const target_type&, const dir_path& dir, const dir_path& out, const string& name, @@ -70,7 +71,8 @@ namespace build2 const optional<project_name>& proj = nullopt); const target* - search_existing (const target_type& type, + search_existing (context&, + const target_type&, const dir_path& dir, const dir_path& out, const string& name, diff --git a/libbuild2/algorithm.ixx b/libbuild2/algorithm.ixx index 6bc771e..8bd69c9 100644 --- a/libbuild2/algorithm.ixx +++ b/libbuild2/algorithm.ixx @@ -57,7 +57,8 @@ namespace build2 } inline const target* - search_existing (const target_type& type, + search_existing (context& ctx, + const target_type& type, const dir_path& dir, const dir_path& out, const string& name, @@ -66,6 +67,7 @@ namespace build2 const optional<project_name>& proj) { return search_existing ( + ctx, prerequisite_key { proj, { diff --git a/libbuild2/bash/rule.cxx b/libbuild2/bash/rule.cxx index d9bf857..2f2de2d 100644 --- a/libbuild2/bash/rule.cxx +++ b/libbuild2/bash/rule.cxx @@ -157,13 +157,13 @@ namespace build2 if (mt != timestamp_nonexistent) { - auto rp (targets.insert_locked (bash::static_type, - ap.directory (), - dir_path () /* out */, - p.name, - ext, - true /* implied */, - trace)); + auto rp (t.ctx.targets.insert_locked (bash::static_type, + ap.directory (), + dir_path () /* out */, + p.name, + ext, + true /* implied */, + trace)); bash& pt (rp.first.as<bash> ()); @@ -281,7 +281,7 @@ namespace build2 continue; } - if (const scope* rs = scopes.find (b->dir).root_scope ()) + if (const scope* rs = t.ctx.scopes.find (b->dir).root_scope ()) { const dir_path& d (pp.sub (rs->src_path ()) ? rs->src_path () diff --git a/libbuild2/config/init.cxx b/libbuild2/config/init.cxx index b790569..9bdfef9 100644 --- a/libbuild2/config/init.cxx +++ b/libbuild2/config/init.cxx @@ -80,7 +80,7 @@ namespace build2 assert (config_hints.empty ()); // We don't known any hints. - auto& vp (var_pool.rw (rs)); + auto& vp (rs.ctx.var_pool.rw (rs)); // Load config.build if one exists (we don't need to worry about // disfigure since we will never be init'ed). @@ -103,7 +103,7 @@ namespace build2 { // Assume missing version is 0. // - auto p (extract_variable (f, c_v)); + auto p (extract_variable (rs.ctx, f, c_v)); uint64_t v (p.second ? cast<uint64_t> (p.first) : 0); if (v != module::version) @@ -126,7 +126,7 @@ namespace build2 // global scope similar to builtin rules. // { - auto& r (rs.global ().rules); + auto& r (rs.global_scope ().rules); r.insert<mtime_target> ( configure_id, 0, "config.file", file_rule::instance); } diff --git a/libbuild2/config/operation.cxx b/libbuild2/config/operation.cxx index c3ce4b7..e235a3a 100644 --- a/libbuild2/config/operation.cxx +++ b/libbuild2/config/operation.cxx @@ -28,12 +28,12 @@ namespace build2 // configure // static void - save_src_root (const scope& root) + save_src_root (const scope& rs) { - const dir_path& out_root (root.out_path ()); - const dir_path& src_root (root.src_path ()); + const dir_path& out_root (rs.out_path ()); + const dir_path& src_root (rs.src_path ()); - path f (out_root / root.root_extra->src_root_file); + path f (out_root / rs.root_extra->src_root_file); if (verb >= 2) text << "cat >" << f; @@ -57,12 +57,12 @@ namespace build2 } static void - save_out_root (const scope& root) + save_out_root (const scope& rs) { - const dir_path& out_root (root.out_path ()); - const dir_path& src_root (root.src_path ()); + const dir_path& out_root (rs.out_path ()); + const dir_path& src_root (rs.src_path ()); - path f (src_root / root.root_extra->out_root_file); + path f (src_root / rs.root_extra->out_root_file); if (verb) text << (verb >= 2 ? "cat >" : "save ") << f; @@ -88,14 +88,14 @@ namespace build2 using project_set = set<const scope*>; // Use pointers to get comparison. static void - save_config (const scope& root, const project_set& projects) + save_config (const scope& rs, const project_set& projects) { - path f (config_file (root)); + path f (config_file (rs)); if (verb) text << (verb >= 2 ? "cat >" : "save ") << f; - const module& mod (*root.lookup_module<const module> (module::name)); + const module& mod (*rs.lookup_module<const module> (module::name)); try { @@ -107,7 +107,7 @@ namespace build2 ofs << "config.version = " << module::version << endl; - if (auto l = root.vars[var_amalgamation]) + if (auto l = rs.vars[var_amalgamation]) { const dir_path& d (cast<dir_path> (l)); @@ -130,10 +130,10 @@ namespace build2 { const variable& var (sv.var); - pair<lookup, size_t> org (root.find_original (var)); + pair<lookup, size_t> org (rs.find_original (var)); pair<lookup, size_t> ovr (var.overrides == nullptr ? org - : root.find_override (var, org)); + : rs.find_override (var, org)); const lookup& l (ovr.first); // We definitely write values that are set on our root scope or @@ -144,7 +144,7 @@ namespace build2 if (!l.defined ()) continue; - if (!(l.belongs (root) || l.belongs (*global_scope))) + if (!(l.belongs (rs) || l.belongs (rs.ctx.global_scope))) { // This is presumably an inherited value. But it could also be // some left-over garbage. For example, an amalgamation could @@ -162,7 +162,7 @@ namespace build2 // root. // bool found (false); - const scope* r (&root); + const scope* r (&rs); while ((r = r->parent_scope ()->root_scope ()) != nullptr) { if (l.belongs (*r)) @@ -307,14 +307,14 @@ namespace build2 } static void - configure_project (action a, const scope& root, project_set& projects) + configure_project (action a, const scope& rs, project_set& projects) { tracer trace ("configure_project"); - const dir_path& out_root (root.out_path ()); - const dir_path& src_root (root.src_path ()); + const dir_path& out_root (rs.out_path ()); + const dir_path& src_root (rs.src_path ()); - if (!projects.insert (&root).second) + if (!projects.insert (&rs).second) { l5 ([&]{trace << "skipping already configured " << out_root;}); return; @@ -324,8 +324,8 @@ namespace build2 // if (out_root != src_root) { - mkdir_p (out_root / root.root_extra->build_dir); - mkdir (out_root / root.root_extra->bootstrap_dir, 2); + mkdir_p (out_root / rs.root_extra->build_dir); + mkdir (out_root / rs.root_extra->bootstrap_dir, 2); } // We distinguish between a complete configure and operation- @@ -338,11 +338,11 @@ namespace build2 // Save src-root.build unless out_root is the same as src. // if (out_root != src_root) - save_src_root (root); + save_src_root (rs); // Save config.build. // - save_config (root, projects); + save_config (rs, projects); } else { @@ -350,54 +350,54 @@ namespace build2 // Configure subprojects that have been loaded. // - if (auto l = root.vars[var_subprojects]) + if (auto l = rs.vars[var_subprojects]) { for (auto p: cast<subprojects> (l)) { const dir_path& pd (p.second); dir_path out_nroot (out_root / pd); - const scope& nroot (scopes.find (out_nroot)); + const scope& nrs (rs.ctx.scopes.find (out_nroot)); // @@ Strictly speaking we need to check whether the config // module was loaded for this subproject. // - if (nroot.out_path () != out_nroot) // This subproject not loaded. + if (nrs.out_path () != out_nroot) // This subproject not loaded. continue; - configure_project (a, nroot, projects); + configure_project (a, nrs, projects); } } } static void - configure_forward (const scope& root, project_set& projects) + configure_forward (const scope& rs, project_set& projects) { tracer trace ("configure_forward"); - const dir_path& out_root (root.out_path ()); - const dir_path& src_root (root.src_path ()); + const dir_path& out_root (rs.out_path ()); + const dir_path& src_root (rs.src_path ()); - if (!projects.insert (&root).second) + if (!projects.insert (&rs).second) { l5 ([&]{trace << "skipping already configured " << src_root;}); return; } - mkdir (src_root / root.root_extra->bootstrap_dir, 2); // Make sure exists. - save_out_root (root); + mkdir (src_root / rs.root_extra->bootstrap_dir, 2); // Make sure exists. + save_out_root (rs); // Configure subprojects. Since we don't load buildfiles if configuring // a forward, we do it for all known subprojects. // - if (auto l = root.vars[var_subprojects]) + if (auto l = rs.vars[var_subprojects]) { for (auto p: cast<subprojects> (l)) { dir_path out_nroot (out_root / p.second); - const scope& nroot (scopes.find (out_nroot)); - assert (nroot.out_path () == out_nroot); + const scope& nrs (rs.ctx.scopes.find (out_nroot)); + assert (nrs.out_path () == out_nroot); - configure_forward (nroot, projects); + configure_forward (nrs, projects); } } } @@ -451,7 +451,7 @@ namespace build2 static void configure_load (const values& params, - scope& root, + scope& rs, const path& buildfile, const dir_path& out_base, const dir_path& src_base, @@ -463,19 +463,19 @@ namespace build2 // forwarding but in order to configure subprojects we have to // bootstrap them (similar to disfigure). // - create_bootstrap_inner (root); + create_bootstrap_inner (rs); - if (root.out_path () == root.src_path ()) - fail (l) << "forwarding to source directory " << root.src_path (); + if (rs.out_path () == rs.src_path ()) + fail (l) << "forwarding to source directory " << rs.src_path (); } else - load (params, root, buildfile, out_base, src_base, l); // Normal load. + load (params, rs, buildfile, out_base, src_base, l); // Normal load. } static void configure_search (const values& params, - const scope& root, - const scope& base, + const scope& rs, + const scope& bs, const path& bf, const target_key& tk, const location& l, @@ -486,10 +486,10 @@ namespace build2 // For forwarding we only collect the projects (again, similar to // disfigure). // - ts.push_back (&root); + ts.push_back (&rs); } else - search (params, root, base, bf, tk, l, ts); // Normal search. + search (params, rs, bs, bf, tk, l, ts); // Normal search. } static void @@ -515,8 +515,8 @@ namespace build2 { // Forward configuration. // - const scope& root (*static_cast<const scope*> (at.target)); - configure_forward (root, projects); + const scope& rs (*static_cast<const scope*> (at.target)); + configure_forward (rs, projects); continue; } @@ -552,7 +552,7 @@ namespace build2 if (oif->id != id) continue; - set_current_oif (*oif); + rs->ctx.current_oif (*oif); phase_lock pl (run_phase::match); match (action (configure_id, id), t); @@ -586,14 +586,14 @@ namespace build2 // static bool - disfigure_project (action a, const scope& root, project_set& projects) + disfigure_project (action a, const scope& rs, project_set& projects) { tracer trace ("disfigure_project"); - const dir_path& out_root (root.out_path ()); - const dir_path& src_root (root.src_path ()); + const dir_path& out_root (rs.out_path ()); + const dir_path& src_root (rs.src_path ()); - if (!projects.insert (&root).second) + if (!projects.insert (&rs).second) { l5 ([&]{trace << "skipping already disfigured " << out_root;}); return false; @@ -604,16 +604,16 @@ namespace build2 // Disfigure subprojects. Since we don't load buildfiles during // disfigure, we do it for all known subprojects. // - if (auto l = root.vars[var_subprojects]) + if (auto l = rs.vars[var_subprojects]) { for (auto p: cast<subprojects> (l)) { const dir_path& pd (p.second); dir_path out_nroot (out_root / pd); - const scope& nroot (scopes.find (out_nroot)); - assert (nroot.out_path () == out_nroot); // See disfigure_load(). + const scope& nrs (rs.ctx.scopes.find (out_nroot)); + assert (nrs.out_path () == out_nroot); // See disfigure_load(). - r = disfigure_project (a, nroot, projects) || r; + r = disfigure_project (a, nrs, projects) || r; // We use mkdir_p() to create the out_root of a subproject // which means there could be empty parent directories left @@ -643,19 +643,19 @@ namespace build2 { l5 ([&]{trace << "completely disfiguring " << out_root;}); - r = rmfile (config_file (root)) || r; + r = rmfile (config_file (rs)) || r; if (out_root != src_root) { - r = rmfile (out_root / root.root_extra->src_root_file, 2) || r; + r = rmfile (out_root / rs.root_extra->src_root_file, 2) || r; // Clean up the directories. // // Note: try to remove the root/ hooks directory if it is empty. // - r = rmdir (out_root / root.root_extra->root_dir, 2) || r; - r = rmdir (out_root / root.root_extra->bootstrap_dir, 2) || r; - r = rmdir (out_root / root.root_extra->build_dir, 2) || r; + r = rmdir (out_root / rs.root_extra->root_dir, 2) || r; + r = rmdir (out_root / rs.root_extra->bootstrap_dir, 2) || r; + r = rmdir (out_root / rs.root_extra->build_dir, 2) || r; switch (rmdir (out_root)) { @@ -687,16 +687,16 @@ namespace build2 } static bool - disfigure_forward (const scope& root, project_set& projects) + disfigure_forward (const scope& rs, project_set& projects) { // Pretty similar logic to disfigure_project(). // tracer trace ("disfigure_forward"); - const dir_path& out_root (root.out_path ()); - const dir_path& src_root (root.src_path ()); + const dir_path& out_root (rs.out_path ()); + const dir_path& src_root (rs.src_path ()); - if (!projects.insert (&root).second) + if (!projects.insert (&rs).second) { l5 ([&]{trace << "skipping already disfigured " << src_root;}); return false; @@ -704,23 +704,23 @@ namespace build2 bool r (false); - if (auto l = root.vars[var_subprojects]) + if (auto l = rs.vars[var_subprojects]) { for (auto p: cast<subprojects> (l)) { dir_path out_nroot (out_root / p.second); - const scope& nroot (scopes.find (out_nroot)); - assert (nroot.out_path () == out_nroot); + const scope& nrs (rs.ctx.scopes.find (out_nroot)); + assert (nrs.out_path () == out_nroot); - r = disfigure_forward (nroot, projects) || r; + r = disfigure_forward (nrs, projects) || r; } } // Remove the out-root.build file and try to remove the bootstrap/ // directory if it is empty. // - r = rmfile (src_root / root.root_extra->out_root_file) || r; - r = rmdir (src_root / root.root_extra->bootstrap_dir, 2) || r; + r = rmfile (src_root / rs.root_extra->out_root_file) || r; + r = rmdir (src_root / rs.root_extra->bootstrap_dir, 2) || r; return r; } @@ -757,14 +757,14 @@ namespace build2 static void disfigure_search (const values&, - const scope& root, + const scope& rs, const scope&, const path&, const target_key&, const location&, action_targets& ts) { - ts.push_back (&root); + ts.push_back (&rs); } static void @@ -790,23 +790,23 @@ namespace build2 // for (const action_target& at: ts) { - const scope& root (*static_cast<const scope*> (at.target)); + const scope& rs (*static_cast<const scope*> (at.target)); if (!(fwd - ? disfigure_forward ( root, projects) - : disfigure_project (a, root, projects))) + ? disfigure_forward ( rs, projects) + : disfigure_project (a, rs, projects))) { // Create a dir{$out_root/} target to signify the project's root in // diagnostics. Not very clean but seems harmless. // target& t ( - targets.insert (dir::static_type, - fwd ? root.src_path () : root.out_path (), - dir_path (), // Out tree. - "", - nullopt, - true, // Implied. - trace).first); + rs.ctx.targets.insert (dir::static_type, + fwd ? rs.src_path () : rs.out_path (), + dir_path (), // Out tree. + "", + nullopt, + true, // Implied. + trace).first); if (verb != 0 && diag >= 2) info << diag_done (a, t); @@ -836,7 +836,7 @@ namespace build2 // create // static void - save_config (const dir_path& d, const variable_overrides& var_ovs) + save_config (context& ctx, const dir_path& d) { // Since there aren't any sub-projects yet, any config.import.* values // that the user may want to specify won't be saved in config.build. So @@ -846,13 +846,13 @@ namespace build2 // going to do is bootstrap the newly created project, similar to the // way main() does it. // - scope& gs (*scope::global_); + scope& gs (ctx.global_scope.rw ()); scope& rs (load_project (gs, d, d, false /* fwd */, false /* load */)); module& m (*rs.lookup_module<module> (module::name)); // Save all the global config.import.* variables. // - variable_pool& vp (var_pool.rw (rs)); + variable_pool& vp (ctx.var_pool.rw (rs)); for (auto p (gs.vars.find_namespace (vp.insert ("config.import"))); p.first != p.second; ++p.first) @@ -869,7 +869,7 @@ namespace build2 // Now project-specific. For now we just save all of them and let // save_config() above weed out the ones that don't apply. // - for (const variable_override& vo: var_ovs) + for (const variable_override& vo: ctx.var_overrides) { const variable& var (vo.var); @@ -879,7 +879,7 @@ namespace build2 } const string& - preprocess_create (const variable_overrides& var_ovs, + preprocess_create (context& ctx, values& params, vector_view<opspec>& spec, bool lifted, @@ -986,7 +986,7 @@ namespace build2 true, /* buildfile */ "the create meta-operation"); - save_config (d, var_ovs); + save_config (ctx, d); } } diff --git a/libbuild2/config/operation.hxx b/libbuild2/config/operation.hxx index 0a88f96..8b2a29d 100644 --- a/libbuild2/config/operation.hxx +++ b/libbuild2/config/operation.hxx @@ -18,7 +18,7 @@ namespace build2 extern const meta_operation_info mo_disfigure; const string& - preprocess_create (const variable_overrides&, + preprocess_create (context&, values&, vector_view<opspec>&, bool, diff --git a/libbuild2/config/utility.cxx b/libbuild2/config/utility.cxx index 746639d..a89fac6 100644 --- a/libbuild2/config/utility.cxx +++ b/libbuild2/config/utility.cxx @@ -75,7 +75,7 @@ namespace build2 // any original values, they will be "visible"; see find_override() for // details. // - const variable& vns (var_pool.rw (r).insert ("config." + n)); + const variable& vns (r.ctx.var_pool.rw (r).insert ("config." + n)); for (scope* s (&r); s != nullptr; s = s->parent_scope ()) { for (auto p (s->vars.find_namespace (vns)); @@ -101,7 +101,7 @@ namespace build2 // Pattern-typed in boot() as bool. // const variable& var ( - var_pool.rw (rs).insert ("config." + n + ".configured")); + rs.ctx.var_pool.rw (rs).insert ("config." + n + ".configured")); if (current_mif->id == configure_id) save_variable (rs, var); @@ -116,7 +116,7 @@ namespace build2 // Pattern-typed in boot() as bool. // const variable& var ( - var_pool.rw (rs).insert ("config." + n + ".configured")); + rs.ctx.var_pool.rw (rs).insert ("config." + n + ".configured")); if (current_mif->id == configure_id) save_variable (rs, var); diff --git a/libbuild2/config/utility.hxx b/libbuild2/config/utility.hxx index a063693..b1995b6 100644 --- a/libbuild2/config/utility.hxx +++ b/libbuild2/config/utility.hxx @@ -58,7 +58,7 @@ namespace build2 uint64_t save_flags = 0) { return required ( - root, var_pool[name], default_value, override, save_flags); + root, root.ctx.var_pool[name], default_value, override, save_flags); } inline pair<lookup, bool> @@ -86,7 +86,7 @@ namespace build2 inline pair<lookup, bool> omitted (scope& root, const string& name) { - return omitted (root, var_pool[name]); + return omitted (root, root.ctx.var_pool[name]); } // Set, if necessary, an optional config.* variable. In particular, an @@ -105,7 +105,7 @@ namespace build2 inline lookup optional (scope& root, const string& name) { - return optional (root, var_pool[name]); + return optional (root, root.ctx.var_pool[name]); } // Check whether there are any variables specified from the config diff --git a/libbuild2/context.cxx b/libbuild2/context.cxx index 1cc8bbc..720e8d8 100644 --- a/libbuild2/context.cxx +++ b/libbuild2/context.cxx @@ -10,6 +10,8 @@ #include <libbuild2/rule.hxx> #include <libbuild2/scope.hxx> #include <libbuild2/target.hxx> +#include <libbuild2/variable.hxx> +#include <libbuild2/operation.hxx> #include <libbuild2/diagnostics.hxx> #include <libbutl/ft/exception.hxx> // uncaught_exceptions @@ -25,379 +27,48 @@ using namespace butl; namespace build2 { - scheduler sched; - - run_phase phase; - run_phase_mutex phase_mutex; - - size_t load_generation; - - bool run_phase_mutex:: - lock (run_phase p) - { - bool r; - - { - mlock l (m_); - bool u (lc_ == 0 && mc_ == 0 && ec_ == 0); // Unlocked. - - // Increment the counter. - // - condition_variable* v (nullptr); - switch (p) - { - case run_phase::load: lc_++; v = &lv_; break; - case run_phase::match: mc_++; v = &mv_; break; - case run_phase::execute: ec_++; v = &ev_; break; - } - - // If unlocked, switch directly to the new phase. Otherwise wait for the - // phase switch. Note that in the unlocked case we don't need to notify - // since there is nobody waiting (all counters are zero). - // - if (u) - { - phase = p; - r = !fail_; - } - else if (phase != p) - { - sched.deactivate (false /* external */); - for (; phase != p; v->wait (l)) ; - r = !fail_; - l.unlock (); // Important: activate() can block. - sched.activate (false /* external */); - } - else - r = !fail_; - } - - // In case of load, acquire the exclusive access mutex. - // - if (p == run_phase::load) - { - lm_.lock (); - r = !fail_; // Re-query. - } - - return r; - } - - void run_phase_mutex:: - unlock (run_phase p) - { - // In case of load, release the exclusive access mutex. - // - if (p == run_phase::load) - lm_.unlock (); - - { - mlock l (m_); - - // Decrement the counter and see if this phase has become unlocked. - // - bool u (false); - switch (p) - { - case run_phase::load: u = (--lc_ == 0); break; - case run_phase::match: u = (--mc_ == 0); break; - case run_phase::execute: u = (--ec_ == 0); break; - } - - // If the phase is unlocked, pick a new phase and notify the waiters. - // Note that we notify all load waiters so that they can all serialize - // behind the second-level mutex. - // - if (u) - { - condition_variable* v; - - if (lc_ != 0) {phase = run_phase::load; v = &lv_;} - else if (mc_ != 0) {phase = run_phase::match; v = &mv_;} - else if (ec_ != 0) {phase = run_phase::execute; v = &ev_;} - else {phase = run_phase::load; v = nullptr;} - - if (v != nullptr) - { - l.unlock (); - v->notify_all (); - } - } - } - } - - bool run_phase_mutex:: - relock (run_phase o, run_phase n) - { - // Pretty much a fused unlock/lock implementation except that we always - // switch into the new phase. - // - assert (o != n); - - bool r; - - if (o == run_phase::load) - lm_.unlock (); - - { - mlock l (m_); - bool u (false); - - switch (o) - { - case run_phase::load: u = (--lc_ == 0); break; - case run_phase::match: u = (--mc_ == 0); break; - case run_phase::execute: u = (--ec_ == 0); break; - } - - // Set if will be waiting or notifying others. - // - condition_variable* v (nullptr); - switch (n) - { - case run_phase::load: v = lc_++ != 0 || !u ? &lv_ : nullptr; break; - case run_phase::match: v = mc_++ != 0 || !u ? &mv_ : nullptr; break; - case run_phase::execute: v = ec_++ != 0 || !u ? &ev_ : nullptr; break; - } - - if (u) - { - phase = n; - r = !fail_; - - // Notify others that could be waiting for this phase. - // - if (v != nullptr) - { - l.unlock (); - v->notify_all (); - } - } - else // phase != n - { - sched.deactivate (false /* external */); - for (; phase != n; v->wait (l)) ; - r = !fail_; - l.unlock (); // Important: activate() can block. - sched.activate (false /* external */); - } - } - - if (n == run_phase::load) - { - lm_.lock (); - r = !fail_; // Re-query. - } - - return r; - } - - // C++17 deprecated uncaught_exception() so use uncaught_exceptions() if - // available. + // Create global scope. Note that the empty path is a prefix for any other + // path. See the comment in <libbutl/prefix-map.mxx> for details. // - static inline bool - uncaught_exception () + static inline scope& + create_global_scope (scope_map& m) { -#ifdef __cpp_lib_uncaught_exceptions - return std::uncaught_exceptions () != 0; -#else - return std::uncaught_exception (); -#endif - } - - // phase_lock - // - static -#ifdef __cpp_thread_local - thread_local -#else - __thread -#endif - phase_lock* phase_lock_instance; - - phase_lock:: - phase_lock (run_phase p) - : p (p) - { - if (phase_lock* l = phase_lock_instance) - assert (l->p == p); - else - { - if (!phase_mutex.lock (p)) - { - phase_mutex.unlock (p); - throw failed (); - } - - phase_lock_instance = this; - - //text << this_thread::get_id () << " phase acquire " << p; - } - } - - phase_lock:: - ~phase_lock () - { - if (phase_lock_instance == this) - { - phase_lock_instance = nullptr; - phase_mutex.unlock (p); - - //text << this_thread::get_id () << " phase release " << p; - } - } - - // phase_unlock - // - phase_unlock:: - phase_unlock (bool u) - : l (u ? phase_lock_instance : nullptr) - { - if (u) - { - phase_lock_instance = nullptr; - phase_mutex.unlock (l->p); - - //text << this_thread::get_id () << " phase unlock " << l->p; - } - } - - phase_unlock:: - ~phase_unlock () noexcept (false) - { - if (l != nullptr) - { - bool r (phase_mutex.lock (l->p)); - phase_lock_instance = l; - - // Fail unless we are already failing. Note that we keep the phase - // locked since there will be phase_lock down the stack to unlock it. - // - if (!r && !uncaught_exception ()) - throw failed (); - - //text << this_thread::get_id () << " phase lock " << l->p; - } - } - - // phase_switch - // - phase_switch:: - phase_switch (run_phase n) - : o (phase), n (n) - { - if (!phase_mutex.relock (o, n)) - { - phase_mutex.relock (n, o); - throw failed (); - } - - phase_lock_instance->p = n; - - if (n == run_phase::load) // Note: load lock is exclusive. - load_generation++; - - //text << this_thread::get_id () << " phase switch " << o << " " << n; - } - - phase_switch:: - ~phase_switch () noexcept (false) - { - // If we are coming off a failed load phase, mark the phase_mutex as - // failed to terminate all other threads since the build state may no - // longer be valid. - // - if (n == run_phase::load && uncaught_exception ()) - { - mlock l (phase_mutex.m_); - phase_mutex.fail_ = true; - } - - bool r (phase_mutex.relock (n, o)); - phase_lock_instance->p = o; - - // Similar logic to ~phase_unlock(). - // - if (!r && !uncaught_exception ()) - throw failed (); - - //text << this_thread::get_id () << " phase restore " << n << " " << o; - } - - string current_mname; - string current_oname; - - const meta_operation_info* current_mif; - const operation_info* current_inner_oif; - const operation_info* current_outer_oif; - size_t current_on; - execution_mode current_mode; - bool current_diag_noise; - - atomic_count dependency_count; - atomic_count target_count; - atomic_count skip_count; - - bool keep_going = false; - bool dry_run = false; - - void - set_current_mif (const meta_operation_info& mif) - { - if (current_mname != mif.name) - { - current_mname = mif.name; - global_scope->rw ().assign (var_build_meta_operation) = mif.name; - } - - current_mif = &mif; - current_on = 0; // Reset. - } + auto i (m.insert (dir_path ())); + scope& r (i->second); + r.out_path_ = &i->first; + return r; + }; - void - set_current_oif (const operation_info& inner_oif, - const operation_info* outer_oif, - bool diag_noise) + struct context::data { - current_oname = (outer_oif == nullptr ? inner_oif : *outer_oif).name; - current_inner_oif = &inner_oif; - current_outer_oif = outer_oif; - current_on++; - current_mode = inner_oif.mode; - current_diag_noise = diag_noise; - - // Reset counters (serial execution). - // - dependency_count.store (0, memory_order_relaxed); - target_count.store (0, memory_order_relaxed); - skip_count.store (0, memory_order_relaxed); - } - - variable_overrides - reset (const strings& cmd_vars) + scope_map scopes; + target_set targets; + variable_pool var_pool; + variable_overrides var_overrides; + + data (context& c): scopes (c), targets (c), var_pool (true /* global */) {} + }; + + context:: + context (scheduler& s, const strings& cmd_vars) + : data_ (new data (*this)), + sched (s), + scopes (data_->scopes), + global_scope (create_global_scope (data_->scopes)), + targets (data_->targets), + var_pool (data_->var_pool), + var_overrides (data_->var_overrides) { - tracer trace ("reset"); - - // @@ Do we want to unload dynamically loaded modules? Note that this will - // be purely an optimization since a module could be linked-in (i.e., a - // module cannot expect to be unloaded/re-initialized for each meta- - // operation). + tracer trace ("context"); - l6 ([&]{trace << "resetting build state";}); + l6 ([&]{trace << "initializing build state";}); - auto& vp (variable_pool::instance); - auto& sm (scope_map::instance); + scope_map& sm (data_->scopes); + variable_pool& vp (data_->var_pool); - variable_overrides vos; - - targets.clear (); - sm.clear (); - vp.clear (); - - // Reset meta/operation tables. Note that the order should match the id - // constants in <libbuild2/operation.hxx>. + // Initialize the meta/operation tables. Note that the order should match + // the id constants in <libbuild2/operation.hxx>. // - meta_operation_table.clear (); meta_operation_table.insert ("noop"); meta_operation_table.insert ("perform"); meta_operation_table.insert ("configure"); @@ -420,23 +91,10 @@ namespace build2 operation_table.insert ("uninstall"); operation_table.insert ("update-for-install"); - // Create global scope. Note that the empty path is a prefix for any other - // path. See the comment in <libbutl/prefix-map.mxx> for details. - // - auto make_global_scope = [] () -> scope& - { - auto i (scope_map::instance.insert (dir_path ())); - scope& r (i->second); - r.out_path_ = &i->first; - global_scope = scope::global_ = &r; - return r; - }; - - scope& gs (make_global_scope ()); - // Setup the global scope before parsing any variable overrides since they // may reference these things. // + scope& gs (global_scope.rw ()); gs.assign<dir_path> ("build.work") = work; gs.assign<dir_path> ("build.home") = home; @@ -458,10 +116,10 @@ namespace build2 { const standard_version& v (build_version); - auto set = [&gs] (const char* var, auto val) + auto set = [&gs, &vp] (const char* var, auto val) { using T = decltype (val); - gs.assign (variable_pool::instance.insert<T> (var)) = move (val); + gs.assign (vp.insert<T> (var)) = move (val); }; // Note: here we assume epoch will always be 1 and therefore omit the @@ -509,7 +167,7 @@ namespace build2 // were built with. While it is not as precise (for example, a binary // built for i686 might be running on x86_64), it is good enough of an // approximation/fallback since most of the time we are interested in just - // the target class (e.g., linux, windows, macosx). + // the target class (e.g., linux, windows, macos). // { // Did the user ask us to use config.guess? @@ -739,7 +397,7 @@ namespace build2 // things simple. Pass original variable for diagnostics. Use current // working directory as pattern base. // - parser p; + parser p (*this); pair<value, token> r (p.parse_variable_value (l, gs, &work, var)); if (r.second.type != token_type::eos) @@ -752,8 +410,7 @@ namespace build2 fail << "typed override of variable " << n; // Global and absolute scope overrides we can enter directly. Project - // and relative scope ones will be entered by the caller for each - // amalgamation/project. + // and relative scope ones will be entered later for each project. // if (c == '!' || (dir && dir->absolute ())) { @@ -766,7 +423,7 @@ namespace build2 v = move (r.first); } else - vos.push_back ( + data_->var_overrides.push_back ( variable_override {var, *o, move (dir), move (r.first)}); } @@ -784,7 +441,7 @@ namespace build2 vp.insert_pattern<path> ( "config.import.**", true, variable_visibility::normal, true); - // module.cxx:load_module(). + // module.cxx:boot/init_module(). // { auto v_p (variable_visibility::project); @@ -847,13 +504,365 @@ namespace build2 r.insert<mtime_target> (perform_update_id, "file", file_rule::instance); r.insert<mtime_target> (perform_clean_id, "file", file_rule::instance); } + } + + context:: + ~context () + { + // Cannot be inline since context::data is undefined. + } + + void context:: + current_mif (const meta_operation_info& mif) + { + if (current_mname != mif.name) + { + current_mname = mif.name; + global_scope.rw ().assign (var_build_meta_operation) = mif.name; + } + + build2::current_mif = &mif; + current_on = 0; // Reset. + } + + void context:: + current_oif (const operation_info& inner_oif, + const operation_info* outer_oif, + bool diag_noise) + { + current_oname = (outer_oif == nullptr ? inner_oif : *outer_oif).name; + current_inner_oif = &inner_oif; + current_outer_oif = outer_oif; + current_on++; + current_mode = inner_oif.mode; + current_diag_noise = diag_noise; + + // Reset counters (serial execution). + // + dependency_count.store (0, memory_order_relaxed); + target_count.store (0, memory_order_relaxed); + skip_count.store (0, memory_order_relaxed); + } + + scheduler sched; + + run_phase phase; + run_phase_mutex phase_mutex; + + size_t load_generation; + + bool run_phase_mutex:: + lock (run_phase p) + { + bool r; + + { + mlock l (m_); + bool u (lc_ == 0 && mc_ == 0 && ec_ == 0); // Unlocked. + + // Increment the counter. + // + condition_variable* v (nullptr); + switch (p) + { + case run_phase::load: lc_++; v = &lv_; break; + case run_phase::match: mc_++; v = &mv_; break; + case run_phase::execute: ec_++; v = &ev_; break; + } + + // If unlocked, switch directly to the new phase. Otherwise wait for the + // phase switch. Note that in the unlocked case we don't need to notify + // since there is nobody waiting (all counters are zero). + // + if (u) + { + phase = p; + r = !fail_; + } + else if (phase != p) + { + sched.deactivate (false /* external */); + for (; phase != p; v->wait (l)) ; + r = !fail_; + l.unlock (); // Important: activate() can block. + sched.activate (false /* external */); + } + else + r = !fail_; + } + + // In case of load, acquire the exclusive access mutex. + // + if (p == run_phase::load) + { + lm_.lock (); + r = !fail_; // Re-query. + } + + return r; + } + + void run_phase_mutex:: + unlock (run_phase p) + { + // In case of load, release the exclusive access mutex. + // + if (p == run_phase::load) + lm_.unlock (); + + { + mlock l (m_); + + // Decrement the counter and see if this phase has become unlocked. + // + bool u (false); + switch (p) + { + case run_phase::load: u = (--lc_ == 0); break; + case run_phase::match: u = (--mc_ == 0); break; + case run_phase::execute: u = (--ec_ == 0); break; + } - return vos; + // If the phase is unlocked, pick a new phase and notify the waiters. + // Note that we notify all load waiters so that they can all serialize + // behind the second-level mutex. + // + if (u) + { + condition_variable* v; + + if (lc_ != 0) {phase = run_phase::load; v = &lv_;} + else if (mc_ != 0) {phase = run_phase::match; v = &mv_;} + else if (ec_ != 0) {phase = run_phase::execute; v = &ev_;} + else {phase = run_phase::load; v = nullptr;} + + if (v != nullptr) + { + l.unlock (); + v->notify_all (); + } + } + } } + bool run_phase_mutex:: + relock (run_phase o, run_phase n) + { + // Pretty much a fused unlock/lock implementation except that we always + // switch into the new phase. + // + assert (o != n); + + bool r; + + if (o == run_phase::load) + lm_.unlock (); + + { + mlock l (m_); + bool u (false); + + switch (o) + { + case run_phase::load: u = (--lc_ == 0); break; + case run_phase::match: u = (--mc_ == 0); break; + case run_phase::execute: u = (--ec_ == 0); break; + } + + // Set if will be waiting or notifying others. + // + condition_variable* v (nullptr); + switch (n) + { + case run_phase::load: v = lc_++ != 0 || !u ? &lv_ : nullptr; break; + case run_phase::match: v = mc_++ != 0 || !u ? &mv_ : nullptr; break; + case run_phase::execute: v = ec_++ != 0 || !u ? &ev_ : nullptr; break; + } + + if (u) + { + phase = n; + r = !fail_; + + // Notify others that could be waiting for this phase. + // + if (v != nullptr) + { + l.unlock (); + v->notify_all (); + } + } + else // phase != n + { + sched.deactivate (false /* external */); + for (; phase != n; v->wait (l)) ; + r = !fail_; + l.unlock (); // Important: activate() can block. + sched.activate (false /* external */); + } + } + + if (n == run_phase::load) + { + lm_.lock (); + r = !fail_; // Re-query. + } + + return r; + } + + // C++17 deprecated uncaught_exception() so use uncaught_exceptions() if + // available. + // + static inline bool + uncaught_exception () + { +#ifdef __cpp_lib_uncaught_exceptions + return std::uncaught_exceptions () != 0; +#else + return std::uncaught_exception (); +#endif + } + + // phase_lock + // + static +#ifdef __cpp_thread_local + thread_local +#else + __thread +#endif + phase_lock* phase_lock_instance; + + phase_lock:: + phase_lock (run_phase p) + : p (p) + { + if (phase_lock* l = phase_lock_instance) + assert (l->p == p); + else + { + if (!phase_mutex.lock (p)) + { + phase_mutex.unlock (p); + throw failed (); + } + + phase_lock_instance = this; + + //text << this_thread::get_id () << " phase acquire " << p; + } + } + + phase_lock:: + ~phase_lock () + { + if (phase_lock_instance == this) + { + phase_lock_instance = nullptr; + phase_mutex.unlock (p); + + //text << this_thread::get_id () << " phase release " << p; + } + } + + // phase_unlock + // + phase_unlock:: + phase_unlock (bool u) + : l (u ? phase_lock_instance : nullptr) + { + if (u) + { + phase_lock_instance = nullptr; + phase_mutex.unlock (l->p); + + //text << this_thread::get_id () << " phase unlock " << l->p; + } + } + + phase_unlock:: + ~phase_unlock () noexcept (false) + { + if (l != nullptr) + { + bool r (phase_mutex.lock (l->p)); + phase_lock_instance = l; + + // Fail unless we are already failing. Note that we keep the phase + // locked since there will be phase_lock down the stack to unlock it. + // + if (!r && !uncaught_exception ()) + throw failed (); + + //text << this_thread::get_id () << " phase lock " << l->p; + } + } + + // phase_switch + // + phase_switch:: + phase_switch (run_phase n) + : o (phase), n (n) + { + if (!phase_mutex.relock (o, n)) + { + phase_mutex.relock (n, o); + throw failed (); + } + + phase_lock_instance->p = n; + + if (n == run_phase::load) // Note: load lock is exclusive. + load_generation++; + + //text << this_thread::get_id () << " phase switch " << o << " " << n; + } + + phase_switch:: + ~phase_switch () noexcept (false) + { + // If we are coming off a failed load phase, mark the phase_mutex as + // failed to terminate all other threads since the build state may no + // longer be valid. + // + if (n == run_phase::load && uncaught_exception ()) + { + mlock l (phase_mutex.m_); + phase_mutex.fail_ = true; + } + + bool r (phase_mutex.relock (n, o)); + phase_lock_instance->p = o; + + // Similar logic to ~phase_unlock(). + // + if (!r && !uncaught_exception ()) + throw failed (); + + //text << this_thread::get_id () << " phase restore " << n << " " << o; + } + + string current_mname; + string current_oname; + + const meta_operation_info* current_mif; + const operation_info* current_inner_oif; + const operation_info* current_outer_oif; + size_t current_on; + execution_mode current_mode; + bool current_diag_noise; + + atomic_count dependency_count; + atomic_count target_count; + atomic_count skip_count; + + bool keep_going = false; + bool dry_run = false; + void (*config_save_variable) (scope&, const variable&, uint64_t); - const string& (*config_preprocess_create) (const variable_overrides&, + const string& (*config_preprocess_create) (context&, values&, vector_view<opspec>&, bool, diff --git a/libbuild2/context.hxx b/libbuild2/context.hxx index ce9a996..f51645e 100644 --- a/libbuild2/context.hxx +++ b/libbuild2/context.hxx @@ -8,8 +8,11 @@ #include <libbuild2/types.hxx> #include <libbuild2/utility.hxx> -#include <libbuild2/variable.hxx> -#include <libbuild2/operation.hxx> +// NOTE: this file is included by pretty much every other "data model" header +// (scope, target, variable, etc) so including any of them here is +// probably a non-starter. +// +#include <libbuild2/action.hxx> #include <libbuild2/scheduler.hxx> #include <libbuild2/export.hxx> @@ -17,11 +20,66 @@ namespace build2 { class scope; + class scope_map; + class target_set; + + class variable; + class variable_pool; + struct variable_override; + using variable_overrides = vector<variable_override>; + class value; + using values = small_vector<value, 1>; + + struct meta_operation_info; + struct operation_info; + + struct opspec; // Main scheduler. Started up and shut down in main(). // + // @@ CTX: move to main(). + // LIBBUILD2_SYMEXPORT extern scheduler sched; + // @@ CTX: document (backlinks, non-overlap etc). RW story. + // + class LIBBUILD2_SYMEXPORT context + { + struct data; + unique_ptr<data> data_; + + public: + scheduler& sched; + + const scope_map& scopes; + const scope& global_scope; + + target_set& targets; + + const variable_pool& var_pool; + const variable_overrides& var_overrides; // Project and relative scope. + + public: + explicit + context (scheduler&, const strings& cmd_vars = {}); + + void + current_mif (const meta_operation_info&); + + void + current_oif (const operation_info& inner, + const operation_info* outer = nullptr, + bool diag_noise = true); + + context (context&&) = delete; + context& operator= (context&&) = delete; + + context (const context&) = delete; + context& operator= (const context&) = delete; + + ~context (); + }; + // In order to perform each operation the build system goes through the // following phases: // @@ -303,14 +361,6 @@ namespace build2 LIBBUILD2_SYMEXPORT extern atomic_count target_count; LIBBUILD2_SYMEXPORT extern atomic_count skip_count; - LIBBUILD2_SYMEXPORT void - set_current_mif (const meta_operation_info&); - - LIBBUILD2_SYMEXPORT void - set_current_oif (const operation_info& inner, - const operation_info* outer = nullptr, - bool diag_noise = true); - // Keep going flag. // // Note that setting it to false is not of much help unless we are running @@ -352,19 +402,13 @@ namespace build2 // LIBBUILD2_SYMEXPORT extern bool dry_run; - // Reset the build state. In particular, this removes all the targets, - // scopes, and variables. - // - LIBBUILD2_SYMEXPORT variable_overrides - reset (const strings& cmd_vars); - // Config module entry points. // LIBBUILD2_SYMEXPORT extern void (*config_save_variable) ( scope&, const variable&, uint64_t flags); LIBBUILD2_SYMEXPORT extern const string& (*config_preprocess_create) ( - const variable_overrides&, + context&, values&, vector_view<opspec>&, bool lifted, diff --git a/libbuild2/dist/init.cxx b/libbuild2/dist/init.cxx index 4729938..c6ffb67 100644 --- a/libbuild2/dist/init.cxx +++ b/libbuild2/dist/init.cxx @@ -37,7 +37,7 @@ namespace build2 // Enter module variables. Do it during boot in case they get assigned // in bootstrap.build (which is customary for, e.g., dist.package). // - auto& vp (var_pool.rw (rs)); + auto& vp (rs.ctx.var_pool.rw (rs)); // Note: some overridable, some not. // diff --git a/libbuild2/dist/operation.cxx b/libbuild2/dist/operation.cxx index ac3912e..cdeb9ee 100644 --- a/libbuild2/dist/operation.cxx +++ b/libbuild2/dist/operation.cxx @@ -79,6 +79,8 @@ namespace build2 if (rs == nullptr) fail << "out of project target " << t; + context& ctx (rs->ctx); + const dir_path& out_root (rs->out_path ()); const dir_path& src_root (rs->src_path ()); @@ -167,7 +169,7 @@ namespace build2 if (operation_id pid = oif->pre (params, dist_id, loc)) { const operation_info* poif (ops[pid]); - set_current_oif (*poif, oif, false /* diag_noise */); + ctx.current_oif (*poif, oif, false /* diag_noise */); action a (dist_id, poif->id, oif->id); match (params, a, ts, 1 /* diag (failures only) */, @@ -175,7 +177,7 @@ namespace build2 } } - set_current_oif (*oif, nullptr, false /* diag_noise */); + ctx.current_oif (*oif, nullptr, false /* diag_noise */); action a (dist_id, oif->id); match (params, a, ts, 1 /* diag (failures only) */, @@ -186,7 +188,7 @@ namespace build2 if (operation_id pid = oif->post (params, dist_id)) { const operation_info* poif (ops[pid]); - set_current_oif (*poif, oif, false /* diag_noise */); + ctx.current_oif (*poif, oif, false /* diag_noise */); action a (dist_id, poif->id, oif->id); match (params, a, ts, 1 /* diag (failures only) */, @@ -213,7 +215,7 @@ namespace build2 ? out_src (d, rs) : dir_path ()); - targets.insert<buildfile> ( + rs.ctx.targets.insert<buildfile> ( move (d), move (out), p.leaf ().base ().string (), @@ -232,7 +234,7 @@ namespace build2 { const dir_path& pd (p.second); dir_path out_nroot (out_root / pd); - const scope& nrs (scopes.find (out_nroot)); + const scope& nrs (ctx.scopes.find (out_nroot)); if (nrs.out_path () != out_nroot) // This subproject not loaded. continue; @@ -251,9 +253,9 @@ namespace build2 // distribute") since it will be useless (too fast). // action_targets files; - const variable& dist_var (var_pool["dist"]); + const variable& dist_var (ctx.var_pool["dist"]); - for (const auto& pt: targets) + for (const auto& pt: ctx.targets) { file* ft (pt->is_a<file> ()); @@ -305,13 +307,13 @@ namespace build2 // Note also that we don't do any structured result printing. // size_t on (current_on); - set_current_mif (mo_perform); + ctx.current_mif (mo_perform); current_on = on + 1; if (mo_perform.operation_pre != nullptr) mo_perform.operation_pre (params, update_id); - set_current_oif (op_update, nullptr, false /* diag_noise */); + ctx.current_oif (op_update, nullptr, false /* diag_noise */); action a (perform_id, update_id); @@ -374,7 +376,7 @@ namespace build2 const dir_path& pd (p.second); if (dl.sub (pd)) { - srs = &scopes.find (out_root / pd); + srs = &ctx.scopes.find (out_root / pd); if (auto* m = srs->lookup_module<module> (module::name)) cbs = &m->callbacks_; diff --git a/libbuild2/dump.cxx b/libbuild2/dump.cxx index 7d59891..27cd9e2 100644 --- a/libbuild2/dump.cxx +++ b/libbuild2/dump.cxx @@ -6,6 +6,7 @@ #include <libbuild2/scope.hxx> #include <libbuild2/target.hxx> +#include <libbuild2/context.hxx> #include <libbuild2/variable.hxx> #include <libbuild2/diagnostics.hxx> @@ -400,7 +401,8 @@ namespace build2 // Nested scopes of which we are an immediate parent. // - for (auto e (scopes.end ()); i != e && i->second.parent_scope () == &p;) + for (auto e (p.ctx.scopes.end ()); + i != e && i->second.parent_scope () == &p; ) { if (vb) { @@ -421,7 +423,7 @@ namespace build2 // Since targets can occupy multiple lines, we separate them with a // blank line. // - for (const auto& pt: targets) + for (const auto& pt: p.ctx.targets) { const target& t (*pt); @@ -447,10 +449,10 @@ namespace build2 } void - dump (optional<action> a) + dump (const context& c, optional<action> a) { - auto i (scopes.cbegin ()); - assert (&i->second == global_scope); + auto i (c.scopes.cbegin ()); + assert (&i->second == &c.global_scope); // We don't lock diag_stream here as dump() is supposed to be called from // the main thread prior/after to any other threads being spawned. @@ -464,7 +466,7 @@ namespace build2 void dump (const scope& s, const char* cind) { - const scope_map_base& m (scopes); // Iterator interface. + const scope_map_base& m (s.ctx.scopes); // Iterator interface. auto i (m.find (s.out_path ())); assert (i != m.end () && &i->second == &s); diff --git a/libbuild2/dump.hxx b/libbuild2/dump.hxx index fd1886b..aead805 100644 --- a/libbuild2/dump.hxx +++ b/libbuild2/dump.hxx @@ -16,13 +16,14 @@ namespace build2 { class scope; class target; + class context; // Dump the build state to diag_stream. If action is specified, then assume // rules have been matched for this action and dump action-specific // information (like rule-specific variables). // LIBBUILD2_SYMEXPORT void - dump (optional<action> = nullopt); + dump (const context&, optional<action> = nullopt); LIBBUILD2_SYMEXPORT void dump (const scope&, const char* ind = ""); diff --git a/libbuild2/file.cxx b/libbuild2/file.cxx index 1da9397..207f569 100644 --- a/libbuild2/file.cxx +++ b/libbuild2/file.cxx @@ -183,7 +183,7 @@ namespace build2 l5 ([&]{trace << "sourcing " << bf;}); - parser p (boot); + parser p (root.ctx, boot); p.parse_buildfile (is, bf, root, base); } catch (const io_error& e) @@ -263,9 +263,9 @@ namespace build2 } scope_map::iterator - create_root (scope& l, const dir_path& out_root, const dir_path& src_root) + create_root (scope& s, const dir_path& out_root, const dir_path& src_root) { - auto i (scopes.rw (l).insert (out_root, true /* root */)); + auto i (s.ctx.scopes.rw (s).insert (out_root, true /* root */)); scope& rs (i->second); // Set out_path. Note that src_path is set in setup_root() below. @@ -373,7 +373,7 @@ namespace build2 // First, enter the scope into the map and see if it is in any project. If // it is not, then there is nothing else to do. // - auto i (scopes.rw (root).insert (p)); + auto i (root.ctx.scopes.rw (root).insert (p)); scope& base (i->second); scope* rs (base.root_scope ()); @@ -410,7 +410,7 @@ namespace build2 } dir_path - bootstrap_fwd (const dir_path& src_root, optional<bool>& altn) + bootstrap_fwd (context& ctx, const dir_path& src_root, optional<bool>& altn) { path f (exists (src_root, std_out_root_file, alt_out_root_file, altn)); @@ -420,7 +420,7 @@ namespace build2 // We cannot just source the buildfile since there is no scope to do // this on yet. // - auto p (extract_variable (f, *var_out_root)); + auto p (extract_variable (ctx, f, *var_out_root)); if (!p.second) fail << "variable out_root expected as first line in " << f; @@ -495,7 +495,7 @@ namespace build2 } pair<value, bool> - extract_variable (const path& bf, const variable& var) + extract_variable (context& ctx, const path& bf, const variable& var) { try { @@ -513,8 +513,8 @@ namespace build2 return make_pair (value (), false); } - parser p; - temp_scope tmp (global_scope->rw ()); + parser p (ctx); + temp_scope tmp (ctx.global_scope.rw ()); p.parse_variable (lex, tmp, var, tt); value* v (tmp.vars.find_to_modify (var).first); @@ -533,7 +533,8 @@ namespace build2 // Extract the project name from bootstrap.build. // static project_name - find_project_name (const dir_path& out_root, + find_project_name (context& ctx, + const dir_path& out_root, const dir_path& fallback_src_root, optional<bool> out_src, // True if out_root is src_root. optional<bool>& altn) @@ -544,7 +545,7 @@ namespace build2 // in which case we will have src_root and maybe even the name. // const dir_path* src_root (nullptr); - const scope& s (scopes.find (out_root)); + const scope& s (ctx.scopes.find (out_root)); if (s.root_scope () == &s && s.out_path () == out_root) { @@ -588,7 +589,7 @@ namespace build2 } else { - auto p (extract_variable (f, *var_src_root)); + auto p (extract_variable (ctx, f, *var_src_root)); if (!p.second) fail << "variable src_root expected as first line in " << f; @@ -610,7 +611,7 @@ namespace build2 if (f.empty ()) fail << "no build/bootstrap.build in " << *src_root; - auto p (extract_variable (f, *var_project)); + auto p (extract_variable (ctx, f, *var_project)); if (!p.second) fail << "variable " << var_project->name << " expected " @@ -628,7 +629,8 @@ namespace build2 // is a subproject, then enter it into the map, handling the duplicates. // static void - find_subprojects (subprojects& sps, + find_subprojects (context& ctx, + subprojects& sps, const dir_path& d, const dir_path& root, bool out) @@ -668,7 +670,8 @@ namespace build2 // Load its name. Note that here we don't use fallback src_root // since this function is used to scan both out_root and src_root. // - project_name name (find_project_name (sd, dir_path (), src, altn)); + project_name name ( + find_project_name (ctx, sd, dir_path (), src, altn)); // If the name is empty, then is is an unnamed project. While the // 'project' variable stays empty, here we come up with a surrogate @@ -707,26 +710,26 @@ namespace build2 } bool - bootstrap_src (scope& root, optional<bool>& altn) + bootstrap_src (scope& rs, optional<bool>& altn) { tracer trace ("bootstrap_src"); bool r (false); - const dir_path& out_root (root.out_path ()); - const dir_path& src_root (root.src_path ()); + const dir_path& out_root (rs.out_path ()); + const dir_path& src_root (rs.src_path ()); { path f (exists (src_root, std_bootstrap_file, alt_bootstrap_file, altn)); - if (root.root_extra == nullptr) + if (rs.root_extra == nullptr) { // If nothing so far has indicated the naming, assume standard. // if (!altn) altn = false; - setup_root_extra (root, altn); + setup_root_extra (rs, altn); } if (!f.empty ()) @@ -736,8 +739,8 @@ namespace build2 // process hard to reason about. But we may try to bootstrap the same // root scope multiple time. // - if (root.buildfiles.insert (f).second) - source (root, root, f, true); + if (rs.buildfiles.insert (f).second) + source (rs, rs, f, true); else l5 ([&]{trace << "skipping already sourced " << f;}); @@ -757,15 +760,15 @@ namespace build2 // Note: the amalgamation variable value is always a relative directory. // { - auto rp (root.vars.insert (*var_amalgamation)); // Set NULL by default. + auto rp (rs.vars.insert (*var_amalgamation)); // Set NULL by default. value& v (rp.first); if (v && v.empty ()) // Convert empty to NULL. v = nullptr; - if (scope* aroot = root.parent_scope ()->root_scope ()) + if (scope* ars = rs.parent_scope ()->root_scope ()) { - const dir_path& ad (aroot->out_path ()); + const dir_path& ad (ars->out_path ()); dir_path rd (ad.relative (out_root)); // If we already have the amalgamation variable set, verify @@ -829,7 +832,7 @@ namespace build2 // NULL value indicates that we found no subprojects. // { - auto rp (root.vars.insert (*var_subprojects)); // Set NULL by default. + auto rp (rs.vars.insert (*var_subprojects)); // Set NULL by default. value& v (rp.first); if (rp.second) @@ -846,13 +849,13 @@ namespace build2 if (exists (out_root)) { l5 ([&]{trace << "looking for subprojects in " << out_root;}); - find_subprojects (sps, out_root, out_root, true); + find_subprojects (rs.ctx, sps, out_root, out_root, true); } if (out_root != src_root) { l5 ([&]{trace << "looking for subprojects in " << src_root;}); - find_subprojects (sps, src_root, src_root, false); + find_subprojects (rs.ctx, sps, src_root, src_root, false); } if (!sps.empty ()) // Keep it NULL if no subprojects. @@ -923,7 +926,8 @@ namespace build2 // Pass fallback src_root since this is a subproject that was // specified by the user so it is most likely in our src. // - n = find_project_name (out_root / d, + n = find_project_name (rs.ctx, + out_root / d, src_root / d, nullopt /* out_src */, altn); @@ -1012,7 +1016,7 @@ namespace build2 // return (out_root != src_root && cast_false<bool> (orig.vars[var_forwarded]) && - bootstrap_fwd (src_root, altn) == out_root); + bootstrap_fwd (orig.ctx, src_root, altn) == out_root); } void @@ -1273,7 +1277,7 @@ namespace build2 // over anything that we may discover. In particular, we will prefer it // over any bundled subprojects. // - auto& vp (var_pool.rw (iroot)); + auto& vp (ibase.ctx.var_pool.rw (iroot)); for (;;) // Break-out loop. { @@ -1411,6 +1415,8 @@ namespace build2 return names {move (target)}; } + context& ctx (ibase.ctx); + // Bootstrap the imported root scope. This is pretty similar to what we do // in main() except that here we don't try to guess src_root. // @@ -1429,7 +1435,7 @@ namespace build2 if (is_src_root (out_root, altn)) { src_root = move (out_root); - out_root = bootstrap_fwd (src_root, altn); + out_root = bootstrap_fwd (ctx, src_root, altn); fwd = (src_root != out_root); } @@ -1556,7 +1562,7 @@ namespace build2 // there is a use-case for the export stub to return a qualified // name? // - parser p; + parser p (ctx); names v (p.parse_export_stub (ifs, es, iroot, ts)); // If there were no export directive executed in an export stub, assume @@ -1575,7 +1581,7 @@ namespace build2 } const target* - import (const prerequisite_key& pk, bool existing) + import (context& ctx, const prerequisite_key& pk, bool existing) { tracer trace ("import"); @@ -1610,18 +1616,18 @@ namespace build2 const exe* t ( !existing - ? &targets.insert<exe> (tt, - p.directory (), - dir_path (), // No out (out of project). - p.leaf ().base ().string (), - p.extension (), // Always specified. - trace) - : targets.find<exe> (tt, - p.directory (), - dir_path (), - p.leaf ().base ().string (), - p.extension (), - trace)); + ? &ctx.targets.insert<exe> (tt, + p.directory (), + dir_path (), // No out (not in project). + p.leaf ().base ().string (), + p.extension (), // Always specified. + trace) + : ctx.targets.find<exe> (tt, + p.directory (), + dir_path (), + p.leaf ().base ().string (), + p.extension (), + trace)); if (t != nullptr) { diff --git a/libbuild2/file.hxx b/libbuild2/file.hxx index 48d1b63..3909643 100644 --- a/libbuild2/file.hxx +++ b/libbuild2/file.hxx @@ -91,8 +91,8 @@ namespace build2 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. The scope argument is only used as proof of - // lock. + // src_root value is not empty. The scope argument is only used for context + // and as a proof of lock. // LIBBUILD2_SYMEXPORT scope_map::iterator create_root (scope&, const dir_path& out_root, const dir_path& src_root); @@ -140,7 +140,7 @@ namespace build2 // argument semantics. // LIBBUILD2_SYMEXPORT dir_path - bootstrap_fwd (const dir_path& src_root, optional<bool>& altn); + bootstrap_fwd (context&, const dir_path& src_root, optional<bool>& altn); // Bootstrap the project's root scope, the out part. // @@ -201,7 +201,7 @@ namespace build2 // an indication of whether the variable was found. // LIBBUILD2_SYMEXPORT pair<value, bool> - extract_variable (const path&, const variable&); + extract_variable (context&, 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 project. Failed that, it @@ -224,7 +224,7 @@ namespace build2 import (scope& base, name, const location&); const target& - import (const prerequisite_key&); + import (context&, const prerequisite_key&); // As above but only imports as an already existing target. Unlike the above // version, this one can be called during the execute phase. @@ -232,7 +232,7 @@ namespace build2 // Note: similar to search_existing(). // const target* - import_existing (const prerequisite_key&); + import_existing (context&, const prerequisite_key&); } #include <libbuild2/file.ixx> diff --git a/libbuild2/file.ixx b/libbuild2/file.ixx index f8a79be..59b53bb 100644 --- a/libbuild2/file.ixx +++ b/libbuild2/file.ixx @@ -13,19 +13,19 @@ namespace build2 } LIBBUILD2_SYMEXPORT const target* - import (const prerequisite_key&, bool existing); + import (context&, const prerequisite_key&, bool existing); inline const target& - import (const prerequisite_key& pk) + import (context& ctx, const prerequisite_key& pk) { assert (phase == run_phase::match); - return *import (pk, false); + return *import (ctx, pk, false); } inline const target* - import_existing (const prerequisite_key& pk) + import_existing (context& ctx, const prerequisite_key& pk) { assert (phase == run_phase::match || phase == run_phase::execute); - return import (pk, true); + return import (ctx, pk, true); } } diff --git a/libbuild2/function.test.cxx b/libbuild2/function.test.cxx index 2380987..670114e 100644 --- a/libbuild2/function.test.cxx +++ b/libbuild2/function.test.cxx @@ -41,7 +41,8 @@ namespace build2 // init_diag (1); init (nullptr, argv[0]); - reset (strings ()); // No command line variables. + sched.startup (1); // Serial execution. + context ctx (sched); function_family f ("dummy"); @@ -114,9 +115,9 @@ namespace build2 try { - scope& s (*scope::global_); + scope& s (ctx.global_scope.rw ()); - parser p; + parser p (ctx); p.parse_buildfile (cin, path ("buildfile"), s, s); } catch (const failed&) diff --git a/libbuild2/in/init.cxx b/libbuild2/in/init.cxx index a067ec3..8b27336 100644 --- a/libbuild2/in/init.cxx +++ b/libbuild2/in/init.cxx @@ -36,7 +36,7 @@ namespace build2 // Enter variables. // { - auto& vp (var_pool.rw (rs)); + auto& vp (rs.ctx.var_pool.rw (rs)); // Alternative variable substitution symbol with '$' being the // default. diff --git a/libbuild2/install/init.cxx b/libbuild2/install/init.cxx index 060007b..dac337f 100644 --- a/libbuild2/install/init.cxx +++ b/libbuild2/install/init.cxx @@ -62,7 +62,7 @@ namespace build2 vn += name; } vn += var; - const variable& vr (var_pool.rw (r).insert<CT> (move (vn), true)); + const variable& vr (r.ctx.var_pool.rw (r).insert<CT> (move (vn), true)); l = dv != nullptr ? config::required (r, vr, *dv, override).first @@ -79,7 +79,7 @@ namespace build2 vn = "install."; vn += name; vn += var; - const variable& vr (var_pool.rw (r).insert<T> (move (vn))); + const variable& vr (r.ctx.var_pool.rw (r).insert<T> (move (vn))); value& v (r.assign (vr)); @@ -122,7 +122,8 @@ namespace build2 // This one doesn't have config.* value (only set in a buildfile). // if (!global) - var_pool.rw (r).insert<bool> (string ("install.") + n + ".subdirs"); + r.ctx.var_pool.rw (r).insert<bool> ( + string ("install.") + n + ".subdirs"); } void @@ -192,7 +193,7 @@ namespace build2 // Enter module variables. // - auto& vp (var_pool.rw (rs)); + auto& vp (rs.ctx.var_pool.rw (rs)); // Note that the set_dir() calls below enter some more. // diff --git a/libbuild2/install/rule.cxx b/libbuild2/install/rule.cxx index 48a404b..478aadd 100644 --- a/libbuild2/install/rule.cxx +++ b/libbuild2/install/rule.cxx @@ -741,7 +741,7 @@ namespace build2 cstrings args; string reld ( - cast<string> ((*global_scope)["build.host.class"]) == "windows" + cast<string> (rs.ctx.global_scope["build.host.class"]) == "windows" ? msys_path (chd) : relative (chd).string ()); @@ -785,7 +785,7 @@ namespace build2 dir_path chd (chroot_path (rs, base.dir)); string reld ( - cast<string> ((*global_scope)["build.host.class"]) == "windows" + cast<string> (rs.ctx.global_scope["build.host.class"]) == "windows" ? msys_path (chd) : relative (chd).string ()); diff --git a/libbuild2/install/utility.hxx b/libbuild2/install/utility.hxx index 13fcceb..24c82d8 100644 --- a/libbuild2/install/utility.hxx +++ b/libbuild2/install/utility.hxx @@ -24,7 +24,7 @@ namespace build2 { auto r ( s.target_vars[tt]["*"].insert ( - var_pool.rw (s).insert ("install"))); + s.ctx.var_pool.rw (s).insert ("install"))); if (r.second) // Already set by the user? r.first.get () = path_cast<path> (move (d)); @@ -42,7 +42,7 @@ namespace build2 { auto r ( s.target_vars[tt]["*"].insert ( - var_pool.rw (s).insert ("install.mode"))); + s.ctx.var_pool.rw (s).insert ("install.mode"))); if (r.second) // Already set by the user? r.first.get () = move (m); diff --git a/libbuild2/module.cxx b/libbuild2/module.cxx index 1feb121..2ed3671 100644 --- a/libbuild2/module.cxx +++ b/libbuild2/module.cxx @@ -282,7 +282,7 @@ namespace build2 module_state {true, false, mf.init, nullptr, loc}).first; i->second.first = mf.boot (rs, loc, i->second.module); - rs.assign (var_pool.rw (rs).insert (mod + ".booted")) = true; + rs.assign (rs.ctx.var_pool.rw (rs).insert (mod + ".booted")) = true; } bool @@ -333,7 +333,7 @@ namespace build2 // buildfile-visible (where we use the term "load a module"; see the note // on terminology above) // - auto& vp (var_pool.rw (rs)); + auto& vp (rs.ctx.var_pool.rw (rs)); value& lv (bs.assign (vp.insert (mod + ".loaded"))); value& cv (bs.assign (vp.insert (mod + ".configured"))); diff --git a/libbuild2/module.hxx b/libbuild2/module.hxx index 200e52f..f583361 100644 --- a/libbuild2/module.hxx +++ b/libbuild2/module.hxx @@ -128,7 +128,7 @@ namespace build2 const string& name, const location&, bool optional = false, - const variable_map& config_hints = variable_map ()); + const variable_map& config_hints = empty_variable_map); // An alias to use from other modules (we could also distinguish between // boot and init). @@ -142,7 +142,7 @@ namespace build2 const string& name, const location& loc, bool optional = false, - const variable_map& config_hints = variable_map ()) + const variable_map& config_hints = empty_variable_map) { return init_module (root, base, name, loc, optional, config_hints); } diff --git a/libbuild2/operation.cxx b/libbuild2/operation.cxx index 168ed5c..879d416 100644 --- a/libbuild2/operation.cxx +++ b/libbuild2/operation.cxx @@ -81,7 +81,7 @@ namespace build2 // Create the base scope. Note that its existence doesn't mean it was // already setup as a base scope; it can be the same as root. // - auto i (scopes.rw (root).insert (out_base)); + auto i (root.ctx.scopes.rw (root).insert (out_base)); scope& base (setup_base (i, out_base, src_base)); // Load the buildfile unless it is implied. @@ -103,7 +103,7 @@ namespace build2 phase_lock pl (run_phase::match); - const target* t (targets.find (tk, trace)); + const target* t (bs.ctx.targets.find (tk, trace)); // Only do the implied buildfile if we haven't loaded one. Failed that we // may try go this route even though we've concluded the implied buildfile @@ -471,7 +471,7 @@ namespace build2 if (rs.out_path () != out_base || rs.src_path () != src_base) fail (l) << "meta-operation info target must be project root directory"; - setup_base (scopes.rw (rs).insert (out_base), out_base, src_base); + setup_base (rs.ctx.scopes.rw (rs).insert (out_base), out_base, src_base); } void diff --git a/libbuild2/operation.hxx b/libbuild2/operation.hxx index 86f93c6..0d56219 100644 --- a/libbuild2/operation.hxx +++ b/libbuild2/operation.hxx @@ -12,7 +12,6 @@ #include <libbuild2/action.hxx> #include <libbuild2/variable.hxx> -#include <libbuild2/prerequisite.hxx> #include <libbuild2/target-state.hxx> #include <libbuild2/export.hxx> @@ -21,8 +20,10 @@ namespace build2 { class location; class scope; - class target_key; class target; + class target_key; + class context; + class include_type; struct prerequisite_member; struct opspec; @@ -275,7 +276,7 @@ namespace build2 // If lifted is true then the operation name in opspec is bogus (has // been lifted) and the default/empty name should be assumed instead. // - using process_func = const string& (const variable_overrides&, + using process_func = const string& (context&, values&, vector_view<opspec>&, bool lifted, diff --git a/libbuild2/parser.cxx b/libbuild2/parser.cxx index c55d434..a05c898 100644 --- a/libbuild2/parser.cxx +++ b/libbuild2/parser.cxx @@ -129,13 +129,13 @@ namespace build2 tracer& tr) { auto r (process_target (p, n, o, loc)); - return targets.insert (*r.first, // target type - move (n.dir), - move (o.dir), - move (n.value), - move (r.second), // extension - implied, - tr).first; + return p.ctx.targets.insert (*r.first, // target type + move (n.dir), + move (o.dir), + move (n.value), + move (r.second), // extension + implied, + tr).first; } // Only find. @@ -148,12 +148,12 @@ namespace build2 tracer& tr) { auto r (process_target (p, n, o, loc)); - return targets.find (*r.first, // target type - n.dir, - o.dir, - n.value, - r.second, // extension - tr); + return p.ctx.targets.find (*r.first, // target type + n.dir, + o.dir, + n.value, + r.second, // extension + tr); } static pair<const target_type*, optional<string>> @@ -1750,7 +1750,7 @@ namespace build2 // Is this the 'foo=...' case? // size_t p (t.value.find ('=')); - auto& vp (var_pool.rw (*scope_)); + auto& vp (ctx.var_pool.rw (*scope_)); if (p != string::npos) var = &vp.insert (split (p), true /* overridable */); @@ -2452,7 +2452,7 @@ namespace build2 //@@ TODO: append namespace if any. } - return var_pool.rw (*scope_).insert (move (n), true /* overridable */); + return ctx.var_pool.rw (*scope_).insert (move (n), true /* overridable */); } void parser:: @@ -2649,7 +2649,7 @@ namespace build2 if (var.type == nullptr) { const bool o (true); // Allow overrides. - var_pool.update (const_cast<variable&> (var), type, nullptr, &o); + ctx.var_pool.update (const_cast<variable&> (var), type, nullptr, &o); } else if (var.type != type) fail (l) << "changing variable " << var << " type from " @@ -5053,7 +5053,7 @@ namespace build2 // lexer l (is, *path_, 1 /* line */, "\'\"\\$("); lexer_ = &l; - scope_ = root_ = scope::global_; + scope_ = root_ = &ctx.global_scope.rw (); pbase_ = &work; // Use current working directory. target_ = nullptr; prerequisite_ = nullptr; @@ -5342,7 +5342,7 @@ namespace build2 // Lookup. // - const auto& var (var_pool.rw (*scope_).insert (move (name), true)); + const auto& var (ctx.var_pool.rw (*scope_).insert (move (name), true)); if (p != nullptr) { @@ -5435,12 +5435,12 @@ namespace build2 target* ct ( const_cast<target*> ( // Ok (serial execution). - targets.find (dir::static_type, // Explicit current dir target. - scope_->out_path (), - dir_path (), // Out tree target. - string (), - nullopt, - trace))); + ctx.targets.find (dir::static_type, // Explicit current dir target. + scope_->out_path (), + dir_path (), // Out tree target. + string (), + nullopt, + trace))); if (ct == nullptr) { @@ -5449,13 +5449,13 @@ namespace build2 // While this target is not explicitly mentioned in the buildfile, we // say that we behave as if it were. Thus not implied. // - ct = &targets.insert (dir::static_type, - scope_->out_path (), - dir_path (), - string (), - nullopt, - false, - trace).first; + ct = &ctx.targets.insert (dir::static_type, + scope_->out_path (), + dir_path (), + string (), + nullopt, + false, + trace).first; // Fall through. } else if (ct->implied) @@ -5487,7 +5487,7 @@ namespace build2 out = out_src (d, *root_); } - targets.insert<buildfile> ( + ctx.targets.insert<buildfile> ( move (d), move (out), p.leaf ().base ().string (), diff --git a/libbuild2/parser.hxx b/libbuild2/parser.hxx index 79cbead..2f70a18 100644 --- a/libbuild2/parser.hxx +++ b/libbuild2/parser.hxx @@ -22,6 +22,7 @@ namespace build2 { class scope; class target; + class context; class prerequisite; class LIBBUILD2_SYMEXPORT parser @@ -31,7 +32,8 @@ namespace build2 // should only be bootstrapped. // explicit - parser (bool boot = false): fail ("error", &path_), boot_ (boot) {} + parser (context& c, bool boot = false) + : fail ("error", &path_), ctx (c), boot_ (boot) {} // Issue diagnostics and throw failed in case of an error. // @@ -645,6 +647,8 @@ namespace build2 const fail_mark fail; protected: + context& ctx; + bool pre_parse_ = false; bool boot_; diff --git a/libbuild2/prerequisite.cxx b/libbuild2/prerequisite.cxx index 7355323..b73aa4e 100644 --- a/libbuild2/prerequisite.cxx +++ b/libbuild2/prerequisite.cxx @@ -64,7 +64,7 @@ namespace build2 ext (to_ext (t.ext ())), scope (t.base_scope ()), target (&t), - vars (false /* global */) + vars (t.ctx, false /* global */) { } diff --git a/libbuild2/prerequisite.hxx b/libbuild2/prerequisite.hxx index f79ce04..3183a29 100644 --- a/libbuild2/prerequisite.hxx +++ b/libbuild2/prerequisite.hxx @@ -8,6 +8,7 @@ #include <libbuild2/types.hxx> #include <libbuild2/utility.hxx> +#include <libbuild2/scope.hxx> #include <libbuild2/action.hxx> #include <libbuild2/variable.hxx> #include <libbuild2/target-key.hxx> @@ -17,7 +18,6 @@ namespace build2 { - class scope; class target; // Light-weight (by being shallow-pointing) prerequisite key, similar @@ -31,7 +31,7 @@ namespace build2 class prerequisite_key { public: - typedef build2::scope scope_type; + using scope_type = build2::scope; const optional<project_name>& proj; target_key tk; // The .dir and .out members can be relative. @@ -117,7 +117,7 @@ namespace build2 name (move (n)), ext (move (e)), scope (s), - vars (false /* global */) {} + vars (s.ctx, false /* global */) {} // Make a prerequisite from a target. // diff --git a/libbuild2/scope.cxx b/libbuild2/scope.cxx index 1ad7455..ab81333 100644 --- a/libbuild2/scope.cxx +++ b/libbuild2/scope.cxx @@ -354,7 +354,7 @@ namespace build2 // global scope. // if (inner_proj == nullptr) - inner_proj = global_scope; + inner_proj = &ctx.global_scope; // Now find our "stem", that is, the value to which we will be appending // suffixes and prepending prefixes. This is either the original or the @@ -430,7 +430,7 @@ namespace build2 // Check the cache. // variable_override_cache& cache ( - inner_proj == global_scope + inner_proj == &ctx.global_scope ? global_override_cache : inner_proj->root_extra->override_cache); @@ -595,7 +595,7 @@ namespace build2 // for (const scope* s (this); s != nullptr; - s = s->root () ? global_scope : s->parent_scope ()) + s = s->root () ? &s->global_scope () : s->parent_scope ()) { if (s->target_types.empty ()) continue; @@ -619,7 +619,9 @@ namespace build2 { // Pretty much the same logic as in find_target_type() above. // - for (; s != nullptr; s = s->root () ? global_scope : s->parent_scope ()) + for (; + s != nullptr; + s = s->root () ? &s->global_scope () : s->parent_scope ()) { if (s->target_types.empty ()) continue; @@ -730,7 +732,7 @@ namespace build2 // factor it back into the name (this way we won't assert when printing // diagnostics; see to_stream(target_key) for details). // - if (ext && + if (ext && tt->fixed_extension == nullptr && tt->default_extension == nullptr) { @@ -743,7 +745,8 @@ namespace build2 } static target* - derived_tt_factory (const target_type& t, dir_path d, dir_path o, string n) + derived_tt_factory (context& c, + const target_type& t, dir_path d, dir_path o, string n) { // Pass our type to the base factory so that it can detect that it is // being called to construct a derived target. This can be used, for @@ -756,7 +759,7 @@ namespace build2 const target_type* bt (t.base); for (; bt->factory == &derived_tt_factory; bt = bt->base) ; - target* r (bt->factory (t, move (d), move (o), move (n))); + target* r (bt->factory (c, t, move (d), move (o), move (n))); r->derived_type = &t; return r; } @@ -818,23 +821,18 @@ namespace build2 return target_types.insert (name, move (dt)); } - scope* scope::global_; + //@@ CTX ??? sounds like should be in context! scope::variable_override_cache scope::global_override_cache; // scope_map // - scope_map scope_map::instance; - const scope_map& scope_map::cinstance = scope_map::instance; - const scope_map& scopes = scope_map::cinstance; - - const scope* global_scope; auto scope_map:: insert (const dir_path& k, bool root) -> iterator { scope_map_base& m (*this); - auto er (m.emplace (k, scope (true))); // Global. + auto er (m.emplace (k, scope (ctx, true /* global */))); scope& s (er.first->second); // If this is a new scope, update the parent chain. diff --git a/libbuild2/scope.hxx b/libbuild2/scope.hxx index 0c4094b..c3816dd 100644 --- a/libbuild2/scope.hxx +++ b/libbuild2/scope.hxx @@ -29,6 +29,10 @@ namespace build2 class LIBBUILD2_SYMEXPORT scope { public: + // Context this scope belongs to. + // + context& ctx; + // Absolute and normalized. // const dir_path& out_path () const {return *out_path_;} @@ -71,6 +75,11 @@ namespace build2 scope* weak_scope (); const scope* weak_scope () const; + // Global scope. + // + scope& global_scope () {return const_cast<scope&> (ctx.global_scope);} + const scope& global_scope () const {return ctx.global_scope;} + // Return true if the specified root scope is a sub-scope of this root // scope. Note that both scopes must be root. // @@ -102,7 +111,7 @@ namespace build2 lookup operator[] (const string& name) const { - const variable* var (var_pool.find (name)); + const variable* var (ctx.var_pool.find (name)); return var != nullptr ? operator[] (*var) : lookup (); } @@ -159,7 +168,7 @@ namespace build2 value& assign (string name) { - return assign (variable_pool::instance.insert (move (name))); + return assign (ctx.var_pool.rw (*this).insert (move (name))); } // Assign a typed non-overridable variable with normal visibility. @@ -168,7 +177,7 @@ namespace build2 value& assign (string name) { - return vars.assign (variable_pool::instance.insert<T> (move (name))); + return vars.assign (ctx.var_pool.rw (*this).insert<T> (move (name))); } // Return a value suitable for appending. If the variable does not @@ -337,14 +346,6 @@ namespace build2 return const_cast<scope&> (*this); } - // RW access to global scope (RO via global global_scope below). - // - scope& - global () {return *global_;} - - public: - static scope* global_; // Normally not accessed directly. - private: friend class parser; friend class scope_map; @@ -356,8 +357,8 @@ namespace build2 friend LIBBUILD2_SYMEXPORT scope& create_bootstrap_inner (scope&, const dir_path&); - explicit - scope (bool global): vars (global), target_vars (global) {} + scope (context& c, bool global) + : ctx (c), vars (c, global), target_vars (c, global) {} scope* parent_; scope* root_; @@ -405,7 +406,7 @@ namespace build2 { public: temp_scope (scope& p) - : scope (false) // Not global. + : scope (p.ctx, false /* global */) { out_path_ = p.out_path_; src_path_ = p.src_path_; @@ -469,24 +470,21 @@ namespace build2 rw (scope&) const {return const_cast<scope_map&> (*this);} private: - LIBBUILD2_SYMEXPORT static scope_map instance; + friend class context; + + explicit + scope_map (context& c): ctx (c) {} // Entities that can access bypassing the lock proof. // friend int main (int, char*[]); - friend LIBBUILD2_SYMEXPORT variable_overrides reset (const strings&); LIBBUILD2_SYMEXPORT scope& find (const dir_path&); - public: - // For var_pool initialization. - // - LIBBUILD2_SYMEXPORT static const scope_map& cinstance; + private: + context& ctx; }; - - LIBBUILD2_SYMEXPORT extern const scope_map& scopes; - LIBBUILD2_SYMEXPORT extern const scope* global_scope; } #include <libbuild2/scope.ixx> diff --git a/libbuild2/search.cxx b/libbuild2/search.cxx index 917d750..199bc10 100644 --- a/libbuild2/search.cxx +++ b/libbuild2/search.cxx @@ -16,7 +16,7 @@ using namespace butl; namespace build2 { const target* - search_existing_target (const prerequisite_key& pk) + search_existing_target (context& ctx, const prerequisite_key& pk) { tracer trace ("search_existing_target"); @@ -69,7 +69,8 @@ namespace build2 o.clear (); } - const target* t (targets.find (*tk.type, d, o, *tk.name, tk.ext, trace)); + const target* t ( + ctx.targets.find (*tk.type, d, o, *tk.name, tk.ext, trace)); if (t != nullptr) l5 ([&]{trace << "existing target " << *t @@ -79,7 +80,7 @@ namespace build2 } const target* - search_existing_file (const prerequisite_key& cpk) + search_existing_file (context& ctx, const prerequisite_key& cpk) { tracer trace ("search_existing_file"); @@ -184,7 +185,7 @@ namespace build2 // Find or insert. Note that we are using our updated extension. // auto r ( - targets.insert ( + ctx.targets.insert ( *tk.type, move (d), move (out), *tk.name, ext, true, trace)); // Has to be a file_target. @@ -201,7 +202,7 @@ namespace build2 } const target& - create_new_target (const prerequisite_key& pk) + create_new_target (context& ctx, const prerequisite_key& pk) { tracer trace ("create_new_target"); @@ -227,13 +228,13 @@ namespace build2 // // @@ OUT: same story as in search_existing_target() re out. // - auto r (targets.insert (*tk.type, - move (d), - *tk.out, - *tk.name, - tk.ext, - true /* implied */, - trace)); + auto r (ctx.targets.insert (*tk.type, + move (d), + *tk.out, + *tk.name, + tk.ext, + true /* implied */, + trace)); const target& t (r.first); l5 ([&]{trace << (r.second ? "new" : "existing") << " target " << t diff --git a/libbuild2/search.hxx b/libbuild2/search.hxx index b281b12..63ea6b1 100644 --- a/libbuild2/search.hxx +++ b/libbuild2/search.hxx @@ -13,12 +13,14 @@ namespace build2 { class target; + class context; class prerequisite_key; - // Search for an existing target in this prerequisite's scope. + // Search for an existing target in this prerequisite's scope. Scope can be + // NULL if directories are absolute. // LIBBUILD2_SYMEXPORT const target* - search_existing_target (const prerequisite_key&); + search_existing_target (context&, const prerequisite_key&); // Search for an existing file. If the prerequisite directory is relative, // then look in the scope's src directory. Otherwise, if the absolute @@ -30,12 +32,12 @@ namespace build2 // contains the search paths. But there wasn't any need for this yet. // LIBBUILD2_SYMEXPORT const target* - search_existing_file (const prerequisite_key&); + search_existing_file (context&, const prerequisite_key&); // Create a new target in this prerequisite's scope. // LIBBUILD2_SYMEXPORT const target& - create_new_target (const prerequisite_key&); + create_new_target (context&, const prerequisite_key&); } #endif // LIBBUILD2_SEARCH_HXX diff --git a/libbuild2/target-key.hxx b/libbuild2/target-key.hxx index e23991d..4df2b1f 100644 --- a/libbuild2/target-key.hxx +++ b/libbuild2/target-key.hxx @@ -53,7 +53,7 @@ namespace build2 else { // Note that for performance reasons here we use the specified extension - // without calling fixed_extension(). + // without calling fixed_extension() to verify it matches. // const char* xe (x.ext ? x.ext->c_str () diff --git a/libbuild2/target-type.hxx b/libbuild2/target-type.hxx index 3537c90..8b308c3 100644 --- a/libbuild2/target-type.hxx +++ b/libbuild2/target-type.hxx @@ -16,6 +16,7 @@ namespace build2 { class scope; class target; + class context; class target_key; class prerequisite_key; @@ -62,10 +63,15 @@ namespace build2 const char* name; const target_type* base; - target* (*factory) (const target_type&, dir_path, dir_path, string); + target* (*factory) (context&, + const target_type&, + dir_path, + dir_path, + string); const char* (*fixed_extension) (const target_key&, const scope* root); + optional<string> (*default_extension) (const target_key&, const scope& base, const char*, diff --git a/libbuild2/target.cxx b/libbuild2/target.cxx index c823e85..9749e94 100644 --- a/libbuild2/target.cxx +++ b/libbuild2/target.cxx @@ -67,7 +67,7 @@ namespace build2 const string& target:: ext (string v) { - ulock l (targets.mutex_); + ulock l (ctx.targets.mutex_); // Once the extension is set, it is immutable. However, it is possible // that someone has already "branded" this target with a different @@ -102,7 +102,7 @@ namespace build2 // If this target is from the src tree, use its out directory to find // the scope. // - return scopes.find (out_dir ()); + return ctx.scopes.find (out_dir ()); } const scope& target:: @@ -292,8 +292,6 @@ namespace build2 // target_set // - target_set targets; - const target* target_set:: find (const target_key& k, tracer& trace) const { @@ -374,7 +372,7 @@ namespace build2 ? string (tt.fixed_extension (tk, nullptr /* root scope */)) : move (tk.ext)); - t = tt.factory (tt, move (dir), move (out), move (name)); + t = tt.factory (ctx, tt, move (dir), move (out), move (name)); // Re-lock for exclusive access. In the meantime, someone could have // inserted this target so emplace() below could return false, in which @@ -658,25 +656,25 @@ namespace build2 // const target* - target_search (const target&, const prerequisite_key& pk) + target_search (const target& t, const prerequisite_key& pk) { // The default behavior is to look for an existing target in the // prerequisite's directory scope. // - return search_existing_target (pk); + return search_existing_target (t.ctx, pk); } const target* - file_search (const target&, const prerequisite_key& pk) + file_search (const target& t, const prerequisite_key& pk) { // First see if there is an existing target. // - if (const target* t = search_existing_target (pk)) - return t; + if (const target* e = search_existing_target (t.ctx, pk)) + return e; // Then look for an existing file in the src tree. // - return search_existing_file (pk); + return search_existing_file (t.ctx, pk); } void @@ -753,17 +751,17 @@ namespace build2 }; static const target* - alias_search (const target&, const prerequisite_key& pk) + alias_search (const target& t, const prerequisite_key& pk) { // For an alias we don't want to silently create a target since it will do // nothing and it most likely not what the user intended. // - const target* t (search_existing_target (pk)); + const target* e (search_existing_target (t.ctx, pk)); - if (t == nullptr || t->implied) + if (e == nullptr || e->implied) fail << "no explicit target for " << pk; - return t; + return e; } const target_type alias::static_type @@ -847,16 +845,16 @@ namespace build2 } static const target* - dir_search (const target&, const prerequisite_key& pk) + dir_search (const target& t, const prerequisite_key& pk) { tracer trace ("dir_search"); // The first step is like in search_alias(): looks for an existing target. // - const target* t (search_existing_target (pk)); + const target* e (search_existing_target (t.ctx, pk)); - if (t != nullptr && !t->implied) - return t; + if (e != nullptr && !e->implied) + return e; // If not found (or is implied), then try to load the corresponding // buildfile (which would normally define this target). Failed that, see @@ -905,10 +903,10 @@ namespace build2 // have loaded the buildfile. So re-test now that we are in exclusive // phase. // - if (t == nullptr) - t = search_existing_target (pk); + if (e == nullptr) + e = search_existing_target (t.ctx, pk); - if (t != nullptr && !t->implied) + if (e != nullptr && !e->implied) retest = true; else { @@ -933,8 +931,8 @@ namespace build2 } else if (exists (src_base)) { - t = dir::search_implied (base, pk, trace); - retest = (t != nullptr); + e = dir::search_implied (base, pk, trace); + retest = (e != nullptr); } } } @@ -945,11 +943,11 @@ namespace build2 // if (retest) { - if (t == nullptr) - t = search_existing_target (pk); + if (e == nullptr) + e = search_existing_target (t.ctx, pk); - if (t != nullptr && !t->implied) - return t; + if (e != nullptr && !e->implied) + return e; } } @@ -1100,7 +1098,10 @@ namespace build2 // Note: we are guaranteed the scope is never NULL for prerequisites // (where out/dir could be relative and none of this will work). // + // @@ CTX TODO +#if 0 root = scopes.find (tk.out->empty () ? *tk.dir : *tk.out).root_scope (); +#endif if (root == nullptr || root->root_extra == nullptr) fail << "unable to determine extension for buildfile target " << tk; diff --git a/libbuild2/target.hxx b/libbuild2/target.hxx index 4bd11fe..5be1aab 100644 --- a/libbuild2/target.hxx +++ b/libbuild2/target.hxx @@ -123,9 +123,11 @@ namespace build2 // class LIBBUILD2_SYMEXPORT target { - optional<string>* ext_; // Reference to value in target_key. - public: + // Context this scope belongs to. + // + context& ctx; + // For targets that are in the src tree of a project we also keep the // corresponding out directory. As a result we may end up with multiple // targets for the same file if we are building multiple configurations of @@ -140,9 +142,10 @@ namespace build2 // when src == out). We also treat out of project targets as being in the // out tree. // - const dir_path dir; // Absolute and normalized. - const dir_path out; // Empty or absolute and normalized. - const string name; + const dir_path dir; // Absolute and normalized. + const dir_path out; // Empty or absolute and normalized. + const string name; + optional<string>* ext_; // Reference to value in target_key. const string* ext () const; // Return NULL if not specified. const string& ext (string); @@ -392,7 +395,7 @@ namespace build2 lookup operator[] (const string& name) const { - const variable* var (var_pool.find (name)); + const variable* var (ctx.var_pool.find (name)); return var != nullptr ? operator[] (*var) : lookup (); } @@ -530,7 +533,7 @@ namespace build2 lookup operator[] (const string& name) const { - const variable* var (var_pool.find (name)); + const variable* var (target_->ctx.var_pool.find (name)); return var != nullptr ? operator[] (*var) : lookup (); } @@ -563,7 +566,8 @@ namespace build2 assign (const variable* var) {return vars.assign (var);} // For cached. public: - opstate (): vars (false /* global */) {} + explicit + opstate (context& c): vars (c, false /* global */) {} private: friend class target_set; @@ -756,9 +760,11 @@ namespace build2 // Targets should be created via the targets set below. // public: - target (dir_path d, dir_path o, string n) - : dir (move (d)), out (move (o)), name (move (n)), - vars (false /* global */) {} + target (context& c, dir_path d, dir_path o, string n) + : ctx (c), + dir (move (d)), out (move (o)), name (move (n)), + vars (c, false /* global */), + state {opstate (c), opstate (c)} {} target (target&&) = delete; target& operator= (target&&) = delete; @@ -1377,13 +1383,17 @@ namespace build2 private: friend class target; // Access to mutex. + friend class context; + + explicit + target_set (context& c): ctx (c) {} + + context& ctx; mutable shared_mutex mutex_; map_type map_; }; - LIBBUILD2_SYMEXPORT extern target_set targets; - // Modification time-based target. // class LIBBUILD2_SYMEXPORT mtime_target: public target @@ -1752,9 +1762,10 @@ namespace build2 // template <typename T> target* - target_factory (const target_type&, dir_path d, dir_path o, string n) + target_factory (context& c, + const target_type&, dir_path d, dir_path o, string n) { - return new T (move (d), move (o), move (n)); + return new T (c, move (d), move (o), move (n)); } // Return fixed target extension unless one was specified. diff --git a/libbuild2/target.ixx b/libbuild2/target.ixx index 1671c25..fea87f3 100644 --- a/libbuild2/target.ixx +++ b/libbuild2/target.ixx @@ -13,7 +13,7 @@ namespace build2 inline const string* target:: ext () const { - slock l (targets.mutex_); + slock l (ctx.targets.mutex_); return *ext_ ? &**ext_ : nullptr; } diff --git a/libbuild2/target.txx b/libbuild2/target.txx index b93a403..8b17383 100644 --- a/libbuild2/target.txx +++ b/libbuild2/target.txx @@ -95,7 +95,7 @@ namespace build2 { // Include target type/pattern-specific variables. // - if (auto l = s.find (var_pool[var], tt, tn)) + if (auto l = s.find (s.ctx.var_pool[var], tt, tn)) { // Help the user here and strip leading '.' from the extension. // @@ -172,13 +172,13 @@ namespace build2 // We behave as if this target was explicitly mentioned in the (implied) // buildfile. Thus not implied. // - target& t (targets.insert (dir::static_type, - bs.out_path (), - dir_path (), - string (), - nullopt, - false, - trace).first); + target& t (bs.ctx.targets.insert (dir::static_type, + bs.out_path (), + dir_path (), + string (), + nullopt, + false, + trace).first); t.prerequisites (move (ps)); return &t; } diff --git a/libbuild2/test/init.cxx b/libbuild2/test/init.cxx index a5afea0..3fb4df6 100644 --- a/libbuild2/test/init.cxx +++ b/libbuild2/test/init.cxx @@ -39,7 +39,7 @@ namespace build2 // Enter module variables. Do it during boot in case they get assigned // in bootstrap.build. // - auto& vp (var_pool.rw (rs)); + auto& vp (rs.ctx.var_pool.rw (rs)); common_data d { @@ -109,7 +109,7 @@ namespace build2 value& v (rs.assign (d.test_target)); if (!v || v.empty ()) - v = cast<target_triplet> ((*global_scope)["build.host"]); + v = cast<target_triplet> (rs.ctx.global_scope["build.host"]); } mod.reset (new module (move (d))); diff --git a/libbuild2/test/rule.cxx b/libbuild2/test/rule.cxx index 1d11063..50eb105 100644 --- a/libbuild2/test/rule.cxx +++ b/libbuild2/test/rule.cxx @@ -364,7 +364,7 @@ namespace build2 build2::test::script::script s (t, ts, wd); { - parser p; + parser p (t.ctx); p.pre_parse (s); default_runner r (c); diff --git a/libbuild2/test/script/builtin.cxx b/libbuild2/test/script/builtin.cxx index ae979da..a725e17 100644 --- a/libbuild2/test/script/builtin.cxx +++ b/libbuild2/test/script/builtin.cxx @@ -956,7 +956,7 @@ namespace build2 auto mv = [ops, &wd, &sp, &error] (const path& from, const path& to) { - const dir_path& rwd (sp.root->wd_path); + const dir_path& rwd (sp.root.wd_path); if (!from.sub (rwd) && !ops.force ()) error () << "'" << from << "' is out of working directory '" @@ -1158,7 +1158,7 @@ namespace build2 error () << "missing file"; const dir_path& wd (sp.wd_path); - const dir_path& rwd (sp.root->wd_path); + const dir_path& rwd (sp.root.wd_path); while (scan.more ()) { @@ -1259,7 +1259,7 @@ namespace build2 error () << "missing directory"; const dir_path& wd (sp.wd_path); - const dir_path& rwd (sp.root->wd_path); + const dir_path& rwd (sp.root.wd_path); while (scan.more ()) { diff --git a/libbuild2/test/script/parser.cxx b/libbuild2/test/script/parser.cxx index 260bc88..a4aeff2 100644 --- a/libbuild2/test/script/parser.cxx +++ b/libbuild2/test/script/parser.cxx @@ -2866,7 +2866,7 @@ namespace build2 { try { - parser p; + parser p (scr.test_target.ctx); p.execute (s, scr, r); } catch (const failed&) diff --git a/libbuild2/test/script/parser.hxx b/libbuild2/test/script/parser.hxx index 1beee49..e4d1f3a 100644 --- a/libbuild2/test/script/parser.hxx +++ b/libbuild2/test/script/parser.hxx @@ -16,6 +16,8 @@ namespace build2 { + class context; + namespace test { namespace script @@ -28,6 +30,8 @@ namespace build2 // Pre-parse. Issue diagnostics and throw failed in case of an error. // public: + parser (context& c): build2::parser (c) {} + void pre_parse (script&); diff --git a/libbuild2/test/script/parser.test.cxx b/libbuild2/test/script/parser.test.cxx index ab1f295..e45674b 100644 --- a/libbuild2/test/script/parser.test.cxx +++ b/libbuild2/test/script/parser.test.cxx @@ -9,7 +9,7 @@ #include <libbuild2/utility.hxx> #include <libbuild2/target.hxx> -#include <libbuild2/context.hxx> // reset() +#include <libbuild2/context.hxx> #include <libbuild2/scheduler.hxx> #include <libbuild2/test/target.hxx> @@ -156,7 +156,7 @@ namespace build2 init_diag (1); init (nullptr, argv[0]); sched.startup (1); // Serial execution. - reset (strings ()); // No command line variables. + context ctx (sched); bool scope (false); bool id (false); @@ -195,32 +195,32 @@ namespace build2 // really care. // file& tt ( - targets.insert<file> (work, - dir_path (), - "driver", - string (), - trace)); + ctx.targets.insert<file> (work, + dir_path (), + "driver", + string (), + trace)); value& v ( tt.assign ( - var_pool.rw ().insert<target_triplet> ( + ctx.var_pool.rw ().insert<target_triplet> ( "test.target", variable_visibility::project))); - v = cast<target_triplet> ((*global_scope)["build.host"]); + v = cast<target_triplet> (ctx.global_scope["build.host"]); testscript& st ( - targets.insert<testscript> (work, - dir_path (), - name.leaf ().base ().string (), - name.leaf ().extension (), - trace)); + ctx.targets.insert<testscript> (work, + dir_path (), + name.leaf ().base ().string (), + name.leaf ().extension (), + trace)); tt.path (path ("driver")); st.path (name); // Parse and run. // - parser p; + parser p (ctx); script s (tt, st, dir_path (work) /= "test-driver"); p.pre_parse (cin, s); diff --git a/libbuild2/test/script/runner.cxx b/libbuild2/test/script/runner.cxx index f0c089b..c27ad39 100644 --- a/libbuild2/test/script/runner.cxx +++ b/libbuild2/test/script/runner.cxx @@ -299,7 +299,7 @@ namespace build2 else { eop = path (op + ".orig"); - save (eop, transform (rd.str, false, rd.modifiers, *sp.root), ll); + save (eop, transform (rd.str, false, rd.modifiers, sp.root), ll); sp.clean_special (eop); } @@ -312,7 +312,7 @@ namespace build2 // Ignore Windows newline fluff if that's what we are running on. // - if (test_target (*sp.root).class_ == "windows") + if (test_target (sp.root).class_ == "windows") args.push_back ("--strip-trailing-cr"); args.push_back (eop.string ().c_str ()); @@ -451,14 +451,14 @@ namespace build2 if (l.regex) // Regex (possibly empty), { r += rl.intro; - r += transform (l.value, true, rd.modifiers, *sp.root); + r += transform (l.value, true, rd.modifiers, sp.root); r += rl.intro; r += l.flags; } else if (!l.special.empty ()) // Special literal. r += rl.intro; else // Textual literal. - r += transform (l.value, false, rd.modifiers, *sp.root); + r += transform (l.value, false, rd.modifiers, sp.root); r += l.special; return r; @@ -534,7 +534,7 @@ namespace build2 { try { - string s (transform (l.value, true, rd.modifiers, *sp.root)); + string s (transform (l.value, true, rd.modifiers, sp.root)); c = line_char ( char_regex (s, gf | parse_flags (l.flags)), pool); @@ -571,7 +571,7 @@ namespace build2 // Append literal line char. // rls += line_char ( - transform (l.value, false, rd.modifiers, *sp.root), pool); + transform (l.value, false, rd.modifiers, sp.root), pool); } for (char c: l.special) @@ -693,7 +693,7 @@ namespace build2 bool default_runner:: test (scope& s) const { - return common_.test (s.root->test_target, s.id_path); + return common_.test (s.root.test_target, s.id_path); } void default_runner:: @@ -724,7 +724,7 @@ namespace build2 sp.parent == nullptr ? mkdir_buildignore ( sp.wd_path, - sp.root->target_scope.root_scope ()->root_extra->buildignore_file, + sp.root.target_scope.root_scope ()->root_extra->buildignore_file, 2) : mkdir (sp.wd_path, 2)); @@ -925,7 +925,7 @@ namespace build2 : (wd && sp.parent == nullptr ? rmdir_buildignore ( d, - sp.root->target_scope.root_scope ()->root_extra->buildignore_file, + sp.root.target_scope.root_scope ()->root_extra->buildignore_file, v) : rmdir (d, v))); @@ -1097,8 +1097,8 @@ namespace build2 // locking as the variable pool is an associative container // (underneath) and we are only adding new variables into it. // - ulock ul (sp.root->var_pool_mutex); - const variable& var (sp.root->var_pool.insert (move (vname))); + ulock ul (sp.root.var_pool_mutex); + const variable& var (sp.root.var_pool.insert (move (vname))); ul.unlock (); value& lhs (sp.assign (var)); @@ -1123,7 +1123,7 @@ namespace build2 dr << info (ll) << "while parsing attributes '" << *ats << "'"; }); - parser p; + parser p (sp.root.test_target.ctx); p.apply_value_attributes (&var, lhs, value (move (ns)), @@ -1175,7 +1175,7 @@ namespace build2 const string& ls (np.leaf ().string ()); bool wc (ls == "*" || ls == "**" || ls == "***"); const path& cp (wc ? np.directory () : np); - const dir_path& wd (sp.root->wd_path); + const dir_path& wd (sp.root.wd_path); if (!cp.sub (wd)) fail (ll) << (wc @@ -1360,7 +1360,7 @@ namespace build2 isp = std_path ("stdin"); save ( - isp, transform (in.str, false, in.modifiers, *sp.root), ll); + isp, transform (in.str, false, in.modifiers, sp.root), ll); sp.clean_special (isp); diff --git a/libbuild2/test/script/script.cxx b/libbuild2/test/script/script.cxx index b879eb4..af9ba82 100644 --- a/libbuild2/test/script/script.cxx +++ b/libbuild2/test/script/script.cxx @@ -418,12 +418,12 @@ namespace build2 // scope // scope:: - scope (const string& id, scope* p, script* r) + scope (const string& id, scope* p, script& r) : parent (p), root (r), - vars (false /* global */), - id_path (cast<path> (assign (root->id_var) = path ())), - wd_path (cast<dir_path> (assign (root->wd_var) = dir_path ())) + vars (r.test_target.ctx, false /* global */), + id_path (cast<path> (assign (root.id_var) = path ())), + wd_path (cast<dir_path> (assign (root.wd_var) = dir_path ())) { // Construct the id_path as a string to ensure POSIX form. In fact, @@ -455,7 +455,7 @@ namespace build2 assert (!implicit || c.type == cleanup_type::always); const path& p (c.path); - if (!p.sub (root->wd_path)) + if (!p.sub (root.wd_path)) { if (implicit) return; @@ -481,8 +481,11 @@ namespace build2 // script_base // script_base:: - script_base () - : // Enter the test.* variables with the same variable types as in + script_base (const target& tt, const testscript& st) + : test_target (tt), + target_scope (tt.base_scope ()), + script_target (st), + // Enter the test.* variables with the same variable types as in // buildfiles except for test: while in buildfiles it can be a // target name, in testscripts it should be resolved to a path. // @@ -515,10 +518,8 @@ namespace build2 script (const target& tt, const testscript& st, const dir_path& rwd) - : group (st.name == "testscript" ? string () : st.name, this), - test_target (tt), - target_scope (tt.base_scope ()), - script_target (st) + : script_base (tt, st), + group (st.name == "testscript" ? string () : st.name, *this) { // Set the script working dir ($~) to $out_base/test/<id> (id_path // for root is just the id which is empty if st is 'testscript'). @@ -634,12 +635,11 @@ namespace build2 // in parallel). Plus, if there is no such variable, then we cannot // possibly find any value. // - const variable* pvar (build2::var_pool.find (n)); + const variable* pvar (root.test_target.ctx.var_pool.find (n)); if (pvar == nullptr) return lookup (); - const script& s (static_cast<const script&> (*root)); const variable& var (*pvar); // First check the target we are testing. @@ -649,12 +649,12 @@ namespace build2 // value. In this case, presumably the override also affects the // script target and we will pick it up there. A bit fuzzy. // - auto p (s.test_target.find_original (var, target_only)); + auto p (root.test_target.find_original (var, target_only)); if (p.first) { if (var.overrides != nullptr) - p = s.target_scope.find_override (var, move (p), true); + p = root.target_scope.find_override (var, move (p), true); return p.first; } @@ -665,7 +665,7 @@ namespace build2 // in different scopes which brings the question of which scopes we // should search. // - return s.script_target[var]; + return root.script_target[var]; } value& scope:: @@ -696,30 +696,30 @@ namespace build2 s.insert (s.end (), v.begin (), v.end ()); }; - if (lookup l = find (root->test_var)) + if (lookup l = find (root.test_var)) s.push_back (cast<path> (l).representation ()); - if (lookup l = find (root->options_var)) + if (lookup l = find (root.options_var)) append (cast<strings> (l)); - if (lookup l = find (root->arguments_var)) + if (lookup l = find (root.arguments_var)) append (cast<strings> (l)); // Keep redirects/cleanups out of $N. // size_t n (s.size ()); - if (lookup l = find (root->redirects_var)) + if (lookup l = find (root.redirects_var)) append (cast<strings> (l)); - if (lookup l = find (root->cleanups_var)) + if (lookup l = find (root.cleanups_var)) append (cast<strings> (l)); // Set the $N values if present. // for (size_t i (0); i <= 9; ++i) { - value& v (assign (*root->cmdN_var[i])); + value& v (assign (*root.cmdN_var[i])); if (i < n) { @@ -734,7 +734,7 @@ namespace build2 // Set $*. // - assign (root->cmd_var) = move (s); + assign (root.cmd_var) = move (s); } } } diff --git a/libbuild2/test/script/script.hxx b/libbuild2/test/script/script.hxx index e3f8251..8b34be8 100644 --- a/libbuild2/test/script/script.hxx +++ b/libbuild2/test/script/script.hxx @@ -343,7 +343,7 @@ namespace build2 { public: scope* const parent; // NULL for the root (script) scope. - script* const root; // Self for the root (script) scope. + script& root; // Self for the root (script) scope. // The chain of if-else scope alternatives. See also if_cond_ below. // @@ -424,7 +424,7 @@ namespace build2 ~scope () = default; protected: - scope (const string& id, scope* parent, script* root); + scope (const string& id, scope* parent, script& root); // Pre-parse data. // @@ -452,7 +452,7 @@ namespace build2 group (const string& id, group& p): scope (id, &p, p.root) {} protected: - group (const string& id, script* r): scope (id, nullptr, r) {} + group (const string& id, script& r): scope (id, nullptr, r) {} // Pre-parse data. // @@ -505,7 +505,13 @@ namespace build2 class script_base // Make sure certain things are initialized early. { protected: - script_base (); + script_base (const target& test_target, + const testscript& script_target); + + public: + const target& test_target; // Target we are testing. + const build2::scope& target_scope; // Base scope of test target. + const testscript& script_target; // Target of the testscript file. public: variable_pool var_pool; @@ -535,11 +541,6 @@ namespace build2 script& operator= (script&&) = delete; script& operator= (const script&) = delete; - public: - const target& test_target; // Target we are testing. - const build2::scope& target_scope; // Base scope of test target. - const testscript& script_target; // Target of the testscript file. - // Pre-parse data. // private: diff --git a/libbuild2/variable.cxx b/libbuild2/variable.cxx index c921bbd..84c1fb3 100644 --- a/libbuild2/variable.cxx +++ b/libbuild2/variable.cxx @@ -1380,12 +1380,10 @@ namespace build2 } } - variable_pool variable_pool::instance (true); - const variable_pool& variable_pool::cinstance = variable_pool::instance; - const variable_pool& var_pool = variable_pool::cinstance; - // variable_map // + const variable_map empty_variable_map (nullptr /* context */); + auto variable_map:: find (const variable& var, bool typed) const -> pair<const value_data*, const variable&> diff --git a/libbuild2/variable.hxx b/libbuild2/variable.hxx index 9a106b5..d153cb0 100644 --- a/libbuild2/variable.hxx +++ b/libbuild2/variable.hxx @@ -16,6 +16,7 @@ #include <libbuild2/types.hxx> #include <libbuild2/utility.hxx> +#include <libbuild2/context.hxx> #include <libbuild2/target-type.hxx> #include <libbuild2/export.hxx> @@ -1018,7 +1019,7 @@ namespace build2 // Variable pool. // - // The global version is protected by the phase mutex. + // The global (as in, context-wide) version is protected by the phase mutex. // class variable_pool { @@ -1175,9 +1176,12 @@ namespace build2 variable_pool& rw (scope&) const {return const_cast<variable_pool&> (*this);} - private: - LIBBUILD2_SYMEXPORT static variable_pool instance; + // Entities that can access bypassing the lock proof. + // + friend class parser; + friend class scope; + private: LIBBUILD2_SYMEXPORT variable& insert (string name, const value_type*, @@ -1191,17 +1195,6 @@ namespace build2 const variable_visibility* = nullptr, const bool* = nullptr) const; - // Entities that can access bypassing the lock proof. - // - friend class parser; - friend class scope; - friend LIBBUILD2_SYMEXPORT variable_overrides reset (const strings&); - - public: - // For var_pool initialization. - // - LIBBUILD2_SYMEXPORT static const variable_pool& cinstance; - // Variable map. // private: @@ -1261,13 +1254,13 @@ namespace build2 // Global pool flag. // private: + friend class context; + explicit variable_pool (bool global): global_ (global) {} bool global_; }; - - LIBBUILD2_SYMEXPORT extern const variable_pool& var_pool; } // variable_map @@ -1361,7 +1354,9 @@ namespace build2 lookup operator[] (const string& name) const { - const variable* var (var_pool.find (name)); + const variable* var (ctx != nullptr + ? ctx->var_pool.find (name) + : nullptr); return var != nullptr ? operator[] (*var) : lookup (); } @@ -1402,7 +1397,7 @@ namespace build2 // Note that the variable is expected to have already been registered. // value& - assign (const string& name) {return insert (var_pool[name]).first;} + assign (const string& name) {return insert (ctx->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 @@ -1436,11 +1431,18 @@ namespace build2 // (e.g., scopes, etc). // explicit - variable_map (bool global = false): global_ (global) {} + variable_map (context& c, bool global = false) + : ctx (&c), global_ (global) {} void clear () {m_.clear ();} + // Implementation details. + // + public: + explicit + variable_map (context* c): ctx (c) {} + private: friend class variable_type_map; @@ -1448,10 +1450,13 @@ namespace build2 typify (const value_data&, const variable&) const; private: - bool global_; + context* ctx; map_type m_; + bool global_; }; + extern const variable_map empty_variable_map; + // Value caching. Used for overrides as well as target type/pattern-specific // append/prepend. // @@ -1528,13 +1533,13 @@ namespace build2 using const_iterator = map_type::const_iterator; using const_reverse_iterator = map_type::const_reverse_iterator; - explicit - variable_pattern_map (bool global): global_ (global) {} + variable_pattern_map (context& c, bool global) + : ctx (c), global_ (global) {} variable_map& operator[] (const string& v) { - return map_.emplace (v, variable_map (global_)).first->second; + return map_.emplace (v, variable_map (ctx, global_)).first->second; } const_iterator begin () const {return map_.begin ();} @@ -1544,8 +1549,10 @@ namespace build2 bool empty () const {return map_.empty ();} private: - bool global_; + context& ctx; map_type map_; + bool global_; + }; class LIBBUILD2_SYMEXPORT variable_type_map @@ -1555,13 +1562,13 @@ namespace build2 variable_pattern_map>; using const_iterator = map_type::const_iterator; - explicit - variable_type_map (bool global): global_ (global) {} + variable_type_map (context& c, bool global): ctx (c), global_ (global) {} variable_pattern_map& operator[] (const target_type& t) { - return map_.emplace (t, variable_pattern_map (global_)).first->second; + return map_.emplace ( + t, variable_pattern_map (ctx, global_)).first->second; } const_iterator begin () const {return map_.begin ();} @@ -1585,8 +1592,10 @@ namespace build2 cache; private: - bool global_; + context& ctx; map_type map_; + bool global_; + }; } diff --git a/libbuild2/version/init.cxx b/libbuild2/version/init.cxx index a4e41d6..faa6e38 100644 --- a/libbuild2/version/init.cxx +++ b/libbuild2/version/init.cxx @@ -219,7 +219,7 @@ namespace build2 // Set all the version.* variables. // - auto& vp (var_pool.rw (rs)); + auto& vp (rs.ctx.var_pool.rw (rs)); auto set = [&vp, &rs] (const char* var, auto val) { |