aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2019-08-22 14:38:57 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2019-08-22 16:16:32 +0200
commit4f5b6cb7ed4e05e98cce7e692462f49e24b7a39a (patch)
tree4184fa33e116ec74747feec0c15e30219c7d087b
parent739f68b9e45c925ccc5a28b9b796030272575e2b (diff)
Targets, scopes, vars
-rw-r--r--build2/b.cxx52
-rw-r--r--build2/bin/init.cxx12
-rw-r--r--build2/bin/target.cxx36
-rw-r--r--build2/c/init.cxx2
-rw-r--r--build2/cc/common.cxx43
-rw-r--r--build2/cc/common.hxx4
-rw-r--r--build2/cc/compile-rule.cxx56
-rw-r--r--build2/cc/init.cxx5
-rw-r--r--build2/cc/link-rule.cxx13
-rw-r--r--build2/cc/module.cxx16
-rw-r--r--build2/cc/msvc.cxx9
-rw-r--r--build2/cc/pkgconfig.cxx4
-rw-r--r--build2/cc/utility.cxx2
-rw-r--r--build2/cli/init.cxx2
-rw-r--r--build2/cli/rule.cxx4
-rw-r--r--build2/cli/target.cxx11
-rw-r--r--build2/cxx/init.cxx7
-rw-r--r--libbuild2/algorithm.cxx38
-rw-r--r--libbuild2/algorithm.hxx8
-rw-r--r--libbuild2/algorithm.ixx4
-rw-r--r--libbuild2/bash/rule.cxx16
-rw-r--r--libbuild2/config/init.cxx6
-rw-r--r--libbuild2/config/operation.cxx182
-rw-r--r--libbuild2/config/operation.hxx2
-rw-r--r--libbuild2/config/utility.cxx6
-rw-r--r--libbuild2/config/utility.hxx6
-rw-r--r--libbuild2/context.cxx785
-rw-r--r--libbuild2/context.hxx78
-rw-r--r--libbuild2/dist/init.cxx2
-rw-r--r--libbuild2/dist/operation.cxx22
-rw-r--r--libbuild2/dump.cxx14
-rw-r--r--libbuild2/dump.hxx3
-rw-r--r--libbuild2/file.cxx98
-rw-r--r--libbuild2/file.hxx12
-rw-r--r--libbuild2/file.ixx10
-rw-r--r--libbuild2/function.test.cxx7
-rw-r--r--libbuild2/in/init.cxx2
-rw-r--r--libbuild2/install/init.cxx9
-rw-r--r--libbuild2/install/rule.cxx4
-rw-r--r--libbuild2/install/utility.hxx4
-rw-r--r--libbuild2/module.cxx4
-rw-r--r--libbuild2/module.hxx4
-rw-r--r--libbuild2/operation.cxx6
-rw-r--r--libbuild2/operation.hxx7
-rw-r--r--libbuild2/parser.cxx64
-rw-r--r--libbuild2/parser.hxx6
-rw-r--r--libbuild2/prerequisite.cxx2
-rw-r--r--libbuild2/prerequisite.hxx6
-rw-r--r--libbuild2/scope.cxx26
-rw-r--r--libbuild2/scope.hxx44
-rw-r--r--libbuild2/search.cxx25
-rw-r--r--libbuild2/search.hxx10
-rw-r--r--libbuild2/target-key.hxx2
-rw-r--r--libbuild2/target-type.hxx8
-rw-r--r--libbuild2/target.cxx57
-rw-r--r--libbuild2/target.hxx41
-rw-r--r--libbuild2/target.ixx2
-rw-r--r--libbuild2/target.txx16
-rw-r--r--libbuild2/test/init.cxx4
-rw-r--r--libbuild2/test/rule.cxx2
-rw-r--r--libbuild2/test/script/builtin.cxx6
-rw-r--r--libbuild2/test/script/parser.cxx2
-rw-r--r--libbuild2/test/script/parser.hxx4
-rw-r--r--libbuild2/test/script/parser.test.cxx30
-rw-r--r--libbuild2/test/script/runner.cxx28
-rw-r--r--libbuild2/test/script/script.cxx46
-rw-r--r--libbuild2/test/script/script.hxx19
-rw-r--r--libbuild2/variable.cxx6
-rw-r--r--libbuild2/variable.hxx65
-rw-r--r--libbuild2/version/init.cxx2
-rw-r--r--tests/libbuild2/driver.cxx6
71 files changed, 1144 insertions, 1002 deletions
diff --git a/build2/b.cxx b/build2/b.cxx
index f359dbc..6a49cb7 100644
--- a/build2/b.cxx
+++ b/build2/b.cxx
@@ -590,6 +590,8 @@ main (int argc, char* argv[])
? optional<size_t> (ops.max_stack () * 1024)
: nullopt));
+ // @@ CTX: should these be per-context?
+ //
variable_cache_mutex_shard_size = sched.shard_size ();
variable_cache_mutex_shard.reset (
new shared_mutex[variable_cache_mutex_shard_size]);
@@ -606,10 +608,11 @@ main (int argc, char* argv[])
trace << "jobs: " << jobs;
}
- // Set the build state before parsing the buildspec since it relies on
- // global scope being setup.
+ // Set the build context before parsing the buildspec since it relies on
+ // the global scope being setup. We reset it for every meta-operation (see
+ // below).
//
- variable_overrides var_ovs (reset (cmd_vars));
+ unique_ptr<context> ctx (new context (sched, cmd_vars));
// Parse the buildspec.
//
@@ -619,7 +622,7 @@ main (int argc, char* argv[])
istringstream is (args);
is.exceptions (istringstream::failbit | istringstream::badbit);
- parser p;
+ parser p (*ctx);
bspec = p.parse_buildspec (is, path ("<buildspec>"));
}
catch (const io_error&)
@@ -745,13 +748,14 @@ main (int argc, char* argv[])
else
opspecs.assign (lifted, 1);
- // Reset the build state for each meta-operation since there is no
+ // Reset the build context for each meta-operation since there is no
// guarantee their assumptions (e.g., in the load callback) are
// compatible.
//
if (dirty)
{
- var_ovs = reset (cmd_vars);
+ ctx = nullptr;
+ ctx.reset (new context (sched, cmd_vars));
dirty = false;
}
@@ -778,15 +782,16 @@ main (int argc, char* argv[])
// Can modify params, opspec, change meta-operation name.
//
if (auto f = meta_operation_table[m].process)
- mname = current_mname =
- f (var_ovs, mparams, opspecs, lifted != nullptr, l);
+ mname = current_mname = f (
+ *ctx, mparams, opspecs, lifted != nullptr, l);
}
}
// Expose early so can be used during bootstrap (with the same
// limitations as for pre-processing).
//
- global_scope->rw ().assign (var_build_meta_operation) = mname;
+ scope& gs (ctx->global_scope.rw ());
+ gs.assign (var_build_meta_operation) = mname;
for (auto oit (opspecs.begin ()); oit != opspecs.end (); ++oit)
{
@@ -978,7 +983,7 @@ main (int argc, char* argv[])
// Handle a forwarded configuration. Note that if we've changed
// out_root then we also have to remap out_base.
//
- out_root = bootstrap_fwd (src_root, altn);
+ out_root = bootstrap_fwd (*ctx, src_root, altn);
if (src_root != out_root)
{
out_base = out_root / out_base.leaf (src_root);
@@ -1023,8 +1028,7 @@ main (int argc, char* argv[])
// use to the bootstrap files (other than src-root.build, which,
// BTW, doesn't need to exist if src_root == out_root).
//
- scope& rs (
- create_root (*scope::global_, out_root, src_root)->second);
+ scope& rs (create_root (gs, out_root, src_root)->second);
bool bstrapped (bootstrapped (rs));
@@ -1238,7 +1242,7 @@ main (int argc, char* argv[])
fail (l) << "unexpected parameters for meta-operation "
<< mif->name;
- set_current_mif (*mif);
+ ctx->current_mif (*mif);
dirty = true;
}
@@ -1427,9 +1431,9 @@ main (int argc, char* argv[])
// So we split it into two passes.
//
{
- auto& sm (scope_map::instance);
+ auto& sm (ctx->scopes.rw ());
- for (const variable_override& o: var_ovs)
+ for (const variable_override& o: ctx->var_overrides)
{
if (o.ovr.visibility != variable_visibility::normal)
continue;
@@ -1450,7 +1454,7 @@ main (int argc, char* argv[])
v = o.val;
}
- for (const variable_override& o: var_ovs)
+ for (const variable_override& o: ctx->var_overrides)
{
// Ours is either project (%foo) or scope (/foo).
//
@@ -1504,7 +1508,7 @@ main (int argc, char* argv[])
// building before we know how to for all the targets in this
// operation batch.
//
- const scope& bs (scopes.find (ts.out_base));
+ const scope& bs (ctx->scopes.find (ts.out_base));
// Find the target type and extract the extension.
//
@@ -1546,7 +1550,7 @@ main (int argc, char* argv[])
} // target
if (dump_load)
- dump ();
+ dump (*ctx);
// Finally, match the rules and perform the operation.
//
@@ -1558,7 +1562,7 @@ main (int argc, char* argv[])
if (mif->operation_pre != nullptr)
mif->operation_pre (mparams, pre_oid); // Cannot be translated.
- set_current_oif (*pre_oif, oif);
+ ctx->current_oif (*pre_oif, oif);
action a (mid, pre_oid, oid);
@@ -1570,7 +1574,7 @@ main (int argc, char* argv[])
mif->match (mparams, a, tgs, diag, true /* progress */);
if (dump_match)
- dump (a);
+ dump (*ctx, a);
if (mif->execute != nullptr && !ops.match_only ())
mif->execute (mparams, a, tgs, diag, true /* progress */);
@@ -1585,7 +1589,7 @@ main (int argc, char* argv[])
tgs.reset ();
}
- set_current_oif (*oif, outer_oif);
+ ctx->current_oif (*oif, outer_oif);
action a (mid, oid, oif->outer_id);
@@ -1597,7 +1601,7 @@ main (int argc, char* argv[])
mif->match (mparams, a, tgs, diag, true /* progress */);
if (dump_match)
- dump (a);
+ dump (*ctx, a);
if (mif->execute != nullptr && !ops.match_only ())
mif->execute (mparams, a, tgs, diag, true /* progress */);
@@ -1613,7 +1617,7 @@ main (int argc, char* argv[])
if (mif->operation_pre != nullptr)
mif->operation_pre (mparams, post_oid); // Cannot be translated.
- set_current_oif (*post_oif, oif);
+ ctx->current_oif (*post_oif, oif);
action a (mid, post_oid, oid);
@@ -1625,7 +1629,7 @@ main (int argc, char* argv[])
mif->match (mparams, a, tgs, diag, true /* progress */);
if (dump_match)
- dump (a);
+ dump (*ctx, a);
if (mif->execute != nullptr && !ops.match_only ())
mif->execute (mparams, a, tgs, diag, true /* progress */);
diff --git a/build2/bin/init.cxx b/build2/bin/init.cxx
index b46d643..4e80834 100644
--- a/build2/bin/init.cxx
+++ b/build2/bin/init.cxx
@@ -56,7 +56,7 @@ namespace build2
// Target is a string and not target_triplet because it can be
// specified by the user.
//
- auto& vp (var_pool.rw (rs));
+ auto& vp (rs.ctx.var_pool.rw (rs));
vp.insert<string> ("config.bin.target", true);
vp.insert<string> ("config.bin.pattern", true);
@@ -266,7 +266,7 @@ namespace build2
// config.bin.target
//
{
- const variable& var (var_pool["config.bin.target"]);
+ const variable& var (rs.ctx.var_pool["config.bin.target"]);
// We first see if the value was specified via the configuration
// mechanism.
@@ -343,7 +343,7 @@ namespace build2
// config.bin.pattern
//
{
- const variable& var (var_pool["config.bin.pattern"]);
+ const variable& var (rs.ctx.var_pool["config.bin.pattern"]);
// We first see if the value was specified via the configuration
// mechanism.
@@ -568,7 +568,7 @@ namespace build2
//
if (first)
{
- auto& v (var_pool.rw (rs));
+ auto& v (rs.ctx.var_pool.rw (rs));
v.insert<process_path> ("bin.ar.path");
v.insert<process_path> ("bin.ranlib.path");
@@ -744,7 +744,7 @@ namespace build2
//
if (first)
{
- auto& v (var_pool.rw (rs));
+ auto& v (rs.ctx.var_pool.rw (rs));
v.insert<process_path> ("bin.ld.path");
v.insert<path> ("config.bin.ld", true);
@@ -857,7 +857,7 @@ namespace build2
//
if (first)
{
- auto& v (var_pool.rw (rs));
+ auto& v (rs.ctx.var_pool.rw (rs));
v.insert<process_path> ("bin.rc.path");
v.insert<path> ("config.bin.rc", true);
diff --git a/build2/bin/target.cxx b/build2/bin/target.cxx
index ec4a0ef..2ef3d87 100644
--- a/build2/bin/target.cxx
+++ b/build2/bin/target.cxx
@@ -88,11 +88,12 @@ namespace build2
//
template <typename M, typename G>
static target*
- m_factory (const target_type&, dir_path dir, dir_path out, string n)
+ m_factory (context& ctx,
+ const target_type&, dir_path dir, dir_path out, string n)
{
- const G* g (targets.find<G> (dir, out, n));
+ const G* g (ctx.targets.find<G> (dir, out, n));
- M* m (new M (move (dir), move (out), move (n)));
+ M* m (new M (ctx, move (dir), move (out), move (n)));
m->group = g;
return m;
@@ -258,21 +259,22 @@ namespace build2
//
template <typename G, typename E, typename A, typename S>
static target*
- g_factory (const target_type&, dir_path dir, dir_path out, string n)
+ g_factory (context& ctx,
+ const target_type&, dir_path dir, dir_path out, string n)
{
// Casts are MT-aware (during serial load).
//
E* e (phase == run_phase::load
- ? const_cast<E*> (targets.find<E> (dir, out, n))
+ ? const_cast<E*> (ctx.targets.find<E> (dir, out, n))
: nullptr);
A* a (phase == run_phase::load
- ? const_cast<A*> (targets.find<A> (dir, out, n))
+ ? const_cast<A*> (ctx.targets.find<A> (dir, out, n))
: nullptr);
S* s (phase == run_phase::load
- ? const_cast<S*> (targets.find<S> (dir, out, n))
+ ? const_cast<S*> (ctx.targets.find<S> (dir, out, n))
: nullptr);
- G* g (new G (move (dir), move (out), move (n)));
+ G* g (new G (ctx, move (dir), move (out), move (n)));
if (e != nullptr) e->group = g;
if (a != nullptr) a->group = g;
@@ -323,16 +325,17 @@ namespace build2
// The same as g_factory() but without E.
//
static target*
- libul_factory (const target_type&, dir_path dir, dir_path out, string n)
+ libul_factory (context& ctx,
+ const target_type&, dir_path dir, dir_path out, string n)
{
libua* a (phase == run_phase::load
- ? const_cast<libua*> (targets.find<libua> (dir, out, n))
+ ? const_cast<libua*> (ctx.targets.find<libua> (dir, out, n))
: nullptr);
libus* s (phase == run_phase::load
- ? const_cast<libus*> (targets.find<libus> (dir, out, n))
+ ? const_cast<libus*> (ctx.targets.find<libus> (dir, out, n))
: nullptr);
- libul* g (new libul (move (dir), move (out), move (n)));
+ libul* g (new libul (ctx, move (dir), move (out), move (n)));
if (a != nullptr) a->group = g;
if (s != nullptr) s->group = g;
@@ -403,18 +406,19 @@ namespace build2
}
static target*
- lib_factory (const target_type&, dir_path dir, dir_path out, string n)
+ lib_factory (context& ctx,
+ const target_type&, dir_path dir, dir_path out, string n)
{
// Casts are MT-aware (during serial load).
//
liba* a (phase == run_phase::load
- ? const_cast<liba*> (targets.find<liba> (dir, out, n))
+ ? const_cast<liba*> (ctx.targets.find<liba> (dir, out, n))
: nullptr);
libs* s (phase == run_phase::load
- ? const_cast<libs*> (targets.find<libs> (dir, out, n))
+ ? const_cast<libs*> (ctx.targets.find<libs> (dir, out, n))
: nullptr);
- lib* l (new lib (move (dir), move (out), move (n)));
+ lib* l (new lib (ctx, move (dir), move (out), move (n)));
if (a != nullptr) a->group = l;
if (s != nullptr) s->group = l;
diff --git a/build2/c/init.cxx b/build2/c/init.cxx
index 5c55281..ff9cc58 100644
--- a/build2/c/init.cxx
+++ b/build2/c/init.cxx
@@ -145,7 +145,7 @@ namespace build2
// Enter all the variables and initialize the module data.
//
- auto& v (var_pool.rw (rs));
+ auto& v (rs.ctx.var_pool.rw (rs));
cc::config_data d {
cc::lang::c,
diff --git a/build2/cc/common.cxx b/build2/cc/common.cxx
index aa0eb89..4f5db4c 100644
--- a/build2/cc/common.cxx
+++ b/build2/cc/common.cxx
@@ -87,7 +87,7 @@ namespace build2
bool impl (proc_impl && proc_impl (l, la));
bool cc (false), same (false);
- auto& vp (var_pool);
+ auto& vp (top_bs.ctx.var_pool);
lookup c_e_libs;
lookup x_e_libs;
@@ -221,7 +221,7 @@ namespace build2
: &cast<dir_paths> (
bs.root_scope ()->vars[same
? x_sys_lib_dirs
- : var_pool[*t + ".sys_lib_dirs"]]);
+ : bs.ctx.var_pool[*t + ".sys_lib_dirs"]]);
};
auto find_linfo = [top_li, t, cc, &bs, &l, &li] ()
@@ -474,7 +474,7 @@ namespace build2
if (xt == nullptr)
{
if (n.qualified ())
- xt = import_existing (pk);
+ xt = import_existing (s.ctx, pk);
}
if (xt == nullptr)
@@ -494,20 +494,21 @@ namespace build2
//
template <typename T>
ulock common::
- insert_library (T*& r,
+ insert_library (context& ctx,
+ T*& r,
const string& name,
const dir_path& d,
optional<string> ext,
bool exist,
tracer& trace)
{
- auto p (targets.insert_locked (T::static_type,
- d,
- dir_path (),
- name,
- move (ext),
- true, // Implied.
- trace));
+ auto p (ctx.targets.insert_locked (T::static_type,
+ d,
+ dir_path (),
+ name,
+ move (ext),
+ true, // Implied.
+ trace));
assert (!exist || !p.second.owns_lock ());
r = &p.first.template as<T> ();
@@ -626,6 +627,8 @@ namespace build2
&name, ext,
&p, &f, exist, &trace, this] (const dir_path& d) -> bool
{
+ context& ctx (p.scope->ctx);
+
timestamp mt;
// libs
@@ -648,9 +651,10 @@ namespace build2
if (tclass == "windows")
{
libi* i (nullptr);
- insert_library (i, name, d, se, exist, trace);
+ insert_library (ctx, i, name, d, se, exist, trace);
- ulock l (insert_library (s, name, d, nullopt, exist, trace));
+ ulock l (
+ insert_library (ctx, s, name, d, nullopt, exist, trace));
if (!exist)
{
@@ -677,7 +681,7 @@ namespace build2
}
else
{
- insert_library (s, name, d, se, exist, trace);
+ insert_library (ctx, s, name, d, se, exist, trace);
s->mtime (mt);
s->path (move (f));
@@ -697,7 +701,7 @@ namespace build2
if (mt != timestamp_nonexistent)
{
- insert_library (s, name, d, se, exist, trace);
+ insert_library (ctx, s, name, d, se, exist, trace);
s->mtime (mt);
s->path (move (f));
@@ -722,7 +726,7 @@ namespace build2
// Note that this target is outside any project which we treat
// as out trees.
//
- insert_library (a, name, d, ae, exist, trace);
+ insert_library (ctx, a, name, d, ae, exist, trace);
a->mtime (mt);
a->path (move (f));
}
@@ -760,14 +764,14 @@ namespace build2
if (na && !r.first.empty ())
{
- insert_library (a, name, d, nullopt, exist, trace);
+ insert_library (ctx, a, name, d, nullopt, exist, trace);
a->mtime (timestamp_unreal);
a->path (empty_path);
}
if (ns && !r.second.empty ())
{
- insert_library (s, name, d, nullopt, exist, trace);
+ insert_library (ctx, s, name, d, nullopt, exist, trace);
s->mtime (timestamp_unreal);
s->path (empty_path);
}
@@ -821,7 +825,8 @@ namespace build2
// Enter (or find) the lib{} target group.
//
lib* lt;
- insert_library (lt, name, *pd, l ? p.tk.ext : nullopt, exist, trace);
+ insert_library (
+ p.scope->ctx, lt, name, *pd, l ? p.tk.ext : nullopt, exist, trace);
// Result.
//
diff --git a/build2/cc/common.hxx b/build2/cc/common.hxx
index c58a7f3..b24eb7d 100644
--- a/build2/cc/common.hxx
+++ b/build2/cc/common.hxx
@@ -8,6 +8,7 @@
#include <libbuild2/types.hxx>
#include <libbuild2/utility.hxx>
+#include <libbuild2/context.hxx>
#include <libbuild2/variable.hxx>
#include <build2/bin/target.hxx>
@@ -278,7 +279,8 @@ namespace build2
template <typename T>
static ulock
- insert_library (T*&,
+ insert_library (context&,
+ T*&,
const string&,
const dir_path&,
optional<string>,
diff --git a/build2/cc/compile-rule.cxx b/build2/cc/compile-rule.cxx
index 833fd44..9dede21 100644
--- a/build2/cc/compile-rule.cxx
+++ b/build2/cc/compile-rule.cxx
@@ -365,7 +365,9 @@ namespace build2
const variable& var (
com
? c_export_poptions
- : (t == x ? x_export_poptions : var_pool[t + ".export.poptions"]));
+ : (t == x
+ ? x_export_poptions
+ : l.ctx.var_pool[t + ".export.poptions"]));
append_options (args, l, var);
};
@@ -418,7 +420,9 @@ namespace build2
const variable& var (
com
? c_export_poptions
- : (t == x ? x_export_poptions : var_pool[t + ".export.poptions"]));
+ : (t == x
+ ? x_export_poptions
+ : l.ctx.var_pool[t + ".export.poptions"]));
hash_options (cs, l, var);
};
@@ -472,7 +476,9 @@ namespace build2
const variable& var (
com
? c_export_poptions
- : (t == x ? x_export_poptions : var_pool[t + ".export.poptions"]));
+ : (t == x
+ ? x_export_poptions
+ : l.ctx.var_pool[t + ".export.poptions"]));
append_prefixes (m, l, var);
};
@@ -2161,7 +2167,7 @@ namespace build2
//
small_vector<const target_type*, 2> tts;
- const scope& bs (scopes.find (d));
+ const scope& bs (t.ctx.scopes.find (d));
if (const scope* rs = bs.root_scope ())
{
tts = map_extension (bs, n, e);
@@ -2201,7 +2207,7 @@ namespace build2
// absolute path with a spelled-out extension to multiple targets.
//
for (const target_type* tt: tts)
- if ((r = targets.find (*tt, d, out, n, e, trace)) != nullptr)
+ if ((r = t.ctx.targets.find (*tt, d, out, n, e, trace)) != nullptr)
break;
// Note: we can't do this because of the in-source builds where
@@ -2887,7 +2893,7 @@ namespace build2
// See if this path is inside a project with an out-of-
// tree build and is in the out directory tree.
//
- const scope& bs (scopes.find (d));
+ const scope& bs (t.ctx.scopes.find (d));
if (bs.root_scope () != nullptr)
{
const dir_path& bp (bs.out_path ());
@@ -5101,7 +5107,7 @@ namespace build2
modules_sidebuild_dir /=
x);
- const scope* ps (&scopes.find (pd));
+ const scope* ps (&rs.ctx.scopes.find (pd));
if (ps->out_path () != pd)
{
@@ -5112,7 +5118,7 @@ namespace build2
// Re-test again now that we are in exclusive phase (another thread
// could have already created and loaded the subproject).
//
- ps = &scopes.find (pd);
+ ps = &rs.ctx.scopes.find (pd);
if (ps->out_path () != pd)
{
@@ -5200,7 +5206,7 @@ namespace build2
// exists then we assume all this is already done (otherwise why would
// someone have created such a target).
//
- if (const file* bt = targets.find<file> (
+ if (const file* bt = bs.ctx.targets.find<file> (
tt,
pd,
dir_path (), // Always in the out tree.
@@ -5237,13 +5243,14 @@ namespace build2
}
}
- auto p (targets.insert_locked (tt,
- move (pd),
- dir_path (), // Always in the out tree.
- move (mf),
- nullopt, // Use default extension.
- true, // Implied.
- trace));
+ auto p (bs.ctx.targets.insert_locked (
+ tt,
+ move (pd),
+ dir_path (), // Always in the out tree.
+ move (mf),
+ nullopt, // Use default extension.
+ true, // Implied.
+ trace));
file& bt (static_cast<file&> (p.first));
// Note that this is racy and someone might have created this target
@@ -5295,7 +5302,7 @@ namespace build2
const target_type& tt (compile_types (li.type).hbmi);
- if (const file* bt = targets.find<file> (
+ if (const file* bt = bs.ctx.targets.find<file> (
tt,
pd,
dir_path (), // Always in the out tree.
@@ -5307,13 +5314,14 @@ namespace build2
prerequisites ps;
ps.push_back (prerequisite (ht));
- auto p (targets.insert_locked (tt,
- move (pd),
- dir_path (), // Always in the out tree.
- move (mf),
- nullopt, // Use default extension.
- true, // Implied.
- trace));
+ auto p (bs.ctx.targets.insert_locked (
+ tt,
+ move (pd),
+ dir_path (), // Always in the out tree.
+ move (mf),
+ nullopt, // Use default extension.
+ true, // Implied.
+ trace));
file& bt (static_cast<file&> (p.first));
// Note that this is racy and someone might have created this target
diff --git a/build2/cc/init.cxx b/build2/cc/init.cxx
index e22ece7..3ff59a1 100644
--- a/build2/cc/init.cxx
+++ b/build2/cc/init.cxx
@@ -77,7 +77,7 @@ namespace build2
// Enter variables. Note: some overridable, some not.
//
- auto& v (var_pool.rw (rs));
+ auto& v (rs.ctx.var_pool.rw (rs));
auto v_t (variable_visibility::target);
@@ -276,7 +276,8 @@ namespace build2
// Prepare configuration hints. They are only used on the first load
// of bin.config so we only populate them on our first load.
//
- variable_map h;
+ variable_map h (rs.ctx);
+
if (first)
{
// Note that all these variables have already been registered.
diff --git a/build2/cc/link-rule.cxx b/build2/cc/link-rule.cxx
index bb91722..09109c2 100644
--- a/build2/cc/link-rule.cxx
+++ b/build2/cc/link-rule.cxx
@@ -165,14 +165,15 @@ namespace build2
// otherwise the above search would have returned the member
// target.
//
- pt = search_existing (p.prerequisite.key (tt));
+ pt = search_existing (t.ctx, p.prerequisite.key (tt));
}
}
else if (!p.is_a<libue> ())
{
// See if we also/instead have a group.
//
- pg = search_existing (p.prerequisite.key (libul::static_type));
+ pg = search_existing (t.ctx,
+ p.prerequisite.key (libul::static_type));
if (pt == nullptr)
swap (pt, pg);
@@ -1163,7 +1164,7 @@ namespace build2
bool u;
if ((u = pt->is_a<libux> ()) || pt->is_a<liba> ())
{
- const variable& var (var_pool["bin.whole"]); // @@ Cache.
+ const variable& var (t.ctx.var_pool["bin.whole"]); // @@ Cache.
// See the bin module for the lookup semantics discussion. Note
// that the variable is not overridable so we omit find_override()
@@ -1473,7 +1474,7 @@ namespace build2
? (exp ? c_export_loptions : c_loptions)
: (t == x
? (exp ? x_export_loptions : x_loptions)
- : var_pool[t + (exp ? ".export.loptions" : ".loptions")]));
+ : l.ctx.var_pool[t + (exp ? ".export.loptions" : ".loptions")]));
append_options (d.args, *g, var);
}
@@ -1575,7 +1576,7 @@ namespace build2
? (exp ? c_export_loptions : c_loptions)
: (t == x
? (exp ? x_export_loptions : x_loptions)
- : var_pool[t + (exp ? ".export.loptions" : ".loptions")]));
+ : l.ctx.var_pool[t + (exp ? ".export.loptions" : ".loptions")]));
hash_options (d.cs, *g, var);
}
@@ -1966,7 +1967,7 @@ namespace build2
const string& cs (
cast<string> (
rs[tsys == "win32-msvc"
- ? var_pool["bin.ld.checksum"]
+ ? t.ctx.var_pool["bin.ld.checksum"]
: x_checksum]));
if (dd.expect (cs) != nullptr)
diff --git a/build2/cc/module.cxx b/build2/cc/module.cxx
index 36cdd1a..064d954 100644
--- a/build2/cc/module.cxx
+++ b/build2/cc/module.cxx
@@ -41,9 +41,13 @@ namespace build2
config::save_module (rs, x, 250);
- const variable& config_c_poptions (var_pool["config.cc.poptions"]);
- const variable& config_c_coptions (var_pool["config.cc.coptions"]);
- const variable& config_c_loptions (var_pool["config.cc.loptions"]);
+ auto& vp (rs.ctx.var_pool.rw (rs));
+
+ // Must already exist.
+ //
+ const variable& config_c_poptions (vp["config.cc.poptions"]);
+ const variable& config_c_coptions (vp["config.cc.coptions"]);
+ const variable& config_c_loptions (vp["config.cc.loptions"]);
// config.x
//
@@ -63,8 +67,6 @@ namespace build2
//
if (!cc_loaded)
{
- auto& vp (var_pool.rw (rs));
-
for (const char* const* pm (x_hinters); *pm != nullptr; ++pm)
{
string m (*pm);
@@ -201,7 +203,7 @@ namespace build2
{
// Prepare configuration hints.
//
- variable_map h;
+ variable_map h (rs.ctx);
// Note that all these variables have already been registered.
//
@@ -545,7 +547,7 @@ namespace build2
//
if (!cast_false<bool> (rs["cc.core.config.loaded"]))
{
- variable_map h;
+ variable_map h (rs.ctx);
if (!ci.bin_pattern.empty ())
h.assign ("config.bin.pattern") = ci.bin_pattern;
diff --git a/build2/cc/msvc.cxx b/build2/cc/msvc.cxx
index 6125fdd..7d8c3f5 100644
--- a/build2/cc/msvc.cxx
+++ b/build2/cc/msvc.cxx
@@ -365,6 +365,7 @@ namespace build2
{
// Pretty similar logic to search_library().
//
+ assert (p.scope != nullptr);
const optional<string>& ext (p.tk.ext);
const string& name (*p.tk.name);
@@ -403,7 +404,7 @@ namespace build2
// Enter the target.
//
T* t;
- common::insert_library (t, name, d, e, exist, trace);
+ common::insert_library (p.scope->ctx, t, name, d, e, exist, trace);
t->mtime (mt);
t->path (move (f));
@@ -453,6 +454,8 @@ namespace build2
{
tracer trace (x, "msvc_search_shared");
+ assert (pk.scope != nullptr);
+
libs* s (nullptr);
auto search = [&s, &ld, &d, &pk, exist, &trace] (
@@ -461,7 +464,9 @@ namespace build2
if (libi* i = msvc_search_library<libi> (
ld, d, pk, otype::s, pf, sf, exist, trace))
{
- ulock l (insert_library (s, *pk.tk.name, d, nullopt, exist, trace));
+ ulock l (
+ insert_library (
+ pk.scope->ctx, s, *pk.tk.name, d, nullopt, exist, trace));
if (!exist)
{
diff --git a/build2/cc/pkgconfig.cxx b/build2/cc/pkgconfig.cxx
index c23b746..13cc752 100644
--- a/build2/cc/pkgconfig.cxx
+++ b/build2/cc/pkgconfig.cxx
@@ -1027,7 +1027,7 @@ namespace build2
// Parse modules and add them to the prerequisites.
//
- auto parse_modules = [&trace, &next, this]
+ auto parse_modules = [&trace, &next, &s, this]
(const pkgconf& pc, prerequisites& ps)
{
string mstr (pc.variable ("cxx_modules"));
@@ -1057,7 +1057,7 @@ namespace build2
// For now there are only C++ modules.
//
auto tl (
- targets.insert_locked (
+ s.ctx.targets.insert_locked (
*x_mod,
mp.directory (),
dir_path (),
diff --git a/build2/cc/utility.cxx b/build2/cc/utility.cxx
index 3271f7c..ff807c9 100644
--- a/build2/cc/utility.cxx
+++ b/build2/cc/utility.cxx
@@ -72,7 +72,7 @@ namespace build2
//
return phase == run_phase::match && !exist
? &search (x, tt, x.dir, x.out, x.name)
- : search_existing (tt, x.dir, x.out, x.name);
+ : search_existing (x.ctx, tt, x.dir, x.out, x.name);
}
else
{
diff --git a/build2/cli/init.cxx b/build2/cli/init.cxx
index 6d20aa0..2e553f7 100644
--- a/build2/cli/init.cxx
+++ b/build2/cli/init.cxx
@@ -43,7 +43,7 @@ namespace build2
//
if (first)
{
- auto& v (var_pool.rw (rs));
+ auto& v (rs.ctx.var_pool.rw (rs));
// Note: some overridable, some not.
//
diff --git a/build2/cli/rule.cxx b/build2/cli/rule.cxx
index f6bebee..f6c0763 100644
--- a/build2/cli/rule.cxx
+++ b/build2/cli/rule.cxx
@@ -112,7 +112,7 @@ namespace build2
// Check if there is a corresponding cli.cxx{} group.
//
- const cli_cxx* g (targets.find<cli_cxx> (t.dir, t.out, t.name));
+ const cli_cxx* g (t.ctx.targets.find<cli_cxx> (t.dir, t.out, t.name));
// If not or if it has no prerequisites (happens when we use it to
// set cli.options) and this target has a cli{} prerequisite, then
@@ -124,7 +124,7 @@ namespace build2
prerequisite_members (a, t)))
{
if (g == nullptr)
- g = &targets.insert<cli_cxx> (t.dir, t.out, t.name, trace);
+ g = &t.ctx.targets.insert<cli_cxx> (t.dir, t.out, t.name, trace);
g->prerequisites (prerequisites {p->as_prerequisite ()});
}
diff --git a/build2/cli/target.cxx b/build2/cli/target.cxx
index 096295a..caa12b0 100644
--- a/build2/cli/target.cxx
+++ b/build2/cli/target.cxx
@@ -45,7 +45,8 @@ namespace build2
}
static target*
- cli_cxx_factory (const target_type&, dir_path d, dir_path o, string n)
+ cli_cxx_factory (context& ctx,
+ const target_type&, dir_path d, dir_path o, string n)
{
tracer trace ("cli::cli_cxx_factory");
@@ -55,11 +56,11 @@ namespace build2
//
// Also required for the src-out remapping logic.
//
- targets.insert<cxx::hxx> (d, o, n, trace);
- targets.insert<cxx::cxx> (d, o, n, trace);
- targets.insert<cxx::ixx> (d, o, n, trace);
+ ctx.targets.insert<cxx::hxx> (d, o, n, trace);
+ ctx.targets.insert<cxx::cxx> (d, o, n, trace);
+ ctx.targets.insert<cxx::ixx> (d, o, n, trace);
- return new cli_cxx (move (d), move (o), move (n));
+ return new cli_cxx (ctx, move (d), move (o), move (n));
}
const target_type cli_cxx::static_type
diff --git a/build2/cxx/init.cxx b/build2/cxx/init.cxx
index c355763..1ffa098 100644
--- a/build2/cxx/init.cxx
+++ b/build2/cxx/init.cxx
@@ -62,7 +62,8 @@ namespace build2
//
auto enter = [&rs] (const char* v) -> const variable&
{
- return var_pool.rw (rs).insert<bool> (v, variable_visibility::project);
+ return rs.ctx.var_pool.rw (rs).insert<bool> (
+ v, variable_visibility::project);
};
// NOTE: see also module sidebuild subproject if changing anything about
@@ -370,7 +371,7 @@ namespace build2
// Enter all the variables and initialize the module data.
//
- auto& v (var_pool.rw (rs));
+ auto& v (rs.ctx.var_pool.rw (rs));
cc::config_data d {
cc::lang::cxx,
@@ -572,7 +573,7 @@ namespace build2
config_module& cm (*rs.lookup_module<config_module> ("cxx.guess"));
- auto& vp (var_pool.rw (rs));
+ auto& vp (rs.ctx.var_pool.rw (rs));
bool modules (cast<bool> (rs["cxx.features.modules"]));
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)
{
diff --git a/tests/libbuild2/driver.cxx b/tests/libbuild2/driver.cxx
index 706d276..8ae83d5 100644
--- a/tests/libbuild2/driver.cxx
+++ b/tests/libbuild2/driver.cxx
@@ -5,7 +5,7 @@
#include <libbuild2/types.hxx>
#include <libbuild2/utility.hxx>
-#include <libbuild2/context.hxx> // sched, reset()
+#include <libbuild2/context.hxx>
#include <libbuild2/scheduler.hxx>
#include <libbuild2/in/init.hxx>
@@ -26,8 +26,8 @@ main (int, char* argv[])
in::build2_in_load ();
version::build2_version_load ();
- sched.startup (1); // Serial execution.
- reset (strings ()); // No command line variables.
+ sched.startup (1); // Serial execution.
+ context ctx (sched);
return 0;
}