• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <algorithm>
6 #include <sstream>
7 
8 #include "gn/ninja_action_target_writer.h"
9 #include "gn/pool.h"
10 #include "gn/substitution_list.h"
11 #include "gn/target.h"
12 #include "gn/test_with_scope.h"
13 #include "util/build_config.h"
14 #include "util/test/test.h"
15 
TEST(NinjaActionTargetWriter,WriteOutputFilesForBuildLine)16 TEST(NinjaActionTargetWriter, WriteOutputFilesForBuildLine) {
17   Err err;
18   TestWithScope setup;
19 
20   Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
21   target.set_output_type(Target::ACTION_FOREACH);
22   target.action_values().outputs() =
23       SubstitutionList::MakeForTest("//out/Debug/gen/a b{{source_name_part}}.h",
24                                     "//out/Debug/gen/{{source_name_part}}.cc");
25 
26   target.SetToolchain(setup.toolchain());
27   ASSERT_TRUE(target.OnResolved(&err));
28 
29   std::ostringstream out;
30   NinjaActionTargetWriter writer(&target, out);
31 
32   SourceFile source("//foo/bar.in");
33   std::vector<OutputFile> output_files;
34   writer.WriteOutputFilesForBuildLine(source, &output_files);
35 
36   EXPECT_EQ(" gen/a$ bbar.h gen/bar.cc", out.str());
37 }
38 
39 // Tests an action with no sources.
TEST(NinjaActionTargetWriter,ActionNoSources)40 TEST(NinjaActionTargetWriter, ActionNoSources) {
41   Err err;
42   TestWithScope setup;
43 
44   Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
45   target.set_output_type(Target::ACTION);
46 
47   target.action_values().set_script(SourceFile("//foo/script.py"));
48   target.config_values().inputs().push_back(SourceFile("//foo/included.txt"));
49 
50   target.action_values().outputs() =
51       SubstitutionList::MakeForTest("//out/Debug/foo.out");
52 
53   target.SetToolchain(setup.toolchain());
54   ASSERT_TRUE(target.OnResolved(&err));
55 
56   setup.build_settings()->set_python_path(
57       base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
58 
59   std::ostringstream out;
60   NinjaActionTargetWriter writer(&target, out);
61   writer.Run();
62 
63   const char* expected = R"(rule __foo_bar___rule
64   command = /usr/bin/python ../../foo/script.py
65   description = ACTION //foo:bar()
66   restat = 1
67 
68 build foo.out: __foo_bar___rule | ../../foo/script.py ../../foo/included.txt
69 
70 build obj/foo/bar.stamp: stamp foo.out
71 )";
72   EXPECT_EQ(expected, out.str()) << expected << "--" << out.str();
73 }
74 
75 // Tests an action with no sources and pool
TEST(NinjaActionTargetWriter,ActionNoSourcesConsole)76 TEST(NinjaActionTargetWriter, ActionNoSourcesConsole) {
77   Err err;
78   TestWithScope setup;
79 
80   Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
81   target.set_output_type(Target::ACTION);
82 
83   target.action_values().set_script(SourceFile("//foo/script.py"));
84   target.config_values().inputs().push_back(SourceFile("//foo/included.txt"));
85 
86   target.action_values().outputs() =
87       SubstitutionList::MakeForTest("//out/Debug/foo.out");
88 
89   Pool pool(setup.settings(),
90             Label(SourceDir("//"), "console", setup.toolchain()->label().dir(),
91                   setup.toolchain()->label().name()));
92   pool.set_depth(1);
93   target.action_values().set_pool(LabelPtrPair<Pool>(&pool));
94 
95   target.SetToolchain(setup.toolchain());
96   ASSERT_TRUE(target.OnResolved(&err));
97 
98   setup.build_settings()->set_python_path(
99       base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
100 
101   std::ostringstream out;
102   NinjaActionTargetWriter writer(&target, out);
103   writer.Run();
104 
105   // The console pool's name must be mapped exactly to the string "console"
106   // which is a special pre-defined pool name in ninja.
107   const char* expected = R"(rule __foo_bar___rule
108   command = /usr/bin/python ../../foo/script.py
109   description = ACTION //foo:bar()
110   restat = 1
111 
112 build foo.out: __foo_bar___rule | ../../foo/script.py ../../foo/included.txt
113   pool = console
114 
115 build obj/foo/bar.stamp: stamp foo.out
116 )";
117   EXPECT_EQ(expected, out.str());
118 }
119 
120 // Makes sure that we write sources as input dependencies for actions with
121 // both sources and inputs (ACTION_FOREACH treats the sources differently).
TEST(NinjaActionTargetWriter,ActionWithSources)122 TEST(NinjaActionTargetWriter, ActionWithSources) {
123   Err err;
124   TestWithScope setup;
125 
126   Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
127   target.set_output_type(Target::ACTION);
128 
129   target.action_values().set_script(SourceFile("//foo/script.py"));
130 
131   target.sources().push_back(SourceFile("//foo/source.txt"));
132   target.config_values().inputs().push_back(SourceFile("//foo/included.txt"));
133 
134   target.action_values().outputs() =
135       SubstitutionList::MakeForTest("//out/Debug/foo.out");
136 
137   target.SetToolchain(setup.toolchain());
138   ASSERT_TRUE(target.OnResolved(&err));
139 
140   setup.build_settings()->set_python_path(
141       base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
142 
143   std::ostringstream out;
144   NinjaActionTargetWriter writer(&target, out);
145   writer.Run();
146 
147   const char expected_linux[] =
148       "rule __foo_bar___rule\n"
149       "  command = /usr/bin/python ../../foo/script.py\n"
150       "  description = ACTION //foo:bar()\n"
151       "  restat = 1\n"
152       "\n"
153       "build foo.out: __foo_bar___rule | ../../foo/script.py "
154       "../../foo/included.txt ../../foo/source.txt\n"
155       "\n"
156       "build obj/foo/bar.stamp: stamp foo.out\n";
157   EXPECT_EQ(expected_linux, out.str());
158 }
159 
TEST(NinjaActionTargetWriter,ForEach)160 TEST(NinjaActionTargetWriter, ForEach) {
161   Err err;
162   TestWithScope setup;
163 
164   // Some dependencies that the action can depend on. Use actions for these
165   // so they have a nice platform-independent stamp file that can appear in the
166   // output (rather than having to worry about how the current platform names
167   // binaries).
168   Target dep(setup.settings(), Label(SourceDir("//foo/"), "dep"));
169   dep.set_output_type(Target::ACTION);
170   dep.visibility().SetPublic();
171   dep.SetToolchain(setup.toolchain());
172   ASSERT_TRUE(dep.OnResolved(&err));
173 
174   Target datadep(setup.settings(), Label(SourceDir("//foo/"), "datadep"));
175   datadep.set_output_type(Target::ACTION);
176   datadep.visibility().SetPublic();
177   datadep.SetToolchain(setup.toolchain());
178   ASSERT_TRUE(datadep.OnResolved(&err));
179 
180   Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
181   target.set_output_type(Target::ACTION_FOREACH);
182   target.private_deps().push_back(LabelTargetPair(&dep));
183   target.data_deps().push_back(LabelTargetPair(&datadep));
184 
185   target.sources().push_back(SourceFile("//foo/input1.txt"));
186   target.sources().push_back(SourceFile("//foo/input2.txt"));
187 
188   target.action_values().set_script(SourceFile("//foo/script.py"));
189 
190   target.action_values().args() = SubstitutionList::MakeForTest(
191       "-i", "{{source}}", "--out=foo bar{{source_name_part}}.o");
192   target.action_values().outputs() =
193       SubstitutionList::MakeForTest("//out/Debug/{{source_name_part}}.out");
194 
195   target.config_values().inputs().push_back(SourceFile("//foo/included.txt"));
196 
197   target.SetToolchain(setup.toolchain());
198   ASSERT_TRUE(target.OnResolved(&err));
199 
200   setup.build_settings()->set_python_path(
201       base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
202 
203   std::ostringstream out;
204   NinjaActionTargetWriter writer(&target, out);
205   writer.Run();
206 
207   const char expected_linux[] =
208       "rule __foo_bar___rule\n"
209       "  command = /usr/bin/python ../../foo/script.py -i ${in} "
210 // Escaping is different between Windows and Posix.
211 #if defined(OS_WIN)
212       "\"--out=foo$ bar${source_name_part}.o\"\n"
213 #else
214       "--out=foo\\$ bar${source_name_part}.o\n"
215 #endif
216       "  description = ACTION //foo:bar()\n"
217       "  restat = 1\n"
218       "build obj/foo/bar.inputdeps.stamp: stamp ../../foo/script.py "
219       "../../foo/included.txt obj/foo/dep.stamp\n"
220       "\n"
221       "build input1.out: __foo_bar___rule ../../foo/input1.txt | "
222       "obj/foo/bar.inputdeps.stamp\n"
223       "  source_name_part = input1\n"
224       "build input2.out: __foo_bar___rule ../../foo/input2.txt | "
225       "obj/foo/bar.inputdeps.stamp\n"
226       "  source_name_part = input2\n"
227       "\n"
228       "build obj/foo/bar.stamp: "
229       "stamp input1.out input2.out || obj/foo/datadep.stamp\n";
230 
231   std::string out_str = out.str();
232 #if defined(OS_WIN)
233   std::replace(out_str.begin(), out_str.end(), '\\', '/');
234 #endif
235   EXPECT_EQ(expected_linux, out_str);
236 }
237 
TEST(NinjaActionTargetWriter,ForEachWithDepfile)238 TEST(NinjaActionTargetWriter, ForEachWithDepfile) {
239   Err err;
240   TestWithScope setup;
241 
242   Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
243   target.set_output_type(Target::ACTION_FOREACH);
244 
245   target.sources().push_back(SourceFile("//foo/input1.txt"));
246   target.sources().push_back(SourceFile("//foo/input2.txt"));
247 
248   target.action_values().set_script(SourceFile("//foo/script.py"));
249 
250   target.SetToolchain(setup.toolchain());
251   ASSERT_TRUE(target.OnResolved(&err));
252 
253   SubstitutionPattern depfile;
254   ASSERT_TRUE(
255       depfile.Parse("//out/Debug/gen/{{source_name_part}}.d", nullptr, &err));
256   target.action_values().set_depfile(depfile);
257 
258   target.action_values().args() = SubstitutionList::MakeForTest(
259       "-i", "{{source}}", "--out=foo bar{{source_name_part}}.o");
260   target.action_values().outputs() =
261       SubstitutionList::MakeForTest("//out/Debug/{{source_name_part}}.out");
262 
263   target.config_values().inputs().push_back(SourceFile("//foo/included.txt"));
264 
265   setup.build_settings()->set_python_path(
266       base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
267 
268   std::ostringstream out;
269   NinjaActionTargetWriter writer(&target, out);
270   writer.Run();
271 
272   const char expected_linux[] =
273       "rule __foo_bar___rule\n"
274       "  command = /usr/bin/python ../../foo/script.py -i ${in} "
275 #if defined(OS_WIN)
276       "\"--out=foo$ bar${source_name_part}.o\"\n"
277 #else
278       "--out=foo\\$ bar${source_name_part}.o\n"
279 #endif
280       "  description = ACTION //foo:bar()\n"
281       "  restat = 1\n"
282       "build obj/foo/bar.inputdeps.stamp: stamp ../../foo/script.py "
283       "../../foo/included.txt\n"
284       "\n"
285       "build input1.out: __foo_bar___rule ../../foo/input1.txt"
286       " | obj/foo/bar.inputdeps.stamp\n"
287       "  source_name_part = input1\n"
288       "  depfile = gen/input1.d\n"
289       "build input2.out: __foo_bar___rule ../../foo/input2.txt"
290       " | obj/foo/bar.inputdeps.stamp\n"
291       "  source_name_part = input2\n"
292       "  depfile = gen/input2.d\n"
293       "\n"
294       "build obj/foo/bar.stamp: stamp input1.out input2.out\n";
295   EXPECT_EQ(expected_linux, out.str());
296 }
297 
TEST(NinjaActionTargetWriter,ForEachWithResponseFile)298 TEST(NinjaActionTargetWriter, ForEachWithResponseFile) {
299   Err err;
300   TestWithScope setup;
301 
302   Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
303   target.set_output_type(Target::ACTION_FOREACH);
304 
305   target.sources().push_back(SourceFile("//foo/input1.txt"));
306   target.action_values().set_script(SourceFile("//foo/script.py"));
307 
308   target.SetToolchain(setup.toolchain());
309   ASSERT_TRUE(target.OnResolved(&err));
310 
311   // Make sure we get interesting substitutions for both the args and the
312   // response file contents.
313   target.action_values().args() = SubstitutionList::MakeForTest(
314       "{{source}}", "{{source_file_part}}", "{{response_file_name}}");
315   target.action_values().rsp_file_contents() =
316       SubstitutionList::MakeForTest("-j", "{{source_name_part}}");
317   target.action_values().outputs() =
318       SubstitutionList::MakeForTest("//out/Debug/{{source_name_part}}.out");
319 
320   setup.build_settings()->set_python_path(
321       base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
322 
323   std::ostringstream out;
324   NinjaActionTargetWriter writer(&target, out);
325   writer.Run();
326 
327   const char expected_linux[] =
328       "rule __foo_bar___rule\n"
329       // This name is autogenerated from the target rule name.
330       "  rspfile = __foo_bar___rule.$unique_name.rsp\n"
331       // These come from rsp_file_contents above.
332       "  rspfile_content = -j ${source_name_part}\n"
333       // These come from the args.
334       "  command = /usr/bin/python ../../foo/script.py ${in} "
335       "${source_file_part} ${rspfile}\n"
336       "  description = ACTION //foo:bar()\n"
337       "  restat = 1\n"
338       "\n"
339       "build input1.out: __foo_bar___rule ../../foo/input1.txt"
340       " | ../../foo/script.py\n"
341       // Necessary for the rspfile defined in the rule.
342       "  unique_name = 0\n"
343       // Substitution for the args.
344       "  source_file_part = input1.txt\n"
345       // Substitution for the rspfile contents.
346       "  source_name_part = input1\n"
347       "\n"
348       "build obj/foo/bar.stamp: stamp input1.out\n";
349   EXPECT_EQ(expected_linux, out.str());
350 }
351 
TEST(NinjaActionTargetWriter,ForEachWithPool)352 TEST(NinjaActionTargetWriter, ForEachWithPool) {
353   Err err;
354   TestWithScope setup;
355 
356   Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
357   target.set_output_type(Target::ACTION_FOREACH);
358 
359   target.sources().push_back(SourceFile("//foo/input1.txt"));
360   target.action_values().set_script(SourceFile("//foo/script.py"));
361 
362   Pool pool(setup.settings(),
363             Label(SourceDir("//foo/"), "pool", setup.toolchain()->label().dir(),
364                   setup.toolchain()->label().name()));
365   pool.set_depth(5);
366   target.action_values().set_pool(LabelPtrPair<Pool>(&pool));
367 
368   target.SetToolchain(setup.toolchain());
369   ASSERT_TRUE(target.OnResolved(&err));
370 
371   // Make sure we get interesting substitutions for both the args and the
372   // response file contents.
373   target.action_values().args() =
374       SubstitutionList::MakeForTest("{{source}}", "{{source_file_part}}");
375   target.action_values().outputs() =
376       SubstitutionList::MakeForTest("//out/Debug/{{source_name_part}}.out");
377 
378   setup.build_settings()->set_python_path(
379       base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
380 
381   std::ostringstream out;
382   NinjaActionTargetWriter writer(&target, out);
383   writer.Run();
384 
385   const char expected_linux[] =
386       "rule __foo_bar___rule\n"
387       // These come from the args.
388       "  command = /usr/bin/python ../../foo/script.py ${in} "
389       "${source_file_part}\n"
390       "  description = ACTION //foo:bar()\n"
391       "  restat = 1\n"
392       "\n"
393       "build input1.out: __foo_bar___rule ../../foo/input1.txt"
394       " | ../../foo/script.py\n"
395       // Substitution for the args.
396       "  source_file_part = input1.txt\n"
397       "  pool = foo_pool\n"
398       "\n"
399       "build obj/foo/bar.stamp: stamp input1.out\n";
400   EXPECT_EQ(expected_linux, out.str());
401 }
402 
TEST(NinjaActionTargetWriter,NoTransitiveHardDeps)403 TEST(NinjaActionTargetWriter, NoTransitiveHardDeps) {
404   Err err;
405   TestWithScope setup;
406 
407   setup.build_settings()->set_python_path(
408       base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
409 
410   Target dep(setup.settings(), Label(SourceDir("//foo/"), "dep"));
411   dep.set_output_type(Target::ACTION);
412   dep.visibility().SetPublic();
413   dep.SetToolchain(setup.toolchain());
414   ASSERT_TRUE(dep.OnResolved(&err));
415 
416   Target foo(setup.settings(), Label(SourceDir("//foo/"), "foo"));
417   foo.set_output_type(Target::ACTION);
418   foo.visibility().SetPublic();
419   foo.sources().push_back(SourceFile("//foo/input1.txt"));
420   foo.action_values().set_script(SourceFile("//foo/script.py"));
421   foo.private_deps().push_back(LabelTargetPair(&dep));
422   foo.SetToolchain(setup.toolchain());
423   foo.action_values().outputs() =
424       SubstitutionList::MakeForTest("//out/Debug/foo.out");
425   ASSERT_TRUE(foo.OnResolved(&err));
426 
427   {
428     std::ostringstream out;
429     NinjaActionTargetWriter writer(&foo, out);
430     writer.Run();
431 
432     const char expected_linux[] =
433         "rule __foo_foo___rule\n"
434         // These come from the args.
435         "  command = /usr/bin/python ../../foo/script.py\n"
436         "  description = ACTION //foo:foo()\n"
437         "  restat = 1\n"
438         "\n"
439         "build foo.out: __foo_foo___rule | ../../foo/script.py"
440         " ../../foo/input1.txt obj/foo/dep.stamp\n"
441         "\n"
442         "build obj/foo/foo.stamp: stamp foo.out\n";
443     EXPECT_EQ(expected_linux, out.str());
444   }
445 
446   Target bar(setup.settings(), Label(SourceDir("//bar/"), "bar"));
447   bar.set_output_type(Target::ACTION);
448   bar.sources().push_back(SourceFile("//bar/input1.txt"));
449   bar.action_values().set_script(SourceFile("//bar/script.py"));
450   bar.private_deps().push_back(LabelTargetPair(&foo));
451   bar.SetToolchain(setup.toolchain());
452   bar.action_values().outputs() =
453       SubstitutionList::MakeForTest("//out/Debug/bar.out");
454   ASSERT_TRUE(bar.OnResolved(&err)) << err.message();
455 
456   {
457     std::ostringstream out;
458     NinjaActionTargetWriter writer(&bar, out);
459     writer.Run();
460 
461     const char expected_linux[] =
462         "rule __bar_bar___rule\n"
463         // These come from the args.
464         "  command = /usr/bin/python ../../bar/script.py\n"
465         "  description = ACTION //bar:bar()\n"
466         "  restat = 1\n"
467         "\n"
468         // Do not have obj/foo/dep.stamp as dependency.
469         "build bar.out: __bar_bar___rule | ../../bar/script.py"
470         " ../../bar/input1.txt obj/foo/foo.stamp\n"
471         "\n"
472         "build obj/bar/bar.stamp: stamp bar.out\n";
473     EXPECT_EQ(expected_linux, out.str());
474   }
475 }
476