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