add support for ignoring failures of some subtasks
authorEvan Martin <martine@danga.com>
Mon, 9 May 2011 04:47:03 +0000 (21:47 -0700)
committerEvan Martin <martine@danga.com>
Mon, 9 May 2011 15:50:32 +0000 (08:50 -0700)
src/build.cc
src/build.h
src/build_test.cc

index 913bc31..2a280e0 100644 (file)
@@ -366,7 +366,7 @@ struct DryRunCommandRunner : public CommandRunner {
 };
 
 Builder::Builder(State* state, const BuildConfig& config)
-    : state_(state) {
+    : state_(state), config_(config) {
   disk_interface_ = new RealDiskInterface;
   if (config.dry_run)
     command_runner_ = new DryRunCommandRunner;
@@ -410,6 +410,7 @@ bool Builder::Build(string* err) {
   }
 
   status_->PlanHasTotalEdges(plan_.command_edge_count());
+  int failures_allowed = config_.swallow_failures;
   while (plan_.more_to_do()) {
     while (command_runner_->CanRunMore()) {
       Edge* edge = plan_.FindWork();
@@ -429,10 +430,13 @@ bool Builder::Build(string* err) {
     bool success;
     if (Edge* edge = command_runner_->NextFinishedCommand(&success)) {
       if (!success) {
-        *err = "subcommand failed";
-        return false;
+        if (--failures_allowed < 0) {
+          *err = "subcommand failed";
+          return false;
+        }
+      } else {
+        FinishEdge(edge);
       }
-      FinishEdge(edge);
     } else {
       if (!command_runner_->WaitForCommands()) {
         *err = "stuck [this is a bug]";
index feb83d5..96ae186 100644 (file)
@@ -80,7 +80,8 @@ struct CommandRunner {
 
 /// Options (e.g. verbosity, parallelism) passed to a build.
 struct BuildConfig {
-  BuildConfig() : verbosity(NORMAL), dry_run(false), parallelism(1) {}
+  BuildConfig() : verbosity(NORMAL), dry_run(false), parallelism(1),
+                  swallow_failures(0) {}
 
   enum Verbosity {
     NORMAL,
@@ -90,6 +91,7 @@ struct BuildConfig {
   Verbosity verbosity;
   bool dry_run;
   int parallelism;
+  int swallow_failures;
 };
 
 /// Builder wraps the build process: starting commands, updating status.
@@ -104,6 +106,7 @@ struct Builder {
   void FinishEdge(Edge* edge);
 
   State* state_;
+  const BuildConfig& config_;
   Plan plan_;
   DiskInterface* disk_interface_;
   CommandRunner* command_runner_;
index d55cae3..c067001 100644 (file)
@@ -253,13 +253,15 @@ bool BuildTest::StartCommand(Edge* edge) {
       (*out)->file_->mtime_ = now_;
       (*out)->dirty_ = false;
     }
-    last_command_ = edge;
-    return true;
+  } else if (edge->rule_->name_ == "fail") {
+    // Don't do anything.
   } else {
-    printf("unkown command\n");
+    printf("unknown command\n");
+    return false;
   }
 
-  return false;
+  last_command_ = edge;
+  return true;
 }
 
 bool BuildTest::WaitForCommands() {
@@ -269,7 +271,10 @@ bool BuildTest::WaitForCommands() {
 
 Edge* BuildTest::NextFinishedCommand(bool* success) {
   if (Edge* edge = last_command_) {
-    *success = true;
+    if (edge->rule_->name_ == "fail")
+      *success = false;
+    else
+      *success = true;
     last_command_ = NULL;
     return edge;
   }
@@ -517,3 +522,38 @@ TEST_F(BuildTest, Phony) {
   ASSERT_NE("", err);
 }
 
+TEST_F(BuildTest, Fail) {
+  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
+"rule fail\n"
+"  command = fail\n"
+"build out1: fail\n"));
+
+  string err;
+  EXPECT_TRUE(builder_.AddTarget("out1", &err));
+  ASSERT_EQ("", err);
+
+  EXPECT_FALSE(builder_.Build(&err));
+  ASSERT_EQ(1u, commands_ran_.size());
+  ASSERT_EQ("subcommand failed", err);
+}
+
+TEST_F(BuildTest, SwallowFailures) {
+  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
+"rule fail\n"
+"  command = fail\n"
+"build out1: fail\n"
+"build out2: fail\n"
+"build out3: fail\n"
+"build all: phony out1 out2 out3\n"));
+
+  // Swallow two failures, die on the third.
+  config_.swallow_failures = 2;
+
+  string err;
+  EXPECT_TRUE(builder_.AddTarget("all", &err));
+  ASSERT_EQ("", err);
+
+  EXPECT_FALSE(builder_.Build(&err));
+  ASSERT_EQ(3u, commands_ran_.size());
+  ASSERT_EQ("subcommand failed", err);
+}