aboutsummaryrefslogtreecommitdiff
path: root/libbuild2
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2')
-rw-r--r--libbuild2/action.hxx14
-rw-r--r--libbuild2/algorithm.cxx318
-rw-r--r--libbuild2/algorithm.hxx27
-rw-r--r--libbuild2/algorithm.ixx52
-rw-r--r--libbuild2/bash/rule.cxx16
-rw-r--r--libbuild2/bash/target.cxx4
-rw-r--r--libbuild2/config/init.cxx10
-rw-r--r--libbuild2/config/operation.cxx202
-rw-r--r--libbuild2/config/operation.hxx2
-rw-r--r--libbuild2/config/utility.cxx18
-rw-r--r--libbuild2/config/utility.hxx6
-rw-r--r--libbuild2/config/utility.txx2
-rw-r--r--libbuild2/context.cxx810
-rw-r--r--libbuild2/context.hxx602
-rw-r--r--libbuild2/context.ixx20
-rw-r--r--libbuild2/diagnostics.cxx38
-rw-r--r--libbuild2/diagnostics.hxx7
-rw-r--r--libbuild2/dist/init.cxx2
-rw-r--r--libbuild2/dist/operation.cxx52
-rw-r--r--libbuild2/dump.cxx16
-rw-r--r--libbuild2/dump.hxx3
-rw-r--r--libbuild2/file.cxx193
-rw-r--r--libbuild2/file.hxx17
-rw-r--r--libbuild2/file.ixx14
-rw-r--r--libbuild2/filesystem.cxx28
-rw-r--r--libbuild2/filesystem.hxx55
-rw-r--r--libbuild2/filesystem.ixx40
-rw-r--r--libbuild2/filesystem.txx9
-rw-r--r--libbuild2/function.cxx63
-rw-r--r--libbuild2/function.hxx18
-rw-r--r--libbuild2/function.test.cxx12
-rw-r--r--libbuild2/functions-builtin.cxx4
-rw-r--r--libbuild2/functions-filesystem.cxx4
-rw-r--r--libbuild2/functions-name.cxx6
-rw-r--r--libbuild2/functions-path.cxx6
-rw-r--r--libbuild2/functions-process-path.cxx4
-rw-r--r--libbuild2/functions-process.cxx4
-rw-r--r--libbuild2/functions-project-name.cxx6
-rw-r--r--libbuild2/functions-regex.cxx4
-rw-r--r--libbuild2/functions-string.cxx6
-rw-r--r--libbuild2/functions-target-triplet.cxx6
-rw-r--r--libbuild2/in/init.cxx2
-rw-r--r--libbuild2/in/rule.cxx8
-rw-r--r--libbuild2/install/functions.cxx4
-rw-r--r--libbuild2/install/init.cxx17
-rw-r--r--libbuild2/install/operation.cxx2
-rw-r--r--libbuild2/install/rule.cxx24
-rw-r--r--libbuild2/install/utility.hxx4
-rw-r--r--libbuild2/module.cxx4
-rw-r--r--libbuild2/module.hxx4
-rw-r--r--libbuild2/operation.cxx103
-rw-r--r--libbuild2/operation.hxx18
-rw-r--r--libbuild2/parser.cxx70
-rw-r--r--libbuild2/parser.hxx6
-rw-r--r--libbuild2/prerequisite.cxx27
-rw-r--r--libbuild2/prerequisite.hxx38
-rw-r--r--libbuild2/prerequisite.ixx34
-rw-r--r--libbuild2/rule.cxx4
-rw-r--r--libbuild2/scope.cxx33
-rw-r--r--libbuild2/scope.hxx64
-rw-r--r--libbuild2/scope.ixx4
-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.cxx108
-rw-r--r--libbuild2/target.hxx93
-rw-r--r--libbuild2/target.ixx47
-rw-r--r--libbuild2/target.txx25
-rw-r--r--libbuild2/test/init.cxx4
-rw-r--r--libbuild2/test/operation.cxx2
-rw-r--r--libbuild2/test/rule.cxx78
-rw-r--r--libbuild2/test/script/builtin.cxx10
-rw-r--r--libbuild2/test/script/parser.cxx32
-rw-r--r--libbuild2/test/script/parser.hxx4
-rw-r--r--libbuild2/test/script/parser.test.cxx32
-rw-r--r--libbuild2/test/script/runner.cxx55
-rw-r--r--libbuild2/test/script/script.cxx46
-rw-r--r--libbuild2/test/script/script.hxx19
-rw-r--r--libbuild2/types.hxx1
-rw-r--r--libbuild2/utility.cxx8
-rw-r--r--libbuild2/utility.hxx7
-rw-r--r--libbuild2/variable.cxx12
-rw-r--r--libbuild2/variable.hxx88
-rw-r--r--libbuild2/variable.ixx2
-rw-r--r--libbuild2/version/init.cxx19
-rw-r--r--libbuild2/version/rule.cxx3
-rw-r--r--libbuild2/version/utility.cxx9
-rw-r--r--libbuild2/version/utility.hxx6
89 files changed, 2115 insertions, 1830 deletions
diff --git a/libbuild2/action.hxx b/libbuild2/action.hxx
index 9fa2a16..01c5307 100644
--- a/libbuild2/action.hxx
+++ b/libbuild2/action.hxx
@@ -116,10 +116,18 @@ namespace build2
template <typename T>
struct action_state
{
- T data[2]; // [0] -- inner, [1] -- outer.
+ T inner;
+ T outer;
- T& operator[] (action a) {return data[a.inner () ? 0 : 1];}
- const T& operator[] (action a) const {return data[a.inner () ? 0 : 1];}
+ T& operator[] (action a) {return a.inner () ? inner : outer;}
+ const T& operator[] (action a) const {return a.inner () ? inner : outer;}
+
+ action_state () = default;
+
+ template <typename... A>
+ explicit
+ action_state (A&&... a)
+ : inner (forward<A> (a)...), outer (forward<A> (a)...) {}
};
// Id constants for build-in and pre-defined meta/operations.
diff --git a/libbuild2/algorithm.cxx b/libbuild2/algorithm.cxx
index 50db5d3..7a616a5 100644
--- a/libbuild2/algorithm.cxx
+++ b/libbuild2/algorithm.cxx
@@ -22,7 +22,7 @@ namespace build2
const target&
search (const target& t, const prerequisite& p)
{
- assert (phase == run_phase::match);
+ assert (t.ctx.phase == run_phase::match);
const target* r (p.target.load (memory_order_consume));
@@ -35,13 +35,15 @@ namespace build2
const target*
search_existing (const prerequisite& p)
{
- assert (phase == run_phase::match || phase == run_phase::execute);
+ context& ctx (p.scope.ctx);
+
+ assert (ctx.phase == run_phase::match || ctx.phase == run_phase::execute);
const target* r (p.target.load (memory_order_consume));
if (r == nullptr)
{
- r = search_existing (p.key ());
+ r = search_existing (ctx, p.key ());
if (r != nullptr)
search_custom (p, *r);
@@ -53,32 +55,34 @@ namespace build2
const target&
search (const target& t, const prerequisite_key& pk)
{
- assert (phase == run_phase::match);
+ assert (t.ctx.phase == run_phase::match);
// If this is a project-qualified prerequisite, then this is import's
// 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);
+ assert (ctx.phase == run_phase::match || ctx.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&
search (const target& t, name n, const scope& s)
{
- assert (phase == run_phase::match);
+ assert (t.ctx.phase == run_phase::match);
auto rp (s.find_target_type (n, location ()));
const target_type* tt (rp.first);
@@ -106,7 +110,8 @@ namespace build2
const target*
search_existing (const name& cn, const scope& s, const dir_path& out)
{
- assert (phase == run_phase::match || phase == run_phase::execute);
+ assert (s.ctx.phase == run_phase::match ||
+ s.ctx.phase == run_phase::execute);
name n (cn);
auto rp (s.find_target_type (n, location ()));
@@ -130,7 +135,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
@@ -162,12 +169,14 @@ namespace build2
target_lock
lock_impl (action a, const target& ct, optional<scheduler::work_queue> wq)
{
- assert (phase == run_phase::match);
+ context& ctx (ct.ctx);
+
+ assert (ctx.phase == run_phase::match);
// Most likely the target's state is (count_touched - 1), that is, 0 or
// previously executed, so let's start with that.
//
- size_t b (target::count_base ());
+ size_t b (ctx.count_base ());
size_t e (b + target::offset_touched - 1);
size_t appl (b + target::offset_applied);
@@ -202,8 +211,8 @@ namespace build2
// to switch the phase to load. Which would result in a deadlock
// unless we release the phase.
//
- phase_unlock ul;
- e = sched.wait (busy - 1, task_count, *wq);
+ phase_unlock ul (ct.ctx);
+ e = ctx.sched.wait (busy - 1, task_count, *wq);
}
// We don't lock already applied or executed targets.
@@ -241,15 +250,17 @@ namespace build2
void
unlock_impl (action a, target& t, size_t offset)
{
- assert (phase == run_phase::match);
+ context& ctx (t.ctx);
+
+ assert (ctx.phase == run_phase::match);
atomic_count& task_count (t[a].task_count);
// Set the task count and wake up any threads that might be waiting for
// this target.
//
- task_count.store (offset + target::count_base (), memory_order_release);
- sched.resume (task_count);
+ task_count.store (offset + ctx.count_base (), memory_order_release);
+ ctx.sched.resume (task_count);
}
target&
@@ -266,13 +277,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 +314,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]);
@@ -625,32 +636,33 @@ namespace build2
// Also pass our diagnostics and lock stacks (this is safe since we
// expect the caller to wait for completion before unwinding its stack).
//
- if (sched.async (start_count,
- *task_count,
- [a, try_match] (const diag_frame* ds,
- const target_lock* ls,
- target& t, size_t offset)
- {
- // Switch to caller's diag and lock stacks.
- //
- diag_frame::stack_guard dsg (ds);
- target_lock::stack_guard lsg (ls);
-
- try
- {
- phase_lock pl (run_phase::match); // Can throw.
- {
- target_lock l {a, &t, offset}; // Reassemble.
- match_impl (l, false /* step */, try_match);
- // Unlock within the match phase.
- }
- }
- catch (const failed&) {} // Phase lock failure.
- },
- diag_frame::stack (),
- target_lock::stack (),
- ref (*ld.target),
- ld.offset))
+ if (ct.ctx.sched.async (
+ start_count,
+ *task_count,
+ [a, try_match] (const diag_frame* ds,
+ const target_lock* ls,
+ target& t, size_t offset)
+ {
+ // Switch to caller's diag and lock stacks.
+ //
+ diag_frame::stack_guard dsg (ds);
+ target_lock::stack_guard lsg (ls);
+
+ try
+ {
+ phase_lock pl (t.ctx, run_phase::match); // Throws.
+ {
+ target_lock l {a, &t, offset}; // Reassemble.
+ match_impl (l, false /* step */, try_match);
+ // Unlock within the match phase.
+ }
+ }
+ catch (const failed&) {} // Phase lock failure.
+ },
+ diag_frame::stack (),
+ target_lock::stack (),
+ ref (*ld.target),
+ ld.offset))
return make_pair (true, target_state::postponed); // Queued.
// Matched synchronously, fall through.
@@ -728,7 +740,7 @@ namespace build2
// to execute it now.
//
{
- phase_switch ps (run_phase::execute);
+ phase_switch ps (g.ctx, run_phase::execute);
execute_direct (a, g);
}
@@ -751,7 +763,7 @@ namespace build2
// We can be called during execute though everything should have been
// already resolved.
//
- switch (phase)
+ switch (g.ctx.phase)
{
case run_phase::match:
{
@@ -793,7 +805,7 @@ namespace build2
// Start asynchronous matching of prerequisites. Wait with unlocked phase
// to allow phase switching.
//
- wait_guard wg (target::count_busy (), t[a].task_count, true);
+ wait_guard wg (t.ctx, t.ctx.count_busy (), t[a].task_count, true);
size_t i (pts.size ()); // Index of the first to be added.
for (auto&& p: forward<R> (r))
@@ -812,7 +824,7 @@ namespace build2
if (pt.target == nullptr || (s != nullptr && !pt.target->in (*s)))
continue;
- match_async (a, *pt.target, target::count_busy (), t[a].task_count);
+ match_async (a, *pt.target, t.ctx.count_busy (), t[a].task_count);
pts.push_back (move (pt));
}
@@ -850,7 +862,7 @@ namespace build2
// Pretty much identical to match_prerequisite_range() except we don't
// search.
//
- wait_guard wg (target::count_busy (), t[a].task_count, true);
+ wait_guard wg (t.ctx, t.ctx.count_busy (), t[a].task_count, true);
for (size_t i (0); i != n; ++i)
{
@@ -859,7 +871,7 @@ namespace build2
if (m == nullptr || marked (m))
continue;
- match_async (a, *m, target::count_busy (), t[a].task_count);
+ match_async (a, *m, t.ctx.count_busy (), t[a].task_count);
}
wg.wait ();
@@ -897,7 +909,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 +985,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 ())
{
@@ -1088,11 +1100,12 @@ namespace build2
if (!exists (d))
mkdir_p (d, 2 /* verbosity */);
- update_backlink (p, l, m);
+ update_backlink (f.ctx, p, l, m);
}
void
- update_backlink (const path& p, const path& l, bool changed, backlink_mode m)
+ update_backlink (context& ctx,
+ const path& p, const path& l, bool changed, backlink_mode m)
{
// As above but with a slightly different diagnostics.
@@ -1126,7 +1139,7 @@ namespace build2
if (!exists (d))
mkdir_p (d, 2 /* verbosity */);
- update_backlink (p, l, m);
+ update_backlink (ctx, p, l, m);
}
static inline void
@@ -1165,7 +1178,8 @@ namespace build2
}
void
- update_backlink (const path& p, const path& l, backlink_mode om)
+ update_backlink (context& ctx,
+ const path& p, const path& l, backlink_mode om)
{
using mode = backlink_mode;
@@ -1196,7 +1210,7 @@ namespace build2
{
// Normally will be there.
//
- if (!dry_run)
+ if (!ctx.dry_run)
try_rmbacklink (l, m);
// Skip (ad hoc) targets that don't exist.
@@ -1240,7 +1254,7 @@ namespace build2
path f (fr / de.path ());
path t (to / de.path ());
- update_backlink (f, t, mode::link);
+ update_backlink (ctx, f, t, mode::link);
}
}
else
@@ -1281,7 +1295,8 @@ namespace build2
}
void
- clean_backlink (const path& l, uint16_t v /*verbosity*/, backlink_mode m)
+ clean_backlink (context& ctx,
+ const path& l, uint16_t v /*verbosity*/, backlink_mode m)
{
// Like try_rmbacklink() but with diagnostics and error handling.
@@ -1293,9 +1308,9 @@ namespace build2
{
case mode::link:
case mode::symbolic:
- case mode::hard: rmsymlink (l, true /* directory */, v); break;
- case mode::copy: rmdir_r (path_cast<dir_path> (l), true, v); break;
- case mode::overwrite: break;
+ case mode::hard: rmsymlink (ctx, l, true /* directory */, v); break;
+ case mode::copy: rmdir_r (ctx, path_cast<dir_path> (l), true, v); break;
+ case mode::overwrite: break;
}
}
else
@@ -1307,8 +1322,8 @@ namespace build2
case mode::link:
case mode::symbolic:
case mode::hard:
- case mode::copy: rmfile (l, v); break;
- case mode::overwrite: break;
+ case mode::copy: rmfile (ctx, l, v); break;
+ case mode::overwrite: break;
}
}
}
@@ -1370,6 +1385,8 @@ namespace build2
static optional<backlink_mode>
backlink_test (action a, target& t)
{
+ context& ctx (t.ctx);
+
// Note: the order of these checks is from the least to most expensive.
// Only for plain update/clean.
@@ -1391,17 +1408,17 @@ namespace build2
// Only for forwarded configurations.
//
- if (!cast_false<bool> (rs->vars[var_forwarded]))
+ if (!cast_false<bool> (rs->vars[ctx.var_forwarded]))
return nullopt;
- lookup l (t.state[a][var_backlink]);
+ lookup l (t.state[a][ctx.var_backlink]);
// If not found, check for some defaults in the global scope (this does
// not happen automatically since target type/pattern-specific lookup
// stops at the project boundary).
//
if (!l.defined ())
- l = global_scope->find (*var_backlink, t.key ());
+ l = ctx.global_scope.find (*ctx.var_backlink, t.key ());
return l ? backlink_test (t, l) : nullopt;
}
@@ -1452,7 +1469,7 @@ namespace build2
// as a target-specific wouldn't be MT-safe). @@ Don't think this
// applies to declared ad hoc members.
//
- lookup l (mt->state[a].vars[var_backlink]);
+ lookup l (mt->state[a].vars[t.ctx.var_backlink]);
optional<mode> bm (l ? backlink_test (*mt, l) : m);
@@ -1488,7 +1505,7 @@ namespace build2
ts == target_state::changed,
bl.mode);
else
- update_backlink (bl.target, bl.path, bl.mode);
+ update_backlink (t.ctx, bl.target, bl.path, bl.mode);
}
// Cancel removal.
@@ -1508,16 +1525,18 @@ namespace build2
//
backlink& bl (*i);
bl.cancel ();
- clean_backlink (bl.path, i == b ? 2 : 3 /* verbosity */, bl.mode);
+ clean_backlink (t.ctx, bl.path, i == b ? 2 : 3 /* verbosity */, bl.mode);
}
}
static target_state
execute_impl (action a, target& t)
{
+ context& ctx (t.ctx);
+
target::opstate& s (t[a]);
- assert (s.task_count.load (memory_order_consume) == target::count_busy ()
+ assert (s.task_count.load (memory_order_consume) == t.ctx.count_busy ()
&& s.state == target_state::unknown);
target_state ts;
@@ -1562,7 +1581,7 @@ namespace build2
{
recipe_function** f (s.recipe.target<recipe_function*> ());
if (f == nullptr || *f != &group_action)
- target_count.fetch_sub (1, memory_order_relaxed);
+ ctx.target_count.fetch_sub (1, memory_order_relaxed);
}
// Decrement the task count (to count_executed) and wake up any threads
@@ -1571,8 +1590,8 @@ namespace build2
size_t tc (s.task_count.fetch_sub (
target::offset_busy - target::offset_executed,
memory_order_release));
- assert (tc == target::count_busy ());
- sched.resume (s.task_count);
+ assert (tc == ctx.count_busy ());
+ ctx.sched.resume (s.task_count);
return ts;
}
@@ -1586,9 +1605,11 @@ namespace build2
target& t (const_cast<target&> (ct)); // MT-aware.
target::opstate& s (t[a]);
+ context& ctx (t.ctx);
+
// Update dependency counts and make sure they are not skew.
//
- size_t gd (dependency_count.fetch_sub (1, memory_order_relaxed));
+ size_t gd (ctx.dependency_count.fetch_sub (1, memory_order_relaxed));
size_t td (s.dependents.fetch_sub (1, memory_order_release));
assert (td != 0 && gd != 0);
td--;
@@ -1614,15 +1635,15 @@ namespace build2
// thread. For other threads the state will still be unknown (until they
// try to execute it).
//
- if (current_mode == execution_mode::last && td != 0)
+ if (ctx.current_mode == execution_mode::last && td != 0)
return target_state::postponed;
// Try to atomically change applied to busy.
//
- size_t tc (target::count_applied ());
+ size_t tc (ctx.count_applied ());
- size_t exec (target::count_executed ());
- size_t busy (target::count_busy ());
+ size_t exec (ctx.count_executed ());
+ size_t busy (ctx.count_busy ());
if (s.task_count.compare_exchange_strong (
tc,
@@ -1640,7 +1661,7 @@ namespace build2
execute_recipe (a, t, nullptr /* recipe */);
s.task_count.store (exec, memory_order_release);
- sched.resume (s.task_count);
+ ctx.sched.resume (s.task_count);
}
else
{
@@ -1650,15 +1671,15 @@ namespace build2
// Pass our diagnostics stack (this is safe since we expect the
// caller to wait for completion before unwinding its diag stack).
//
- if (sched.async (start_count,
- *task_count,
- [a] (const diag_frame* ds, target& t)
- {
- diag_frame::stack_guard dsg (ds);
- execute_impl (a, t);
- },
- diag_frame::stack (),
- ref (t)))
+ if (ctx.sched.async (start_count,
+ *task_count,
+ [a] (const diag_frame* ds, target& t)
+ {
+ diag_frame::stack_guard dsg (ds);
+ execute_impl (a, t);
+ },
+ diag_frame::stack (),
+ ref (t)))
return target_state::unknown; // Queued.
// Executed synchronously, fall through.
@@ -1678,15 +1699,17 @@ namespace build2
target_state
execute_direct (action a, const target& ct)
{
+ context& ctx (ct.ctx);
+
target& t (const_cast<target&> (ct)); // MT-aware.
target::opstate& s (t[a]);
// Similar logic to match() above except we execute synchronously.
//
- size_t tc (target::count_applied ());
+ size_t tc (ctx.count_applied ());
- size_t exec (target::count_executed ());
- size_t busy (target::count_busy ());
+ size_t exec (ctx.count_executed ());
+ size_t busy (ctx.count_busy ());
if (s.task_count.compare_exchange_strong (
tc,
@@ -1708,15 +1731,17 @@ namespace build2
}
s.task_count.store (exec, memory_order_release);
- sched.resume (s.task_count);
+ ctx.sched.resume (s.task_count);
}
}
else
{
// If the target is busy, wait for it.
//
- if (tc >= busy) sched.wait (exec, s.task_count, scheduler::work_none);
- else assert (tc == exec);
+ if (tc >= busy)
+ ctx.sched.wait (exec, s.task_count, scheduler::work_none);
+ else
+ assert (tc == exec);
}
return t.executed_state (a);
@@ -1736,14 +1761,17 @@ namespace build2
template <typename T>
target_state
- straight_execute_members (action a, atomic_count& tc,
+ straight_execute_members (context& ctx, action a, atomic_count& tc,
T ts[], size_t n, size_t p)
{
target_state r (target_state::unchanged);
+ size_t busy (ctx.count_busy ());
+ size_t exec (ctx.count_executed ());
+
// Start asynchronous execution of prerequisites.
//
- wait_guard wg (target::count_busy (), tc);
+ wait_guard wg (ctx, busy, tc);
n += p;
for (size_t i (p); i != n; ++i)
@@ -1753,7 +1781,7 @@ namespace build2
if (mt == nullptr) // Skipped.
continue;
- target_state s (execute_async (a, *mt, target::count_busy (), tc));
+ target_state s (execute_async (a, *mt, busy, tc));
if (s == target_state::postponed)
{
@@ -1778,8 +1806,8 @@ namespace build2
// If the target is still busy, wait for its completion.
//
const auto& tc (mt[a].task_count);
- if (tc.load (memory_order_acquire) >= target::count_busy ())
- sched.wait (target::count_executed (), tc, scheduler::work_none);
+ if (tc.load (memory_order_acquire) >= busy)
+ ctx.sched.wait (exec, tc, scheduler::work_none);
r |= mt.executed_state (a);
@@ -1791,14 +1819,17 @@ namespace build2
template <typename T>
target_state
- reverse_execute_members (action a, atomic_count& tc,
+ reverse_execute_members (context& ctx, action a, atomic_count& tc,
T ts[], size_t n, size_t p)
{
// Pretty much as straight_execute_members() but in reverse order.
//
target_state r (target_state::unchanged);
- wait_guard wg (target::count_busy (), tc);
+ size_t busy (ctx.count_busy ());
+ size_t exec (ctx.count_executed ());
+
+ wait_guard wg (ctx, busy, tc);
n = p - n;
for (size_t i (p); i != n; )
@@ -1808,7 +1839,7 @@ namespace build2
if (mt == nullptr)
continue;
- target_state s (execute_async (a, *mt, target::count_busy (), tc));
+ target_state s (execute_async (a, *mt, busy, tc));
if (s == target_state::postponed)
{
@@ -1827,8 +1858,8 @@ namespace build2
const target& mt (*ts[i]);
const auto& tc (mt[a].task_count);
- if (tc.load (memory_order_acquire) >= target::count_busy ())
- sched.wait (target::count_executed (), tc, scheduler::work_none);
+ if (tc.load (memory_order_acquire) >= busy)
+ ctx.sched.wait (exec, tc, scheduler::work_none);
r |= mt.executed_state (a);
@@ -1842,19 +1873,19 @@ namespace build2
//
template LIBBUILD2_SYMEXPORT target_state
straight_execute_members<const target*> (
- action, atomic_count&, const target*[], size_t, size_t);
+ context&, action, atomic_count&, const target*[], size_t, size_t);
template LIBBUILD2_SYMEXPORT target_state
reverse_execute_members<const target*> (
- action, atomic_count&, const target*[], size_t, size_t);
+ context&, action, atomic_count&, const target*[], size_t, size_t);
template LIBBUILD2_SYMEXPORT target_state
straight_execute_members<prerequisite_target> (
- action, atomic_count&, prerequisite_target[], size_t, size_t);
+ context&, action, atomic_count&, prerequisite_target[], size_t, size_t);
template LIBBUILD2_SYMEXPORT target_state
reverse_execute_members<prerequisite_target> (
- action, atomic_count&, prerequisite_target[], size_t, size_t);
+ context&, action, atomic_count&, prerequisite_target[], size_t, size_t);
pair<optional<target_state>, const target*>
execute_prerequisites (const target_type* tt,
@@ -1862,7 +1893,12 @@ namespace build2
const timestamp& mt, const execute_filter& ef,
size_t n)
{
- assert (current_mode == execution_mode::first);
+ context& ctx (t.ctx);
+
+ assert (ctx.current_mode == execution_mode::first);
+
+ size_t busy (ctx.count_busy ());
+ size_t exec (ctx.count_executed ());
auto& pts (t.prerequisite_targets[a]);
@@ -1873,7 +1909,7 @@ namespace build2
//
target_state rs (target_state::unchanged);
- wait_guard wg (target::count_busy (), t[a].task_count);
+ wait_guard wg (ctx, busy, t[a].task_count);
for (size_t i (0); i != n; ++i)
{
@@ -1882,9 +1918,7 @@ namespace build2
if (pt == nullptr) // Skipped.
continue;
- target_state s (
- execute_async (
- a, *pt, target::count_busy (), t[a].task_count));
+ target_state s (execute_async (a, *pt, busy, t[a].task_count));
if (s == target_state::postponed)
{
@@ -1908,8 +1942,8 @@ namespace build2
const target& pt (*p.target);
const auto& tc (pt[a].task_count);
- if (tc.load (memory_order_acquire) >= target::count_busy ())
- sched.wait (target::count_executed (), tc, scheduler::work_none);
+ if (tc.load (memory_order_acquire) >= busy)
+ ctx.sched.wait (exec, tc, scheduler::work_none);
target_state s (pt.executed_state (a));
rs |= s;
@@ -1966,6 +2000,8 @@ namespace build2
target_state
group_action (action a, const target& t)
{
+ context& ctx (t.ctx);
+
// If the group is busy, we wait, similar to prerequisites.
//
const target& g (*t.group);
@@ -1973,9 +2009,9 @@ namespace build2
target_state gs (execute (a, g));
if (gs == target_state::busy)
- sched.wait (target::count_executed (),
- g[a].task_count,
- scheduler::work_none);
+ ctx.sched.wait (ctx.count_executed (),
+ g[a].task_count,
+ scheduler::work_none);
// Return target_state::group to signal to execute() that this target's
// state comes from the group (which, BTW, can be failed).
@@ -2016,9 +2052,11 @@ namespace build2
bool ed (false);
path ep;
- auto clean_extra = [&er, &ed, &ep] (const file& f,
- const path* fp,
- const clean_extras& es)
+ context& ctx (ft.ctx);
+
+ auto clean_extra = [&er, &ed, &ep, &ctx] (const file& f,
+ const path* fp,
+ const clean_extras& es)
{
for (const char* e: es)
{
@@ -2058,7 +2096,7 @@ namespace build2
{
dir_path dp (path_cast<dir_path> (p));
- switch (build2::rmdir_r (dp, true, 3))
+ switch (rmdir_r (ctx, dp, true, 3))
{
case rmdir_status::success:
{
@@ -2077,7 +2115,7 @@ namespace build2
}
else
{
- if (rmfile (p, 3))
+ if (rmfile (ctx, p, 3))
r = target_state::changed;
}
@@ -2105,7 +2143,7 @@ namespace build2
// depdb so for now we treat them as "to remove" but in the future we may
// need to have two lists.
//
- bool clean (cast_true<bool> (ft[var_clean]));
+ bool clean (cast_true<bool> (ft[ctx.var_clean]));
// Now clean the ad hoc group file members, if any.
//
@@ -2143,7 +2181,7 @@ namespace build2
}
else
{
- target_state r (rmfile (*mp, 3)
+ target_state r (rmfile (ctx, *mp, 3)
? target_state::changed
: target_state::unchanged);
@@ -2180,7 +2218,7 @@ namespace build2
//
if (tr != target_state::changed && er == target_state::changed)
{
- if (verb > (current_diag_noise ? 0 : 1) && verb < 3)
+ if (verb > (ctx.current_diag_noise ? 0 : 1) && verb < 3)
{
if (ed)
text << "rm -r " << path_cast<dir_path> (ep);
@@ -2218,7 +2256,7 @@ namespace build2
//
target_state r (target_state::unchanged);
- if (cast_true<bool> (g[var_clean]))
+ if (cast_true<bool> (g[g.ctx.var_clean]))
{
for (group_view gv (g.group_members (a)); gv.count != 0; --gv.count)
{
@@ -2239,6 +2277,8 @@ namespace build2
target_state
perform_clean_group_depdb (action a, const target& g)
{
+ context& ctx (g.ctx);
+
// The same twisted target state merging logic as in perform_clean_extra().
//
target_state er (target_state::unchanged);
@@ -2249,7 +2289,7 @@ namespace build2
{
ep = gv.members[0]->as<file> ().path () + ".d";
- if (rmfile (ep, 3))
+ if (rmfile (ctx, ep, 3))
er = target_state::changed;
}
@@ -2257,7 +2297,7 @@ namespace build2
if (tr != target_state::changed && er == target_state::changed)
{
- if (verb > (current_diag_noise ? 0 : 1) && verb < 3)
+ if (verb > (ctx.current_diag_noise ? 0 : 1) && verb < 3)
text << "rm " << ep;
}
diff --git a/libbuild2/algorithm.hxx b/libbuild2/algorithm.hxx
index 4707ae7..cda464b 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,
@@ -611,18 +613,20 @@ namespace build2
//
template <typename T>
target_state
- straight_execute_members (action, atomic_count&, T[], size_t, size_t);
+ straight_execute_members (context&, action, atomic_count&,
+ T[], size_t, size_t);
template <typename T>
target_state
- reverse_execute_members (action, atomic_count&, T[], size_t, size_t);
+ reverse_execute_members (context&, action, atomic_count&,
+ T[], size_t, size_t);
template <typename T>
inline target_state
straight_execute_members (action a, const target& t,
T ts[], size_t c, size_t s)
{
- return straight_execute_members (a, t[a].task_count, ts, c, s);
+ return straight_execute_members (t.ctx, a, t[a].task_count, ts, c, s);
}
template <typename T>
@@ -630,7 +634,7 @@ namespace build2
reverse_execute_members (action a, const target& t,
T ts[], size_t c, size_t s)
{
- return reverse_execute_members (a, t[a].task_count, ts, c, s);
+ return reverse_execute_members (t.ctx, a, t[a].task_count, ts, c, s);
}
// Call straight or reverse depending on the current mode.
@@ -757,18 +761,21 @@ namespace build2
backlink_mode = backlink_mode::link);
LIBBUILD2_SYMEXPORT void
- update_backlink (const path& target,
+ update_backlink (context&,
+ const path& target,
const path& link,
bool changed,
backlink_mode = backlink_mode::link);
LIBBUILD2_SYMEXPORT void
- update_backlink (const path& target,
+ update_backlink (context&,
+ const path& target,
const path& link,
backlink_mode = backlink_mode::link);
LIBBUILD2_SYMEXPORT void
- clean_backlink (const path& link,
+ clean_backlink (context&,
+ const path& link,
uint16_t verbosity,
backlink_mode = backlink_mode::link);
}
diff --git a/libbuild2/algorithm.ixx b/libbuild2/algorithm.ixx
index 6bc771e..9593ac0 100644
--- a/libbuild2/algorithm.ixx
+++ b/libbuild2/algorithm.ixx
@@ -13,7 +13,8 @@ namespace build2
inline const target&
search_custom (const prerequisite& p, const target& t)
{
- assert (phase == run_phase::match || phase == run_phase::execute);
+ assert (t.ctx.phase == run_phase::match ||
+ t.ctx.phase == run_phase::execute);
const target* e (nullptr);
if (!p.target.compare_exchange_strong (
@@ -57,7 +58,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 +68,7 @@ namespace build2
const optional<project_name>& proj)
{
return search_existing (
+ ctx,
prerequisite_key {
proj,
{
@@ -272,14 +275,14 @@ namespace build2
inline void
match_inc_dependens (action a, const target& t)
{
- dependency_count.fetch_add (1, memory_order_relaxed);
+ t.ctx.dependency_count.fetch_add (1, memory_order_relaxed);
t[a].dependents.fetch_add (1, memory_order_release);
}
inline target_state
match (action a, const target& t, bool fail)
{
- assert (phase == run_phase::match);
+ assert (t.ctx.phase == run_phase::match);
target_state r (match (a, t, 0, nullptr).second);
@@ -294,7 +297,7 @@ namespace build2
inline pair<bool, target_state>
try_match (action a, const target& t, bool fail)
{
- assert (phase == run_phase::match);
+ assert (t.ctx.phase == run_phase::match);
pair<bool, target_state> r (
match (a, t, 0, nullptr, true /* try_match */));
@@ -313,7 +316,7 @@ namespace build2
inline bool
match (action a, const target& t, unmatch um)
{
- assert (phase == run_phase::match);
+ assert (t.ctx.phase == run_phase::match);
target_state s (match (a, t, 0, nullptr).second);
@@ -353,10 +356,12 @@ namespace build2
size_t sc, atomic_count& tc,
bool fail)
{
- assert (phase == run_phase::match);
+ context& ctx (t.ctx);
+
+ assert (ctx.phase == run_phase::match);
target_state r (match (a, t, sc, &tc).second);
- if (fail && !keep_going && r == target_state::failed)
+ if (fail && !ctx.keep_going && r == target_state::failed)
throw failed ();
return r;
@@ -365,7 +370,8 @@ namespace build2
inline void
set_recipe (target_lock& l, recipe&& r)
{
- target::opstate& s ((*l.target)[l.action]);
+ target& t (*l.target);
+ target::opstate& s (t[l.action]);
s.recipe = move (r);
@@ -396,7 +402,7 @@ namespace build2
if (l.action.inner ())
{
if (f == nullptr || *f != &group_action)
- target_count.fetch_add (1, memory_order_relaxed);
+ t.ctx.target_count.fetch_add (1, memory_order_relaxed);
}
}
}
@@ -404,7 +410,7 @@ namespace build2
inline void
match_recipe (target_lock& l, recipe r)
{
- assert (phase == run_phase::match && l.target != nullptr);
+ assert (l.target != nullptr && l.target->ctx.phase == run_phase::match);
(*l.target)[l.action].rule = nullptr; // No rule.
set_recipe (l, move (r));
@@ -414,7 +420,7 @@ namespace build2
inline recipe
match_delegate (action a, target& t, const rule& dr, bool try_match)
{
- assert (phase == run_phase::match);
+ assert (t.ctx.phase == run_phase::match);
// Note: we don't touch any of the t[a] state since that was/will be set
// for the delegating rule.
@@ -448,7 +454,7 @@ namespace build2
if (a.outer ())
a = a.inner_action ();
- switch (phase)
+ switch (t.ctx.phase)
{
case run_phase::match:
{
@@ -541,9 +547,9 @@ namespace build2
execute_wait (action a, const target& t)
{
if (execute (a, t) == target_state::busy)
- sched.wait (target::count_executed (),
- t[a].task_count,
- scheduler::work_none);
+ t.ctx.sched.wait (t.ctx.count_executed (),
+ t[a].task_count,
+ scheduler::work_none);
return t.executed_state (a);
}
@@ -555,7 +561,7 @@ namespace build2
{
target_state r (execute (a, t, sc, &tc));
- if (fail && !keep_going && r == target_state::failed)
+ if (fail && !t.ctx.keep_going && r == target_state::failed)
throw failed ();
return r;
@@ -598,7 +604,7 @@ namespace build2
inline target_state
execute_prerequisites (action a, const target& t, size_t c)
{
- return current_mode == execution_mode::first
+ return t.ctx.current_mode == execution_mode::first
? straight_execute_prerequisites (a, t, c)
: reverse_execute_prerequisites (a, t, c);
}
@@ -609,7 +615,8 @@ namespace build2
{
assert (a.outer ());
auto& p (t.prerequisite_targets[a]);
- return straight_execute_members (a.inner_action (),
+ return straight_execute_members (t.ctx,
+ a.inner_action (),
t[a].task_count,
p.data (),
c == 0 ? p.size () - s : c,
@@ -621,7 +628,8 @@ namespace build2
{
assert (a.outer ());
auto& p (t.prerequisite_targets[a]);
- return reverse_execute_members (a.inner_action (),
+ return reverse_execute_members (t.ctx,
+ a.inner_action (),
t[a].task_count,
p.data (),
c == 0 ? p.size () : c,
@@ -631,7 +639,7 @@ namespace build2
inline target_state
execute_prerequisites_inner (action a, const target& t, size_t c)
{
- return current_mode == execution_mode::first
+ return t.ctx.current_mode == execution_mode::first
? straight_execute_prerequisites_inner (a, t, c)
: reverse_execute_prerequisites_inner (a, t, c);
}
@@ -689,7 +697,7 @@ namespace build2
inline target_state
execute_members (action a, const target& t, const target* ts[], size_t n)
{
- return current_mode == execution_mode::first
+ return t.ctx.current_mode == execution_mode::first
? straight_execute_members (a, t, ts, n, 0)
: reverse_execute_members (a, t, ts, n, n);
}
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/bash/target.cxx b/libbuild2/bash/target.cxx
index 7313316..386842b 100644
--- a/libbuild2/bash/target.cxx
+++ b/libbuild2/bash/target.cxx
@@ -20,8 +20,8 @@ namespace build2
&file::static_type,
&target_factory<bash>,
nullptr, /* fixed_extension */
- &target_extension_var<var_extension, bash_ext_def>,
- &target_pattern_var<var_extension, bash_ext_def>,
+ &target_extension_var<bash_ext_def>,
+ &target_pattern_var<bash_ext_def>,
nullptr,
&file_search,
false
diff --git a/libbuild2/config/init.cxx b/libbuild2/config/init.cxx
index b790569..6998017 100644
--- a/libbuild2/config/init.cxx
+++ b/libbuild2/config/init.cxx
@@ -29,8 +29,8 @@ namespace build2
l5 ([&]{trace << "for " << rs;});
- const string& mname (current_mname);
- const string& oname (current_oname);
+ const string& mname (rs.ctx.current_mname);
+ const string& oname (rs.ctx.current_oname);
// Only create the module if we are configuring or creating. This is a
// bit tricky since the build2 core may not yet know if this is the
@@ -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..264eb93 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,16 @@ 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));
+ context& ctx (rs.ctx);
+
+ 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 +109,7 @@ namespace build2
ofs << "config.version = " << module::version << endl;
- if (auto l = root.vars[var_amalgamation])
+ if (auto l = rs.vars[ctx.var_amalgamation])
{
const dir_path& d (cast<dir_path> (l));
@@ -130,10 +132,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 +146,7 @@ namespace build2
if (!l.defined ())
continue;
- if (!(l.belongs (root) || l.belongs (*global_scope)))
+ if (!(l.belongs (rs) || l.belongs (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 +164,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 +309,16 @@ 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 ());
+ context& ctx (rs.ctx);
+
+ 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 +328,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 +342,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 +354,56 @@ namespace build2
// Configure subprojects that have been loaded.
//
- if (auto l = root.vars[var_subprojects])
+ if (auto l = rs.vars[ctx.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 (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 ());
+ context& ctx (rs.ctx);
- if (!projects.insert (&root).second)
+ const dir_path& out_root (rs.out_path ());
+ const dir_path& src_root (rs.src_path ());
+
+ 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[ctx.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 (ctx.scopes.find (out_nroot));
+ assert (nrs.out_path () == out_nroot);
- configure_forward (nroot, projects);
+ configure_forward (nrs, projects);
}
}
}
@@ -451,7 +457,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 +469,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 +492,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 +521,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;
}
@@ -539,6 +545,8 @@ namespace build2
if (rs == nullptr)
fail << "out of project target " << t;
+ context& ctx (t.ctx);
+
const operations& ops (rs->root_extra->operations);
for (operation_id id (default_id + 1); // Skip default_id.
@@ -552,9 +560,9 @@ namespace build2
if (oif->id != id)
continue;
- set_current_oif (*oif);
+ ctx.current_operation (*oif);
- phase_lock pl (run_phase::match);
+ phase_lock pl (ctx, run_phase::match);
match (action (configure_id, id), t);
}
}
@@ -586,14 +594,16 @@ 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 ());
+ context& ctx (rs.ctx);
+
+ 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 +614,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[ctx.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 (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
@@ -625,7 +635,7 @@ namespace build2
!d.empty ();
d = d.directory ())
{
- rmdir_status s (rmdir (out_root / d, 2));
+ rmdir_status s (rmdir (ctx, out_root / d, 2));
if (s == rmdir_status::not_empty)
break; // No use trying do remove parent ones.
@@ -643,21 +653,21 @@ namespace build2
{
l5 ([&]{trace << "completely disfiguring " << out_root;});
- r = rmfile (config_file (root)) || r;
+ r = rmfile (ctx, config_file (rs)) || r;
if (out_root != src_root)
{
- r = rmfile (out_root / root.root_extra->src_root_file, 2) || r;
+ r = rmfile (ctx, 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 (ctx, out_root / rs.root_extra->root_dir, 2) || r;
+ r = rmdir (ctx, out_root / rs.root_extra->bootstrap_dir, 2) || r;
+ r = rmdir (ctx, out_root / rs.root_extra->build_dir, 2) || r;
- switch (rmdir (out_root))
+ switch (rmdir (ctx, out_root))
{
case rmdir_status::not_empty:
{
@@ -687,16 +697,18 @@ 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 ());
+ context& ctx (rs.ctx);
+
+ 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 +716,23 @@ namespace build2
bool r (false);
- if (auto l = root.vars[var_subprojects])
+ if (auto l = rs.vars[ctx.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 (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 (ctx, src_root / rs.root_extra->out_root_file) || r;
+ r = rmdir (ctx, src_root / rs.root_extra->bootstrap_dir, 2) || r;
return r;
}
@@ -757,14 +769,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 +802,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 +848,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 +858,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 +881,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 +891,7 @@ namespace build2
}
const string&
- preprocess_create (const variable_overrides& var_ovs,
+ preprocess_create (context& ctx,
values& params,
vector_view<opspec>& spec,
bool lifted,
@@ -916,7 +928,7 @@ namespace build2
fail (l) << "invalid module name: " << e.what ();
}
- current_oname = empty_string; // Make sure valid.
+ ctx.current_oname = empty_string; // Make sure valid.
// Now handle each target in each operation spec.
//
@@ -986,7 +998,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..355e896 100644
--- a/libbuild2/config/utility.cxx
+++ b/libbuild2/config/utility.cxx
@@ -46,7 +46,7 @@ namespace build2
}
}
- if (l.defined () && current_mif->id == configure_id)
+ if (l.defined () && r.ctx.current_mif->id == configure_id)
save_variable (r, var);
return pair<lookup, bool> (l, n);
@@ -55,7 +55,7 @@ namespace build2
lookup
optional (scope& r, const variable& var)
{
- if (current_mif->id == configure_id)
+ if (r.ctx.current_mif->id == configure_id)
save_variable (r, var);
auto l (r[var]);
@@ -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,9 +101,9 @@ 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)
+ if (rs.ctx.current_mif->id == configure_id)
save_variable (rs, var);
auto l (rs[var]); // Include inherited values.
@@ -116,9 +116,9 @@ 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)
+ if (rs.ctx.current_mif->id == configure_id)
save_variable (rs, var);
value& x (rs.assign (var));
@@ -135,7 +135,7 @@ namespace build2
void
save_variable (scope& r, const variable& var, uint64_t flags)
{
- if (current_mif->id != configure_id)
+ if (r.ctx.current_mif->id != configure_id)
return;
// The project might not be using the config module. But then how
@@ -148,7 +148,7 @@ namespace build2
void
save_module (scope& r, const char* name, int prio)
{
- if (current_mif->id != configure_id)
+ if (r.ctx.current_mif->id != configure_id)
return;
if (module* m = r.lookup_module<module> (module::name))
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/config/utility.txx b/libbuild2/config/utility.txx
index 841c408..9c1455f 100644
--- a/libbuild2/config/utility.txx
+++ b/libbuild2/config/utility.txx
@@ -19,7 +19,7 @@ namespace build2
{
// Note: see also omitted() if changing anything here.
- if (current_mif->id == configure_id)
+ if (root.ctx.current_mif->id == configure_id)
save_variable (root, var, save_flags);
pair<lookup, size_t> org (root.find_original (var));
diff --git a/libbuild2/context.cxx b/libbuild2/context.cxx
index 1cc8bbc..687e9aa 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/function.hxx>
#include <libbuild2/diagnostics.hxx>
#include <libbutl/ft/exception.hxx> // uncaught_exceptions
@@ -25,379 +27,57 @@ 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;
+ variable_override_cache global_override_cache;
+ function_map functions;
+
+ data (context& c): scopes (c), targets (c), var_pool (&c /* global */) {}
+ };
+
+ context::
+ context (scheduler& s, const strings& cmd_vars, bool dr, bool kg)
+ : data_ (new data (*this)),
+ sched (s),
+ dry_run_option (dr),
+ keep_going (kg),
+ phase_mutex (*this),
+ scopes (data_->scopes),
+ global_scope (create_global_scope (data_->scopes)),
+ targets (data_->targets),
+ var_pool (data_->var_pool),
+ var_overrides (data_->var_overrides),
+ global_override_cache (data_->global_override_cache),
+ functions (data_->functions)
{
- 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;
+ register_builtin_functions (functions);
- 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 +100,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 +125,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 +176,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 +406,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 +419,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 +432,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 +450,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);
@@ -819,11 +485,10 @@ namespace build2
var_import_target = &vp.insert<name> ("import.target");
- var_clean = &vp.insert<bool> ("clean", v_t);
- var_backlink = &vp.insert<string> ("backlink", v_t);
- var_include = &vp.insert<string> ("include", v_q);
-
- vp.insert<string> (var_extension, v_t);
+ var_extension = &vp.insert<string> ("extension", v_t);
+ var_clean = &vp.insert<bool> ("clean", v_t);
+ var_backlink = &vp.insert<string> ("backlink", v_t);
+ var_include = &vp.insert<string> ("include", v_q);
// Backlink executables and (generated) documentation by default.
//
@@ -847,39 +512,356 @@ namespace build2
r.insert<mtime_target> (perform_update_id, "file", file_rule::instance);
r.insert<mtime_target> (perform_clean_id, "file", file_rule::instance);
}
+ }
- return vos;
+ context::
+ ~context ()
+ {
+ // Cannot be inline since context::data is undefined.
}
- void (*config_save_variable) (scope&, const variable&, uint64_t);
+ void context::
+ current_meta_operation (const meta_operation_info& mif)
+ {
+ if (current_mname != mif.name)
+ {
+ current_mname = mif.name;
+ global_scope.rw ().assign (var_build_meta_operation) = mif.name;
+ }
- const string& (*config_preprocess_create) (const variable_overrides&,
- values&,
- vector_view<opspec>&,
- bool,
- const location&);
+ current_mif = &mif;
+ current_on = 0; // Reset.
+ }
- const variable* var_src_root;
- const variable* var_out_root;
- const variable* var_src_base;
- const variable* var_out_base;
- const variable* var_forwarded;
+ void context::
+ current_operation (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;
- const variable* var_project;
- const variable* var_amalgamation;
- const variable* var_subprojects;
- const variable* var_version;
+ // 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);
+ }
- const variable* var_project_url;
- const variable* var_project_summary;
+ bool run_phase_mutex::
+ lock (run_phase p)
+ {
+ bool r;
- const variable* var_import_target;
+ {
+ mlock l (m_);
+ bool u (lc_ == 0 && mc_ == 0 && ec_ == 0); // Unlocked.
- const variable* var_clean;
- const variable* var_backlink;
- const variable* var_include;
+ // 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)
+ {
+ ctx_.phase = p;
+ r = !fail_;
+ }
+ else if (ctx_.phase != p)
+ {
+ ctx_.sched.deactivate (false /* external */);
+ for (; ctx_.phase != p; v->wait (l)) ;
+ r = !fail_;
+ l.unlock (); // Important: activate() can block.
+ ctx_.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.
+ }
- const char var_extension[10] = "extension";
+ return r;
+ }
- const variable* var_build_meta_operation;
+ 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) {ctx_.phase = run_phase::load; v = &lv_;}
+ else if (mc_ != 0) {ctx_.phase = run_phase::match; v = &mv_;}
+ else if (ec_ != 0) {ctx_.phase = run_phase::execute; v = &ev_;}
+ else {ctx_.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)
+ {
+ ctx_.phase = n;
+ r = !fail_;
+
+ // Notify others that could be waiting for this phase.
+ //
+ if (v != nullptr)
+ {
+ l.unlock ();
+ v->notify_all ();
+ }
+ }
+ else // phase != n
+ {
+ ctx_.sched.deactivate (false /* external */);
+ for (; ctx_.phase != n; v->wait (l)) ;
+ r = !fail_;
+ l.unlock (); // Important: activate() can block.
+ ctx_.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 (context& c, run_phase p)
+ : ctx (c), phase (p)
+ {
+ phase_lock* pl (phase_lock_instance);
+
+ // This is tricky: we might be switching to another context.
+ //
+ if (pl != nullptr && &pl->ctx == &ctx)
+ assert (pl->phase == phase);
+ else
+ {
+ if (!ctx.phase_mutex.lock (phase))
+ {
+ ctx.phase_mutex.unlock (phase);
+ throw failed ();
+ }
+
+ prev = pl;
+ phase_lock_instance = this;
+
+ //text << this_thread::get_id () << " phase acquire " << phase;
+ }
+ }
+
+ phase_lock::
+ ~phase_lock ()
+ {
+ if (phase_lock_instance == this)
+ {
+ phase_lock_instance = prev;
+ ctx.phase_mutex.unlock (phase);
+
+ //text << this_thread::get_id () << " phase release " << p;
+ }
+ }
+
+ // phase_unlock
+ //
+ phase_unlock::
+ phase_unlock (context& ctx, bool u)
+ : l (u ? phase_lock_instance : nullptr)
+ {
+ if (u)
+ {
+ assert (&l->ctx == &ctx);
+
+ phase_lock_instance = nullptr; // Note: not l->prev.
+ ctx.phase_mutex.unlock (l->phase);
+
+ //text << this_thread::get_id () << " phase unlock " << l->p;
+ }
+ }
+
+ phase_unlock::
+ ~phase_unlock () noexcept (false)
+ {
+ if (l != nullptr)
+ {
+ bool r (l->ctx.phase_mutex.lock (l->phase));
+ 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 (context& ctx, run_phase n)
+ : old_phase (ctx.phase), new_phase (n)
+ {
+ phase_lock* pl (phase_lock_instance);
+ assert (&pl->ctx == &ctx);
+
+ if (!ctx.phase_mutex.relock (old_phase, new_phase))
+ {
+ ctx.phase_mutex.relock (new_phase, old_phase);
+ throw failed ();
+ }
+
+ pl->phase = new_phase;
+
+ if (new_phase == run_phase::load) // Note: load lock is exclusive.
+ ctx.load_generation++;
+
+ //text << this_thread::get_id () << " phase switch " << o << " " << n;
+ }
+
+ phase_switch::
+ ~phase_switch () noexcept (false)
+ {
+ phase_lock* pl (phase_lock_instance);
+ run_phase_mutex& pm (pl->ctx.phase_mutex);
+
+ // 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 (new_phase == run_phase::load && uncaught_exception ())
+ {
+ mlock l (pm.m_);
+ pm.fail_ = true;
+ }
+
+ bool r (pm.relock (new_phase, old_phase));
+ pl->phase = old_phase;
+
+ // Similar logic to ~phase_unlock().
+ //
+ if (!r && !uncaught_exception ())
+ throw failed ();
+
+ //text << this_thread::get_id () << " phase restore " << n << " " << o;
+ }
+
+ void (*config_save_variable) (scope&, const variable&, uint64_t);
+
+ const string& (*config_preprocess_create) (context&,
+ values&,
+ vector_view<opspec>&,
+ bool,
+ const location&);
}
diff --git a/libbuild2/context.hxx b/libbuild2/context.hxx
index ce9a996..243ad2f 100644
--- a/libbuild2/context.hxx
+++ b/libbuild2/context.hxx
@@ -8,7 +8,11 @@
#include <libbuild2/types.hxx>
#include <libbuild2/utility.hxx>
-#include <libbuild2/variable.hxx>
+// NOTE: this file is included by pretty much every other build state header
+// (scope, target, variable, etc) so including any of them here is most
+// likely a non-starter.
+//
+#include <libbuild2/action.hxx>
#include <libbuild2/operation.hxx>
#include <libbuild2/scheduler.hxx>
@@ -16,71 +20,25 @@
namespace build2
{
+ class context;
+
class scope;
+ class scope_map;
+ class target_set;
- // Main scheduler. Started up and shut down in main().
- //
- LIBBUILD2_SYMEXPORT extern scheduler sched;
+ class value;
+ using values = small_vector<value, 1>;
- // In order to perform each operation the build system goes through the
- // following phases:
- //
- // load - load the buildfiles
- // match - search prerequisites and match rules
- // execute - execute the matched rule
- //
- // The build system starts with a "serial load" phase and then continues
- // with parallel match and execute. Match, however, can be interrupted
- // both with load and execute.
- //
- // Match can be interrupted with "exclusive load" in order to load
- // additional buildfiles. Similarly, it can be interrupted with (parallel)
- // execute in order to build targetd required to complete the match (for
- // example, generated source code or source code generators themselves).
- //
- // Such interruptions are performed by phase change that is protected by
- // phase_mutex (which is also used to synchronize the state changes between
- // phases).
- //
- // Serial load can perform arbitrary changes to the build state. Exclusive
- // load, however, can only perform "island appends". That is, it can create
- // new "nodes" (variables, scopes, etc) but not (semantically) change
- // already existing nodes or invalidate any references to such (the idea
- // here is that one should be able to load additional buildfiles as long as
- // they don't interfere with the existing build state). The "islands" are
- // identified by the load_generation number (0 for the initial/serial
- // load). It is incremented in case of a phase switch and can be stored in
- // various "nodes" to verify modifications are only done "within the
- // islands".
- //
- LIBBUILD2_SYMEXPORT extern run_phase phase;
- LIBBUILD2_SYMEXPORT extern size_t load_generation;
+ struct variable;
+ class variable_pool;
+ struct variable_override;
+ using variable_overrides = vector<variable_override>;
+ class variable_override_cache;
+
+ class function_map;
+
+ struct opspec;
- // A "tri-mutex" that keeps all the threads in one of the three phases. When
- // a thread wants to switch a phase, it has to wait for all the other
- // threads to do the same (or release their phase locks). The load phase is
- // exclusive.
- //
- // The interleaving match and execute is interesting: during match we read
- // the "external state" (e.g., filesystem entries, modifications times, etc)
- // and capture it in the "internal state" (our dependency graph). During
- // execute we are modifying the external state with controlled modifications
- // of the internal state to reflect the changes (e.g., update mtimes). If
- // you think about it, it's pretty clear that we cannot safely perform both
- // of these actions simultaneously. A good example would be running a code
- // generator and header dependency extraction simultaneously: the extraction
- // process may pick up headers as they are being generated. As a result, we
- // either have everyone treat the external state as read-only or write-only.
- //
- // There is also one more complication: if we are returning from a load
- // phase that has failed, then the build state could be seriously messed up
- // (things like scopes not being setup completely, etc). And once we release
- // the lock, other threads that are waiting will start relying on this
- // messed up state. So a load phase can mark the phase_mutex as failed in
- // which case all currently blocked and future lock()/relock() calls return
- // false. Note that in this case we still switch to the desired phase. See
- // the phase_{lock,switch,unlock} implementations for details.
- //
class LIBBUILD2_SYMEXPORT run_phase_mutex
{
public:
@@ -102,12 +60,11 @@ namespace build2
bool
relock (run_phase unlock, run_phase lock);
- public:
- run_phase_mutex ()
- : fail_ (false), lc_ (0), mc_ (0), ec_ (0)
- {
- phase = run_phase::load;
- }
+ private:
+ friend class context;
+
+ run_phase_mutex (context& c)
+ : ctx_ (c), fail_ (false), lc_ (0), mc_ (0), ec_ (0) {}
private:
friend struct phase_lock;
@@ -124,6 +81,8 @@ namespace build2
// When the mutex is unlocked (all three counters become zero, the phase
// is always changed to load (this is also the initial state).
//
+ context& ctx_;
+
mutex m_;
bool fail_;
@@ -139,7 +98,311 @@ namespace build2
mutex lm_;
};
- extern run_phase_mutex phase_mutex;
+ // @@ CTX: document (backlinks, non-overlap etc). RW story.
+ //
+ class LIBBUILD2_SYMEXPORT context
+ {
+ struct data;
+ unique_ptr<data> data_;
+
+ public:
+ scheduler& sched;
+
+ // Dry run flag (see --dry-run|-n).
+ //
+ // This flag is set (based on dry_run_option) only for the final execute
+ // phase (as opposed to those that interrupt match) by the perform meta
+ // operation's execute() callback.
+ //
+ // Note that for this mode to function properly we have to use fake
+ // mtimes. Specifically, a rule that pretends to update a target must set
+ // its mtime to system_clock::now() and everyone else must use this cached
+ // value. In other words, there should be no mtime re-query from the
+ // filesystem. The same is required for "logical clean" (i.e., dry-run
+ // 'clean update' in order to see all the command lines).
+ //
+ // At first, it may seem like we should also "dry-run" changes to depdb.
+ // But that would be both problematic (some rules update it in apply()
+ // during the match phase) and wasteful (why discard information). Also,
+ // depdb may serve as an input to some commands (for example, to provide
+ // C++ module mapping) which means that without updating it the commands
+ // we print might not be runnable (think of the compilation database).
+ //
+ // One thing we need to be careful about if we are updating depdb is to
+ // not render the target up-to-date. But in this case the depdb file will
+ // be older than the target which in our model is treated as an
+ // interrupted update (see depdb for details).
+ //
+ // Note also that sometimes it makes sense to do a bit more than
+ // absolutely necessary or to discard information in order to keep the
+ // rule logic sane. And some rules may choose to ignore this flag
+ // altogether. In this case, however, the rule should be careful not to
+ // rely on functions (notably from filesystem) that respect this flag in
+ // order not to end up with a job half done.
+ //
+ bool dry_run = false;
+ bool dry_run_option;
+
+ // Keep going flag.
+ //
+ // Note that setting it to false is not of much help unless we are running
+ // serially: in parallel we queue most of the things up before we see any
+ // failures.
+ //
+ bool keep_going;
+
+ // In order to perform each operation the build system goes through the
+ // following phases:
+ //
+ // load - load the buildfiles
+ // match - search prerequisites and match rules
+ // execute - execute the matched rule
+ //
+ // The build system starts with a "serial load" phase and then continues
+ // with parallel match and execute. Match, however, can be interrupted
+ // both with load and execute.
+ //
+ // Match can be interrupted with "exclusive load" in order to load
+ // additional buildfiles. Similarly, it can be interrupted with (parallel)
+ // execute in order to build targetd required to complete the match (for
+ // example, generated source code or source code generators themselves).
+ //
+ // Such interruptions are performed by phase change that is protected by
+ // phase_mutex (which is also used to synchronize the state changes
+ // between phases).
+ //
+ // Serial load can perform arbitrary changes to the build state. Exclusive
+ // load, however, can only perform "island appends". That is, it can
+ // create new "nodes" (variables, scopes, etc) but not (semantically)
+ // change already existing nodes or invalidate any references to such (the
+ // idea here is that one should be able to load additional buildfiles as
+ // long as they don't interfere with the existing build state). The
+ // "islands" are identified by the load_generation number (0 for the
+ // initial/serial load). It is incremented in case of a phase switch and
+ // can be stored in various "nodes" to verify modifications are only done
+ // "within the islands".
+ //
+ run_phase phase = run_phase::load;
+ size_t load_generation = 0;
+
+ // A "tri-mutex" that keeps all the threads in one of the three phases.
+ // When a thread wants to switch a phase, it has to wait for all the other
+ // threads to do the same (or release their phase locks). The load phase
+ // is exclusive.
+ //
+ // The interleaving match and execute is interesting: during match we read
+ // the "external state" (e.g., filesystem entries, modifications times,
+ // etc) and capture it in the "internal state" (our dependency graph).
+ // During execute we are modifying the external state with controlled
+ // modifications of the internal state to reflect the changes (e.g.,
+ // update mtimes). If you think about it, it's pretty clear that we cannot
+ // safely perform both of these actions simultaneously. A good example
+ // would be running a code generator and header dependency extraction
+ // simultaneously: the extraction process may pick up headers as they are
+ // being generated. As a result, we either have everyone treat the
+ // external state as read-only or write-only.
+ //
+ // There is also one more complication: if we are returning from a load
+ // phase that has failed, then the build state could be seriously messed
+ // up (things like scopes not being setup completely, etc). And once we
+ // release the lock, other threads that are waiting will start relying on
+ // this messed up state. So a load phase can mark the phase_mutex as
+ // failed in which case all currently blocked and future lock()/relock()
+ // calls return false. Note that in this case we still switch to the
+ // desired phase. See the phase_{lock,switch,unlock} implementations for
+ // details.
+ //
+ run_phase_mutex phase_mutex;
+
+ // Current action (meta/operation).
+ //
+ // The names unlike info are available during boot but may not yet be
+ // lifted. The name is always for an outer operation (or meta operation
+ // that hasn't been recognized as such yet).
+ //
+ string current_mname;
+ string current_oname;
+
+ const meta_operation_info* current_mif;
+ const operation_info* current_inner_oif;
+ const operation_info* current_outer_oif;
+
+ // Current operation number (1-based) in the meta-operation batch.
+ //
+ size_t current_on;
+
+ // Note: we canote use the corresponding target::offeset_* values.
+ //
+ size_t count_base () const {return 5 * (current_on - 1);}
+
+ size_t count_touched () const {return 1 + count_base ();}
+ size_t count_tried () const {return 2 + count_base ();}
+ size_t count_matched () const {return 3 + count_base ();}
+ size_t count_applied () const {return 4 + count_base ();}
+ size_t count_executed () const {return 5 + count_base ();}
+ size_t count_busy () const {return 6 + count_base ();}
+
+ // Execution mode.
+ //
+ execution_mode current_mode;
+
+ // Some diagnostics (for example output directory creation/removal by the
+ // fsdir rule) is just noise at verbosity level 1 unless it is the only
+ // thing that is printed. So we can only suppress it in certain situations
+ // (e.g., dist) where we know we have already printed something.
+ //
+ bool current_diag_noise;
+
+ // Total number of dependency relationships and targets with non-noop
+ // recipe in the current action.
+ //
+ // Together with target::dependents the dependency count is incremented
+ // during the rule search & match phase and is decremented during
+ // execution with the expectation of it reaching 0. Used as a sanity
+ // check.
+ //
+ // The target count is incremented after a non-noop recipe is matched and
+ // decremented after such recipe has been executed. If such a recipe has
+ // skipped executing the operation, then it should increment the skip
+ // count. These two counters are used for progress monitoring and
+ // diagnostics.
+ //
+ atomic_count dependency_count;
+ atomic_count target_count;
+ atomic_count skip_count;
+
+ // Build state (scopes, targets, variables, etc).
+ //
+ 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.
+ variable_override_cache& global_override_cache;
+
+ function_map& functions;
+
+ // Cached variables.
+ //
+
+ // Note: consider printing in info meta-operation if adding anything here.
+ //
+ const variable* var_src_root;
+ const variable* var_out_root;
+ const variable* var_src_base;
+ const variable* var_out_base;
+ const variable* var_forwarded;
+
+ const variable* var_project;
+ const variable* var_amalgamation;
+ const variable* var_subprojects;
+ const variable* var_version;
+
+ // project.url
+ //
+ const variable* var_project_url;
+
+ // project.summary
+ //
+ const variable* var_project_summary;
+
+ // import.target
+ //
+ const variable* var_import_target;
+
+ // [string] target visibility
+ //
+ const variable* var_extension;
+
+ // [bool] target visibility
+ //
+ const variable* var_clean;
+
+ // Forwarded configuration backlink mode. Valid values are:
+ //
+ // false - no link.
+ // true - make a link using appropriate mechanism.
+ // symbolic - make a symbolic link.
+ // hard - make a hard link.
+ // copy - make a copy.
+ // overwrite - copy over but don't remove on clean (committed gen code).
+ //
+ // Note that it can be set by a matching rule as a rule-specific variable.
+ //
+ // [string] target visibility
+ //
+ const variable* var_backlink;
+
+ // Prerequisite inclusion/exclusion. Valid values are:
+ //
+ // false - exclude.
+ // true - include.
+ // adhoc - include but treat as an ad hoc input.
+ //
+ // If a rule uses prerequisites as inputs (as opposed to just matching
+ // them with the "pass-through" semantics), then the adhoc value signals
+ // that a prerequisite is an ad hoc input. A rule should match and execute
+ // such a prerequisite (whether its target type is recognized as suitable
+ // input or not) and assume that the rest will be handled by the user
+ // (e.g., it will be passed via a command line argument or some such).
+ // Note that this mechanism can be used to both treat unknown prerequisite
+ // types as inputs (for example, linker scripts) as well as prevent
+ // treatment of known prerequisite types as such while still matching and
+ // executing them (for example, plugin libraries).
+ //
+ // A rule with the "pass-through" semantics should treat the adhoc value
+ // the same as true.
+ //
+ // To query this value in rule implementations use the include() helpers
+ // from <libbuild2/prerequisites.hxx>.
+ //
+ // [string] prereq visibility
+ //
+ const variable* var_include;
+
+ // The build.* namespace.
+ //
+ // .meta_operation
+ //
+ const variable* var_build_meta_operation;
+
+ // Known meta-operation and operation tables.
+ //
+ build2::meta_operation_table meta_operation_table;
+ build2::operation_table operation_table;
+
+ // The old/new src_root remapping for subprojects.
+ //
+ dir_path old_src_root;
+ dir_path new_src_root;
+
+ public:
+ explicit
+ context (scheduler&,
+ const strings& cmd_vars = {},
+ bool dry_run = false,
+ bool keep_going = true);
+
+ // Set current meta-operation and operation.
+ //
+ void
+ current_meta_operation (const meta_operation_info&);
+
+ void
+ current_operation (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 ();
+ };
// Grab a new phase lock releasing it on destruction. The lock can be
// "owning" or "referencing" (recursive).
@@ -195,7 +458,7 @@ namespace build2
//
struct LIBBUILD2_SYMEXPORT phase_lock
{
- explicit phase_lock (run_phase);
+ explicit phase_lock (context&, run_phase);
~phase_lock ();
phase_lock (phase_lock&&) = delete;
@@ -204,7 +467,9 @@ namespace build2
phase_lock& operator= (phase_lock&&) = delete;
phase_lock& operator= (const phase_lock&) = delete;
- run_phase p;
+ context& ctx;
+ phase_lock* prev; // From another context.
+ run_phase phase;
};
// Assuming we have a lock on the current phase, temporarily release it
@@ -212,7 +477,7 @@ namespace build2
//
struct LIBBUILD2_SYMEXPORT phase_unlock
{
- phase_unlock (bool unlock = true);
+ phase_unlock (context&, bool unlock = true);
~phase_unlock () noexcept (false);
phase_lock* l;
@@ -223,10 +488,10 @@ namespace build2
//
struct LIBBUILD2_SYMEXPORT phase_switch
{
- explicit phase_switch (run_phase);
+ explicit phase_switch (context&, run_phase);
~phase_switch () noexcept (false);
- run_phase o, n;
+ run_phase old_phase, new_phase;
};
// Wait for a task count optionally and temporarily unlocking the phase.
@@ -237,11 +502,12 @@ namespace build2
wait_guard (); // Empty.
- explicit
- wait_guard (atomic_count& task_count,
+ wait_guard (context&,
+ atomic_count& task_count,
bool phase = false);
- wait_guard (size_t start_count,
+ wait_guard (context&,
+ size_t start_count,
atomic_count& task_count,
bool phase = false);
@@ -256,201 +522,23 @@ namespace build2
wait_guard (const wait_guard&) = delete;
wait_guard& operator= (const wait_guard&) = delete;
+ context* ctx;
size_t start_count;
atomic_count* task_count;
bool phase;
};
- // Current action (meta/operation).
- //
- // The names unlike info are available during boot but may not yet be
- // lifted. The name is always for an outer operation (or meta operation
- // that hasn't been recognized as such yet).
- //
- LIBBUILD2_SYMEXPORT extern string current_mname;
- LIBBUILD2_SYMEXPORT extern string current_oname;
-
- LIBBUILD2_SYMEXPORT extern const meta_operation_info* current_mif;
- LIBBUILD2_SYMEXPORT extern const operation_info* current_inner_oif;
- LIBBUILD2_SYMEXPORT extern const operation_info* current_outer_oif;
-
- // Current operation number (1-based) in the meta-operation batch.
- //
- LIBBUILD2_SYMEXPORT extern size_t current_on;
-
- LIBBUILD2_SYMEXPORT extern execution_mode current_mode;
-
- // Some diagnostics (for example output directory creation/removal by the
- // fsdir rule) is just noise at verbosity level 1 unless it is the only
- // thing that is printed. So we can only suppress it in certain situations
- // (e.g., dist) where we know we have already printed something.
- //
- LIBBUILD2_SYMEXPORT extern bool current_diag_noise;
-
- // Total number of dependency relationships and targets with non-noop
- // recipe in the current action.
- //
- // Together with target::dependents the dependency count is incremented
- // during the rule search & match phase and is decremented during execution
- // with the expectation of it reaching 0. Used as a sanity check.
- //
- // The target count is incremented after a non-noop recipe is matched and
- // decremented after such recipe has been executed. If such a recipe has
- // skipped executing the operation, then it should increment the skip count.
- // These two counters are used for progress monitoring and diagnostics.
- //
- LIBBUILD2_SYMEXPORT extern atomic_count dependency_count;
- 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
- // serially. In parallel we queue most of the things up before we see any
- // failures.
- //
- LIBBUILD2_SYMEXPORT extern bool keep_going;
-
- // Dry run flag (see --dry-run|-n).
- //
- // This flag is set only for the final execute phase (as opposed to those
- // that interrupt match) by the perform meta operation's execute() callback.
- //
- // Note that for this mode to function properly we have to use fake mtimes.
- // Specifically, a rule that pretends to update a target must set its mtime
- // to system_clock::now() and everyone else must use this cached value. In
- // other words, there should be no mtime re-query from the filesystem. The
- // same is required for "logical clean" (i.e., dry-run 'clean update' in
- // order to see all the command lines).
- //
- // At first, it may seem like we should also "dry-run" changes to depdb. But
- // that would be both problematic (some rules update it in apply() during
- // the match phase) and wasteful (why discard information). Also, depdb may
- // serve as an input to some commands (for example, to provide C++ module
- // mapping) which means that without updating it the commands we print might
- // not be runnable (think of the compilation database).
- //
- // One thing we need to be careful about if we are updating depdb is to not
- // render the target up-to-date. But in this case the depdb file will be
- // older than the target which in our model is treated as an interrupted
- // update (see depdb for details).
- //
- // Note also that sometimes it makes sense to do a bit more than absolutely
- // necessary or to discard information in order to keep the rule logic sane.
- // And some rules may choose to ignore this flag altogether. In this case,
- // however, the rule should be careful not to rely on functions (notably
- // from filesystem) that respect this flag in order not to end up with a
- // job half done.
- //
- 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,
const location&);
-
- // Cached variables.
- //
-
- // Note: consider printing in info meta-operation if adding anything here.
- //
- LIBBUILD2_SYMEXPORT extern const variable* var_src_root;
- LIBBUILD2_SYMEXPORT extern const variable* var_out_root;
- LIBBUILD2_SYMEXPORT extern const variable* var_src_base;
- LIBBUILD2_SYMEXPORT extern const variable* var_out_base;
- LIBBUILD2_SYMEXPORT extern const variable* var_forwarded;
-
- LIBBUILD2_SYMEXPORT extern const variable* var_project;
- LIBBUILD2_SYMEXPORT extern const variable* var_amalgamation;
- LIBBUILD2_SYMEXPORT extern const variable* var_subprojects;
- LIBBUILD2_SYMEXPORT extern const variable* var_version;
-
- // project.url
- //
- LIBBUILD2_SYMEXPORT extern const variable* var_project_url;
-
- // project.summary
- //
- LIBBUILD2_SYMEXPORT extern const variable* var_project_summary;
-
- // import.target
- //
- LIBBUILD2_SYMEXPORT extern const variable* var_import_target;
-
- // [bool] target visibility
- //
- LIBBUILD2_SYMEXPORT extern const variable* var_clean;
-
- // Forwarded configuration backlink mode. Valid values are:
- //
- // false - no link.
- // true - make a link using appropriate mechanism.
- // symbolic - make a symbolic link.
- // hard - make a hard link.
- // copy - make a copy.
- // overwrite - copy over but don't remove on clean (committed gen code).
- //
- // Note that it can be set by a matching rule as a rule-specific variable.
- //
- // [string] target visibility
- //
- LIBBUILD2_SYMEXPORT extern const variable* var_backlink;
-
- // Prerequisite inclusion/exclusion. Valid values are:
- //
- // false - exclude.
- // true - include.
- // adhoc - include but treat as an ad hoc input.
- //
- // If a rule uses prerequisites as inputs (as opposed to just matching them
- // with the "pass-through" semantics), then the adhoc value signals that a
- // prerequisite is an ad hoc input. A rule should match and execute such a
- // prerequisite (whether its target type is recognized as suitable input or
- // not) and assume that the rest will be handled by the user (e.g., it will
- // be passed via a command line argument or some such). Note that this
- // mechanism can be used to both treat unknown prerequisite types as inputs
- // (for example, linker scripts) as well as prevent treatment of known
- // prerequisite types as such while still matching and executing them (for
- // example, plugin libraries).
- //
- // A rule with the "pass-through" semantics should treat the adhoc value
- // the same as true.
- //
- // To query this value in rule implementations use the include() helpers
- // from <libbuild2/prerequisites.hxx>.
- //
- // [string] prereq visibility
- //
- LIBBUILD2_SYMEXPORT extern const variable* var_include;
-
- LIBBUILD2_SYMEXPORT extern const char var_extension[10]; // "extension"
-
- // The build.* namespace.
- //
- // .meta_operation
- //
- LIBBUILD2_SYMEXPORT extern const variable* var_build_meta_operation;
}
#include <libbuild2/context.ixx>
diff --git a/libbuild2/context.ixx b/libbuild2/context.ixx
index f947bd7..7fb85ad 100644
--- a/libbuild2/context.ixx
+++ b/libbuild2/context.ixx
@@ -8,19 +8,19 @@ namespace build2
//
inline wait_guard::
wait_guard ()
- : start_count (0), task_count (nullptr), phase (false)
+ : ctx (nullptr), start_count (0), task_count (nullptr), phase (false)
{
}
inline wait_guard::
- wait_guard (atomic_count& tc, bool p)
- : wait_guard (0, tc, p)
+ wait_guard (context& c, atomic_count& tc, bool p)
+ : wait_guard (c, 0, tc, p)
{
}
inline wait_guard::
- wait_guard (size_t sc, atomic_count& tc, bool p)
- : start_count (sc), task_count (&tc), phase (p)
+ wait_guard (context& c, size_t sc, atomic_count& tc, bool p)
+ : ctx (&c), start_count (sc), task_count (&tc), phase (p)
{
}
@@ -33,7 +33,10 @@ namespace build2
inline wait_guard::
wait_guard (wait_guard&& x)
- : start_count (x.start_count), task_count (x.task_count), phase (x.phase)
+ : ctx (x.ctx),
+ start_count (x.start_count),
+ task_count (x.task_count),
+ phase (x.phase)
{
x.task_count = nullptr;
}
@@ -44,6 +47,7 @@ namespace build2
if (&x != this)
{
assert (task_count == nullptr);
+ ctx = x.ctx;
start_count = x.start_count; task_count = x.task_count; phase = x.phase;
x.task_count = nullptr;
}
@@ -53,8 +57,8 @@ namespace build2
inline void wait_guard::
wait ()
{
- phase_unlock u (phase);
- sched.wait (start_count, *task_count);
+ phase_unlock u (*ctx, phase);
+ ctx->sched.wait (start_count, *task_count);
task_count = nullptr;
}
}
diff --git a/libbuild2/diagnostics.cxx b/libbuild2/diagnostics.cxx
index 3375e00..71f3d48 100644
--- a/libbuild2/diagnostics.cxx
+++ b/libbuild2/diagnostics.cxx
@@ -141,14 +141,14 @@ namespace build2
const fail_mark fail ("error");
const fail_end endf;
- // diag_do(), etc.
+ // diag_do(), etc.
//
string
- diag_do (const action&)
+ diag_do (context& ctx, const action&)
{
- const meta_operation_info& m (*current_mif);
- const operation_info& io (*current_inner_oif);
- const operation_info* oo (current_outer_oif);
+ const meta_operation_info& m (*ctx.current_mif);
+ const operation_info& io (*ctx.current_inner_oif);
+ const operation_info* oo (ctx.current_outer_oif);
string r;
@@ -181,15 +181,15 @@ namespace build2
void
diag_do (ostream& os, const action& a, const target& t)
{
- os << diag_do (a) << ' ' << t;
+ os << diag_do (t.ctx, a) << ' ' << t;
}
string
- diag_doing (const action&)
+ diag_doing (context& ctx, const action&)
{
- const meta_operation_info& m (*current_mif);
- const operation_info& io (*current_inner_oif);
- const operation_info* oo (current_outer_oif);
+ const meta_operation_info& m (*ctx.current_mif);
+ const operation_info& io (*ctx.current_inner_oif);
+ const operation_info* oo (ctx.current_outer_oif);
string r;
@@ -218,15 +218,15 @@ namespace build2
void
diag_doing (ostream& os, const action& a, const target& t)
{
- os << diag_doing (a) << ' ' << t;
+ os << diag_doing (t.ctx, a) << ' ' << t;
}
string
- diag_did (const action&)
+ diag_did (context& ctx, const action&)
{
- const meta_operation_info& m (*current_mif);
- const operation_info& io (*current_inner_oif);
- const operation_info* oo (current_outer_oif);
+ const meta_operation_info& m (*ctx.current_mif);
+ const operation_info& io (*ctx.current_inner_oif);
+ const operation_info* oo (ctx.current_outer_oif);
string r;
@@ -259,15 +259,15 @@ namespace build2
void
diag_did (ostream& os, const action& a, const target& t)
{
- os << diag_did (a) << ' ' << t;
+ os << diag_did (t.ctx, a) << ' ' << t;
}
void
diag_done (ostream& os, const action&, const target& t)
{
- const meta_operation_info& m (*current_mif);
- const operation_info& io (*current_inner_oif);
- const operation_info* oo (current_outer_oif);
+ const meta_operation_info& m (*t.ctx.current_mif);
+ const operation_info& io (*t.ctx.current_inner_oif);
+ const operation_info* oo (t.ctx.current_outer_oif);
// perform(update(x)) -> "x is up to date"
// configure(update(x)) -> "updating x is configured"
diff --git a/libbuild2/diagnostics.hxx b/libbuild2/diagnostics.hxx
index dbc8351..5d69132 100644
--- a/libbuild2/diagnostics.hxx
+++ b/libbuild2/diagnostics.hxx
@@ -439,6 +439,7 @@ namespace build2
//
class scope;
class target;
+ class context;
struct action;
struct diag_phrase
@@ -456,7 +457,7 @@ namespace build2
}
LIBBUILD2_SYMEXPORT string
- diag_do (const action&);
+ diag_do (context&, const action&);
LIBBUILD2_SYMEXPORT void
diag_do (ostream&, const action&, const target&);
@@ -468,7 +469,7 @@ namespace build2
}
LIBBUILD2_SYMEXPORT string
- diag_doing (const action&);
+ diag_doing (context&, const action&);
LIBBUILD2_SYMEXPORT void
diag_doing (ostream&, const action&, const target&);
@@ -480,7 +481,7 @@ namespace build2
}
LIBBUILD2_SYMEXPORT string
- diag_did (const action&);
+ diag_did (context&, const action&);
LIBBUILD2_SYMEXPORT void
diag_did (ostream&, const action&, const target&);
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..ad2ee7f 100644
--- a/libbuild2/dist/operation.cxx
+++ b/libbuild2/dist/operation.cxx
@@ -44,7 +44,8 @@ namespace build2
// Return the archive file path.
//
static path
- archive (const dir_path& root,
+ archive (context& ctx,
+ const dir_path& root,
const string& pkg,
const dir_path& dir,
const string& ext);
@@ -54,7 +55,8 @@ namespace build2
// Return the checksum file path.
//
static path
- checksum (const path& arc, const dir_path& dir, const string& ext);
+ checksum (context&,
+ const path& arc, const dir_path& dir, const string& ext);
static operation_id
dist_operation_pre (const values&, operation_id o)
@@ -79,6 +81,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 +171,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_operation (*poif, oif, false /* diag_noise */);
action a (dist_id, poif->id, oif->id);
match (params, a, ts,
1 /* diag (failures only) */,
@@ -175,7 +179,7 @@ namespace build2
}
}
- set_current_oif (*oif, nullptr, false /* diag_noise */);
+ ctx.current_operation (*oif, nullptr, false /* diag_noise */);
action a (dist_id, oif->id);
match (params, a, ts,
1 /* diag (failures only) */,
@@ -186,7 +190,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_operation (*poif, oif, false /* diag_noise */);
action a (dist_id, poif->id, oif->id);
match (params, a, ts,
1 /* diag (failures only) */,
@@ -213,7 +217,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 (),
@@ -226,13 +230,13 @@ namespace build2
// The same for subprojects that have been loaded.
//
- if (auto l = rs->vars[var_subprojects])
+ if (auto l = rs->vars[ctx.var_subprojects])
{
for (auto p: cast<subprojects> (l))
{
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 +255,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> ());
@@ -304,14 +308,14 @@ namespace build2
//
// Note also that we don't do any structured result printing.
//
- size_t on (current_on);
- set_current_mif (mo_perform);
- current_on = on + 1;
+ size_t on (ctx.current_on);
+ ctx.current_meta_operation (mo_perform);
+ ctx.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_operation (op_update, nullptr, false /* diag_noise */);
action a (perform_id, update_id);
@@ -334,7 +338,7 @@ namespace build2
// Clean up the target directory.
//
- if (build2::rmdir_r (td, true, 2) == rmdir_status::not_empty)
+ if (rmdir_r (ctx, td, true, 2) == rmdir_status::not_empty)
fail << "unable to clean target directory " << td;
auto_rmdir rm_td (td); // Clean it up if things go bad.
@@ -367,14 +371,14 @@ namespace build2
const scope* srs (rs);
const module::callbacks* cbs (&mod.callbacks_);
- if (auto l = rs->vars[var_subprojects])
+ if (auto l = rs->vars[ctx.var_subprojects])
{
for (auto p: cast<subprojects> (l))
{
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_;
@@ -467,14 +471,14 @@ namespace build2
for (const path& p: cast<paths> (as))
{
auto ap (split (p, dist_root, "dist.archives"));
- path a (archive (dist_root, dist_package, ap.first, ap.second));
+ path a (archive (ctx, dist_root, dist_package, ap.first, ap.second));
if (cs)
{
for (const path& p: cast<paths> (cs))
{
auto cp (split (p, ap.first, "dist.checksums"));
- checksum (a, cp.first, cp.second);
+ checksum (ctx, a, cp.first, cp.second);
}
}
}
@@ -541,7 +545,8 @@ namespace build2
}
static path
- archive (const dir_path& root,
+ archive (context& ctx,
+ const dir_path& root,
const string& pkg,
const dir_path& dir,
const string& e)
@@ -552,7 +557,7 @@ namespace build2
//
path ap (dir / an);
if (exists (ap, false))
- rmfile (ap);
+ rmfile (ctx, ap);
// Use zip for .zip archives. Also recognize and handle a few well-known
// tar.xx cases (in case tar doesn't support -a or has other issues like
@@ -713,7 +718,8 @@ namespace build2
}
static path
- checksum (const path& ap, const dir_path& dir, const string& e)
+ checksum (context& ctx,
+ const path& ap, const dir_path& dir, const string& e)
{
path an (ap.leaf ());
dir_path ad (ap.directory ());
@@ -724,7 +730,7 @@ namespace build2
//
path cp (dir / cn);
if (exists (cp, false))
- rmfile (cp);
+ rmfile (ctx, cp);
auto_rmfile c_rm; // Note: must come first.
auto_fd c_fd;
diff --git a/libbuild2/dump.cxx b/libbuild2/dump.cxx
index 7d59891..738ef36 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>
@@ -275,7 +276,7 @@ namespace build2
if (size_t c = t[inner].task_count.load (memory_order_relaxed))
{
- if (c == target::count_applied () || c == target::count_executed ())
+ if (c == t.ctx.count_applied () || c == t.ctx.count_executed ())
{
bool f (false);
for (const target* pt: t.prerequisite_targets[inner])
@@ -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..9140e59 100644
--- a/libbuild2/file.cxx
+++ b/libbuild2/file.cxx
@@ -146,20 +146,17 @@ namespace build2
return make_pair (dir_path (), false);
}
- dir_path old_src_root;
- dir_path new_src_root;
-
// Remap the src_root variable value if it is inside old_src_root.
//
static inline void
- remap_src_root (value& v)
+ remap_src_root (context& ctx, value& v)
{
- if (!old_src_root.empty ())
+ if (!ctx.old_src_root.empty ())
{
dir_path& d (cast<dir_path> (v));
- if (d.sub (old_src_root))
- d = new_src_root / d.leaf (old_src_root);
+ if (d.sub (ctx.old_src_root))
+ d = ctx.new_src_root / d.leaf (ctx.old_src_root);
}
}
@@ -183,7 +180,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,11 +260,13 @@ 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);
+ context& ctx (rs.ctx);
+
// Set out_path. Note that src_path is set in setup_root() below.
//
if (rs.out_path_ != &i->first)
@@ -279,7 +278,7 @@ namespace build2
// If this is already a root scope, verify that things are consistent.
//
{
- value& v (rs.assign (var_out_root));
+ value& v (rs.assign (ctx.var_out_root));
if (!v)
v = out_root;
@@ -295,7 +294,7 @@ namespace build2
if (!src_root.empty ())
{
- value& v (rs.assign (var_src_root));
+ value& v (rs.assign (ctx.var_src_root));
if (!v)
v = src_root;
@@ -315,9 +314,11 @@ namespace build2
void
setup_root (scope& s, bool forwarded)
{
+ context& ctx (s.ctx);
+
// The caller must have made sure src_root is set on this scope.
//
- value& v (s.assign (var_src_root));
+ value& v (s.assign (ctx.var_src_root));
assert (v);
const dir_path& d (cast<dir_path> (v));
@@ -326,7 +327,7 @@ namespace build2
else
assert (s.src_path_ == &d);
- s.assign (var_forwarded) = forwarded;
+ s.assign (ctx.var_forwarded) = forwarded;
}
scope&
@@ -335,17 +336,18 @@ namespace build2
const dir_path& src_base)
{
scope& s (i->second);
+ context& ctx (s.ctx);
// Set src/out_base variables.
//
- value& ov (s.assign (var_out_base));
+ value& ov (s.assign (ctx.var_out_base));
if (!ov)
ov = out_base;
else
assert (cast<dir_path> (ov) == out_base);
- value& sv (s.assign (var_src_base));
+ value& sv (s.assign (ctx.var_src_base));
if (!sv)
sv = src_base;
@@ -373,7 +375,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 +412,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 +422,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, *ctx.var_out_root));
if (!p.second)
fail << "variable out_root expected as first line in " << f;
@@ -495,7 +497,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 +515,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 +535,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 +547,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)
{
@@ -556,7 +559,7 @@ namespace build2
assert (*altn == s.root_extra->altn);
}
- if (lookup l = s.vars[var_project])
+ if (lookup l = s.vars[ctx.var_project])
return cast<project_name> (l);
src_root = s.src_path_;
@@ -588,13 +591,13 @@ namespace build2
}
else
{
- auto p (extract_variable (f, *var_src_root));
+ auto p (extract_variable (ctx, f, *ctx.var_src_root));
if (!p.second)
fail << "variable src_root expected as first line in " << f;
src_root_v = move (p.first);
- remap_src_root (src_root_v); // Remap if inside old_src_root.
+ remap_src_root (ctx, src_root_v); // Remap if inside old_src_root.
src_root = &cast<dir_path> (src_root_v);
l5 ([&]{trace << "extracted src_root " << *src_root
@@ -610,10 +613,10 @@ 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, *ctx.var_project));
if (!p.second)
- fail << "variable " << var_project->name << " expected "
+ fail << "variable " << ctx.var_project->name << " expected "
<< "as a first line in " << f;
name = cast<project_name> (move (p.first));
@@ -628,7 +631,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 +672,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 +712,28 @@ namespace build2
}
bool
- bootstrap_src (scope& root, optional<bool>& altn)
+ bootstrap_src (scope& rs, optional<bool>& altn)
{
tracer trace ("bootstrap_src");
+ context& ctx (rs.ctx);
+
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 +743,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 +764,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 (*ctx.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 +836,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 (*ctx.var_subprojects)); // Set NULL by default.
value& v (rp.first);
if (rp.second)
@@ -846,13 +853,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 +930,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);
@@ -983,13 +991,13 @@ namespace build2
}
bool
- bootstrapped (scope& root)
+ bootstrapped (scope& rs)
{
// Use the subprojects variable set by bootstrap_src() as an indicator.
// It should either be NULL or typed (so we assume that the user will
// never set it to NULL).
//
- auto l (root.vars[var_subprojects]);
+ auto l (rs.vars[rs.ctx.var_subprojects]);
return l.defined () && (l->null || l->type != nullptr);
}
@@ -1002,6 +1010,8 @@ namespace build2
const dir_path& src_root,
optional<bool>& altn)
{
+ context& ctx (orig.ctx);
+
// The conditions are:
//
// 1. Origin is itself forwarded.
@@ -1010,15 +1020,17 @@ namespace build2
//
// 3. Inner/outer out-root.build exists in src_root and refers out_root.
//
- return (out_root != src_root &&
- cast_false<bool> (orig.vars[var_forwarded]) &&
- bootstrap_fwd (src_root, altn) == out_root);
+ return (out_root != src_root &&
+ cast_false<bool> (orig.vars[ctx.var_forwarded]) &&
+ bootstrap_fwd (ctx, src_root, altn) == out_root);
}
void
create_bootstrap_outer (scope& root)
{
- auto l (root.vars[var_amalgamation]);
+ context& ctx (root.ctx);
+
+ auto l (root.vars[ctx.var_amalgamation]);
if (!l)
return;
@@ -1046,7 +1058,7 @@ namespace build2
{
bootstrap_out (rs, altn); // #3 happens here (or it can be #1).
- value& v (rs.assign (var_src_root));
+ value& v (rs.assign (ctx.var_src_root));
if (!v)
{
@@ -1060,7 +1072,7 @@ namespace build2
}
}
else
- remap_src_root (v); // Remap if inside old_src_root.
+ remap_src_root (ctx, v); // Remap if inside old_src_root.
setup_root (rs, forwarded (root, out_root, v.as<dir_path> (), altn));
bootstrap_pre (rs, altn);
@@ -1072,7 +1084,7 @@ namespace build2
altn = rs.root_extra->altn;
if (forwarded (root, rs.out_path (), rs.src_path (), altn))
- rs.assign (var_forwarded) = true; // Only upgrade (see main()).
+ rs.assign (ctx.var_forwarded) = true; // Only upgrade (see main()).
}
create_bootstrap_outer (rs);
@@ -1089,9 +1101,11 @@ namespace build2
scope&
create_bootstrap_inner (scope& root, const dir_path& out_base)
{
+ context& ctx (root.ctx);
+
scope* r (&root);
- if (auto l = root.vars[var_subprojects])
+ if (auto l = root.vars[ctx.var_subprojects])
{
for (const auto& p: cast<subprojects> (l))
{
@@ -1109,7 +1123,7 @@ namespace build2
{
bootstrap_out (rs, altn);
- value& v (rs.assign (var_src_root));
+ value& v (rs.assign (ctx.var_src_root));
if (!v)
{
@@ -1118,7 +1132,7 @@ namespace build2
: (root.src_path () / p.second);
}
else
- remap_src_root (v); // Remap if inside old_src_root.
+ remap_src_root (ctx, v); // Remap if inside old_src_root.
setup_root (rs, forwarded (root, out_root, v.as<dir_path> (), altn));
bootstrap_pre (rs, altn);
@@ -1129,7 +1143,7 @@ namespace build2
{
altn = rs.root_extra->altn;
if (forwarded (root, rs.out_path (), rs.src_path (), altn))
- rs.assign (var_forwarded) = true; // Only upgrade (see main()).
+ rs.assign (ctx.var_forwarded) = true; // Only upgrade (see main()).
}
// Check if we strongly amalgamated this inner root scope.
@@ -1205,7 +1219,7 @@ namespace build2
}
scope&
- load_project (scope& lock,
+ load_project (scope& s,
const dir_path& out_root,
const dir_path& src_root,
bool forwarded,
@@ -1213,7 +1227,9 @@ namespace build2
{
assert (!forwarded || out_root != src_root);
- auto i (create_root (lock, out_root, src_root));
+ context& ctx (s.ctx);
+
+ auto i (create_root (s, out_root, src_root));
scope& rs (i->second);
if (!bootstrapped (rs))
@@ -1228,7 +1244,7 @@ namespace build2
else
{
if (forwarded)
- rs.assign (var_forwarded) = true; // Only upgrade (see main()).
+ rs.assign (ctx.var_forwarded) = true; // Only upgrade (see main()).
}
if (load)
@@ -1257,6 +1273,8 @@ namespace build2
return names {move (target)};
}
+ context& ctx (ibase.ctx);
+
// Otherwise, get the project name and convert the target to unqualified.
//
project_name proj (move (*target.proj));
@@ -1273,7 +1291,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.
{
@@ -1375,13 +1393,14 @@ namespace build2
// First check the amalgamation itself.
//
- if (r != &iroot && cast<project_name> (r->vars[var_project]) == proj)
+ if (r != &iroot &&
+ cast<project_name> (r->vars[ctx.var_project]) == proj)
{
out_root = r->out_path ();
break;
}
- if (auto l = r->vars[var_subprojects])
+ if (auto l = r->vars[ctx.var_subprojects])
{
const auto& m (cast<subprojects> (l));
auto i (m.find (proj));
@@ -1394,7 +1413,7 @@ namespace build2
}
}
- if (!r->vars[var_amalgamation])
+ if (!r->vars[ctx.var_amalgamation])
break;
}
@@ -1429,7 +1448,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);
}
@@ -1447,7 +1466,7 @@ namespace build2
// Check that the bootstrap process set src_root.
//
- auto l (root->vars[*var_src_root]);
+ auto l (root->vars[*ctx.var_src_root]);
if (l)
{
// Note that unlike main() here we fail hard. The idea is that if
@@ -1482,7 +1501,7 @@ namespace build2
src_root = root->src_path ();
if (top ? fwd : forwarded (*proot, out_root, src_root, altn))
- root->assign (var_forwarded) = true; // Only upgrade (see main()).
+ root->assign (ctx.var_forwarded) = true; // Only upgrade (see main()).
}
if (top)
@@ -1495,10 +1514,10 @@ namespace build2
// Now we know this project's name as well as all its subprojects.
//
- if (cast<project_name> (root->vars[var_project]) == proj)
+ if (cast<project_name> (root->vars[ctx.var_project]) == proj)
break;
- if (auto l = root->vars[var_subprojects])
+ if (auto l = root->vars[ctx.var_subprojects])
{
const auto& m (cast<subprojects> (l));
auto i (m.find (proj));
@@ -1527,13 +1546,13 @@ namespace build2
// "Pass" the imported project's roots to the stub.
//
- ts.assign (var_out_root) = move (out_root);
- ts.assign (var_src_root) = move (src_root);
+ ts.assign (ctx.var_out_root) = move (out_root);
+ ts.assign (ctx.var_src_root) = move (src_root);
// Also pass the target being imported in the import.target variable.
//
{
- value& v (ts.assign (var_import_target));
+ value& v (ts.assign (ctx.var_import_target));
if (!target.empty ()) // Otherwise leave NULL.
v = target; // Can't move (need for diagnostics below).
@@ -1556,7 +1575,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 +1594,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 +1629,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..aaa0fa0 100644
--- a/libbuild2/file.hxx
+++ b/libbuild2/file.hxx
@@ -69,11 +69,6 @@ namespace build2
LIBBUILD2_SYMEXPORT pair<dir_path, bool>
find_out_root (const dir_path&, optional<bool>& altn);
- // The old/new src_root paths. See main() (where they are set) for details.
- //
- LIBBUILD2_SYMEXPORT extern dir_path old_src_root;
- LIBBUILD2_SYMEXPORT extern dir_path new_src_root;
-
// If buildfile is '-', then read from STDIN.
//
LIBBUILD2_SYMEXPORT void
@@ -91,8 +86,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 +135,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 +196,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 +219,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 +227,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..564fc11 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);
+ assert (ctx.phase == run_phase::match);
+ 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);
+ assert (ctx.phase == run_phase::match || ctx.phase == run_phase::execute);
+ return import (ctx, pk, true);
}
}
diff --git a/libbuild2/filesystem.cxx b/libbuild2/filesystem.cxx
index 83408fa..1cbaa58 100644
--- a/libbuild2/filesystem.cxx
+++ b/libbuild2/filesystem.cxx
@@ -13,12 +13,12 @@ using namespace butl;
namespace build2
{
void
- touch (const path& p, bool create, uint16_t v)
+ touch (context& ctx, const path& p, bool create, uint16_t v)
{
if (verb >= v)
text << "touch " << p;
- if (dry_run)
+ if (ctx.dry_run)
return;
try
@@ -104,7 +104,7 @@ namespace build2
}
fs_status<rmfile_status>
- rmsymlink (const path& p, bool d, uint16_t v)
+ rmsymlink (context& ctx, const path& p, bool d, uint16_t v)
{
auto print = [&p, v] ()
{
@@ -116,7 +116,7 @@ namespace build2
try
{
- rs = dry_run
+ rs = ctx.dry_run
? (butl::entry_exists (p)
? rmfile_status::success
: rmfile_status::not_exist)
@@ -135,7 +135,7 @@ namespace build2
}
fs_status<butl::rmdir_status>
- rmdir_r (const dir_path& d, bool dir, uint16_t v)
+ rmdir_r (context& ctx, const dir_path& d, bool dir, uint16_t v)
{
using namespace butl;
@@ -148,7 +148,7 @@ namespace build2
if (verb >= v)
text << "rmdir -r " << d;
- if (!dry_run)
+ if (!ctx.dry_run)
{
try
{
@@ -216,7 +216,10 @@ namespace build2
}
fs_status<mkdir_status>
- mkdir_buildignore (const dir_path& d, const path& n, uint16_t verbosity)
+ mkdir_buildignore (context& ctx,
+ const dir_path& d,
+ const path& n,
+ uint16_t verbosity)
{
fs_status<mkdir_status> r (mkdir (d, verbosity));
@@ -225,7 +228,7 @@ namespace build2
//
path p (d / n);
if (r || !exists (p))
- touch (p, true /* create */, verbosity);
+ touch (ctx, p, true /* create */, verbosity);
return r;
}
@@ -253,7 +256,10 @@ namespace build2
}
fs_status<rmdir_status>
- rmdir_buildignore (const dir_path& d, const path& n, uint16_t verbosity)
+ rmdir_buildignore (context& ctx,
+ const dir_path& d,
+ const path& n,
+ uint16_t verbosity)
{
// We should remove the .buildignore file only if the subsequent rmdir()
// will succeed. In other words if the directory stays after the function
@@ -263,12 +269,12 @@ namespace build2
//
path p (d / n);
if (exists (p) && empty_buildignore (d, n) && !work.sub (d))
- rmfile (p, verbosity);
+ rmfile (ctx, p, verbosity);
// Note that in case of a system error the directory is likely to stay with
// the .buildignore file already removed. Trying to restore it feels like
// an overkill here.
//
- return rmdir (d, verbosity);
+ return rmdir (ctx, d, verbosity);
}
}
diff --git a/libbuild2/filesystem.hxx b/libbuild2/filesystem.hxx
index 6dca528..e7b3094 100644
--- a/libbuild2/filesystem.hxx
+++ b/libbuild2/filesystem.hxx
@@ -10,6 +10,8 @@
#include <libbuild2/types.hxx>
#include <libbuild2/utility.hxx>
+#include <libbuild2/context.hxx>
+
#include <libbuild2/export.hxx>
// Higher-level filesystem utilities built on top of <libbutl/filesystem.mxx>.
@@ -43,7 +45,7 @@ namespace build2
// create it and fail otherwise.
//
LIBBUILD2_SYMEXPORT void
- touch (const path&, bool create, uint16_t verbosity = 1);
+ touch (context&, const path&, bool create, uint16_t verbosity = 1);
// Return the modification time for an existing regular file and
// timestamp_nonexistent otherwise. Print the diagnostics and fail on system
@@ -88,22 +90,19 @@ namespace build2
fs_status<rmfile_status>
rmfile (const path&, const T& target, uint16_t verbosity = 1);
- inline fs_status<rmfile_status>
- rmfile (const path& f, int verbosity = 1) // Literal overload (int).
- {
- return rmfile (f, f, static_cast<uint16_t> (verbosity));
- }
+ fs_status<rmfile_status>
+ rmfile (context&, const path&, uint16_t verbosity = 1);
- inline fs_status<rmfile_status>
- rmfile (const path& f, uint16_t verbosity) // Overload (verb_never).
- {
- return rmfile (f, f, verbosity);
- }
+ fs_status<rmfile_status>
+ rmfile (const path&, int = 1) = delete;
+
+ fs_status<rmfile_status>
+ rmfile (const path&, uint16_t) = delete;
// Similar to rmfile() but for symlinks.
//
LIBBUILD2_SYMEXPORT fs_status<rmfile_status>
- rmsymlink (const path&, bool dir, uint16_t verbosity);
+ rmsymlink (context&, const path&, bool dir, uint16_t verbosity);
// Similar to rmfile() but for directories (note: not -r).
//
@@ -113,27 +112,26 @@ namespace build2
fs_status<rmdir_status>
rmdir (const dir_path&, const T& target, uint16_t verbosity = 1);
- inline fs_status<rmdir_status>
- rmdir (const dir_path& d, int verbosity = 1) // Literal overload (int).
- {
- return rmdir (d, d, static_cast<uint16_t> (verbosity));
- }
+ fs_status<rmdir_status>
+ rmdir (context&, const dir_path&, uint16_t verbosity = 1);
- inline fs_status<rmdir_status>
- rmdir (const dir_path& d, uint16_t verbosity) // Overload (verb_never).
- {
- return rmdir (d, d, verbosity);
- }
+ fs_status<rmdir_status>
+ rmdir (const dir_path&, int = 1) = delete;
+
+ fs_status<rmdir_status>
+ rmdir (const dir_path&, uint16_t) = delete;
// Remove the directory recursively (unless dry-run) and print the standard
// diagnostics starting from the specified verbosity level. Note that this
// function returns not_empty if we try to remove a working directory. If
// the dir argument is false, then the directory itself is not removed.
//
- // @@ Collides (via ADL) with butl::rmdir_r(), which sucks.
- //
LIBBUILD2_SYMEXPORT fs_status<rmdir_status>
- rmdir_r (const dir_path&, bool dir = true, uint16_t verbosity = 1);
+ rmdir_r (context& ctx,
+ const dir_path&, bool dir = true, uint16_t verbosity = 1);
+
+ fs_status<rmdir_status>
+ rmdir_r (const dir_path&, bool = true, uint16_t = 1) = delete;
// Check for a file, directory or filesystem entry existence. Print the
// diagnostics and fail on system error, unless ignore_error is true.
@@ -163,7 +161,8 @@ namespace build2
// Create a directory containing an empty .buildignore file.
//
LIBBUILD2_SYMEXPORT fs_status<mkdir_status>
- mkdir_buildignore (const dir_path&, const path&, uint16_t verbosity = 1);
+ mkdir_buildignore (context&,
+ const dir_path&, const path&, uint16_t verbosity = 1);
// Return true if the directory is empty or only contains the .buildignore
// file. Fail if the directory doesn't exist.
@@ -174,9 +173,11 @@ namespace build2
// Remove a directory if it is empty or only contains the .buildignore file.
//
LIBBUILD2_SYMEXPORT fs_status<rmdir_status>
- rmdir_buildignore (const dir_path&, const path&, uint16_t verbosity = 1);
+ rmdir_buildignore (context&,
+ const dir_path&, const path&, uint16_t verbosity = 1);
}
+#include <libbuild2/filesystem.ixx>
#include <libbuild2/filesystem.txx>
#endif // LIBBUILD2_FILESYSTEM_HXX
diff --git a/libbuild2/filesystem.ixx b/libbuild2/filesystem.ixx
new file mode 100644
index 0000000..6dab3ad
--- /dev/null
+++ b/libbuild2/filesystem.ixx
@@ -0,0 +1,40 @@
+// file : libbuild2/filesystem.ixx -*- C++ -*-
+// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+namespace build2
+{
+ template <typename T>
+ fs_status<butl::rmfile_status>
+ rmfile (context&, const path&, const T&, uint16_t);
+
+ template <typename T>
+ inline fs_status<rmfile_status>
+ rmfile (const path& f, const T& t, uint16_t v)
+ {
+ return rmfile (t.ctx, f, t, v);
+ }
+
+ inline fs_status<rmfile_status>
+ rmfile (context& ctx, const path& f, uint16_t v)
+ {
+ return rmfile (ctx, f, f, v);
+ }
+
+ template <typename T>
+ fs_status<rmdir_status>
+ rmdir (context&, const dir_path&, const T&, uint16_t);
+
+ template <typename T>
+ inline fs_status<rmdir_status>
+ rmdir (const dir_path& d, const T& t, uint16_t v)
+ {
+ return rmdir (t.ctx, d, t, v);
+ }
+
+ inline fs_status<rmdir_status>
+ rmdir (context& ctx, const dir_path& d, uint16_t v)
+ {
+ return rmdir (ctx, d, d, v);
+ }
+}
diff --git a/libbuild2/filesystem.txx b/libbuild2/filesystem.txx
index 057975a..e3cdef3 100644
--- a/libbuild2/filesystem.txx
+++ b/libbuild2/filesystem.txx
@@ -4,14 +4,13 @@
#include <type_traits> // is_base_of
-#include <libbuild2/context.hxx> // dry_run
#include <libbuild2/diagnostics.hxx>
namespace build2
{
template <typename T>
fs_status<butl::rmfile_status>
- rmfile (const path& f, const T& t, uint16_t v)
+ rmfile (context& ctx, const path& f, const T& t, uint16_t v)
{
using namespace butl;
@@ -34,7 +33,7 @@ namespace build2
try
{
- rs = dry_run
+ rs = ctx.dry_run
? file_exists (f) ? rmfile_status::success : rmfile_status::not_exist
: try_rmfile (f);
}
@@ -52,7 +51,7 @@ namespace build2
template <typename T>
fs_status<butl::rmdir_status>
- rmdir (const dir_path& d, const T& t, uint16_t v)
+ rmdir (context& ctx, const dir_path& d, const T& t, uint16_t v)
{
using namespace butl;
@@ -75,7 +74,7 @@ namespace build2
rmdir_status rs;
try
{
- rs = dry_run
+ rs = ctx.dry_run
? dir_exists (d) ? rmdir_status::success : rmdir_status::not_exist
: !(w = work.sub (d)) ? try_rmdir (d) : rmdir_status::not_empty;
}
diff --git a/libbuild2/function.cxx b/libbuild2/function.cxx
index 2d4dce9..ebf880a 100644
--- a/libbuild2/function.cxx
+++ b/libbuild2/function.cxx
@@ -275,7 +275,7 @@ namespace build2
{
size_t n (name.size ());
- for (auto i (functions.begin ()); i != functions.end (); ++i)
+ for (auto i (begin ()); i != end (); ++i)
{
const string& q (i->first);
const function_overload& f (i->second);
@@ -352,12 +352,12 @@ namespace build2
n.insert (0, qual);
}
- auto i (qn.empty () ? functions.end () : functions.insert (move (qn), f));
- auto j (functions.insert (move (n), move (f)));
+ auto i (qn.empty () ? map_.end () : map_.insert (move (qn), f));
+ auto j (map_.insert (move (n), move (f)));
// If we have both, then set alternative names.
//
- if (i != functions.end ())
+ if (i != map_.end ())
{
i->second.alt_name = j->first.c_str ();
j->second.alt_name = i->first.c_str ();
@@ -366,35 +366,30 @@ namespace build2
// Static-initialize the function map and populate with builtin functions.
//
- function_map functions;
-
- void builtin_functions (); // functions-builtin.cxx
- void filesystem_functions (); // functions-filesystem.cxx
- void name_functions (); // functions-name.cxx
- void path_functions (); // functions-path.cxx
- void process_functions (); // functions-process.cxx
- void process_path_functions (); // functions-process-path.cxx
- void regex_functions (); // functions-regex.cxx
- void string_functions (); // functions-string.cxx
- void target_triplet_functions (); // functions-target-triplet.cxx
- void project_name_functions (); // functions-target-triplet.cxx
-
- struct functions_init
- {
- functions_init ()
- {
- builtin_functions ();
- filesystem_functions ();
- name_functions ();
- path_functions ();
- process_functions ();
- process_path_functions ();
- regex_functions ();
- string_functions ();
- target_triplet_functions ();
- project_name_functions ();
- }
- };
- static const functions_init init_;
+ void builtin_functions (function_map&); // functions-builtin.cxx
+ void filesystem_functions (function_map&); // functions-filesystem.cxx
+ void name_functions (function_map&); // functions-name.cxx
+ void path_functions (function_map&); // functions-path.cxx
+ void process_functions (function_map&); // functions-process.cxx
+ void process_path_functions (function_map&); // functions-process-path.cxx
+ void regex_functions (function_map&); // functions-regex.cxx
+ void string_functions (function_map&); // functions-string.cxx
+ void target_triplet_functions (function_map&); // functions-target-triplet.cxx
+ void project_name_functions (function_map&); // functions-target-triplet.cxx
+
+ void
+ register_builtin_functions (function_map& m)
+ {
+ builtin_functions (m);
+ filesystem_functions (m);
+ name_functions (m);
+ path_functions (m);
+ process_functions (m);
+ process_path_functions (m);
+ regex_functions (m);
+ string_functions (m);
+ target_triplet_functions (m);
+ project_name_functions (m);
+ }
}
diff --git a/libbuild2/function.hxx b/libbuild2/function.hxx
index 51c17c0..bb3fe3a 100644
--- a/libbuild2/function.hxx
+++ b/libbuild2/function.hxx
@@ -216,7 +216,8 @@ namespace build2
map_type map_;
};
- LIBBUILD2_SYMEXPORT extern function_map functions;
+ LIBBUILD2_SYMEXPORT void
+ register_builtin_functions (function_map&);
class LIBBUILD2_SYMEXPORT function_family
{
@@ -237,9 +238,10 @@ namespace build2
// containing a leading dot is a shortcut notation for a qualified-only
// name.
//
- explicit
- function_family (string qual, function_impl* thunk = &default_thunk)
- : qual_ (qual), thunk_ (thunk) {}
+ function_family (function_map& map,
+ string qual,
+ function_impl* thunk = &default_thunk)
+ : map_ (map), qual_ (move (qual)), thunk_ (thunk) {}
struct entry;
@@ -247,13 +249,14 @@ namespace build2
operator[] (string name) const;
static bool
- defined (string qual)
+ defined (function_map& map, string qual)
{
qual += '.';
- return functions.defined (qual);
+ return map.defined (qual);
}
private:
+ function_map& map_;
const string qual_;
function_impl* thunk_;
};
@@ -744,6 +747,7 @@ namespace build2
struct LIBBUILD2_SYMEXPORT function_family::entry
{
+ function_map& map_;
string name;
const string& qual;
function_impl* thunk;
@@ -898,7 +902,7 @@ namespace build2
inline auto function_family::
operator[] (string name) const -> entry
{
- return entry {move (name), qual_, thunk_};
+ return entry {map_, move (name), qual_, thunk_};
}
}
diff --git a/libbuild2/function.test.cxx b/libbuild2/function.test.cxx
index 2380987..bd0be62 100644
--- a/libbuild2/function.test.cxx
+++ b/libbuild2/function.test.cxx
@@ -10,6 +10,7 @@
#include <libbuild2/scope.hxx>
#include <libbuild2/parser.hxx>
#include <libbuild2/context.hxx>
+#include <libbuild2/scheduler.hxx>
#include <libbuild2/function.hxx>
#include <libbuild2/variable.hxx>
#include <libbuild2/diagnostics.hxx>
@@ -41,9 +42,12 @@ namespace build2
//
init_diag (1);
init (nullptr, argv[0]);
- reset (strings ()); // No command line variables.
+ scheduler sched (1); // Serial execution.
+ context ctx (sched);
- function_family f ("dummy");
+ auto& functions (ctx.functions);
+
+ function_family f (functions, "dummy");
f["fail"] = []() {fail << "failed" << endf;};
f["fail_arg"] = [](names a) {return convert<uint64_t> (move (a[0]));};
@@ -114,9 +118,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/functions-builtin.cxx b/libbuild2/functions-builtin.cxx
index 44ae534..2acd5b4 100644
--- a/libbuild2/functions-builtin.cxx
+++ b/libbuild2/functions-builtin.cxx
@@ -24,9 +24,9 @@ namespace build2
}
void
- builtin_functions ()
+ builtin_functions (function_map& m)
{
- function_family f ("builtin");
+ function_family f (m, "builtin");
f["type"] = [](value* v) {return v->type != nullptr ? v->type->name : "";};
diff --git a/libbuild2/functions-filesystem.cxx b/libbuild2/functions-filesystem.cxx
index d98c75d..2fcd305 100644
--- a/libbuild2/functions-filesystem.cxx
+++ b/libbuild2/functions-filesystem.cxx
@@ -121,9 +121,9 @@ namespace build2
}
void
- filesystem_functions ()
+ filesystem_functions (function_map& m)
{
- function_family f ("filesystem");
+ function_family f (m, "filesystem");
// path_search
//
diff --git a/libbuild2/functions-name.cxx b/libbuild2/functions-name.cxx
index a8e08b6..8013a0c 100644
--- a/libbuild2/functions-name.cxx
+++ b/libbuild2/functions-name.cxx
@@ -33,9 +33,9 @@ namespace build2
}
void
- name_functions ()
+ name_functions (function_map& m)
{
- function_family f ("name");
+ function_family f (m, "name");
// These functions treat a name as a target/prerequisite name.
//
@@ -97,7 +97,7 @@ namespace build2
// Name-specific overloads from builtins.
//
- function_family b ("builtin");
+ function_family b (m, "builtin");
b[".concat"] = [](dir_path d, name n)
{
diff --git a/libbuild2/functions-path.cxx b/libbuild2/functions-path.cxx
index 6e39812..03f9be3 100644
--- a/libbuild2/functions-path.cxx
+++ b/libbuild2/functions-path.cxx
@@ -96,9 +96,9 @@ namespace build2
}
void
- path_functions ()
+ path_functions (function_map& m)
{
- function_family f ("path", &path_thunk);
+ function_family f (m, "path", &path_thunk);
// string
//
@@ -343,7 +343,7 @@ namespace build2
// Path-specific overloads from builtins.
//
- function_family b ("builtin", &path_thunk);
+ function_family b (m, "builtin", &path_thunk);
b[".concat"] = &concat_path_string;
b[".concat"] = &concat_dir_path_string;
diff --git a/libbuild2/functions-process-path.cxx b/libbuild2/functions-process-path.cxx
index 65e426b..124bd55 100644
--- a/libbuild2/functions-process-path.cxx
+++ b/libbuild2/functions-process-path.cxx
@@ -10,9 +10,9 @@ using namespace std;
namespace build2
{
void
- process_path_functions ()
+ process_path_functions (function_map& m)
{
- function_family f ("process_path");
+ function_family f (m, "process_path");
// As discussed in value_traits<process_path>, we always have recall.
//
diff --git a/libbuild2/functions-process.cxx b/libbuild2/functions-process.cxx
index 83188d3..2cc3385 100644
--- a/libbuild2/functions-process.cxx
+++ b/libbuild2/functions-process.cxx
@@ -191,9 +191,9 @@ namespace build2
}
void
- process_functions ()
+ process_functions (function_map& m)
{
- function_family f ("process");
+ function_family f (m, "process");
// $process.run(<prog>[ <args>...])
//
diff --git a/libbuild2/functions-project-name.cxx b/libbuild2/functions-project-name.cxx
index 163e865..f70a1e7 100644
--- a/libbuild2/functions-project-name.cxx
+++ b/libbuild2/functions-project-name.cxx
@@ -10,9 +10,9 @@ using namespace std;
namespace build2
{
void
- project_name_functions ()
+ project_name_functions (function_map& m)
{
- function_family f ("project_name");
+ function_family f (m, "project_name");
f["string"] = [](project_name p) {return move (p).string ();};
@@ -31,7 +31,7 @@ namespace build2
// Project name-specific overloads from builtins.
//
- function_family b ("builtin");
+ function_family b (m, "builtin");
b[".concat"] = [](project_name n, string s)
{
diff --git a/libbuild2/functions-regex.cxx b/libbuild2/functions-regex.cxx
index 2c478fe..339224e 100644
--- a/libbuild2/functions-regex.cxx
+++ b/libbuild2/functions-regex.cxx
@@ -339,9 +339,9 @@ namespace build2
}
void
- regex_functions ()
+ regex_functions (function_map& m)
{
- function_family f ("regex");
+ function_family f (m, "regex");
// $regex.match(<val>, <pat> [, <flags>])
//
diff --git a/libbuild2/functions-string.cxx b/libbuild2/functions-string.cxx
index 22860cb..1e93943 100644
--- a/libbuild2/functions-string.cxx
+++ b/libbuild2/functions-string.cxx
@@ -10,9 +10,9 @@ using namespace std;
namespace build2
{
void
- string_functions ()
+ string_functions (function_map& m)
{
- function_family f ("string");
+ function_family f (m, "string");
f["string"] = [](string s) {return s;};
@@ -23,7 +23,7 @@ namespace build2
// String-specific overloads from builtins.
//
- function_family b ("builtin");
+ function_family b (m, "builtin");
b[".concat"] = [](string l, string r) {l += r; return l;};
diff --git a/libbuild2/functions-target-triplet.cxx b/libbuild2/functions-target-triplet.cxx
index 4394c5a..9ae2514 100644
--- a/libbuild2/functions-target-triplet.cxx
+++ b/libbuild2/functions-target-triplet.cxx
@@ -10,15 +10,15 @@ using namespace std;
namespace build2
{
void
- target_triplet_functions ()
+ target_triplet_functions (function_map& m)
{
- function_family f ("target_triplet");
+ function_family f (m, "target_triplet");
f["string"] = [](target_triplet t) {return t.string ();};
// Target triplet-specific overloads from builtins.
//
- function_family b ("builtin");
+ function_family b (m, "builtin");
b[".concat"] = [](target_triplet l, string sr) {return l.string () + sr;};
b[".concat"] = [](string sl, target_triplet r) {return sl + r.string ();};
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/in/rule.cxx b/libbuild2/in/rule.cxx
index 434250e..de7ad88 100644
--- a/libbuild2/in/rule.cxx
+++ b/libbuild2/in/rule.cxx
@@ -435,10 +435,10 @@ namespace build2
return convert<string> (
v.type == nullptr
? move (v)
- : functions.call (&t.base_scope (),
- "string",
- vector_view<value> (&v, 1),
- l));
+ : t.ctx.functions.call (&t.base_scope (),
+ "string",
+ vector_view<value> (&v, 1),
+ l));
}
catch (const invalid_argument& e)
{
diff --git a/libbuild2/install/functions.cxx b/libbuild2/install/functions.cxx
index f067918..6052dd9 100644
--- a/libbuild2/install/functions.cxx
+++ b/libbuild2/install/functions.cxx
@@ -14,9 +14,9 @@ namespace build2
namespace install
{
void
- functions ()
+ functions (function_map& m)
{
- function_family f ("install");
+ function_family f (m, "install");
// Resolve potentially relative install.* value to an absolute directory
// based on (other) install.* values visible from the calling scope.
diff --git a/libbuild2/install/init.cxx b/libbuild2/install/init.cxx
index 060007b..d2321b5 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,11 +122,12 @@ 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
- functions (); // functions.cxx
+ functions (function_map&); // functions.cxx
bool
boot (scope& rs, const location&, unique_ptr<module_base>&)
@@ -134,11 +135,13 @@ namespace build2
tracer trace ("install::boot");
l5 ([&]{trace << "for " << rs;});
+ context& ctx (rs.ctx);
+
// Register install function family if this is the first instance of the
// install modules.
//
- if (!function_family::defined ("install"))
- functions ();
+ if (!function_family::defined (ctx.functions, "install"))
+ functions (ctx.functions);
// Register our operations.
//
@@ -192,7 +195,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/operation.cxx b/libbuild2/install/operation.cxx
index 1135ad6..a2ad7d0 100644
--- a/libbuild2/install/operation.cxx
+++ b/libbuild2/install/operation.cxx
@@ -4,6 +4,8 @@
#include <libbuild2/install/operation.hxx>
+#include <libbuild2/variable.hxx>
+
using namespace std;
using namespace butl;
diff --git a/libbuild2/install/rule.cxx b/libbuild2/install/rule.cxx
index 48a404b..7cee10e 100644
--- a/libbuild2/install/rule.cxx
+++ b/libbuild2/install/rule.cxx
@@ -698,6 +698,8 @@ namespace build2
const dir_path& d,
bool verbose = true)
{
+ context& ctx (rs.ctx);
+
// Here is the problem: if this is a dry-run, then we will keep showing
// the same directory creation commands over and over again (because we
// don't actually create them). There are two alternative ways to solve
@@ -708,7 +710,7 @@ namespace build2
// with uninstall since the directories won't be empty (because we don't
// actually uninstall any files).
//
- if (dry_run)
+ if (ctx.dry_run)
return;
dir_path chd (chroot_path (rs, d));
@@ -741,7 +743,7 @@ namespace build2
cstrings args;
string reld (
- cast<string> ((*global_scope)["build.host.class"]) == "windows"
+ cast<string> (ctx.global_scope["build.host.class"]) == "windows"
? msys_path (chd)
: relative (chd).string ());
@@ -780,12 +782,14 @@ namespace build2
const path& f,
bool verbose)
{
+ context& ctx (rs.ctx);
+
path relf (relative (f));
dir_path chd (chroot_path (rs, base.dir));
string reld (
- cast<string> ((*global_scope)["build.host.class"]) == "windows"
+ cast<string> (ctx.global_scope["build.host.class"]) == "windows"
? msys_path (chd)
: relative (chd).string ());
@@ -818,7 +822,7 @@ namespace build2
else if (verb && verbose)
text << "install " << t;
- if (!dry_run)
+ if (!ctx.dry_run)
run (pp, args);
}
@@ -829,6 +833,8 @@ namespace build2
const path& link,
uint16_t verbosity)
{
+ context& ctx (rs.ctx);
+
path rell (relative (chroot_path (rs, base.dir)));
rell /= link;
@@ -859,7 +865,7 @@ namespace build2
text << "install " << rell << " -> " << target;
}
- if (!dry_run)
+ if (!ctx.dry_run)
run (pp, args);
#else
// The -f part.
@@ -877,7 +883,7 @@ namespace build2
text << "install " << rell << " -> " << target;
}
- if (!dry_run)
+ if (!ctx.dry_run)
try
{
// We have to go the roundabout way by adding directory to the target
@@ -1014,7 +1020,7 @@ namespace build2
{
// See install_d() for the rationale.
//
- if (dry_run)
+ if (rs.ctx.dry_run)
return false;
dir_path chd (chroot_path (rs, d));
@@ -1150,7 +1156,7 @@ namespace build2
if (verb >= verbosity && verb >= 2)
text << "rm " << relf;
- if (!dry_run)
+ if (!rs.ctx.dry_run)
{
try
{
@@ -1179,7 +1185,7 @@ namespace build2
if (verb >= verbosity && verb >= 2)
print_process (args);
- if (!dry_run)
+ if (!rs.ctx.dry_run)
run (pp, args);
}
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 b06b030..ff70de6 100644
--- a/libbuild2/module.cxx
+++ b/libbuild2/module.cxx
@@ -293,7 +293,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
@@ -344,7 +344,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..0bf87d5 100644
--- a/libbuild2/operation.cxx
+++ b/libbuild2/operation.cxx
@@ -10,6 +10,7 @@
#include <libbuild2/scope.hxx>
#include <libbuild2/target.hxx>
#include <libbuild2/context.hxx>
+#include <libbuild2/variable.hxx>
#include <libbuild2/algorithm.hxx>
#include <libbuild2/diagnostics.hxx>
@@ -81,7 +82,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.
@@ -101,9 +102,10 @@ namespace build2
{
tracer trace ("search");
- phase_lock pl (run_phase::match);
+ context& ctx (bs.ctx);
+ phase_lock pl (ctx, run_phase::match);
- const target* t (targets.find (tk, trace));
+ const target* t (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
@@ -131,8 +133,13 @@ namespace build2
{
tracer trace ("match");
+ if (ts.empty ())
+ return;
+
+ context& ctx (ts[0].as_target ().ctx);
+
{
- phase_lock l (run_phase::match);
+ phase_lock l (ctx, run_phase::match);
// Setup progress reporting if requested.
//
@@ -143,10 +150,10 @@ namespace build2
{
size_t incr (stderr_term ? 1 : 10); // Scale depending on output type.
- what = " targets to " + diag_do (a);
+ what = " targets to " + diag_do (ctx, a);
- mg = sched.monitor (
- target_count,
+ mg = ctx.sched.monitor (
+ ctx.target_count,
incr,
[incr, &what] (size_t c) -> size_t
{
@@ -165,7 +172,7 @@ namespace build2
size_t i (0), n (ts.size ());
{
atomic_count task_count (0);
- wait_guard wg (task_count, true);
+ wait_guard wg (ctx, task_count, true);
for (; i != n; ++i)
{
@@ -177,7 +184,7 @@ namespace build2
// Bail out if the target has failed and we weren't instructed to
// keep going.
//
- if (s == target_state::failed && !keep_going)
+ if (s == target_state::failed && !ctx.keep_going)
{
++i;
break;
@@ -245,7 +252,7 @@ namespace build2
// Phase restored to load.
//
- assert (phase == run_phase::load);
+ assert (ctx.phase == run_phase::load);
}
void
@@ -254,25 +261,30 @@ namespace build2
{
tracer trace ("execute");
+ if (ts.empty ())
+ return;
+
+ context& ctx (ts[0].as_target ().ctx);
+
// Reverse the order of targets if the execution mode is 'last'.
//
- if (current_mode == execution_mode::last)
+ if (ctx.current_mode == execution_mode::last)
reverse (ts.begin (), ts.end ());
// Tune the scheduler.
//
- switch (current_inner_oif->concurrency)
+ switch (ctx.current_inner_oif->concurrency)
{
- case 0: sched.tune (1); break; // Run serially.
- case 1: break; // Run as is.
- default: assert (false); // Not yet supported.
+ case 0: ctx.sched.tune (1); break; // Run serially.
+ case 1: break; // Run as is.
+ default: assert (false); // Not yet supported.
}
- phase_lock pl (run_phase::execute); // Never switched.
+ phase_lock pl (ctx, run_phase::execute); // Never switched.
// Set the dry-run flag.
//
- dry_run = dry_run_option;
+ ctx.dry_run = ctx.dry_run_option;
// Setup progress reporting if requested.
//
@@ -281,20 +293,20 @@ namespace build2
if (prog && show_progress (1 /* max_verb */))
{
- size_t init (target_count.load (memory_order_relaxed));
+ size_t init (ctx.target_count.load (memory_order_relaxed));
size_t incr (init > 100 ? init / 100 : 1); // 1%.
if (init != incr)
{
- what = "% of targets " + diag_did (a);
+ what = "% of targets " + diag_did (ctx, a);
- mg = sched.monitor (
- target_count,
+ mg = ctx.sched.monitor (
+ ctx.target_count,
init - incr,
- [init, incr, &what] (size_t c) -> size_t
+ [init, incr, &what, &ctx] (size_t c) -> size_t
{
size_t p ((init - c) * 100 / init);
- size_t s (skip_count.load (memory_order_relaxed));
+ size_t s (ctx.skip_count.load (memory_order_relaxed));
diag_progress_lock pl;
diag_progress = ' ';
@@ -318,7 +330,7 @@ namespace build2
//
{
atomic_count task_count (0);
- wait_guard wg (task_count);
+ wait_guard wg (ctx, task_count);
for (const action_target& at: ts)
{
@@ -331,7 +343,7 @@ namespace build2
// Bail out if the target has failed and we weren't instructed to keep
// going.
//
- if (s == target_state::failed && !keep_going)
+ if (s == target_state::failed && !ctx.keep_going)
break;
}
@@ -341,11 +353,11 @@ namespace build2
// We are now running serially.
//
- sched.tune (0); // Restore original scheduler settings.
+ ctx.sched.tune (0); // Restore original scheduler settings.
// Clear the dry-run flag.
//
- dry_run = false;
+ ctx.dry_run = false;
// Clear the progress if present.
//
@@ -361,9 +373,9 @@ namespace build2
//
if (verb != 0)
{
- if (size_t s = skip_count.load (memory_order_relaxed))
+ if (size_t s = ctx.skip_count.load (memory_order_relaxed))
{
- text << "skipped " << diag_doing (a) << ' ' << s << " targets";
+ text << "skipped " << diag_doing (ctx, a) << ' ' << s << " targets";
}
}
@@ -422,8 +434,8 @@ namespace build2
// We should have executed every target that we matched, provided we
// haven't failed (in which case we could have bailed out early).
//
- assert (target_count.load (memory_order_relaxed) == 0);
- assert (dependency_count.load (memory_order_relaxed) == 0);
+ assert (ctx.target_count.load (memory_order_relaxed) == 0);
+ assert (ctx.dependency_count.load (memory_order_relaxed) == 0);
}
const meta_operation_info mo_perform {
@@ -471,7 +483,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
@@ -506,6 +518,8 @@ namespace build2
const scope& rs (*static_cast<const scope*> (ts[i].target));
+ context& ctx (rs.ctx);
+
// Print [meta_]operation names. Due to the way our aliasing works, we
// have to go through the [meta_]operation_table.
//
@@ -525,16 +539,16 @@ namespace build2
// This could be a simple project that doesn't set project name.
//
cout
- << "project: " << cast_empty<project_name> (rs[var_project]) << endl
- << "version: " << cast_empty<string> (rs[var_version]) << endl
- << "summary: " << cast_empty<string> (rs[var_project_summary]) << endl
- << "url: " << cast_empty<string> (rs[var_project_url]) << endl
- << "src_root: " << cast<dir_path> (rs[var_src_root]) << endl
- << "out_root: " << cast<dir_path> (rs[var_out_root]) << endl
- << "amalgamation: " << cast_empty<dir_path> (rs[var_amalgamation]) << endl
- << "subprojects: " << cast_empty<subprojects> (rs[var_subprojects]) << endl
- << "operations:"; print_ops (rs.root_extra->operations, operation_table); cout << endl
- << "meta-operations:"; print_ops (rs.root_extra->meta_operations, meta_operation_table); cout << endl;
+ << "project: " << cast_empty<project_name> (rs[ctx.var_project]) << endl
+ << "version: " << cast_empty<string> (rs[ctx.var_version]) << endl
+ << "summary: " << cast_empty<string> (rs[ctx.var_project_summary]) << endl
+ << "url: " << cast_empty<string> (rs[ctx.var_project_url]) << endl
+ << "src_root: " << cast<dir_path> (rs[ctx.var_src_root]) << endl
+ << "out_root: " << cast<dir_path> (rs[ctx.var_out_root]) << endl
+ << "amalgamation: " << cast_empty<dir_path> (rs[ctx.var_amalgamation]) << endl
+ << "subprojects: " << cast_empty<subprojects> (rs[ctx.var_subprojects]) << endl
+ << "operations:"; print_ops (rs.root_extra->operations, ctx.operation_table); cout << endl
+ << "meta-operations:"; print_ops (rs.root_extra->meta_operations, ctx.meta_operation_table); cout << endl;
}
}
@@ -610,9 +624,4 @@ namespace build2
nullptr,
nullptr
};
-
- // Tables.
- //
- string_table<meta_operation_id, meta_operation_data> meta_operation_table;
- string_table<operation_id> operation_table;
}
diff --git a/libbuild2/operation.hxx b/libbuild2/operation.hxx
index 86f93c6..520b37b 100644
--- a/libbuild2/operation.hxx
+++ b/libbuild2/operation.hxx
@@ -11,8 +11,6 @@
#include <libbuild2/utility.hxx>
#include <libbuild2/action.hxx>
-#include <libbuild2/variable.hxx>
-#include <libbuild2/prerequisite.hxx>
#include <libbuild2/target-state.hxx>
#include <libbuild2/export.hxx>
@@ -21,10 +19,15 @@ namespace build2
{
class location;
class scope;
- class target_key;
class target;
+ class target_key;
+ class context;
+ class include_type;
struct prerequisite_member;
+ class value;
+ using values = small_vector<value, 1>;
+
struct opspec;
// Meta-operation info.
@@ -275,7 +278,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,
@@ -295,11 +298,10 @@ namespace build2
return os << d.name;
}
- LIBBUILD2_SYMEXPORT extern butl::string_table<meta_operation_id,
- meta_operation_data>
- meta_operation_table;
+ using meta_operation_table = butl::string_table<meta_operation_id,
+ meta_operation_data>;
- LIBBUILD2_SYMEXPORT extern butl::string_table<operation_id> operation_table;
+ using operation_table = butl::string_table<operation_id>;
// These are "sparse" in the sense that we may have "holes" that
// are represented as NULL pointers. Also, lookup out of bounds
diff --git a/libbuild2/parser.cxx b/libbuild2/parser.cxx
index c55d434..d346afc 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 "
@@ -3967,7 +3967,7 @@ namespace build2
info << "use quoting to force untyped concatenation";
}));
- p = functions.try_call (
+ p = ctx.functions.try_call (
scope_, "builtin.concat", vector_view<value> (a), loc);
}
@@ -4636,7 +4636,7 @@ namespace build2
// Note that we "move" args to call().
//
- result_data = functions.call (scope_, name, args, loc);
+ result_data = ctx.functions.call (scope_, name, args, loc);
what = "function call";
}
else
@@ -4744,7 +4744,7 @@ namespace build2
info (loc) << "while converting " << t << " to string";
}));
- p = functions.try_call (
+ p = ctx.functions.try_call (
scope_, "string", vector_view<value> (&result_data, 1), loc);
}
@@ -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..7b815d5 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 */)
{
}
@@ -92,29 +92,4 @@ namespace build2
return r;
}
-
- // include()
- //
- include_type
- include_impl (action a,
- const target& t,
- const string& v,
- const prerequisite& p,
- const target* m)
- {
- include_type r (false);
-
- if (v == "false") r = include_type::excluded;
- else if (v == "adhoc") r = include_type::adhoc;
- else if (v == "true") r = include_type::normal;
- else
- fail << "invalid " << var_include->name << " variable value "
- << "'" << v << "' specified for prerequisite " << p;
-
- // Call the meta-operation override, if any (currently used by dist).
- //
- return current_mif->include == nullptr
- ? r
- : current_mif->include (a, t, prerequisite_member {p, m}, r);
- }
}
diff --git a/libbuild2/prerequisite.hxx b/libbuild2/prerequisite.hxx
index f79ce04..fe6d10a 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.
//
@@ -192,38 +192,6 @@ namespace build2
}
using prerequisites = vector<prerequisite>;
-
- // Helpers for dealing with the prerequisite inclusion/exclusion (the
- // 'include' buildfile variable, see var_include in context.hxx).
- //
- // Note that the include(prerequisite_member) overload is also provided.
- //
- // @@ Maybe this filtering should be incorporated into *_prerequisites() and
- // *_prerequisite_members() logic? Could make normal > adhoc > excluded and
- // then pass the "threshold".
- //
- class include_type
- {
- public:
- enum value {excluded, adhoc, normal};
-
- include_type (value v): v_ (v) {}
- include_type (bool v): v_ (v ? normal : excluded) {}
-
- operator value () const {return v_;}
- explicit operator bool () const {return v_ != excluded;}
-
- private:
- value v_;
- };
-
- include_type
- include (action,
- const target&,
- const prerequisite&,
- const target* = nullptr);
}
-#include <libbuild2/prerequisite.ixx>
-
#endif // LIBBUILD2_PREREQUISITE_HXX
diff --git a/libbuild2/prerequisite.ixx b/libbuild2/prerequisite.ixx
deleted file mode 100644
index d62af49..0000000
--- a/libbuild2/prerequisite.ixx
+++ /dev/null
@@ -1,34 +0,0 @@
-// file : libbuild2/prerequisite.ixx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <libbuild2/export.hxx>
-
-namespace build2
-{
- LIBBUILD2_SYMEXPORT include_type
- include_impl (action,
- const target&,
- const string&,
- const prerequisite&,
- const target*);
-
- LIBBUILD2_SYMEXPORT extern const variable* var_include; // context.cxx
-
- inline include_type
- include (action a, const target& t, const prerequisite& p, const target* m)
- {
- // Most of the time this variable will not be specified, so let's optimize
- // for that.
- //
- if (p.vars.empty ())
- return true;
-
- const string* v (cast_null<string> (p.vars[var_include]));
-
- if (v == nullptr)
- return true;
-
- return include_impl (a, t, *v, p, m);
- }
-}
diff --git a/libbuild2/rule.cxx b/libbuild2/rule.cxx
index 0ade8a3..d69e817 100644
--- a/libbuild2/rule.cxx
+++ b/libbuild2/rule.cxx
@@ -192,7 +192,7 @@ namespace build2
{
if (verb >= 2)
text << "mkdir " << d;
- else if (verb && current_diag_noise)
+ else if (verb && t.ctx.current_diag_noise)
text << "mkdir " << t;
};
@@ -279,7 +279,7 @@ namespace build2
// (or is current working directory). In this case rmdir() will issue a
// warning when appropriate.
//
- target_state ts (rmdir (t.dir, t, current_diag_noise ? 1 : 2)
+ target_state ts (rmdir (t.dir, t, t.ctx.current_diag_noise ? 1 : 2)
? target_state::changed
: target_state::unchanged);
diff --git a/libbuild2/scope.cxx b/libbuild2/scope.cxx
index 1ad7455..6e00511 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,8 +430,8 @@ namespace build2
// Check the cache.
//
variable_override_cache& cache (
- inner_proj == global_scope
- ? global_override_cache
+ inner_proj == &ctx.global_scope
+ ? ctx.global_override_cache
: inner_proj->root_extra->override_cache);
pair<value&, ulock> entry (
@@ -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;
}
@@ -798,12 +801,12 @@ namespace build2
//
dt->default_extension =
ext && dt->fixed_extension == nullptr
- ? &target_extension_var<var_extension, nullptr>
+ ? &target_extension_var<nullptr>
: nullptr;
dt->pattern =
dt->fixed_extension != nullptr ? nullptr /*&target_pattern_fix<???>*/ :
- dt->default_extension != nullptr ? &target_pattern_var<var_extension, nullptr> :
+ dt->default_extension != nullptr ? &target_pattern_var<nullptr> :
nullptr;
// There is actually a difference between "fixed fixed" (like man1{}) and
@@ -818,23 +821,15 @@ namespace build2
return target_types.insert (name, move (dt));
}
- scope* scope::global_;
- 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..35f07dd 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
@@ -184,20 +193,6 @@ namespace build2
//
variable_type_map target_vars;
- // Variable override caches. Only on project roots (in root_extra) plus a
- // global one for the global scope.
- //
- // The key is the variable plus the innermost (scope-wise) variable map to
- // which this override applies. See find_override() for details.
- //
- // Note: since it can be modified on any lookup (including during the
- // execute phase), the cache is protected by its own mutex shard.
- //
- using variable_override_cache = variable_cache<pair<const variable*,
- const variable_map*>>;
-
- static variable_override_cache global_override_cache;
-
// Set of buildfiles already loaded for this scope. The included
// buildfiles are checked against the project's root scope while
// imported -- against the global scope (global_scope).
@@ -301,7 +296,7 @@ namespace build2
//
module_map modules;
- // Variable override cache (see above).
+ // Variable override cache.
//
mutable variable_override_cache override_cache;
};
@@ -333,18 +328,10 @@ namespace build2
scope&
rw () const
{
- assert (phase == run_phase::load);
+ assert (ctx.phase == run_phase::load);
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 +343,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 +392,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_;
@@ -461,7 +448,7 @@ namespace build2
scope_map&
rw () const
{
- assert (phase == run_phase::load);
+ assert (ctx.phase == run_phase::load);
return const_cast<scope_map&> (*this);
}
@@ -469,24 +456,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/scope.ixx b/libbuild2/scope.ixx
index aa1247f..14907d3 100644
--- a/libbuild2/scope.ixx
+++ b/libbuild2/scope.ixx
@@ -83,9 +83,9 @@ namespace build2
}
inline const project_name&
- project (const scope& root)
+ project (const scope& rs)
{
- auto l (root[var_project]);
+ auto l (rs[rs.ctx.var_project]);
return l ? cast<project_name> (l) : empty_project_name;
}
}
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..dbca6cd 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::
@@ -290,10 +290,36 @@ namespace build2
}
}
- // target_set
+ // include()
//
- target_set targets;
+ include_type
+ include_impl (action a,
+ const target& t,
+ const string& v,
+ const prerequisite& p,
+ const target* m)
+ {
+ context& ctx (t.ctx);
+
+ include_type r (false);
+
+ if (v == "false") r = include_type::excluded;
+ else if (v == "adhoc") r = include_type::adhoc;
+ else if (v == "true") r = include_type::normal;
+ else
+ fail << "invalid " << ctx.var_include->name << " variable value "
+ << "'" << v << "' specified for prerequisite " << p;
+
+ // Call the meta-operation override, if any (currently used by dist).
+ //
+ if (auto f = ctx.current_mif->include)
+ r = f (a, t, prerequisite_member {p, m}, r);
+ return r;
+ }
+
+ // target_set
+ //
const target* target_set::
find (const target_key& k, tracer& trace) const
{
@@ -367,14 +393,14 @@ namespace build2
// We sometimes call insert() even if we expect to find an existing
// target in order to keep the same code (see cc/search_library()).
//
- assert (phase != run_phase::execute);
+ assert (ctx.phase != run_phase::execute);
optional<string> e (
tt.fixed_extension != nullptr
? 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
@@ -392,8 +418,8 @@ namespace build2
{
t->ext_ = &i->first.ext;
t->implied = implied;
- t->state.data[0].target_ = t;
- t->state.data[1].target_ = t;
+ t->state.inner.target_ = t;
+ t->state.outer.target_ = t;
return pair<target&, ulock> (*t, move (ul));
}
@@ -432,7 +458,7 @@ namespace build2
{
// The implied flag can only be cleared during the load phase.
//
- assert (phase == run_phase::load);
+ assert (ctx.phase == run_phase::load);
// Clear the implied flag.
//
@@ -538,7 +564,7 @@ namespace build2
//
const mtime_target* t (this);
- switch (phase)
+ switch (ctx.phase)
{
case run_phase::load: break;
case run_phase::match:
@@ -546,10 +572,11 @@ namespace build2
// Similar logic to matched_state_impl().
//
const opstate& s (state[action () /* inner */]);
- size_t o (s.task_count.load (memory_order_relaxed) - // Synchronized.
- target::count_base ());
- if (o != target::offset_applied && o != target::offset_executed)
+ // Note: already synchronized.
+ size_t o (s.task_count.load (memory_order_relaxed) - ctx.count_base ());
+
+ if (o != offset_applied && o != offset_executed)
break;
}
// Fall through.
@@ -658,25 +685,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 +780,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 +874,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
@@ -895,20 +922,20 @@ namespace build2
//
bool retest (false);
- assert (phase == run_phase::match);
+ assert (t.ctx.phase == run_phase::match);
{
// Switch the phase to load.
//
- phase_switch ps (run_phase::load);
+ phase_switch ps (t.ctx, run_phase::load);
// This is subtle: while we were fussing around another thread may
// 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,23 +960,23 @@ 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);
}
}
}
}
- assert (phase == run_phase::match);
+ assert (t.ctx.phase == run_phase::match);
// If we loaded/implied the buildfile, examine the target again.
//
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 +1127,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..b0d46e9 100644
--- a/libbuild2/target.hxx
+++ b/libbuild2/target.hxx
@@ -38,6 +38,23 @@ namespace build2
LIBBUILD2_SYMEXPORT const target*
search_existing (const prerequisite&);
+ // Prerequisite inclusion/exclusion (see include() function below).
+ //
+ class include_type
+ {
+ public:
+ enum value {excluded, adhoc, normal};
+
+ include_type (value v): v_ (v) {}
+ include_type (bool v): v_ (v ? normal : excluded) {}
+
+ operator value () const {return v_;}
+ explicit operator bool () const {return v_ != excluded;}
+
+ private:
+ value v_;
+ };
+
// Recipe.
//
// The returned target state is normally changed or unchanged. If there is
@@ -123,9 +140,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 +159,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 +412,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 ();
}
@@ -452,6 +472,9 @@ namespace build2
// the target is synchronized, then we can access and modify (second case)
// its state etc.
//
+ // NOTE: see also the corresponding count_*() fuctions in context (must be
+ // kept in sync).
+ //
static const size_t offset_touched = 1; // Target has been locked.
static const size_t offset_tried = 2; // Rule match has been tried.
static const size_t offset_matched = 3; // Rule has been matched.
@@ -459,15 +482,6 @@ namespace build2
static const size_t offset_executed = 5; // Recipe has been executed.
static const size_t offset_busy = 6; // Match/execute in progress.
- static size_t count_base () {return 5 * (current_on - 1);}
-
- static size_t count_touched () {return offset_touched + count_base ();}
- static size_t count_tried () {return offset_tried + count_base ();}
- static size_t count_matched () {return offset_matched + count_base ();}
- static size_t count_applied () {return offset_applied + count_base ();}
- static size_t count_executed () {return offset_executed + count_base ();}
- static size_t count_busy () {return offset_busy + count_base ();}
-
// Inner/outer operation state. See <libbuild2/operation.hxx> for details.
//
class LIBBUILD2_SYMEXPORT opstate
@@ -530,7 +544,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 +577,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 +771,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 (c) {}
target (target&&) = delete;
target& operator= (target&&) = delete;
@@ -798,6 +815,21 @@ namespace build2
uint8_t
unmark (const target*&);
+ // Helper for dealing with the prerequisite inclusion/exclusion (the
+ // 'include' buildfile variable, see var_include in context.hxx).
+ //
+ // Note that the include(prerequisite_member) overload is also provided.
+ //
+ // @@ Maybe this filtering should be incorporated into *_prerequisites() and
+ // *_prerequisite_members() logic? Could make normal > adhoc > excluded and
+ // then pass the "threshold".
+ //
+ include_type
+ include (action,
+ const target&,
+ const prerequisite&,
+ const target* = nullptr);
+
// A "range" that presents the prerequisites of a group and one of
// its members as one continuous sequence, or, in other words, as
// if they were in a single container. The group's prerequisites
@@ -1377,13 +1409,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 +1788,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.
@@ -1769,14 +1806,14 @@ namespace build2
string&, optional<string>&, const location&,
bool);
- // Get the extension from the variable or use the default if none set. If
- // the default is NULL, then return NULL.
+ // Get the extension from the `extension` variable or use the default if
+ // none set. If the default is NULL, then return NULL.
//
- template <const char* var, const char* def>
+ template <const char* def>
optional<string>
target_extension_var (const target_key&, const scope&, const char*, bool);
- template <const char* var, const char* def>
+ template <const char* def>
bool
target_pattern_var (const target_type&, const scope&,
string&, optional<string>&, const location&,
diff --git a/libbuild2/target.ixx b/libbuild2/target.ixx
index 1671c25..dd377b0 100644
--- a/libbuild2/target.ixx
+++ b/libbuild2/target.ixx
@@ -6,6 +6,8 @@
#include <libbuild2/filesystem.hxx> // mtime()
+#include <libbuild2/export.hxx>
+
namespace build2
{
// target
@@ -13,7 +15,7 @@ namespace build2
inline const string* target::
ext () const
{
- slock l (targets.mutex_);
+ slock l (ctx.targets.mutex_);
return *ext_ ? &**ext_ : nullptr;
}
@@ -88,21 +90,22 @@ namespace build2
inline pair<bool, target_state> target::
matched_state_impl (action a) const
{
- assert (phase == run_phase::match);
+ assert (ctx.phase == run_phase::match);
// Note that the "tried" state is "final".
//
const opstate& s (state[a]);
- size_t o (s.task_count.load (memory_order_relaxed) - // Synchronized.
- target::count_base ());
- if (o == target::offset_tried)
+ // Note: already synchronized.
+ size_t o (s.task_count.load (memory_order_relaxed) - ctx.count_base ());
+
+ if (o == offset_tried)
return make_pair (false, target_state::unknown);
else
{
// Normally applied but can also be already executed.
//
- assert (o == target::offset_applied || o == target::offset_executed);
+ assert (o == offset_applied || o == offset_executed);
return make_pair (true, (group_state (a) ? group->state[a] : s).state);
}
}
@@ -110,7 +113,7 @@ namespace build2
inline target_state target::
executed_state_impl (action a) const
{
- assert (phase == run_phase::execute);
+ assert (ctx.phase == run_phase::execute);
return (group_state (a) ? group->state : state)[a].state;
}
@@ -211,6 +214,32 @@ namespace build2
return m;
}
+ // include()
+ //
+ LIBBUILD2_SYMEXPORT include_type
+ include_impl (action,
+ const target&,
+ const string&,
+ const prerequisite&,
+ const target*);
+
+ inline include_type
+ include (action a, const target& t, const prerequisite& p, const target* m)
+ {
+ // Most of the time this variable will not be specified, so let's optimize
+ // for that.
+ //
+ if (p.vars.empty ())
+ return true;
+
+ const string* v (cast_null<string> (p.vars[t.ctx.var_include]));
+
+ if (v == nullptr)
+ return true;
+
+ return include_impl (a, t, *v, p, m);
+ }
+
// group_prerequisites
//
inline group_prerequisites::
@@ -422,7 +451,7 @@ namespace build2
inline timestamp mtime_target::
load_mtime (const path& p) const
{
- assert (phase == run_phase::execute &&
+ assert (ctx.phase == run_phase::execute &&
!group_state (action () /* inner */));
duration::rep r (mtime_.load (memory_order_consume));
@@ -440,7 +469,7 @@ namespace build2
inline bool mtime_target::
newer (timestamp mt) const
{
- assert (phase == run_phase::execute);
+ assert (ctx.phase == run_phase::execute);
timestamp mp (mtime ());
diff --git a/libbuild2/target.txx b/libbuild2/target.txx
index b93a403..17c38c8 100644
--- a/libbuild2/target.txx
+++ b/libbuild2/target.txx
@@ -90,12 +90,11 @@ namespace build2
target_extension_var_impl (const target_type& tt,
const string& tn,
const scope& s,
- const char* var,
const char* def)
{
// Include target type/pattern-specific variables.
//
- if (auto l = s.find (var_pool[var], tt, tn))
+ if (auto l = s.find (*s.ctx.var_extension, tt, tn))
{
// Help the user here and strip leading '.' from the extension.
//
@@ -106,17 +105,17 @@ namespace build2
return def != nullptr ? optional<string> (def) : nullopt;
}
- template <const char* var, const char* def>
+ template <const char* def>
optional<string>
target_extension_var (const target_key& tk,
const scope& s,
const char*,
bool)
{
- return target_extension_var_impl (*tk.type, *tk.name, s, var, def);
+ return target_extension_var_impl (*tk.type, *tk.name, s, def);
}
- template <const char* var, const char* def>
+ template <const char* def>
bool
target_pattern_var (const target_type& tt,
const scope& s,
@@ -144,7 +143,7 @@ namespace build2
// Use empty name as a target since we only want target type/pattern-
// specific variables that match any target ('*' but not '*.txt').
//
- if ((e = target_extension_var_impl (tt, string (), s, var, def)))
+ if ((e = target_extension_var_impl (tt, string (), s, def)))
return true;
}
}
@@ -172,13 +171,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/operation.cxx b/libbuild2/test/operation.cxx
index 3ff7702..0ca8da0 100644
--- a/libbuild2/test/operation.cxx
+++ b/libbuild2/test/operation.cxx
@@ -4,6 +4,8 @@
#include <libbuild2/test/operation.hxx>
+#include <libbuild2/variable.hxx>
+
using namespace std;
using namespace butl;
diff --git a/libbuild2/test/rule.cxx b/libbuild2/test/rule.cxx
index 1d11063..c216e66 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);
@@ -384,6 +384,8 @@ namespace build2
target_state rule::
perform_script (action a, const target& t, size_t pass_n) const
{
+ context& ctx (t.ctx);
+
// First pass through.
//
if (pass_n != 0)
@@ -433,10 +435,10 @@ namespace build2
const path& buildignore_file (rs.root_extra->buildignore_file);
dir_path bl;
- if (cast_false<bool> (rs.vars[var_forwarded]))
+ if (cast_false<bool> (rs.vars[ctx.var_forwarded]))
{
bl = bs.src_path () / wd.leaf (bs.out_path ());
- clean_backlink (bl, verb_never);
+ clean_backlink (ctx, bl, verb_never);
}
// If this is a (potentially) multi-testscript test, then create (and
@@ -477,7 +479,7 @@ namespace build2
// Remove the directory itself not to confuse the runner which tries
// to detect when tests stomp on each others feet.
//
- build2::rmdir_r (wd, true, 2);
+ rmdir_r (ctx, wd, true, 2);
}
// Delay actually creating the directory in case all the tests are
@@ -489,8 +491,8 @@ namespace build2
//
wait_guard wg;
- if (!dry_run)
- wg = wait_guard (target::count_busy (), t[a].task_count);
+ if (!ctx.dry_run)
+ wg = wait_guard (ctx, ctx.count_busy (), t[a].task_count);
// Result vector.
//
@@ -514,11 +516,11 @@ namespace build2
// don't clean the existing one), we are going to ignore it for
// dry-run.
//
- if (!dry_run)
+ if (!ctx.dry_run)
{
if (mk)
{
- mkdir_buildignore (wd, buildignore_file, 2);
+ mkdir_buildignore (ctx, wd, buildignore_file, 2);
mk = false;
}
}
@@ -532,40 +534,42 @@ namespace build2
dr << ' ' << t;
}
- res.push_back (dry_run ? scope_state::passed : scope_state::unknown);
+ res.push_back (ctx.dry_run
+ ? scope_state::passed
+ : scope_state::unknown);
- if (!dry_run)
+ if (!ctx.dry_run)
{
scope_state& r (res.back ());
- if (!sched.async (target::count_busy (),
- t[a].task_count,
- [this] (const diag_frame* ds,
- scope_state& r,
- const target& t,
- const testscript& ts,
- const dir_path& wd)
- {
- diag_frame::stack_guard dsg (ds);
- r = perform_script_impl (t, ts, wd, *this);
- },
- diag_frame::stack (),
- ref (r),
- cref (t),
- cref (ts),
- cref (wd)))
+ if (!ctx.sched.async (ctx.count_busy (),
+ t[a].task_count,
+ [this] (const diag_frame* ds,
+ scope_state& r,
+ const target& t,
+ const testscript& ts,
+ const dir_path& wd)
+ {
+ diag_frame::stack_guard dsg (ds);
+ r = perform_script_impl (t, ts, wd, *this);
+ },
+ diag_frame::stack (),
+ ref (r),
+ cref (t),
+ cref (ts),
+ cref (wd)))
{
// Executed synchronously. If failed and we were not asked to
// keep going, bail out.
//
- if (r == scope_state::failed && !keep_going)
+ if (r == scope_state::failed && !ctx.keep_going)
break;
}
}
}
}
- if (!dry_run)
+ if (!ctx.dry_run)
wg.wait ();
// Re-examine.
@@ -586,7 +590,7 @@ namespace build2
// Cleanup.
//
- if (!dry_run)
+ if (!ctx.dry_run)
{
if (!bad && !one && !mk && after == output_after::clean)
{
@@ -594,7 +598,7 @@ namespace build2
fail << "working directory " << wd << " is not empty at the "
<< "end of the test";
- rmdir_buildignore (wd, buildignore_file, 2);
+ rmdir_buildignore (ctx, wd, buildignore_file, 2);
}
}
@@ -603,8 +607,10 @@ namespace build2
// If we dry-run then presumably all tests passed and we shouldn't
// have anything left unless we are keeping the output.
//
- if (!bl.empty () && (dry_run ? after == output_after::keep : exists (wd)))
- update_backlink (wd, bl, true /* changed */);
+ if (!bl.empty () && (ctx.dry_run
+ ? after == output_after::keep
+ : exists (wd)))
+ update_backlink (ctx, wd, bl, true /* changed */);
if (bad)
throw failed ();
@@ -677,6 +683,8 @@ namespace build2
target_state rule::
perform_test (action a, const target& tt, size_t pass_n) const
{
+ context& ctx (tt.ctx);
+
// First pass through.
//
if (pass_n != 0)
@@ -777,7 +785,7 @@ namespace build2
cat = process (process_exit (0)); // Successfully exited.
- if (!dry_run)
+ if (!ctx.dry_run)
{
try
{
@@ -798,7 +806,7 @@ namespace build2
// If dry-run, the target may not exist.
//
- process_path pp (!dry_run
+ process_path pp (!ctx.dry_run
? run_search (p, true /* init */)
: try_run_search (p, true));
args.push_back (pp.empty () ? p.string ().c_str () : pp.recall_string ());
@@ -863,7 +871,7 @@ namespace build2
else if (verb)
text << "test " << tt;
- if (!dry_run)
+ if (!ctx.dry_run)
{
diag_record dr;
if (!run_test (tt,
diff --git a/libbuild2/test/script/builtin.cxx b/libbuild2/test/script/builtin.cxx
index ae979da..06b0cec 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 ())
{
@@ -1583,7 +1583,7 @@ namespace build2
// Note: can be executed synchronously.
//
static uint8_t
- sleep (scope&,
+ sleep (scope& s,
const strings& args,
auto_fd in, auto_fd out, auto_fd err) noexcept
try
@@ -1637,7 +1637,7 @@ namespace build2
// If/when required we could probably support the precise sleep mode
// (e.g., via an option).
//
- sched.sleep (chrono::seconds (n));
+ s.root.test_target.ctx.sched.sleep (chrono::seconds (n));
r = 0;
}
diff --git a/libbuild2/test/script/parser.cxx b/libbuild2/test/script/parser.cxx
index 260bc88..8b8f705 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&)
@@ -2896,7 +2896,7 @@ namespace build2
if (exec_scope)
{
atomic_count task_count (0);
- wait_guard wg (task_count);
+ wait_guard wg (g->root.test_target.ctx, task_count);
// Start asynchronous execution of inner scopes keeping track of
// how many we have handled.
@@ -3016,24 +3016,24 @@ namespace build2
// UBSan workaround.
//
const diag_frame* df (diag_frame::stack ());
- if (!sched.async (task_count,
- [] (const diag_frame* ds,
- scope& s,
- script& scr,
- runner& r)
- {
- diag_frame::stack_guard dsg (ds);
- execute_impl (s, scr, r);
- },
- df,
- ref (*chain),
- ref (*script_),
- ref (*runner_)))
+ if (!ctx.sched.async (task_count,
+ [] (const diag_frame* ds,
+ scope& s,
+ script& scr,
+ runner& r)
+ {
+ diag_frame::stack_guard dsg (ds);
+ execute_impl (s, scr, r);
+ },
+ df,
+ ref (*chain),
+ ref (*script_),
+ ref (*runner_)))
{
// Bail out if the scope has failed and we weren't instructed
// to keep going.
//
- if (chain->state == scope_state::failed && !keep_going)
+ if (chain->state == scope_state::failed && !ctx.keep_going)
throw 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..56630fe 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>
@@ -155,8 +155,8 @@ namespace build2
//
init_diag (1);
init (nullptr, argv[0]);
- sched.startup (1); // Serial execution.
- reset (strings ()); // No command line variables.
+ scheduler sched (1); // Serial execution.
+ 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..53f6741 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,12 +693,14 @@ 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::
enter (scope& sp, const location&)
{
+ context& ctx (sp.root.target_scope.ctx);
+
auto df = make_diag_frame (
[&sp](const diag_record& dr)
{
@@ -723,8 +725,9 @@ namespace build2
fs_status<mkdir_status> r (
sp.parent == nullptr
? mkdir_buildignore (
+ ctx,
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));
@@ -744,6 +747,8 @@ namespace build2
void default_runner::
leave (scope& sp, const location& ll)
{
+ context& ctx (sp.root.target_scope.ctx);
+
auto df = make_diag_frame (
[&sp](const diag_record& dr)
{
@@ -766,7 +771,7 @@ namespace build2
{
// Remove the file if exists. Fail otherwise.
//
- if (rmfile (p, 3) == rmfile_status::not_exist)
+ if (rmfile (ctx, p, 3) == rmfile_status::not_exist)
fail (ll) << "registered for cleanup special file " << p
<< " does not exist";
}
@@ -800,9 +805,8 @@ namespace build2
{
bool removed (false);
- auto rm = [&cp, recursive, &removed, &sp, &ll] (path&& pe,
- const string&,
- bool interm)
+ auto rm = [&cp, recursive, &removed, &sp, &ll, &ctx]
+ (path&& pe, const string&, bool interm)
{
if (!interm)
{
@@ -819,7 +823,7 @@ namespace build2
if (!recursive)
{
- rmdir_status r (rmdir (d, 3));
+ rmdir_status r (rmdir (ctx, d, 3));
if (r != rmdir_status::not_empty)
return true;
@@ -839,9 +843,7 @@ namespace build2
// Cast to uint16_t to avoid ambiguity with
// libbutl::rmdir_r().
//
- rmdir_status r (rmdir_r (d,
- d != sp.wd_path,
- static_cast<uint16_t> (3)));
+ rmdir_status r (rmdir_r (ctx, d, d != sp.wd_path, 3));
if (r != rmdir_status::not_empty)
return true;
@@ -854,7 +856,7 @@ namespace build2
}
}
else
- rmfile (pe, 3);
+ rmfile (ctx, pe, 3);
}
return true;
@@ -921,13 +923,14 @@ namespace build2
//
rmdir_status r (
recursive
- ? rmdir_r (d, !wd, static_cast <uint16_t> (v))
+ ? rmdir_r (ctx, d, !wd, static_cast <uint16_t> (v))
: (wd && sp.parent == nullptr
? rmdir_buildignore (
+ ctx,
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)));
+ : rmdir (ctx, d, v)));
if (r == rmdir_status::success ||
(r == rmdir_status::not_exist && t == cleanup_type::maybe))
@@ -948,7 +951,7 @@ namespace build2
// Remove the file if exists. Fail otherwise. Removal of
// non-existing file is not an error for 'maybe' cleanup type.
//
- if (rmfile (p, 3) == rmfile_status::not_exist &&
+ if (rmfile (ctx, p, 3) == rmfile_status::not_exist &&
t == cleanup_type::always)
fail (ll) << "registered for cleanup file " << p
<< " does not exist";
@@ -1097,8 +1100,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 +1126,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 +1178,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 +1363,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/types.hxx b/libbuild2/types.hxx
index 61880ed..b14a365 100644
--- a/libbuild2/types.hxx
+++ b/libbuild2/types.hxx
@@ -334,7 +334,6 @@ namespace build2
LIBBUILD2_SYMEXPORT ostream&
operator<< (ostream&, run_phase); // utility.cxx
- LIBBUILD2_SYMEXPORT extern run_phase phase;
}
// In order to be found (via ADL) these have to be either in std:: or in
diff --git a/libbuild2/utility.cxx b/libbuild2/utility.cxx
index 63fa609..ba50c5a 100644
--- a/libbuild2/utility.cxx
+++ b/libbuild2/utility.cxx
@@ -81,7 +81,6 @@ namespace build2
: (to_string (build_version.major ()) + '.' +
to_string (build_version.minor ())));
- bool dry_run_option;
optional<bool> mtime_check_option;
optional<path> config_sub;
@@ -495,15 +494,14 @@ namespace build2
void
init (void (*t) (bool),
const char* a0,
- bool kg, bool dr, optional<bool> mc,
- optional<path> cs, optional<path> cg)
+ optional<bool> mc,
+ optional<path> cs,
+ optional<path> cg)
{
terminate = t;
argv0 = process::path_search (a0, true);
- keep_going = kg;
- dry_run_option = dr;
mtime_check_option = mc;
config_sub = move (cs);
diff --git a/libbuild2/utility.hxx b/libbuild2/utility.hxx
index ed94a08..a6a7ac3 100644
--- a/libbuild2/utility.hxx
+++ b/libbuild2/utility.hxx
@@ -124,8 +124,6 @@ namespace build2
LIBBUILD2_SYMEXPORT void
init (void (*terminate) (bool),
const char* argv0,
- bool keep_going = false,
- bool dry_run = false,
optional<bool> mtime_check = nullopt,
optional<path> config_sub = nullopt,
optional<path> config_guess = nullopt);
@@ -144,8 +142,6 @@ namespace build2
LIBBUILD2_SYMEXPORT extern const standard_version build_version;
LIBBUILD2_SYMEXPORT extern const string build_version_interface;
- LIBBUILD2_SYMEXPORT extern bool dry_run_option; // --dry-run
-
// --[no-]mtime-check
//
LIBBUILD2_SYMEXPORT extern optional<bool> mtime_check_option;
@@ -169,6 +165,9 @@ namespace build2
// state dump). If it is empty, then relative() below returns the original
// path.
//
+ // @@ CTX: this could be an issue if changed concurrently from several
+ // contexts.
+ //
LIBBUILD2_SYMEXPORT extern const dir_path* relative_base;
// If possible and beneficial, translate an absolute, normalized path into
diff --git a/libbuild2/variable.cxx b/libbuild2/variable.cxx
index c921bbd..86109d2 100644
--- a/libbuild2/variable.cxx
+++ b/libbuild2/variable.cxx
@@ -1243,7 +1243,7 @@ namespace build2
const bool* o,
bool pat)
{
- assert (!global_ || phase == run_phase::load);
+ assert (!global_ || global_->phase == run_phase::load);
// Apply pattern.
//
@@ -1318,7 +1318,7 @@ namespace build2
bool retro,
bool match)
{
- assert (!global_ || phase == run_phase::load);
+ assert (!global_ || global_->phase == run_phase::load);
size_t pn (p.size ());
@@ -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&>
@@ -1434,7 +1432,7 @@ namespace build2
pair<reference_wrapper<value>, bool> variable_map::
insert (const variable& var, bool typed)
{
- assert (!global_ || phase == run_phase::load);
+ assert (!global_ || ctx->phase == run_phase::load);
auto p (m_.emplace (var, value_data (typed ? var.type : nullptr)));
value_data& r (p.first->second);
diff --git a/libbuild2/variable.hxx b/libbuild2/variable.hxx
index 9a106b5..3eed61e 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
{
@@ -1161,23 +1162,26 @@ namespace build2
void
clear () {map_.clear ();}
- variable_pool (): variable_pool (false) {}
+ variable_pool (): variable_pool (nullptr) {}
- // RW access.
+ // RW access (only for the global pool).
//
variable_pool&
rw () const
{
- assert (phase == run_phase::load);
+ assert (global_->phase == run_phase::load);
return const_cast<variable_pool&> (*this);
}
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:
@@ -1258,16 +1251,16 @@ namespace build2
private:
std::multiset<pattern> patterns_;
- // Global pool flag.
+ // Global pool flag/context.
//
private:
+ friend class context;
+
explicit
- variable_pool (bool global): global_ (global) {}
+ variable_pool (context* global): global_ (global) {}
- bool global_;
+ context* 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_;
};
+ LIBBUILD2_SYMEXPORT extern const variable_map empty_variable_map;
+
// Value caching. Used for overrides as well as target type/pattern-specific
// append/prepend.
//
@@ -1519,6 +1524,18 @@ namespace build2
map_type m_;
};
+ // Variable override cache. Only on project roots (in scope::root_extra)
+ // plus a global one (in context) for the global scope.
+ //
+ // The key is the variable plus the innermost (scope-wise) variable map to
+ // which this override applies. See scope::find_override() for details.
+ //
+ // Note: since it can be modified on any lookup (including during the
+ // execute phase), the cache is protected by a mutex shard.
+ //
+ class variable_override_cache:
+ public variable_cache<pair<const variable*, const variable_map*>> {};
+
// Target type/pattern-specific variables.
//
class variable_pattern_map
@@ -1528,13 +1545,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 +1561,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 +1574,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 +1604,9 @@ namespace build2
cache;
private:
- bool global_;
+ context& ctx;
map_type map_;
+ bool global_;
};
}
diff --git a/libbuild2/variable.ixx b/libbuild2/variable.ixx
index f0bde09..e5353ed 100644
--- a/libbuild2/variable.ixx
+++ b/libbuild2/variable.ixx
@@ -764,7 +764,7 @@ namespace build2
{
// We assume typification is not modification so no version increment.
//
- if (phase == run_phase::load)
+ if (ctx->phase == run_phase::load)
{
if (v.type != var.type)
build2::typify (const_cast<value_data&> (v), *var.type, &var);
diff --git a/libbuild2/version/init.cxx b/libbuild2/version/init.cxx
index a4e41d6..123dc65 100644
--- a/libbuild2/version/init.cxx
+++ b/libbuild2/version/init.cxx
@@ -37,6 +37,8 @@ namespace build2
tracer trace ("version::boot");
l5 ([&]{trace << "for " << rs;});
+ context& ctx (rs.ctx);
+
// Extract the version from the manifest file. As well as summary and
// url while at it.
//
@@ -67,7 +69,7 @@ namespace build2
{
if (nv.name == "name")
{
- auto& pn (cast<project_name> (rs.vars[var_project]));
+ auto& pn (cast<project_name> (rs.vars[ctx.var_project]));
if (nv.value != pn.string ())
{
@@ -219,7 +221,7 @@ namespace build2
// Set all the version.* variables.
//
- auto& vp (var_pool.rw (rs));
+ auto& vp (ctx.var_pool.rw (rs));
auto set = [&vp, &rs] (const char* var, auto val)
{
@@ -228,8 +230,8 @@ namespace build2
rs.assign (v) = move (val);
};
- if (!sum.empty ()) rs.assign (var_project_summary) = move (sum);
- if (!url.empty ()) rs.assign (var_project_url) = move (url);
+ if (!sum.empty ()) rs.assign (ctx.var_project_summary) = move (sum);
+ if (!url.empty ()) rs.assign (ctx.var_project_url) = move (url);
set ("version", v.string ()); // Project version (var_version).
@@ -268,7 +270,7 @@ namespace build2
// Create the module.
//
- mod.reset (new module (cast<project_name> (rs.vars[var_project]),
+ mod.reset (new module (cast<project_name> (rs.vars[ctx.var_project]),
move (v),
committed,
rewritten,
@@ -294,6 +296,8 @@ namespace build2
if (!first)
fail (l) << "multiple version module initializations";
+ context& ctx (rs.ctx);
+
// Load in.base (in.* variables, in{} target type).
//
if (!cast_false<bool> (rs["in.base.loaded"]))
@@ -320,7 +324,7 @@ namespace build2
if (!val)
{
- string p (cast<project_name> (rs.vars[var_project]).string ());
+ string p (cast<project_name> (rs.vars[ctx.var_project]).string ());
p += '-';
p += v.string ();
val = move (p);
@@ -370,7 +374,8 @@ namespace build2
//
try
{
- auto_rmfile t (fixup_manifest (f,
+ auto_rmfile t (fixup_manifest (rs.ctx,
+ f,
path::temp_path ("manifest"),
m.version));
diff --git a/libbuild2/version/rule.cxx b/libbuild2/version/rule.cxx
index 37e6b0f..fe999b3 100644
--- a/libbuild2/version/rule.cxx
+++ b/libbuild2/version/rule.cxx
@@ -328,7 +328,8 @@ namespace build2
// the out tree. Somehow the latter feels more appropriate (even though
// if we crash in between, we won't clean it up).
//
- return fixup_manifest (p, rs.out_path () / "manifest.t", m.version);
+ return fixup_manifest (
+ t.ctx, p, rs.out_path () / "manifest.t", m.version);
}
}
}
diff --git a/libbuild2/version/utility.cxx b/libbuild2/version/utility.cxx
index 70daab1..0669da7 100644
--- a/libbuild2/version/utility.cxx
+++ b/libbuild2/version/utility.cxx
@@ -17,11 +17,14 @@ namespace build2
namespace version
{
auto_rmfile
- fixup_manifest (const path& in, path out, const standard_version& v)
+ fixup_manifest (context& ctx,
+ const path& in,
+ path out,
+ const standard_version& v)
{
- auto_rmfile r (move (out), !dry_run /* active */);
+ auto_rmfile r (move (out), !ctx.dry_run /* active */);
- if (!dry_run)
+ if (!ctx.dry_run)
{
try
{
diff --git a/libbuild2/version/utility.hxx b/libbuild2/version/utility.hxx
index 16e8c78..170488d 100644
--- a/libbuild2/version/utility.hxx
+++ b/libbuild2/version/utility.hxx
@@ -8,6 +8,7 @@
#include <libbuild2/types.hxx>
#include <libbuild2/utility.hxx>
+#include <libbuild2/context.hxx>
#include <libbuild2/filesystem.hxx>
namespace build2
@@ -18,7 +19,10 @@ namespace build2
// not preserve comments. Probably acceptable for snapshots.
//
auto_rmfile
- fixup_manifest (const path& in, path out, const standard_version&);
+ fixup_manifest (context&,
+ const path& in,
+ path out,
+ const standard_version&);
}
}