summaryrefslogtreecommitdiffstats
path: root/src/build_test.cc
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2015-08-11 17:17:12 (GMT)
committerBrad King <brad.king@kitware.com>2016-02-03 21:23:11 (GMT)
commite8d855f2622693a4b616cba96084cd7a58cb0611 (patch)
treea8c0acd4cd1e0d35eb9f4aab2586f469836d55cf /src/build_test.cc
parentca5fc795f38c77e42657e9d276f1dfc5127a8243 (diff)
downloadNinja-e8d855f2622693a4b616cba96084cd7a58cb0611.zip
Ninja-e8d855f2622693a4b616cba96084cd7a58cb0611.tar.gz
Ninja-e8d855f2622693a4b616cba96084cd7a58cb0611.tar.bz2
Avoid double-scheduling build edges in another case
The change in commit v1.2.0~3^2~3^2~3 (Fix duplicate edge Pool crash in the minimally invasive way, 2013-03-18) avoids double-scheduling in a case involving duplicate out edges. However, double-scheduling may also occur on a consistent graph when an edge and one of its dependencies share an order-only input: $ cat build.ninja ... build c: touch build b: touch || c build a: touch | b || c $ ninja a $ rm a c $ ninja a In this case 'c' will build first. When NodeFinished('c') loops over the out edges it will find AllInputsReady is true for 'b' and call EdgeFinished('b') since it is not wanted (up to date). This will call NodeFinished('b') which will loop over its out edges, find AllInputsReady is true for 'a', and call ScheduleEdge('a'). When we eventually return to the loop in NodeFinished('c') it will move on to its second output and find that AllInputsReady is true for 'a' and call ScheduleEdge('a') again. Teach ScheduleEdge to tolerate duplicate calls for an edge that has already been scheduled. Avoid calling EdgeScheduled more than once for the same edge.
Diffstat (limited to 'src/build_test.cc')
-rw-r--r--src/build_test.cc23
1 files changed, 23 insertions, 0 deletions
diff --git a/src/build_test.cc b/src/build_test.cc
index 7c6060d..26bf7ef 100644
--- a/src/build_test.cc
+++ b/src/build_test.cc
@@ -875,6 +875,29 @@ TEST_F(BuildTest, DepFileParseError) {
EXPECT_EQ("foo.o.d: expected ':' in depfile", err);
}
+TEST_F(BuildTest, EncounterReadyTwice) {
+ string err;
+ ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
+"rule touch\n"
+" command = touch $out\n"
+"build c: touch\n"
+"build b: touch || c\n"
+"build a: touch | b || c\n"));
+
+ vector<Edge*> c_out = GetNode("c")->out_edges();
+ ASSERT_EQ(2u, c_out.size());
+ EXPECT_EQ("b", c_out[0]->outputs_[0]->path());
+ EXPECT_EQ("a", c_out[1]->outputs_[0]->path());
+
+ fs_.Create("b", "");
+ EXPECT_TRUE(builder_.AddTarget("a", &err));
+ ASSERT_EQ("", err);
+
+ EXPECT_TRUE(builder_.Build(&err));
+ ASSERT_EQ("", err);
+ ASSERT_EQ(2u, command_runner_.commands_ran_.size());
+}
+
TEST_F(BuildTest, OrderOnlyDeps) {
string err;
ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,