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