1695b41eeSopenharmony_ci// Copyright 2011 Google Inc. All Rights Reserved. 2695b41eeSopenharmony_ci// 3695b41eeSopenharmony_ci// Licensed under the Apache License, Version 2.0 (the "License"); 4695b41eeSopenharmony_ci// you may not use this file except in compliance with the License. 5695b41eeSopenharmony_ci// You may obtain a copy of the License at 6695b41eeSopenharmony_ci// 7695b41eeSopenharmony_ci// http://www.apache.org/licenses/LICENSE-2.0 8695b41eeSopenharmony_ci// 9695b41eeSopenharmony_ci// Unless required by applicable law or agreed to in writing, software 10695b41eeSopenharmony_ci// distributed under the License is distributed on an "AS IS" BASIS, 11695b41eeSopenharmony_ci// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12695b41eeSopenharmony_ci// See the License for the specific language governing permissions and 13695b41eeSopenharmony_ci// limitations under the License. 14695b41eeSopenharmony_ci 15695b41eeSopenharmony_ci#include "graph.h" 16695b41eeSopenharmony_ci#include "build.h" 17695b41eeSopenharmony_ci 18695b41eeSopenharmony_ci#include "test.h" 19695b41eeSopenharmony_ci 20695b41eeSopenharmony_ciusing namespace std; 21695b41eeSopenharmony_ci 22695b41eeSopenharmony_cistruct GraphTest : public StateTestWithBuiltinRules { 23695b41eeSopenharmony_ci GraphTest() : scan_(&state_, NULL, NULL, &fs_, NULL) {} 24695b41eeSopenharmony_ci 25695b41eeSopenharmony_ci VirtualFileSystem fs_; 26695b41eeSopenharmony_ci DependencyScan scan_; 27695b41eeSopenharmony_ci}; 28695b41eeSopenharmony_ci 29695b41eeSopenharmony_ciTEST_F(GraphTest, MissingImplicit) { 30695b41eeSopenharmony_ci ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, 31695b41eeSopenharmony_ci"build out: cat in | implicit\n")); 32695b41eeSopenharmony_ci fs_.Create("in", ""); 33695b41eeSopenharmony_ci fs_.Create("out", ""); 34695b41eeSopenharmony_ci 35695b41eeSopenharmony_ci string err; 36695b41eeSopenharmony_ci EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err)); 37695b41eeSopenharmony_ci ASSERT_EQ("", err); 38695b41eeSopenharmony_ci 39695b41eeSopenharmony_ci // A missing implicit dep *should* make the output dirty. 40695b41eeSopenharmony_ci // (In fact, a build will fail.) 41695b41eeSopenharmony_ci // This is a change from prior semantics of ninja. 42695b41eeSopenharmony_ci EXPECT_TRUE(GetNode("out")->dirty()); 43695b41eeSopenharmony_ci} 44695b41eeSopenharmony_ci 45695b41eeSopenharmony_ciTEST_F(GraphTest, ModifiedImplicit) { 46695b41eeSopenharmony_ci ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, 47695b41eeSopenharmony_ci"build out: cat in | implicit\n")); 48695b41eeSopenharmony_ci fs_.Create("in", ""); 49695b41eeSopenharmony_ci fs_.Create("out", ""); 50695b41eeSopenharmony_ci fs_.Tick(); 51695b41eeSopenharmony_ci fs_.Create("implicit", ""); 52695b41eeSopenharmony_ci 53695b41eeSopenharmony_ci string err; 54695b41eeSopenharmony_ci EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err)); 55695b41eeSopenharmony_ci ASSERT_EQ("", err); 56695b41eeSopenharmony_ci 57695b41eeSopenharmony_ci // A modified implicit dep should make the output dirty. 58695b41eeSopenharmony_ci EXPECT_TRUE(GetNode("out")->dirty()); 59695b41eeSopenharmony_ci} 60695b41eeSopenharmony_ci 61695b41eeSopenharmony_ciTEST_F(GraphTest, FunkyMakefilePath) { 62695b41eeSopenharmony_ci ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, 63695b41eeSopenharmony_ci"rule catdep\n" 64695b41eeSopenharmony_ci" depfile = $out.d\n" 65695b41eeSopenharmony_ci" command = cat $in > $out\n" 66695b41eeSopenharmony_ci"build out.o: catdep foo.cc\n")); 67695b41eeSopenharmony_ci fs_.Create("foo.cc", ""); 68695b41eeSopenharmony_ci fs_.Create("out.o.d", "out.o: ./foo/../implicit.h\n"); 69695b41eeSopenharmony_ci fs_.Create("out.o", ""); 70695b41eeSopenharmony_ci fs_.Tick(); 71695b41eeSopenharmony_ci fs_.Create("implicit.h", ""); 72695b41eeSopenharmony_ci 73695b41eeSopenharmony_ci string err; 74695b41eeSopenharmony_ci EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), NULL, &err)); 75695b41eeSopenharmony_ci ASSERT_EQ("", err); 76695b41eeSopenharmony_ci 77695b41eeSopenharmony_ci // implicit.h has changed, though our depfile refers to it with a 78695b41eeSopenharmony_ci // non-canonical path; we should still find it. 79695b41eeSopenharmony_ci EXPECT_TRUE(GetNode("out.o")->dirty()); 80695b41eeSopenharmony_ci} 81695b41eeSopenharmony_ci 82695b41eeSopenharmony_ciTEST_F(GraphTest, ExplicitImplicit) { 83695b41eeSopenharmony_ci ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, 84695b41eeSopenharmony_ci"rule catdep\n" 85695b41eeSopenharmony_ci" depfile = $out.d\n" 86695b41eeSopenharmony_ci" command = cat $in > $out\n" 87695b41eeSopenharmony_ci"build implicit.h: cat data\n" 88695b41eeSopenharmony_ci"build out.o: catdep foo.cc || implicit.h\n")); 89695b41eeSopenharmony_ci fs_.Create("implicit.h", ""); 90695b41eeSopenharmony_ci fs_.Create("foo.cc", ""); 91695b41eeSopenharmony_ci fs_.Create("out.o.d", "out.o: implicit.h\n"); 92695b41eeSopenharmony_ci fs_.Create("out.o", ""); 93695b41eeSopenharmony_ci fs_.Tick(); 94695b41eeSopenharmony_ci fs_.Create("data", ""); 95695b41eeSopenharmony_ci 96695b41eeSopenharmony_ci string err; 97695b41eeSopenharmony_ci EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), NULL, &err)); 98695b41eeSopenharmony_ci ASSERT_EQ("", err); 99695b41eeSopenharmony_ci 100695b41eeSopenharmony_ci // We have both an implicit and an explicit dep on implicit.h. 101695b41eeSopenharmony_ci // The implicit dep should "win" (in the sense that it should cause 102695b41eeSopenharmony_ci // the output to be dirty). 103695b41eeSopenharmony_ci EXPECT_TRUE(GetNode("out.o")->dirty()); 104695b41eeSopenharmony_ci} 105695b41eeSopenharmony_ci 106695b41eeSopenharmony_ciTEST_F(GraphTest, ImplicitOutputParse) { 107695b41eeSopenharmony_ci ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, 108695b41eeSopenharmony_ci"build out | out.imp: cat in\n")); 109695b41eeSopenharmony_ci 110695b41eeSopenharmony_ci Edge* edge = GetNode("out")->in_edge(); 111695b41eeSopenharmony_ci EXPECT_EQ(2, edge->outputs_.size()); 112695b41eeSopenharmony_ci EXPECT_EQ("out", edge->outputs_[0]->path()); 113695b41eeSopenharmony_ci EXPECT_EQ("out.imp", edge->outputs_[1]->path()); 114695b41eeSopenharmony_ci EXPECT_EQ(1, edge->implicit_outs_); 115695b41eeSopenharmony_ci EXPECT_EQ(edge, GetNode("out.imp")->in_edge()); 116695b41eeSopenharmony_ci} 117695b41eeSopenharmony_ci 118695b41eeSopenharmony_ciTEST_F(GraphTest, ImplicitOutputMissing) { 119695b41eeSopenharmony_ci ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, 120695b41eeSopenharmony_ci"build out | out.imp: cat in\n")); 121695b41eeSopenharmony_ci fs_.Create("in", ""); 122695b41eeSopenharmony_ci fs_.Create("out", ""); 123695b41eeSopenharmony_ci 124695b41eeSopenharmony_ci string err; 125695b41eeSopenharmony_ci EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err)); 126695b41eeSopenharmony_ci ASSERT_EQ("", err); 127695b41eeSopenharmony_ci 128695b41eeSopenharmony_ci EXPECT_TRUE(GetNode("out")->dirty()); 129695b41eeSopenharmony_ci EXPECT_TRUE(GetNode("out.imp")->dirty()); 130695b41eeSopenharmony_ci} 131695b41eeSopenharmony_ci 132695b41eeSopenharmony_ciTEST_F(GraphTest, ImplicitOutputOutOfDate) { 133695b41eeSopenharmony_ci ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, 134695b41eeSopenharmony_ci"build out | out.imp: cat in\n")); 135695b41eeSopenharmony_ci fs_.Create("out.imp", ""); 136695b41eeSopenharmony_ci fs_.Tick(); 137695b41eeSopenharmony_ci fs_.Create("in", ""); 138695b41eeSopenharmony_ci fs_.Create("out", ""); 139695b41eeSopenharmony_ci 140695b41eeSopenharmony_ci string err; 141695b41eeSopenharmony_ci EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err)); 142695b41eeSopenharmony_ci ASSERT_EQ("", err); 143695b41eeSopenharmony_ci 144695b41eeSopenharmony_ci EXPECT_TRUE(GetNode("out")->dirty()); 145695b41eeSopenharmony_ci EXPECT_TRUE(GetNode("out.imp")->dirty()); 146695b41eeSopenharmony_ci} 147695b41eeSopenharmony_ci 148695b41eeSopenharmony_ciTEST_F(GraphTest, ImplicitOutputOnlyParse) { 149695b41eeSopenharmony_ci ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, 150695b41eeSopenharmony_ci"build | out.imp: cat in\n")); 151695b41eeSopenharmony_ci 152695b41eeSopenharmony_ci Edge* edge = GetNode("out.imp")->in_edge(); 153695b41eeSopenharmony_ci EXPECT_EQ(1, edge->outputs_.size()); 154695b41eeSopenharmony_ci EXPECT_EQ("out.imp", edge->outputs_[0]->path()); 155695b41eeSopenharmony_ci EXPECT_EQ(1, edge->implicit_outs_); 156695b41eeSopenharmony_ci EXPECT_EQ(edge, GetNode("out.imp")->in_edge()); 157695b41eeSopenharmony_ci} 158695b41eeSopenharmony_ci 159695b41eeSopenharmony_ciTEST_F(GraphTest, ImplicitOutputOnlyMissing) { 160695b41eeSopenharmony_ci ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, 161695b41eeSopenharmony_ci"build | out.imp: cat in\n")); 162695b41eeSopenharmony_ci fs_.Create("in", ""); 163695b41eeSopenharmony_ci 164695b41eeSopenharmony_ci string err; 165695b41eeSopenharmony_ci EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.imp"), NULL, &err)); 166695b41eeSopenharmony_ci ASSERT_EQ("", err); 167695b41eeSopenharmony_ci 168695b41eeSopenharmony_ci EXPECT_TRUE(GetNode("out.imp")->dirty()); 169695b41eeSopenharmony_ci} 170695b41eeSopenharmony_ci 171695b41eeSopenharmony_ciTEST_F(GraphTest, ImplicitOutputOnlyOutOfDate) { 172695b41eeSopenharmony_ci ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, 173695b41eeSopenharmony_ci"build | out.imp: cat in\n")); 174695b41eeSopenharmony_ci fs_.Create("out.imp", ""); 175695b41eeSopenharmony_ci fs_.Tick(); 176695b41eeSopenharmony_ci fs_.Create("in", ""); 177695b41eeSopenharmony_ci 178695b41eeSopenharmony_ci string err; 179695b41eeSopenharmony_ci EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.imp"), NULL, &err)); 180695b41eeSopenharmony_ci ASSERT_EQ("", err); 181695b41eeSopenharmony_ci 182695b41eeSopenharmony_ci EXPECT_TRUE(GetNode("out.imp")->dirty()); 183695b41eeSopenharmony_ci} 184695b41eeSopenharmony_ci 185695b41eeSopenharmony_ciTEST_F(GraphTest, PathWithCurrentDirectory) { 186695b41eeSopenharmony_ci ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, 187695b41eeSopenharmony_ci"rule catdep\n" 188695b41eeSopenharmony_ci" depfile = $out.d\n" 189695b41eeSopenharmony_ci" command = cat $in > $out\n" 190695b41eeSopenharmony_ci"build ./out.o: catdep ./foo.cc\n")); 191695b41eeSopenharmony_ci fs_.Create("foo.cc", ""); 192695b41eeSopenharmony_ci fs_.Create("out.o.d", "out.o: foo.cc\n"); 193695b41eeSopenharmony_ci fs_.Create("out.o", ""); 194695b41eeSopenharmony_ci 195695b41eeSopenharmony_ci string err; 196695b41eeSopenharmony_ci EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), NULL, &err)); 197695b41eeSopenharmony_ci ASSERT_EQ("", err); 198695b41eeSopenharmony_ci 199695b41eeSopenharmony_ci EXPECT_FALSE(GetNode("out.o")->dirty()); 200695b41eeSopenharmony_ci} 201695b41eeSopenharmony_ci 202695b41eeSopenharmony_ciTEST_F(GraphTest, RootNodes) { 203695b41eeSopenharmony_ci ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, 204695b41eeSopenharmony_ci"build out1: cat in1\n" 205695b41eeSopenharmony_ci"build mid1: cat in1\n" 206695b41eeSopenharmony_ci"build out2: cat mid1\n" 207695b41eeSopenharmony_ci"build out3 out4: cat mid1\n")); 208695b41eeSopenharmony_ci 209695b41eeSopenharmony_ci string err; 210695b41eeSopenharmony_ci vector<Node*> root_nodes = state_.RootNodes(&err); 211695b41eeSopenharmony_ci EXPECT_EQ(4u, root_nodes.size()); 212695b41eeSopenharmony_ci for (size_t i = 0; i < root_nodes.size(); ++i) { 213695b41eeSopenharmony_ci string name = root_nodes[i]->path(); 214695b41eeSopenharmony_ci EXPECT_EQ("out", name.substr(0, 3)); 215695b41eeSopenharmony_ci } 216695b41eeSopenharmony_ci} 217695b41eeSopenharmony_ci 218695b41eeSopenharmony_ciTEST_F(GraphTest, CollectInputs) { 219695b41eeSopenharmony_ci ASSERT_NO_FATAL_FAILURE(AssertParse( 220695b41eeSopenharmony_ci &state_, 221695b41eeSopenharmony_ci "build out$ 1: cat in1 in2 in$ with$ space | implicit || order_only\n")); 222695b41eeSopenharmony_ci 223695b41eeSopenharmony_ci std::vector<std::string> inputs; 224695b41eeSopenharmony_ci Edge* edge = GetNode("out 1")->in_edge(); 225695b41eeSopenharmony_ci 226695b41eeSopenharmony_ci // Test without shell escaping. 227695b41eeSopenharmony_ci inputs.clear(); 228695b41eeSopenharmony_ci edge->CollectInputs(false, &inputs); 229695b41eeSopenharmony_ci EXPECT_EQ(5u, inputs.size()); 230695b41eeSopenharmony_ci EXPECT_EQ("in1", inputs[0]); 231695b41eeSopenharmony_ci EXPECT_EQ("in2", inputs[1]); 232695b41eeSopenharmony_ci EXPECT_EQ("in with space", inputs[2]); 233695b41eeSopenharmony_ci EXPECT_EQ("implicit", inputs[3]); 234695b41eeSopenharmony_ci EXPECT_EQ("order_only", inputs[4]); 235695b41eeSopenharmony_ci 236695b41eeSopenharmony_ci // Test with shell escaping. 237695b41eeSopenharmony_ci inputs.clear(); 238695b41eeSopenharmony_ci edge->CollectInputs(true, &inputs); 239695b41eeSopenharmony_ci EXPECT_EQ(5u, inputs.size()); 240695b41eeSopenharmony_ci EXPECT_EQ("in1", inputs[0]); 241695b41eeSopenharmony_ci EXPECT_EQ("in2", inputs[1]); 242695b41eeSopenharmony_ci#ifdef _WIN32 243695b41eeSopenharmony_ci EXPECT_EQ("\"in with space\"", inputs[2]); 244695b41eeSopenharmony_ci#else 245695b41eeSopenharmony_ci EXPECT_EQ("'in with space'", inputs[2]); 246695b41eeSopenharmony_ci#endif 247695b41eeSopenharmony_ci EXPECT_EQ("implicit", inputs[3]); 248695b41eeSopenharmony_ci EXPECT_EQ("order_only", inputs[4]); 249695b41eeSopenharmony_ci} 250695b41eeSopenharmony_ci 251695b41eeSopenharmony_ciTEST_F(GraphTest, VarInOutPathEscaping) { 252695b41eeSopenharmony_ci ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, 253695b41eeSopenharmony_ci"build a$ b: cat no'space with$ space$$ no\"space2\n")); 254695b41eeSopenharmony_ci 255695b41eeSopenharmony_ci Edge* edge = GetNode("a b")->in_edge(); 256695b41eeSopenharmony_ci#ifdef _WIN32 257695b41eeSopenharmony_ci EXPECT_EQ("cat no'space \"with space$\" \"no\\\"space2\" > \"a b\"", 258695b41eeSopenharmony_ci edge->EvaluateCommand()); 259695b41eeSopenharmony_ci#else 260695b41eeSopenharmony_ci EXPECT_EQ("cat 'no'\\''space' 'with space$' 'no\"space2' > 'a b'", 261695b41eeSopenharmony_ci edge->EvaluateCommand()); 262695b41eeSopenharmony_ci#endif 263695b41eeSopenharmony_ci} 264695b41eeSopenharmony_ci 265695b41eeSopenharmony_ci// Regression test for https://github.com/ninja-build/ninja/issues/380 266695b41eeSopenharmony_ciTEST_F(GraphTest, DepfileWithCanonicalizablePath) { 267695b41eeSopenharmony_ci ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, 268695b41eeSopenharmony_ci"rule catdep\n" 269695b41eeSopenharmony_ci" depfile = $out.d\n" 270695b41eeSopenharmony_ci" command = cat $in > $out\n" 271695b41eeSopenharmony_ci"build ./out.o: catdep ./foo.cc\n")); 272695b41eeSopenharmony_ci fs_.Create("foo.cc", ""); 273695b41eeSopenharmony_ci fs_.Create("out.o.d", "out.o: bar/../foo.cc\n"); 274695b41eeSopenharmony_ci fs_.Create("out.o", ""); 275695b41eeSopenharmony_ci 276695b41eeSopenharmony_ci string err; 277695b41eeSopenharmony_ci EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), NULL, &err)); 278695b41eeSopenharmony_ci ASSERT_EQ("", err); 279695b41eeSopenharmony_ci 280695b41eeSopenharmony_ci EXPECT_FALSE(GetNode("out.o")->dirty()); 281695b41eeSopenharmony_ci} 282695b41eeSopenharmony_ci 283695b41eeSopenharmony_ci// Regression test for https://github.com/ninja-build/ninja/issues/404 284695b41eeSopenharmony_ciTEST_F(GraphTest, DepfileRemoved) { 285695b41eeSopenharmony_ci ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, 286695b41eeSopenharmony_ci"rule catdep\n" 287695b41eeSopenharmony_ci" depfile = $out.d\n" 288695b41eeSopenharmony_ci" command = cat $in > $out\n" 289695b41eeSopenharmony_ci"build ./out.o: catdep ./foo.cc\n")); 290695b41eeSopenharmony_ci fs_.Create("foo.h", ""); 291695b41eeSopenharmony_ci fs_.Create("foo.cc", ""); 292695b41eeSopenharmony_ci fs_.Tick(); 293695b41eeSopenharmony_ci fs_.Create("out.o.d", "out.o: foo.h\n"); 294695b41eeSopenharmony_ci fs_.Create("out.o", ""); 295695b41eeSopenharmony_ci 296695b41eeSopenharmony_ci string err; 297695b41eeSopenharmony_ci EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), NULL, &err)); 298695b41eeSopenharmony_ci ASSERT_EQ("", err); 299695b41eeSopenharmony_ci EXPECT_FALSE(GetNode("out.o")->dirty()); 300695b41eeSopenharmony_ci 301695b41eeSopenharmony_ci state_.Reset(); 302695b41eeSopenharmony_ci fs_.RemoveFile("out.o.d"); 303695b41eeSopenharmony_ci EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), NULL, &err)); 304695b41eeSopenharmony_ci ASSERT_EQ("", err); 305695b41eeSopenharmony_ci EXPECT_TRUE(GetNode("out.o")->dirty()); 306695b41eeSopenharmony_ci} 307695b41eeSopenharmony_ci 308695b41eeSopenharmony_ci// Check that rule-level variables are in scope for eval. 309695b41eeSopenharmony_ciTEST_F(GraphTest, RuleVariablesInScope) { 310695b41eeSopenharmony_ci ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, 311695b41eeSopenharmony_ci"rule r\n" 312695b41eeSopenharmony_ci" depfile = x\n" 313695b41eeSopenharmony_ci" command = depfile is $depfile\n" 314695b41eeSopenharmony_ci"build out: r in\n")); 315695b41eeSopenharmony_ci Edge* edge = GetNode("out")->in_edge(); 316695b41eeSopenharmony_ci EXPECT_EQ("depfile is x", edge->EvaluateCommand()); 317695b41eeSopenharmony_ci} 318695b41eeSopenharmony_ci 319695b41eeSopenharmony_ci// Check that build statements can override rule builtins like depfile. 320695b41eeSopenharmony_ciTEST_F(GraphTest, DepfileOverride) { 321695b41eeSopenharmony_ci ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, 322695b41eeSopenharmony_ci"rule r\n" 323695b41eeSopenharmony_ci" depfile = x\n" 324695b41eeSopenharmony_ci" command = unused\n" 325695b41eeSopenharmony_ci"build out: r in\n" 326695b41eeSopenharmony_ci" depfile = y\n")); 327695b41eeSopenharmony_ci Edge* edge = GetNode("out")->in_edge(); 328695b41eeSopenharmony_ci EXPECT_EQ("y", edge->GetBinding("depfile")); 329695b41eeSopenharmony_ci} 330695b41eeSopenharmony_ci 331695b41eeSopenharmony_ci// Check that overridden values show up in expansion of rule-level bindings. 332695b41eeSopenharmony_ciTEST_F(GraphTest, DepfileOverrideParent) { 333695b41eeSopenharmony_ci ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, 334695b41eeSopenharmony_ci"rule r\n" 335695b41eeSopenharmony_ci" depfile = x\n" 336695b41eeSopenharmony_ci" command = depfile is $depfile\n" 337695b41eeSopenharmony_ci"build out: r in\n" 338695b41eeSopenharmony_ci" depfile = y\n")); 339695b41eeSopenharmony_ci Edge* edge = GetNode("out")->in_edge(); 340695b41eeSopenharmony_ci EXPECT_EQ("depfile is y", edge->GetBinding("command")); 341695b41eeSopenharmony_ci} 342695b41eeSopenharmony_ci 343695b41eeSopenharmony_ci// Verify that building a nested phony rule prints "no work to do" 344695b41eeSopenharmony_ciTEST_F(GraphTest, NestedPhonyPrintsDone) { 345695b41eeSopenharmony_ci AssertParse(&state_, 346695b41eeSopenharmony_ci"build n1: phony \n" 347695b41eeSopenharmony_ci"build n2: phony n1\n" 348695b41eeSopenharmony_ci ); 349695b41eeSopenharmony_ci string err; 350695b41eeSopenharmony_ci EXPECT_TRUE(scan_.RecomputeDirty(GetNode("n2"), NULL, &err)); 351695b41eeSopenharmony_ci ASSERT_EQ("", err); 352695b41eeSopenharmony_ci 353695b41eeSopenharmony_ci Plan plan_; 354695b41eeSopenharmony_ci EXPECT_TRUE(plan_.AddTarget(GetNode("n2"), &err)); 355695b41eeSopenharmony_ci ASSERT_EQ("", err); 356695b41eeSopenharmony_ci 357695b41eeSopenharmony_ci EXPECT_EQ(0, plan_.command_edge_count()); 358695b41eeSopenharmony_ci ASSERT_FALSE(plan_.more_to_do()); 359695b41eeSopenharmony_ci} 360695b41eeSopenharmony_ci 361695b41eeSopenharmony_ciTEST_F(GraphTest, PhonySelfReferenceError) { 362695b41eeSopenharmony_ci ManifestParserOptions parser_opts; 363695b41eeSopenharmony_ci parser_opts.phony_cycle_action_ = kPhonyCycleActionError; 364695b41eeSopenharmony_ci AssertParse(&state_, 365695b41eeSopenharmony_ci"build a: phony a\n", 366695b41eeSopenharmony_ci parser_opts); 367695b41eeSopenharmony_ci 368695b41eeSopenharmony_ci string err; 369695b41eeSopenharmony_ci EXPECT_FALSE(scan_.RecomputeDirty(GetNode("a"), NULL, &err)); 370695b41eeSopenharmony_ci ASSERT_EQ("dependency cycle: a -> a [-w phonycycle=err]", err); 371695b41eeSopenharmony_ci} 372695b41eeSopenharmony_ci 373695b41eeSopenharmony_ciTEST_F(GraphTest, DependencyCycle) { 374695b41eeSopenharmony_ci AssertParse(&state_, 375695b41eeSopenharmony_ci"build out: cat mid\n" 376695b41eeSopenharmony_ci"build mid: cat in\n" 377695b41eeSopenharmony_ci"build in: cat pre\n" 378695b41eeSopenharmony_ci"build pre: cat out\n"); 379695b41eeSopenharmony_ci 380695b41eeSopenharmony_ci string err; 381695b41eeSopenharmony_ci EXPECT_FALSE(scan_.RecomputeDirty(GetNode("out"), NULL, &err)); 382695b41eeSopenharmony_ci ASSERT_EQ("dependency cycle: out -> mid -> in -> pre -> out", err); 383695b41eeSopenharmony_ci} 384695b41eeSopenharmony_ci 385695b41eeSopenharmony_ciTEST_F(GraphTest, CycleInEdgesButNotInNodes1) { 386695b41eeSopenharmony_ci string err; 387695b41eeSopenharmony_ci AssertParse(&state_, 388695b41eeSopenharmony_ci"build a b: cat a\n"); 389695b41eeSopenharmony_ci EXPECT_FALSE(scan_.RecomputeDirty(GetNode("b"), NULL, &err)); 390695b41eeSopenharmony_ci ASSERT_EQ("dependency cycle: a -> a", err); 391695b41eeSopenharmony_ci} 392695b41eeSopenharmony_ci 393695b41eeSopenharmony_ciTEST_F(GraphTest, CycleInEdgesButNotInNodes2) { 394695b41eeSopenharmony_ci string err; 395695b41eeSopenharmony_ci ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, 396695b41eeSopenharmony_ci"build b a: cat a\n")); 397695b41eeSopenharmony_ci EXPECT_FALSE(scan_.RecomputeDirty(GetNode("b"), NULL, &err)); 398695b41eeSopenharmony_ci ASSERT_EQ("dependency cycle: a -> a", err); 399695b41eeSopenharmony_ci} 400695b41eeSopenharmony_ci 401695b41eeSopenharmony_ciTEST_F(GraphTest, CycleInEdgesButNotInNodes3) { 402695b41eeSopenharmony_ci string err; 403695b41eeSopenharmony_ci ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, 404695b41eeSopenharmony_ci"build a b: cat c\n" 405695b41eeSopenharmony_ci"build c: cat a\n")); 406695b41eeSopenharmony_ci EXPECT_FALSE(scan_.RecomputeDirty(GetNode("b"), NULL, &err)); 407695b41eeSopenharmony_ci ASSERT_EQ("dependency cycle: a -> c -> a", err); 408695b41eeSopenharmony_ci} 409695b41eeSopenharmony_ci 410695b41eeSopenharmony_ciTEST_F(GraphTest, CycleInEdgesButNotInNodes4) { 411695b41eeSopenharmony_ci string err; 412695b41eeSopenharmony_ci ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, 413695b41eeSopenharmony_ci"build d: cat c\n" 414695b41eeSopenharmony_ci"build c: cat b\n" 415695b41eeSopenharmony_ci"build b: cat a\n" 416695b41eeSopenharmony_ci"build a e: cat d\n" 417695b41eeSopenharmony_ci"build f: cat e\n")); 418695b41eeSopenharmony_ci EXPECT_FALSE(scan_.RecomputeDirty(GetNode("f"), NULL, &err)); 419695b41eeSopenharmony_ci ASSERT_EQ("dependency cycle: a -> d -> c -> b -> a", err); 420695b41eeSopenharmony_ci} 421695b41eeSopenharmony_ci 422695b41eeSopenharmony_ci// Verify that cycles in graphs with multiple outputs are handled correctly 423695b41eeSopenharmony_ci// in RecomputeDirty() and don't cause deps to be loaded multiple times. 424695b41eeSopenharmony_ciTEST_F(GraphTest, CycleWithLengthZeroFromDepfile) { 425695b41eeSopenharmony_ci AssertParse(&state_, 426695b41eeSopenharmony_ci"rule deprule\n" 427695b41eeSopenharmony_ci" depfile = dep.d\n" 428695b41eeSopenharmony_ci" command = unused\n" 429695b41eeSopenharmony_ci"build a b: deprule\n" 430695b41eeSopenharmony_ci ); 431695b41eeSopenharmony_ci fs_.Create("dep.d", "a: b\n"); 432695b41eeSopenharmony_ci 433695b41eeSopenharmony_ci string err; 434695b41eeSopenharmony_ci EXPECT_FALSE(scan_.RecomputeDirty(GetNode("a"), NULL, &err)); 435695b41eeSopenharmony_ci ASSERT_EQ("dependency cycle: b -> b", err); 436695b41eeSopenharmony_ci 437695b41eeSopenharmony_ci // Despite the depfile causing edge to be a cycle (it has outputs a and b, 438695b41eeSopenharmony_ci // but the depfile also adds b as an input), the deps should have been loaded 439695b41eeSopenharmony_ci // only once: 440695b41eeSopenharmony_ci Edge* edge = GetNode("a")->in_edge(); 441695b41eeSopenharmony_ci EXPECT_EQ(1, edge->inputs_.size()); 442695b41eeSopenharmony_ci EXPECT_EQ("b", edge->inputs_[0]->path()); 443695b41eeSopenharmony_ci} 444695b41eeSopenharmony_ci 445695b41eeSopenharmony_ci// Like CycleWithLengthZeroFromDepfile but with a higher cycle length. 446695b41eeSopenharmony_ciTEST_F(GraphTest, CycleWithLengthOneFromDepfile) { 447695b41eeSopenharmony_ci AssertParse(&state_, 448695b41eeSopenharmony_ci"rule deprule\n" 449695b41eeSopenharmony_ci" depfile = dep.d\n" 450695b41eeSopenharmony_ci" command = unused\n" 451695b41eeSopenharmony_ci"rule r\n" 452695b41eeSopenharmony_ci" command = unused\n" 453695b41eeSopenharmony_ci"build a b: deprule\n" 454695b41eeSopenharmony_ci"build c: r b\n" 455695b41eeSopenharmony_ci ); 456695b41eeSopenharmony_ci fs_.Create("dep.d", "a: c\n"); 457695b41eeSopenharmony_ci 458695b41eeSopenharmony_ci string err; 459695b41eeSopenharmony_ci EXPECT_FALSE(scan_.RecomputeDirty(GetNode("a"), NULL, &err)); 460695b41eeSopenharmony_ci ASSERT_EQ("dependency cycle: b -> c -> b", err); 461695b41eeSopenharmony_ci 462695b41eeSopenharmony_ci // Despite the depfile causing edge to be a cycle (|edge| has outputs a and b, 463695b41eeSopenharmony_ci // but c's in_edge has b as input but the depfile also adds |edge| as 464695b41eeSopenharmony_ci // output)), the deps should have been loaded only once: 465695b41eeSopenharmony_ci Edge* edge = GetNode("a")->in_edge(); 466695b41eeSopenharmony_ci EXPECT_EQ(1, edge->inputs_.size()); 467695b41eeSopenharmony_ci EXPECT_EQ("c", edge->inputs_[0]->path()); 468695b41eeSopenharmony_ci} 469695b41eeSopenharmony_ci 470695b41eeSopenharmony_ci// Like CycleWithLengthOneFromDepfile but building a node one hop away from 471695b41eeSopenharmony_ci// the cycle. 472695b41eeSopenharmony_ciTEST_F(GraphTest, CycleWithLengthOneFromDepfileOneHopAway) { 473695b41eeSopenharmony_ci AssertParse(&state_, 474695b41eeSopenharmony_ci"rule deprule\n" 475695b41eeSopenharmony_ci" depfile = dep.d\n" 476695b41eeSopenharmony_ci" command = unused\n" 477695b41eeSopenharmony_ci"rule r\n" 478695b41eeSopenharmony_ci" command = unused\n" 479695b41eeSopenharmony_ci"build a b: deprule\n" 480695b41eeSopenharmony_ci"build c: r b\n" 481695b41eeSopenharmony_ci"build d: r a\n" 482695b41eeSopenharmony_ci ); 483695b41eeSopenharmony_ci fs_.Create("dep.d", "a: c\n"); 484695b41eeSopenharmony_ci 485695b41eeSopenharmony_ci string err; 486695b41eeSopenharmony_ci EXPECT_FALSE(scan_.RecomputeDirty(GetNode("d"), NULL, &err)); 487695b41eeSopenharmony_ci ASSERT_EQ("dependency cycle: b -> c -> b", err); 488695b41eeSopenharmony_ci 489695b41eeSopenharmony_ci // Despite the depfile causing edge to be a cycle (|edge| has outputs a and b, 490695b41eeSopenharmony_ci // but c's in_edge has b as input but the depfile also adds |edge| as 491695b41eeSopenharmony_ci // output)), the deps should have been loaded only once: 492695b41eeSopenharmony_ci Edge* edge = GetNode("a")->in_edge(); 493695b41eeSopenharmony_ci EXPECT_EQ(1, edge->inputs_.size()); 494695b41eeSopenharmony_ci EXPECT_EQ("c", edge->inputs_[0]->path()); 495695b41eeSopenharmony_ci} 496695b41eeSopenharmony_ci 497695b41eeSopenharmony_ci#ifdef _WIN32 498695b41eeSopenharmony_ciTEST_F(GraphTest, Decanonicalize) { 499695b41eeSopenharmony_ci ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, 500695b41eeSopenharmony_ci"build out\\out1: cat src\\in1\n" 501695b41eeSopenharmony_ci"build out\\out2/out3\\out4: cat mid1\n" 502695b41eeSopenharmony_ci"build out3 out4\\foo: cat mid1\n")); 503695b41eeSopenharmony_ci 504695b41eeSopenharmony_ci string err; 505695b41eeSopenharmony_ci vector<Node*> root_nodes = state_.RootNodes(&err); 506695b41eeSopenharmony_ci EXPECT_EQ(4u, root_nodes.size()); 507695b41eeSopenharmony_ci EXPECT_EQ(root_nodes[0]->path(), "out/out1"); 508695b41eeSopenharmony_ci EXPECT_EQ(root_nodes[1]->path(), "out/out2/out3/out4"); 509695b41eeSopenharmony_ci EXPECT_EQ(root_nodes[2]->path(), "out3"); 510695b41eeSopenharmony_ci EXPECT_EQ(root_nodes[3]->path(), "out4/foo"); 511695b41eeSopenharmony_ci EXPECT_EQ(root_nodes[0]->PathDecanonicalized(), "out\\out1"); 512695b41eeSopenharmony_ci EXPECT_EQ(root_nodes[1]->PathDecanonicalized(), "out\\out2/out3\\out4"); 513695b41eeSopenharmony_ci EXPECT_EQ(root_nodes[2]->PathDecanonicalized(), "out3"); 514695b41eeSopenharmony_ci EXPECT_EQ(root_nodes[3]->PathDecanonicalized(), "out4\\foo"); 515695b41eeSopenharmony_ci} 516695b41eeSopenharmony_ci#endif 517695b41eeSopenharmony_ci 518695b41eeSopenharmony_ciTEST_F(GraphTest, DyndepLoadTrivial) { 519695b41eeSopenharmony_ci AssertParse(&state_, 520695b41eeSopenharmony_ci"rule r\n" 521695b41eeSopenharmony_ci" command = unused\n" 522695b41eeSopenharmony_ci"build out: r in || dd\n" 523695b41eeSopenharmony_ci" dyndep = dd\n" 524695b41eeSopenharmony_ci ); 525695b41eeSopenharmony_ci fs_.Create("dd", 526695b41eeSopenharmony_ci"ninja_dyndep_version = 1\n" 527695b41eeSopenharmony_ci"build out: dyndep\n" 528695b41eeSopenharmony_ci ); 529695b41eeSopenharmony_ci 530695b41eeSopenharmony_ci string err; 531695b41eeSopenharmony_ci ASSERT_TRUE(GetNode("dd")->dyndep_pending()); 532695b41eeSopenharmony_ci EXPECT_TRUE(scan_.LoadDyndeps(GetNode("dd"), &err)); 533695b41eeSopenharmony_ci EXPECT_EQ("", err); 534695b41eeSopenharmony_ci EXPECT_FALSE(GetNode("dd")->dyndep_pending()); 535695b41eeSopenharmony_ci 536695b41eeSopenharmony_ci Edge* edge = GetNode("out")->in_edge(); 537695b41eeSopenharmony_ci ASSERT_EQ(1u, edge->outputs_.size()); 538695b41eeSopenharmony_ci EXPECT_EQ("out", edge->outputs_[0]->path()); 539695b41eeSopenharmony_ci ASSERT_EQ(2u, edge->inputs_.size()); 540695b41eeSopenharmony_ci EXPECT_EQ("in", edge->inputs_[0]->path()); 541695b41eeSopenharmony_ci EXPECT_EQ("dd", edge->inputs_[1]->path()); 542695b41eeSopenharmony_ci EXPECT_EQ(0u, edge->implicit_deps_); 543695b41eeSopenharmony_ci EXPECT_EQ(1u, edge->order_only_deps_); 544695b41eeSopenharmony_ci EXPECT_FALSE(edge->GetBindingBool("restat")); 545695b41eeSopenharmony_ci} 546695b41eeSopenharmony_ci 547695b41eeSopenharmony_ciTEST_F(GraphTest, DyndepLoadImplicit) { 548695b41eeSopenharmony_ci AssertParse(&state_, 549695b41eeSopenharmony_ci"rule r\n" 550695b41eeSopenharmony_ci" command = unused\n" 551695b41eeSopenharmony_ci"build out1: r in || dd\n" 552695b41eeSopenharmony_ci" dyndep = dd\n" 553695b41eeSopenharmony_ci"build out2: r in\n" 554695b41eeSopenharmony_ci ); 555695b41eeSopenharmony_ci fs_.Create("dd", 556695b41eeSopenharmony_ci"ninja_dyndep_version = 1\n" 557695b41eeSopenharmony_ci"build out1: dyndep | out2\n" 558695b41eeSopenharmony_ci ); 559695b41eeSopenharmony_ci 560695b41eeSopenharmony_ci string err; 561695b41eeSopenharmony_ci ASSERT_TRUE(GetNode("dd")->dyndep_pending()); 562695b41eeSopenharmony_ci EXPECT_TRUE(scan_.LoadDyndeps(GetNode("dd"), &err)); 563695b41eeSopenharmony_ci EXPECT_EQ("", err); 564695b41eeSopenharmony_ci EXPECT_FALSE(GetNode("dd")->dyndep_pending()); 565695b41eeSopenharmony_ci 566695b41eeSopenharmony_ci Edge* edge = GetNode("out1")->in_edge(); 567695b41eeSopenharmony_ci ASSERT_EQ(1u, edge->outputs_.size()); 568695b41eeSopenharmony_ci EXPECT_EQ("out1", edge->outputs_[0]->path()); 569695b41eeSopenharmony_ci ASSERT_EQ(3u, edge->inputs_.size()); 570695b41eeSopenharmony_ci EXPECT_EQ("in", edge->inputs_[0]->path()); 571695b41eeSopenharmony_ci EXPECT_EQ("out2", edge->inputs_[1]->path()); 572695b41eeSopenharmony_ci EXPECT_EQ("dd", edge->inputs_[2]->path()); 573695b41eeSopenharmony_ci EXPECT_EQ(1u, edge->implicit_deps_); 574695b41eeSopenharmony_ci EXPECT_EQ(1u, edge->order_only_deps_); 575695b41eeSopenharmony_ci EXPECT_FALSE(edge->GetBindingBool("restat")); 576695b41eeSopenharmony_ci} 577695b41eeSopenharmony_ci 578695b41eeSopenharmony_ciTEST_F(GraphTest, DyndepLoadMissingFile) { 579695b41eeSopenharmony_ci AssertParse(&state_, 580695b41eeSopenharmony_ci"rule r\n" 581695b41eeSopenharmony_ci" command = unused\n" 582695b41eeSopenharmony_ci"build out: r in || dd\n" 583695b41eeSopenharmony_ci" dyndep = dd\n" 584695b41eeSopenharmony_ci ); 585695b41eeSopenharmony_ci 586695b41eeSopenharmony_ci string err; 587695b41eeSopenharmony_ci ASSERT_TRUE(GetNode("dd")->dyndep_pending()); 588695b41eeSopenharmony_ci EXPECT_FALSE(scan_.LoadDyndeps(GetNode("dd"), &err)); 589695b41eeSopenharmony_ci EXPECT_EQ("loading 'dd': No such file or directory", err); 590695b41eeSopenharmony_ci} 591695b41eeSopenharmony_ci 592695b41eeSopenharmony_ciTEST_F(GraphTest, DyndepLoadMissingEntry) { 593695b41eeSopenharmony_ci AssertParse(&state_, 594695b41eeSopenharmony_ci"rule r\n" 595695b41eeSopenharmony_ci" command = unused\n" 596695b41eeSopenharmony_ci"build out: r in || dd\n" 597695b41eeSopenharmony_ci" dyndep = dd\n" 598695b41eeSopenharmony_ci ); 599695b41eeSopenharmony_ci fs_.Create("dd", 600695b41eeSopenharmony_ci"ninja_dyndep_version = 1\n" 601695b41eeSopenharmony_ci ); 602695b41eeSopenharmony_ci 603695b41eeSopenharmony_ci string err; 604695b41eeSopenharmony_ci ASSERT_TRUE(GetNode("dd")->dyndep_pending()); 605695b41eeSopenharmony_ci EXPECT_FALSE(scan_.LoadDyndeps(GetNode("dd"), &err)); 606695b41eeSopenharmony_ci EXPECT_EQ("'out' not mentioned in its dyndep file 'dd'", err); 607695b41eeSopenharmony_ci} 608695b41eeSopenharmony_ci 609695b41eeSopenharmony_ciTEST_F(GraphTest, DyndepLoadExtraEntry) { 610695b41eeSopenharmony_ci AssertParse(&state_, 611695b41eeSopenharmony_ci"rule r\n" 612695b41eeSopenharmony_ci" command = unused\n" 613695b41eeSopenharmony_ci"build out: r in || dd\n" 614695b41eeSopenharmony_ci" dyndep = dd\n" 615695b41eeSopenharmony_ci"build out2: r in || dd\n" 616695b41eeSopenharmony_ci ); 617695b41eeSopenharmony_ci fs_.Create("dd", 618695b41eeSopenharmony_ci"ninja_dyndep_version = 1\n" 619695b41eeSopenharmony_ci"build out: dyndep\n" 620695b41eeSopenharmony_ci"build out2: dyndep\n" 621695b41eeSopenharmony_ci ); 622695b41eeSopenharmony_ci 623695b41eeSopenharmony_ci string err; 624695b41eeSopenharmony_ci ASSERT_TRUE(GetNode("dd")->dyndep_pending()); 625695b41eeSopenharmony_ci EXPECT_FALSE(scan_.LoadDyndeps(GetNode("dd"), &err)); 626695b41eeSopenharmony_ci EXPECT_EQ("dyndep file 'dd' mentions output 'out2' whose build statement " 627695b41eeSopenharmony_ci "does not have a dyndep binding for the file", err); 628695b41eeSopenharmony_ci} 629695b41eeSopenharmony_ci 630695b41eeSopenharmony_ciTEST_F(GraphTest, DyndepLoadOutputWithMultipleRules1) { 631695b41eeSopenharmony_ci AssertParse(&state_, 632695b41eeSopenharmony_ci"rule r\n" 633695b41eeSopenharmony_ci" command = unused\n" 634695b41eeSopenharmony_ci"build out1 | out-twice.imp: r in1\n" 635695b41eeSopenharmony_ci"build out2: r in2 || dd\n" 636695b41eeSopenharmony_ci" dyndep = dd\n" 637695b41eeSopenharmony_ci ); 638695b41eeSopenharmony_ci fs_.Create("dd", 639695b41eeSopenharmony_ci"ninja_dyndep_version = 1\n" 640695b41eeSopenharmony_ci"build out2 | out-twice.imp: dyndep\n" 641695b41eeSopenharmony_ci ); 642695b41eeSopenharmony_ci 643695b41eeSopenharmony_ci string err; 644695b41eeSopenharmony_ci ASSERT_TRUE(GetNode("dd")->dyndep_pending()); 645695b41eeSopenharmony_ci EXPECT_FALSE(scan_.LoadDyndeps(GetNode("dd"), &err)); 646695b41eeSopenharmony_ci EXPECT_EQ("multiple rules generate out-twice.imp", err); 647695b41eeSopenharmony_ci} 648695b41eeSopenharmony_ci 649695b41eeSopenharmony_ciTEST_F(GraphTest, DyndepLoadOutputWithMultipleRules2) { 650695b41eeSopenharmony_ci AssertParse(&state_, 651695b41eeSopenharmony_ci"rule r\n" 652695b41eeSopenharmony_ci" command = unused\n" 653695b41eeSopenharmony_ci"build out1: r in1 || dd1\n" 654695b41eeSopenharmony_ci" dyndep = dd1\n" 655695b41eeSopenharmony_ci"build out2: r in2 || dd2\n" 656695b41eeSopenharmony_ci" dyndep = dd2\n" 657695b41eeSopenharmony_ci ); 658695b41eeSopenharmony_ci fs_.Create("dd1", 659695b41eeSopenharmony_ci"ninja_dyndep_version = 1\n" 660695b41eeSopenharmony_ci"build out1 | out-twice.imp: dyndep\n" 661695b41eeSopenharmony_ci ); 662695b41eeSopenharmony_ci fs_.Create("dd2", 663695b41eeSopenharmony_ci"ninja_dyndep_version = 1\n" 664695b41eeSopenharmony_ci"build out2 | out-twice.imp: dyndep\n" 665695b41eeSopenharmony_ci ); 666695b41eeSopenharmony_ci 667695b41eeSopenharmony_ci string err; 668695b41eeSopenharmony_ci ASSERT_TRUE(GetNode("dd1")->dyndep_pending()); 669695b41eeSopenharmony_ci EXPECT_TRUE(scan_.LoadDyndeps(GetNode("dd1"), &err)); 670695b41eeSopenharmony_ci EXPECT_EQ("", err); 671695b41eeSopenharmony_ci ASSERT_TRUE(GetNode("dd2")->dyndep_pending()); 672695b41eeSopenharmony_ci EXPECT_FALSE(scan_.LoadDyndeps(GetNode("dd2"), &err)); 673695b41eeSopenharmony_ci EXPECT_EQ("multiple rules generate out-twice.imp", err); 674695b41eeSopenharmony_ci} 675695b41eeSopenharmony_ci 676695b41eeSopenharmony_ciTEST_F(GraphTest, DyndepLoadMultiple) { 677695b41eeSopenharmony_ci AssertParse(&state_, 678695b41eeSopenharmony_ci"rule r\n" 679695b41eeSopenharmony_ci" command = unused\n" 680695b41eeSopenharmony_ci"build out1: r in1 || dd\n" 681695b41eeSopenharmony_ci" dyndep = dd\n" 682695b41eeSopenharmony_ci"build out2: r in2 || dd\n" 683695b41eeSopenharmony_ci" dyndep = dd\n" 684695b41eeSopenharmony_ci"build outNot: r in3 || dd\n" 685695b41eeSopenharmony_ci ); 686695b41eeSopenharmony_ci fs_.Create("dd", 687695b41eeSopenharmony_ci"ninja_dyndep_version = 1\n" 688695b41eeSopenharmony_ci"build out1 | out1imp: dyndep | in1imp\n" 689695b41eeSopenharmony_ci"build out2: dyndep | in2imp\n" 690695b41eeSopenharmony_ci" restat = 1\n" 691695b41eeSopenharmony_ci ); 692695b41eeSopenharmony_ci 693695b41eeSopenharmony_ci string err; 694695b41eeSopenharmony_ci ASSERT_TRUE(GetNode("dd")->dyndep_pending()); 695695b41eeSopenharmony_ci EXPECT_TRUE(scan_.LoadDyndeps(GetNode("dd"), &err)); 696695b41eeSopenharmony_ci EXPECT_EQ("", err); 697695b41eeSopenharmony_ci EXPECT_FALSE(GetNode("dd")->dyndep_pending()); 698695b41eeSopenharmony_ci 699695b41eeSopenharmony_ci Edge* edge1 = GetNode("out1")->in_edge(); 700695b41eeSopenharmony_ci ASSERT_EQ(2u, edge1->outputs_.size()); 701695b41eeSopenharmony_ci EXPECT_EQ("out1", edge1->outputs_[0]->path()); 702695b41eeSopenharmony_ci EXPECT_EQ("out1imp", edge1->outputs_[1]->path()); 703695b41eeSopenharmony_ci EXPECT_EQ(1u, edge1->implicit_outs_); 704695b41eeSopenharmony_ci ASSERT_EQ(3u, edge1->inputs_.size()); 705695b41eeSopenharmony_ci EXPECT_EQ("in1", edge1->inputs_[0]->path()); 706695b41eeSopenharmony_ci EXPECT_EQ("in1imp", edge1->inputs_[1]->path()); 707695b41eeSopenharmony_ci EXPECT_EQ("dd", edge1->inputs_[2]->path()); 708695b41eeSopenharmony_ci EXPECT_EQ(1u, edge1->implicit_deps_); 709695b41eeSopenharmony_ci EXPECT_EQ(1u, edge1->order_only_deps_); 710695b41eeSopenharmony_ci EXPECT_FALSE(edge1->GetBindingBool("restat")); 711695b41eeSopenharmony_ci EXPECT_EQ(edge1, GetNode("out1imp")->in_edge()); 712695b41eeSopenharmony_ci Node* in1imp = GetNode("in1imp"); 713695b41eeSopenharmony_ci ASSERT_EQ(1u, in1imp->out_edges().size()); 714695b41eeSopenharmony_ci EXPECT_EQ(edge1, in1imp->out_edges()[0]); 715695b41eeSopenharmony_ci 716695b41eeSopenharmony_ci Edge* edge2 = GetNode("out2")->in_edge(); 717695b41eeSopenharmony_ci ASSERT_EQ(1u, edge2->outputs_.size()); 718695b41eeSopenharmony_ci EXPECT_EQ("out2", edge2->outputs_[0]->path()); 719695b41eeSopenharmony_ci EXPECT_EQ(0u, edge2->implicit_outs_); 720695b41eeSopenharmony_ci ASSERT_EQ(3u, edge2->inputs_.size()); 721695b41eeSopenharmony_ci EXPECT_EQ("in2", edge2->inputs_[0]->path()); 722695b41eeSopenharmony_ci EXPECT_EQ("in2imp", edge2->inputs_[1]->path()); 723695b41eeSopenharmony_ci EXPECT_EQ("dd", edge2->inputs_[2]->path()); 724695b41eeSopenharmony_ci EXPECT_EQ(1u, edge2->implicit_deps_); 725695b41eeSopenharmony_ci EXPECT_EQ(1u, edge2->order_only_deps_); 726695b41eeSopenharmony_ci EXPECT_TRUE(edge2->GetBindingBool("restat")); 727695b41eeSopenharmony_ci Node* in2imp = GetNode("in2imp"); 728695b41eeSopenharmony_ci ASSERT_EQ(1u, in2imp->out_edges().size()); 729695b41eeSopenharmony_ci EXPECT_EQ(edge2, in2imp->out_edges()[0]); 730695b41eeSopenharmony_ci} 731695b41eeSopenharmony_ci 732695b41eeSopenharmony_ciTEST_F(GraphTest, DyndepFileMissing) { 733695b41eeSopenharmony_ci AssertParse(&state_, 734695b41eeSopenharmony_ci"rule r\n" 735695b41eeSopenharmony_ci" command = unused\n" 736695b41eeSopenharmony_ci"build out: r || dd\n" 737695b41eeSopenharmony_ci" dyndep = dd\n" 738695b41eeSopenharmony_ci ); 739695b41eeSopenharmony_ci 740695b41eeSopenharmony_ci string err; 741695b41eeSopenharmony_ci EXPECT_FALSE(scan_.RecomputeDirty(GetNode("out"), NULL, &err)); 742695b41eeSopenharmony_ci ASSERT_EQ("loading 'dd': No such file or directory", err); 743695b41eeSopenharmony_ci} 744695b41eeSopenharmony_ci 745695b41eeSopenharmony_ciTEST_F(GraphTest, DyndepFileError) { 746695b41eeSopenharmony_ci AssertParse(&state_, 747695b41eeSopenharmony_ci"rule r\n" 748695b41eeSopenharmony_ci" command = unused\n" 749695b41eeSopenharmony_ci"build out: r || dd\n" 750695b41eeSopenharmony_ci" dyndep = dd\n" 751695b41eeSopenharmony_ci ); 752695b41eeSopenharmony_ci fs_.Create("dd", 753695b41eeSopenharmony_ci"ninja_dyndep_version = 1\n" 754695b41eeSopenharmony_ci ); 755695b41eeSopenharmony_ci 756695b41eeSopenharmony_ci string err; 757695b41eeSopenharmony_ci EXPECT_FALSE(scan_.RecomputeDirty(GetNode("out"), NULL, &err)); 758695b41eeSopenharmony_ci ASSERT_EQ("'out' not mentioned in its dyndep file 'dd'", err); 759695b41eeSopenharmony_ci} 760695b41eeSopenharmony_ci 761695b41eeSopenharmony_ciTEST_F(GraphTest, DyndepImplicitInputNewer) { 762695b41eeSopenharmony_ci AssertParse(&state_, 763695b41eeSopenharmony_ci"rule r\n" 764695b41eeSopenharmony_ci" command = unused\n" 765695b41eeSopenharmony_ci"build out: r || dd\n" 766695b41eeSopenharmony_ci" dyndep = dd\n" 767695b41eeSopenharmony_ci ); 768695b41eeSopenharmony_ci fs_.Create("dd", 769695b41eeSopenharmony_ci"ninja_dyndep_version = 1\n" 770695b41eeSopenharmony_ci"build out: dyndep | in\n" 771695b41eeSopenharmony_ci ); 772695b41eeSopenharmony_ci fs_.Create("out", ""); 773695b41eeSopenharmony_ci fs_.Tick(); 774695b41eeSopenharmony_ci fs_.Create("in", ""); 775695b41eeSopenharmony_ci 776695b41eeSopenharmony_ci string err; 777695b41eeSopenharmony_ci EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err)); 778695b41eeSopenharmony_ci ASSERT_EQ("", err); 779695b41eeSopenharmony_ci 780695b41eeSopenharmony_ci EXPECT_FALSE(GetNode("in")->dirty()); 781695b41eeSopenharmony_ci EXPECT_FALSE(GetNode("dd")->dirty()); 782695b41eeSopenharmony_ci 783695b41eeSopenharmony_ci // "out" is dirty due to dyndep-specified implicit input 784695b41eeSopenharmony_ci EXPECT_TRUE(GetNode("out")->dirty()); 785695b41eeSopenharmony_ci} 786695b41eeSopenharmony_ci 787695b41eeSopenharmony_ciTEST_F(GraphTest, DyndepFileReady) { 788695b41eeSopenharmony_ci AssertParse(&state_, 789695b41eeSopenharmony_ci"rule r\n" 790695b41eeSopenharmony_ci" command = unused\n" 791695b41eeSopenharmony_ci"build dd: r dd-in\n" 792695b41eeSopenharmony_ci"build out: r || dd\n" 793695b41eeSopenharmony_ci" dyndep = dd\n" 794695b41eeSopenharmony_ci ); 795695b41eeSopenharmony_ci fs_.Create("dd-in", ""); 796695b41eeSopenharmony_ci fs_.Create("dd", 797695b41eeSopenharmony_ci"ninja_dyndep_version = 1\n" 798695b41eeSopenharmony_ci"build out: dyndep | in\n" 799695b41eeSopenharmony_ci ); 800695b41eeSopenharmony_ci fs_.Create("out", ""); 801695b41eeSopenharmony_ci fs_.Tick(); 802695b41eeSopenharmony_ci fs_.Create("in", ""); 803695b41eeSopenharmony_ci 804695b41eeSopenharmony_ci string err; 805695b41eeSopenharmony_ci EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err)); 806695b41eeSopenharmony_ci ASSERT_EQ("", err); 807695b41eeSopenharmony_ci 808695b41eeSopenharmony_ci EXPECT_FALSE(GetNode("in")->dirty()); 809695b41eeSopenharmony_ci EXPECT_FALSE(GetNode("dd")->dirty()); 810695b41eeSopenharmony_ci EXPECT_TRUE(GetNode("dd")->in_edge()->outputs_ready()); 811695b41eeSopenharmony_ci 812695b41eeSopenharmony_ci // "out" is dirty due to dyndep-specified implicit input 813695b41eeSopenharmony_ci EXPECT_TRUE(GetNode("out")->dirty()); 814695b41eeSopenharmony_ci} 815695b41eeSopenharmony_ci 816695b41eeSopenharmony_ciTEST_F(GraphTest, DyndepFileNotClean) { 817695b41eeSopenharmony_ci AssertParse(&state_, 818695b41eeSopenharmony_ci"rule r\n" 819695b41eeSopenharmony_ci" command = unused\n" 820695b41eeSopenharmony_ci"build dd: r dd-in\n" 821695b41eeSopenharmony_ci"build out: r || dd\n" 822695b41eeSopenharmony_ci" dyndep = dd\n" 823695b41eeSopenharmony_ci ); 824695b41eeSopenharmony_ci fs_.Create("dd", "this-should-not-be-loaded"); 825695b41eeSopenharmony_ci fs_.Tick(); 826695b41eeSopenharmony_ci fs_.Create("dd-in", ""); 827695b41eeSopenharmony_ci fs_.Create("out", ""); 828695b41eeSopenharmony_ci 829695b41eeSopenharmony_ci string err; 830695b41eeSopenharmony_ci EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err)); 831695b41eeSopenharmony_ci ASSERT_EQ("", err); 832695b41eeSopenharmony_ci 833695b41eeSopenharmony_ci EXPECT_TRUE(GetNode("dd")->dirty()); 834695b41eeSopenharmony_ci EXPECT_FALSE(GetNode("dd")->in_edge()->outputs_ready()); 835695b41eeSopenharmony_ci 836695b41eeSopenharmony_ci // "out" is clean but not ready since "dd" is not ready 837695b41eeSopenharmony_ci EXPECT_FALSE(GetNode("out")->dirty()); 838695b41eeSopenharmony_ci EXPECT_FALSE(GetNode("out")->in_edge()->outputs_ready()); 839695b41eeSopenharmony_ci} 840695b41eeSopenharmony_ci 841695b41eeSopenharmony_ciTEST_F(GraphTest, DyndepFileNotReady) { 842695b41eeSopenharmony_ci AssertParse(&state_, 843695b41eeSopenharmony_ci"rule r\n" 844695b41eeSopenharmony_ci" command = unused\n" 845695b41eeSopenharmony_ci"build tmp: r\n" 846695b41eeSopenharmony_ci"build dd: r dd-in || tmp\n" 847695b41eeSopenharmony_ci"build out: r || dd\n" 848695b41eeSopenharmony_ci" dyndep = dd\n" 849695b41eeSopenharmony_ci ); 850695b41eeSopenharmony_ci fs_.Create("dd", "this-should-not-be-loaded"); 851695b41eeSopenharmony_ci fs_.Create("dd-in", ""); 852695b41eeSopenharmony_ci fs_.Tick(); 853695b41eeSopenharmony_ci fs_.Create("out", ""); 854695b41eeSopenharmony_ci 855695b41eeSopenharmony_ci string err; 856695b41eeSopenharmony_ci EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err)); 857695b41eeSopenharmony_ci ASSERT_EQ("", err); 858695b41eeSopenharmony_ci 859695b41eeSopenharmony_ci EXPECT_FALSE(GetNode("dd")->dirty()); 860695b41eeSopenharmony_ci EXPECT_FALSE(GetNode("dd")->in_edge()->outputs_ready()); 861695b41eeSopenharmony_ci EXPECT_FALSE(GetNode("out")->dirty()); 862695b41eeSopenharmony_ci EXPECT_FALSE(GetNode("out")->in_edge()->outputs_ready()); 863695b41eeSopenharmony_ci} 864695b41eeSopenharmony_ci 865695b41eeSopenharmony_ciTEST_F(GraphTest, DyndepFileSecondNotReady) { 866695b41eeSopenharmony_ci AssertParse(&state_, 867695b41eeSopenharmony_ci"rule r\n" 868695b41eeSopenharmony_ci" command = unused\n" 869695b41eeSopenharmony_ci"build dd1: r dd1-in\n" 870695b41eeSopenharmony_ci"build dd2-in: r || dd1\n" 871695b41eeSopenharmony_ci" dyndep = dd1\n" 872695b41eeSopenharmony_ci"build dd2: r dd2-in\n" 873695b41eeSopenharmony_ci"build out: r || dd2\n" 874695b41eeSopenharmony_ci" dyndep = dd2\n" 875695b41eeSopenharmony_ci ); 876695b41eeSopenharmony_ci fs_.Create("dd1", ""); 877695b41eeSopenharmony_ci fs_.Create("dd2", ""); 878695b41eeSopenharmony_ci fs_.Create("dd2-in", ""); 879695b41eeSopenharmony_ci fs_.Tick(); 880695b41eeSopenharmony_ci fs_.Create("dd1-in", ""); 881695b41eeSopenharmony_ci fs_.Create("out", ""); 882695b41eeSopenharmony_ci 883695b41eeSopenharmony_ci string err; 884695b41eeSopenharmony_ci EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err)); 885695b41eeSopenharmony_ci ASSERT_EQ("", err); 886695b41eeSopenharmony_ci 887695b41eeSopenharmony_ci EXPECT_TRUE(GetNode("dd1")->dirty()); 888695b41eeSopenharmony_ci EXPECT_FALSE(GetNode("dd1")->in_edge()->outputs_ready()); 889695b41eeSopenharmony_ci EXPECT_FALSE(GetNode("dd2")->dirty()); 890695b41eeSopenharmony_ci EXPECT_FALSE(GetNode("dd2")->in_edge()->outputs_ready()); 891695b41eeSopenharmony_ci EXPECT_FALSE(GetNode("out")->dirty()); 892695b41eeSopenharmony_ci EXPECT_FALSE(GetNode("out")->in_edge()->outputs_ready()); 893695b41eeSopenharmony_ci} 894695b41eeSopenharmony_ci 895695b41eeSopenharmony_ciTEST_F(GraphTest, DyndepFileCircular) { 896695b41eeSopenharmony_ci AssertParse(&state_, 897695b41eeSopenharmony_ci"rule r\n" 898695b41eeSopenharmony_ci" command = unused\n" 899695b41eeSopenharmony_ci"build out: r in || dd\n" 900695b41eeSopenharmony_ci" depfile = out.d\n" 901695b41eeSopenharmony_ci" dyndep = dd\n" 902695b41eeSopenharmony_ci"build in: r circ\n" 903695b41eeSopenharmony_ci ); 904695b41eeSopenharmony_ci fs_.Create("out.d", "out: inimp\n"); 905695b41eeSopenharmony_ci fs_.Create("dd", 906695b41eeSopenharmony_ci"ninja_dyndep_version = 1\n" 907695b41eeSopenharmony_ci"build out | circ: dyndep\n" 908695b41eeSopenharmony_ci ); 909695b41eeSopenharmony_ci fs_.Create("out", ""); 910695b41eeSopenharmony_ci 911695b41eeSopenharmony_ci Edge* edge = GetNode("out")->in_edge(); 912695b41eeSopenharmony_ci string err; 913695b41eeSopenharmony_ci EXPECT_FALSE(scan_.RecomputeDirty(GetNode("out"), NULL, &err)); 914695b41eeSopenharmony_ci EXPECT_EQ("dependency cycle: circ -> in -> circ", err); 915695b41eeSopenharmony_ci 916695b41eeSopenharmony_ci // Verify that "out.d" was loaded exactly once despite 917695b41eeSopenharmony_ci // circular reference discovered from dyndep file. 918695b41eeSopenharmony_ci ASSERT_EQ(3u, edge->inputs_.size()); 919695b41eeSopenharmony_ci EXPECT_EQ("in", edge->inputs_[0]->path()); 920695b41eeSopenharmony_ci EXPECT_EQ("inimp", edge->inputs_[1]->path()); 921695b41eeSopenharmony_ci EXPECT_EQ("dd", edge->inputs_[2]->path()); 922695b41eeSopenharmony_ci EXPECT_EQ(1u, edge->implicit_deps_); 923695b41eeSopenharmony_ci EXPECT_EQ(1u, edge->order_only_deps_); 924695b41eeSopenharmony_ci} 925695b41eeSopenharmony_ci 926695b41eeSopenharmony_ciTEST_F(GraphTest, Validation) { 927695b41eeSopenharmony_ci ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, 928695b41eeSopenharmony_ci"build out: cat in |@ validate\n" 929695b41eeSopenharmony_ci"build validate: cat in\n")); 930695b41eeSopenharmony_ci 931695b41eeSopenharmony_ci fs_.Create("in", ""); 932695b41eeSopenharmony_ci string err; 933695b41eeSopenharmony_ci std::vector<Node*> validation_nodes; 934695b41eeSopenharmony_ci EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), &validation_nodes, &err)); 935695b41eeSopenharmony_ci ASSERT_EQ("", err); 936695b41eeSopenharmony_ci 937695b41eeSopenharmony_ci ASSERT_EQ(validation_nodes.size(), 1); 938695b41eeSopenharmony_ci EXPECT_EQ(validation_nodes[0]->path(), "validate"); 939695b41eeSopenharmony_ci 940695b41eeSopenharmony_ci EXPECT_TRUE(GetNode("out")->dirty()); 941695b41eeSopenharmony_ci EXPECT_TRUE(GetNode("validate")->dirty()); 942695b41eeSopenharmony_ci} 943695b41eeSopenharmony_ci 944695b41eeSopenharmony_ci// Check that phony's dependencies' mtimes are propagated. 945695b41eeSopenharmony_ciTEST_F(GraphTest, PhonyDepsMtimes) { 946695b41eeSopenharmony_ci string err; 947695b41eeSopenharmony_ci ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, 948695b41eeSopenharmony_ci"rule touch\n" 949695b41eeSopenharmony_ci" command = touch $out\n" 950695b41eeSopenharmony_ci"build in_ph: phony in1\n" 951695b41eeSopenharmony_ci"build out1: touch in_ph\n" 952695b41eeSopenharmony_ci)); 953695b41eeSopenharmony_ci fs_.Create("in1", ""); 954695b41eeSopenharmony_ci fs_.Create("out1", ""); 955695b41eeSopenharmony_ci Node* out1 = GetNode("out1"); 956695b41eeSopenharmony_ci Node* in1 = GetNode("in1"); 957695b41eeSopenharmony_ci 958695b41eeSopenharmony_ci EXPECT_TRUE(scan_.RecomputeDirty(out1, NULL, &err)); 959695b41eeSopenharmony_ci EXPECT_TRUE(!out1->dirty()); 960695b41eeSopenharmony_ci 961695b41eeSopenharmony_ci // Get the mtime of out1 962695b41eeSopenharmony_ci ASSERT_TRUE(in1->Stat(&fs_, &err)); 963695b41eeSopenharmony_ci ASSERT_TRUE(out1->Stat(&fs_, &err)); 964695b41eeSopenharmony_ci TimeStamp out1Mtime1 = out1->mtime(); 965695b41eeSopenharmony_ci TimeStamp in1Mtime1 = in1->mtime(); 966695b41eeSopenharmony_ci 967695b41eeSopenharmony_ci // Touch in1. This should cause out1 to be dirty 968695b41eeSopenharmony_ci state_.Reset(); 969695b41eeSopenharmony_ci fs_.Tick(); 970695b41eeSopenharmony_ci fs_.Create("in1", ""); 971695b41eeSopenharmony_ci 972695b41eeSopenharmony_ci ASSERT_TRUE(in1->Stat(&fs_, &err)); 973695b41eeSopenharmony_ci EXPECT_GT(in1->mtime(), in1Mtime1); 974695b41eeSopenharmony_ci 975695b41eeSopenharmony_ci EXPECT_TRUE(scan_.RecomputeDirty(out1, NULL, &err)); 976695b41eeSopenharmony_ci EXPECT_GT(in1->mtime(), in1Mtime1); 977695b41eeSopenharmony_ci EXPECT_EQ(out1->mtime(), out1Mtime1); 978695b41eeSopenharmony_ci EXPECT_TRUE(out1->dirty()); 979695b41eeSopenharmony_ci} 980695b41eeSopenharmony_ci 981