aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-03-13 10:38:11 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-03-13 10:38:11 +0200
commit0cee33621a93d3348a1bf19a0c94441b717cbcbc (patch)
tree1f017e61794f705d9c0b872df108170cc4576b77
parentcf6b3e34b59ad120111e0c1ead779bbb3a70c38d (diff)
Add postponed recipe execution support
-rw-r--r--build/algorithm7
-rw-r--r--build/algorithm.cxx6
-rw-r--r--build/b.cxx34
-rw-r--r--build/rule.cxx15
-rw-r--r--build/target17
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<reference_wrapper<target>> tgs;
+ vector<reference_wrapper<target>> 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<recipe_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 <operation>).
//
// 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