diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2024-08-21 09:03:42 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2024-10-08 15:05:21 +0200 |
commit | 734bf117f0eb596fc40579810613a6e13c43d3c4 (patch) | |
tree | 57c7ffda0ae33287d5ba7a7c23a2a89ec3942b21 /libbuild2/operation.cxx | |
parent | 2aeb84fd76f1f310022fe15f03efca78a2ccdd26 (diff) |
Add context-wide pre/post operation callbacks
Diffstat (limited to 'libbuild2/operation.cxx')
-rw-r--r-- | libbuild2/operation.cxx | 135 |
1 files changed, 109 insertions, 26 deletions
diff --git a/libbuild2/operation.cxx b/libbuild2/operation.cxx index d7b5b92..38e5bb4 100644 --- a/libbuild2/operation.cxx +++ b/libbuild2/operation.cxx @@ -335,6 +335,16 @@ namespace build2 }); } + // Call the pre operation callbacks. + // + // See a comment in perform_execute() for why we are doing it here + // (short answer: phase switches). + // + auto cs (ctx.operation_callbacks.equal_range (a)); + for (auto i (cs.first); i != cs.second; ++i) + if (const auto& f = i->second.pre) + f (ctx, a, ts); + // Start asynchronous matching of prerequisites keeping track of how // many we have started. Wait with unlocked phase to allow phase // switching. @@ -437,7 +447,11 @@ namespace build2 diag_progress.clear (); } - // We are now running serially. Re-examine targets that we have matched. + // We are now running serially. + // + + // Re-examine targets that we have matched and determine whether we have + // failed. // for (size_t j (0); j != n; ++j) { @@ -463,11 +477,8 @@ namespace build2 case target_state::postponed: { // We bailed before matching it (leave state in action_target as - // unknown). + // unknown for the structured result printing). // - if (verb != 0 && diag >= 1) - info << "not " << diag_did (a, t); - break; } case target_state::unknown: @@ -480,9 +491,6 @@ namespace build2 { // Things didn't go well for this target. // - if (verb != 0 && diag >= 1) - info << "failed to " << diag_do (a, t); - at.state = s; fail = true; break; @@ -492,6 +500,36 @@ namespace build2 } } + // Call the post operation callbacks if perform_execute() won't be + // called. + // + if (fail) + perform_post_operation_callbacks (ctx, a, ts, fail); + + // Re-examine targets that we have matched and print diagnostics. + // + if (verb != 0 && diag >= 1) + { + for (size_t j (0); j != n; ++j) + { + action_target& at (ts[j]); + const target& t (at.as<target> ()); + + if (at.state == target_state::failed) + { + // Things didn't go well for this target. + // + info << "failed to " << diag_do (a, t); + } + else if (j >= i || t.matched_state (a) == target_state::postponed) + { + // We bailed before matching it. + // + info << "not " << diag_did (a, t); + } + } + } + if (fail) throw failed (); @@ -622,8 +660,8 @@ namespace build2 switch (ctx.current_inner_oif->concurrency) { case 0: sched_tune = tune_guard (*ctx.sched, 1); break; // Run serially. - case 1: break; // Run as is. - default: assert (false); // Not supported. + case 1: break; // Run as is. + default: assert (false); // Not supported. } // Set the dry-run flag. @@ -675,6 +713,13 @@ namespace build2 } } + // Note that while this would seem like the natural place to call the + // pre operation callbacks, it is actually too late since during match + // we may switch to the execute phase and execute some recipes (think + // building a tool to generate some code). So we have to do this in + // perform_match() and then carefully make sure the post callbacks are + // called for all the exit paths (match failed, match_only, etc). + // In the 'last' execution mode run post hoc first. // if (ctx.current_mode == execution_mode::last) @@ -723,9 +768,44 @@ namespace build2 // We are now running serially. // - // Clear the dry-run flag. + // Re-examine all the targets and determine whether we have failed. // - ctx.dry_run = false; + for (action_target& at: ts) + { + const target& t (at.as<target> ()); + + // Similar to match we cannot attribute post hoc failures to specific + // targets so it seems the best we can do is just fail them all. + // + if (!posthoc_fail) + { + // Note that here we call executed_state() directly instead of + // execute_complete() since we know there is no need to wait. + // + at.state = t.executed_state (a, false /* fail */); + } + else + at.state = /*t.state[a].state =*/ target_state::failed; + + switch (at.state) + { + case target_state::unknown: + case target_state::unchanged: + case target_state::changed: + break; + case target_state::failed: + { + fail = true; + break; + } + default: + assert (false); + } + } + + // Call the post operation callbacks. + // + perform_post_operation_callbacks (ctx, a, ts, fail); // Clear the progress if present. // @@ -735,6 +815,10 @@ namespace build2 diag_progress.clear (); } + // Clear the dry-run flag. + // + ctx.dry_run = false; + // Restore original scheduler settings. } @@ -759,19 +843,6 @@ namespace build2 { const target& t (at.as<target> ()); - // Similar to match we cannot attribute post hoc failures to specific - // targets so it seems the best we can do is just fail them all. - // - if (!posthoc_fail) - { - // Note that here we call executed_state() directly instead of - // execute_complete() since we know there is no need to wait. - // - at.state = t.executed_state (a, false /* fail */); - } - else - at.state = /*t.state[a].state =*/ target_state::failed; - switch (at.state) { case target_state::unknown: @@ -806,7 +877,6 @@ namespace build2 if (verb != 0 && diag >= 1) info << "failed to " << diag_do (a, t); - fail = true; break; } default: @@ -1004,6 +1074,19 @@ namespace build2 #endif } + void + perform_post_operation_callbacks (context& ctx, + action a, + const action_targets& ts, + bool failed) + { + auto cs (ctx.operation_callbacks.equal_range (a)); + + for (auto i (cs.first); i != cs.second; ++i) + if (const auto& f = i->second.post) + f (ctx, a, ts, failed); + } + const meta_operation_info mo_perform { perform_id, "perform", |