Ninja
graph_test.cc
Go to the documentation of this file.
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 
17 #include "build.h"
18 #include "command_collector.h"
19 #include "test.h"
20 
21 using namespace std;
22 
24  GraphTest() : scan_(&state_, NULL, NULL, &fs_, NULL, NULL) {}
25 
28 };
29 
30 TEST_F(GraphTest, MissingImplicit) {
31  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
32 "build out: cat in | implicit\n"));
33  fs_.Create("in", "");
34  fs_.Create("out", "");
35 
36  string err;
37  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
38  ASSERT_EQ("", err);
39 
40  // A missing implicit dep *should* make the output dirty.
41  // (In fact, a build will fail.)
42  // This is a change from prior semantics of ninja.
43  EXPECT_TRUE(GetNode("out")->dirty());
44 }
45 
46 TEST_F(GraphTest, ModifiedImplicit) {
47  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
48 "build out: cat in | implicit\n"));
49  fs_.Create("in", "");
50  fs_.Create("out", "");
51  fs_.Tick();
52  fs_.Create("implicit", "");
53 
54  string err;
55  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
56  ASSERT_EQ("", err);
57 
58  // A modified implicit dep should make the output dirty.
59  EXPECT_TRUE(GetNode("out")->dirty());
60 }
61 
62 TEST_F(GraphTest, FunkyMakefilePath) {
63  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
64 "rule catdep\n"
65 " depfile = $out.d\n"
66 " command = cat $in > $out\n"
67 "build out.o: catdep foo.cc\n"));
68  fs_.Create("foo.cc", "");
69  fs_.Create("out.o.d", "out.o: ./foo/../implicit.h\n");
70  fs_.Create("out.o", "");
71  fs_.Tick();
72  fs_.Create("implicit.h", "");
73 
74  string err;
75  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), NULL, &err));
76  ASSERT_EQ("", err);
77 
78  // implicit.h has changed, though our depfile refers to it with a
79  // non-canonical path; we should still find it.
80  EXPECT_TRUE(GetNode("out.o")->dirty());
81 }
82 
83 TEST_F(GraphTest, ExplicitImplicit) {
84  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
85 "rule catdep\n"
86 " depfile = $out.d\n"
87 " command = cat $in > $out\n"
88 "build implicit.h: cat data\n"
89 "build out.o: catdep foo.cc || implicit.h\n"));
90  fs_.Create("implicit.h", "");
91  fs_.Create("foo.cc", "");
92  fs_.Create("out.o.d", "out.o: implicit.h\n");
93  fs_.Create("out.o", "");
94  fs_.Tick();
95  fs_.Create("data", "");
96 
97  string err;
98  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), NULL, &err));
99  ASSERT_EQ("", err);
100 
101  // We have both an implicit and an explicit dep on implicit.h.
102  // The implicit dep should "win" (in the sense that it should cause
103  // the output to be dirty).
104  EXPECT_TRUE(GetNode("out.o")->dirty());
105 }
106 
107 TEST_F(GraphTest, ImplicitOutputParse) {
108  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
109 "build out | out.imp: cat in\n"));
110 
111  Edge* edge = GetNode("out")->in_edge();
112  EXPECT_EQ(size_t(2), edge->outputs_.size());
113  EXPECT_EQ("out", edge->outputs_[0]->path());
114  EXPECT_EQ("out.imp", edge->outputs_[1]->path());
115  EXPECT_EQ(1, edge->implicit_outs_);
116  EXPECT_EQ(edge, GetNode("out.imp")->in_edge());
117 }
118 
119 TEST_F(GraphTest, ImplicitOutputMissing) {
120  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
121 "build out | out.imp: cat in\n"));
122  fs_.Create("in", "");
123  fs_.Create("out", "");
124 
125  string err;
126  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
127  ASSERT_EQ("", err);
128 
129  EXPECT_TRUE(GetNode("out")->dirty());
130  EXPECT_TRUE(GetNode("out.imp")->dirty());
131 }
132 
133 TEST_F(GraphTest, ImplicitOutputOutOfDate) {
134  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
135 "build out | out.imp: cat in\n"));
136  fs_.Create("out.imp", "");
137  fs_.Tick();
138  fs_.Create("in", "");
139  fs_.Create("out", "");
140 
141  string err;
142  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
143  ASSERT_EQ("", err);
144 
145  EXPECT_TRUE(GetNode("out")->dirty());
146  EXPECT_TRUE(GetNode("out.imp")->dirty());
147 }
148 
149 TEST_F(GraphTest, ImplicitOutputOnlyParse) {
150  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
151 "build | out.imp: cat in\n"));
152 
153  Edge* edge = GetNode("out.imp")->in_edge();
154  EXPECT_EQ(size_t(1), edge->outputs_.size());
155  EXPECT_EQ("out.imp", edge->outputs_[0]->path());
156  EXPECT_EQ(1, edge->implicit_outs_);
157  EXPECT_EQ(edge, GetNode("out.imp")->in_edge());
158 }
159 
160 TEST_F(GraphTest, ImplicitOutputOnlyMissing) {
161  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
162 "build | out.imp: cat in\n"));
163  fs_.Create("in", "");
164 
165  string err;
166  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.imp"), NULL, &err));
167  ASSERT_EQ("", err);
168 
169  EXPECT_TRUE(GetNode("out.imp")->dirty());
170 }
171 
172 TEST_F(GraphTest, ImplicitOutputOnlyOutOfDate) {
173  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
174 "build | out.imp: cat in\n"));
175  fs_.Create("out.imp", "");
176  fs_.Tick();
177  fs_.Create("in", "");
178 
179  string err;
180  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.imp"), NULL, &err));
181  ASSERT_EQ("", err);
182 
183  EXPECT_TRUE(GetNode("out.imp")->dirty());
184 }
185 
186 TEST_F(GraphTest, PathWithCurrentDirectory) {
187  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
188 "rule catdep\n"
189 " depfile = $out.d\n"
190 " command = cat $in > $out\n"
191 "build ./out.o: catdep ./foo.cc\n"));
192  fs_.Create("foo.cc", "");
193  fs_.Create("out.o.d", "out.o: foo.cc\n");
194  fs_.Create("out.o", "");
195 
196  string err;
197  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), NULL, &err));
198  ASSERT_EQ("", err);
199 
200  EXPECT_FALSE(GetNode("out.o")->dirty());
201 }
202 
203 TEST_F(GraphTest, RootNodes) {
204  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
205 "build out1: cat in1\n"
206 "build mid1: cat in1\n"
207 "build out2: cat mid1\n"
208 "build out3 out4: cat mid1\n"));
209 
210  string err;
211  vector<Node*> root_nodes = state_.RootNodes(&err);
212  EXPECT_EQ(4u, root_nodes.size());
213  for (size_t i = 0; i < root_nodes.size(); ++i) {
214  string name = root_nodes[i]->path();
215  EXPECT_EQ("out", name.substr(0, 3));
216  }
217 }
218 
220  // Build plan for the following graph:
221  //
222  // in1
223  // |___________
224  // | |
225  // === ===
226  // | |
227  // out1 mid1
228  // | ____|_____
229  // | | |
230  // | === =======
231  // | | | |
232  // | out2 out3 out4
233  // | | |
234  // =======phony======
235  // |
236  // all
237  //
238  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
239  "build out1: cat in1\n"
240  "build mid1: cat in1\n"
241  "build out2: cat mid1\n"
242  "build out3 out4: cat mid1\n"
243  "build all: phony out1 out2 out3\n"));
244 
245  InputsCollector collector;
246 
247  // Start visit from out1, this should add in1 to the inputs.
248  collector.Reset();
249  collector.VisitNode(GetNode("out1"));
250  auto inputs = collector.GetInputsAsStrings();
251  ASSERT_EQ(1u, inputs.size());
252  EXPECT_EQ("in1", inputs[0]);
253 
254  // Add a visit from out2, this should add mid1.
255  collector.VisitNode(GetNode("out2"));
256  inputs = collector.GetInputsAsStrings();
257  ASSERT_EQ(2u, inputs.size());
258  EXPECT_EQ("in1", inputs[0]);
259  EXPECT_EQ("mid1", inputs[1]);
260 
261  // Another visit from all, this should add out1, out2 and out3,
262  // but not out4.
263  collector.VisitNode(GetNode("all"));
264  inputs = collector.GetInputsAsStrings();
265  ASSERT_EQ(5u, inputs.size());
266  EXPECT_EQ("in1", inputs[0]);
267  EXPECT_EQ("mid1", inputs[1]);
268  EXPECT_EQ("out1", inputs[2]);
269  EXPECT_EQ("out2", inputs[3]);
270  EXPECT_EQ("out3", inputs[4]);
271 
272  collector.Reset();
273 
274  // Starting directly from all, will add out1 before mid1 compared
275  // to the previous example above.
276  collector.VisitNode(GetNode("all"));
277  inputs = collector.GetInputsAsStrings();
278  ASSERT_EQ(5u, inputs.size());
279  EXPECT_EQ("in1", inputs[0]);
280  EXPECT_EQ("out1", inputs[1]);
281  EXPECT_EQ("mid1", inputs[2]);
282  EXPECT_EQ("out2", inputs[3]);
283  EXPECT_EQ("out3", inputs[4]);
284 }
285 
286 TEST_F(GraphTest, InputsCollectorWithEscapes) {
287  ASSERT_NO_FATAL_FAILURE(AssertParse(
288  &state_,
289  "build out$ 1: cat in1 in2 in$ with$ space | implicit || order_only\n"));
290 
291  InputsCollector collector;
292  collector.VisitNode(GetNode("out 1"));
293  auto inputs = collector.GetInputsAsStrings();
294  ASSERT_EQ(5u, inputs.size());
295  EXPECT_EQ("in1", inputs[0]);
296  EXPECT_EQ("in2", inputs[1]);
297  EXPECT_EQ("in with space", inputs[2]);
298  EXPECT_EQ("implicit", inputs[3]);
299  EXPECT_EQ("order_only", inputs[4]);
300 
301  inputs = collector.GetInputsAsStrings(true);
302  ASSERT_EQ(5u, inputs.size());
303  EXPECT_EQ("in1", inputs[0]);
304  EXPECT_EQ("in2", inputs[1]);
305 #ifdef _WIN32
306  EXPECT_EQ("\"in with space\"", inputs[2]);
307 #else
308  EXPECT_EQ("'in with space'", inputs[2]);
309 #endif
310  EXPECT_EQ("implicit", inputs[3]);
311  EXPECT_EQ("order_only", inputs[4]);
312 }
313 
315  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
316  "build out1: cat in1\n"
317  "build mid1: cat in1\n"
318  "build out2: cat mid1\n"
319  "build out3 out4: cat mid1\n"
320  "build all: phony out1 out2 out3\n"));
321  {
322  CommandCollector collector;
323  auto& edges = collector.in_edges;
324 
325  // Start visit from out2; this should add `build mid1` and `build out2` to
326  // the edge list.
327  collector.CollectFrom(GetNode("out2"));
328  ASSERT_EQ(2u, edges.size());
329  EXPECT_EQ("cat in1 > mid1", edges[0]->EvaluateCommand());
330  EXPECT_EQ("cat mid1 > out2", edges[1]->EvaluateCommand());
331 
332  // Add a visit from out1, this should append `build out1`
333  collector.CollectFrom(GetNode("out1"));
334  ASSERT_EQ(3u, edges.size());
335  EXPECT_EQ("cat in1 > out1", edges[2]->EvaluateCommand());
336 
337  // Another visit from all; this should add edges for out1, out2 and out3,
338  // but not all (because it's phony).
339  collector.CollectFrom(GetNode("all"));
340  ASSERT_EQ(4u, edges.size());
341  EXPECT_EQ("cat in1 > mid1", edges[0]->EvaluateCommand());
342  EXPECT_EQ("cat mid1 > out2", edges[1]->EvaluateCommand());
343  EXPECT_EQ("cat in1 > out1", edges[2]->EvaluateCommand());
344  EXPECT_EQ("cat mid1 > out3 out4", edges[3]->EvaluateCommand());
345  }
346 
347  {
348  CommandCollector collector;
349  auto& edges = collector.in_edges;
350 
351  // Starting directly from all, will add `build out1` before `build mid1`
352  // compared to the previous example above.
353  collector.CollectFrom(GetNode("all"));
354  ASSERT_EQ(4u, edges.size());
355  EXPECT_EQ("cat in1 > out1", edges[0]->EvaluateCommand());
356  EXPECT_EQ("cat in1 > mid1", edges[1]->EvaluateCommand());
357  EXPECT_EQ("cat mid1 > out2", edges[2]->EvaluateCommand());
358  EXPECT_EQ("cat mid1 > out3 out4", edges[3]->EvaluateCommand());
359  }
360 }
361 
362 TEST_F(GraphTest, VarInOutPathEscaping) {
363  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
364 "build a$ b: cat no'space with$ space$$ no\"space2\n"));
365 
366  Edge* edge = GetNode("a b")->in_edge();
367 #ifdef _WIN32
368  EXPECT_EQ("cat no'space \"with space$\" \"no\\\"space2\" > \"a b\"",
369  edge->EvaluateCommand());
370 #else
371  EXPECT_EQ("cat 'no'\\''space' 'with space$' 'no\"space2' > 'a b'",
372  edge->EvaluateCommand());
373 #endif
374 }
375 
376 // Regression test for https://github.com/ninja-build/ninja/issues/380
377 TEST_F(GraphTest, DepfileWithCanonicalizablePath) {
378  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
379 "rule catdep\n"
380 " depfile = $out.d\n"
381 " command = cat $in > $out\n"
382 "build ./out.o: catdep ./foo.cc\n"));
383  fs_.Create("foo.cc", "");
384  fs_.Create("out.o.d", "out.o: bar/../foo.cc\n");
385  fs_.Create("out.o", "");
386 
387  string err;
388  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), NULL, &err));
389  ASSERT_EQ("", err);
390 
391  EXPECT_FALSE(GetNode("out.o")->dirty());
392 }
393 
394 // Regression test for https://github.com/ninja-build/ninja/issues/404
395 TEST_F(GraphTest, DepfileRemoved) {
396  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
397 "rule catdep\n"
398 " depfile = $out.d\n"
399 " command = cat $in > $out\n"
400 "build ./out.o: catdep ./foo.cc\n"));
401  fs_.Create("foo.h", "");
402  fs_.Create("foo.cc", "");
403  fs_.Tick();
404  fs_.Create("out.o.d", "out.o: foo.h\n");
405  fs_.Create("out.o", "");
406 
407  string err;
408  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), NULL, &err));
409  ASSERT_EQ("", err);
410  EXPECT_FALSE(GetNode("out.o")->dirty());
411 
412  state_.Reset();
413  fs_.RemoveFile("out.o.d");
414  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), NULL, &err));
415  ASSERT_EQ("", err);
416  EXPECT_TRUE(GetNode("out.o")->dirty());
417 }
418 
419 // Check that rule-level variables are in scope for eval.
420 TEST_F(GraphTest, RuleVariablesInScope) {
421  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
422 "rule r\n"
423 " depfile = x\n"
424 " command = depfile is $depfile\n"
425 "build out: r in\n"));
426  Edge* edge = GetNode("out")->in_edge();
427  EXPECT_EQ("depfile is x", edge->EvaluateCommand());
428 }
429 
430 // Check that build statements can override rule builtins like depfile.
431 TEST_F(GraphTest, DepfileOverride) {
432  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
433 "rule r\n"
434 " depfile = x\n"
435 " command = unused\n"
436 "build out: r in\n"
437 " depfile = y\n"));
438  Edge* edge = GetNode("out")->in_edge();
439  EXPECT_EQ("y", edge->GetBinding("depfile"));
440 }
441 
442 // Check that overridden values show up in expansion of rule-level bindings.
443 TEST_F(GraphTest, DepfileOverrideParent) {
444  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
445 "rule r\n"
446 " depfile = x\n"
447 " command = depfile is $depfile\n"
448 "build out: r in\n"
449 " depfile = y\n"));
450  Edge* edge = GetNode("out")->in_edge();
451  EXPECT_EQ("depfile is y", edge->GetBinding("command"));
452 }
453 
454 // Verify that building a nested phony rule prints "no work to do"
455 TEST_F(GraphTest, NestedPhonyPrintsDone) {
456  AssertParse(&state_,
457 "build n1: phony \n"
458 "build n2: phony n1\n"
459  );
460  string err;
461  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("n2"), NULL, &err));
462  ASSERT_EQ("", err);
463 
464  Plan plan_;
465  EXPECT_TRUE(plan_.AddTarget(GetNode("n2"), &err));
466  ASSERT_EQ("", err);
467 
468  EXPECT_EQ(0, plan_.command_edge_count());
469  ASSERT_FALSE(plan_.more_to_do());
470 }
471 
472 TEST_F(GraphTest, PhonySelfReferenceError) {
473  ManifestParserOptions parser_opts;
475  AssertParse(&state_,
476 "build a: phony a\n",
477  parser_opts);
478 
479  string err;
480  EXPECT_FALSE(scan_.RecomputeDirty(GetNode("a"), NULL, &err));
481  ASSERT_EQ("dependency cycle: a -> a [-w phonycycle=err]", err);
482 }
483 
484 TEST_F(GraphTest, DependencyCycle) {
485  AssertParse(&state_,
486 "build out: cat mid\n"
487 "build mid: cat in\n"
488 "build in: cat pre\n"
489 "build pre: cat out\n");
490 
491  string err;
492  EXPECT_FALSE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
493  ASSERT_EQ("dependency cycle: out -> mid -> in -> pre -> out", err);
494 }
495 
496 TEST_F(GraphTest, CycleInEdgesButNotInNodes1) {
497  string err;
498  AssertParse(&state_,
499 "build a b: cat a\n");
500  EXPECT_FALSE(scan_.RecomputeDirty(GetNode("b"), NULL, &err));
501  ASSERT_EQ("dependency cycle: a -> a", err);
502 }
503 
504 TEST_F(GraphTest, CycleInEdgesButNotInNodes2) {
505  string err;
506  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
507 "build b a: cat a\n"));
508  EXPECT_FALSE(scan_.RecomputeDirty(GetNode("b"), NULL, &err));
509  ASSERT_EQ("dependency cycle: a -> a", err);
510 }
511 
512 TEST_F(GraphTest, CycleInEdgesButNotInNodes3) {
513  string err;
514  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
515 "build a b: cat c\n"
516 "build c: cat a\n"));
517  EXPECT_FALSE(scan_.RecomputeDirty(GetNode("b"), NULL, &err));
518  ASSERT_EQ("dependency cycle: a -> c -> a", err);
519 }
520 
521 TEST_F(GraphTest, CycleInEdgesButNotInNodes4) {
522  string err;
523  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
524 "build d: cat c\n"
525 "build c: cat b\n"
526 "build b: cat a\n"
527 "build a e: cat d\n"
528 "build f: cat e\n"));
529  EXPECT_FALSE(scan_.RecomputeDirty(GetNode("f"), NULL, &err));
530  ASSERT_EQ("dependency cycle: a -> d -> c -> b -> a", err);
531 }
532 
533 // Verify that cycles in graphs with multiple outputs are handled correctly
534 // in RecomputeDirty() and don't cause deps to be loaded multiple times.
535 TEST_F(GraphTest, CycleWithLengthZeroFromDepfile) {
536  AssertParse(&state_,
537 "rule deprule\n"
538 " depfile = dep.d\n"
539 " command = unused\n"
540 "build a b: deprule\n"
541  );
542  fs_.Create("dep.d", "a: b\n");
543 
544  string err;
545  EXPECT_FALSE(scan_.RecomputeDirty(GetNode("a"), NULL, &err));
546  ASSERT_EQ("dependency cycle: b -> b", err);
547 
548  // Despite the depfile causing edge to be a cycle (it has outputs a and b,
549  // but the depfile also adds b as an input), the deps should have been loaded
550  // only once:
551  Edge* edge = GetNode("a")->in_edge();
552  EXPECT_EQ(size_t(1), edge->inputs_.size());
553  EXPECT_EQ("b", edge->inputs_[0]->path());
554 }
555 
556 // Like CycleWithLengthZeroFromDepfile but with a higher cycle length.
557 TEST_F(GraphTest, CycleWithLengthOneFromDepfile) {
558  AssertParse(&state_,
559 "rule deprule\n"
560 " depfile = dep.d\n"
561 " command = unused\n"
562 "rule r\n"
563 " command = unused\n"
564 "build a b: deprule\n"
565 "build c: r b\n"
566  );
567  fs_.Create("dep.d", "a: c\n");
568 
569  string err;
570  EXPECT_FALSE(scan_.RecomputeDirty(GetNode("a"), NULL, &err));
571  ASSERT_EQ("dependency cycle: b -> c -> b", err);
572 
573  // Despite the depfile causing edge to be a cycle (|edge| has outputs a and b,
574  // but c's in_edge has b as input but the depfile also adds |edge| as
575  // output)), the deps should have been loaded only once:
576  Edge* edge = GetNode("a")->in_edge();
577  EXPECT_EQ(size_t(1), edge->inputs_.size());
578  EXPECT_EQ("c", edge->inputs_[0]->path());
579 }
580 
581 // Like CycleWithLengthOneFromDepfile but building a node one hop away from
582 // the cycle.
583 TEST_F(GraphTest, CycleWithLengthOneFromDepfileOneHopAway) {
584  AssertParse(&state_,
585 "rule deprule\n"
586 " depfile = dep.d\n"
587 " command = unused\n"
588 "rule r\n"
589 " command = unused\n"
590 "build a b: deprule\n"
591 "build c: r b\n"
592 "build d: r a\n"
593  );
594  fs_.Create("dep.d", "a: c\n");
595 
596  string err;
597  EXPECT_FALSE(scan_.RecomputeDirty(GetNode("d"), NULL, &err));
598  ASSERT_EQ("dependency cycle: b -> c -> b", err);
599 
600  // Despite the depfile causing edge to be a cycle (|edge| has outputs a and b,
601  // but c's in_edge has b as input but the depfile also adds |edge| as
602  // output)), the deps should have been loaded only once:
603  Edge* edge = GetNode("a")->in_edge();
604  EXPECT_EQ(size_t(1), edge->inputs_.size());
605  EXPECT_EQ("c", edge->inputs_[0]->path());
606 }
607 
608 #ifdef _WIN32
609 TEST_F(GraphTest, Decanonicalize) {
610  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
611 "build out\\out1: cat src\\in1\n"
612 "build out\\out2/out3\\out4: cat mid1\n"
613 "build out3 out4\\foo: cat mid1\n"));
614 
615  string err;
616  vector<Node*> root_nodes = state_.RootNodes(&err);
617  EXPECT_EQ(4u, root_nodes.size());
618  EXPECT_EQ(root_nodes[0]->path(), "out/out1");
619  EXPECT_EQ(root_nodes[1]->path(), "out/out2/out3/out4");
620  EXPECT_EQ(root_nodes[2]->path(), "out3");
621  EXPECT_EQ(root_nodes[3]->path(), "out4/foo");
622  EXPECT_EQ(root_nodes[0]->PathDecanonicalized(), "out\\out1");
623  EXPECT_EQ(root_nodes[1]->PathDecanonicalized(), "out\\out2/out3\\out4");
624  EXPECT_EQ(root_nodes[2]->PathDecanonicalized(), "out3");
625  EXPECT_EQ(root_nodes[3]->PathDecanonicalized(), "out4\\foo");
626 }
627 #endif
628 
629 TEST_F(GraphTest, DyndepLoadTrivial) {
630  AssertParse(&state_,
631 "rule r\n"
632 " command = unused\n"
633 "build out: r in || dd\n"
634 " dyndep = dd\n"
635  );
636  fs_.Create("dd",
637 "ninja_dyndep_version = 1\n"
638 "build out: dyndep\n"
639  );
640 
641  string err;
642  ASSERT_TRUE(GetNode("dd")->dyndep_pending());
643  EXPECT_TRUE(scan_.LoadDyndeps(GetNode("dd"), &err));
644  EXPECT_EQ("", err);
645  EXPECT_FALSE(GetNode("dd")->dyndep_pending());
646 
647  Edge* edge = GetNode("out")->in_edge();
648  ASSERT_EQ(size_t(1), edge->outputs_.size());
649  EXPECT_EQ("out", edge->outputs_[0]->path());
650  ASSERT_EQ(size_t(2), edge->inputs_.size());
651  EXPECT_EQ("in", edge->inputs_[0]->path());
652  EXPECT_EQ("dd", edge->inputs_[1]->path());
653  EXPECT_EQ(0, edge->implicit_deps_);
654  EXPECT_EQ(1, edge->order_only_deps_);
655  EXPECT_FALSE(edge->GetBindingBool("restat"));
656 }
657 
658 TEST_F(GraphTest, DyndepLoadImplicit) {
659  AssertParse(&state_,
660 "rule r\n"
661 " command = unused\n"
662 "build out1: r in || dd\n"
663 " dyndep = dd\n"
664 "build out2: r in\n"
665  );
666  fs_.Create("dd",
667 "ninja_dyndep_version = 1\n"
668 "build out1: dyndep | out2\n"
669  );
670 
671  string err;
672  ASSERT_TRUE(GetNode("dd")->dyndep_pending());
673  EXPECT_TRUE(scan_.LoadDyndeps(GetNode("dd"), &err));
674  EXPECT_EQ("", err);
675  EXPECT_FALSE(GetNode("dd")->dyndep_pending());
676 
677  Edge* edge = GetNode("out1")->in_edge();
678  ASSERT_EQ(size_t(1), edge->outputs_.size());
679  EXPECT_EQ("out1", edge->outputs_[0]->path());
680  ASSERT_EQ(size_t(3), edge->inputs_.size());
681  EXPECT_EQ("in", edge->inputs_[0]->path());
682  EXPECT_EQ("out2", edge->inputs_[1]->path());
683  EXPECT_EQ("dd", edge->inputs_[2]->path());
684  EXPECT_EQ(1, edge->implicit_deps_);
685  EXPECT_EQ(1, edge->order_only_deps_);
686  EXPECT_FALSE(edge->GetBindingBool("restat"));
687 }
688 
689 TEST_F(GraphTest, DyndepLoadMissingFile) {
690  AssertParse(&state_,
691 "rule r\n"
692 " command = unused\n"
693 "build out: r in || dd\n"
694 " dyndep = dd\n"
695  );
696 
697  string err;
698  ASSERT_TRUE(GetNode("dd")->dyndep_pending());
699  EXPECT_FALSE(scan_.LoadDyndeps(GetNode("dd"), &err));
700  EXPECT_EQ("loading 'dd': No such file or directory", err);
701 }
702 
703 TEST_F(GraphTest, DyndepLoadMissingEntry) {
704  AssertParse(&state_,
705 "rule r\n"
706 " command = unused\n"
707 "build out: r in || dd\n"
708 " dyndep = dd\n"
709  );
710  fs_.Create("dd",
711 "ninja_dyndep_version = 1\n"
712  );
713 
714  string err;
715  ASSERT_TRUE(GetNode("dd")->dyndep_pending());
716  EXPECT_FALSE(scan_.LoadDyndeps(GetNode("dd"), &err));
717  EXPECT_EQ("'out' not mentioned in its dyndep file 'dd'", err);
718 }
719 
720 TEST_F(GraphTest, DyndepLoadExtraEntry) {
721  AssertParse(&state_,
722 "rule r\n"
723 " command = unused\n"
724 "build out: r in || dd\n"
725 " dyndep = dd\n"
726 "build out2: r in || dd\n"
727  );
728  fs_.Create("dd",
729 "ninja_dyndep_version = 1\n"
730 "build out: dyndep\n"
731 "build out2: dyndep\n"
732  );
733 
734  string err;
735  ASSERT_TRUE(GetNode("dd")->dyndep_pending());
736  EXPECT_FALSE(scan_.LoadDyndeps(GetNode("dd"), &err));
737  EXPECT_EQ("dyndep file 'dd' mentions output 'out2' whose build statement "
738  "does not have a dyndep binding for the file", err);
739 }
740 
741 TEST_F(GraphTest, DyndepLoadOutputWithMultipleRules1) {
742  AssertParse(&state_,
743 "rule r\n"
744 " command = unused\n"
745 "build out1 | out-twice.imp: r in1\n"
746 "build out2: r in2 || dd\n"
747 " dyndep = dd\n"
748  );
749  fs_.Create("dd",
750 "ninja_dyndep_version = 1\n"
751 "build out2 | out-twice.imp: dyndep\n"
752  );
753 
754  string err;
755  ASSERT_TRUE(GetNode("dd")->dyndep_pending());
756  EXPECT_FALSE(scan_.LoadDyndeps(GetNode("dd"), &err));
757  EXPECT_EQ("multiple rules generate out-twice.imp", err);
758 }
759 
760 TEST_F(GraphTest, DyndepLoadOutputWithMultipleRules2) {
761  AssertParse(&state_,
762 "rule r\n"
763 " command = unused\n"
764 "build out1: r in1 || dd1\n"
765 " dyndep = dd1\n"
766 "build out2: r in2 || dd2\n"
767 " dyndep = dd2\n"
768  );
769  fs_.Create("dd1",
770 "ninja_dyndep_version = 1\n"
771 "build out1 | out-twice.imp: dyndep\n"
772  );
773  fs_.Create("dd2",
774 "ninja_dyndep_version = 1\n"
775 "build out2 | out-twice.imp: dyndep\n"
776  );
777 
778  string err;
779  ASSERT_TRUE(GetNode("dd1")->dyndep_pending());
780  EXPECT_TRUE(scan_.LoadDyndeps(GetNode("dd1"), &err));
781  EXPECT_EQ("", err);
782  ASSERT_TRUE(GetNode("dd2")->dyndep_pending());
783  EXPECT_FALSE(scan_.LoadDyndeps(GetNode("dd2"), &err));
784  EXPECT_EQ("multiple rules generate out-twice.imp", err);
785 }
786 
787 TEST_F(GraphTest, DyndepLoadMultiple) {
788  AssertParse(&state_,
789 "rule r\n"
790 " command = unused\n"
791 "build out1: r in1 || dd\n"
792 " dyndep = dd\n"
793 "build out2: r in2 || dd\n"
794 " dyndep = dd\n"
795 "build outNot: r in3 || dd\n"
796  );
797  fs_.Create("dd",
798 "ninja_dyndep_version = 1\n"
799 "build out1 | out1imp: dyndep | in1imp\n"
800 "build out2: dyndep | in2imp\n"
801 " restat = 1\n"
802  );
803 
804  string err;
805  ASSERT_TRUE(GetNode("dd")->dyndep_pending());
806  EXPECT_TRUE(scan_.LoadDyndeps(GetNode("dd"), &err));
807  EXPECT_EQ("", err);
808  EXPECT_FALSE(GetNode("dd")->dyndep_pending());
809 
810  Edge* edge1 = GetNode("out1")->in_edge();
811  ASSERT_EQ(size_t(2), edge1->outputs_.size());
812  EXPECT_EQ("out1", edge1->outputs_[0]->path());
813  EXPECT_EQ("out1imp", edge1->outputs_[1]->path());
814  EXPECT_EQ(1, edge1->implicit_outs_);
815  ASSERT_EQ(size_t(3), edge1->inputs_.size());
816  EXPECT_EQ("in1", edge1->inputs_[0]->path());
817  EXPECT_EQ("in1imp", edge1->inputs_[1]->path());
818  EXPECT_EQ("dd", edge1->inputs_[2]->path());
819  EXPECT_EQ(1, edge1->implicit_deps_);
820  EXPECT_EQ(1, edge1->order_only_deps_);
821  EXPECT_FALSE(edge1->GetBindingBool("restat"));
822  EXPECT_EQ(edge1, GetNode("out1imp")->in_edge());
823  Node* in1imp = GetNode("in1imp");
824  ASSERT_EQ(size_t(1), in1imp->out_edges().size());
825  EXPECT_EQ(edge1, in1imp->out_edges()[0]);
826 
827  Edge* edge2 = GetNode("out2")->in_edge();
828  ASSERT_EQ(size_t(1), edge2->outputs_.size());
829  EXPECT_EQ("out2", edge2->outputs_[0]->path());
830  EXPECT_EQ(0, edge2->implicit_outs_);
831  ASSERT_EQ(size_t(3), edge2->inputs_.size());
832  EXPECT_EQ("in2", edge2->inputs_[0]->path());
833  EXPECT_EQ("in2imp", edge2->inputs_[1]->path());
834  EXPECT_EQ("dd", edge2->inputs_[2]->path());
835  EXPECT_EQ(1, edge2->implicit_deps_);
836  EXPECT_EQ(1, edge2->order_only_deps_);
837  EXPECT_TRUE(edge2->GetBindingBool("restat"));
838  Node* in2imp = GetNode("in2imp");
839  ASSERT_EQ(size_t(1), in2imp->out_edges().size());
840  EXPECT_EQ(edge2, in2imp->out_edges()[0]);
841 }
842 
843 TEST_F(GraphTest, DyndepFileMissing) {
844  AssertParse(&state_,
845 "rule r\n"
846 " command = unused\n"
847 "build out: r || dd\n"
848 " dyndep = dd\n"
849  );
850 
851  string err;
852  EXPECT_FALSE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
853  ASSERT_EQ("loading 'dd': No such file or directory", err);
854 }
855 
856 TEST_F(GraphTest, DyndepFileError) {
857  AssertParse(&state_,
858 "rule r\n"
859 " command = unused\n"
860 "build out: r || dd\n"
861 " dyndep = dd\n"
862  );
863  fs_.Create("dd",
864 "ninja_dyndep_version = 1\n"
865  );
866 
867  string err;
868  EXPECT_FALSE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
869  ASSERT_EQ("'out' not mentioned in its dyndep file 'dd'", err);
870 }
871 
872 TEST_F(GraphTest, DyndepImplicitInputNewer) {
873  AssertParse(&state_,
874 "rule r\n"
875 " command = unused\n"
876 "build out: r || dd\n"
877 " dyndep = dd\n"
878  );
879  fs_.Create("dd",
880 "ninja_dyndep_version = 1\n"
881 "build out: dyndep | in\n"
882  );
883  fs_.Create("out", "");
884  fs_.Tick();
885  fs_.Create("in", "");
886 
887  string err;
888  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
889  ASSERT_EQ("", err);
890 
891  EXPECT_FALSE(GetNode("in")->dirty());
892  EXPECT_FALSE(GetNode("dd")->dirty());
893 
894  // "out" is dirty due to dyndep-specified implicit input
895  EXPECT_TRUE(GetNode("out")->dirty());
896 }
897 
898 TEST_F(GraphTest, DyndepFileReady) {
899  AssertParse(&state_,
900 "rule r\n"
901 " command = unused\n"
902 "build dd: r dd-in\n"
903 "build out: r || dd\n"
904 " dyndep = dd\n"
905  );
906  fs_.Create("dd-in", "");
907  fs_.Create("dd",
908 "ninja_dyndep_version = 1\n"
909 "build out: dyndep | in\n"
910  );
911  fs_.Create("out", "");
912  fs_.Tick();
913  fs_.Create("in", "");
914 
915  string err;
916  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
917  ASSERT_EQ("", err);
918 
919  EXPECT_FALSE(GetNode("in")->dirty());
920  EXPECT_FALSE(GetNode("dd")->dirty());
921  EXPECT_TRUE(GetNode("dd")->in_edge()->outputs_ready());
922 
923  // "out" is dirty due to dyndep-specified implicit input
924  EXPECT_TRUE(GetNode("out")->dirty());
925 }
926 
927 TEST_F(GraphTest, DyndepFileNotClean) {
928  AssertParse(&state_,
929 "rule r\n"
930 " command = unused\n"
931 "build dd: r dd-in\n"
932 "build out: r || dd\n"
933 " dyndep = dd\n"
934  );
935  fs_.Create("dd", "this-should-not-be-loaded");
936  fs_.Tick();
937  fs_.Create("dd-in", "");
938  fs_.Create("out", "");
939 
940  string err;
941  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
942  ASSERT_EQ("", err);
943 
944  EXPECT_TRUE(GetNode("dd")->dirty());
945  EXPECT_FALSE(GetNode("dd")->in_edge()->outputs_ready());
946 
947  // "out" is clean but not ready since "dd" is not ready
948  EXPECT_FALSE(GetNode("out")->dirty());
949  EXPECT_FALSE(GetNode("out")->in_edge()->outputs_ready());
950 }
951 
952 TEST_F(GraphTest, DyndepFileNotReady) {
953  AssertParse(&state_,
954 "rule r\n"
955 " command = unused\n"
956 "build tmp: r\n"
957 "build dd: r dd-in || tmp\n"
958 "build out: r || dd\n"
959 " dyndep = dd\n"
960  );
961  fs_.Create("dd", "this-should-not-be-loaded");
962  fs_.Create("dd-in", "");
963  fs_.Tick();
964  fs_.Create("out", "");
965 
966  string err;
967  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
968  ASSERT_EQ("", err);
969 
970  EXPECT_FALSE(GetNode("dd")->dirty());
971  EXPECT_FALSE(GetNode("dd")->in_edge()->outputs_ready());
972  EXPECT_FALSE(GetNode("out")->dirty());
973  EXPECT_FALSE(GetNode("out")->in_edge()->outputs_ready());
974 }
975 
976 TEST_F(GraphTest, DyndepFileSecondNotReady) {
977  AssertParse(&state_,
978 "rule r\n"
979 " command = unused\n"
980 "build dd1: r dd1-in\n"
981 "build dd2-in: r || dd1\n"
982 " dyndep = dd1\n"
983 "build dd2: r dd2-in\n"
984 "build out: r || dd2\n"
985 " dyndep = dd2\n"
986  );
987  fs_.Create("dd1", "");
988  fs_.Create("dd2", "");
989  fs_.Create("dd2-in", "");
990  fs_.Tick();
991  fs_.Create("dd1-in", "");
992  fs_.Create("out", "");
993 
994  string err;
995  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
996  ASSERT_EQ("", err);
997 
998  EXPECT_TRUE(GetNode("dd1")->dirty());
999  EXPECT_FALSE(GetNode("dd1")->in_edge()->outputs_ready());
1000  EXPECT_FALSE(GetNode("dd2")->dirty());
1001  EXPECT_FALSE(GetNode("dd2")->in_edge()->outputs_ready());
1002  EXPECT_FALSE(GetNode("out")->dirty());
1003  EXPECT_FALSE(GetNode("out")->in_edge()->outputs_ready());
1004 }
1005 
1006 TEST_F(GraphTest, DyndepFileCircular) {
1007  AssertParse(&state_,
1008 "rule r\n"
1009 " command = unused\n"
1010 "build out: r in || dd\n"
1011 " depfile = out.d\n"
1012 " dyndep = dd\n"
1013 "build in: r circ\n"
1014  );
1015  fs_.Create("out.d", "out: inimp\n");
1016  fs_.Create("dd",
1017 "ninja_dyndep_version = 1\n"
1018 "build out | circ: dyndep\n"
1019  );
1020  fs_.Create("out", "");
1021 
1022  Edge* edge = GetNode("out")->in_edge();
1023  string err;
1024  EXPECT_FALSE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
1025  EXPECT_EQ("dependency cycle: circ -> in -> circ", err);
1026 
1027  // Verify that "out.d" was loaded exactly once despite
1028  // circular reference discovered from dyndep file.
1029  ASSERT_EQ(size_t(3), edge->inputs_.size());
1030  EXPECT_EQ("in", edge->inputs_[0]->path());
1031  EXPECT_EQ("inimp", edge->inputs_[1]->path());
1032  EXPECT_EQ("dd", edge->inputs_[2]->path());
1033  EXPECT_EQ(1, edge->implicit_deps_);
1034  EXPECT_EQ(1, edge->order_only_deps_);
1035 }
1036 
1037 TEST_F(GraphTest, Validation) {
1038  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
1039 "build out: cat in |@ validate\n"
1040 "build validate: cat in\n"));
1041 
1042  fs_.Create("in", "");
1043  string err;
1044  std::vector<Node*> validation_nodes;
1045  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), &validation_nodes, &err));
1046  ASSERT_EQ("", err);
1047 
1048  ASSERT_EQ(validation_nodes.size(), size_t(1));
1049  EXPECT_EQ(validation_nodes[0]->path(), "validate");
1050 
1051  EXPECT_TRUE(GetNode("out")->dirty());
1052  EXPECT_TRUE(GetNode("validate")->dirty());
1053 }
1054 
1055 // Check that phony's dependencies' mtimes are propagated.
1056 TEST_F(GraphTest, PhonyDepsMtimes) {
1057  string err;
1058  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
1059 "rule touch\n"
1060 " command = touch $out\n"
1061 "build in_ph: phony in1\n"
1062 "build out1: touch in_ph\n"
1063 ));
1064  fs_.Create("in1", "");
1065  fs_.Create("out1", "");
1066  Node* out1 = GetNode("out1");
1067  Node* in1 = GetNode("in1");
1068 
1069  EXPECT_TRUE(scan_.RecomputeDirty(out1, NULL, &err));
1070  EXPECT_TRUE(!out1->dirty());
1071 
1072  // Get the mtime of out1
1073  ASSERT_TRUE(in1->Stat(&fs_, &err));
1074  ASSERT_TRUE(out1->Stat(&fs_, &err));
1075  TimeStamp out1Mtime1 = out1->mtime();
1076  TimeStamp in1Mtime1 = in1->mtime();
1077 
1078  // Touch in1. This should cause out1 to be dirty
1079  state_.Reset();
1080  fs_.Tick();
1081  fs_.Create("in1", "");
1082 
1083  ASSERT_TRUE(in1->Stat(&fs_, &err));
1084  EXPECT_GT(in1->mtime(), in1Mtime1);
1085 
1086  EXPECT_TRUE(scan_.RecomputeDirty(out1, NULL, &err));
1087  EXPECT_GT(in1->mtime(), in1Mtime1);
1088  EXPECT_EQ(out1->mtime(), out1Mtime1);
1089  EXPECT_TRUE(out1->dirty());
1090 }
1091 
1092 // Test that EdgeQueue correctly prioritizes by critical time
1093 TEST_F(GraphTest, EdgeQueuePriority) {
1094 
1095  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
1096 "rule r\n"
1097 " command = unused\n"
1098 "build out1: r in1\n"
1099 "build out2: r in2\n"
1100 "build out3: r in3\n"
1101 ));
1102 
1103  const int n_edges = 3;
1104  Edge *(edges)[n_edges] = {
1105  GetNode("out1")->in_edge(),
1106  GetNode("out2")->in_edge(),
1107  GetNode("out3")->in_edge(),
1108  };
1109 
1110  // Output is largest critical time to smallest
1111  for (int i = 0; i < n_edges; ++i) {
1112  edges[i]->set_critical_path_weight(i * 10);
1113  }
1114 
1115  EdgePriorityQueue queue;
1116  for (int i = 0; i < n_edges; ++i) {
1117  queue.push(edges[i]);
1118  }
1119 
1120  EXPECT_EQ(queue.size(), static_cast<size_t>(n_edges));
1121  for (int i = 0; i < n_edges; ++i) {
1122  EXPECT_EQ(queue.top(), edges[n_edges - 1 - i]);
1123  queue.pop();
1124  }
1125  EXPECT_TRUE(queue.empty());
1126 
1127  // When there is ambiguity, the lowest edge id comes first
1128  for (int i = 0; i < n_edges; ++i) {
1129  edges[i]->set_critical_path_weight(0);
1130  }
1131 
1132  queue.push(edges[1]);
1133  queue.push(edges[2]);
1134  queue.push(edges[0]);
1135 
1136  for (int i = 0; i < n_edges; ++i) {
1137  EXPECT_EQ(queue.top(), edges[i]);
1138  queue.pop();
1139  }
1140  EXPECT_TRUE(queue.empty());
1141 }
1142 
1143 
TEST_F(GraphTest, MissingImplicit)
Definition: graph_test.cc:30
@ kPhonyCycleActionError
Definition: hash_map.h:26
Collects the transitive set of edges that lead into a given set of starting nodes.
std::vector< Edge * > in_edges
we use a vector to preserve order from requisites to their dependents.
void CollectFrom(const Node *node)
DependencyScan manages the process of scanning the files in a graph and updating the dirty/outputs_re...
Definition: graph.h:332
An edge in the dependency graph; links between Nodes using Rules.
Definition: graph.h:175
std::string GetBinding(const std::string &key) const
Returns the shell-escaped value of |key|.
Definition: graph.cc:511
std::vector< Node * > outputs_
Definition: graph.h:217
int implicit_deps_
Definition: graph.h:243
void set_critical_path_weight(int64_t critical_path_weight)
Definition: graph.h:210
int order_only_deps_
Definition: graph.h:244
bool GetBindingBool(const std::string &key) const
Definition: graph.cc:516
int implicit_outs_
Definition: graph.h:258
std::string EvaluateCommand(bool incl_rsp_file=false) const
Expand all variables in a command and return it as a string.
Definition: graph.cc:501
std::vector< Node * > inputs_
Definition: graph.h:216
VirtualFileSystem fs_
Definition: graph_test.cc:26
DependencyScan scan_
Definition: graph_test.cc:27
A class used to collect the transitive set of inputs from a given set of starting nodes.
Definition: graph.h:442
void VisitNode(const Node *node)
Visit a single.
Definition: graph.cc:764
std::vector< std::string > GetInputsAsStrings(bool shell_escape=false) const
Same as inputs(), but returns the list of visited nodes as a list of strings, with optional shell esc...
Definition: graph.cc:786
void Reset()
Reset collector state.
Definition: graph.h:456
PhonyCycleAction phony_cycle_action_
Information about a node in the dependency graph: the file, whether it's dirty, mtime,...
Definition: graph.h:42
bool dirty() const
Definition: graph.h:93
TimeStamp mtime() const
Definition: graph.h:91
bool Stat(DiskInterface *disk_interface, std::string *err)
Return false on error.
Definition: graph.cc:34
const std::vector< Edge * > & out_edges() const
Definition: graph.h:114
Plan stores the state of a build plan: what we intend to build, which steps we're ready to execute.
Definition: build.h:41
int command_edge_count() const
Number of edges with commands to run.
Definition: build.h:75
bool AddTarget(const Node *target, std::string *err)
Add a target to our plan (including all its dependencies).
Definition: build.cc:94
bool more_to_do() const
Returns true if there's more work to be done.
Definition: build.h:54
A base test fixture that includes a State object with a builtin "cat" rule.
Definition: test.h:30
An implementation of DiskInterface that uses an in-memory representation of disk state.
Definition: test.h:51
void AssertParse(State *state, const char *input, ManifestParserOptions opts)
Definition: test.cc:100
int64_t TimeStamp
Definition: timestamp.h:31