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 | |
20 | using namespace std; |
21 | |
22 | struct GraphTest : public StateTestWithBuiltinRules { |
23 | GraphTest() : scan_(&state_, NULL, NULL, &fs_, NULL) {} |
24 | |
25 | VirtualFileSystem fs_; |
26 | DependencyScan scan_; |
27 | }; |
28 | |
29 | TEST_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 | |
45 | TEST_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 | |
61 | TEST_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 | |
82 | TEST_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 | |
106 | TEST_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 | |
118 | TEST_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 | |
132 | TEST_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 | |
148 | TEST_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 | |
159 | TEST_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 | |
171 | TEST_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 | |
185 | TEST_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 | |
202 | TEST_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 | |
218 | TEST_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 | |
251 | TEST_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 |
266 | TEST_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 |
284 | TEST_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. |
309 | TEST_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. |
320 | TEST_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. |
332 | TEST_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" |
344 | TEST_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 | |
361 | TEST_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 | |
373 | TEST_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 | |
385 | TEST_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 | |
393 | TEST_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 | |
401 | TEST_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 | |
410 | TEST_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. |
424 | TEST_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. |
446 | TEST_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. |
472 | TEST_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 |
498 | TEST_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 | |
518 | TEST_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 | |
547 | TEST_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 | |
578 | TEST_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 | |
592 | TEST_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 | |
609 | TEST_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 | |
630 | TEST_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 | |
649 | TEST_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 | |
676 | TEST_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 | |
732 | TEST_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 | |
745 | TEST_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 | |
761 | TEST_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 | |
787 | TEST_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 | |
816 | TEST_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 | |
841 | TEST_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 | |
865 | TEST_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 | |
895 | TEST_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 | |
926 | TEST_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. |
945 | TEST_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 | |