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.
8 #include "testing/gtest/include/gtest/gtest.h"
9 #include "tools/gn/ninja_action_target_writer.h"
10 #include "tools/gn/substitution_list.h"
11 #include "tools/gn/target.h"
12 #include "tools/gn/test_with_scope.h"
14 TEST(NinjaActionTargetWriter
, WriteOutputFilesForBuildLine
) {
18 setup
.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
20 Target
target(setup
.settings(), Label(SourceDir("//foo/"), "bar"));
21 target
.set_output_type(Target::ACTION_FOREACH
);
22 target
.action_values().outputs() = SubstitutionList::MakeForTest(
23 "//out/Debug/gen/a b{{source_name_part}}.h",
24 "//out/Debug/gen/{{source_name_part}}.cc");
26 target
.SetToolchain(setup
.toolchain());
27 ASSERT_TRUE(target
.OnResolved(&err
));
29 std::ostringstream out
;
30 NinjaActionTargetWriter
writer(&target
, out
);
32 SourceFile
source("//foo/bar.in");
33 std::vector
<OutputFile
> output_files
;
34 writer
.WriteOutputFilesForBuildLine(source
, &output_files
);
36 EXPECT_EQ(" gen/a$ bbar.h gen/bar.cc", out
.str());
39 // Tests an action with no sources.
40 TEST(NinjaActionTargetWriter
, ActionNoSources
) {
44 setup
.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
45 Target
target(setup
.settings(), Label(SourceDir("//foo/"), "bar"));
46 target
.set_output_type(Target::ACTION
);
48 target
.action_values().set_script(SourceFile("//foo/script.py"));
49 target
.inputs().push_back(SourceFile("//foo/included.txt"));
51 target
.action_values().outputs() =
52 SubstitutionList::MakeForTest("//out/Debug/foo.out");
54 target
.SetToolchain(setup
.toolchain());
55 ASSERT_TRUE(target
.OnResolved(&err
));
57 setup
.settings()->set_target_os(Settings::LINUX
);
58 setup
.build_settings()->set_python_path(base::FilePath(FILE_PATH_LITERAL(
61 std::ostringstream out
;
62 NinjaActionTargetWriter
writer(&target
, out
);
65 const char expected
[] =
66 "rule __foo_bar___rule\n"
67 " command = /usr/bin/python ../../foo/script.py\n"
68 " description = ACTION //foo:bar()\n"
70 "build obj/foo/bar.inputdeps.stamp: stamp ../../foo/script.py "
71 "../../foo/included.txt\n"
73 "build foo.out: __foo_bar___rule | obj/foo/bar.inputdeps.stamp\n"
75 "build obj/foo/bar.stamp: stamp foo.out\n";
76 EXPECT_EQ(expected
, out
.str());
79 // Makes sure that we write sources as input dependencies for actions with
80 // both sources and inputs (ACTION_FOREACH treats the sources differently).
81 TEST(NinjaActionTargetWriter
, ActionWithSources
) {
85 setup
.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
86 Target
target(setup
.settings(), Label(SourceDir("//foo/"), "bar"));
87 target
.set_output_type(Target::ACTION
);
89 target
.action_values().set_script(SourceFile("//foo/script.py"));
91 target
.sources().push_back(SourceFile("//foo/source.txt"));
92 target
.inputs().push_back(SourceFile("//foo/included.txt"));
94 target
.action_values().outputs() =
95 SubstitutionList::MakeForTest("//out/Debug/foo.out");
97 target
.SetToolchain(setup
.toolchain());
98 ASSERT_TRUE(target
.OnResolved(&err
));
102 setup
.settings()->set_target_os(Settings::LINUX
);
103 setup
.build_settings()->set_python_path(base::FilePath(FILE_PATH_LITERAL(
104 "/usr/bin/python")));
106 std::ostringstream out
;
107 NinjaActionTargetWriter
writer(&target
, out
);
110 const char expected_linux
[] =
111 "rule __foo_bar___rule\n"
112 " command = /usr/bin/python ../../foo/script.py\n"
113 " description = ACTION //foo:bar()\n"
115 "build obj/foo/bar.inputdeps.stamp: stamp ../../foo/script.py "
116 "../../foo/included.txt ../../foo/source.txt\n"
118 "build foo.out: __foo_bar___rule | obj/foo/bar.inputdeps.stamp\n"
120 "build obj/foo/bar.stamp: stamp foo.out\n";
121 EXPECT_EQ(expected_linux
, out
.str());
126 // Note: we use forward slashes here so that the output will be the same on
127 // Linux and Windows.
128 setup
.build_settings()->set_python_path(base::FilePath(FILE_PATH_LITERAL(
129 "C:/python/python.exe")));
130 setup
.settings()->set_target_os(Settings::WIN
);
132 std::ostringstream out
;
133 NinjaActionTargetWriter
writer(&target
, out
);
136 const char expected_win
[] =
137 "rule __foo_bar___rule\n"
138 " command = C$:/python/python.exe gyp-win-tool action-wrapper environment.x86 __foo_bar___rule.$unique_name.rsp\n"
139 " description = ACTION //foo:bar()\n"
141 " rspfile = __foo_bar___rule.$unique_name.rsp\n"
142 " rspfile_content = C$:/python/python.exe ../../foo/script.py\n"
143 "build obj/foo/bar.inputdeps.stamp: stamp ../../foo/script.py "
144 "../../foo/included.txt ../../foo/source.txt\n"
146 "build foo.out: __foo_bar___rule | obj/foo/bar.inputdeps.stamp\n"
148 "build obj/foo/bar.stamp: stamp foo.out\n";
149 EXPECT_EQ(expected_win
, out
.str());
153 TEST(NinjaActionTargetWriter
, ForEach
) {
157 setup
.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
159 // Some dependencies that the action can depend on. Use actions for these
160 // so they have a nice platform-independent stamp file that can appear in the
161 // output (rather than having to worry about how the current platform names
163 Target
dep(setup
.settings(), Label(SourceDir("//foo/"), "dep"));
164 dep
.set_output_type(Target::ACTION
);
165 dep
.visibility().SetPublic();
166 dep
.SetToolchain(setup
.toolchain());
167 ASSERT_TRUE(dep
.OnResolved(&err
));
169 Target
datadep(setup
.settings(), Label(SourceDir("//foo/"), "datadep"));
170 datadep
.set_output_type(Target::ACTION
);
171 datadep
.visibility().SetPublic();
172 datadep
.SetToolchain(setup
.toolchain());
173 ASSERT_TRUE(datadep
.OnResolved(&err
));
175 Target
target(setup
.settings(), Label(SourceDir("//foo/"), "bar"));
176 target
.set_output_type(Target::ACTION_FOREACH
);
177 target
.private_deps().push_back(LabelTargetPair(&dep
));
178 target
.data_deps().push_back(LabelTargetPair(&datadep
));
180 target
.sources().push_back(SourceFile("//foo/input1.txt"));
181 target
.sources().push_back(SourceFile("//foo/input2.txt"));
183 target
.action_values().set_script(SourceFile("//foo/script.py"));
185 target
.action_values().args() = SubstitutionList::MakeForTest(
188 "--out=foo bar{{source_name_part}}.o");
189 target
.action_values().outputs() = SubstitutionList::MakeForTest(
190 "//out/Debug/{{source_name_part}}.out");
192 target
.inputs().push_back(SourceFile("//foo/included.txt"));
194 target
.SetToolchain(setup
.toolchain());
195 ASSERT_TRUE(target
.OnResolved(&err
));
199 setup
.settings()->set_target_os(Settings::LINUX
);
200 setup
.build_settings()->set_python_path(base::FilePath(FILE_PATH_LITERAL(
201 "/usr/bin/python")));
203 std::ostringstream out
;
204 NinjaActionTargetWriter
writer(&target
, out
);
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.
212 "\"--out=foo$ bar${source_name_part}.o\"\n"
214 "--out=foo\\$ bar${source_name_part}.o\n"
216 " description = ACTION //foo:bar()\n"
218 "build obj/foo/bar.inputdeps.stamp: stamp ../../foo/script.py "
219 "../../foo/included.txt obj/foo/dep.stamp\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"
228 "build obj/foo/bar.stamp: "
229 "stamp input1.out input2.out || obj/foo/datadep.stamp\n";
231 std::string out_str
= out
.str();
233 std::replace(out_str
.begin(), out_str
.end(), '\\', '/');
235 EXPECT_EQ(expected_linux
, out_str
);
240 setup
.build_settings()->set_python_path(base::FilePath(FILE_PATH_LITERAL(
241 "C:/python/python.exe")));
242 setup
.settings()->set_target_os(Settings::WIN
);
244 std::ostringstream out
;
245 NinjaActionTargetWriter
writer(&target
, out
);
248 const char expected_win
[] =
249 "rule __foo_bar___rule\n"
250 " command = C$:/python/python.exe gyp-win-tool action-wrapper "
251 "environment.x86 __foo_bar___rule.$unique_name.rsp\n"
252 " description = ACTION //foo:bar()\n"
254 " rspfile = __foo_bar___rule.$unique_name.rsp\n"
255 " rspfile_content = C$:/python/python.exe ../../foo/script.py -i "
257 "${in} \"--out=foo$ bar${source_name_part}.o\"\n"
259 "${in} --out=foo\\$ bar${source_name_part}.o\n"
261 "build obj/foo/bar.inputdeps.stamp: stamp ../../foo/script.py "
262 "../../foo/included.txt obj/foo/dep.stamp\n"
264 "build input1.out: __foo_bar___rule ../../foo/input1.txt | "
265 "obj/foo/bar.inputdeps.stamp\n"
267 " source_name_part = input1\n"
268 "build input2.out: __foo_bar___rule ../../foo/input2.txt | "
269 "obj/foo/bar.inputdeps.stamp\n"
271 " source_name_part = input2\n"
273 "build obj/foo/bar.stamp: "
274 "stamp input1.out input2.out || obj/foo/datadep.stamp\n";
275 EXPECT_EQ(expected_win
, out
.str());
279 TEST(NinjaActionTargetWriter
, ForEachWithDepfile
) {
283 setup
.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
284 Target
target(setup
.settings(), Label(SourceDir("//foo/"), "bar"));
285 target
.set_output_type(Target::ACTION_FOREACH
);
287 target
.sources().push_back(SourceFile("//foo/input1.txt"));
288 target
.sources().push_back(SourceFile("//foo/input2.txt"));
290 target
.action_values().set_script(SourceFile("//foo/script.py"));
292 target
.SetToolchain(setup
.toolchain());
293 ASSERT_TRUE(target
.OnResolved(&err
));
295 SubstitutionPattern depfile
;
297 depfile
.Parse("//out/Debug/gen/{{source_name_part}}.d", nullptr, &err
));
298 target
.action_values().set_depfile(depfile
);
300 target
.action_values().args() = SubstitutionList::MakeForTest(
303 "--out=foo bar{{source_name_part}}.o");
304 target
.action_values().outputs() = SubstitutionList::MakeForTest(
305 "//out/Debug/{{source_name_part}}.out");
307 target
.inputs().push_back(SourceFile("//foo/included.txt"));
311 setup
.settings()->set_target_os(Settings::LINUX
);
312 setup
.build_settings()->set_python_path(base::FilePath(FILE_PATH_LITERAL(
313 "/usr/bin/python")));
315 std::ostringstream out
;
316 NinjaActionTargetWriter
writer(&target
, out
);
319 const char expected_linux
[] =
320 "rule __foo_bar___rule\n"
321 " command = /usr/bin/python ../../foo/script.py -i ${in} "
323 "\"--out=foo$ bar${source_name_part}.o\"\n"
325 "--out=foo\\$ bar${source_name_part}.o\n"
327 " description = ACTION //foo:bar()\n"
329 "build obj/foo/bar.inputdeps.stamp: stamp ../../foo/script.py "
330 "../../foo/included.txt\n"
332 "build input1.out: __foo_bar___rule ../../foo/input1.txt"
333 " | obj/foo/bar.inputdeps.stamp\n"
334 " source_name_part = input1\n"
335 " depfile = gen/input1.d\n"
336 "build input2.out: __foo_bar___rule ../../foo/input2.txt"
337 " | obj/foo/bar.inputdeps.stamp\n"
338 " source_name_part = input2\n"
339 " depfile = gen/input2.d\n"
341 "build obj/foo/bar.stamp: stamp input1.out input2.out\n";
342 EXPECT_EQ(expected_linux
, out
.str());
347 setup
.build_settings()->set_python_path(base::FilePath(FILE_PATH_LITERAL(
348 "C:/python/python.exe")));
349 setup
.settings()->set_target_os(Settings::WIN
);
351 std::ostringstream out
;
352 NinjaActionTargetWriter
writer(&target
, out
);
355 const char expected_win
[] =
356 "rule __foo_bar___rule\n"
357 " command = C$:/python/python.exe gyp-win-tool action-wrapper "
358 "environment.x86 __foo_bar___rule.$unique_name.rsp\n"
359 " description = ACTION //foo:bar()\n"
361 " rspfile = __foo_bar___rule.$unique_name.rsp\n"
362 " rspfile_content = C$:/python/python.exe ../../foo/script.py -i "
364 "${in} \"--out=foo$ bar${source_name_part}.o\"\n"
366 "${in} --out=foo\\$ bar${source_name_part}.o\n"
368 "build obj/foo/bar.inputdeps.stamp: stamp ../../foo/script.py "
369 "../../foo/included.txt\n"
371 "build input1.out: __foo_bar___rule ../../foo/input1.txt"
372 " | obj/foo/bar.inputdeps.stamp\n"
374 " source_name_part = input1\n"
375 " depfile = gen/input1.d\n"
376 "build input2.out: __foo_bar___rule ../../foo/input2.txt"
377 " | obj/foo/bar.inputdeps.stamp\n"
379 " source_name_part = input2\n"
380 " depfile = gen/input2.d\n"
382 "build obj/foo/bar.stamp: stamp input1.out input2.out\n";
383 EXPECT_EQ(expected_win
, out
.str());