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