• 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.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,ActionWithOrderOnlyDeps)161 TEST(NinjaActionTargetWriter, ActionWithOrderOnlyDeps) {
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 datadep(setup.settings(), Label(SourceDir("//foo/"), "datadep"));
176   datadep.set_output_type(Target::ACTION);
177   datadep.visibility().SetPublic();
178   datadep.SetToolchain(setup.toolchain());
179   ASSERT_TRUE(datadep.OnResolved(&err));
180 
181   Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
182   target.set_output_type(Target::ACTION);
183 
184   target.action_values().set_script(SourceFile("//foo/script.py"));
185 
186   target.sources().push_back(SourceFile("//foo/source.txt"));
187   target.config_values().inputs().push_back(SourceFile("//foo/included.txt"));
188 
189   target.action_values().outputs() =
190       SubstitutionList::MakeForTest("//out/Debug/foo.out");
191 
192   target.private_deps().push_back(LabelTargetPair(&dep));
193   target.data_deps().push_back(LabelTargetPair(&datadep));
194 
195   target.SetToolchain(setup.toolchain());
196   ASSERT_TRUE(target.OnResolved(&err));
197 
198   setup.build_settings()->set_python_path(
199       base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
200 
201   std::ostringstream out;
202   NinjaActionTargetWriter writer(&target, out);
203   writer.Run();
204 
205   const char expected[] =
206       "rule __foo_bar___rule\n"
207       "  command = /usr/bin/python ../../foo/script.py\n"
208       "  description = ACTION //foo:bar()\n"
209       "  restat = 1\n"
210       "\n"
211       "build foo.out: __foo_bar___rule | ../../foo/script.py "
212       "../../foo/included.txt ../../foo/source.txt obj/foo/dep.stamp || "
213       "obj/foo/datadep.stamp\n"
214       "\n"
215       "build obj/foo/bar.stamp: stamp foo.out\n";
216 
217   EXPECT_EQ(expected, out.str());
218 }
219 
TEST(NinjaActionTargetWriter,ForEach)220 TEST(NinjaActionTargetWriter, ForEach) {
221   Err err;
222   TestWithScope setup;
223 
224   // Some dependencies that the action can depend on. Use actions for these
225   // so they have a nice platform-independent stamp file that can appear in the
226   // output (rather than having to worry about how the current platform names
227   // binaries).
228   Target dep(setup.settings(), Label(SourceDir("//foo/"), "dep"));
229   dep.set_output_type(Target::ACTION);
230   dep.visibility().SetPublic();
231   dep.SetToolchain(setup.toolchain());
232   ASSERT_TRUE(dep.OnResolved(&err));
233 
234   Target bundle_data_dep(setup.settings(),
235                          Label(SourceDir("//foo/"), "bundle_data_dep"));
236   bundle_data_dep.sources().push_back(SourceFile("//foo/some_data.txt"));
237   bundle_data_dep.set_output_type(Target::BUNDLE_DATA);
238   bundle_data_dep.visibility().SetPublic();
239   bundle_data_dep.SetToolchain(setup.toolchain());
240   ASSERT_TRUE(bundle_data_dep.OnResolved(&err));
241 
242   Target datadep(setup.settings(), Label(SourceDir("//foo/"), "datadep"));
243   datadep.set_output_type(Target::ACTION);
244   datadep.visibility().SetPublic();
245   datadep.SetToolchain(setup.toolchain());
246   ASSERT_TRUE(datadep.OnResolved(&err));
247 
248   Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
249   target.set_output_type(Target::ACTION_FOREACH);
250   target.private_deps().push_back(LabelTargetPair(&dep));
251   target.private_deps().push_back(LabelTargetPair(&bundle_data_dep));
252   target.data_deps().push_back(LabelTargetPair(&datadep));
253 
254   target.sources().push_back(SourceFile("//foo/input1.txt"));
255   target.sources().push_back(SourceFile("//foo/input2.txt"));
256 
257   target.action_values().set_script(SourceFile("//foo/script.py"));
258 
259   target.action_values().args() = SubstitutionList::MakeForTest(
260       "-i", "{{source}}", "--out=foo bar{{source_name_part}}.o");
261   target.action_values().outputs() =
262       SubstitutionList::MakeForTest("//out/Debug/{{source_name_part}}.out");
263 
264   target.config_values().inputs().push_back(SourceFile("//foo/included.txt"));
265 
266   target.SetToolchain(setup.toolchain());
267   ASSERT_TRUE(target.OnResolved(&err));
268 
269   setup.build_settings()->set_python_path(
270       base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
271 
272   std::ostringstream out;
273   NinjaActionTargetWriter writer(&target, out);
274   writer.Run();
275 
276   const char expected_linux[] =
277       "rule __foo_bar___rule\n"
278       "  command = /usr/bin/python ../../foo/script.py -i ${in} "
279 // Escaping is different between Windows and Posix.
280 #if defined(OS_WIN)
281       "\"--out=foo$ bar${source_name_part}.o\"\n"
282 #else
283       "--out=foo\\$ bar${source_name_part}.o\n"
284 #endif
285       "  description = ACTION //foo:bar()\n"
286       "  restat = 1\n"
287       "build obj/foo/bar.inputdeps.stamp: stamp ../../foo/script.py "
288       "../../foo/included.txt obj/foo/dep.stamp\n"
289       "\n"
290       "build input1.out: __foo_bar___rule ../../foo/input1.txt | "
291       "obj/foo/bar.inputdeps.stamp || obj/foo/bundle_data_dep.stamp "
292       "obj/foo/datadep.stamp\n"
293       "  source_name_part = input1\n"
294       "build input2.out: __foo_bar___rule ../../foo/input2.txt | "
295       "obj/foo/bar.inputdeps.stamp || obj/foo/bundle_data_dep.stamp "
296       "obj/foo/datadep.stamp\n"
297       "  source_name_part = input2\n"
298       "\n"
299       "build obj/foo/bar.stamp: "
300       "stamp input1.out input2.out\n";
301 
302   std::string out_str = out.str();
303 #if defined(OS_WIN)
304   std::replace(out_str.begin(), out_str.end(), '\\', '/');
305 #endif
306   EXPECT_EQ(expected_linux, out_str);
307 }
308 
TEST(NinjaActionTargetWriter,ForEachWithDepfile)309 TEST(NinjaActionTargetWriter, ForEachWithDepfile) {
310   Err err;
311   TestWithScope setup;
312 
313   Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
314   target.set_output_type(Target::ACTION_FOREACH);
315 
316   target.sources().push_back(SourceFile("//foo/input1.txt"));
317   target.sources().push_back(SourceFile("//foo/input2.txt"));
318 
319   target.action_values().set_script(SourceFile("//foo/script.py"));
320 
321   target.SetToolchain(setup.toolchain());
322   ASSERT_TRUE(target.OnResolved(&err));
323 
324   SubstitutionPattern depfile;
325   ASSERT_TRUE(
326       depfile.Parse("//out/Debug/gen/{{source_name_part}}.d", nullptr, &err));
327   target.action_values().set_depfile(depfile);
328 
329   target.action_values().args() = SubstitutionList::MakeForTest(
330       "-i", "{{source}}", "--out=foo bar{{source_name_part}}.o");
331   target.action_values().outputs() =
332       SubstitutionList::MakeForTest("//out/Debug/{{source_name_part}}.out");
333 
334   target.config_values().inputs().push_back(SourceFile("//foo/included.txt"));
335 
336   setup.build_settings()->set_python_path(
337       base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
338   setup.build_settings()->set_ninja_required_version(Version{1, 9, 0});
339 
340   std::ostringstream out;
341   NinjaActionTargetWriter writer(&target, out);
342   writer.Run();
343 
344   const char expected_linux[] =
345       "rule __foo_bar___rule\n"
346       "  command = /usr/bin/python ../../foo/script.py -i ${in} "
347 #if defined(OS_WIN)
348       "\"--out=foo$ bar${source_name_part}.o\"\n"
349 #else
350       "--out=foo\\$ bar${source_name_part}.o\n"
351 #endif
352       "  description = ACTION //foo:bar()\n"
353       "  restat = 1\n"
354       "build obj/foo/bar.inputdeps.stamp: stamp ../../foo/script.py "
355       "../../foo/included.txt\n"
356       "\n"
357       "build input1.out: __foo_bar___rule ../../foo/input1.txt"
358       " | obj/foo/bar.inputdeps.stamp\n"
359       "  source_name_part = input1\n"
360       "  depfile = gen/input1.d\n"
361       "  deps = gcc\n"
362       "build input2.out: __foo_bar___rule ../../foo/input2.txt"
363       " | obj/foo/bar.inputdeps.stamp\n"
364       "  source_name_part = input2\n"
365       "  depfile = gen/input2.d\n"
366       "  deps = gcc\n"
367       "\n"
368       "build obj/foo/bar.stamp: stamp input1.out input2.out\n";
369   EXPECT_EQ(expected_linux, out.str());
370 }
371 
TEST(NinjaActionTargetWriter,ForEachWithResponseFile)372 TEST(NinjaActionTargetWriter, ForEachWithResponseFile) {
373   Err err;
374   TestWithScope setup;
375 
376   Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
377   target.set_output_type(Target::ACTION_FOREACH);
378 
379   target.sources().push_back(SourceFile("//foo/input1.txt"));
380   target.action_values().set_script(SourceFile("//foo/script.py"));
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() = SubstitutionList::MakeForTest(
388       "{{source}}", "{{source_file_part}}", "{{response_file_name}}");
389   target.action_values().rsp_file_contents() =
390       SubstitutionList::MakeForTest("-j", "{{source_name_part}}");
391   target.action_values().outputs() =
392       SubstitutionList::MakeForTest("//out/Debug/{{source_name_part}}.out");
393 
394   setup.build_settings()->set_python_path(
395       base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
396 
397   std::ostringstream out;
398   NinjaActionTargetWriter writer(&target, out);
399   writer.Run();
400 
401   const char expected_linux[] =
402       "rule __foo_bar___rule\n"
403       // This name is autogenerated from the target rule name.
404       "  rspfile = __foo_bar___rule.$unique_name.rsp\n"
405       // These come from rsp_file_contents above.
406       "  rspfile_content = -j ${source_name_part}\n"
407       // These come from the args.
408       "  command = /usr/bin/python ../../foo/script.py ${in} "
409       "${source_file_part} ${rspfile}\n"
410       "  description = ACTION //foo:bar()\n"
411       "  restat = 1\n"
412       "\n"
413       "build input1.out: __foo_bar___rule ../../foo/input1.txt"
414       " | ../../foo/script.py\n"
415       // Necessary for the rspfile defined in the rule.
416       "  unique_name = 0\n"
417       // Substitution for the args.
418       "  source_file_part = input1.txt\n"
419       // Substitution for the rspfile contents.
420       "  source_name_part = input1\n"
421       "\n"
422       "build obj/foo/bar.stamp: stamp input1.out\n";
423   EXPECT_EQ(expected_linux, out.str());
424 }
425 
TEST(NinjaActionTargetWriter,ForEachWithPool)426 TEST(NinjaActionTargetWriter, ForEachWithPool) {
427   Err err;
428   TestWithScope setup;
429 
430   Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
431   target.set_output_type(Target::ACTION_FOREACH);
432 
433   target.sources().push_back(SourceFile("//foo/input1.txt"));
434   target.action_values().set_script(SourceFile("//foo/script.py"));
435 
436   Pool pool(setup.settings(),
437             Label(SourceDir("//foo/"), "pool", setup.toolchain()->label().dir(),
438                   setup.toolchain()->label().name()));
439   pool.set_depth(5);
440   target.set_pool(LabelPtrPair<Pool>(&pool));
441 
442   target.SetToolchain(setup.toolchain());
443   ASSERT_TRUE(target.OnResolved(&err));
444 
445   // Make sure we get interesting substitutions for both the args and the
446   // response file contents.
447   target.action_values().args() =
448       SubstitutionList::MakeForTest("{{source}}", "{{source_file_part}}");
449   target.action_values().outputs() =
450       SubstitutionList::MakeForTest("//out/Debug/{{source_name_part}}.out");
451 
452   setup.build_settings()->set_python_path(
453       base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
454 
455   std::ostringstream out;
456   NinjaActionTargetWriter writer(&target, out);
457   writer.Run();
458 
459   const char expected_linux[] =
460       "rule __foo_bar___rule\n"
461       // These come from the args.
462       "  command = /usr/bin/python ../../foo/script.py ${in} "
463       "${source_file_part}\n"
464       "  description = ACTION //foo:bar()\n"
465       "  restat = 1\n"
466       "\n"
467       "build input1.out: __foo_bar___rule ../../foo/input1.txt"
468       " | ../../foo/script.py\n"
469       // Substitution for the args.
470       "  source_file_part = input1.txt\n"
471       "  pool = foo_pool\n"
472       "\n"
473       "build obj/foo/bar.stamp: stamp input1.out\n";
474   EXPECT_EQ(expected_linux, out.str());
475 }
476 
TEST(NinjaActionTargetWriter,NoTransitiveHardDeps)477 TEST(NinjaActionTargetWriter, NoTransitiveHardDeps) {
478   Err err;
479   TestWithScope setup;
480 
481   setup.build_settings()->set_python_path(
482       base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
483 
484   Target dep(setup.settings(), Label(SourceDir("//foo/"), "dep"));
485   dep.set_output_type(Target::ACTION);
486   dep.visibility().SetPublic();
487   dep.SetToolchain(setup.toolchain());
488   ASSERT_TRUE(dep.OnResolved(&err));
489 
490   Target foo(setup.settings(), Label(SourceDir("//foo/"), "foo"));
491   foo.set_output_type(Target::ACTION);
492   foo.visibility().SetPublic();
493   foo.sources().push_back(SourceFile("//foo/input1.txt"));
494   foo.action_values().set_script(SourceFile("//foo/script.py"));
495   foo.private_deps().push_back(LabelTargetPair(&dep));
496   foo.SetToolchain(setup.toolchain());
497   foo.action_values().outputs() =
498       SubstitutionList::MakeForTest("//out/Debug/foo.out");
499   ASSERT_TRUE(foo.OnResolved(&err));
500 
501   {
502     std::ostringstream out;
503     NinjaActionTargetWriter writer(&foo, out);
504     writer.Run();
505 
506     const char expected_linux[] =
507         "rule __foo_foo___rule\n"
508         // These come from the args.
509         "  command = /usr/bin/python ../../foo/script.py\n"
510         "  description = ACTION //foo:foo()\n"
511         "  restat = 1\n"
512         "\n"
513         "build foo.out: __foo_foo___rule | ../../foo/script.py"
514         " ../../foo/input1.txt obj/foo/dep.stamp\n"
515         "\n"
516         "build obj/foo/foo.stamp: stamp foo.out\n";
517     EXPECT_EQ(expected_linux, out.str());
518   }
519 
520   Target bar(setup.settings(), Label(SourceDir("//bar/"), "bar"));
521   bar.set_output_type(Target::ACTION);
522   bar.sources().push_back(SourceFile("//bar/input1.txt"));
523   bar.action_values().set_script(SourceFile("//bar/script.py"));
524   bar.private_deps().push_back(LabelTargetPair(&foo));
525   bar.SetToolchain(setup.toolchain());
526   bar.action_values().outputs() =
527       SubstitutionList::MakeForTest("//out/Debug/bar.out");
528   ASSERT_TRUE(bar.OnResolved(&err)) << err.message();
529 
530   {
531     std::ostringstream out;
532     NinjaActionTargetWriter writer(&bar, out);
533     writer.Run();
534 
535     const char expected_linux[] =
536         "rule __bar_bar___rule\n"
537         // These come from the args.
538         "  command = /usr/bin/python ../../bar/script.py\n"
539         "  description = ACTION //bar:bar()\n"
540         "  restat = 1\n"
541         "\n"
542         // Do not have obj/foo/dep.stamp as dependency.
543         "build bar.out: __bar_bar___rule | ../../bar/script.py"
544         " ../../bar/input1.txt obj/foo/foo.stamp\n"
545         "\n"
546         "build obj/bar/bar.stamp: stamp bar.out\n";
547     EXPECT_EQ(expected_linux, out.str());
548   }
549 }
550 
TEST(NinjaActionTargetWriter,SeesConfig)551 TEST(NinjaActionTargetWriter, SeesConfig) {
552   Err err;
553   TestWithScope setup;
554 
555   setup.build_settings()->set_python_path(
556       base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
557 
558   Config farcfg(setup.settings(), Label(SourceDir("//foo/"), "farcfg"));
559   farcfg.own_values().defines().push_back("MY_DEFINE2");
560   farcfg.own_values().cflags().push_back("-isysroot=baz");
561   farcfg.visibility().SetPublic();
562   ASSERT_TRUE(farcfg.OnResolved(&err));
563 
564   Config cfgdep(setup.settings(), Label(SourceDir("//foo/"), "cfgdep"));
565   cfgdep.own_values().rustenv().push_back("my_rustenv");
566   cfgdep.own_values().include_dirs().push_back(SourceDir("//my_inc_dir/"));
567   cfgdep.own_values().defines().push_back("MY_DEFINE");
568   cfgdep.visibility().SetPublic();
569   cfgdep.configs().push_back(LabelConfigPair(&farcfg));
570   ASSERT_TRUE(cfgdep.OnResolved(&err));
571 
572   Target foo(setup.settings(), Label(SourceDir("//foo/"), "foo"));
573   foo.set_output_type(Target::ACTION);
574   foo.visibility().SetPublic();
575   foo.sources().push_back(SourceFile("//foo/input1.txt"));
576   foo.action_values().set_script(SourceFile("//foo/script.py"));
577   foo.action_values().args() = SubstitutionList::MakeForTest(
578       "{{rustenv}}", "{{include_dirs}}", "{{defines}}", "{{cflags}}");
579   foo.configs().push_back(LabelConfigPair(&cfgdep));
580   foo.SetToolchain(setup.toolchain());
581   foo.action_values().outputs() =
582       SubstitutionList::MakeForTest("//out/Debug/foo.out");
583   ASSERT_TRUE(foo.OnResolved(&err));
584 
585   {
586     std::ostringstream out;
587     NinjaActionTargetWriter writer(&foo, out);
588     writer.Run();
589 
590     const char expected[] =
591         "rule __foo_foo___rule\n"
592         // These come from the args.
593         "  command = /usr/bin/python ../../foo/script.py ${rustenv} "
594         "${include_dirs} ${defines} ${cflags}\n"
595         "  description = ACTION //foo:foo()\n"
596         "  restat = 1\n"
597         "\n"
598         "build foo.out: __foo_foo___rule | ../../foo/script.py"
599         " ../../foo/input1.txt\n"
600         "  rustenv = my_rustenv\n"
601         "  defines = -DMY_DEFINE -DMY_DEFINE2\n"
602         "  include_dirs = -I../../my_inc_dir\n"
603         "  cflags = -isysroot=baz\n"
604         "\n"
605         "build obj/foo/foo.stamp: stamp foo.out\n";
606     std::string out_str = out.str();
607     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
608   }
609 }
610 
611 // Check for proper escaping of actions with spaces in python & script.
TEST(NinjaActionTargetWriter,ActionWithSpaces)612 TEST(NinjaActionTargetWriter, ActionWithSpaces) {
613   Err err;
614   TestWithScope setup;
615 
616   Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
617   target.set_output_type(Target::ACTION);
618 
619   target.action_values().set_script(SourceFile("//foo/my script.py"));
620   target.action_values().args() = SubstitutionList::MakeForTest("my argument");
621   target.config_values().inputs().push_back(SourceFile("//foo/input file.txt"));
622 
623   target.action_values().outputs() =
624       SubstitutionList::MakeForTest("//out/Debug/foo.out");
625 
626   target.SetToolchain(setup.toolchain());
627   ASSERT_TRUE(target.OnResolved(&err));
628 
629   setup.build_settings()->set_python_path(
630       base::FilePath(FILE_PATH_LITERAL("/Program Files/python")));
631 
632   std::ostringstream out;
633   NinjaActionTargetWriter writer(&target, out);
634   writer.Run();
635 
636   const char expected[] =
637       R"(rule __foo_bar___rule)"
638       "\n"
639 // Escaping is different between Windows and Posix.
640 #if defined(OS_WIN)
641       R"(  command = "/Program$ Files/python" "../../foo/my$ script.py" "my$ argument")"
642       "\n"
643 #else
644       R"(  command = /Program\$ Files/python ../../foo/my\$ script.py my\$ argument)"
645       "\n"
646 #endif
647       R"(  description = ACTION //foo:bar()
648   restat = 1
649 
650 build foo.out: __foo_bar___rule | ../../foo/my$ script.py ../../foo/input$ file.txt
651 
652 build obj/foo/bar.stamp: stamp foo.out
653 )";
654   EXPECT_EQ(expected, out.str()) << expected << "--" << out.str();
655 }
656