xref: /third_party/ninja/src/graph_test.cc (revision 695b41ee)
1// Copyright 2011 Google Inc. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include "graph.h"
16#include "build.h"
17
18#include "test.h"
19
20using namespace std;
21
22struct GraphTest : public StateTestWithBuiltinRules {
23  GraphTest() : scan_(&state_, NULL, NULL, &fs_, NULL) {}
24
25  VirtualFileSystem fs_;
26  DependencyScan scan_;
27};
28
29TEST_F(GraphTest, MissingImplicit) {
30  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
31"build out: cat in | implicit\n"));
32  fs_.Create("in", "");
33  fs_.Create("out", "");
34
35  string err;
36  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
37  ASSERT_EQ("", err);
38
39  // A missing implicit dep *should* make the output dirty.
40  // (In fact, a build will fail.)
41  // This is a change from prior semantics of ninja.
42  EXPECT_TRUE(GetNode("out")->dirty());
43}
44
45TEST_F(GraphTest, ModifiedImplicit) {
46  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
47"build out: cat in | implicit\n"));
48  fs_.Create("in", "");
49  fs_.Create("out", "");
50  fs_.Tick();
51  fs_.Create("implicit", "");
52
53  string err;
54  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
55  ASSERT_EQ("", err);
56
57  // A modified implicit dep should make the output dirty.
58  EXPECT_TRUE(GetNode("out")->dirty());
59}
60
61TEST_F(GraphTest, FunkyMakefilePath) {
62  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
63"rule catdep\n"
64"  depfile = $out.d\n"
65"  command = cat $in > $out\n"
66"build out.o: catdep foo.cc\n"));
67  fs_.Create("foo.cc",  "");
68  fs_.Create("out.o.d", "out.o: ./foo/../implicit.h\n");
69  fs_.Create("out.o", "");
70  fs_.Tick();
71  fs_.Create("implicit.h", "");
72
73  string err;
74  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), NULL, &err));
75  ASSERT_EQ("", err);
76
77  // implicit.h has changed, though our depfile refers to it with a
78  // non-canonical path; we should still find it.
79  EXPECT_TRUE(GetNode("out.o")->dirty());
80}
81
82TEST_F(GraphTest, ExplicitImplicit) {
83  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
84"rule catdep\n"
85"  depfile = $out.d\n"
86"  command = cat $in > $out\n"
87"build implicit.h: cat data\n"
88"build out.o: catdep foo.cc || implicit.h\n"));
89  fs_.Create("implicit.h", "");
90  fs_.Create("foo.cc", "");
91  fs_.Create("out.o.d", "out.o: implicit.h\n");
92  fs_.Create("out.o", "");
93  fs_.Tick();
94  fs_.Create("data", "");
95
96  string err;
97  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), NULL, &err));
98  ASSERT_EQ("", err);
99
100  // We have both an implicit and an explicit dep on implicit.h.
101  // The implicit dep should "win" (in the sense that it should cause
102  // the output to be dirty).
103  EXPECT_TRUE(GetNode("out.o")->dirty());
104}
105
106TEST_F(GraphTest, ImplicitOutputParse) {
107  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
108"build out | out.imp: cat in\n"));
109
110  Edge* edge = GetNode("out")->in_edge();
111  EXPECT_EQ(2, edge->outputs_.size());
112  EXPECT_EQ("out", edge->outputs_[0]->path());
113  EXPECT_EQ("out.imp", edge->outputs_[1]->path());
114  EXPECT_EQ(1, edge->implicit_outs_);
115  EXPECT_EQ(edge, GetNode("out.imp")->in_edge());
116}
117
118TEST_F(GraphTest, ImplicitOutputMissing) {
119  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
120"build out | out.imp: cat in\n"));
121  fs_.Create("in", "");
122  fs_.Create("out", "");
123
124  string err;
125  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
126  ASSERT_EQ("", err);
127
128  EXPECT_TRUE(GetNode("out")->dirty());
129  EXPECT_TRUE(GetNode("out.imp")->dirty());
130}
131
132TEST_F(GraphTest, ImplicitOutputOutOfDate) {
133  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
134"build out | out.imp: cat in\n"));
135  fs_.Create("out.imp", "");
136  fs_.Tick();
137  fs_.Create("in", "");
138  fs_.Create("out", "");
139
140  string err;
141  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
142  ASSERT_EQ("", err);
143
144  EXPECT_TRUE(GetNode("out")->dirty());
145  EXPECT_TRUE(GetNode("out.imp")->dirty());
146}
147
148TEST_F(GraphTest, ImplicitOutputOnlyParse) {
149  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
150"build | out.imp: cat in\n"));
151
152  Edge* edge = GetNode("out.imp")->in_edge();
153  EXPECT_EQ(1, edge->outputs_.size());
154  EXPECT_EQ("out.imp", edge->outputs_[0]->path());
155  EXPECT_EQ(1, edge->implicit_outs_);
156  EXPECT_EQ(edge, GetNode("out.imp")->in_edge());
157}
158
159TEST_F(GraphTest, ImplicitOutputOnlyMissing) {
160  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
161"build | out.imp: cat in\n"));
162  fs_.Create("in", "");
163
164  string err;
165  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.imp"), NULL, &err));
166  ASSERT_EQ("", err);
167
168  EXPECT_TRUE(GetNode("out.imp")->dirty());
169}
170
171TEST_F(GraphTest, ImplicitOutputOnlyOutOfDate) {
172  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
173"build | out.imp: cat in\n"));
174  fs_.Create("out.imp", "");
175  fs_.Tick();
176  fs_.Create("in", "");
177
178  string err;
179  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.imp"), NULL, &err));
180  ASSERT_EQ("", err);
181
182  EXPECT_TRUE(GetNode("out.imp")->dirty());
183}
184
185TEST_F(GraphTest, PathWithCurrentDirectory) {
186  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
187"rule catdep\n"
188"  depfile = $out.d\n"
189"  command = cat $in > $out\n"
190"build ./out.o: catdep ./foo.cc\n"));
191  fs_.Create("foo.cc", "");
192  fs_.Create("out.o.d", "out.o: foo.cc\n");
193  fs_.Create("out.o", "");
194
195  string err;
196  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), NULL, &err));
197  ASSERT_EQ("", err);
198
199  EXPECT_FALSE(GetNode("out.o")->dirty());
200}
201
202TEST_F(GraphTest, RootNodes) {
203  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
204"build out1: cat in1\n"
205"build mid1: cat in1\n"
206"build out2: cat mid1\n"
207"build out3 out4: cat mid1\n"));
208
209  string err;
210  vector<Node*> root_nodes = state_.RootNodes(&err);
211  EXPECT_EQ(4u, root_nodes.size());
212  for (size_t i = 0; i < root_nodes.size(); ++i) {
213    string name = root_nodes[i]->path();
214    EXPECT_EQ("out", name.substr(0, 3));
215  }
216}
217
218TEST_F(GraphTest, CollectInputs) {
219  ASSERT_NO_FATAL_FAILURE(AssertParse(
220      &state_,
221      "build out$ 1: cat in1 in2 in$ with$ space | implicit || order_only\n"));
222
223  std::vector<std::string> inputs;
224  Edge* edge = GetNode("out 1")->in_edge();
225
226  // Test without shell escaping.
227  inputs.clear();
228  edge->CollectInputs(false, &inputs);
229  EXPECT_EQ(5u, inputs.size());
230  EXPECT_EQ("in1", inputs[0]);
231  EXPECT_EQ("in2", inputs[1]);
232  EXPECT_EQ("in with space", inputs[2]);
233  EXPECT_EQ("implicit", inputs[3]);
234  EXPECT_EQ("order_only", inputs[4]);
235
236  // Test with shell escaping.
237  inputs.clear();
238  edge->CollectInputs(true, &inputs);
239  EXPECT_EQ(5u, inputs.size());
240  EXPECT_EQ("in1", inputs[0]);
241  EXPECT_EQ("in2", inputs[1]);
242#ifdef _WIN32
243  EXPECT_EQ("\"in with space\"", inputs[2]);
244#else
245  EXPECT_EQ("'in with space'", inputs[2]);
246#endif
247  EXPECT_EQ("implicit", inputs[3]);
248  EXPECT_EQ("order_only", inputs[4]);
249}
250
251TEST_F(GraphTest, VarInOutPathEscaping) {
252  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
253"build a$ b: cat no'space with$ space$$ no\"space2\n"));
254
255  Edge* edge = GetNode("a b")->in_edge();
256#ifdef _WIN32
257  EXPECT_EQ("cat no'space \"with space$\" \"no\\\"space2\" > \"a b\"",
258      edge->EvaluateCommand());
259#else
260  EXPECT_EQ("cat 'no'\\''space' 'with space$' 'no\"space2' > 'a b'",
261      edge->EvaluateCommand());
262#endif
263}
264
265// Regression test for https://github.com/ninja-build/ninja/issues/380
266TEST_F(GraphTest, DepfileWithCanonicalizablePath) {
267  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
268"rule catdep\n"
269"  depfile = $out.d\n"
270"  command = cat $in > $out\n"
271"build ./out.o: catdep ./foo.cc\n"));
272  fs_.Create("foo.cc", "");
273  fs_.Create("out.o.d", "out.o: bar/../foo.cc\n");
274  fs_.Create("out.o", "");
275
276  string err;
277  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), NULL, &err));
278  ASSERT_EQ("", err);
279
280  EXPECT_FALSE(GetNode("out.o")->dirty());
281}
282
283// Regression test for https://github.com/ninja-build/ninja/issues/404
284TEST_F(GraphTest, DepfileRemoved) {
285  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
286"rule catdep\n"
287"  depfile = $out.d\n"
288"  command = cat $in > $out\n"
289"build ./out.o: catdep ./foo.cc\n"));
290  fs_.Create("foo.h", "");
291  fs_.Create("foo.cc", "");
292  fs_.Tick();
293  fs_.Create("out.o.d", "out.o: foo.h\n");
294  fs_.Create("out.o", "");
295
296  string err;
297  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), NULL, &err));
298  ASSERT_EQ("", err);
299  EXPECT_FALSE(GetNode("out.o")->dirty());
300
301  state_.Reset();
302  fs_.RemoveFile("out.o.d");
303  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), NULL, &err));
304  ASSERT_EQ("", err);
305  EXPECT_TRUE(GetNode("out.o")->dirty());
306}
307
308// Check that rule-level variables are in scope for eval.
309TEST_F(GraphTest, RuleVariablesInScope) {
310  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
311"rule r\n"
312"  depfile = x\n"
313"  command = depfile is $depfile\n"
314"build out: r in\n"));
315  Edge* edge = GetNode("out")->in_edge();
316  EXPECT_EQ("depfile is x", edge->EvaluateCommand());
317}
318
319// Check that build statements can override rule builtins like depfile.
320TEST_F(GraphTest, DepfileOverride) {
321  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
322"rule r\n"
323"  depfile = x\n"
324"  command = unused\n"
325"build out: r in\n"
326"  depfile = y\n"));
327  Edge* edge = GetNode("out")->in_edge();
328  EXPECT_EQ("y", edge->GetBinding("depfile"));
329}
330
331// Check that overridden values show up in expansion of rule-level bindings.
332TEST_F(GraphTest, DepfileOverrideParent) {
333  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
334"rule r\n"
335"  depfile = x\n"
336"  command = depfile is $depfile\n"
337"build out: r in\n"
338"  depfile = y\n"));
339  Edge* edge = GetNode("out")->in_edge();
340  EXPECT_EQ("depfile is y", edge->GetBinding("command"));
341}
342
343// Verify that building a nested phony rule prints "no work to do"
344TEST_F(GraphTest, NestedPhonyPrintsDone) {
345  AssertParse(&state_,
346"build n1: phony \n"
347"build n2: phony n1\n"
348  );
349  string err;
350  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("n2"), NULL, &err));
351  ASSERT_EQ("", err);
352
353  Plan plan_;
354  EXPECT_TRUE(plan_.AddTarget(GetNode("n2"), &err));
355  ASSERT_EQ("", err);
356
357  EXPECT_EQ(0, plan_.command_edge_count());
358  ASSERT_FALSE(plan_.more_to_do());
359}
360
361TEST_F(GraphTest, PhonySelfReferenceError) {
362  ManifestParserOptions parser_opts;
363  parser_opts.phony_cycle_action_ = kPhonyCycleActionError;
364  AssertParse(&state_,
365"build a: phony a\n",
366  parser_opts);
367
368  string err;
369  EXPECT_FALSE(scan_.RecomputeDirty(GetNode("a"), NULL, &err));
370  ASSERT_EQ("dependency cycle: a -> a [-w phonycycle=err]", err);
371}
372
373TEST_F(GraphTest, DependencyCycle) {
374  AssertParse(&state_,
375"build out: cat mid\n"
376"build mid: cat in\n"
377"build in: cat pre\n"
378"build pre: cat out\n");
379
380  string err;
381  EXPECT_FALSE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
382  ASSERT_EQ("dependency cycle: out -> mid -> in -> pre -> out", err);
383}
384
385TEST_F(GraphTest, CycleInEdgesButNotInNodes1) {
386  string err;
387  AssertParse(&state_,
388"build a b: cat a\n");
389  EXPECT_FALSE(scan_.RecomputeDirty(GetNode("b"), NULL, &err));
390  ASSERT_EQ("dependency cycle: a -> a", err);
391}
392
393TEST_F(GraphTest, CycleInEdgesButNotInNodes2) {
394  string err;
395  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
396"build b a: cat a\n"));
397  EXPECT_FALSE(scan_.RecomputeDirty(GetNode("b"), NULL, &err));
398  ASSERT_EQ("dependency cycle: a -> a", err);
399}
400
401TEST_F(GraphTest, CycleInEdgesButNotInNodes3) {
402  string err;
403  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
404"build a b: cat c\n"
405"build c: cat a\n"));
406  EXPECT_FALSE(scan_.RecomputeDirty(GetNode("b"), NULL, &err));
407  ASSERT_EQ("dependency cycle: a -> c -> a", err);
408}
409
410TEST_F(GraphTest, CycleInEdgesButNotInNodes4) {
411  string err;
412  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
413"build d: cat c\n"
414"build c: cat b\n"
415"build b: cat a\n"
416"build a e: cat d\n"
417"build f: cat e\n"));
418  EXPECT_FALSE(scan_.RecomputeDirty(GetNode("f"), NULL, &err));
419  ASSERT_EQ("dependency cycle: a -> d -> c -> b -> a", err);
420}
421
422// Verify that cycles in graphs with multiple outputs are handled correctly
423// in RecomputeDirty() and don't cause deps to be loaded multiple times.
424TEST_F(GraphTest, CycleWithLengthZeroFromDepfile) {
425  AssertParse(&state_,
426"rule deprule\n"
427"   depfile = dep.d\n"
428"   command = unused\n"
429"build a b: deprule\n"
430  );
431  fs_.Create("dep.d", "a: b\n");
432
433  string err;
434  EXPECT_FALSE(scan_.RecomputeDirty(GetNode("a"), NULL, &err));
435  ASSERT_EQ("dependency cycle: b -> b", err);
436
437  // Despite the depfile causing edge to be a cycle (it has outputs a and b,
438  // but the depfile also adds b as an input), the deps should have been loaded
439  // only once:
440  Edge* edge = GetNode("a")->in_edge();
441  EXPECT_EQ(1, edge->inputs_.size());
442  EXPECT_EQ("b", edge->inputs_[0]->path());
443}
444
445// Like CycleWithLengthZeroFromDepfile but with a higher cycle length.
446TEST_F(GraphTest, CycleWithLengthOneFromDepfile) {
447  AssertParse(&state_,
448"rule deprule\n"
449"   depfile = dep.d\n"
450"   command = unused\n"
451"rule r\n"
452"   command = unused\n"
453"build a b: deprule\n"
454"build c: r b\n"
455  );
456  fs_.Create("dep.d", "a: c\n");
457
458  string err;
459  EXPECT_FALSE(scan_.RecomputeDirty(GetNode("a"), NULL, &err));
460  ASSERT_EQ("dependency cycle: b -> c -> b", err);
461
462  // Despite the depfile causing edge to be a cycle (|edge| has outputs a and b,
463  // but c's in_edge has b as input but the depfile also adds |edge| as
464  // output)), the deps should have been loaded only once:
465  Edge* edge = GetNode("a")->in_edge();
466  EXPECT_EQ(1, edge->inputs_.size());
467  EXPECT_EQ("c", edge->inputs_[0]->path());
468}
469
470// Like CycleWithLengthOneFromDepfile but building a node one hop away from
471// the cycle.
472TEST_F(GraphTest, CycleWithLengthOneFromDepfileOneHopAway) {
473  AssertParse(&state_,
474"rule deprule\n"
475"   depfile = dep.d\n"
476"   command = unused\n"
477"rule r\n"
478"   command = unused\n"
479"build a b: deprule\n"
480"build c: r b\n"
481"build d: r a\n"
482  );
483  fs_.Create("dep.d", "a: c\n");
484
485  string err;
486  EXPECT_FALSE(scan_.RecomputeDirty(GetNode("d"), NULL, &err));
487  ASSERT_EQ("dependency cycle: b -> c -> b", err);
488
489  // Despite the depfile causing edge to be a cycle (|edge| has outputs a and b,
490  // but c's in_edge has b as input but the depfile also adds |edge| as
491  // output)), the deps should have been loaded only once:
492  Edge* edge = GetNode("a")->in_edge();
493  EXPECT_EQ(1, edge->inputs_.size());
494  EXPECT_EQ("c", edge->inputs_[0]->path());
495}
496
497#ifdef _WIN32
498TEST_F(GraphTest, Decanonicalize) {
499  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
500"build out\\out1: cat src\\in1\n"
501"build out\\out2/out3\\out4: cat mid1\n"
502"build out3 out4\\foo: cat mid1\n"));
503
504  string err;
505  vector<Node*> root_nodes = state_.RootNodes(&err);
506  EXPECT_EQ(4u, root_nodes.size());
507  EXPECT_EQ(root_nodes[0]->path(), "out/out1");
508  EXPECT_EQ(root_nodes[1]->path(), "out/out2/out3/out4");
509  EXPECT_EQ(root_nodes[2]->path(), "out3");
510  EXPECT_EQ(root_nodes[3]->path(), "out4/foo");
511  EXPECT_EQ(root_nodes[0]->PathDecanonicalized(), "out\\out1");
512  EXPECT_EQ(root_nodes[1]->PathDecanonicalized(), "out\\out2/out3\\out4");
513  EXPECT_EQ(root_nodes[2]->PathDecanonicalized(), "out3");
514  EXPECT_EQ(root_nodes[3]->PathDecanonicalized(), "out4\\foo");
515}
516#endif
517
518TEST_F(GraphTest, DyndepLoadTrivial) {
519  AssertParse(&state_,
520"rule r\n"
521"  command = unused\n"
522"build out: r in || dd\n"
523"  dyndep = dd\n"
524  );
525  fs_.Create("dd",
526"ninja_dyndep_version = 1\n"
527"build out: dyndep\n"
528  );
529
530  string err;
531  ASSERT_TRUE(GetNode("dd")->dyndep_pending());
532  EXPECT_TRUE(scan_.LoadDyndeps(GetNode("dd"), &err));
533  EXPECT_EQ("", err);
534  EXPECT_FALSE(GetNode("dd")->dyndep_pending());
535
536  Edge* edge = GetNode("out")->in_edge();
537  ASSERT_EQ(1u, edge->outputs_.size());
538  EXPECT_EQ("out", edge->outputs_[0]->path());
539  ASSERT_EQ(2u, edge->inputs_.size());
540  EXPECT_EQ("in", edge->inputs_[0]->path());
541  EXPECT_EQ("dd", edge->inputs_[1]->path());
542  EXPECT_EQ(0u, edge->implicit_deps_);
543  EXPECT_EQ(1u, edge->order_only_deps_);
544  EXPECT_FALSE(edge->GetBindingBool("restat"));
545}
546
547TEST_F(GraphTest, DyndepLoadImplicit) {
548  AssertParse(&state_,
549"rule r\n"
550"  command = unused\n"
551"build out1: r in || dd\n"
552"  dyndep = dd\n"
553"build out2: r in\n"
554  );
555  fs_.Create("dd",
556"ninja_dyndep_version = 1\n"
557"build out1: dyndep | out2\n"
558  );
559
560  string err;
561  ASSERT_TRUE(GetNode("dd")->dyndep_pending());
562  EXPECT_TRUE(scan_.LoadDyndeps(GetNode("dd"), &err));
563  EXPECT_EQ("", err);
564  EXPECT_FALSE(GetNode("dd")->dyndep_pending());
565
566  Edge* edge = GetNode("out1")->in_edge();
567  ASSERT_EQ(1u, edge->outputs_.size());
568  EXPECT_EQ("out1", edge->outputs_[0]->path());
569  ASSERT_EQ(3u, edge->inputs_.size());
570  EXPECT_EQ("in", edge->inputs_[0]->path());
571  EXPECT_EQ("out2", edge->inputs_[1]->path());
572  EXPECT_EQ("dd", edge->inputs_[2]->path());
573  EXPECT_EQ(1u, edge->implicit_deps_);
574  EXPECT_EQ(1u, edge->order_only_deps_);
575  EXPECT_FALSE(edge->GetBindingBool("restat"));
576}
577
578TEST_F(GraphTest, DyndepLoadMissingFile) {
579  AssertParse(&state_,
580"rule r\n"
581"  command = unused\n"
582"build out: r in || dd\n"
583"  dyndep = dd\n"
584  );
585
586  string err;
587  ASSERT_TRUE(GetNode("dd")->dyndep_pending());
588  EXPECT_FALSE(scan_.LoadDyndeps(GetNode("dd"), &err));
589  EXPECT_EQ("loading 'dd': No such file or directory", err);
590}
591
592TEST_F(GraphTest, DyndepLoadMissingEntry) {
593  AssertParse(&state_,
594"rule r\n"
595"  command = unused\n"
596"build out: r in || dd\n"
597"  dyndep = dd\n"
598  );
599  fs_.Create("dd",
600"ninja_dyndep_version = 1\n"
601  );
602
603  string err;
604  ASSERT_TRUE(GetNode("dd")->dyndep_pending());
605  EXPECT_FALSE(scan_.LoadDyndeps(GetNode("dd"), &err));
606  EXPECT_EQ("'out' not mentioned in its dyndep file 'dd'", err);
607}
608
609TEST_F(GraphTest, DyndepLoadExtraEntry) {
610  AssertParse(&state_,
611"rule r\n"
612"  command = unused\n"
613"build out: r in || dd\n"
614"  dyndep = dd\n"
615"build out2: r in || dd\n"
616  );
617  fs_.Create("dd",
618"ninja_dyndep_version = 1\n"
619"build out: dyndep\n"
620"build out2: dyndep\n"
621  );
622
623  string err;
624  ASSERT_TRUE(GetNode("dd")->dyndep_pending());
625  EXPECT_FALSE(scan_.LoadDyndeps(GetNode("dd"), &err));
626  EXPECT_EQ("dyndep file 'dd' mentions output 'out2' whose build statement "
627            "does not have a dyndep binding for the file", err);
628}
629
630TEST_F(GraphTest, DyndepLoadOutputWithMultipleRules1) {
631  AssertParse(&state_,
632"rule r\n"
633"  command = unused\n"
634"build out1 | out-twice.imp: r in1\n"
635"build out2: r in2 || dd\n"
636"  dyndep = dd\n"
637  );
638  fs_.Create("dd",
639"ninja_dyndep_version = 1\n"
640"build out2 | out-twice.imp: dyndep\n"
641  );
642
643  string err;
644  ASSERT_TRUE(GetNode("dd")->dyndep_pending());
645  EXPECT_FALSE(scan_.LoadDyndeps(GetNode("dd"), &err));
646  EXPECT_EQ("multiple rules generate out-twice.imp", err);
647}
648
649TEST_F(GraphTest, DyndepLoadOutputWithMultipleRules2) {
650  AssertParse(&state_,
651"rule r\n"
652"  command = unused\n"
653"build out1: r in1 || dd1\n"
654"  dyndep = dd1\n"
655"build out2: r in2 || dd2\n"
656"  dyndep = dd2\n"
657  );
658  fs_.Create("dd1",
659"ninja_dyndep_version = 1\n"
660"build out1 | out-twice.imp: dyndep\n"
661  );
662  fs_.Create("dd2",
663"ninja_dyndep_version = 1\n"
664"build out2 | out-twice.imp: dyndep\n"
665  );
666
667  string err;
668  ASSERT_TRUE(GetNode("dd1")->dyndep_pending());
669  EXPECT_TRUE(scan_.LoadDyndeps(GetNode("dd1"), &err));
670  EXPECT_EQ("", err);
671  ASSERT_TRUE(GetNode("dd2")->dyndep_pending());
672  EXPECT_FALSE(scan_.LoadDyndeps(GetNode("dd2"), &err));
673  EXPECT_EQ("multiple rules generate out-twice.imp", err);
674}
675
676TEST_F(GraphTest, DyndepLoadMultiple) {
677  AssertParse(&state_,
678"rule r\n"
679"  command = unused\n"
680"build out1: r in1 || dd\n"
681"  dyndep = dd\n"
682"build out2: r in2 || dd\n"
683"  dyndep = dd\n"
684"build outNot: r in3 || dd\n"
685  );
686  fs_.Create("dd",
687"ninja_dyndep_version = 1\n"
688"build out1 | out1imp: dyndep | in1imp\n"
689"build out2: dyndep | in2imp\n"
690"  restat = 1\n"
691  );
692
693  string err;
694  ASSERT_TRUE(GetNode("dd")->dyndep_pending());
695  EXPECT_TRUE(scan_.LoadDyndeps(GetNode("dd"), &err));
696  EXPECT_EQ("", err);
697  EXPECT_FALSE(GetNode("dd")->dyndep_pending());
698
699  Edge* edge1 = GetNode("out1")->in_edge();
700  ASSERT_EQ(2u, edge1->outputs_.size());
701  EXPECT_EQ("out1", edge1->outputs_[0]->path());
702  EXPECT_EQ("out1imp", edge1->outputs_[1]->path());
703  EXPECT_EQ(1u, edge1->implicit_outs_);
704  ASSERT_EQ(3u, edge1->inputs_.size());
705  EXPECT_EQ("in1", edge1->inputs_[0]->path());
706  EXPECT_EQ("in1imp", edge1->inputs_[1]->path());
707  EXPECT_EQ("dd", edge1->inputs_[2]->path());
708  EXPECT_EQ(1u, edge1->implicit_deps_);
709  EXPECT_EQ(1u, edge1->order_only_deps_);
710  EXPECT_FALSE(edge1->GetBindingBool("restat"));
711  EXPECT_EQ(edge1, GetNode("out1imp")->in_edge());
712  Node* in1imp = GetNode("in1imp");
713  ASSERT_EQ(1u, in1imp->out_edges().size());
714  EXPECT_EQ(edge1, in1imp->out_edges()[0]);
715
716  Edge* edge2 = GetNode("out2")->in_edge();
717  ASSERT_EQ(1u, edge2->outputs_.size());
718  EXPECT_EQ("out2", edge2->outputs_[0]->path());
719  EXPECT_EQ(0u, edge2->implicit_outs_);
720  ASSERT_EQ(3u, edge2->inputs_.size());
721  EXPECT_EQ("in2", edge2->inputs_[0]->path());
722  EXPECT_EQ("in2imp", edge2->inputs_[1]->path());
723  EXPECT_EQ("dd", edge2->inputs_[2]->path());
724  EXPECT_EQ(1u, edge2->implicit_deps_);
725  EXPECT_EQ(1u, edge2->order_only_deps_);
726  EXPECT_TRUE(edge2->GetBindingBool("restat"));
727  Node* in2imp = GetNode("in2imp");
728  ASSERT_EQ(1u, in2imp->out_edges().size());
729  EXPECT_EQ(edge2, in2imp->out_edges()[0]);
730}
731
732TEST_F(GraphTest, DyndepFileMissing) {
733  AssertParse(&state_,
734"rule r\n"
735"  command = unused\n"
736"build out: r || dd\n"
737"  dyndep = dd\n"
738  );
739
740  string err;
741  EXPECT_FALSE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
742  ASSERT_EQ("loading 'dd': No such file or directory", err);
743}
744
745TEST_F(GraphTest, DyndepFileError) {
746  AssertParse(&state_,
747"rule r\n"
748"  command = unused\n"
749"build out: r || dd\n"
750"  dyndep = dd\n"
751  );
752  fs_.Create("dd",
753"ninja_dyndep_version = 1\n"
754  );
755
756  string err;
757  EXPECT_FALSE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
758  ASSERT_EQ("'out' not mentioned in its dyndep file 'dd'", err);
759}
760
761TEST_F(GraphTest, DyndepImplicitInputNewer) {
762  AssertParse(&state_,
763"rule r\n"
764"  command = unused\n"
765"build out: r || dd\n"
766"  dyndep = dd\n"
767  );
768  fs_.Create("dd",
769"ninja_dyndep_version = 1\n"
770"build out: dyndep | in\n"
771  );
772  fs_.Create("out", "");
773  fs_.Tick();
774  fs_.Create("in", "");
775
776  string err;
777  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
778  ASSERT_EQ("", err);
779
780  EXPECT_FALSE(GetNode("in")->dirty());
781  EXPECT_FALSE(GetNode("dd")->dirty());
782
783  // "out" is dirty due to dyndep-specified implicit input
784  EXPECT_TRUE(GetNode("out")->dirty());
785}
786
787TEST_F(GraphTest, DyndepFileReady) {
788  AssertParse(&state_,
789"rule r\n"
790"  command = unused\n"
791"build dd: r dd-in\n"
792"build out: r || dd\n"
793"  dyndep = dd\n"
794  );
795  fs_.Create("dd-in", "");
796  fs_.Create("dd",
797"ninja_dyndep_version = 1\n"
798"build out: dyndep | in\n"
799  );
800  fs_.Create("out", "");
801  fs_.Tick();
802  fs_.Create("in", "");
803
804  string err;
805  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
806  ASSERT_EQ("", err);
807
808  EXPECT_FALSE(GetNode("in")->dirty());
809  EXPECT_FALSE(GetNode("dd")->dirty());
810  EXPECT_TRUE(GetNode("dd")->in_edge()->outputs_ready());
811
812  // "out" is dirty due to dyndep-specified implicit input
813  EXPECT_TRUE(GetNode("out")->dirty());
814}
815
816TEST_F(GraphTest, DyndepFileNotClean) {
817  AssertParse(&state_,
818"rule r\n"
819"  command = unused\n"
820"build dd: r dd-in\n"
821"build out: r || dd\n"
822"  dyndep = dd\n"
823  );
824  fs_.Create("dd", "this-should-not-be-loaded");
825  fs_.Tick();
826  fs_.Create("dd-in", "");
827  fs_.Create("out", "");
828
829  string err;
830  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
831  ASSERT_EQ("", err);
832
833  EXPECT_TRUE(GetNode("dd")->dirty());
834  EXPECT_FALSE(GetNode("dd")->in_edge()->outputs_ready());
835
836  // "out" is clean but not ready since "dd" is not ready
837  EXPECT_FALSE(GetNode("out")->dirty());
838  EXPECT_FALSE(GetNode("out")->in_edge()->outputs_ready());
839}
840
841TEST_F(GraphTest, DyndepFileNotReady) {
842  AssertParse(&state_,
843"rule r\n"
844"  command = unused\n"
845"build tmp: r\n"
846"build dd: r dd-in || tmp\n"
847"build out: r || dd\n"
848"  dyndep = dd\n"
849  );
850  fs_.Create("dd", "this-should-not-be-loaded");
851  fs_.Create("dd-in", "");
852  fs_.Tick();
853  fs_.Create("out", "");
854
855  string err;
856  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
857  ASSERT_EQ("", err);
858
859  EXPECT_FALSE(GetNode("dd")->dirty());
860  EXPECT_FALSE(GetNode("dd")->in_edge()->outputs_ready());
861  EXPECT_FALSE(GetNode("out")->dirty());
862  EXPECT_FALSE(GetNode("out")->in_edge()->outputs_ready());
863}
864
865TEST_F(GraphTest, DyndepFileSecondNotReady) {
866  AssertParse(&state_,
867"rule r\n"
868"  command = unused\n"
869"build dd1: r dd1-in\n"
870"build dd2-in: r || dd1\n"
871"  dyndep = dd1\n"
872"build dd2: r dd2-in\n"
873"build out: r || dd2\n"
874"  dyndep = dd2\n"
875  );
876  fs_.Create("dd1", "");
877  fs_.Create("dd2", "");
878  fs_.Create("dd2-in", "");
879  fs_.Tick();
880  fs_.Create("dd1-in", "");
881  fs_.Create("out", "");
882
883  string err;
884  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
885  ASSERT_EQ("", err);
886
887  EXPECT_TRUE(GetNode("dd1")->dirty());
888  EXPECT_FALSE(GetNode("dd1")->in_edge()->outputs_ready());
889  EXPECT_FALSE(GetNode("dd2")->dirty());
890  EXPECT_FALSE(GetNode("dd2")->in_edge()->outputs_ready());
891  EXPECT_FALSE(GetNode("out")->dirty());
892  EXPECT_FALSE(GetNode("out")->in_edge()->outputs_ready());
893}
894
895TEST_F(GraphTest, DyndepFileCircular) {
896  AssertParse(&state_,
897"rule r\n"
898"  command = unused\n"
899"build out: r in || dd\n"
900"  depfile = out.d\n"
901"  dyndep = dd\n"
902"build in: r circ\n"
903  );
904  fs_.Create("out.d", "out: inimp\n");
905  fs_.Create("dd",
906"ninja_dyndep_version = 1\n"
907"build out | circ: dyndep\n"
908  );
909  fs_.Create("out", "");
910
911  Edge* edge = GetNode("out")->in_edge();
912  string err;
913  EXPECT_FALSE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
914  EXPECT_EQ("dependency cycle: circ -> in -> circ", err);
915
916  // Verify that "out.d" was loaded exactly once despite
917  // circular reference discovered from dyndep file.
918  ASSERT_EQ(3u, edge->inputs_.size());
919  EXPECT_EQ("in", edge->inputs_[0]->path());
920  EXPECT_EQ("inimp", edge->inputs_[1]->path());
921  EXPECT_EQ("dd", edge->inputs_[2]->path());
922  EXPECT_EQ(1u, edge->implicit_deps_);
923  EXPECT_EQ(1u, edge->order_only_deps_);
924}
925
926TEST_F(GraphTest, Validation) {
927  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
928"build out: cat in |@ validate\n"
929"build validate: cat in\n"));
930
931  fs_.Create("in", "");
932  string err;
933  std::vector<Node*> validation_nodes;
934  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), &validation_nodes, &err));
935  ASSERT_EQ("", err);
936
937  ASSERT_EQ(validation_nodes.size(), 1);
938  EXPECT_EQ(validation_nodes[0]->path(), "validate");
939
940  EXPECT_TRUE(GetNode("out")->dirty());
941  EXPECT_TRUE(GetNode("validate")->dirty());
942}
943
944// Check that phony's dependencies' mtimes are propagated.
945TEST_F(GraphTest, PhonyDepsMtimes) {
946  string err;
947  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
948"rule touch\n"
949" command = touch $out\n"
950"build in_ph: phony in1\n"
951"build out1: touch in_ph\n"
952));
953  fs_.Create("in1", "");
954  fs_.Create("out1", "");
955  Node* out1 = GetNode("out1");
956  Node* in1  = GetNode("in1");
957
958  EXPECT_TRUE(scan_.RecomputeDirty(out1, NULL, &err));
959  EXPECT_TRUE(!out1->dirty());
960
961  // Get the mtime of out1
962  ASSERT_TRUE(in1->Stat(&fs_, &err));
963  ASSERT_TRUE(out1->Stat(&fs_, &err));
964  TimeStamp out1Mtime1 = out1->mtime();
965  TimeStamp in1Mtime1 = in1->mtime();
966
967  // Touch in1. This should cause out1 to be dirty
968  state_.Reset();
969  fs_.Tick();
970  fs_.Create("in1", "");
971
972  ASSERT_TRUE(in1->Stat(&fs_, &err));
973  EXPECT_GT(in1->mtime(), in1Mtime1);
974
975  EXPECT_TRUE(scan_.RecomputeDirty(out1, NULL, &err));
976  EXPECT_GT(in1->mtime(), in1Mtime1);
977  EXPECT_EQ(out1->mtime(), out1Mtime1);
978  EXPECT_TRUE(out1->dirty());
979}
980
981