ninja_action_target_writer_unittest.cc revision 116680a4aac90f2aa7413d9095a592090648e557
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 "testing/gtest/include/gtest/gtest.h"
9#include "tools/gn/file_template.h"
10#include "tools/gn/ninja_action_target_writer.h"
11#include "tools/gn/test_with_scope.h"
12
13TEST(NinjaActionTargetWriter, WriteOutputFilesForBuildLine) {
14  TestWithScope setup;
15  setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
16  Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
17
18  target.action_values().outputs().push_back(
19      "//out/Debug/gen/a b{{source_name_part}}.h");
20  target.action_values().outputs().push_back(
21      "//out/Debug/gen/{{source_name_part}}.cc");
22
23  std::ostringstream out;
24  NinjaActionTargetWriter writer(&target, setup.toolchain(), out);
25
26  FileTemplate output_template = FileTemplate::GetForTargetOutputs(&target);
27
28  SourceFile source("//foo/bar.in");
29  std::vector<OutputFile> output_files;
30  writer.WriteOutputFilesForBuildLine(output_template, source, &output_files);
31
32  EXPECT_EQ(" gen/a$ bbar.h gen/bar.cc", out.str());
33}
34
35TEST(NinjaActionTargetWriter, WriteArgsSubstitutions) {
36  TestWithScope setup;
37  setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
38  Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
39
40  std::ostringstream out;
41  NinjaActionTargetWriter writer(&target, setup.toolchain(), out);
42
43  std::vector<std::string> args;
44  args.push_back("-i");
45  args.push_back("{{source}}");
46  args.push_back("--out=foo bar{{source_name_part}}.o");
47  FileTemplate args_template(setup.settings(), args,
48                             FileTemplate::OUTPUT_RELATIVE,
49                             setup.settings()->build_settings()->build_dir());
50
51  writer.WriteArgsSubstitutions(SourceFile("//foo/b ar.in"), args_template);
52#if defined(OS_WIN)
53  EXPECT_EQ("  source = \"../../foo/b$ ar.in\"\n"
54            "  source_name_part = \"b$ ar\"\n",
55            out.str());
56#else
57  EXPECT_EQ("  source = ../../foo/b\\$ ar.in\n"
58            "  source_name_part = b\\$ ar\n",
59            out.str());
60#endif
61}
62
63// Makes sure that we write sources as input dependencies for actions with
64// both sources and inputs (ACTION_FOREACH treats the sources differently).
65TEST(NinjaActionTargetWriter, ActionWithSources) {
66  TestWithScope setup;
67  setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
68  Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
69  target.set_output_type(Target::ACTION);
70
71  target.action_values().set_script(SourceFile("//foo/script.py"));
72
73  target.sources().push_back(SourceFile("//foo/source.txt"));
74  target.inputs().push_back(SourceFile("//foo/included.txt"));
75
76  target.action_values().outputs().push_back("//out/Debug/foo.out");
77
78  // Posix.
79  {
80    setup.settings()->set_target_os(Settings::LINUX);
81    setup.build_settings()->set_python_path(base::FilePath(FILE_PATH_LITERAL(
82        "/usr/bin/python")));
83
84    std::ostringstream out;
85    NinjaActionTargetWriter writer(&target, setup.toolchain(), out);
86    writer.Run();
87
88    const char expected_linux[] =
89        "rule __foo_bar___rule\n"
90        "  command = /usr/bin/python ../../foo/script.py\n"
91        "  description = ACTION //foo:bar()\n"
92        "  restat = 1\n"
93        "build obj/foo/bar.inputdeps.stamp: stamp ../../foo/script.py "
94            "../../foo/included.txt ../../foo/source.txt\n"
95        "\n"
96        "build foo.out: __foo_bar___rule | obj/foo/bar.inputdeps.stamp\n"
97        "\n"
98        "build obj/foo/bar.stamp: stamp foo.out\n";
99    EXPECT_EQ(expected_linux, out.str());
100  }
101
102  // Windows.
103  {
104    // Note: we use forward slashes here so that the output will be the same on
105    // Linux and Windows.
106    setup.build_settings()->set_python_path(base::FilePath(FILE_PATH_LITERAL(
107        "C:/python/python.exe")));
108    setup.settings()->set_target_os(Settings::WIN);
109
110    std::ostringstream out;
111    NinjaActionTargetWriter writer(&target, setup.toolchain(), out);
112    writer.Run();
113
114    const char expected_win[] =
115        "rule __foo_bar___rule\n"
116        "  command = C$:/python/python.exe gyp-win-tool action-wrapper environment.x86 __foo_bar___rule.$unique_name.rsp\n"
117        "  description = ACTION //foo:bar()\n"
118        "  restat = 1\n"
119        "  rspfile = __foo_bar___rule.$unique_name.rsp\n"
120        "  rspfile_content = C$:/python/python.exe ../../foo/script.py\n"
121        "build obj/foo/bar.inputdeps.stamp: stamp ../../foo/script.py "
122            "../../foo/included.txt ../../foo/source.txt\n"
123        "\n"
124        "build foo.out: __foo_bar___rule | obj/foo/bar.inputdeps.stamp\n"
125        "\n"
126        "build obj/foo/bar.stamp: stamp foo.out\n";
127    EXPECT_EQ(expected_win, out.str());
128  }
129}
130
131TEST(NinjaActionTargetWriter, ForEach) {
132  TestWithScope setup;
133  setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
134
135  // Some dependencies that the action can depend on. Use actions for these
136  // so they have a nice platform-independent stamp file that can appear in the
137  // output (rather than having to worry about how the current platform names
138  // binaries).
139  Target dep(setup.settings(), Label(SourceDir("//foo/"), "dep"));
140  dep.set_output_type(Target::ACTION);
141  Target datadep(setup.settings(), Label(SourceDir("//foo/"), "datadep"));
142  datadep.set_output_type(Target::ACTION);
143
144  Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
145  target.set_output_type(Target::ACTION_FOREACH);
146  target.deps().push_back(LabelTargetPair(&dep));
147  target.datadeps().push_back(LabelTargetPair(&datadep));
148
149  target.sources().push_back(SourceFile("//foo/input1.txt"));
150  target.sources().push_back(SourceFile("//foo/input2.txt"));
151
152  target.action_values().set_script(SourceFile("//foo/script.py"));
153
154  target.action_values().args().push_back("-i");
155  target.action_values().args().push_back("{{source}}");
156  target.action_values().args().push_back(
157      "--out=foo bar{{source_name_part}}.o");
158
159  target.action_values().outputs().push_back(
160      "//out/Debug/{{source_name_part}}.out");
161
162  target.inputs().push_back(SourceFile("//foo/included.txt"));
163
164  // Posix.
165  {
166    setup.settings()->set_target_os(Settings::LINUX);
167    setup.build_settings()->set_python_path(base::FilePath(FILE_PATH_LITERAL(
168        "/usr/bin/python")));
169
170    std::ostringstream out;
171    NinjaActionTargetWriter writer(&target, setup.toolchain(), out);
172    writer.Run();
173
174    const char expected_linux[] =
175        "rule __foo_bar___rule\n"
176        "  command = /usr/bin/python ../../foo/script.py -i ${source} "
177            // Escaping is different between Windows and Posix.
178#if defined(OS_WIN)
179            "\"--out=foo$ bar${source_name_part}.o\"\n"
180#else
181            "--out=foo\\$ bar${source_name_part}.o\n"
182#endif
183        "  description = ACTION //foo:bar()\n"
184        "  restat = 1\n"
185        "build obj/foo/bar.inputdeps.stamp: stamp ../../foo/script.py "
186            "../../foo/included.txt obj/foo/dep.stamp\n"
187        "\n"
188        "build input1.out: __foo_bar___rule ../../foo/input1.txt | "
189            "obj/foo/bar.inputdeps.stamp\n"
190        "  source = ../../foo/input1.txt\n"
191        "  source_name_part = input1\n"
192        "build input2.out: __foo_bar___rule ../../foo/input2.txt | "
193            "obj/foo/bar.inputdeps.stamp\n"
194        "  source = ../../foo/input2.txt\n"
195        "  source_name_part = input2\n"
196        "\n"
197        "build obj/foo/bar.stamp: "
198            "stamp input1.out input2.out obj/foo/datadep.stamp\n";
199
200    std::string out_str = out.str();
201#if defined(OS_WIN)
202    std::replace(out_str.begin(), out_str.end(), '\\', '/');
203#endif
204    EXPECT_EQ(expected_linux, out_str);
205  }
206
207  // Windows.
208  {
209    setup.build_settings()->set_python_path(base::FilePath(FILE_PATH_LITERAL(
210        "C:/python/python.exe")));
211    setup.settings()->set_target_os(Settings::WIN);
212
213    std::ostringstream out;
214    NinjaActionTargetWriter writer(&target, setup.toolchain(), out);
215    writer.Run();
216
217    const char expected_win[] =
218        "rule __foo_bar___rule\n"
219        "  command = C$:/python/python.exe gyp-win-tool action-wrapper "
220            "environment.x86 __foo_bar___rule.$unique_name.rsp\n"
221        "  description = ACTION //foo:bar()\n"
222        "  restat = 1\n"
223        "  rspfile = __foo_bar___rule.$unique_name.rsp\n"
224        "  rspfile_content = C$:/python/python.exe ../../foo/script.py -i "
225#if defined(OS_WIN)
226            "${source} \"--out=foo$ bar${source_name_part}.o\"\n"
227#else
228            "${source} --out=foo\\$ bar${source_name_part}.o\n"
229#endif
230        "build obj/foo/bar.inputdeps.stamp: stamp ../../foo/script.py "
231            "../../foo/included.txt obj/foo/dep.stamp\n"
232        "\n"
233        "build input1.out: __foo_bar___rule ../../foo/input1.txt | "
234            "obj/foo/bar.inputdeps.stamp\n"
235        "  unique_name = 0\n"
236        "  source = ../../foo/input1.txt\n"
237        "  source_name_part = input1\n"
238        "build input2.out: __foo_bar___rule ../../foo/input2.txt | "
239            "obj/foo/bar.inputdeps.stamp\n"
240        "  unique_name = 1\n"
241        "  source = ../../foo/input2.txt\n"
242        "  source_name_part = input2\n"
243        "\n"
244        "build obj/foo/bar.stamp: "
245            "stamp input1.out input2.out obj/foo/datadep.stamp\n";
246    EXPECT_EQ(expected_win, out.str());
247  }
248}
249
250TEST(NinjaActionTargetWriter, ForEachWithDepfile) {
251  TestWithScope setup;
252  setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
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  target.action_values().set_depfile(
261      SourceFile("//out/Debug/gen/{{source_name_part}}.d"));
262
263  target.action_values().args().push_back("-i");
264  target.action_values().args().push_back("{{source}}");
265  target.action_values().args().push_back(
266      "--out=foo bar{{source_name_part}}.o");
267
268  target.action_values().outputs().push_back(
269      "//out/Debug/{{source_name_part}}.out");
270
271  target.inputs().push_back(SourceFile("//foo/included.txt"));
272
273  // Posix.
274  {
275    setup.settings()->set_target_os(Settings::LINUX);
276    setup.build_settings()->set_python_path(base::FilePath(FILE_PATH_LITERAL(
277        "/usr/bin/python")));
278
279    std::ostringstream out;
280    NinjaActionTargetWriter writer(&target, setup.toolchain(), out);
281    writer.Run();
282
283    const char expected_linux[] =
284        "rule __foo_bar___rule\n"
285        "  command = /usr/bin/python ../../foo/script.py -i ${source} "
286#if defined(OS_WIN)
287            "\"--out=foo$ bar${source_name_part}.o\"\n"
288#else
289            "--out=foo\\$ bar${source_name_part}.o\n"
290#endif
291        "  description = ACTION //foo:bar()\n"
292        "  restat = 1\n"
293        "build obj/foo/bar.inputdeps.stamp: stamp ../../foo/script.py "
294            "../../foo/included.txt\n"
295        "\n"
296        "build input1.out: __foo_bar___rule ../../foo/input1.txt"
297            " | obj/foo/bar.inputdeps.stamp\n"
298        "  source = ../../foo/input1.txt\n"
299        "  source_name_part = input1\n"
300        "  depfile = gen/input1.d\n"
301        "build input2.out: __foo_bar___rule ../../foo/input2.txt"
302            " | obj/foo/bar.inputdeps.stamp\n"
303        "  source = ../../foo/input2.txt\n"
304        "  source_name_part = input2\n"
305        "  depfile = gen/input2.d\n"
306        "\n"
307        "build obj/foo/bar.stamp: stamp input1.out input2.out\n";
308    EXPECT_EQ(expected_linux, out.str());
309  }
310
311  // Windows.
312  {
313    setup.build_settings()->set_python_path(base::FilePath(FILE_PATH_LITERAL(
314        "C:/python/python.exe")));
315    setup.settings()->set_target_os(Settings::WIN);
316
317    std::ostringstream out;
318    NinjaActionTargetWriter writer(&target, setup.toolchain(), out);
319    writer.Run();
320
321    const char expected_win[] =
322        "rule __foo_bar___rule\n"
323        "  command = C$:/python/python.exe gyp-win-tool action-wrapper "
324            "environment.x86 __foo_bar___rule.$unique_name.rsp\n"
325        "  description = ACTION //foo:bar()\n"
326        "  restat = 1\n"
327        "  rspfile = __foo_bar___rule.$unique_name.rsp\n"
328        "  rspfile_content = C$:/python/python.exe ../../foo/script.py -i "
329#if defined(OS_WIN)
330            "${source} \"--out=foo$ bar${source_name_part}.o\"\n"
331#else
332            "${source} --out=foo\\$ bar${source_name_part}.o\n"
333#endif
334        "build obj/foo/bar.inputdeps.stamp: stamp ../../foo/script.py "
335            "../../foo/included.txt\n"
336        "\n"
337        "build input1.out: __foo_bar___rule ../../foo/input1.txt"
338            " | obj/foo/bar.inputdeps.stamp\n"
339        "  unique_name = 0\n"
340        "  source = ../../foo/input1.txt\n"
341        "  source_name_part = input1\n"
342        "  depfile = gen/input1.d\n"
343        "build input2.out: __foo_bar___rule ../../foo/input2.txt"
344            " | obj/foo/bar.inputdeps.stamp\n"
345        "  unique_name = 1\n"
346        "  source = ../../foo/input2.txt\n"
347        "  source_name_part = input2\n"
348        "  depfile = gen/input2.d\n"
349        "\n"
350        "build obj/foo/bar.stamp: stamp input1.out input2.out\n";
351    EXPECT_EQ(expected_win, out.str());
352  }
353}
354