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
21using namespace std;
22
24 GraphTest() : scan_(&state_, NULL, NULL, &fs_, NULL, NULL) {}
25
28};
29
30TEST_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
46TEST_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
62TEST_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
83TEST_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
107TEST_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
119TEST_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
133TEST_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
149TEST_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
160TEST_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
172TEST_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
186TEST_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
203TEST_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
286TEST_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
362TEST_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
377TEST_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
395TEST_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.
420TEST_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.
431TEST_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.
443TEST_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"
455TEST_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
472TEST_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
484TEST_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
496TEST_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
504TEST_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
512TEST_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
521TEST_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.
535TEST_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.
557TEST_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.
583TEST_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
609TEST_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
629TEST_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
658TEST_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
689TEST_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
703TEST_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
720TEST_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
741TEST_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
760TEST_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
787TEST_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
843TEST_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
856TEST_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
872TEST_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
898TEST_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
927TEST_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
952TEST_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
976TEST_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
1006TEST_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
1037TEST_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.
1056TEST_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
1093TEST_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
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
const std::vector< Edge * > & out_edges() const
Definition graph.h:114
bool Stat(DiskInterface *disk_interface, std::string *err)
Return false on error.
Definition graph.cc:34
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
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