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 "manifest_parser.h" |
16 | |
17 | #include <map> |
18 | #include <vector> |
19 | |
20 | #include "graph.h" |
21 | #include "state.h" |
22 | #include "test.h" |
23 | |
24 | using namespace std; |
25 | |
26 | struct ParserTest : public testing::Test { |
27 | void AssertParse(const char* input) { |
28 | ManifestParser parser(&state, &fs_); |
29 | string err; |
30 | EXPECT_TRUE(parser.ParseTest(input, &err)); |
31 | ASSERT_EQ("" , err); |
32 | VerifyGraph(state); |
33 | } |
34 | |
35 | State state; |
36 | VirtualFileSystem fs_; |
37 | }; |
38 | |
39 | TEST_F(ParserTest, Empty) { |
40 | ASSERT_NO_FATAL_FAILURE(AssertParse("" )); |
41 | } |
42 | |
43 | TEST_F(ParserTest, Rules) { |
44 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
45 | "rule cat\n" |
46 | " command = cat $in > $out\n" |
47 | "\n" |
48 | "rule date\n" |
49 | " command = date > $out\n" |
50 | "\n" |
51 | "build result: cat in_1.cc in-2.O\n" )); |
52 | |
53 | ASSERT_EQ(3u, state.bindings_.GetRules().size()); |
54 | const Rule* rule = state.bindings_.GetRules().begin()->second; |
55 | EXPECT_EQ("cat" , rule->name()); |
56 | EXPECT_EQ("[cat ][$in][ > ][$out]" , |
57 | rule->GetBinding("command" )->Serialize()); |
58 | } |
59 | |
60 | TEST_F(ParserTest, RuleAttributes) { |
61 | // Check that all of the allowed rule attributes are parsed ok. |
62 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
63 | "rule cat\n" |
64 | " command = a\n" |
65 | " depfile = a\n" |
66 | " deps = a\n" |
67 | " description = a\n" |
68 | " generator = a\n" |
69 | " restat = a\n" |
70 | " rspfile = a\n" |
71 | " rspfile_content = a\n" |
72 | )); |
73 | } |
74 | |
75 | TEST_F(ParserTest, IgnoreIndentedComments) { |
76 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
77 | " #indented comment\n" |
78 | "rule cat\n" |
79 | " command = cat $in > $out\n" |
80 | " #generator = 1\n" |
81 | " restat = 1 # comment\n" |
82 | " #comment\n" |
83 | "build result: cat in_1.cc in-2.O\n" |
84 | " #comment\n" )); |
85 | |
86 | ASSERT_EQ(2u, state.bindings_.GetRules().size()); |
87 | const Rule* rule = state.bindings_.GetRules().begin()->second; |
88 | EXPECT_EQ("cat" , rule->name()); |
89 | Edge* edge = state.GetNode("result" , 0)->in_edge(); |
90 | EXPECT_TRUE(edge->GetBindingBool("restat" )); |
91 | EXPECT_FALSE(edge->GetBindingBool("generator" )); |
92 | } |
93 | |
94 | TEST_F(ParserTest, IgnoreIndentedBlankLines) { |
95 | // the indented blanks used to cause parse errors |
96 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
97 | " \n" |
98 | "rule cat\n" |
99 | " command = cat $in > $out\n" |
100 | " \n" |
101 | "build result: cat in_1.cc in-2.O\n" |
102 | " \n" |
103 | "variable=1\n" )); |
104 | |
105 | // the variable must be in the top level environment |
106 | EXPECT_EQ("1" , state.bindings_.LookupVariable("variable" )); |
107 | } |
108 | |
109 | TEST_F(ParserTest, ResponseFiles) { |
110 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
111 | "rule cat_rsp\n" |
112 | " command = cat $rspfile > $out\n" |
113 | " rspfile = $rspfile\n" |
114 | " rspfile_content = $in\n" |
115 | "\n" |
116 | "build out: cat_rsp in\n" |
117 | " rspfile=out.rsp\n" )); |
118 | |
119 | ASSERT_EQ(2u, state.bindings_.GetRules().size()); |
120 | const Rule* rule = state.bindings_.GetRules().begin()->second; |
121 | EXPECT_EQ("cat_rsp" , rule->name()); |
122 | EXPECT_EQ("[cat ][$rspfile][ > ][$out]" , |
123 | rule->GetBinding("command" )->Serialize()); |
124 | EXPECT_EQ("[$rspfile]" , rule->GetBinding("rspfile" )->Serialize()); |
125 | EXPECT_EQ("[$in]" , rule->GetBinding("rspfile_content" )->Serialize()); |
126 | } |
127 | |
128 | TEST_F(ParserTest, InNewline) { |
129 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
130 | "rule cat_rsp\n" |
131 | " command = cat $in_newline > $out\n" |
132 | "\n" |
133 | "build out: cat_rsp in in2\n" |
134 | " rspfile=out.rsp\n" )); |
135 | |
136 | ASSERT_EQ(2u, state.bindings_.GetRules().size()); |
137 | const Rule* rule = state.bindings_.GetRules().begin()->second; |
138 | EXPECT_EQ("cat_rsp" , rule->name()); |
139 | EXPECT_EQ("[cat ][$in_newline][ > ][$out]" , |
140 | rule->GetBinding("command" )->Serialize()); |
141 | |
142 | Edge* edge = state.edges_[0]; |
143 | EXPECT_EQ("cat in\nin2 > out" , edge->EvaluateCommand()); |
144 | } |
145 | |
146 | TEST_F(ParserTest, Variables) { |
147 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
148 | "l = one-letter-test\n" |
149 | "rule link\n" |
150 | " command = ld $l $extra $with_under -o $out $in\n" |
151 | "\n" |
152 | "extra = -pthread\n" |
153 | "with_under = -under\n" |
154 | "build a: link b c\n" |
155 | "nested1 = 1\n" |
156 | "nested2 = $nested1/2\n" |
157 | "build supernested: link x\n" |
158 | " extra = $nested2/3\n" )); |
159 | |
160 | ASSERT_EQ(2u, state.edges_.size()); |
161 | Edge* edge = state.edges_[0]; |
162 | EXPECT_EQ("ld one-letter-test -pthread -under -o a b c" , |
163 | edge->EvaluateCommand()); |
164 | EXPECT_EQ("1/2" , state.bindings_.LookupVariable("nested2" )); |
165 | |
166 | edge = state.edges_[1]; |
167 | EXPECT_EQ("ld one-letter-test 1/2/3 -under -o supernested x" , |
168 | edge->EvaluateCommand()); |
169 | } |
170 | |
171 | TEST_F(ParserTest, VariableScope) { |
172 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
173 | "foo = bar\n" |
174 | "rule cmd\n" |
175 | " command = cmd $foo $in $out\n" |
176 | "\n" |
177 | "build inner: cmd a\n" |
178 | " foo = baz\n" |
179 | "build outer: cmd b\n" |
180 | "\n" // Extra newline after build line tickles a regression. |
181 | )); |
182 | |
183 | ASSERT_EQ(2u, state.edges_.size()); |
184 | EXPECT_EQ("cmd baz a inner" , state.edges_[0]->EvaluateCommand()); |
185 | EXPECT_EQ("cmd bar b outer" , state.edges_[1]->EvaluateCommand()); |
186 | } |
187 | |
188 | TEST_F(ParserTest, Continuation) { |
189 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
190 | "rule link\n" |
191 | " command = foo bar $\n" |
192 | " baz\n" |
193 | "\n" |
194 | "build a: link c $\n" |
195 | " d e f\n" )); |
196 | |
197 | ASSERT_EQ(2u, state.bindings_.GetRules().size()); |
198 | const Rule* rule = state.bindings_.GetRules().begin()->second; |
199 | EXPECT_EQ("link" , rule->name()); |
200 | EXPECT_EQ("[foo bar baz]" , rule->GetBinding("command" )->Serialize()); |
201 | } |
202 | |
203 | TEST_F(ParserTest, Backslash) { |
204 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
205 | "foo = bar\\baz\n" |
206 | "foo2 = bar\\ baz\n" |
207 | )); |
208 | EXPECT_EQ("bar\\baz" , state.bindings_.LookupVariable("foo" )); |
209 | EXPECT_EQ("bar\\ baz" , state.bindings_.LookupVariable("foo2" )); |
210 | } |
211 | |
212 | TEST_F(ParserTest, Comment) { |
213 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
214 | "# this is a comment\n" |
215 | "foo = not # a comment\n" )); |
216 | EXPECT_EQ("not # a comment" , state.bindings_.LookupVariable("foo" )); |
217 | } |
218 | |
219 | TEST_F(ParserTest, Dollars) { |
220 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
221 | "rule foo\n" |
222 | " command = ${out}bar$$baz$$$\n" |
223 | "blah\n" |
224 | "x = $$dollar\n" |
225 | "build $x: foo y\n" |
226 | )); |
227 | EXPECT_EQ("$dollar" , state.bindings_.LookupVariable("x" )); |
228 | #ifdef _WIN32 |
229 | EXPECT_EQ("$dollarbar$baz$blah" , state.edges_[0]->EvaluateCommand()); |
230 | #else |
231 | EXPECT_EQ("'$dollar'bar$baz$blah" , state.edges_[0]->EvaluateCommand()); |
232 | #endif |
233 | } |
234 | |
235 | TEST_F(ParserTest, EscapeSpaces) { |
236 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
237 | "rule spaces\n" |
238 | " command = something\n" |
239 | "build foo$ bar: spaces $$one two$$$ three\n" |
240 | )); |
241 | EXPECT_TRUE(state.LookupNode("foo bar" )); |
242 | EXPECT_EQ(state.edges_[0]->outputs_[0]->path(), "foo bar" ); |
243 | EXPECT_EQ(state.edges_[0]->inputs_[0]->path(), "$one" ); |
244 | EXPECT_EQ(state.edges_[0]->inputs_[1]->path(), "two$ three" ); |
245 | EXPECT_EQ(state.edges_[0]->EvaluateCommand(), "something" ); |
246 | } |
247 | |
248 | TEST_F(ParserTest, CanonicalizeFile) { |
249 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
250 | "rule cat\n" |
251 | " command = cat $in > $out\n" |
252 | "build out: cat in/1 in//2\n" |
253 | "build in/1: cat\n" |
254 | "build in/2: cat\n" )); |
255 | |
256 | EXPECT_TRUE(state.LookupNode("in/1" )); |
257 | EXPECT_TRUE(state.LookupNode("in/2" )); |
258 | EXPECT_FALSE(state.LookupNode("in//1" )); |
259 | EXPECT_FALSE(state.LookupNode("in//2" )); |
260 | } |
261 | |
262 | #ifdef _WIN32 |
263 | TEST_F(ParserTest, CanonicalizeFileBackslashes) { |
264 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
265 | "rule cat\n" |
266 | " command = cat $in > $out\n" |
267 | "build out: cat in\\1 in\\\\2\n" |
268 | "build in\\1: cat\n" |
269 | "build in\\2: cat\n" )); |
270 | |
271 | Node* node = state.LookupNode("in/1" );; |
272 | EXPECT_TRUE(node); |
273 | EXPECT_EQ(1, node->slash_bits()); |
274 | node = state.LookupNode("in/2" ); |
275 | EXPECT_TRUE(node); |
276 | EXPECT_EQ(1, node->slash_bits()); |
277 | EXPECT_FALSE(state.LookupNode("in//1" )); |
278 | EXPECT_FALSE(state.LookupNode("in//2" )); |
279 | } |
280 | #endif |
281 | |
282 | TEST_F(ParserTest, PathVariables) { |
283 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
284 | "rule cat\n" |
285 | " command = cat $in > $out\n" |
286 | "dir = out\n" |
287 | "build $dir/exe: cat src\n" )); |
288 | |
289 | EXPECT_FALSE(state.LookupNode("$dir/exe" )); |
290 | EXPECT_TRUE(state.LookupNode("out/exe" )); |
291 | } |
292 | |
293 | TEST_F(ParserTest, CanonicalizePaths) { |
294 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
295 | "rule cat\n" |
296 | " command = cat $in > $out\n" |
297 | "build ./out.o: cat ./bar/baz/../foo.cc\n" )); |
298 | |
299 | EXPECT_FALSE(state.LookupNode("./out.o" )); |
300 | EXPECT_TRUE(state.LookupNode("out.o" )); |
301 | EXPECT_FALSE(state.LookupNode("./bar/baz/../foo.cc" )); |
302 | EXPECT_TRUE(state.LookupNode("bar/foo.cc" )); |
303 | } |
304 | |
305 | #ifdef _WIN32 |
306 | TEST_F(ParserTest, CanonicalizePathsBackslashes) { |
307 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
308 | "rule cat\n" |
309 | " command = cat $in > $out\n" |
310 | "build ./out.o: cat ./bar/baz/../foo.cc\n" |
311 | "build .\\out2.o: cat .\\bar/baz\\..\\foo.cc\n" |
312 | "build .\\out3.o: cat .\\bar\\baz\\..\\foo3.cc\n" |
313 | )); |
314 | |
315 | EXPECT_FALSE(state.LookupNode("./out.o" )); |
316 | EXPECT_FALSE(state.LookupNode(".\\out2.o" )); |
317 | EXPECT_FALSE(state.LookupNode(".\\out3.o" )); |
318 | EXPECT_TRUE(state.LookupNode("out.o" )); |
319 | EXPECT_TRUE(state.LookupNode("out2.o" )); |
320 | EXPECT_TRUE(state.LookupNode("out3.o" )); |
321 | EXPECT_FALSE(state.LookupNode("./bar/baz/../foo.cc" )); |
322 | EXPECT_FALSE(state.LookupNode(".\\bar/baz\\..\\foo.cc" )); |
323 | EXPECT_FALSE(state.LookupNode(".\\bar/baz\\..\\foo3.cc" )); |
324 | Node* node = state.LookupNode("bar/foo.cc" ); |
325 | EXPECT_TRUE(node); |
326 | EXPECT_EQ(0, node->slash_bits()); |
327 | node = state.LookupNode("bar/foo3.cc" ); |
328 | EXPECT_TRUE(node); |
329 | EXPECT_EQ(1, node->slash_bits()); |
330 | } |
331 | #endif |
332 | |
333 | TEST_F(ParserTest, DuplicateEdgeWithMultipleOutputs) { |
334 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
335 | "rule cat\n" |
336 | " command = cat $in > $out\n" |
337 | "build out1 out2: cat in1\n" |
338 | "build out1: cat in2\n" |
339 | "build final: cat out1\n" |
340 | )); |
341 | // AssertParse() checks that the generated build graph is self-consistent. |
342 | // That's all the checking that this test needs. |
343 | } |
344 | |
345 | TEST_F(ParserTest, NoDeadPointerFromDuplicateEdge) { |
346 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
347 | "rule cat\n" |
348 | " command = cat $in > $out\n" |
349 | "build out: cat in\n" |
350 | "build out: cat in\n" |
351 | )); |
352 | // AssertParse() checks that the generated build graph is self-consistent. |
353 | // That's all the checking that this test needs. |
354 | } |
355 | |
356 | TEST_F(ParserTest, DuplicateEdgeWithMultipleOutputsError) { |
357 | const char kInput[] = |
358 | "rule cat\n" |
359 | " command = cat $in > $out\n" |
360 | "build out1 out2: cat in1\n" |
361 | "build out1: cat in2\n" |
362 | "build final: cat out1\n" ; |
363 | ManifestParserOptions parser_opts; |
364 | parser_opts.dupe_edge_action_ = kDupeEdgeActionError; |
365 | ManifestParser parser(&state, &fs_, parser_opts); |
366 | string err; |
367 | EXPECT_FALSE(parser.ParseTest(kInput, &err)); |
368 | EXPECT_EQ("input:5: multiple rules generate out1\n" , err); |
369 | } |
370 | |
371 | TEST_F(ParserTest, DuplicateEdgeInIncludedFile) { |
372 | fs_.Create("sub.ninja" , |
373 | "rule cat\n" |
374 | " command = cat $in > $out\n" |
375 | "build out1 out2: cat in1\n" |
376 | "build out1: cat in2\n" |
377 | "build final: cat out1\n" ); |
378 | const char kInput[] = |
379 | "subninja sub.ninja\n" ; |
380 | ManifestParserOptions parser_opts; |
381 | parser_opts.dupe_edge_action_ = kDupeEdgeActionError; |
382 | ManifestParser parser(&state, &fs_, parser_opts); |
383 | string err; |
384 | EXPECT_FALSE(parser.ParseTest(kInput, &err)); |
385 | EXPECT_EQ("sub.ninja:5: multiple rules generate out1\n" , err); |
386 | } |
387 | |
388 | TEST_F(ParserTest, PhonySelfReferenceIgnored) { |
389 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
390 | "build a: phony a\n" |
391 | )); |
392 | |
393 | Node* node = state.LookupNode("a" ); |
394 | Edge* edge = node->in_edge(); |
395 | ASSERT_TRUE(edge->inputs_.empty()); |
396 | } |
397 | |
398 | TEST_F(ParserTest, PhonySelfReferenceKept) { |
399 | const char kInput[] = |
400 | "build a: phony a\n" ; |
401 | ManifestParserOptions parser_opts; |
402 | parser_opts.phony_cycle_action_ = kPhonyCycleActionError; |
403 | ManifestParser parser(&state, &fs_, parser_opts); |
404 | string err; |
405 | EXPECT_TRUE(parser.ParseTest(kInput, &err)); |
406 | EXPECT_EQ("" , err); |
407 | |
408 | Node* node = state.LookupNode("a" ); |
409 | Edge* edge = node->in_edge(); |
410 | ASSERT_EQ(edge->inputs_.size(), 1); |
411 | ASSERT_EQ(edge->inputs_[0], node); |
412 | } |
413 | |
414 | TEST_F(ParserTest, ReservedWords) { |
415 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
416 | "rule build\n" |
417 | " command = rule run $out\n" |
418 | "build subninja: build include default foo.cc\n" |
419 | "default subninja\n" )); |
420 | } |
421 | |
422 | TEST_F(ParserTest, Errors) { |
423 | { |
424 | State local_state; |
425 | ManifestParser parser(&local_state, NULL); |
426 | string err; |
427 | EXPECT_FALSE(parser.ParseTest(string("subn" , 4), &err)); |
428 | EXPECT_EQ("input:1: expected '=', got eof\n" |
429 | "subn\n" |
430 | " ^ near here" |
431 | , err); |
432 | } |
433 | |
434 | { |
435 | State local_state; |
436 | ManifestParser parser(&local_state, NULL); |
437 | string err; |
438 | EXPECT_FALSE(parser.ParseTest("foobar" , &err)); |
439 | EXPECT_EQ("input:1: expected '=', got eof\n" |
440 | "foobar\n" |
441 | " ^ near here" |
442 | , err); |
443 | } |
444 | |
445 | { |
446 | State local_state; |
447 | ManifestParser parser(&local_state, NULL); |
448 | string err; |
449 | EXPECT_FALSE(parser.ParseTest("x 3" , &err)); |
450 | EXPECT_EQ("input:1: expected '=', got identifier\n" |
451 | "x 3\n" |
452 | " ^ near here" |
453 | , err); |
454 | } |
455 | |
456 | { |
457 | State local_state; |
458 | ManifestParser parser(&local_state, NULL); |
459 | string err; |
460 | EXPECT_FALSE(parser.ParseTest("x = 3" , &err)); |
461 | EXPECT_EQ("input:1: unexpected EOF\n" |
462 | "x = 3\n" |
463 | " ^ near here" |
464 | , err); |
465 | } |
466 | |
467 | { |
468 | State local_state; |
469 | ManifestParser parser(&local_state, NULL); |
470 | string err; |
471 | EXPECT_FALSE(parser.ParseTest("x = 3\ny 2" , &err)); |
472 | EXPECT_EQ("input:2: expected '=', got identifier\n" |
473 | "y 2\n" |
474 | " ^ near here" |
475 | , err); |
476 | } |
477 | |
478 | { |
479 | State local_state; |
480 | ManifestParser parser(&local_state, NULL); |
481 | string err; |
482 | EXPECT_FALSE(parser.ParseTest("x = $" , &err)); |
483 | EXPECT_EQ("input:1: bad $-escape (literal $ must be written as $$)\n" |
484 | "x = $\n" |
485 | " ^ near here" |
486 | , err); |
487 | } |
488 | |
489 | { |
490 | State local_state; |
491 | ManifestParser parser(&local_state, NULL); |
492 | string err; |
493 | EXPECT_FALSE(parser.ParseTest("x = $\n $[\n" , &err)); |
494 | EXPECT_EQ("input:2: bad $-escape (literal $ must be written as $$)\n" |
495 | " $[\n" |
496 | " ^ near here" |
497 | , err); |
498 | } |
499 | |
500 | { |
501 | State local_state; |
502 | ManifestParser parser(&local_state, NULL); |
503 | string err; |
504 | EXPECT_FALSE(parser.ParseTest("x = a$\n b$\n $\n" , &err)); |
505 | EXPECT_EQ("input:4: unexpected EOF\n" |
506 | , err); |
507 | } |
508 | |
509 | { |
510 | State local_state; |
511 | ManifestParser parser(&local_state, NULL); |
512 | string err; |
513 | EXPECT_FALSE(parser.ParseTest("build\n" , &err)); |
514 | EXPECT_EQ("input:1: expected path\n" |
515 | "build\n" |
516 | " ^ near here" |
517 | , err); |
518 | } |
519 | |
520 | { |
521 | State local_state; |
522 | ManifestParser parser(&local_state, NULL); |
523 | string err; |
524 | EXPECT_FALSE(parser.ParseTest("build x: y z\n" , &err)); |
525 | EXPECT_EQ("input:1: unknown build rule 'y'\n" |
526 | "build x: y z\n" |
527 | " ^ near here" |
528 | , err); |
529 | } |
530 | |
531 | { |
532 | State local_state; |
533 | ManifestParser parser(&local_state, NULL); |
534 | string err; |
535 | EXPECT_FALSE(parser.ParseTest("build x:: y z\n" , &err)); |
536 | EXPECT_EQ("input:1: expected build command name\n" |
537 | "build x:: y z\n" |
538 | " ^ near here" |
539 | , err); |
540 | } |
541 | |
542 | { |
543 | State local_state; |
544 | ManifestParser parser(&local_state, NULL); |
545 | string err; |
546 | EXPECT_FALSE(parser.ParseTest("rule cat\n command = cat ok\n" |
547 | "build x: cat $\n :\n" , |
548 | &err)); |
549 | EXPECT_EQ("input:4: expected newline, got ':'\n" |
550 | " :\n" |
551 | " ^ near here" |
552 | , err); |
553 | } |
554 | |
555 | { |
556 | State local_state; |
557 | ManifestParser parser(&local_state, NULL); |
558 | string err; |
559 | EXPECT_FALSE(parser.ParseTest("rule cat\n" , |
560 | &err)); |
561 | EXPECT_EQ("input:2: expected 'command =' line\n" , err); |
562 | } |
563 | |
564 | { |
565 | State local_state; |
566 | ManifestParser parser(&local_state, NULL); |
567 | string err; |
568 | EXPECT_FALSE(parser.ParseTest("rule cat\n" |
569 | " command = echo\n" |
570 | "rule cat\n" |
571 | " command = echo\n" , &err)); |
572 | EXPECT_EQ("input:3: duplicate rule 'cat'\n" |
573 | "rule cat\n" |
574 | " ^ near here" |
575 | , err); |
576 | } |
577 | |
578 | { |
579 | State local_state; |
580 | ManifestParser parser(&local_state, NULL); |
581 | string err; |
582 | EXPECT_FALSE(parser.ParseTest("rule cat\n" |
583 | " command = echo\n" |
584 | " rspfile = cat.rsp\n" , &err)); |
585 | EXPECT_EQ( |
586 | "input:4: rspfile and rspfile_content need to be both specified\n" , |
587 | err); |
588 | } |
589 | |
590 | { |
591 | State local_state; |
592 | ManifestParser parser(&local_state, NULL); |
593 | string err; |
594 | EXPECT_FALSE(parser.ParseTest("rule cat\n" |
595 | " command = ${fafsd\n" |
596 | "foo = bar\n" , |
597 | &err)); |
598 | EXPECT_EQ("input:2: bad $-escape (literal $ must be written as $$)\n" |
599 | " command = ${fafsd\n" |
600 | " ^ near here" |
601 | , err); |
602 | } |
603 | |
604 | |
605 | { |
606 | State local_state; |
607 | ManifestParser parser(&local_state, NULL); |
608 | string err; |
609 | EXPECT_FALSE(parser.ParseTest("rule cat\n" |
610 | " command = cat\n" |
611 | "build $.: cat foo\n" , |
612 | &err)); |
613 | EXPECT_EQ("input:3: bad $-escape (literal $ must be written as $$)\n" |
614 | "build $.: cat foo\n" |
615 | " ^ near here" |
616 | , err); |
617 | } |
618 | |
619 | |
620 | { |
621 | State local_state; |
622 | ManifestParser parser(&local_state, NULL); |
623 | string err; |
624 | EXPECT_FALSE(parser.ParseTest("rule cat\n" |
625 | " command = cat\n" |
626 | "build $: cat foo\n" , |
627 | &err)); |
628 | EXPECT_EQ("input:3: expected ':', got newline ($ also escapes ':')\n" |
629 | "build $: cat foo\n" |
630 | " ^ near here" |
631 | , err); |
632 | } |
633 | |
634 | { |
635 | State local_state; |
636 | ManifestParser parser(&local_state, NULL); |
637 | string err; |
638 | EXPECT_FALSE(parser.ParseTest("rule %foo\n" , |
639 | &err)); |
640 | EXPECT_EQ("input:1: expected rule name\n" |
641 | "rule %foo\n" |
642 | " ^ near here" , |
643 | err); |
644 | } |
645 | |
646 | { |
647 | State local_state; |
648 | ManifestParser parser(&local_state, NULL); |
649 | string err; |
650 | EXPECT_FALSE(parser.ParseTest("rule cc\n" |
651 | " command = foo\n" |
652 | " othervar = bar\n" , |
653 | &err)); |
654 | EXPECT_EQ("input:3: unexpected variable 'othervar'\n" |
655 | " othervar = bar\n" |
656 | " ^ near here" |
657 | , err); |
658 | } |
659 | |
660 | { |
661 | State local_state; |
662 | ManifestParser parser(&local_state, NULL); |
663 | string err; |
664 | EXPECT_FALSE(parser.ParseTest("rule cc\n command = foo\n" |
665 | "build $.: cc bar.cc\n" , |
666 | &err)); |
667 | EXPECT_EQ("input:3: bad $-escape (literal $ must be written as $$)\n" |
668 | "build $.: cc bar.cc\n" |
669 | " ^ near here" |
670 | , err); |
671 | } |
672 | |
673 | { |
674 | State local_state; |
675 | ManifestParser parser(&local_state, NULL); |
676 | string err; |
677 | EXPECT_FALSE(parser.ParseTest("rule cc\n command = foo\n && bar" , |
678 | &err)); |
679 | EXPECT_EQ("input:3: expected variable name\n" |
680 | " && bar\n" |
681 | " ^ near here" , |
682 | err); |
683 | } |
684 | |
685 | { |
686 | State local_state; |
687 | ManifestParser parser(&local_state, NULL); |
688 | string err; |
689 | EXPECT_FALSE(parser.ParseTest("rule cc\n command = foo\n" |
690 | "build $: cc bar.cc\n" , |
691 | &err)); |
692 | EXPECT_EQ("input:3: expected ':', got newline ($ also escapes ':')\n" |
693 | "build $: cc bar.cc\n" |
694 | " ^ near here" |
695 | , err); |
696 | } |
697 | |
698 | { |
699 | State local_state; |
700 | ManifestParser parser(&local_state, NULL); |
701 | string err; |
702 | EXPECT_FALSE(parser.ParseTest("default\n" , |
703 | &err)); |
704 | EXPECT_EQ("input:1: expected target name\n" |
705 | "default\n" |
706 | " ^ near here" |
707 | , err); |
708 | } |
709 | |
710 | { |
711 | State local_state; |
712 | ManifestParser parser(&local_state, NULL); |
713 | string err; |
714 | EXPECT_FALSE(parser.ParseTest("default nonexistent\n" , |
715 | &err)); |
716 | EXPECT_EQ("input:1: unknown target 'nonexistent'\n" |
717 | "default nonexistent\n" |
718 | " ^ near here" |
719 | , err); |
720 | } |
721 | |
722 | { |
723 | State local_state; |
724 | ManifestParser parser(&local_state, NULL); |
725 | string err; |
726 | EXPECT_FALSE(parser.ParseTest("rule r\n command = r\n" |
727 | "build b: r\n" |
728 | "default b:\n" , |
729 | &err)); |
730 | EXPECT_EQ("input:4: expected newline, got ':'\n" |
731 | "default b:\n" |
732 | " ^ near here" |
733 | , err); |
734 | } |
735 | |
736 | { |
737 | State local_state; |
738 | ManifestParser parser(&local_state, NULL); |
739 | string err; |
740 | EXPECT_FALSE(parser.ParseTest("default $a\n" , &err)); |
741 | EXPECT_EQ("input:1: empty path\n" |
742 | "default $a\n" |
743 | " ^ near here" |
744 | , err); |
745 | } |
746 | |
747 | { |
748 | State local_state; |
749 | ManifestParser parser(&local_state, NULL); |
750 | string err; |
751 | EXPECT_FALSE(parser.ParseTest("rule r\n" |
752 | " command = r\n" |
753 | "build $a: r $c\n" , &err)); |
754 | // XXX the line number is wrong; we should evaluate paths in ParseEdge |
755 | // as we see them, not after we've read them all! |
756 | EXPECT_EQ("input:4: empty path\n" , err); |
757 | } |
758 | |
759 | { |
760 | State local_state; |
761 | ManifestParser parser(&local_state, NULL); |
762 | string err; |
763 | // the indented blank line must terminate the rule |
764 | // this also verifies that "unexpected (token)" errors are correct |
765 | EXPECT_FALSE(parser.ParseTest("rule r\n" |
766 | " command = r\n" |
767 | " \n" |
768 | " generator = 1\n" , &err)); |
769 | EXPECT_EQ("input:4: unexpected indent\n" , err); |
770 | } |
771 | |
772 | { |
773 | State local_state; |
774 | ManifestParser parser(&local_state, NULL); |
775 | string err; |
776 | EXPECT_FALSE(parser.ParseTest("pool\n" , &err)); |
777 | EXPECT_EQ("input:1: expected pool name\n" |
778 | "pool\n" |
779 | " ^ near here" , err); |
780 | } |
781 | |
782 | { |
783 | State local_state; |
784 | ManifestParser parser(&local_state, NULL); |
785 | string err; |
786 | EXPECT_FALSE(parser.ParseTest("pool foo\n" , &err)); |
787 | EXPECT_EQ("input:2: expected 'depth =' line\n" , err); |
788 | } |
789 | |
790 | { |
791 | State local_state; |
792 | ManifestParser parser(&local_state, NULL); |
793 | string err; |
794 | EXPECT_FALSE(parser.ParseTest("pool foo\n" |
795 | " depth = 4\n" |
796 | "pool foo\n" , &err)); |
797 | EXPECT_EQ("input:3: duplicate pool 'foo'\n" |
798 | "pool foo\n" |
799 | " ^ near here" |
800 | , err); |
801 | } |
802 | |
803 | { |
804 | State local_state; |
805 | ManifestParser parser(&local_state, NULL); |
806 | string err; |
807 | EXPECT_FALSE(parser.ParseTest("pool foo\n" |
808 | " depth = -1\n" , &err)); |
809 | EXPECT_EQ("input:2: invalid pool depth\n" |
810 | " depth = -1\n" |
811 | " ^ near here" |
812 | , err); |
813 | } |
814 | |
815 | { |
816 | State local_state; |
817 | ManifestParser parser(&local_state, NULL); |
818 | string err; |
819 | EXPECT_FALSE(parser.ParseTest("pool foo\n" |
820 | " bar = 1\n" , &err)); |
821 | EXPECT_EQ("input:2: unexpected variable 'bar'\n" |
822 | " bar = 1\n" |
823 | " ^ near here" |
824 | , err); |
825 | } |
826 | |
827 | { |
828 | State local_state; |
829 | ManifestParser parser(&local_state, NULL); |
830 | string err; |
831 | // Pool names are dereferenced at edge parsing time. |
832 | EXPECT_FALSE(parser.ParseTest("rule run\n" |
833 | " command = echo\n" |
834 | " pool = unnamed_pool\n" |
835 | "build out: run in\n" , &err)); |
836 | EXPECT_EQ("input:5: unknown pool name 'unnamed_pool'\n" , err); |
837 | } |
838 | } |
839 | |
840 | TEST_F(ParserTest, MissingInput) { |
841 | State local_state; |
842 | ManifestParser parser(&local_state, &fs_); |
843 | string err; |
844 | EXPECT_FALSE(parser.Load("build.ninja" , &err)); |
845 | EXPECT_EQ("loading 'build.ninja': No such file or directory" , err); |
846 | } |
847 | |
848 | TEST_F(ParserTest, MultipleOutputs) { |
849 | State local_state; |
850 | ManifestParser parser(&local_state, NULL); |
851 | string err; |
852 | EXPECT_TRUE(parser.ParseTest("rule cc\n command = foo\n depfile = bar\n" |
853 | "build a.o b.o: cc c.cc\n" , |
854 | &err)); |
855 | EXPECT_EQ("" , err); |
856 | } |
857 | |
858 | TEST_F(ParserTest, MultipleOutputsWithDeps) { |
859 | State local_state; |
860 | ManifestParser parser(&local_state, NULL); |
861 | string err; |
862 | EXPECT_TRUE(parser.ParseTest("rule cc\n command = foo\n deps = gcc\n" |
863 | "build a.o b.o: cc c.cc\n" , |
864 | &err)); |
865 | EXPECT_EQ("" , err); |
866 | } |
867 | |
868 | TEST_F(ParserTest, SubNinja) { |
869 | fs_.Create("test.ninja" , |
870 | "var = inner\n" |
871 | "build $builddir/inner: varref\n" ); |
872 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
873 | "builddir = some_dir/\n" |
874 | "rule varref\n" |
875 | " command = varref $var\n" |
876 | "var = outer\n" |
877 | "build $builddir/outer: varref\n" |
878 | "subninja test.ninja\n" |
879 | "build $builddir/outer2: varref\n" )); |
880 | ASSERT_EQ(1u, fs_.files_read_.size()); |
881 | |
882 | EXPECT_EQ("test.ninja" , fs_.files_read_[0]); |
883 | EXPECT_TRUE(state.LookupNode("some_dir/outer" )); |
884 | // Verify our builddir setting is inherited. |
885 | EXPECT_TRUE(state.LookupNode("some_dir/inner" )); |
886 | |
887 | ASSERT_EQ(3u, state.edges_.size()); |
888 | EXPECT_EQ("varref outer" , state.edges_[0]->EvaluateCommand()); |
889 | EXPECT_EQ("varref inner" , state.edges_[1]->EvaluateCommand()); |
890 | EXPECT_EQ("varref outer" , state.edges_[2]->EvaluateCommand()); |
891 | } |
892 | |
893 | TEST_F(ParserTest, MissingSubNinja) { |
894 | ManifestParser parser(&state, &fs_); |
895 | string err; |
896 | EXPECT_FALSE(parser.ParseTest("subninja foo.ninja\n" , &err)); |
897 | EXPECT_EQ("input:1: loading 'foo.ninja': No such file or directory\n" |
898 | "subninja foo.ninja\n" |
899 | " ^ near here" |
900 | , err); |
901 | } |
902 | |
903 | TEST_F(ParserTest, DuplicateRuleInDifferentSubninjas) { |
904 | // Test that rules are scoped to subninjas. |
905 | fs_.Create("test.ninja" , "rule cat\n" |
906 | " command = cat\n" ); |
907 | ManifestParser parser(&state, &fs_); |
908 | string err; |
909 | EXPECT_TRUE(parser.ParseTest("rule cat\n" |
910 | " command = cat\n" |
911 | "subninja test.ninja\n" , &err)); |
912 | } |
913 | |
914 | TEST_F(ParserTest, DuplicateRuleInDifferentSubninjasWithInclude) { |
915 | // Test that rules are scoped to subninjas even with includes. |
916 | fs_.Create("rules.ninja" , "rule cat\n" |
917 | " command = cat\n" ); |
918 | fs_.Create("test.ninja" , "include rules.ninja\n" |
919 | "build x : cat\n" ); |
920 | ManifestParser parser(&state, &fs_); |
921 | string err; |
922 | EXPECT_TRUE(parser.ParseTest("include rules.ninja\n" |
923 | "subninja test.ninja\n" |
924 | "build y : cat\n" , &err)); |
925 | } |
926 | |
927 | TEST_F(ParserTest, Include) { |
928 | fs_.Create("include.ninja" , "var = inner\n" ); |
929 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
930 | "var = outer\n" |
931 | "include include.ninja\n" )); |
932 | |
933 | ASSERT_EQ(1u, fs_.files_read_.size()); |
934 | EXPECT_EQ("include.ninja" , fs_.files_read_[0]); |
935 | EXPECT_EQ("inner" , state.bindings_.LookupVariable("var" )); |
936 | } |
937 | |
938 | TEST_F(ParserTest, BrokenInclude) { |
939 | fs_.Create("include.ninja" , "build\n" ); |
940 | ManifestParser parser(&state, &fs_); |
941 | string err; |
942 | EXPECT_FALSE(parser.ParseTest("include include.ninja\n" , &err)); |
943 | EXPECT_EQ("include.ninja:1: expected path\n" |
944 | "build\n" |
945 | " ^ near here" |
946 | , err); |
947 | } |
948 | |
949 | TEST_F(ParserTest, Implicit) { |
950 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
951 | "rule cat\n" |
952 | " command = cat $in > $out\n" |
953 | "build foo: cat bar | baz\n" )); |
954 | |
955 | Edge* edge = state.LookupNode("foo" )->in_edge(); |
956 | ASSERT_TRUE(edge->is_implicit(1)); |
957 | } |
958 | |
959 | TEST_F(ParserTest, OrderOnly) { |
960 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
961 | "rule cat\n command = cat $in > $out\n" |
962 | "build foo: cat bar || baz\n" )); |
963 | |
964 | Edge* edge = state.LookupNode("foo" )->in_edge(); |
965 | ASSERT_TRUE(edge->is_order_only(1)); |
966 | } |
967 | |
968 | TEST_F(ParserTest, Validations) { |
969 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
970 | "rule cat\n command = cat $in > $out\n" |
971 | "build foo: cat bar |@ baz\n" )); |
972 | |
973 | Edge* edge = state.LookupNode("foo" )->in_edge(); |
974 | ASSERT_EQ(edge->validations_.size(), 1); |
975 | EXPECT_EQ(edge->validations_[0]->path(), "baz" ); |
976 | } |
977 | |
978 | TEST_F(ParserTest, ImplicitOutput) { |
979 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
980 | "rule cat\n" |
981 | " command = cat $in > $out\n" |
982 | "build foo | imp: cat bar\n" )); |
983 | |
984 | Edge* edge = state.LookupNode("imp" )->in_edge(); |
985 | ASSERT_EQ(edge->outputs_.size(), 2); |
986 | EXPECT_TRUE(edge->is_implicit_out(1)); |
987 | } |
988 | |
989 | TEST_F(ParserTest, ImplicitOutputEmpty) { |
990 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
991 | "rule cat\n" |
992 | " command = cat $in > $out\n" |
993 | "build foo | : cat bar\n" )); |
994 | |
995 | Edge* edge = state.LookupNode("foo" )->in_edge(); |
996 | ASSERT_EQ(edge->outputs_.size(), 1); |
997 | EXPECT_FALSE(edge->is_implicit_out(0)); |
998 | } |
999 | |
1000 | TEST_F(ParserTest, ImplicitOutputDupe) { |
1001 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
1002 | "rule cat\n" |
1003 | " command = cat $in > $out\n" |
1004 | "build foo baz | foo baq foo: cat bar\n" )); |
1005 | |
1006 | Edge* edge = state.LookupNode("foo" )->in_edge(); |
1007 | ASSERT_EQ(edge->outputs_.size(), 3); |
1008 | EXPECT_FALSE(edge->is_implicit_out(0)); |
1009 | EXPECT_FALSE(edge->is_implicit_out(1)); |
1010 | EXPECT_TRUE(edge->is_implicit_out(2)); |
1011 | } |
1012 | |
1013 | TEST_F(ParserTest, ImplicitOutputDupes) { |
1014 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
1015 | "rule cat\n" |
1016 | " command = cat $in > $out\n" |
1017 | "build foo foo foo | foo foo foo foo: cat bar\n" )); |
1018 | |
1019 | Edge* edge = state.LookupNode("foo" )->in_edge(); |
1020 | ASSERT_EQ(edge->outputs_.size(), 1); |
1021 | EXPECT_FALSE(edge->is_implicit_out(0)); |
1022 | } |
1023 | |
1024 | TEST_F(ParserTest, NoExplicitOutput) { |
1025 | ManifestParser parser(&state, NULL); |
1026 | string err; |
1027 | EXPECT_TRUE(parser.ParseTest( |
1028 | "rule cat\n" |
1029 | " command = cat $in > $out\n" |
1030 | "build | imp : cat bar\n" , &err)); |
1031 | } |
1032 | |
1033 | TEST_F(ParserTest, DefaultDefault) { |
1034 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
1035 | "rule cat\n command = cat $in > $out\n" |
1036 | "build a: cat foo\n" |
1037 | "build b: cat foo\n" |
1038 | "build c: cat foo\n" |
1039 | "build d: cat foo\n" )); |
1040 | |
1041 | string err; |
1042 | EXPECT_EQ(4u, state.DefaultNodes(&err).size()); |
1043 | EXPECT_EQ("" , err); |
1044 | } |
1045 | |
1046 | TEST_F(ParserTest, DefaultDefaultCycle) { |
1047 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
1048 | "rule cat\n command = cat $in > $out\n" |
1049 | "build a: cat a\n" )); |
1050 | |
1051 | string err; |
1052 | EXPECT_EQ(0u, state.DefaultNodes(&err).size()); |
1053 | EXPECT_EQ("could not determine root nodes of build graph" , err); |
1054 | } |
1055 | |
1056 | TEST_F(ParserTest, DefaultStatements) { |
1057 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
1058 | "rule cat\n command = cat $in > $out\n" |
1059 | "build a: cat foo\n" |
1060 | "build b: cat foo\n" |
1061 | "build c: cat foo\n" |
1062 | "build d: cat foo\n" |
1063 | "third = c\n" |
1064 | "default a b\n" |
1065 | "default $third\n" )); |
1066 | |
1067 | string err; |
1068 | vector<Node*> nodes = state.DefaultNodes(&err); |
1069 | EXPECT_EQ("" , err); |
1070 | ASSERT_EQ(3u, nodes.size()); |
1071 | EXPECT_EQ("a" , nodes[0]->path()); |
1072 | EXPECT_EQ("b" , nodes[1]->path()); |
1073 | EXPECT_EQ("c" , nodes[2]->path()); |
1074 | } |
1075 | |
1076 | TEST_F(ParserTest, UTF8) { |
1077 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
1078 | "rule utf8\n" |
1079 | " command = true\n" |
1080 | " description = compilaci\xC3\xB3\n" )); |
1081 | } |
1082 | |
1083 | TEST_F(ParserTest, CRLF) { |
1084 | State local_state; |
1085 | ManifestParser parser(&local_state, NULL); |
1086 | string err; |
1087 | |
1088 | EXPECT_TRUE(parser.ParseTest("# comment with crlf\r\n" , &err)); |
1089 | EXPECT_TRUE(parser.ParseTest("foo = foo\nbar = bar\r\n" , &err)); |
1090 | EXPECT_TRUE(parser.ParseTest( |
1091 | "pool link_pool\r\n" |
1092 | " depth = 15\r\n\r\n" |
1093 | "rule xyz\r\n" |
1094 | " command = something$expand \r\n" |
1095 | " description = YAY!\r\n" , |
1096 | &err)); |
1097 | } |
1098 | |
1099 | TEST_F(ParserTest, DyndepNotSpecified) { |
1100 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
1101 | "rule cat\n" |
1102 | " command = cat $in > $out\n" |
1103 | "build result: cat in\n" )); |
1104 | Edge* edge = state.GetNode("result" , 0)->in_edge(); |
1105 | ASSERT_FALSE(edge->dyndep_); |
1106 | } |
1107 | |
1108 | TEST_F(ParserTest, DyndepNotInput) { |
1109 | State lstate; |
1110 | ManifestParser parser(&lstate, NULL); |
1111 | string err; |
1112 | EXPECT_FALSE(parser.ParseTest( |
1113 | "rule touch\n" |
1114 | " command = touch $out\n" |
1115 | "build result: touch\n" |
1116 | " dyndep = notin\n" , |
1117 | &err)); |
1118 | EXPECT_EQ("input:5: dyndep 'notin' is not an input\n" , err); |
1119 | } |
1120 | |
1121 | TEST_F(ParserTest, DyndepExplicitInput) { |
1122 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
1123 | "rule cat\n" |
1124 | " command = cat $in > $out\n" |
1125 | "build result: cat in\n" |
1126 | " dyndep = in\n" )); |
1127 | Edge* edge = state.GetNode("result" , 0)->in_edge(); |
1128 | ASSERT_TRUE(edge->dyndep_); |
1129 | EXPECT_TRUE(edge->dyndep_->dyndep_pending()); |
1130 | EXPECT_EQ(edge->dyndep_->path(), "in" ); |
1131 | } |
1132 | |
1133 | TEST_F(ParserTest, DyndepImplicitInput) { |
1134 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
1135 | "rule cat\n" |
1136 | " command = cat $in > $out\n" |
1137 | "build result: cat in | dd\n" |
1138 | " dyndep = dd\n" )); |
1139 | Edge* edge = state.GetNode("result" , 0)->in_edge(); |
1140 | ASSERT_TRUE(edge->dyndep_); |
1141 | EXPECT_TRUE(edge->dyndep_->dyndep_pending()); |
1142 | EXPECT_EQ(edge->dyndep_->path(), "dd" ); |
1143 | } |
1144 | |
1145 | TEST_F(ParserTest, DyndepOrderOnlyInput) { |
1146 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
1147 | "rule cat\n" |
1148 | " command = cat $in > $out\n" |
1149 | "build result: cat in || dd\n" |
1150 | " dyndep = dd\n" )); |
1151 | Edge* edge = state.GetNode("result" , 0)->in_edge(); |
1152 | ASSERT_TRUE(edge->dyndep_); |
1153 | EXPECT_TRUE(edge->dyndep_->dyndep_pending()); |
1154 | EXPECT_EQ(edge->dyndep_->path(), "dd" ); |
1155 | } |
1156 | |
1157 | TEST_F(ParserTest, DyndepRuleInput) { |
1158 | ASSERT_NO_FATAL_FAILURE(AssertParse( |
1159 | "rule cat\n" |
1160 | " command = cat $in > $out\n" |
1161 | " dyndep = $in\n" |
1162 | "build result: cat in\n" )); |
1163 | Edge* edge = state.GetNode("result" , 0)->in_edge(); |
1164 | ASSERT_TRUE(edge->dyndep_); |
1165 | EXPECT_TRUE(edge->dyndep_->dyndep_pending()); |
1166 | EXPECT_EQ(edge->dyndep_->path(), "in" ); |
1167 | } |
1168 | |