aboutsummaryrefslogtreecommitdiff
path: root/build2/algorithm.cxx
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2017-11-30 14:48:19 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2017-11-30 14:48:19 +0200
commit879b5f52cb86f24352f4ed245fcce5f1ab885f97 (patch)
treeff483ce4aa64dda7d17b82f956333f0705257568 /build2/algorithm.cxx
parent074a8c04a384a9752466bd2af69b695333b2955c (diff)
Implement support for scope operation callbacks
An entity (module, core) can register a function that will be called when an action is executed on the dir{} target that corresponds to the scope. The pre callback is called just before the recipe and the post -- immediately after.
Diffstat (limited to 'build2/algorithm.cxx')
-rw-r--r--build2/algorithm.cxx102
1 files changed, 88 insertions, 14 deletions
diff --git a/build2/algorithm.cxx b/build2/algorithm.cxx
index 6976343..fc71c1a 100644
--- a/build2/algorithm.cxx
+++ b/build2/algorithm.cxx
@@ -843,13 +843,13 @@ namespace build2
return r;
}
+ // Execute the specified recipe (if any) and the scope operation callbacks
+ // (if any/applicable) then merge and return the resulting target state.
+ //
static target_state
- execute_impl (action a, target& t)
+ execute_recipe (action a, target& t, const recipe& r)
{
- assert (t.task_count.load (memory_order_consume) == target::count_busy ()
- && t.state_ == target_state::unknown);
-
- target_state ts;
+ target_state ts (target_state::unknown);
try
{
@@ -860,14 +860,55 @@ namespace build2
dr << info << "while " << diag_doing (a, t);
});
- ts = t.recipe_ (a, t);
+ // If this is a dir{} target, see if we have any operation callbacks
+ // in the corresponding scope.
+ //
+ const dir* op_t (t.is_a<dir> ());
+ const scope* op_s (nullptr);
+
+ using op_iterator = scope::operation_callback_map::const_iterator;
+ pair<op_iterator, op_iterator> op_p;
+
+ if (op_t != nullptr)
+ {
+ op_s = &scopes.find (t.dir);
+
+ if (op_s->out_path () == t.dir && !op_s->operation_callbacks.empty ())
+ {
+ op_p = op_s->operation_callbacks.equal_range (a);
+
+ if (op_p.first == op_p.second)
+ op_s = nullptr; // Ignore.
+ }
+ else
+ op_s = nullptr; // Ignore.
+ }
+
+ // Pre operations.
+ //
+ // Note that here we assume the dir{} target cannot be part of a group
+ // and as a result we (a) don't try to avoid calling post callbacks in
+ // case of a group failure and (b) merge the pre and post states with
+ // the group state.
+ //
+ if (op_s != nullptr)
+ {
+ for (auto i (op_p.first); i != op_p.second; ++i)
+ if (const auto& f = i->second.pre)
+ ts |= f (a, *op_s, *op_t);
+ }
+
+ // Recipe.
+ //
+ ts |= r != nullptr ? r (a, t) : target_state::unchanged;
- // Decrement the target count (see target::recipe() for details).
+ // Post operations.
//
+ if (op_s != nullptr)
{
- recipe_function** f (t.recipe_.target<recipe_function*> ());
- if (f == nullptr || *f != &group_action)
- target_count.fetch_sub (1, memory_order_relaxed);
+ for (auto i (op_p.first); i != op_p.second; ++i)
+ if (const auto& f = i->second.post)
+ ts |= f (a, *op_s, *op_t);
}
// See the recipe documentation for details on what's going on here.
@@ -894,6 +935,25 @@ namespace build2
ts = t.state_ = target_state::failed;
}
+ return ts;
+ }
+
+ static target_state
+ execute_impl (action a, target& t)
+ {
+ assert (t.task_count.load (memory_order_consume) == target::count_busy ()
+ && t.state_ == target_state::unknown);
+
+ target_state ts (execute_recipe (a, t, t.recipe_));
+
+ // Decrement the target count (see target::recipe() for details).
+ //
+ {
+ recipe_function** f (t.recipe_.target<recipe_function*> ());
+ if (f == nullptr || *f != &group_action)
+ target_count.fetch_sub (1, memory_order_relaxed);
+ }
+
// Decrement the task count (to count_executed) and wake up any threads
// that might be waiting for this target.
//
@@ -950,10 +1010,11 @@ namespace build2
//
size_t touc (target::count_touched ());
size_t matc (target::count_matched ());
+ size_t appc (target::count_applied ());
size_t exec (target::count_executed ());
size_t busy (target::count_busy ());
- for (size_t tc (target::count_applied ());;)
+ for (size_t tc (appc);;)
{
if (t.task_count.compare_exchange_strong (
tc,
@@ -965,7 +1026,13 @@ namespace build2
//
if ((tc >= touc && tc <= matc) || t.state_ == target_state::unchanged)
{
- t.state_ = target_state::unchanged;
+ // If we have a noop recipe, there could still be scope operations.
+ //
+ if (tc == appc && t.is_a<dir> ())
+ execute_recipe (a, t, nullptr /* recipe */);
+ else
+ t.state_ = target_state::unchanged;
+
t.task_count.store (exec, memory_order_release);
sched.resume (t.task_count);
}
@@ -1022,10 +1089,11 @@ namespace build2
//
size_t touc (target::count_touched ());
size_t matc (target::count_matched ());
+ size_t appc (target::count_applied ());
size_t exec (target::count_executed ());
size_t busy (target::count_busy ());
- for (size_t tc (target::count_applied ());;)
+ for (size_t tc (appc);;)
{
if (t.task_count.compare_exchange_strong (
tc,
@@ -1035,7 +1103,13 @@ namespace build2
{
if ((tc >= touc && tc <= matc) || t.state_ == target_state::unchanged)
{
- t.state_ = target_state::unchanged;
+ // If we have a noop recipe, there could still be scope operations.
+ //
+ if (tc == appc && t.is_a<dir> ())
+ execute_recipe (a, t, nullptr /* recipe */);
+ else
+ t.state_ = target_state::unchanged;
+
t.task_count.store (exec, memory_order_release);
sched.resume (t.task_count);
}