DCHECK if shader compilation fails that it's due to context loss.
[chromium-blink-merge.git] / tools / gn / substitution_writer.cc
bloba642e478c98e373a6907d94ee2c73aa183108973
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.
5 #include "tools/gn/substitution_writer.h"
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/string_utils.h"
14 #include "tools/gn/substitution_list.h"
15 #include "tools/gn/substitution_pattern.h"
16 #include "tools/gn/target.h"
18 namespace {
20 // Sets the given directory string to the destination, trimming any trailing
21 // slash from the directory (SourceDirs and OutputFiles representing
22 // directories will end in a trailing slash). If the directory is empty,
23 // it will be replaced with a ".".
24 void SetDirOrDotWithNoSlash(const std::string& dir, std::string* dest) {
25 if (!dir.empty() && dir[dir.size() - 1] == '/')
26 dest->assign(dir.data(), dir.size() - 1);
27 else
28 dest->assign(dir);
30 if (dest->empty())
31 dest->push_back('.');
34 } // namespace
36 const char kSourceExpansion_Help[] =
37 "How Source Expansion Works\n"
38 "\n"
39 " Source expansion is used for the action_foreach and copy target types\n"
40 " to map source file names to output file names or arguments.\n"
41 "\n"
42 " To perform source expansion in the outputs, GN maps every entry in the\n"
43 " sources to every entry in the outputs list, producing the cross\n"
44 " product of all combinations, expanding placeholders (see below).\n"
45 "\n"
46 " Source expansion in the args works similarly, but performing the\n"
47 " placeholder substitution produces a different set of arguments for\n"
48 " each invocation of the script.\n"
49 "\n"
50 " If no placeholders are found, the outputs or args list will be treated\n"
51 " as a static list of literal file names that do not depend on the\n"
52 " sources.\n"
53 "\n"
54 " See \"gn help copy\" and \"gn help action_foreach\" for more on how\n"
55 " this is applied.\n"
56 "\n"
57 "Placeholders\n"
58 "\n"
59 " {{source}}\n"
60 " The name of the source file including directory (*). This will\n"
61 " generally be used for specifying inputs to a script in the\n"
62 " \"args\" variable.\n"
63 " \"//foo/bar/baz.txt\" => \"../../foo/bar/baz.txt\"\n"
64 "\n"
65 " {{source_file_part}}\n"
66 " The file part of the source including the extension.\n"
67 " \"//foo/bar/baz.txt\" => \"baz.txt\"\n"
68 "\n"
69 " {{source_name_part}}\n"
70 " The filename part of the source file with no directory or\n"
71 " extension. This will generally be used for specifying a\n"
72 " transformation from a soruce file to a destination file with the\n"
73 " same name but different extension.\n"
74 " \"//foo/bar/baz.txt\" => \"baz\"\n"
75 "\n"
76 " {{source_dir}}\n"
77 " The directory (*) containing the source file with no\n"
78 " trailing slash.\n"
79 " \"//foo/bar/baz.txt\" => \"../../foo/bar\"\n"
80 "\n"
81 " {{source_root_relative_dir}}\n"
82 " The path to the source file's directory relative to the source\n"
83 " root, with no leading \"//\" or trailing slashes. If the path is\n"
84 " system-absolute, (beginning in a single slash) this will just\n"
85 " return the path with no trailing slash. This value will always\n"
86 " be the same, regardless of whether it appears in the \"outputs\"\n"
87 " or \"args\" section.\n"
88 " \"//foo/bar/baz.txt\" => \"foo/bar\"\n"
89 "\n"
90 " {{source_gen_dir}}\n"
91 " The generated file directory (*) corresponding to the source\n"
92 " file's path. This will be different than the target's generated\n"
93 " file directory if the source file is in a different directory\n"
94 " than the BUILD.gn file.\n"
95 " \"//foo/bar/baz.txt\" => \"gen/foo/bar\"\n"
96 "\n"
97 " {{source_out_dir}}\n"
98 " The object file directory (*) corresponding to the source file's\n"
99 " path, relative to the build directory. this us be different than\n"
100 " the target's out directory if the source file is in a different\n"
101 " directory than the build.gn file.\n"
102 " \"//foo/bar/baz.txt\" => \"obj/foo/bar\"\n"
103 "\n"
104 "(*) Note on directories\n"
105 "\n"
106 " Paths containing directories (except the source_root_relative_dir)\n"
107 " will be different depending on what context the expansion is evaluated\n"
108 " in. Generally it should \"just work\" but it means you can't\n"
109 " concatenate strings containing these values with reasonable results.\n"
110 "\n"
111 " Details: source expansions can be used in the \"outputs\" variable,\n"
112 " the \"args\" variable, and in calls to \"process_file_template\". The\n"
113 " \"args\" are passed to a script which is run from the build directory,\n"
114 " so these directories will relative to the build directory for the\n"
115 " script to find. In the other cases, the directories will be source-\n"
116 " absolute (begin with a \"//\") because the results of those expansions\n"
117 " will be handled by GN internally.\n"
118 "\n"
119 "Examples\n"
120 "\n"
121 " Non-varying outputs:\n"
122 " action(\"hardcoded_outputs\") {\n"
123 " sources = [ \"input1.idl\", \"input2.idl\" ]\n"
124 " outputs = [ \"$target_out_dir/output1.dat\",\n"
125 " \"$target_out_dir/output2.dat\" ]\n"
126 " }\n"
127 " The outputs in this case will be the two literal files given.\n"
128 "\n"
129 " Varying outputs:\n"
130 " action_foreach(\"varying_outputs\") {\n"
131 " sources = [ \"input1.idl\", \"input2.idl\" ]\n"
132 " outputs = [ \"{{source_gen_dir}}/{{source_name_part}}.h\",\n"
133 " \"{{source_gen_dir}}/{{source_name_part}}.cc\" ]\n"
134 " }\n"
135 " Performing source expansion will result in the following output names:\n"
136 " //out/Debug/obj/mydirectory/input1.h\n"
137 " //out/Debug/obj/mydirectory/input1.cc\n"
138 " //out/Debug/obj/mydirectory/input2.h\n"
139 " //out/Debug/obj/mydirectory/input2.cc\n";
141 // static
142 void SubstitutionWriter::WriteWithNinjaVariables(
143 const SubstitutionPattern& pattern,
144 const EscapeOptions& escape_options,
145 std::ostream& out) {
146 // The result needs to be quoted as if it was one string, but the $ for
147 // the inserted Ninja variables can't be escaped. So write to a buffer with
148 // no quoting, and then quote the whole thing if necessary.
149 EscapeOptions no_quoting(escape_options);
150 no_quoting.inhibit_quoting = true;
152 bool needs_quotes = false;
153 std::string result;
154 for (const auto& range : pattern.ranges()) {
155 if (range.type == SUBSTITUTION_LITERAL) {
156 result.append(EscapeString(range.literal, no_quoting, &needs_quotes));
157 } else {
158 result.append("${");
159 result.append(kSubstitutionNinjaNames[range.type]);
160 result.append("}");
164 if (needs_quotes && !escape_options.inhibit_quoting)
165 out << "\"" << result << "\"";
166 else
167 out << result;
170 // static
171 void SubstitutionWriter::GetListAsSourceFiles(
172 const SubstitutionList& list,
173 std::vector<SourceFile>* output) {
174 for (const auto& pattern : list.list()) {
175 CHECK(pattern.ranges().size() == 1 &&
176 pattern.ranges()[0].type == SUBSTITUTION_LITERAL)
177 << "The substitution patterm \""
178 << pattern.AsString()
179 << "\" was expected to be a literal with no {{substitutions}}.";
180 const std::string& literal = pattern.ranges()[0].literal;
181 CHECK(literal.size() >= 1 && literal[0] == '/')
182 << "The result of the pattern \""
183 << pattern.AsString()
184 << "\" was not an absolute path.";
185 output->push_back(SourceFile(literal));
189 // static
190 void SubstitutionWriter::GetListAsOutputFiles(
191 const Settings* settings,
192 const SubstitutionList& list,
193 std::vector<OutputFile>* output) {
194 std::vector<SourceFile> output_as_sources;
195 GetListAsSourceFiles(list, &output_as_sources);
196 for (const auto& file : output_as_sources)
197 output->push_back(OutputFile(settings->build_settings(), file));
200 // static
201 SourceFile SubstitutionWriter::ApplyPatternToSource(
202 const Settings* settings,
203 const SubstitutionPattern& pattern,
204 const SourceFile& source) {
205 std::string result_value = ApplyPatternToSourceAsString(
206 settings, pattern, source);
207 CHECK(!result_value.empty() && result_value[0] == '/')
208 << "The result of the pattern \""
209 << pattern.AsString()
210 << "\" was not a path beginning in \"/\" or \"//\".";
211 return SourceFile(SourceFile::SWAP_IN, &result_value);
214 // static
215 std::string SubstitutionWriter::ApplyPatternToSourceAsString(
216 const Settings* settings,
217 const SubstitutionPattern& pattern,
218 const SourceFile& source) {
219 std::string result_value;
220 for (const auto& subrange : pattern.ranges()) {
221 if (subrange.type == SUBSTITUTION_LITERAL) {
222 result_value.append(subrange.literal);
223 } else {
224 result_value.append(
225 GetSourceSubstitution(settings, source, subrange.type,
226 OUTPUT_ABSOLUTE, SourceDir()));
229 return result_value;
232 // static
233 OutputFile SubstitutionWriter::ApplyPatternToSourceAsOutputFile(
234 const Settings* settings,
235 const SubstitutionPattern& pattern,
236 const SourceFile& source) {
237 SourceFile result_as_source = ApplyPatternToSource(settings, pattern, source);
238 return OutputFile(settings->build_settings(), result_as_source);
241 // static
242 void SubstitutionWriter::ApplyListToSource(
243 const Settings* settings,
244 const SubstitutionList& list,
245 const SourceFile& source,
246 std::vector<SourceFile>* output) {
247 for (const auto& item : list.list())
248 output->push_back(ApplyPatternToSource(settings, item, source));
251 // static
252 void SubstitutionWriter::ApplyListToSourceAsString(
253 const Settings* settings,
254 const SubstitutionList& list,
255 const SourceFile& source,
256 std::vector<std::string>* output) {
257 for (const auto& item : list.list())
258 output->push_back(ApplyPatternToSourceAsString(settings, item, source));
261 // static
262 void SubstitutionWriter::ApplyListToSourceAsOutputFile(
263 const Settings* settings,
264 const SubstitutionList& list,
265 const SourceFile& source,
266 std::vector<OutputFile>* output) {
267 for (const auto& item : list.list())
268 output->push_back(ApplyPatternToSourceAsOutputFile(settings, item, source));
271 // static
272 void SubstitutionWriter::ApplyListToSources(
273 const Settings* settings,
274 const SubstitutionList& list,
275 const std::vector<SourceFile>& sources,
276 std::vector<SourceFile>* output) {
277 output->clear();
278 for (const auto& source : sources)
279 ApplyListToSource(settings, list, source, output);
282 // static
283 void SubstitutionWriter::ApplyListToSourcesAsString(
284 const Settings* settings,
285 const SubstitutionList& list,
286 const std::vector<SourceFile>& sources,
287 std::vector<std::string>* output) {
288 output->clear();
289 for (const auto& source : sources)
290 ApplyListToSourceAsString(settings, list, source, output);
293 // static
294 void SubstitutionWriter::ApplyListToSourcesAsOutputFile(
295 const Settings* settings,
296 const SubstitutionList& list,
297 const std::vector<SourceFile>& sources,
298 std::vector<OutputFile>* output) {
299 output->clear();
300 for (const auto& source : sources)
301 ApplyListToSourceAsOutputFile(settings, list, source, output);
304 // static
305 void SubstitutionWriter::WriteNinjaVariablesForSource(
306 const Settings* settings,
307 const SourceFile& source,
308 const std::vector<SubstitutionType>& types,
309 const EscapeOptions& escape_options,
310 std::ostream& out) {
311 for (const auto& type : types) {
312 // Don't write SOURCE since that just maps to Ninja's $in variable, which
313 // is implicit in the rule.
314 if (type != SUBSTITUTION_SOURCE) {
315 out << " " << kSubstitutionNinjaNames[type] << " = ";
316 EscapeStringToStream(
317 out,
318 GetSourceSubstitution(settings, source, type, OUTPUT_RELATIVE,
319 settings->build_settings()->build_dir()),
320 escape_options);
321 out << std::endl;
326 // static
327 std::string SubstitutionWriter::GetSourceSubstitution(
328 const Settings* settings,
329 const SourceFile& source,
330 SubstitutionType type,
331 OutputStyle output_style,
332 const SourceDir& relative_to) {
333 std::string to_rebase;
334 switch (type) {
335 case SUBSTITUTION_SOURCE:
336 if (source.is_system_absolute())
337 return source.value();
338 to_rebase = source.value();
339 break;
341 case SUBSTITUTION_SOURCE_NAME_PART:
342 return FindFilenameNoExtension(&source.value()).as_string();
344 case SUBSTITUTION_SOURCE_FILE_PART:
345 return source.GetName();
347 case SUBSTITUTION_SOURCE_DIR:
348 if (source.is_system_absolute())
349 return DirectoryWithNoLastSlash(source.GetDir());
350 to_rebase = DirectoryWithNoLastSlash(source.GetDir());
351 break;
353 case SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR:
354 if (source.is_system_absolute())
355 return DirectoryWithNoLastSlash(source.GetDir());
356 return RebasePath(
357 DirectoryWithNoLastSlash(source.GetDir()), SourceDir("//"),
358 settings->build_settings()->root_path_utf8());
360 case SUBSTITUTION_SOURCE_GEN_DIR:
361 to_rebase = DirectoryWithNoLastSlash(
362 GetGenDirForSourceDir(settings, source.GetDir()));
363 break;
365 case SUBSTITUTION_SOURCE_OUT_DIR:
366 to_rebase = DirectoryWithNoLastSlash(
367 GetOutputDirForSourceDir(settings, source.GetDir()));
368 break;
370 default:
371 NOTREACHED()
372 << "Unsupported substitution for this function: "
373 << kSubstitutionNames[type];
374 return std::string();
377 // If we get here, the result is a path that should be made relative or
378 // absolute according to the output_style. Other cases (just file name or
379 // extension extraction) will have been handled via early return above.
380 if (output_style == OUTPUT_ABSOLUTE)
381 return to_rebase;
382 return RebasePath(to_rebase, relative_to,
383 settings->build_settings()->root_path_utf8());
386 // static
387 OutputFile SubstitutionWriter::ApplyPatternToTargetAsOutputFile(
388 const Target* target,
389 const Tool* tool,
390 const SubstitutionPattern& pattern) {
391 std::string result_value;
392 for (const auto& subrange : pattern.ranges()) {
393 if (subrange.type == SUBSTITUTION_LITERAL) {
394 result_value.append(subrange.literal);
395 } else {
396 std::string subst;
397 CHECK(GetTargetSubstitution(target, subrange.type, &subst));
398 result_value.append(subst);
401 return OutputFile(result_value);
404 // static
405 void SubstitutionWriter::ApplyListToTargetAsOutputFile(
406 const Target* target,
407 const Tool* tool,
408 const SubstitutionList& list,
409 std::vector<OutputFile>* output) {
410 for (const auto& item : list.list())
411 output->push_back(ApplyPatternToTargetAsOutputFile(target, tool, item));
414 // static
415 bool SubstitutionWriter::GetTargetSubstitution(
416 const Target* target,
417 SubstitutionType type,
418 std::string* result) {
419 switch (type) {
420 case SUBSTITUTION_LABEL:
421 // Only include the toolchain for non-default toolchains.
422 *result = target->label().GetUserVisibleName(
423 !target->settings()->is_default());
424 break;
425 case SUBSTITUTION_ROOT_GEN_DIR:
426 SetDirOrDotWithNoSlash(
427 GetToolchainGenDirAsOutputFile(target->settings()).value(),
428 result);
429 break;
430 case SUBSTITUTION_ROOT_OUT_DIR:
431 SetDirOrDotWithNoSlash(
432 target->settings()->toolchain_output_subdir().value(),
433 result);
434 break;
435 case SUBSTITUTION_TARGET_GEN_DIR:
436 SetDirOrDotWithNoSlash(
437 GetTargetGenDirAsOutputFile(target).value(),
438 result);
439 break;
440 case SUBSTITUTION_TARGET_OUT_DIR:
441 SetDirOrDotWithNoSlash(
442 GetTargetOutputDirAsOutputFile(target).value(),
443 result);
444 break;
445 case SUBSTITUTION_TARGET_OUTPUT_NAME:
446 *result = target->GetComputedOutputName(true);
447 break;
448 default:
449 return false;
451 return true;
454 // static
455 std::string SubstitutionWriter::GetTargetSubstitution(
456 const Target* target,
457 SubstitutionType type) {
458 std::string result;
459 GetTargetSubstitution(target, type, &result);
460 return result;
463 // static
464 OutputFile SubstitutionWriter::ApplyPatternToCompilerAsOutputFile(
465 const Target* target,
466 const SourceFile& source,
467 const SubstitutionPattern& pattern) {
468 OutputFile result;
469 for (const auto& subrange : pattern.ranges()) {
470 if (subrange.type == SUBSTITUTION_LITERAL) {
471 result.value().append(subrange.literal);
472 } else {
473 result.value().append(
474 GetCompilerSubstitution(target, source, subrange.type));
477 return result;
480 // static
481 void SubstitutionWriter::ApplyListToCompilerAsOutputFile(
482 const Target* target,
483 const SourceFile& source,
484 const SubstitutionList& list,
485 std::vector<OutputFile>* output) {
486 for (const auto& item : list.list())
487 output->push_back(ApplyPatternToCompilerAsOutputFile(target, source, item));
490 // static
491 std::string SubstitutionWriter::GetCompilerSubstitution(
492 const Target* target,
493 const SourceFile& source,
494 SubstitutionType type) {
495 // First try the common tool ones.
496 std::string result;
497 if (GetTargetSubstitution(target, type, &result))
498 return result;
500 // Fall-through to the source ones.
501 return GetSourceSubstitution(
502 target->settings(), source, type, OUTPUT_RELATIVE,
503 target->settings()->build_settings()->build_dir());
506 // static
507 OutputFile SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
508 const Target* target,
509 const Tool* tool,
510 const SubstitutionPattern& pattern) {
511 OutputFile result;
512 for (const auto& subrange : pattern.ranges()) {
513 if (subrange.type == SUBSTITUTION_LITERAL) {
514 result.value().append(subrange.literal);
515 } else {
516 result.value().append(GetLinkerSubstitution(target, tool, subrange.type));
519 return result;
522 // static
523 void SubstitutionWriter::ApplyListToLinkerAsOutputFile(
524 const Target* target,
525 const Tool* tool,
526 const SubstitutionList& list,
527 std::vector<OutputFile>* output) {
528 for (const auto& item : list.list())
529 output->push_back(ApplyPatternToLinkerAsOutputFile(target, tool, item));
532 // static
533 std::string SubstitutionWriter::GetLinkerSubstitution(
534 const Target* target,
535 const Tool* tool,
536 SubstitutionType type) {
537 // First try the common tool ones.
538 std::string result;
539 if (GetTargetSubstitution(target, type, &result))
540 return result;
542 // Fall-through to the linker-specific ones.
543 switch (type) {
544 case SUBSTITUTION_OUTPUT_EXTENSION:
545 // Use the extension provided on the target if nonempty, otherwise
546 // fall back on the default. Note that the target's output extension
547 // does not include the dot but the tool's does.
548 if (target->output_extension().empty())
549 return tool->default_output_extension();
550 return std::string(".") + target->output_extension();
552 default:
553 NOTREACHED();
554 return std::string();