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

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

Issue 440333002: Support more configurability in GN toolchains (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: unsigned check 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
« no previous file with comments | « tools/gn/substitution_writer.h ('k') | tools/gn/substitution_writer_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "tools/gn/substitution_writer.h" 5 #include "tools/gn/substitution_writer.h"
6 6
7 #include "tools/gn/build_settings.h" 7 #include "tools/gn/build_settings.h"
8 #include "tools/gn/escape.h" 8 #include "tools/gn/escape.h"
9 #include "tools/gn/filesystem_utils.h" 9 #include "tools/gn/filesystem_utils.h"
10 #include "tools/gn/output_file.h" 10 #include "tools/gn/output_file.h"
11 #include "tools/gn/settings.h" 11 #include "tools/gn/settings.h"
12 #include "tools/gn/source_file.h" 12 #include "tools/gn/source_file.h"
13 #include "tools/gn/string_utils.h"
13 #include "tools/gn/substitution_list.h" 14 #include "tools/gn/substitution_list.h"
14 #include "tools/gn/substitution_pattern.h" 15 #include "tools/gn/substitution_pattern.h"
16 #include "tools/gn/target.h"
17
18 namespace {
19
20 // This happens when the output of a substitution looks like
21 // <some_output_dir>/<other_stuff>. and we're computing a file in the output
22 // directory. If <some_output_dir> resolves to the empty string because it
23 // refers to the root build directory, the result will start with a slash which
24 // is wrong.
25 //
26 // There are several possible solutions:
27 //
28 // - Could convert empty directories to a ".". However, this looks weird in the
29 // Ninja file and Ninja doesn't canonicalize this away.
30 //
31 // - Make all substitutions compute SourceFiles and then convert to
32 // OutputFiles. The root_build_dir will never be empty in this case, and the
33 // Rebase function will properly strip the slash away when it is rebased to
34 // be relative to the output directory. However, we never need compiler and
35 // linker outputs as SourceFiles, and we do a lot of these conversions which
36 // requires a lot of unnecessary path rebasing.
37 //
38 // - Detect this as a special case and delete the slash.
39 //
40 // This function implements the special case solution. This problem only arises
41 // in the very specific case where we're appending a literal beginning in a
42 // slash, the result string is empty, and the preceeding pattern identifies
43 // an output directory.
44 //
45 // If we find too many problems with this implementation, it would probably be
46 // cleanest to implement the "round trip through SourceFile" solution for
47 // simplicity and guaranteed correctness, rather than adding even more special
48 // cases.
49 //
50 // This function only needs to be called when computing substitutions as
51 // OutputFiles (which are relative to the build dir) and not round-tripping
52 // through SourceFiles.
53 void AppendLiteralWithPossibleSlashEliding(
54 const std::vector<SubstitutionPattern::Subrange>& ranges,
55 size_t literal_index,
56 std::string* result) {
57 const std::string& literal = ranges[literal_index].literal;
58
59 if (// When the literal's index is 0 and it begins with a slash the user
60 // must have wanted it to start with a slash. Likewise, if it's 2 or
61 // more, it's impossible to have a length > 1 sequence of substitutions
62 // that both make sense as a path and resolve to the build directory.
63 literal_index != 1 ||
64 // When the result is nonempty, appending the slash as a separator is
65 // always OK.
66 !result->empty() ||
67 // If the literal doesn't begin in a slash, appending directly is fine.
68 literal.empty() || literal[0] != '/') {
69 result->append(literal);
70 return;
71 }
72
73 // If we get here, we need to collapse the slash. Assert that the first
74 // substitution should have ended up in the output directory. This should
75 // have already been checked since linker and compiler outputs (which is
76 // what this is used for) should always bein the output directory.
77 DCHECK(SubstitutionIsInOutputDir(ranges[0].type));
78 result->append(&literal[1], literal.size() - 1);
79 }
80
81 } // namespace
15 82
16 const char kSourceExpansion_Help[] = 83 const char kSourceExpansion_Help[] =
17 "How Source Expansion Works\n" 84 "How Source Expansion Works\n"
18 "\n" 85 "\n"
19 " Source expansion is used for the action_foreach and copy target types\n" 86 " 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" 87 " to map source file names to output file names or arguments.\n"
21 "\n" 88 "\n"
22 " To perform source expansion in the outputs, GN maps every entry in the\n" 89 " 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" 90 " sources to every entry in the outputs list, producing the cross\n"
24 " product of all combinations, expanding placeholders (see below).\n" 91 " product of all combinations, expanding placeholders (see below).\n"
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
111 " sources = [ \"input1.idl\", \"input2.idl\" ]\n" 178 " sources = [ \"input1.idl\", \"input2.idl\" ]\n"
112 " outputs = [ \"{{source_gen_dir}}/{{source_name_part}}.h\",\n" 179 " outputs = [ \"{{source_gen_dir}}/{{source_name_part}}.h\",\n"
113 " \"{{source_gen_dir}}/{{source_name_part}}.cc\" ]\n" 180 " \"{{source_gen_dir}}/{{source_name_part}}.cc\" ]\n"
114 " }\n" 181 " }\n"
115 " Performing source expansion will result in the following output names:\n" 182 " Performing source expansion will result in the following output names:\n"
116 " //out/Debug/obj/mydirectory/input1.h\n" 183 " //out/Debug/obj/mydirectory/input1.h\n"
117 " //out/Debug/obj/mydirectory/input1.cc\n" 184 " //out/Debug/obj/mydirectory/input1.cc\n"
118 " //out/Debug/obj/mydirectory/input2.h\n" 185 " //out/Debug/obj/mydirectory/input2.h\n"
119 " //out/Debug/obj/mydirectory/input2.cc\n"; 186 " //out/Debug/obj/mydirectory/input2.cc\n";
120 187
121 SubstitutionWriter::SubstitutionWriter() { 188 // static
122 } 189 void SubstitutionWriter::WriteWithNinjaVariables(
190 const SubstitutionPattern& pattern,
191 const EscapeOptions& escape_options,
192 std::ostream& out) {
193 // The result needs to be quoted as if it was one string, but the $ for
194 // the inserted Ninja variables can't be escaped. So write to a buffer with
195 // no quoting, and then quote the whole thing if necessary.
196 EscapeOptions no_quoting(escape_options);
197 no_quoting.inhibit_quoting = true;
123 198
124 SubstitutionWriter::~SubstitutionWriter() { 199 bool needs_quotes = false;
200 std::string result;
201 for (size_t i = 0; i < pattern.ranges().size(); i++) {
202 const SubstitutionPattern::Subrange range = pattern.ranges()[i];
203 if (range.type == SUBSTITUTION_LITERAL) {
204 result.append(EscapeString(range.literal, no_quoting, &needs_quotes));
205 } else {
206 result.append("${");
207 result.append(kSubstitutionNinjaNames[range.type]);
208 result.append("}");
209 }
210 }
211
212 if (needs_quotes && !escape_options.inhibit_quoting)
213 out << "\"" << result << "\"";
214 else
215 out << result;
125 } 216 }
126 217
127 // static 218 // static
128 void SubstitutionWriter::GetListAsSourceFiles( 219 void SubstitutionWriter::GetListAsSourceFiles(
129 const Settings* settings,
130 const SubstitutionList& list, 220 const SubstitutionList& list,
131 std::vector<SourceFile>* output) { 221 std::vector<SourceFile>* output) {
132 for (size_t i = 0; i < list.list().size(); i++) { 222 for (size_t i = 0; i < list.list().size(); i++) {
133 const SubstitutionPattern& pattern = list.list()[i]; 223 const SubstitutionPattern& pattern = list.list()[i];
134 CHECK(pattern.ranges().size() == 1 && 224 CHECK(pattern.ranges().size() == 1 &&
135 pattern.ranges()[0].type == SUBSTITUTION_LITERAL) 225 pattern.ranges()[0].type == SUBSTITUTION_LITERAL)
136 << "The substitution patterm \"" 226 << "The substitution patterm \""
137 << pattern.AsString() 227 << pattern.AsString()
138 << "\" was expected to be a literal with no {{substitutions}}."; 228 << "\" was expected to be a literal with no {{substitutions}}.";
139 const std::string& literal = pattern.ranges()[0].literal; 229 const std::string& literal = pattern.ranges()[0].literal;
140 CHECK(literal.size() >= 1 && literal[0] == '/') 230 CHECK(literal.size() >= 1 && literal[0] == '/')
141 << "The result of the pattern \"" 231 << "The result of the pattern \""
142 << pattern.AsString() 232 << pattern.AsString()
143 << "\" was not an absolute path."; 233 << "\" was not an absolute path.";
144 output->push_back(SourceFile(literal)); 234 output->push_back(SourceFile(literal));
145 } 235 }
146 } 236 }
147 237
238 // static
148 void SubstitutionWriter::GetListAsOutputFiles( 239 void SubstitutionWriter::GetListAsOutputFiles(
149 const Settings* settings, 240 const Settings* settings,
150 const SubstitutionList& list, 241 const SubstitutionList& list,
151 std::vector<OutputFile>* output) { 242 std::vector<OutputFile>* output) {
152 std::vector<SourceFile> output_as_sources; 243 std::vector<SourceFile> output_as_sources;
153 GetListAsSourceFiles(settings, list, &output_as_sources); 244 GetListAsSourceFiles(list, &output_as_sources);
154 for (size_t i = 0; i < output_as_sources.size(); i++) { 245 for (size_t i = 0; i < output_as_sources.size(); i++) {
155 output->push_back(OutputFile( 246 output->push_back(OutputFile(settings->build_settings(),
156 RebaseSourceAbsolutePath(output_as_sources[i].value(), 247 output_as_sources[i]));
157 settings->build_settings()->build_dir())));
158 } 248 }
159 } 249 }
160 250
161 // static 251 // static
162 SourceFile SubstitutionWriter::ApplyPatternToSource( 252 SourceFile SubstitutionWriter::ApplyPatternToSource(
163 const Settings* settings, 253 const Settings* settings,
164 const SubstitutionPattern& pattern, 254 const SubstitutionPattern& pattern,
165 const SourceFile& source) { 255 const SourceFile& source) {
166 std::string result_value = ApplyPatternToSourceAsString( 256 std::string result_value = ApplyPatternToSourceAsString(
167 settings, pattern, source); 257 settings, pattern, source);
(...skipping 26 matching lines...) Expand all
194 // static 284 // static
195 OutputFile SubstitutionWriter::ApplyPatternToSourceAsOutputFile( 285 OutputFile SubstitutionWriter::ApplyPatternToSourceAsOutputFile(
196 const Settings* settings, 286 const Settings* settings,
197 const SubstitutionPattern& pattern, 287 const SubstitutionPattern& pattern,
198 const SourceFile& source) { 288 const SourceFile& source) {
199 SourceFile result_as_source = ApplyPatternToSource(settings, pattern, source); 289 SourceFile result_as_source = ApplyPatternToSource(settings, pattern, source);
200 CHECK(result_as_source.is_source_absolute()) 290 CHECK(result_as_source.is_source_absolute())
201 << "The result of the pattern \"" 291 << "The result of the pattern \""
202 << pattern.AsString() 292 << pattern.AsString()
203 << "\" was not an absolute path beginning in \"//\"."; 293 << "\" was not an absolute path beginning in \"//\".";
204 return OutputFile( 294 return OutputFile(settings->build_settings(), result_as_source);
205 RebaseSourceAbsolutePath(result_as_source.value(),
206 settings->build_settings()->build_dir()));
207 } 295 }
208 296
209 // static 297 // static
210 void SubstitutionWriter::ApplyListToSource( 298 void SubstitutionWriter::ApplyListToSource(
211 const Settings* settings, 299 const Settings* settings,
212 const SubstitutionList& list, 300 const SubstitutionList& list,
213 const SourceFile& source, 301 const SourceFile& source,
214 std::vector<SourceFile>* output) { 302 std::vector<SourceFile>* output) {
215 for (size_t i = 0; i < list.list().size(); i++) { 303 for (size_t i = 0; i < list.list().size(); i++) {
216 output->push_back(ApplyPatternToSource( 304 output->push_back(ApplyPatternToSource(
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
291 out, 379 out,
292 GetSourceSubstitution(settings, source, types[i], OUTPUT_RELATIVE, 380 GetSourceSubstitution(settings, source, types[i], OUTPUT_RELATIVE,
293 settings->build_settings()->build_dir()), 381 settings->build_settings()->build_dir()),
294 escape_options); 382 escape_options);
295 out << std::endl; 383 out << std::endl;
296 } 384 }
297 } 385 }
298 } 386 }
299 387
300 // static 388 // static
301 void SubstitutionWriter::WriteWithNinjaVariables(
302 const SubstitutionPattern& pattern,
303 const EscapeOptions& escape_options,
304 std::ostream& out) {
305 // The result needs to be quoted as if it was one string, but the $ for
306 // the inserted Ninja variables can't be escaped. So write to a buffer with
307 // no quoting, and then quote the whole thing if necessary.
308 EscapeOptions no_quoting(escape_options);
309 no_quoting.inhibit_quoting = true;
310
311 bool needs_quotes = false;
312 std::string result;
313 for (size_t i = 0; i < pattern.ranges().size(); i++) {
314 const SubstitutionPattern::Subrange range = pattern.ranges()[i];
315 if (range.type == SUBSTITUTION_LITERAL) {
316 result.append(EscapeString(range.literal, no_quoting, &needs_quotes));
317 } else {
318 result.append("${");
319 result.append(kSubstitutionNinjaNames[range.type]);
320 result.append("}");
321 }
322 }
323
324 if (needs_quotes && !escape_options.inhibit_quoting)
325 out << "\"" << result << "\"";
326 else
327 out << result;
328 }
329
330 // static
331 std::string SubstitutionWriter::GetSourceSubstitution( 389 std::string SubstitutionWriter::GetSourceSubstitution(
332 const Settings* settings, 390 const Settings* settings,
333 const SourceFile& source, 391 const SourceFile& source,
334 SubstitutionType type, 392 SubstitutionType type,
335 OutputStyle output_style, 393 OutputStyle output_style,
336 const SourceDir& relative_to) { 394 const SourceDir& relative_to) {
337 std::string to_rebase; 395 std::string to_rebase;
338 switch (type) { 396 switch (type) {
339 case SUBSTITUTION_SOURCE: 397 case SUBSTITUTION_SOURCE:
340 if (source.is_system_absolute()) 398 if (source.is_system_absolute())
(...skipping 23 matching lines...) Expand all
364 to_rebase = DirectoryWithNoLastSlash( 422 to_rebase = DirectoryWithNoLastSlash(
365 GetGenDirForSourceDir(settings, source.GetDir())); 423 GetGenDirForSourceDir(settings, source.GetDir()));
366 break; 424 break;
367 425
368 case SUBSTITUTION_SOURCE_OUT_DIR: 426 case SUBSTITUTION_SOURCE_OUT_DIR:
369 to_rebase = DirectoryWithNoLastSlash( 427 to_rebase = DirectoryWithNoLastSlash(
370 GetOutputDirForSourceDir(settings, source.GetDir())); 428 GetOutputDirForSourceDir(settings, source.GetDir()));
371 break; 429 break;
372 430
373 default: 431 default:
374 NOTREACHED(); 432 NOTREACHED()
433 << "Unsupported substitution for this function: "
434 << kSubstitutionNames[type];
375 return std::string(); 435 return std::string();
376 } 436 }
377 437
378 // If we get here, the result is a path that should be made relative or 438 // If we get here, the result is a path that should be made relative or
379 // absolute according to the output_style. Other cases (just file name or 439 // absolute according to the output_style. Other cases (just file name or
380 // extension extraction) will have been handled via early return above. 440 // extension extraction) will have been handled via early return above.
381 if (output_style == OUTPUT_ABSOLUTE) 441 if (output_style == OUTPUT_ABSOLUTE)
382 return to_rebase; 442 return to_rebase;
383 return RebaseSourceAbsolutePath(to_rebase, relative_to); 443 return RebaseSourceAbsolutePath(to_rebase, relative_to);
384 } 444 }
445
446 // static
447 OutputFile SubstitutionWriter::ApplyPatternToTargetAsOutputFile(
448 const Target* target,
449 const Tool* tool,
450 const SubstitutionPattern& pattern) {
451 std::string result_value;
452 for (size_t i = 0; i < pattern.ranges().size(); i++) {
453 const SubstitutionPattern::Subrange& subrange = pattern.ranges()[i];
454 if (subrange.type == SUBSTITUTION_LITERAL) {
455 result_value.append(subrange.literal);
456 } else {
457 std::string subst;
458 CHECK(GetTargetSubstitution(target, subrange.type, &subst));
459 result_value.append(subst);
460 }
461 }
462 return OutputFile(result_value);
463 }
464
465 // static
466 void SubstitutionWriter::ApplyListToTargetAsOutputFile(
467 const Target* target,
468 const Tool* tool,
469 const SubstitutionList& list,
470 std::vector<OutputFile>* output) {
471 for (size_t i = 0; i < list.list().size(); i++) {
472 output->push_back(ApplyPatternToTargetAsOutputFile(
473 target, tool, list.list()[i]));
474 }
475 }
476
477 // static
478 bool SubstitutionWriter::GetTargetSubstitution(
479 const Target* target,
480 SubstitutionType type,
481 std::string* result) {
482 switch (type) {
483 case SUBSTITUTION_LABEL:
484 // Only include the toolchain for non-default toolchains.
485 *result = target->label().GetUserVisibleName(
486 !target->settings()->is_default());
487 break;
488 case SUBSTITUTION_ROOT_GEN_DIR:
489 *result = GetToolchainGenDirAsOutputFile(target->settings()).value();
490 TrimTrailingSlash(result);
491 break;
492 case SUBSTITUTION_ROOT_OUT_DIR:
493 *result = target->settings()->toolchain_output_subdir().value();
494 TrimTrailingSlash(result);
495 break;
496 case SUBSTITUTION_TARGET_GEN_DIR:
497 *result = GetTargetGenDirAsOutputFile(target).value();
498 TrimTrailingSlash(result);
499 break;
500 case SUBSTITUTION_TARGET_OUT_DIR:
501 *result = GetTargetOutputDirAsOutputFile(target).value();
502 TrimTrailingSlash(result);
503 break;
504 case SUBSTITUTION_TARGET_OUTPUT_NAME:
505 *result = target->GetComputedOutputName(true);
506 break;
507 default:
508 return false;
509 }
510 return true;
511 }
512
513 // static
514 std::string SubstitutionWriter::GetTargetSubstitution(
515 const Target* target,
516 SubstitutionType type) {
517 std::string result;
518 GetTargetSubstitution(target, type, &result);
519 return result;
520 }
521
522 // static
523 OutputFile SubstitutionWriter::ApplyPatternToCompilerAsOutputFile(
524 const Target* target,
525 const SourceFile& source,
526 const SubstitutionPattern& pattern) {
527 OutputFile result;
528 for (size_t i = 0; i < pattern.ranges().size(); i++) {
529 const SubstitutionPattern::Subrange& subrange = pattern.ranges()[i];
530 if (subrange.type == SUBSTITUTION_LITERAL) {
531 AppendLiteralWithPossibleSlashEliding(
532 pattern.ranges(), i, &result.value());
533 } else {
534 result.value().append(
535 GetCompilerSubstitution(target, source, subrange.type));
536 }
537 }
538 return result;
539 }
540
541 // static
542 void SubstitutionWriter::ApplyListToCompilerAsOutputFile(
543 const Target* target,
544 const SourceFile& source,
545 const SubstitutionList& list,
546 std::vector<OutputFile>* output) {
547 for (size_t i = 0; i < list.list().size(); i++) {
548 output->push_back(ApplyPatternToCompilerAsOutputFile(
549 target, source, list.list()[i]));
550 }
551 }
552
553 // static
554 std::string SubstitutionWriter::GetCompilerSubstitution(
555 const Target* target,
556 const SourceFile& source,
557 SubstitutionType type) {
558 // First try the common tool ones.
559 std::string result;
560 if (GetTargetSubstitution(target, type, &result))
561 return result;
562
563 // Fall-through to the source ones.
564 return GetSourceSubstitution(
565 target->settings(), source, type, OUTPUT_RELATIVE,
566 target->settings()->build_settings()->build_dir());
567 }
568
569 // static
570 OutputFile SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
571 const Target* target,
572 const Tool* tool,
573 const SubstitutionPattern& pattern) {
574 OutputFile result;
575 for (size_t i = 0; i < pattern.ranges().size(); i++) {
576 const SubstitutionPattern::Subrange& subrange = pattern.ranges()[i];
577 if (subrange.type == SUBSTITUTION_LITERAL) {
578 AppendLiteralWithPossibleSlashEliding(
579 pattern.ranges(), i, &result.value());
580 } else {
581 result.value().append(GetLinkerSubstitution(target, tool, subrange.type));
582 }
583 }
584 return result;
585 }
586
587 // static
588 void SubstitutionWriter::ApplyListToLinkerAsOutputFile(
589 const Target* target,
590 const Tool* tool,
591 const SubstitutionList& list,
592 std::vector<OutputFile>* output) {
593 for (size_t i = 0; i < list.list().size(); i++) {
594 output->push_back(ApplyPatternToLinkerAsOutputFile(
595 target, tool, list.list()[i]));
596 }
597 }
598
599 // static
600 std::string SubstitutionWriter::GetLinkerSubstitution(
601 const Target* target,
602 const Tool* tool,
603 SubstitutionType type) {
604 // First try the common tool ones.
605 std::string result;
606 if (GetTargetSubstitution(target, type, &result))
607 return result;
608
609 // Fall-through to the linker-specific ones.
610 switch (type) {
611 case SUBSTITUTION_OUTPUT_EXTENSION:
612 // Use the extension provided on the target if nonempty, otherwise
613 // fall back on the default. Note that the target's output extension
614 // does not include the dot but the tool's does.
615 if (target->output_extension().empty())
616 return tool->default_output_extension();
617 return std::string(".") + target->output_extension();
618
619 default:
620 NOTREACHED();
621 return std::string();
622 }
623 }
OLDNEW
« no previous file with comments | « tools/gn/substitution_writer.h ('k') | tools/gn/substitution_writer_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698