OLD | NEW |
---|---|
(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 } | |
OLD | NEW |