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