};
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;
}
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();
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]";
/// 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,
Verbosity verbosity;
bool dry_run;
int parallelism;
+ int swallow_failures;
};
/// Builder wraps the build process: starting commands, updating status.
void FinishEdge(Edge* edge);
State* state_;
+ const BuildConfig& config_;
Plan plan_;
DiskInterface* disk_interface_;
CommandRunner* command_runner_;
(*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() {
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;
}
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);
+}