• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 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 <sstream>
6 
7 #include "gn/c_substitution_type.h"
8 #include "gn/err.h"
9 #include "gn/escape.h"
10 #include "gn/substitution_list.h"
11 #include "gn/substitution_pattern.h"
12 #include "gn/substitution_writer.h"
13 #include "gn/target.h"
14 #include "gn/test_with_scope.h"
15 #include "util/build_config.h"
16 #include "util/test/test.h"
17 
TEST(SubstitutionWriter,GetListAs)18 TEST(SubstitutionWriter, GetListAs) {
19   TestWithScope setup;
20 
21   SubstitutionList list =
22       SubstitutionList::MakeForTest("//foo/bar/a.cc", "//foo/bar/b.cc");
23 
24   std::vector<SourceFile> sources;
25   SubstitutionWriter::GetListAsSourceFiles(list, &sources);
26   ASSERT_EQ(2u, sources.size());
27   EXPECT_EQ("//foo/bar/a.cc", sources[0].value());
28   EXPECT_EQ("//foo/bar/b.cc", sources[1].value());
29 
30   std::vector<OutputFile> outputs;
31   SubstitutionWriter::GetListAsOutputFiles(setup.settings(), list, &outputs);
32   ASSERT_EQ(2u, outputs.size());
33   EXPECT_EQ("../../foo/bar/a.cc", outputs[0].value());
34   EXPECT_EQ("../../foo/bar/b.cc", outputs[1].value());
35 }
36 
TEST(SubstitutionWriter,ApplyPatternToSource)37 TEST(SubstitutionWriter, ApplyPatternToSource) {
38   TestWithScope setup;
39 
40   SubstitutionPattern pattern;
41   Err err;
42   ASSERT_TRUE(pattern.Parse("{{source_gen_dir}}/{{source_name_part}}.tmp",
43                             nullptr, &err));
44 
45   SourceFile result = SubstitutionWriter::ApplyPatternToSource(
46       nullptr, setup.settings(), pattern, SourceFile("//foo/bar/myfile.txt"));
47   ASSERT_EQ("//out/Debug/gen/foo/bar/myfile.tmp", result.value());
48 
49   result = SubstitutionWriter::ApplyPatternToSource(
50       nullptr, setup.settings(), pattern,
51       SourceFile("//out/Debug/gen/generated_file.cc"));
52   ASSERT_EQ("//out/Debug/gen/BUILD_DIR/gen/generated_file.tmp", result.value())
53       << result.value();
54 }
55 
TEST(SubstitutionWriter,ApplyPatternToSourceAsOutputFile)56 TEST(SubstitutionWriter, ApplyPatternToSourceAsOutputFile) {
57   TestWithScope setup;
58 
59   SubstitutionPattern pattern;
60   Err err;
61   ASSERT_TRUE(pattern.Parse("{{source_gen_dir}}/{{source_name_part}}.tmp",
62                             nullptr, &err));
63 
64   OutputFile result = SubstitutionWriter::ApplyPatternToSourceAsOutputFile(
65       nullptr, setup.settings(), pattern, SourceFile("//foo/bar/myfile.txt"));
66   ASSERT_EQ("gen/foo/bar/myfile.tmp", result.value());
67 }
68 
TEST(SubstitutionWriter,WriteNinjaVariablesForSource)69 TEST(SubstitutionWriter, WriteNinjaVariablesForSource) {
70   TestWithScope setup;
71 
72   std::vector<const Substitution*> types;
73   types.push_back(&SubstitutionSource);
74   types.push_back(&SubstitutionSourceNamePart);
75   types.push_back(&SubstitutionSourceDir);
76 
77   EscapeOptions options;
78   options.mode = ESCAPE_NONE;
79 
80   std::ostringstream out;
81   SubstitutionWriter::WriteNinjaVariablesForSource(
82       nullptr, setup.settings(), SourceFile("//foo/bar/baz.txt"), types,
83       options, out);
84 
85   // The "source" should be skipped since that will expand to $in which is
86   // implicit.
87   EXPECT_EQ(
88       "  source_name_part = baz\n"
89       "  source_dir = ../../foo/bar\n",
90       out.str());
91 }
92 
TEST(SubstitutionWriter,WriteWithNinjaVariables)93 TEST(SubstitutionWriter, WriteWithNinjaVariables) {
94   Err err;
95   SubstitutionPattern pattern;
96   ASSERT_TRUE(pattern.Parse("-i {{source}} --out=bar\"{{source_name_part}}\".o",
97                             nullptr, &err));
98   EXPECT_FALSE(err.has_error());
99 
100   EscapeOptions options;
101   options.mode = ESCAPE_NONE;
102 
103   std::ostringstream out;
104   SubstitutionWriter::WriteWithNinjaVariables(pattern, options, out);
105 
106   EXPECT_EQ("-i ${in} --out=bar\"${source_name_part}\".o", out.str());
107 }
108 
TEST(SubstitutionWriter,SourceSubstitutions)109 TEST(SubstitutionWriter, SourceSubstitutions) {
110   TestWithScope setup;
111   Err err;
112 
113   Target target(setup.settings(), Label(SourceDir("//foo/bar/"), "baz"));
114   target.set_output_type(Target::STATIC_LIBRARY);
115   target.SetToolchain(setup.toolchain());
116   ASSERT_TRUE(target.OnResolved(&err));
117 
118 // Call to get substitutions relative to the build dir.
119 #define GetRelSubst(str, what)                          \
120   SubstitutionWriter::GetSourceSubstitution(            \
121       &target, setup.settings(), SourceFile(str), what, \
122       SubstitutionWriter::OUTPUT_RELATIVE,              \
123       setup.settings()->build_settings()->build_dir())
124 
125 // Call to get absolute directory substitutions.
126 #define GetAbsSubst(str, what)                          \
127   SubstitutionWriter::GetSourceSubstitution(            \
128       &target, setup.settings(), SourceFile(str), what, \
129       SubstitutionWriter::OUTPUT_ABSOLUTE, SourceDir())
130 
131   // Try all possible templates with a normal looking string.
132   EXPECT_EQ("../../foo/bar/baz.txt",
133             GetRelSubst("//foo/bar/baz.txt", &SubstitutionSource));
134   EXPECT_EQ("//foo/bar/baz.txt",
135             GetAbsSubst("//foo/bar/baz.txt", &SubstitutionSource));
136 
137   EXPECT_EQ("baz",
138             GetRelSubst("//foo/bar/baz.txt", &SubstitutionSourceNamePart));
139   EXPECT_EQ("baz",
140             GetAbsSubst("//foo/bar/baz.txt", &SubstitutionSourceNamePart));
141 
142   EXPECT_EQ("baz.txt",
143             GetRelSubst("//foo/bar/baz.txt", &SubstitutionSourceFilePart));
144   EXPECT_EQ("baz.txt",
145             GetAbsSubst("//foo/bar/baz.txt", &SubstitutionSourceFilePart));
146 
147   EXPECT_EQ("../../foo/bar",
148             GetRelSubst("//foo/bar/baz.txt", &SubstitutionSourceDir));
149   EXPECT_EQ("//foo/bar",
150             GetAbsSubst("//foo/bar/baz.txt", &SubstitutionSourceDir));
151 
152   EXPECT_EQ("foo/bar", GetRelSubst("//foo/bar/baz.txt",
153                                    &SubstitutionSourceRootRelativeDir));
154   EXPECT_EQ("foo/bar", GetAbsSubst("//foo/bar/baz.txt",
155                                    &SubstitutionSourceRootRelativeDir));
156 
157   EXPECT_EQ("gen/foo/bar",
158             GetRelSubst("//foo/bar/baz.txt", &SubstitutionSourceGenDir));
159   EXPECT_EQ("//out/Debug/gen/foo/bar",
160             GetAbsSubst("//foo/bar/baz.txt", &SubstitutionSourceGenDir));
161 
162   EXPECT_EQ("obj/foo/bar",
163             GetRelSubst("//foo/bar/baz.txt", &SubstitutionSourceOutDir));
164   EXPECT_EQ("//out/Debug/obj/foo/bar",
165             GetAbsSubst("//foo/bar/baz.txt", &SubstitutionSourceOutDir));
166 
167   // Operations on an absolute path.
168   EXPECT_EQ("/baz.txt", GetRelSubst("/baz.txt", &SubstitutionSource));
169   EXPECT_EQ("/.", GetRelSubst("/baz.txt", &SubstitutionSourceDir));
170   EXPECT_EQ("gen/ABS_PATH", GetRelSubst("/baz.txt", &SubstitutionSourceGenDir));
171   EXPECT_EQ("obj/ABS_PATH", GetRelSubst("/baz.txt", &SubstitutionSourceOutDir));
172 #if defined(OS_WIN)
173   EXPECT_EQ("gen/ABS_PATH/C",
174             GetRelSubst("/C:/baz.txt", &SubstitutionSourceGenDir));
175   EXPECT_EQ("obj/ABS_PATH/C",
176             GetRelSubst("/C:/baz.txt", &SubstitutionSourceOutDir));
177 #endif
178 
179   EXPECT_EQ(".", GetRelSubst("//baz.txt", &SubstitutionSourceRootRelativeDir));
180 
181   EXPECT_EQ("baz.txt", GetRelSubst("//foo/bar/baz.txt",
182                                    &SubstitutionSourceTargetRelative));
183   EXPECT_EQ("baz.txt", GetAbsSubst("//foo/bar/baz.txt",
184                                    &SubstitutionSourceTargetRelative));
185 
186 #undef GetAbsSubst
187 #undef GetRelSubst
188 }
189 
TEST(SubstitutionWriter,TargetSubstitutions)190 TEST(SubstitutionWriter, TargetSubstitutions) {
191   TestWithScope setup;
192   Err err;
193 
194   Target target(setup.settings(), Label(SourceDir("//foo/bar/"), "baz"));
195   target.set_output_type(Target::STATIC_LIBRARY);
196   target.SetToolchain(setup.toolchain());
197   ASSERT_TRUE(target.OnResolved(&err));
198 
199   std::string result;
200   EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
201       &target, &SubstitutionLabel, &result));
202   EXPECT_EQ("//foo/bar:baz", result);
203 
204   EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
205       &target, &SubstitutionLabelName, &result));
206   EXPECT_EQ("baz", result);
207 
208   EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
209       &target, &SubstitutionLabelNoToolchain, &result));
210   EXPECT_EQ("//foo/bar:baz", result);
211 
212   EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
213       &target, &SubstitutionRootGenDir, &result));
214   EXPECT_EQ("gen", result);
215 
216   EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
217       &target, &SubstitutionRootOutDir, &result));
218   EXPECT_EQ(".", result);
219 
220   EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
221       &target, &SubstitutionTargetGenDir, &result));
222   EXPECT_EQ("gen/foo/bar", result);
223 
224   EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
225       &target, &SubstitutionTargetOutDir, &result));
226   EXPECT_EQ("obj/foo/bar", result);
227 
228   EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
229       &target, &SubstitutionTargetOutputName, &result));
230   EXPECT_EQ("libbaz", result);
231 }
232 
TEST(SubstitutionWriter,CompilerSubstitutions)233 TEST(SubstitutionWriter, CompilerSubstitutions) {
234   TestWithScope setup;
235   Err err;
236 
237   Target target(setup.settings(), Label(SourceDir("//foo/bar/"), "baz"));
238   target.set_output_type(Target::STATIC_LIBRARY);
239   target.SetToolchain(setup.toolchain());
240   ASSERT_TRUE(target.OnResolved(&err));
241 
242   // The compiler substitution is just source + target combined. So test one
243   // of each of those classes of things to make sure this is hooked up.
244   EXPECT_EQ("file", SubstitutionWriter::GetCompilerSubstitution(
245                         &target, SourceFile("//foo/bar/file.txt"),
246                         &SubstitutionSourceNamePart));
247   EXPECT_EQ("gen/foo/bar", SubstitutionWriter::GetCompilerSubstitution(
248                                &target, SourceFile("//foo/bar/file.txt"),
249                                &SubstitutionTargetGenDir));
250 }
251 
TEST(SubstitutionWriter,LinkerSubstitutions)252 TEST(SubstitutionWriter, LinkerSubstitutions) {
253   TestWithScope setup;
254   Err err;
255 
256   Target target(setup.settings(), Label(SourceDir("//foo/bar/"), "baz"));
257   target.set_output_type(Target::SHARED_LIBRARY);
258   target.SetToolchain(setup.toolchain());
259   ASSERT_TRUE(target.OnResolved(&err));
260 
261   const Tool* tool = setup.toolchain()->GetToolForTargetFinalOutput(&target);
262 
263   // The compiler substitution is just target + OUTPUT_EXTENSION combined. So
264   // test one target one plus the output extension.
265   EXPECT_EQ(".so", SubstitutionWriter::GetLinkerSubstitution(
266                        &target, tool, &SubstitutionOutputExtension));
267   EXPECT_EQ("gen/foo/bar", SubstitutionWriter::GetLinkerSubstitution(
268                                &target, tool, &SubstitutionTargetGenDir));
269 
270   // Test that we handle paths that end up in the root build dir properly
271   // (no leading "./" or "/").
272   SubstitutionPattern pattern;
273   ASSERT_TRUE(pattern.Parse("{{root_out_dir}}/{{target_output_name}}.so",
274                             nullptr, &err));
275 
276   OutputFile output = SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
277       &target, tool, pattern);
278   EXPECT_EQ("./libbaz.so", output.value());
279 
280   // Output extensions can be overridden.
281   target.set_output_extension("extension");
282   EXPECT_EQ(".extension", SubstitutionWriter::GetLinkerSubstitution(
283                               &target, tool, &SubstitutionOutputExtension));
284   target.set_output_extension("");
285   EXPECT_EQ("", SubstitutionWriter::GetLinkerSubstitution(
286                     &target, tool, &SubstitutionOutputExtension));
287 
288   // Output directory is tested in a separate test below.
289 }
290 
TEST(SubstitutionWriter,OutputDir)291 TEST(SubstitutionWriter, OutputDir) {
292   TestWithScope setup;
293   Err err;
294 
295   // This tool has an output directory pattern and uses that for the
296   // output name.
297   std::unique_ptr<Tool> tool = Tool::CreateTool(CTool::kCToolLink);
298   SubstitutionPattern out_dir_pattern;
299   ASSERT_TRUE(out_dir_pattern.Parse("{{root_out_dir}}/{{target_output_name}}",
300                                     nullptr, &err));
301   tool->set_default_output_dir(out_dir_pattern);
302   tool->SetComplete();
303 
304   // Default target with no output dir overrides.
305   Target target(setup.settings(), Label(SourceDir("//foo/"), "baz"));
306   target.set_output_type(Target::EXECUTABLE);
307   target.SetToolchain(setup.toolchain());
308   ASSERT_TRUE(target.OnResolved(&err));
309 
310   // The output should expand the default from the patterns in the tool.
311   SubstitutionPattern output_name;
312   ASSERT_TRUE(output_name.Parse("{{output_dir}}/{{target_output_name}}.exe",
313                                 nullptr, &err));
314   EXPECT_EQ("./baz/baz.exe",
315             SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
316                 &target, tool.get(), output_name)
317                 .value());
318 
319   // Override the output name to the root build dir.
320   target.set_output_dir(SourceDir("//out/Debug/"));
321   EXPECT_EQ("./baz.exe", SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
322                              &target, tool.get(), output_name)
323                              .value());
324 
325   // Override the output name to a new subdirectory.
326   target.set_output_dir(SourceDir("//out/Debug/foo/bar"));
327   EXPECT_EQ("foo/bar/baz.exe",
328             SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
329                 &target, tool.get(), output_name)
330                 .value());
331 }
332