From d515f551bb2f03ca6a39611ce8680f0d5255bc3e Mon Sep 17 00:00:00 2001 From: "Mikhail f. Shiryaev" Date: Tue, 3 Dec 2024 15:51:23 +0100 Subject: [PATCH] Propagate jobs' exit codes to the ninja's exit code Make all the methods return ExitStatus: Subprocess::Finish-> CommandRunner::Result.status-> Builder::Build-> NinjaMain::RunBuild Ninja now return different exit codes for Win32 and Posix systems: - Win32 -> 2 - Posix -> 130 (128+2) --- src/build.cc | 25 +++-- src/build.h | 9 +- src/build_test.cc | 226 ++++++++++++++++++++-------------------- src/exit_status.h | 26 ++++- src/ninja.cc | 24 ++--- src/subprocess-posix.cc | 14 ++- src/subprocess-win32.cc | 6 +- src/subprocess_test.cc | 17 ++- 8 files changed, 200 insertions(+), 147 deletions(-) diff --git a/src/build.cc b/src/build.cc index d256d940b0..b2687974e3 100644 --- a/src/build.cc +++ b/src/build.cc @@ -23,6 +23,7 @@ #include #include #include +#include "exit_status.h" #if defined(__SVR4) && defined(__sun) #include @@ -39,7 +40,6 @@ #include "metrics.h" #include "state.h" #include "status.h" -#include "subprocess.h" #include "util.h" using namespace std; @@ -687,7 +687,7 @@ bool Builder::AlreadyUpToDate() const { return !plan_.more_to_do(); } -bool Builder::Build(string* err) { +ExitStatus Builder::Build(string* err) { assert(!AlreadyUpToDate()); plan_.PrepareQueue(); @@ -726,14 +726,14 @@ bool Builder::Build(string* err) { if (!StartEdge(edge, err)) { Cleanup(); status_->BuildFinished(); - return false; + return ExitFailure; } if (edge->is_phony()) { if (!plan_.EdgeFinished(edge, Plan::kEdgeSucceeded, err)) { Cleanup(); status_->BuildFinished(); - return false; + return ExitFailure; } } else { ++pending_commands; @@ -760,14 +760,16 @@ bool Builder::Build(string* err) { Cleanup(); status_->BuildFinished(); *err = "interrupted by user"; - return false; + return result.status; } --pending_commands; - if (!FinishCommand(&result, err)) { + bool command_finished = FinishCommand(&result, err); + SetExitCode(result.status); + if (!command_finished) { Cleanup(); status_->BuildFinished(); - return false; + return result.status; } if (!result.success()) { @@ -791,11 +793,11 @@ bool Builder::Build(string* err) { else *err = "stuck [this is a bug]"; - return false; + return GetExitCode(); } status_->BuildFinished(); - return true; + return ExitSuccess; } bool Builder::StartEdge(Edge* edge, string* err) { @@ -1036,3 +1038,8 @@ bool Builder::LoadDyndeps(Node* node, string* err) { return true; } + +void Builder::SetExitCode(ExitStatus code) { + // Set code to the most recent error + if (code != 0) exit_code_ = code; +} diff --git a/src/build.h b/src/build.h index ba39e7728a..6ce2174db0 100644 --- a/src/build.h +++ b/src/build.h @@ -211,7 +211,7 @@ struct Builder { /// Run the build. Returns false on error. /// It is an error to call this function when AlreadyUpToDate() is true. - bool Build(std::string* err); + ExitStatus Build(std::string* err); bool StartEdge(Edge* edge, std::string* err); @@ -233,6 +233,9 @@ struct Builder { std::unique_ptr command_runner_; Status* status_; + // Get the global ExitStatus for the build + ExitStatus GetExitCode() { return exit_code_; } + private: bool ExtractDeps(CommandRunner::Result* result, const std::string& deps_type, const std::string& deps_prefix, @@ -253,6 +256,10 @@ struct Builder { DependencyScan scan_; + /// Keep the global exit code for the build + ExitStatus exit_code_; + void SetExitCode(ExitStatus code); + // Unimplemented copy ctor and operator= ensure we don't copy the auto_ptr. Builder(const Builder &other); // DO NOT IMPLEMENT void operator=(const Builder &other); // DO NOT IMPLEMENT diff --git a/src/build_test.cc b/src/build_test.cc index 7675aceecf..26050c3e55 100644 --- a/src/build_test.cc +++ b/src/build_test.cc @@ -16,10 +16,12 @@ #include #include +#include #include #include "build_log.h" #include "deps_log.h" +#include "exit_status.h" #include "graph.h" #include "status_printer.h" #include "test.h" @@ -616,8 +618,8 @@ void BuildTest::RebuildTarget(const string& target, const char* manifest, command_runner_.commands_ran_.clear(); builder.command_runner_.reset(&command_runner_); if (!builder.AlreadyUpToDate()) { - bool build_res = builder.Build(&err); - EXPECT_TRUE(build_res); + ExitStatus build_res = builder.Build(&err); + EXPECT_EQ(build_res, ExitSuccess); } builder.command_runner_.release(); } @@ -823,7 +825,7 @@ TEST_F(BuildTest, OneStep) { string err; EXPECT_TRUE(builder_.AddTarget("cat1", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); ASSERT_EQ("", err); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); @@ -837,7 +839,7 @@ TEST_F(BuildTest, OneStep2) { string err; EXPECT_TRUE(builder_.AddTarget("cat1", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); @@ -848,7 +850,7 @@ TEST_F(BuildTest, TwoStep) { string err; EXPECT_TRUE(builder_.AddTarget("cat12", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); ASSERT_EQ(3u, command_runner_.commands_ran_.size()); // Depending on how the pointers work out, we could've ran @@ -868,7 +870,7 @@ TEST_F(BuildTest, TwoStep) { state_.Reset(); EXPECT_TRUE(builder_.AddTarget("cat12", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); ASSERT_EQ("", err); ASSERT_EQ(5u, command_runner_.commands_ran_.size()); EXPECT_EQ("cat in1 in2 > cat2", command_runner_.commands_ran_[3]); @@ -886,7 +888,7 @@ TEST_F(BuildTest, TwoOutputs) { string err; EXPECT_TRUE(builder_.AddTarget("out1", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); EXPECT_EQ("touch out1 out2", command_runner_.commands_ran_[0]); @@ -902,7 +904,7 @@ TEST_F(BuildTest, ImplicitOutput) { string err; EXPECT_TRUE(builder_.AddTarget("out.imp", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); EXPECT_EQ("touch out out.imp", command_runner_.commands_ran_[0]); @@ -924,7 +926,7 @@ TEST_F(BuildTest, MultiOutIn) { string err; EXPECT_TRUE(builder_.AddTarget("out", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); } @@ -940,7 +942,7 @@ TEST_F(BuildTest, Chain) { string err; EXPECT_TRUE(builder_.AddTarget("c5", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); ASSERT_EQ(4u, command_runner_.commands_ran_.size()); @@ -960,7 +962,7 @@ TEST_F(BuildTest, Chain) { EXPECT_TRUE(builder_.AddTarget("c5", &err)); ASSERT_EQ("", err); EXPECT_FALSE(builder_.AlreadyUpToDate()); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); ASSERT_EQ(2u, command_runner_.commands_ran_.size()); // 3->4, 4->5 } @@ -1001,7 +1003,7 @@ TEST_F(BuildTest, MakeDirs) { EXPECT_TRUE(builder_.AddTarget("subdir/dir2/file", &err)); EXPECT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); ASSERT_EQ("", err); ASSERT_EQ(2u, fs_.directories_made_.size()); EXPECT_EQ("subdir", fs_.directories_made_[0]); @@ -1086,7 +1088,7 @@ TEST_F(BuildTest, EncounterReadyTwice) { EXPECT_TRUE(builder_.AddTarget("a", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); ASSERT_EQ("", err); ASSERT_EQ(2u, command_runner_.commands_ran_.size()); } @@ -1119,7 +1121,7 @@ TEST_F(BuildTest, OrderOnlyDeps) { ASSERT_EQ("cc foo.c", edge->EvaluateCommand()); // explicit dep dirty, expect a rebuild. - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); ASSERT_EQ("", err); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); @@ -1134,7 +1136,7 @@ TEST_F(BuildTest, OrderOnlyDeps) { command_runner_.commands_ran_.clear(); state_.Reset(); EXPECT_TRUE(builder_.AddTarget("foo.o", &err)); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); ASSERT_EQ("", err); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); @@ -1156,7 +1158,7 @@ TEST_F(BuildTest, OrderOnlyDeps) { command_runner_.commands_ran_.clear(); state_.Reset(); EXPECT_TRUE(builder_.AddTarget("foo.o", &err)); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); ASSERT_EQ("", err); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); } @@ -1174,7 +1176,7 @@ TEST_F(BuildTest, RebuildOrderOnlyDeps) { // foo.o and order-only dep dirty, build both. EXPECT_TRUE(builder_.AddTarget("foo.o", &err)); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); ASSERT_EQ("", err); ASSERT_EQ(2u, command_runner_.commands_ran_.size()); @@ -1190,7 +1192,7 @@ TEST_F(BuildTest, RebuildOrderOnlyDeps) { command_runner_.commands_ran_.clear(); state_.Reset(); EXPECT_TRUE(builder_.AddTarget("foo.o", &err)); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); ASSERT_EQ("", err); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); ASSERT_EQ("cc oo.h.in", command_runner_.commands_ran_[0]); @@ -1202,7 +1204,7 @@ TEST_F(BuildTest, RebuildOrderOnlyDeps) { command_runner_.commands_ran_.clear(); state_.Reset(); EXPECT_TRUE(builder_.AddTarget("foo.o", &err)); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); ASSERT_EQ("", err); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); ASSERT_EQ("cc oo.h.in", command_runner_.commands_ran_[0]); @@ -1251,7 +1253,7 @@ TEST_F(BuildTest, Phony) { // Only one command to run, because phony runs no command. EXPECT_FALSE(builder_.AlreadyUpToDate()); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); ASSERT_EQ("", err); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); } @@ -1345,7 +1347,7 @@ void TestPhonyUseCase(BuildTest* t, int i) { ASSERT_EQ("", err); EXPECT_TRUE(builder_.AddTarget("test6", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); ASSERT_EQ("", err); string ci; @@ -1364,7 +1366,7 @@ void TestPhonyUseCase(BuildTest* t, int i) { EXPECT_TRUE(builder_.AddTarget("test" + ci, &err)); ASSERT_EQ("", err); if (!builder_.AlreadyUpToDate()) { - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); } ASSERT_EQ("", err); @@ -1379,7 +1381,7 @@ void TestPhonyUseCase(BuildTest* t, int i) { // Second build, expect testN edge to be rebuilt // and phonyN node's mtime to be updated. EXPECT_FALSE(builder_.AlreadyUpToDate()); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); ASSERT_EQ("", err); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); EXPECT_EQ(string("touch test") + ci, command_runner_.commands_ran_[0]); @@ -1405,7 +1407,7 @@ void TestPhonyUseCase(BuildTest* t, int i) { EXPECT_TRUE(builder_.AddTarget("test" + ci, &err)); ASSERT_EQ("", err); EXPECT_FALSE(builder_.AlreadyUpToDate()); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); ASSERT_EQ("", err); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); EXPECT_EQ("touch test" + ci, command_runner_.commands_ran_[0]); @@ -1415,7 +1417,7 @@ void TestPhonyUseCase(BuildTest* t, int i) { EXPECT_TRUE(builder_.AddTarget("test" + ci, &err)); ASSERT_EQ("", err); EXPECT_FALSE(builder_.AlreadyUpToDate()); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); ASSERT_EQ("", err); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); EXPECT_EQ("touch test" + ci, command_runner_.commands_ran_[0]); @@ -1439,7 +1441,7 @@ TEST_F(BuildTest, Fail) { EXPECT_TRUE(builder_.AddTarget("out1", &err)); ASSERT_EQ("", err); - EXPECT_FALSE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitFailure); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); ASSERT_EQ("subcommand failed", err); } @@ -1460,7 +1462,7 @@ TEST_F(BuildTest, SwallowFailures) { EXPECT_TRUE(builder_.AddTarget("all", &err)); ASSERT_EQ("", err); - EXPECT_FALSE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitFailure); ASSERT_EQ(3u, command_runner_.commands_ran_.size()); ASSERT_EQ("subcommands failed", err); } @@ -1481,7 +1483,7 @@ TEST_F(BuildTest, SwallowFailuresLimit) { EXPECT_TRUE(builder_.AddTarget("final", &err)); ASSERT_EQ("", err); - EXPECT_FALSE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitFailure); ASSERT_EQ(3u, command_runner_.commands_ran_.size()); ASSERT_EQ("cannot make progress due to previous errors", err); } @@ -1505,7 +1507,7 @@ TEST_F(BuildTest, SwallowFailuresPool) { EXPECT_TRUE(builder_.AddTarget("final", &err)); ASSERT_EQ("", err); - EXPECT_FALSE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitFailure); ASSERT_EQ(3u, command_runner_.commands_ran_.size()); ASSERT_EQ("cannot make progress due to previous errors", err); } @@ -1579,7 +1581,7 @@ TEST_F(BuildWithLogTest, ImplicitGeneratedOutOfDate2) { EXPECT_TRUE(builder_.AddTarget("out.imp", &err)); EXPECT_FALSE(builder_.AlreadyUpToDate()); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_TRUE(builder_.AlreadyUpToDate()); command_runner_.commands_ran_.clear(); @@ -1602,7 +1604,7 @@ TEST_F(BuildWithLogTest, ImplicitGeneratedOutOfDate2) { EXPECT_TRUE(builder_.AddTarget("out.imp", &err)); EXPECT_FALSE(builder_.AlreadyUpToDate()); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_TRUE(builder_.AlreadyUpToDate()); command_runner_.commands_ran_.clear(); @@ -1636,7 +1638,7 @@ TEST_F(BuildWithLogTest, NotInLogButOnDisk) { state_.Reset(); EXPECT_TRUE(builder_.AddTarget("out1", &err)); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_TRUE(builder_.AlreadyUpToDate()); } @@ -1652,7 +1654,7 @@ TEST_F(BuildWithLogTest, RebuildAfterFailure) { // Run once successfully to get out1 in the log EXPECT_TRUE(builder_.AddTarget("out1", &err)); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); EXPECT_EQ(1u, command_runner_.commands_ran_.size()); @@ -1666,7 +1668,7 @@ TEST_F(BuildWithLogTest, RebuildAfterFailure) { // Run again with a failure that updates the output file timestamp EXPECT_TRUE(builder_.AddTarget("out1", &err)); - EXPECT_FALSE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitFailure); EXPECT_EQ("subcommand failed", err); EXPECT_EQ(1u, command_runner_.commands_ran_.size()); @@ -1680,7 +1682,7 @@ TEST_F(BuildWithLogTest, RebuildAfterFailure) { // Run again, should rerun even though the output file is up to date on disk EXPECT_TRUE(builder_.AddTarget("out1", &err)); EXPECT_FALSE(builder_.AlreadyUpToDate()); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ(1u, command_runner_.commands_ran_.size()); EXPECT_EQ("", err); } @@ -1698,7 +1700,7 @@ TEST_F(BuildWithLogTest, RebuildWithNoInputs) { EXPECT_TRUE(builder_.AddTarget("out1", &err)); EXPECT_TRUE(builder_.AddTarget("out2", &err)); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); EXPECT_EQ(2u, command_runner_.commands_ran_.size()); @@ -1711,7 +1713,7 @@ TEST_F(BuildWithLogTest, RebuildWithNoInputs) { EXPECT_TRUE(builder_.AddTarget("out1", &err)); EXPECT_TRUE(builder_.AddTarget("out2", &err)); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); EXPECT_EQ(1u, command_runner_.commands_ran_.size()); } @@ -1742,7 +1744,7 @@ TEST_F(BuildWithLogTest, RestatTest) { string err; EXPECT_TRUE(builder_.AddTarget("out3", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); ASSERT_EQ("", err); EXPECT_EQ(3u, command_runner_.commands_ran_.size()); EXPECT_EQ(3u, builder_.plan_.command_edge_count()); @@ -1756,7 +1758,7 @@ TEST_F(BuildWithLogTest, RestatTest) { // touch out2, we should cancel the build of out3. EXPECT_TRUE(builder_.AddTarget("out3", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); ASSERT_EQ(2u, command_runner_.commands_ran_.size()); // If we run again, it should be a no-op, because the build log has recorded @@ -1777,7 +1779,7 @@ TEST_F(BuildWithLogTest, RestatTest) { state_.Reset(); EXPECT_TRUE(builder_.AddTarget("out3", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); ASSERT_EQ(2u, command_runner_.commands_ran_.size()); } @@ -1804,7 +1806,7 @@ TEST_F(BuildWithLogTest, RestatMissingFile) { string err; EXPECT_TRUE(builder_.AddTarget("out2", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); ASSERT_EQ("", err); command_runner_.commands_ran_.clear(); state_.Reset(); @@ -1818,7 +1820,7 @@ TEST_F(BuildWithLogTest, RestatMissingFile) { // we shouldn't run the dependent build. EXPECT_TRUE(builder_.AddTarget("out2", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); } @@ -1840,7 +1842,7 @@ TEST_F(BuildWithLogTest, RestatSingleDependentOutputDirty) { string err; EXPECT_TRUE(builder_.AddTarget("out4", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); ASSERT_EQ("", err); ASSERT_EQ(3u, command_runner_.commands_ran_.size()); @@ -1857,7 +1859,7 @@ TEST_F(BuildWithLogTest, RestatSingleDependentOutputDirty) { state_.Reset(); EXPECT_TRUE(builder_.AddTarget("out4", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); ASSERT_EQ("", err); ASSERT_EQ(3u, command_runner_.commands_ran_.size()); } @@ -1889,7 +1891,7 @@ TEST_F(BuildWithLogTest, RestatMissingInput) { string err; EXPECT_TRUE(builder_.AddTarget("out2", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); ASSERT_EQ(2u, command_runner_.commands_ran_.size()); // See that an entry in the logfile is created, capturing @@ -1907,7 +1909,7 @@ TEST_F(BuildWithLogTest, RestatMissingInput) { state_.Reset(); EXPECT_TRUE(builder_.AddTarget("out2", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); // Check that the logfile entry remains correctly set @@ -1933,7 +1935,7 @@ TEST_F(BuildWithLogTest, RestatInputChangesDueToRule) { string err; EXPECT_TRUE(builder_.AddTarget("out1", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); ASSERT_EQ("", err); EXPECT_EQ(2u, command_runner_.commands_ran_.size()); EXPECT_EQ(2u, builder_.plan_.command_edge_count()); @@ -1956,7 +1958,7 @@ TEST_F(BuildWithLogTest, RestatInputChangesDueToRule) { EXPECT_TRUE(builder_.AddTarget("out1", &err)); ASSERT_EQ("", err); EXPECT_TRUE(!state_.GetNode("out1", 0)->dirty()); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); ASSERT_EQ("", err); EXPECT_EQ(1u, command_runner_.commands_ran_.size()); EXPECT_EQ(1u, builder_.plan_.command_edge_count()); @@ -1977,7 +1979,7 @@ TEST_F(BuildWithLogTest, GeneratedPlainDepfileMtime) { EXPECT_TRUE(builder_.AddTarget("out", &err)); EXPECT_FALSE(builder_.AlreadyUpToDate()); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_TRUE(builder_.AlreadyUpToDate()); command_runner_.commands_ran_.clear(); @@ -2020,7 +2022,7 @@ TEST_F(BuildDryRun, AllCommandsShown) { string err; EXPECT_TRUE(builder_.AddTarget("out3", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); ASSERT_EQ(3u, command_runner_.commands_ran_.size()); } @@ -2063,7 +2065,7 @@ TEST_F(BuildTest, RspFileSuccess) size_t files_created = fs_.files_created_.size(); size_t files_removed = fs_.files_removed_.size(); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); ASSERT_EQ(3u, command_runner_.commands_ran_.size()); // The RSP files and temp file to acquire output mtimes were created @@ -2100,7 +2102,7 @@ TEST_F(BuildTest, RspFileFailure) { size_t files_created = fs_.files_created_.size(); size_t files_removed = fs_.files_removed_.size(); - EXPECT_FALSE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitFailure); ASSERT_EQ("subcommand failed", err); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); @@ -2138,7 +2140,7 @@ TEST_F(BuildWithLogTest, RspFileCmdLineChange) { ASSERT_EQ("", err); // 1. Build for the 1st time (-> populate log) - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); // 2. Build again (no change) @@ -2161,7 +2163,7 @@ TEST_F(BuildWithLogTest, RspFileCmdLineChange) { state_.Reset(); EXPECT_TRUE(builder_.AddTarget("out", &err)); EXPECT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ(1u, command_runner_.commands_ran_.size()); } @@ -2184,7 +2186,7 @@ TEST_F(BuildTest, InterruptCleanup) { string err; EXPECT_TRUE(builder_.AddTarget("out1", &err)); EXPECT_EQ("", err); - EXPECT_FALSE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitInterrupted); EXPECT_EQ("interrupted by user", err); builder_.Cleanup(); EXPECT_GT(fs_.Stat("out1", &err), 0); @@ -2193,7 +2195,7 @@ TEST_F(BuildTest, InterruptCleanup) { // A touched output of an interrupted command should be deleted. EXPECT_TRUE(builder_.AddTarget("out2", &err)); EXPECT_EQ("", err); - EXPECT_FALSE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitInterrupted); EXPECT_EQ("interrupted by user", err); builder_.Cleanup(); EXPECT_EQ(0, fs_.Stat("out2", &err)); @@ -2235,7 +2237,7 @@ TEST_F(BuildTest, PhonyWithNoInputs) { state_.Reset(); EXPECT_TRUE(builder_.AddTarget("out2", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); } @@ -2253,7 +2255,7 @@ TEST_F(BuildTest, DepsGccWithEmptyDepfileErrorsOut) { ASSERT_EQ("", err); EXPECT_FALSE(builder_.AlreadyUpToDate()); - EXPECT_FALSE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitFailure); ASSERT_EQ("subcommand failed", err); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); } @@ -2301,7 +2303,7 @@ TEST_F(BuildTest, FailedDepsParse) { // path to the left of the colon. fs_.Create("in1.d", "AAA BBB"); - EXPECT_FALSE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitFailure); EXPECT_EQ("subcommand failed", err); } @@ -2341,7 +2343,7 @@ TEST_F(BuildWithQueryDepsLogTest, TwoOutputsDepFileMSVC) { std::string err; EXPECT_TRUE(builder_.AddTarget("out1", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); EXPECT_EQ("echo 'using in1' && for file in out1 out2; do cp in1 $file; done", command_runner_.commands_ran_[0]); @@ -2370,7 +2372,7 @@ TEST_F(BuildWithQueryDepsLogTest, TwoOutputsDepFileGCCOneLine) { EXPECT_TRUE(builder_.AddTarget("out1", &err)); ASSERT_EQ("", err); fs_.Create("in.d", "out1 out2: in1 in2"); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); EXPECT_EQ("echo 'out1 out2: in1 in2' > in.d && for file in out1 out2; do cp in1 $file; done", command_runner_.commands_ran_[0]); @@ -2401,7 +2403,7 @@ TEST_F(BuildWithQueryDepsLogTest, TwoOutputsDepFileGCCMultiLineInput) { EXPECT_TRUE(builder_.AddTarget("out1", &err)); ASSERT_EQ("", err); fs_.Create("in.d", "out1 out2: in1\nout1 out2: in2"); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); EXPECT_EQ("echo 'out1 out2: in1\\nout1 out2: in2' > in.d && for file in out1 out2; do cp in1 $file; done", command_runner_.commands_ran_[0]); @@ -2432,7 +2434,7 @@ TEST_F(BuildWithQueryDepsLogTest, TwoOutputsDepFileGCCMultiLineOutput) { EXPECT_TRUE(builder_.AddTarget("out1", &err)); ASSERT_EQ("", err); fs_.Create("in.d", "out1: in1 in2\nout2: in1 in2"); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); EXPECT_EQ("echo 'out1: in1 in2\\nout2: in1 in2' > in.d && for file in out1 out2; do cp in1 $file; done", command_runner_.commands_ran_[0]); @@ -2463,7 +2465,7 @@ TEST_F(BuildWithQueryDepsLogTest, TwoOutputsDepFileGCCOnlyMainOutput) { EXPECT_TRUE(builder_.AddTarget("out1", &err)); ASSERT_EQ("", err); fs_.Create("in.d", "out1: in1 in2"); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); EXPECT_EQ("echo 'out1: in1 in2' > in.d && for file in out1 out2; do cp in1 $file; done", command_runner_.commands_ran_[0]); @@ -2496,7 +2498,7 @@ TEST_F(BuildWithQueryDepsLogTest, TwoOutputsDepFileGCCOnlySecondaryOutput) { EXPECT_TRUE(builder_.AddTarget("out1", &err)); ASSERT_EQ("", err); fs_.Create("in.d", "out2: in1 in2"); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); EXPECT_EQ("echo 'out2: in1 in2' > in.d && for file in out1 out2; do cp in1 $file; done", command_runner_.commands_ran_[0]); @@ -2564,7 +2566,7 @@ TEST_F(BuildWithDepsLogTest, Straightforward) { EXPECT_TRUE(builder.AddTarget("out", &err)); ASSERT_EQ("", err); fs_.Create("in1.d", "out: in2"); - EXPECT_TRUE(builder.Build(&err)); + EXPECT_EQ(builder.Build(&err), ExitSuccess); EXPECT_EQ("", err); // The deps file should have been removed. @@ -2594,7 +2596,7 @@ TEST_F(BuildWithDepsLogTest, Straightforward) { command_runner_.commands_ran_.clear(); EXPECT_TRUE(builder.AddTarget("out", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder.Build(&err)); + EXPECT_EQ(builder.Build(&err), ExitSuccess); EXPECT_EQ("", err); // We should have rebuilt the output due to in2 being @@ -2634,7 +2636,7 @@ TEST_F(BuildWithDepsLogTest, ObsoleteDeps) { builder.command_runner_.reset(&command_runner_); EXPECT_TRUE(builder.AddTarget("out", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder.Build(&err)); + EXPECT_EQ(builder.Build(&err), ExitSuccess); EXPECT_EQ("", err); deps_log.Close(); @@ -2668,7 +2670,7 @@ TEST_F(BuildWithDepsLogTest, ObsoleteDeps) { // Recreate the deps file here because the build expects them to exist. fs_.Create("in1.d", "out: "); - EXPECT_TRUE(builder.Build(&err)); + EXPECT_EQ(builder.Build(&err), ExitSuccess); EXPECT_EQ("", err); // We should have rebuilt the output due to the deps being @@ -2702,7 +2704,7 @@ TEST_F(BuildWithDepsLogTest, DepsIgnoredInDryRun) { string err; EXPECT_TRUE(builder.AddTarget("out", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder.Build(&err)); + EXPECT_EQ(builder.Build(&err), ExitSuccess); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); builder.command_runner_.release(); @@ -2737,7 +2739,7 @@ TEST_F(BuildWithDepsLogTest, TestInputMtimeRaceCondition) { // Run the build, out gets built, dep file is created EXPECT_TRUE(builder.AddTarget("out", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder.Build(&err)); + EXPECT_EQ(builder.Build(&err), ExitSuccess); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); // See that an entry in the logfile is created. the input_mtime is 1 since that was @@ -2761,7 +2763,7 @@ TEST_F(BuildWithDepsLogTest, TestInputMtimeRaceCondition) { state.Reset(); EXPECT_TRUE(builder.AddTarget("out", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder.Build(&err)); + EXPECT_EQ(builder.Build(&err), ExitSuccess); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); // Check that the logfile entry is still correct @@ -2817,7 +2819,7 @@ TEST_F(BuildWithDepsLogTest, TestInputMtimeRaceConditionWithDepFile) { // Run the build, out gets built, dep file is created EXPECT_TRUE(builder.AddTarget("out", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder.Build(&err)); + EXPECT_EQ(builder.Build(&err), ExitSuccess); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); // See that an entry in the logfile is created. the mtime is 1 due to the command @@ -2839,7 +2841,7 @@ TEST_F(BuildWithDepsLogTest, TestInputMtimeRaceConditionWithDepFile) { state.Reset(); EXPECT_TRUE(builder.AddTarget("out", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder.Build(&err)); + EXPECT_EQ(builder.Build(&err), ExitSuccess); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); builder.command_runner_.release(); @@ -2874,7 +2876,7 @@ TEST_F(BuildWithDepsLogTest, TestInputMtimeRaceConditionWithDepFile) { state.Reset(); EXPECT_TRUE(builder.AddTarget("out", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder.Build(&err)); + EXPECT_EQ(builder.Build(&err), ExitSuccess); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); builder.command_runner_.release(); @@ -2890,7 +2892,7 @@ TEST_F(BuildWithDepsLogTest, TestInputMtimeRaceConditionWithDepFile) { state.Reset(); EXPECT_TRUE(builder.AddTarget("out", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder.Build(&err)); + EXPECT_EQ(builder.Build(&err), ExitSuccess); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); builder.command_runner_.release(); @@ -2930,7 +2932,7 @@ TEST_F(BuildTest, RestatDepfileDependency) { string err; EXPECT_TRUE(builder_.AddTarget("out", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); } @@ -2962,7 +2964,7 @@ TEST_F(BuildWithDepsLogTest, RestatDepfileDependencyDepsLog) { EXPECT_TRUE(builder.AddTarget("out", &err)); ASSERT_EQ("", err); fs_.Create("in1.d", "out: header.h"); - EXPECT_TRUE(builder.Build(&err)); + EXPECT_EQ(builder.Build(&err), ExitSuccess); EXPECT_EQ("", err); deps_log.Close(); @@ -2988,7 +2990,7 @@ TEST_F(BuildWithDepsLogTest, RestatDepfileDependencyDepsLog) { command_runner_.commands_ran_.clear(); EXPECT_TRUE(builder.AddTarget("out", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder.Build(&err)); + EXPECT_EQ(builder.Build(&err), ExitSuccess); EXPECT_EQ("", err); // Rule "true" should have run again, but the build of "out" should have @@ -3021,7 +3023,7 @@ TEST_F(BuildWithDepsLogTest, DepFileOKDepsLog) { EXPECT_TRUE(builder.AddTarget("fo o.o", &err)); ASSERT_EQ("", err); fs_.Create("fo o.o.d", "fo\\ o.o: blah.h bar.h\n"); - EXPECT_TRUE(builder.Build(&err)); + EXPECT_EQ(builder.Build(&err), ExitSuccess); EXPECT_EQ("", err); deps_log.Close(); @@ -3092,7 +3094,7 @@ TEST_F(BuildWithDepsLogTest, DiscoveredDepDuringBuildChanged) { EXPECT_TRUE(builder.AddTarget("out2", &err)); EXPECT_FALSE(builder.AlreadyUpToDate()); - EXPECT_TRUE(builder.Build(&err)); + EXPECT_EQ(builder.Build(&err), ExitSuccess); EXPECT_TRUE(builder.AlreadyUpToDate()); deps_log.Close(); @@ -3116,7 +3118,7 @@ TEST_F(BuildWithDepsLogTest, DiscoveredDepDuringBuildChanged) { EXPECT_TRUE(builder.AddTarget("out2", &err)); EXPECT_FALSE(builder.AlreadyUpToDate()); - EXPECT_TRUE(builder.Build(&err)); + EXPECT_EQ(builder.Build(&err), ExitSuccess); EXPECT_TRUE(builder.AlreadyUpToDate()); deps_log.Close(); @@ -3169,7 +3171,7 @@ TEST_F(BuildWithDepsLogTest, DepFileDepsLogCanonicalize) { // Note, different slashes from manifest. fs_.Create("a/b\\c\\d/e/fo o.o.d", "a\\b\\c\\d\\e\\fo\\ o.o: blah.h bar.h\n"); - EXPECT_TRUE(builder.Build(&err)); + EXPECT_EQ(builder.Build(&err), ExitSuccess); EXPECT_EQ("", err); deps_log.Close(); @@ -3320,7 +3322,7 @@ TEST_F(BuildTest, Console) { string err; EXPECT_TRUE(builder_.AddTarget("cons", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); } @@ -3361,7 +3363,7 @@ TEST_F(BuildTest, DyndepReadyImplicitConnection) { string err; EXPECT_TRUE(builder_.AddTarget("out", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); ASSERT_EQ(2u, command_runner_.commands_ran_.size()); EXPECT_EQ("touch tmp tmp.imp", command_runner_.commands_ran_[0]); @@ -3428,7 +3430,7 @@ TEST_F(BuildTest, DyndepBuild) { EXPECT_EQ("", err); size_t files_created = fs_.files_created_.size(); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); ASSERT_EQ(2u, command_runner_.commands_ran_.size()); @@ -3491,7 +3493,7 @@ TEST_F(BuildTest, DyndepBuildUnrelatedOutput) { EXPECT_TRUE(builder_.AddTarget("out", &err)); EXPECT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); ASSERT_EQ(3u, command_runner_.commands_ran_.size()); EXPECT_EQ("cp dd-in dd", command_runner_.commands_ran_[0]); @@ -3523,7 +3525,7 @@ TEST_F(BuildTest, DyndepBuildDiscoverNewOutput) { EXPECT_TRUE(builder_.AddTarget("out", &err)); EXPECT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); ASSERT_EQ(2u, command_runner_.commands_ran_.size()); EXPECT_EQ("cp dd-in dd", command_runner_.commands_ran_[0]); @@ -3625,7 +3627,7 @@ TEST_F(BuildTest, DyndepBuildDiscoverNewInput) { EXPECT_TRUE(builder_.AddTarget("out", &err)); EXPECT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); ASSERT_EQ(3u, command_runner_.commands_ran_.size()); EXPECT_EQ("cp dd-in dd", command_runner_.commands_ran_[0]); @@ -3685,7 +3687,7 @@ TEST_F(BuildTest, DyndepBuildDiscoverNewInputWithTransitiveValidation) { EXPECT_TRUE(builder_.AddTarget("out", &err)); EXPECT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); ASSERT_EQ(4u, command_runner_.commands_ran_.size()); EXPECT_EQ("cp dd-in dd", command_runner_.commands_ran_[0]); @@ -3718,7 +3720,7 @@ TEST_F(BuildTest, DyndepBuildDiscoverImplicitConnection) { string err; EXPECT_TRUE(builder_.AddTarget("out", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); ASSERT_EQ(3u, command_runner_.commands_ran_.size()); EXPECT_EQ("cp dd-in dd", command_runner_.commands_ran_[0]); @@ -3754,7 +3756,7 @@ TEST_F(BuildTest, DyndepBuildDiscoverOutputAndDepfileInput) { // Loading the depfile did not give tmp.imp a phony input edge. ASSERT_FALSE(GetNode("tmp.imp")->in_edge()); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); // Loading the dyndep file gave tmp.imp a real input edge. @@ -3793,7 +3795,7 @@ TEST_F(BuildTest, DyndepBuildDiscoverNowWantEdge) { string err; EXPECT_TRUE(builder_.AddTarget("out", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); ASSERT_EQ(3u, command_runner_.commands_ran_.size()); EXPECT_EQ("cp dd-in dd", command_runner_.commands_ran_[0]); @@ -3824,7 +3826,7 @@ TEST_F(BuildTest, DyndepBuildDiscoverNowWantEdgeAndDependent) { string err; EXPECT_TRUE(builder_.AddTarget("out", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); ASSERT_EQ(3u, command_runner_.commands_ran_.size()); EXPECT_EQ("cp dd-in dd", command_runner_.commands_ran_[0]); @@ -3895,7 +3897,7 @@ TEST_F(BuildWithLogTest, DyndepBuildDiscoverRestat) { string err; EXPECT_TRUE(builder_.AddTarget("out2", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); ASSERT_EQ("", err); ASSERT_EQ(3u, command_runner_.commands_ran_.size()); EXPECT_EQ("cp dd-in dd", command_runner_.commands_ran_[0]); @@ -3911,7 +3913,7 @@ TEST_F(BuildWithLogTest, DyndepBuildDiscoverRestat) { // touch "out1", we should cancel the build of "out2". EXPECT_TRUE(builder_.AddTarget("out2", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); EXPECT_EQ("true", command_runner_.commands_ran_[0]); } @@ -3952,7 +3954,7 @@ TEST_F(BuildTest, DyndepBuildDiscoverScheduledEdge) { EXPECT_TRUE(builder_.AddTarget("out1", &err)); EXPECT_TRUE(builder_.AddTarget("out2", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); ASSERT_EQ(3u, command_runner_.commands_ran_.size()); // Depending on how the pointers in Plan::ready_ work out, the first @@ -4003,7 +4005,7 @@ TEST_F(BuildTest, DyndepTwoLevelDirect) { string err; EXPECT_TRUE(builder_.AddTarget("out2", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); ASSERT_EQ(3u, command_runner_.commands_ran_.size()); EXPECT_EQ("cp dd1-in dd1", command_runner_.commands_ran_[0]); @@ -4048,7 +4050,7 @@ TEST_F(BuildTest, DyndepTwoLevelIndirect) { string err; EXPECT_TRUE(builder_.AddTarget("out2", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); ASSERT_EQ(3u, command_runner_.commands_ran_.size()); EXPECT_EQ("cp dd1-in dd1", command_runner_.commands_ran_[0]); @@ -4088,7 +4090,7 @@ TEST_F(BuildTest, DyndepTwoLevelDiscoveredReady) { EXPECT_TRUE(builder_.AddTarget("out", &err)); EXPECT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); ASSERT_EQ(4u, command_runner_.commands_ran_.size()); EXPECT_EQ("cp dd1-in dd1", command_runner_.commands_ran_[0]); @@ -4128,7 +4130,7 @@ TEST_F(BuildTest, DyndepTwoLevelDiscoveredDirty) { EXPECT_TRUE(builder_.AddTarget("out", &err)); EXPECT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); ASSERT_EQ(5u, command_runner_.commands_ran_.size()); EXPECT_EQ("cp dd1-in dd1", command_runner_.commands_ran_[0]); @@ -4150,7 +4152,7 @@ TEST_F(BuildTest, Validation) { EXPECT_TRUE(builder_.AddTarget("out", &err)); EXPECT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); EXPECT_EQ(2u, command_runner_.commands_ran_.size()); @@ -4166,7 +4168,7 @@ TEST_F(BuildTest, Validation) { EXPECT_TRUE(builder_.AddTarget("out", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); @@ -4183,7 +4185,7 @@ TEST_F(BuildTest, Validation) { EXPECT_TRUE(builder_.AddTarget("out", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); @@ -4202,7 +4204,7 @@ TEST_F(BuildTest, ValidationDependsOnOutput) { EXPECT_TRUE(builder_.AddTarget("out", &err)); EXPECT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); EXPECT_EQ(2u, command_runner_.commands_ran_.size()); @@ -4217,7 +4219,7 @@ TEST_F(BuildTest, ValidationDependsOnOutput) { EXPECT_TRUE(builder_.AddTarget("out", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); EXPECT_EQ(2u, command_runner_.commands_ran_.size()); @@ -4233,7 +4235,7 @@ TEST_F(BuildTest, ValidationDependsOnOutput) { EXPECT_TRUE(builder_.AddTarget("out", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); @@ -4270,7 +4272,7 @@ TEST_F(BuildWithDepsLogTest, ValidationThroughDepfile) { EXPECT_TRUE(builder.AddTarget("out2", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder.Build(&err)); + EXPECT_EQ(builder.Build(&err), ExitSuccess); EXPECT_EQ("", err); // On the first build, only the out2 command is run. @@ -4306,7 +4308,7 @@ TEST_F(BuildWithDepsLogTest, ValidationThroughDepfile) { EXPECT_TRUE(builder.AddTarget("out2", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder.Build(&err)); + EXPECT_EQ(builder.Build(&err), ExitSuccess); EXPECT_EQ("", err); // The out and validate actions should have been run as well as out2. @@ -4331,7 +4333,7 @@ TEST_F(BuildTest, ValidationCircular) { EXPECT_TRUE(builder_.AddTarget("out", &err)); EXPECT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); EXPECT_EQ(2u, command_runner_.commands_ran_.size()); @@ -4346,7 +4348,7 @@ TEST_F(BuildTest, ValidationCircular) { EXPECT_TRUE(builder_.AddTarget("out", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); @@ -4362,7 +4364,7 @@ TEST_F(BuildTest, ValidationCircular) { EXPECT_TRUE(builder_.AddTarget("out", &err)); ASSERT_EQ("", err); - EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ(builder_.Build(&err), ExitSuccess); EXPECT_EQ("", err); ASSERT_EQ(1u, command_runner_.commands_ran_.size()); diff --git a/src/exit_status.h b/src/exit_status.h index a714ece791..492eb40db8 100644 --- a/src/exit_status.h +++ b/src/exit_status.h @@ -15,10 +15,30 @@ #ifndef NINJA_EXIT_STATUS_H_ #define NINJA_EXIT_STATUS_H_ -enum ExitStatus { - ExitSuccess, +// The underlying type of the ExitStatus enum, used to represent a platform-specific +// process exit code. +#ifdef _WIN32 +#define EXIT_STATUS_TYPE unsigned long +#else // !_WIN32 +#define EXIT_STATUS_TYPE int +#endif // !_WIN32 + +// The platform-specific value of ExitInterrupted returned by Ninja in case of use interruption (e.g. Ctrl-C). +// On Windows, the convention used by the C runtime library is to return 3, even +// though the system error `ERROR_CONTROL_C_EXIT` is 572. Some applications also return 1 +// which is ExitFailure. The value 2 is chosen to preserve the existing Ninja behavior on this +// platform. On Posix, this is simply 128 + SIGINT. Note that Ninja will map SIGHUP +// and SIGTERM interrupted to ExitInterrupted as well. +#ifdef _WIN32 +# define EXIT_INTERRUPTED_VALUE 2 +#else // !_WIN32 +# define EXIT_INTERRUPTED_VALUE 130 +#endif // !_WIN32 + +enum ExitStatus : EXIT_STATUS_TYPE { + ExitSuccess=0, ExitFailure, - ExitInterrupted + ExitInterrupted=EXIT_INTERRUPTED_VALUE, }; #endif // NINJA_EXIT_STATUS_H_ diff --git a/src/ninja.cc b/src/ninja.cc index f681bfec11..119e7d4467 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -42,8 +42,8 @@ #include "clean.h" #include "command_collector.h" #include "debug_flags.h" -#include "depfile_parser.h" #include "disk_interface.h" +#include "exit_status.h" #include "graph.h" #include "graphviz.h" #include "json.h" @@ -165,7 +165,7 @@ struct NinjaMain : public BuildLogUser { /// Build the targets listed on the command line. /// @return an exit code. - int RunBuild(int argc, char** argv, Status* status); + ExitStatus RunBuild(int argc, char** argv, Status* status); /// Dump the output requested by '-d stats'. void DumpMetrics(); @@ -281,7 +281,7 @@ bool NinjaMain::RebuildManifest(const char* input_file, string* err, if (builder.AlreadyUpToDate()) return false; // Not an error, but we didn't rebuild. - if (!builder.Build(err)) + if (builder.Build(err) != ExitSuccess) return false; // The manifest was only rebuilt if it is now dirty (it may have been cleaned @@ -1541,12 +1541,12 @@ bool NinjaMain::EnsureBuildDirExists() { return true; } -int NinjaMain::RunBuild(int argc, char** argv, Status* status) { +ExitStatus NinjaMain::RunBuild(int argc, char** argv, Status* status) { string err; vector targets; if (!CollectTargetsFromArgs(argc, argv, &targets, &err)) { status->Error("%s", err.c_str()); - return 1; + return ExitFailure; } disk_interface_.AllowStatCache(g_experimental_statcache); @@ -1557,7 +1557,7 @@ int NinjaMain::RunBuild(int argc, char** argv, Status* status) { if (!builder.AddTarget(targets[i], &err)) { if (!err.empty()) { status->Error("%s", err.c_str()); - return 1; + return ExitFailure; } else { // Added a target that is already up-to-date; not really // an error. @@ -1572,18 +1572,18 @@ int NinjaMain::RunBuild(int argc, char** argv, Status* status) { if (config_.verbosity != BuildConfig::NO_STATUS_UPDATE) { status->Info("no work to do."); } - return 0; + return ExitSuccess; } - if (!builder.Build(&err)) { + ExitStatus exit_status = builder.Build(&err); + if (exit_status != ExitSuccess) { status->Info("build stopped: %s.", err.c_str()); if (err.find("interrupted by user") != string::npos) { - return 130; + return ExitInterrupted; } - return 1; } - return 0; + return exit_status; } #ifdef _MSC_VER @@ -1801,7 +1801,7 @@ NORETURN void real_main(int argc, char** argv) { ninja.ParsePreviousElapsedTimes(); - int result = ninja.RunBuild(argc, argv, status); + ExitStatus result = ninja.RunBuild(argc, argv, status); if (g_metrics) ninja.DumpMetrics(); exit(result); diff --git a/src/subprocess-posix.cc b/src/subprocess-posix.cc index 8e785406c9..311751c7ae 100644 --- a/src/subprocess-posix.cc +++ b/src/subprocess-posix.cc @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "exit_status.h" #include "subprocess.h" #include @@ -23,6 +24,7 @@ #include #include #include +#include #if defined(USE_PPOLL) #include @@ -165,15 +167,17 @@ ExitStatus Subprocess::Finish() { #endif if (WIFEXITED(status)) { - int exit = WEXITSTATUS(status); - if (exit == 0) - return ExitSuccess; - } else if (WIFSIGNALED(status)) { + // propagate the status transparently + return static_cast(WEXITSTATUS(status)); + } + if (WIFSIGNALED(status)) { + // Overwrite interrupts to exit code 2 if (WTERMSIG(status) == SIGINT || WTERMSIG(status) == SIGTERM || WTERMSIG(status) == SIGHUP) return ExitInterrupted; } - return ExitFailure; + // At this point, we exit with any other signal+128 + return static_cast(status | 0x80); } bool Subprocess::Done() const { diff --git a/src/subprocess-win32.cc b/src/subprocess-win32.cc index ff3baaca7f..4cb8472141 100644 --- a/src/subprocess-win32.cc +++ b/src/subprocess-win32.cc @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "exit_status.h" #include "subprocess.h" #include @@ -198,9 +199,8 @@ ExitStatus Subprocess::Finish() { CloseHandle(child_); child_ = NULL; - return exit_code == 0 ? ExitSuccess : - exit_code == CONTROL_C_EXIT ? ExitInterrupted : - ExitFailure; + return exit_code == CONTROL_C_EXIT ? ExitInterrupted : + static_cast(exit_code); } bool Subprocess::Done() const { diff --git a/src/subprocess_test.cc b/src/subprocess_test.cc index 073fe86931..34cb393e08 100644 --- a/src/subprocess_test.cc +++ b/src/subprocess_test.cc @@ -14,6 +14,7 @@ #include "subprocess.h" +#include "exit_status.h" #include "test.h" #ifndef _WIN32 @@ -50,7 +51,13 @@ TEST_F(SubprocessTest, BadCommandStderr) { subprocs_.DoWork(); } - EXPECT_EQ(ExitFailure, subproc->Finish()); + ExitStatus exit = subproc->Finish(); +#ifdef _POSIX_VERSION + EXPECT_EQ(127, exit); +#endif +#ifdef _WIN32 + EXPECT_EQ(ExitFailure, exit); +#endif EXPECT_NE("", subproc->GetOutput()); } @@ -64,7 +71,13 @@ TEST_F(SubprocessTest, NoSuchCommand) { subprocs_.DoWork(); } - EXPECT_EQ(ExitFailure, subproc->Finish()); + ExitStatus exit = subproc->Finish(); +#ifdef _POSIX_VERSION + EXPECT_EQ(127, exit); +#endif +#ifdef _WIN32 + EXPECT_EQ(ExitFailure, exit); +#endif EXPECT_NE("", subproc->GetOutput()); #ifdef _WIN32 ASSERT_EQ("CreateProcess failed: The system cannot find the file "