Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(7)

Side by Side Diff: tools/gn/substitution_writer.cc

Issue 429423002: Refactor GN source expansions. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Clang warning Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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 "tools/gn/substitution_writer.h"
6
7 #include "tools/gn/build_settings.h"
8 #include "tools/gn/escape.h"
9 #include "tools/gn/filesystem_utils.h"
10 #include "tools/gn/output_file.h"
11 #include "tools/gn/settings.h"
12 #include "tools/gn/source_file.h"
13 #include "tools/gn/substitution_list.h"
14 #include "tools/gn/substitution_pattern.h"
15
16 const char kSourceExpansion_Help[] =
17 "How Source Expansion Works\n"
18 "\n"
19 " Source expansion is used for the action_foreach and copy target types\n"
20 " to map source file names to output file names or arguments.\n"
21 "\n"
22 " To perform source expansion in the outputs, GN maps every entry in the\n"
23 " sources to every entry in the outputs list, producing the cross\n"
24 " product of all combinations, expanding placeholders (see below).\n"
25 "\n"
26 " Source expansion in the args works similarly, but performing the\n"
27 " placeholder substitution produces a different set of arguments for\n"
28 " each invocation of the script.\n"
29 "\n"
30 " If no placeholders are found, the outputs or args list will be treated\n"
31 " as a static list of literal file names that do not depend on the\n"
32 " sources.\n"
33 "\n"
34 " See \"gn help copy\" and \"gn help action_foreach\" for more on how\n"
35 " this is applied.\n"
36 "\n"
37 "Placeholders\n"
38 "\n"
39 " {{source}}\n"
40 " The name of the source file including directory (*). This will\n"
41 " generally be used for specifying inputs to a script in the\n"
42 " \"args\" variable.\n"
43 " \"//foo/bar/baz.txt\" => \"../../foo/bar/baz.txt\"\n"
44 "\n"
45 " {{source_file_part}}\n"
46 " The file part of the source including the extension.\n"
47 " \"//foo/bar/baz.txt\" => \"baz.txt\"\n"
48 "\n"
49 " {{source_name_part}}\n"
50 " The filename part of the source file with no directory or\n"
51 " extension. This will generally be used for specifying a\n"
52 " transformation from a soruce file to a destination file with the\n"
53 " same name but different extension.\n"
54 " \"//foo/bar/baz.txt\" => \"baz\"\n"
55 "\n"
56 " {{source_dir}}\n"
57 " The directory (*) containing the source file with no\n"
58 " trailing slash.\n"
59 " \"//foo/bar/baz.txt\" => \"../../foo/bar\"\n"
60 "\n"
61 " {{source_root_relative_dir}}\n"
62 " The path to the source file's directory relative to the source\n"
63 " root, with no leading \"//\" or trailing slashes. If the path is\n"
64 " system-absolute, (beginning in a single slash) this will just\n"
65 " return the path with no trailing slash. This value will always\n"
66 " be the same, regardless of whether it appears in the \"outputs\"\n"
67 " or \"args\" section.\n"
68 " \"//foo/bar/baz.txt\" => \"foo/bar\"\n"
69 "\n"
70 " {{source_gen_dir}}\n"
71 " The generated file directory (*) corresponding to the source\n"
72 " file's path. This will be different than the target's generated\n"
73 " file directory if the source file is in a different directory\n"
74 " than the BUILD.gn file.\n"
75 " \"//foo/bar/baz.txt\" => \"gen/foo/bar\"\n"
76 "\n"
77 " {{source_out_dir}}\n"
78 " The object file directory (*) corresponding to the source file's\n"
79 " path, relative to the build directory. this us be different than\n"
80 " the target's out directory if the source file is in a different\n"
81 " directory than the build.gn file.\n"
82 " \"//foo/bar/baz.txt\" => \"obj/foo/bar\"\n"
83 "\n"
84 "(*) Note on directories\n"
85 "\n"
86 " Paths containing directories (except the source_root_relative_dir)\n"
87 " will be different depending on what context the expansion is evaluated\n"
88 " in. Generally it should \"just work\" but it means you can't\n"
89 " concatenate strings containing these values with reasonable results.\n"
90 "\n"
91 " Details: source expansions can be used in the \"outputs\" variable,\n"
92 " the \"args\" variable, and in calls to \"process_file_template\". The\n"
93 " \"args\" are passed to a script which is run from the build directory,\n"
94 " so these directories will relative to the build directory for the\n"
95 " script to find. In the other cases, the directories will be source-\n"
96 " absolute (begin with a \"//\") because the results of those expansions\n"
97 " will be handled by GN internally.\n"
98 "\n"
99 "Examples\n"
100 "\n"
101 " Non-varying outputs:\n"
102 " action(\"hardcoded_outputs\") {\n"
103 " sources = [ \"input1.idl\", \"input2.idl\" ]\n"
104 " outputs = [ \"$target_out_dir/output1.dat\",\n"
105 " \"$target_out_dir/output2.dat\" ]\n"
106 " }\n"
107 " The outputs in this case will be the two literal files given.\n"
108 "\n"
109 " Varying outputs:\n"
110 " action_foreach(\"varying_outputs\") {\n"
111 " sources = [ \"input1.idl\", \"input2.idl\" ]\n"
112 " outputs = [ \"{{source_gen_dir}}/{{source_name_part}}.h\",\n"
113 " \"{{source_gen_dir}}/{{source_name_part}}.cc\" ]\n"
114 " }\n"
115 " Performing source expansion will result in the following output names:\n"
116 " //out/Debug/obj/mydirectory/input1.h\n"
117 " //out/Debug/obj/mydirectory/input1.cc\n"
118 " //out/Debug/obj/mydirectory/input2.h\n"
119 " //out/Debug/obj/mydirectory/input2.cc\n";
120
121 SubstitutionWriter::SubstitutionWriter() {
122 }
123
124 SubstitutionWriter::~SubstitutionWriter() {
125 }
126
127 // static
128 SourceFile SubstitutionWriter::ApplyPatternToSource(
129 const Settings* settings,
130 const SubstitutionPattern& pattern,
131 const SourceFile& source) {
132 std::string result_value;
133 for (size_t i = 0; i < pattern.ranges().size(); i++) {
134 const SubstitutionPattern::Subrange& subrange = pattern.ranges()[i];
135 if (subrange.type == SUBSTITUTION_LITERAL) {
136 result_value.append(subrange.literal);
137 } else {
138 result_value.append(
139 GetSourceSubstitution(settings, source, subrange.type,
140 OUTPUT_ABSOLUTE, SourceDir()));
141 }
142 }
143 CHECK(!result_value.empty() && result_value[0] == '/')
144 << "The result of the pattern \""
145 << pattern.AsString()
146 << "\" was not a path beginning in \"/\" or \"//\".";
147 return SourceFile(SourceFile::SWAP_IN, &result_value);
148 }
149
150 // static
151 OutputFile SubstitutionWriter::ApplyPatternToSourceAsOutputFile(
152 const Settings* settings,
153 const SubstitutionPattern& pattern,
154 const SourceFile& source) {
155 SourceFile result_as_source = ApplyPatternToSource(settings, pattern, source);
156 CHECK(result_as_source.is_source_absolute())
157 << "The result of the pattern \""
158 << pattern.AsString()
159 << "\" was not an absolute path beginning in \"//\".";
160 return OutputFile(
161 RebaseSourceAbsolutePath(result_as_source.value(),
162 settings->build_settings()->build_dir()));
163 }
164
165 // static
166 void SubstitutionWriter::ApplyListToSource(
167 const Settings* settings,
168 const SubstitutionList& list,
169 const SourceFile& source,
170 std::vector<SourceFile>* output) {
171 for (size_t i = 0; i < list.list().size(); i++) {
172 output->push_back(ApplyPatternToSource(
173 settings, list.list()[i], source));
174 }
175 }
176
177 // static
178 void SubstitutionWriter::ApplyListToSourceAsOutputFile(
179 const Settings* settings,
180 const SubstitutionList& list,
181 const SourceFile& source,
182 std::vector<OutputFile>* output) {
183 for (size_t i = 0; i < list.list().size(); i++) {
184 output->push_back(ApplyPatternToSourceAsOutputFile(
185 settings, list.list()[i], source));
186 }
187 }
188
189 // static
190 void SubstitutionWriter::ApplyListToSources(
191 const Settings* settings,
192 const SubstitutionList& list,
193 const std::vector<SourceFile>& sources,
194 std::vector<SourceFile>* output) {
195 output->clear();
196 for (size_t i = 0; i < sources.size(); i++)
197 ApplyListToSource(settings, list, sources[i], output);
198 }
199
200 // static
201 void SubstitutionWriter::ApplyListToSourcesAsOutputFile(
202 const Settings* settings,
203 const SubstitutionList& list,
204 const std::vector<SourceFile>& sources,
205 std::vector<OutputFile>* output) {
206 output->clear();
207 for (size_t i = 0; i < sources.size(); i++)
208 ApplyListToSourceAsOutputFile(settings, list, sources[i], output);
209 }
210
211 // static
212 void SubstitutionWriter::WriteNinjaVariablesForSource(
213 const Settings* settings,
214 const SourceFile& source,
215 const std::vector<SubstitutionType>& types,
216 const EscapeOptions& escape_options,
217 std::ostream& out) {
218 for (size_t i = 0; i < types.size(); i++) {
219 // Don't write SOURCE since that just maps to Ninja's $in variable, which
220 // is implicit in the rule.
221 if (types[i] != SUBSTITUTION_SOURCE) {
222 out << " " << kSubstitutionNinjaNames[types[i]] << " = ";
223 EscapeStringToStream(
224 out,
225 GetSourceSubstitution(settings, source, types[i], OUTPUT_RELATIVE,
226 settings->build_settings()->build_dir()),
227 escape_options);
228 out << std::endl;
229 }
230 }
231 }
232
233 // static
234 void SubstitutionWriter::WriteWithNinjaVariables(
235 const SubstitutionPattern& pattern,
236 const EscapeOptions& escape_options,
237 std::ostream& out) {
238 // The result needs to be quoted as if it was one string, but the $ for
239 // the inserted Ninja variables can't be escaped. So write to a buffer with
240 // no quoting, and then quote the whole thing if necessary.
241 EscapeOptions no_quoting(escape_options);
242 no_quoting.inhibit_quoting = true;
243
244 bool needs_quotes = false;
245 std::string result;
246 for (size_t i = 0; i < pattern.ranges().size(); i++) {
247 const SubstitutionPattern::Subrange range = pattern.ranges()[i];
248 if (range.type == SUBSTITUTION_LITERAL) {
249 result.append(EscapeString(range.literal, no_quoting, &needs_quotes));
250 } else {
251 result.append("${");
252 result.append(kSubstitutionNinjaNames[range.type]);
253 result.append("}");
254 }
255 }
256
257 if (needs_quotes && !escape_options.inhibit_quoting)
258 out << "\"" << result << "\"";
259 else
260 out << result;
261 /*
scottmg 2014/08/05 22:30:32 What's up here?
262
263 // Special-case the common case of writing a literal.
264 if (pattern.ranges().size() == 1 &&
265 pattern.ranges()[0].type == SUBSTITUTION_LITERAL) {
266 EscapeStringToStream(out, pattern.ranges()[0].literal, escape_options);
267 return;
268 }
269
270 // Need to escape everything after it's been concatenated or else we may end
271 // up with quotes around fragments.
272 std::string result;
273 for (size_t i = 0; i < pattern.ranges().size(); i++) {
274 const SubstitutionPattern::Subrange range = pattern.ranges()[i];
275 if (range.type == SUBSTITUTION_LITERAL) {
276 result.append(range.literal);
277 } else {
278 result.append("${");
279 result.append(kSubstitutionNinjaNames[range.type]);
280 result.append("}");
281 }
282 }
283 EscapeStringToStream(out, result, escape_options);
284 */
285 }
286
287 // static
288 std::string SubstitutionWriter::GetSourceSubstitution(
289 const Settings* settings,
290 const SourceFile& source,
291 SubstitutionType type,
292 OutputStyle output_style,
293 const SourceDir& relative_to) {
294 std::string to_rebase;
295 switch (type) {
296 case SUBSTITUTION_SOURCE:
297 if (source.is_system_absolute())
298 return source.value();
299 to_rebase = source.value();
300 break;
301
302 case SUBSTITUTION_SOURCE_NAME_PART:
303 return FindFilenameNoExtension(&source.value()).as_string();
304
305 case SUBSTITUTION_SOURCE_FILE_PART:
306 return source.GetName();
307
308 case SUBSTITUTION_SOURCE_DIR:
309 if (source.is_system_absolute())
310 return DirectoryWithNoLastSlash(source.GetDir());
311 to_rebase = DirectoryWithNoLastSlash(source.GetDir());
312 break;
313
314 case SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR:
315 if (source.is_system_absolute())
316 return DirectoryWithNoLastSlash(source.GetDir());
317 return RebaseSourceAbsolutePath(
318 DirectoryWithNoLastSlash(source.GetDir()), SourceDir("//"));
319
320 case SUBSTITUTION_SOURCE_GEN_DIR:
321 to_rebase = DirectoryWithNoLastSlash(
322 GetGenDirForSourceDir(settings, source.GetDir()));
323 break;
324
325 case SUBSTITUTION_SOURCE_OUT_DIR:
326 to_rebase = DirectoryWithNoLastSlash(
327 GetOutputDirForSourceDir(settings, source.GetDir()));
328 break;
329
330 default:
331 NOTREACHED();
332 return std::string();
333 }
334
335 // If we get here, the result is a path that should be made relative or
336 // absolute according to the output_style. Other cases (just file name or
337 // extension extraction) will have been handled via early return above.
338 if (output_style == OUTPUT_ABSOLUTE)
339 return to_rebase;
340 return RebaseSourceAbsolutePath(to_rebase, relative_to);
341 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698