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