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 "depfile_parser.h"
16 
17 #include "test.h"
18 
19 using namespace std;
20 
21 struct DepfileParserTest : public testing::Test {
22   bool Parse(const char* input, string* err);
23 
24   DepfileParser parser_;
25   string input_;
26 };
27 
Parse(const char * input,string * err)28 bool DepfileParserTest::Parse(const char* input, string* err) {
29   input_ = input;
30   return parser_.Parse(&input_, err);
31 }
32 
TEST_F(DepfileParserTest,Basic)33 TEST_F(DepfileParserTest, Basic) {
34   string err;
35   EXPECT_TRUE(Parse(
36 "build/ninja.o: ninja.cc ninja.h eval_env.h manifest_parser.h\n",
37       &err));
38   ASSERT_EQ("", err);
39   ASSERT_EQ(1u, parser_.outs_.size());
40   EXPECT_EQ("build/ninja.o", parser_.outs_[0].AsString());
41   EXPECT_EQ(4u, parser_.ins_.size());
42 }
43 
TEST_F(DepfileParserTest,EarlyNewlineAndWhitespace)44 TEST_F(DepfileParserTest, EarlyNewlineAndWhitespace) {
45   string err;
46   EXPECT_TRUE(Parse(
47 " \\\n"
48 "  out: in\n",
49       &err));
50   ASSERT_EQ("", err);
51 }
52 
TEST_F(DepfileParserTest,Continuation)53 TEST_F(DepfileParserTest, Continuation) {
54   string err;
55   EXPECT_TRUE(Parse(
56 "foo.o: \\\n"
57 "  bar.h baz.h\n",
58       &err));
59   ASSERT_EQ("", err);
60   ASSERT_EQ(1u, parser_.outs_.size());
61   EXPECT_EQ("foo.o", parser_.outs_[0].AsString());
62   EXPECT_EQ(2u, parser_.ins_.size());
63 }
64 
TEST_F(DepfileParserTest,CarriageReturnContinuation)65 TEST_F(DepfileParserTest, CarriageReturnContinuation) {
66   string err;
67   EXPECT_TRUE(Parse(
68 "foo.o: \\\r\n"
69 "  bar.h baz.h\r\n",
70       &err));
71   ASSERT_EQ("", err);
72   ASSERT_EQ(1u, parser_.outs_.size());
73   EXPECT_EQ("foo.o", parser_.outs_[0].AsString());
74   EXPECT_EQ(2u, parser_.ins_.size());
75 }
76 
TEST_F(DepfileParserTest,BackSlashes)77 TEST_F(DepfileParserTest, BackSlashes) {
78   string err;
79   EXPECT_TRUE(Parse(
80 "Project\\Dir\\Build\\Release8\\Foo\\Foo.res : \\\n"
81 "  Dir\\Library\\Foo.rc \\\n"
82 "  Dir\\Library\\Version\\Bar.h \\\n"
83 "  Dir\\Library\\Foo.ico \\\n"
84 "  Project\\Thing\\Bar.tlb \\\n",
85       &err));
86   ASSERT_EQ("", err);
87   ASSERT_EQ(1u, parser_.outs_.size());
88   EXPECT_EQ("Project\\Dir\\Build\\Release8\\Foo\\Foo.res",
89             parser_.outs_[0].AsString());
90   EXPECT_EQ(4u, parser_.ins_.size());
91 }
92 
TEST_F(DepfileParserTest,Spaces)93 TEST_F(DepfileParserTest, Spaces) {
94   string err;
95   EXPECT_TRUE(Parse(
96 "a\\ bc\\ def:   a\\ b c d",
97       &err));
98   ASSERT_EQ("", err);
99   ASSERT_EQ(1u, parser_.outs_.size());
100   EXPECT_EQ("a bc def",
101             parser_.outs_[0].AsString());
102   ASSERT_EQ(3u, parser_.ins_.size());
103   EXPECT_EQ("a b",
104             parser_.ins_[0].AsString());
105   EXPECT_EQ("c",
106             parser_.ins_[1].AsString());
107   EXPECT_EQ("d",
108             parser_.ins_[2].AsString());
109 }
110 
TEST_F(DepfileParserTest,MultipleBackslashes)111 TEST_F(DepfileParserTest, MultipleBackslashes) {
112   // Successive 2N+1 backslashes followed by space (' ') are replaced by N >= 0
113   // backslashes and the space. A single backslash before hash sign is removed.
114   // Other backslashes remain untouched (including 2N backslashes followed by
115   // space).
116   string err;
117   EXPECT_TRUE(Parse(
118 "a\\ b\\#c.h: \\\\\\\\\\  \\\\\\\\ \\\\share\\info\\\\#1",
119       &err));
120   ASSERT_EQ("", err);
121   ASSERT_EQ(1u, parser_.outs_.size());
122   EXPECT_EQ("a b#c.h",
123             parser_.outs_[0].AsString());
124   ASSERT_EQ(3u, parser_.ins_.size());
125   EXPECT_EQ("\\\\ ",
126             parser_.ins_[0].AsString());
127   EXPECT_EQ("\\\\\\\\",
128             parser_.ins_[1].AsString());
129   EXPECT_EQ("\\\\share\\info\\#1",
130             parser_.ins_[2].AsString());
131 }
132 
TEST_F(DepfileParserTest,Escapes)133 TEST_F(DepfileParserTest, Escapes) {
134   // Put backslashes before a variety of characters, see which ones make
135   // it through.
136   string err;
137   EXPECT_TRUE(Parse(
138 "\\!\\@\\#$$\\%\\^\\&\\[\\]\\\\:",
139       &err));
140   ASSERT_EQ("", err);
141   ASSERT_EQ(1u, parser_.outs_.size());
142   EXPECT_EQ("\\!\\@#$\\%\\^\\&\\[\\]\\\\",
143             parser_.outs_[0].AsString());
144   ASSERT_EQ(0u, parser_.ins_.size());
145 }
146 
TEST_F(DepfileParserTest,EscapedColons)147 TEST_F(DepfileParserTest, EscapedColons)
148 {
149   std::string err;
150   // Tests for correct parsing of depfiles produced on Windows
151   // by both Clang, GCC pre 10 and GCC 10
152   EXPECT_TRUE(Parse(
153 "c\\:\\gcc\\x86_64-w64-mingw32\\include\\stddef.o: \\\n"
154 " c:\\gcc\\x86_64-w64-mingw32\\include\\stddef.h \n",
155       &err));
156   ASSERT_EQ("", err);
157   ASSERT_EQ(1u, parser_.outs_.size());
158   EXPECT_EQ("c:\\gcc\\x86_64-w64-mingw32\\include\\stddef.o",
159             parser_.outs_[0].AsString());
160   ASSERT_EQ(1u, parser_.ins_.size());
161   EXPECT_EQ("c:\\gcc\\x86_64-w64-mingw32\\include\\stddef.h",
162             parser_.ins_[0].AsString());
163 }
164 
TEST_F(DepfileParserTest,EscapedTargetColon)165 TEST_F(DepfileParserTest, EscapedTargetColon)
166 {
167   std::string err;
168   EXPECT_TRUE(Parse(
169 "foo1\\: x\n"
170 "foo1\\:\n"
171 "foo1\\:\r\n"
172 "foo1\\:\t\n"
173 "foo1\\:",
174       &err));
175   ASSERT_EQ("", err);
176   ASSERT_EQ(1u, parser_.outs_.size());
177   EXPECT_EQ("foo1\\", parser_.outs_[0].AsString());
178   ASSERT_EQ(1u, parser_.ins_.size());
179   EXPECT_EQ("x", parser_.ins_[0].AsString());
180 }
181 
TEST_F(DepfileParserTest,SpecialChars)182 TEST_F(DepfileParserTest, SpecialChars) {
183   // See filenames like istreambuf.iterator_op!= in
184   // https://github.com/google/libcxx/tree/master/test/iterators/stream.iterators/istreambuf.iterator/
185   string err;
186   EXPECT_TRUE(Parse(
187 "C:/Program\\ Files\\ (x86)/Microsoft\\ crtdefs.h: \\\n"
188 " en@quot.header~ t+t-x!=1 \\\n"
189 " openldap/slapd.d/cn=config/cn=schema/cn={0}core.ldif\\\n"
190 " Fu\303\244ball\\\n"
191 " a[1]b@2%c",
192       &err));
193   ASSERT_EQ("", err);
194   ASSERT_EQ(1u, parser_.outs_.size());
195   EXPECT_EQ("C:/Program Files (x86)/Microsoft crtdefs.h",
196             parser_.outs_[0].AsString());
197   ASSERT_EQ(5u, parser_.ins_.size());
198   EXPECT_EQ("en@quot.header~",
199             parser_.ins_[0].AsString());
200   EXPECT_EQ("t+t-x!=1",
201             parser_.ins_[1].AsString());
202   EXPECT_EQ("openldap/slapd.d/cn=config/cn=schema/cn={0}core.ldif",
203             parser_.ins_[2].AsString());
204   EXPECT_EQ("Fu\303\244ball",
205             parser_.ins_[3].AsString());
206   EXPECT_EQ("a[1]b@2%c",
207             parser_.ins_[4].AsString());
208 }
209 
TEST_F(DepfileParserTest,UnifyMultipleOutputs)210 TEST_F(DepfileParserTest, UnifyMultipleOutputs) {
211   // check that multiple duplicate targets are properly unified
212   string err;
213   EXPECT_TRUE(Parse("foo foo: x y z", &err));
214   ASSERT_EQ(1u, parser_.outs_.size());
215   ASSERT_EQ("foo", parser_.outs_[0].AsString());
216   ASSERT_EQ(3u, parser_.ins_.size());
217   EXPECT_EQ("x", parser_.ins_[0].AsString());
218   EXPECT_EQ("y", parser_.ins_[1].AsString());
219   EXPECT_EQ("z", parser_.ins_[2].AsString());
220 }
221 
TEST_F(DepfileParserTest,MultipleDifferentOutputs)222 TEST_F(DepfileParserTest, MultipleDifferentOutputs) {
223   // check that multiple different outputs are accepted by the parser
224   string err;
225   EXPECT_TRUE(Parse("foo bar: x y z", &err));
226   ASSERT_EQ(2u, parser_.outs_.size());
227   ASSERT_EQ("foo", parser_.outs_[0].AsString());
228   ASSERT_EQ("bar", parser_.outs_[1].AsString());
229   ASSERT_EQ(3u, parser_.ins_.size());
230   EXPECT_EQ("x", parser_.ins_[0].AsString());
231   EXPECT_EQ("y", parser_.ins_[1].AsString());
232   EXPECT_EQ("z", parser_.ins_[2].AsString());
233 }
234 
TEST_F(DepfileParserTest,MultipleEmptyRules)235 TEST_F(DepfileParserTest, MultipleEmptyRules) {
236   string err;
237   EXPECT_TRUE(Parse("foo: x\n"
238                     "foo: \n"
239                     "foo:\n", &err));
240   ASSERT_EQ(1u, parser_.outs_.size());
241   ASSERT_EQ("foo", parser_.outs_[0].AsString());
242   ASSERT_EQ(1u, parser_.ins_.size());
243   EXPECT_EQ("x", parser_.ins_[0].AsString());
244 }
245 
TEST_F(DepfileParserTest,UnifyMultipleRulesLF)246 TEST_F(DepfileParserTest, UnifyMultipleRulesLF) {
247   string err;
248   EXPECT_TRUE(Parse("foo: x\n"
249                     "foo: y\n"
250                     "foo \\\n"
251                     "foo: z\n", &err));
252   ASSERT_EQ(1u, parser_.outs_.size());
253   ASSERT_EQ("foo", parser_.outs_[0].AsString());
254   ASSERT_EQ(3u, parser_.ins_.size());
255   EXPECT_EQ("x", parser_.ins_[0].AsString());
256   EXPECT_EQ("y", parser_.ins_[1].AsString());
257   EXPECT_EQ("z", parser_.ins_[2].AsString());
258 }
259 
TEST_F(DepfileParserTest,UnifyMultipleRulesCRLF)260 TEST_F(DepfileParserTest, UnifyMultipleRulesCRLF) {
261   string err;
262   EXPECT_TRUE(Parse("foo: x\r\n"
263                     "foo: y\r\n"
264                     "foo \\\r\n"
265                     "foo: z\r\n", &err));
266   ASSERT_EQ(1u, parser_.outs_.size());
267   ASSERT_EQ("foo", parser_.outs_[0].AsString());
268   ASSERT_EQ(3u, parser_.ins_.size());
269   EXPECT_EQ("x", parser_.ins_[0].AsString());
270   EXPECT_EQ("y", parser_.ins_[1].AsString());
271   EXPECT_EQ("z", parser_.ins_[2].AsString());
272 }
273 
TEST_F(DepfileParserTest,UnifyMixedRulesLF)274 TEST_F(DepfileParserTest, UnifyMixedRulesLF) {
275   string err;
276   EXPECT_TRUE(Parse("foo: x\\\n"
277                     "     y\n"
278                     "foo \\\n"
279                     "foo: z\n", &err));
280   ASSERT_EQ(1u, parser_.outs_.size());
281   ASSERT_EQ("foo", parser_.outs_[0].AsString());
282   ASSERT_EQ(3u, parser_.ins_.size());
283   EXPECT_EQ("x", parser_.ins_[0].AsString());
284   EXPECT_EQ("y", parser_.ins_[1].AsString());
285   EXPECT_EQ("z", parser_.ins_[2].AsString());
286 }
287 
TEST_F(DepfileParserTest,UnifyMixedRulesCRLF)288 TEST_F(DepfileParserTest, UnifyMixedRulesCRLF) {
289   string err;
290   EXPECT_TRUE(Parse("foo: x\\\r\n"
291                     "     y\r\n"
292                     "foo \\\r\n"
293                     "foo: z\r\n", &err));
294   ASSERT_EQ(1u, parser_.outs_.size());
295   ASSERT_EQ("foo", parser_.outs_[0].AsString());
296   ASSERT_EQ(3u, parser_.ins_.size());
297   EXPECT_EQ("x", parser_.ins_[0].AsString());
298   EXPECT_EQ("y", parser_.ins_[1].AsString());
299   EXPECT_EQ("z", parser_.ins_[2].AsString());
300 }
301 
TEST_F(DepfileParserTest,IndentedRulesLF)302 TEST_F(DepfileParserTest, IndentedRulesLF) {
303   string err;
304   EXPECT_TRUE(Parse(" foo: x\n"
305                     " foo: y\n"
306                     " foo: z\n", &err));
307   ASSERT_EQ(1u, parser_.outs_.size());
308   ASSERT_EQ("foo", parser_.outs_[0].AsString());
309   ASSERT_EQ(3u, parser_.ins_.size());
310   EXPECT_EQ("x", parser_.ins_[0].AsString());
311   EXPECT_EQ("y", parser_.ins_[1].AsString());
312   EXPECT_EQ("z", parser_.ins_[2].AsString());
313 }
314 
TEST_F(DepfileParserTest,IndentedRulesCRLF)315 TEST_F(DepfileParserTest, IndentedRulesCRLF) {
316   string err;
317   EXPECT_TRUE(Parse(" foo: x\r\n"
318                     " foo: y\r\n"
319                     " foo: z\r\n", &err));
320   ASSERT_EQ(1u, parser_.outs_.size());
321   ASSERT_EQ("foo", parser_.outs_[0].AsString());
322   ASSERT_EQ(3u, parser_.ins_.size());
323   EXPECT_EQ("x", parser_.ins_[0].AsString());
324   EXPECT_EQ("y", parser_.ins_[1].AsString());
325   EXPECT_EQ("z", parser_.ins_[2].AsString());
326 }
327 
TEST_F(DepfileParserTest,TolerateMP)328 TEST_F(DepfileParserTest, TolerateMP) {
329   string err;
330   EXPECT_TRUE(Parse("foo: x y z\n"
331                     "x:\n"
332                     "y:\n"
333                     "z:\n", &err));
334   ASSERT_EQ(1u, parser_.outs_.size());
335   ASSERT_EQ("foo", parser_.outs_[0].AsString());
336   ASSERT_EQ(3u, parser_.ins_.size());
337   EXPECT_EQ("x", parser_.ins_[0].AsString());
338   EXPECT_EQ("y", parser_.ins_[1].AsString());
339   EXPECT_EQ("z", parser_.ins_[2].AsString());
340 }
341 
TEST_F(DepfileParserTest,MultipleRulesTolerateMP)342 TEST_F(DepfileParserTest, MultipleRulesTolerateMP) {
343   string err;
344   EXPECT_TRUE(Parse("foo: x\n"
345                     "x:\n"
346                     "foo: y\n"
347                     "y:\n"
348                     "foo: z\n"
349                     "z:\n", &err));
350   ASSERT_EQ(1u, parser_.outs_.size());
351   ASSERT_EQ("foo", parser_.outs_[0].AsString());
352   ASSERT_EQ(3u, parser_.ins_.size());
353   EXPECT_EQ("x", parser_.ins_[0].AsString());
354   EXPECT_EQ("y", parser_.ins_[1].AsString());
355   EXPECT_EQ("z", parser_.ins_[2].AsString());
356 }
357 
TEST_F(DepfileParserTest,MultipleRulesDifferentOutputs)358 TEST_F(DepfileParserTest, MultipleRulesDifferentOutputs) {
359   // check that multiple different outputs are accepted by the parser
360   // when spread across multiple rules
361   string err;
362   EXPECT_TRUE(Parse("foo: x y\n"
363                     "bar: y z\n", &err));
364   ASSERT_EQ(2u, parser_.outs_.size());
365   ASSERT_EQ("foo", parser_.outs_[0].AsString());
366   ASSERT_EQ("bar", parser_.outs_[1].AsString());
367   ASSERT_EQ(3u, parser_.ins_.size());
368   EXPECT_EQ("x", parser_.ins_[0].AsString());
369   EXPECT_EQ("y", parser_.ins_[1].AsString());
370   EXPECT_EQ("z", parser_.ins_[2].AsString());
371 }
372 
TEST_F(DepfileParserTest,BuggyMP)373 TEST_F(DepfileParserTest, BuggyMP) {
374   std::string err;
375   EXPECT_FALSE(Parse("foo: x y z\n"
376                      "x: alsoin\n"
377                      "y:\n"
378                      "z:\n", &err));
379   ASSERT_EQ("inputs may not also have inputs", err);
380 }
381