From 0cee33621a93d3348a1bf19a0c94441b717cbcbc Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 13 Mar 2015 10:38:11 +0200 Subject: Add postponed recipe execution support --- build/algorithm | 7 ++++--- build/algorithm.cxx | 6 ++++-- build/b.cxx | 34 +++++++++++++++++++++++++++++++++- build/rule.cxx | 15 ++++++++++----- build/target | 17 ++++++++++------- 5 files changed, 61 insertions(+), 18 deletions(-) diff --git a/build/algorithm b/build/algorithm index e4b319a..fa74769 100644 --- a/build/algorithm +++ b/build/algorithm @@ -44,9 +44,10 @@ namespace build execute (action, target&); // The default prerequisite execute implementation. It calls execute() - // on each non-ignored (NULL target) prerequisite in a loop. Returns - // target_state::changed if any of them were changed and - // target_state::unchanged otherwise. Note that this function can be + // on each non-ignored (non-NULL target) prerequisite in a loop. + // Returns target_state::changed if any of them were changed and + // target_state::unchanged otherwise. It treats targets with postponed + // execution the same as ignored. Note that this function can be // used as a recipe. // target_state diff --git a/build/algorithm.cxx b/build/algorithm.cxx index cccda49..776ea3e 100644 --- a/build/algorithm.cxx +++ b/build/algorithm.cxx @@ -176,6 +176,7 @@ namespace build switch (target_state ts = t.state) { case target_state::unknown: + case target_state::postponed: { t.state = target_state::failed; // So the rule can just throw. @@ -196,7 +197,8 @@ namespace build } case target_state::unchanged: case target_state::changed: - assert (false); // Should have been handled by inline execute(). + // Should have been handled by inline execute(). + assert (false); case target_state::failed: throw failed (); } @@ -214,7 +216,7 @@ namespace build target& pt (*p.target); - if (execute (a, pt) != target_state::unchanged) + if (execute (a, pt) == target_state::changed) ts = target_state::changed; } diff --git a/build/b.cxx b/build/b.cxx index 0764622..5096bc2 100644 --- a/build/b.cxx +++ b/build/b.cxx @@ -480,7 +480,7 @@ main (int argc, char* argv[]) // Multiple targets in the same operation can be done in parallel. // - vector> tgs; + vector> tgs, psp; tgs.reserve (os.size ()); // First resolve and match all the targets. We don't want to @@ -531,11 +531,43 @@ main (int argc, char* argv[]) switch (execute (act, t)) { + case target_state::postponed: + { + info << "target " << t << " is postponed"; + psp.push_back (t); + break; + } + case target_state::unchanged: + { + info << "target " << t << " is up to date"; + break; + } + case target_state::changed: + break; + case target_state::failed: + //@@ This could probably happen in a parallel build. + default: + assert (false); + } + } + + // Re-examine postponed targets. + // + for (target& t: psp) + { + switch (t.state) + { + case target_state::postponed: + { + info << "target " << t << " unable to do at this time"; + break; + } case target_state::unchanged: { info << "target " << t << " is up to date"; break; } + case target_state::unknown: // Assume something was done to it. case target_state::changed: break; case target_state::failed: diff --git a/build/rule.cxx b/build/rule.cxx index 2359445..eb7f5b4 100644 --- a/build/rule.cxx +++ b/build/rule.cxx @@ -250,10 +250,7 @@ namespace build // Wait until the last dependent to get an empty directory. // if (t.dependents != 0) - { - t.state = target_state::unknown; - return target_state::unchanged; - } + return target_state::postponed; // The reverse order of update: first delete this directory, // then clean prerequisites (e.g., delete parent directories). @@ -311,6 +308,14 @@ namespace build if (!t.prerequisites.empty ()) ts = execute_prerequisites (a, t); - return rs == rmdir_status::success ? target_state::changed : ts; + // If we couldn't remove the directory, return postponed meaning + // that the operation could not be performed at this time. + // + switch (rs) + { + case rmdir_status::success: return target_state::changed; + case rmdir_status::not_empty: return target_state::postponed; + default: return ts; + } } } diff --git a/build/target b/build/target index f9aa50a..3d395d4 100644 --- a/build/target +++ b/build/target @@ -30,13 +30,13 @@ namespace build // Target state. // - enum class target_state {unknown, unchanged, changed, failed}; + enum class target_state {unknown, postponed, unchanged, changed, failed}; // Recipe. // - // The returned target state should be either changed or unchanged. - // If there is an error, then the recipe should throw rather than - // returning failed. + // The returned target state should be changed, unchanged, or + // postponed. If there is an error, then the recipe should throw + // rather than returning failed. // // The recipe execution protocol is as follows: before executing // the recipe, the caller sets the target's state to failed. If @@ -44,10 +44,13 @@ namespace build // failed, then the caller sets it to the returned value. This // means that the recipe can set the target's state manually to // some other value. For example, setting it to unknown will - // result in the recipe to be executed again if this target is - // a prerequisite of another target. Note that in this case the + // result in the recipe to be executed again if this target is a + // prerequisite of another target. Note that in this case the // returned by the recipe value is still used (by the caller) as // the resulting target state for this execution of the recipe. + // Returning postponed from the last call to the recipe means + // that the action could not be executed at this time (see fsdir + // clean for an example). // using recipe_function = target_state (action, target&); using recipe = std::function; @@ -107,7 +110,7 @@ namespace build // action. It is incremented during the match phase and then decremented // during execution, before running the recipe. As a result, the recipe // can detect the last chance (i.e., last dependent) to execute the - // command ("front-running" vs "back-running" recipes). + // command (see alsoe first/last execution modes in ). // // Note that setting a new recipe (which happens when we match the rule // and which in turn is triggered by the first dependent) clears this -- cgit v1.1