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