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