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

Unified 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tools/gn/substitution_writer.h ('k') | tools/gn/substitution_writer_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/gn/substitution_writer.cc
diff --git a/tools/gn/substitution_writer.cc b/tools/gn/substitution_writer.cc
index d474381ea289d4f7886dd533261341d45ab43fb6..3a41b380173a62c2b30d5fe0c514eb904cfbffeb 100644
--- a/tools/gn/substitution_writer.cc
+++ b/tools/gn/substitution_writer.cc
@@ -10,8 +10,75 @@
#include "tools/gn/output_file.h"
#include "tools/gn/settings.h"
#include "tools/gn/source_file.h"
+#include "tools/gn/string_utils.h"
#include "tools/gn/substitution_list.h"
#include "tools/gn/substitution_pattern.h"
+#include "tools/gn/target.h"
+
+namespace {
+
+// This happens when the output of a substitution looks like
+// <some_output_dir>/<other_stuff>. and we're computing a file in the output
+// directory. If <some_output_dir> resolves to the empty string because it
+// refers to the root build directory, the result will start with a slash which
+// is wrong.
+//
+// There are several possible solutions:
+//
+// - Could convert empty directories to a ".". However, this looks weird in the
+// Ninja file and Ninja doesn't canonicalize this away.
+//
+// - Make all substitutions compute SourceFiles and then convert to
+// OutputFiles. The root_build_dir will never be empty in this case, and the
+// Rebase function will properly strip the slash away when it is rebased to
+// be relative to the output directory. However, we never need compiler and
+// linker outputs as SourceFiles, and we do a lot of these conversions which
+// requires a lot of unnecessary path rebasing.
+//
+// - Detect this as a special case and delete the slash.
+//
+// This function implements the special case solution. This problem only arises
+// in the very specific case where we're appending a literal beginning in a
+// slash, the result string is empty, and the preceeding pattern identifies
+// an output directory.
+//
+// If we find too many problems with this implementation, it would probably be
+// cleanest to implement the "round trip through SourceFile" solution for
+// simplicity and guaranteed correctness, rather than adding even more special
+// cases.
+//
+// This function only needs to be called when computing substitutions as
+// OutputFiles (which are relative to the build dir) and not round-tripping
+// through SourceFiles.
+void AppendLiteralWithPossibleSlashEliding(
+ const std::vector<SubstitutionPattern::Subrange>& ranges,
+ size_t literal_index,
+ std::string* result) {
+ const std::string& literal = ranges[literal_index].literal;
+
+ if (// When the literal's index is 0 and it begins with a slash the user
+ // must have wanted it to start with a slash. Likewise, if it's 2 or
+ // more, it's impossible to have a length > 1 sequence of substitutions
+ // that both make sense as a path and resolve to the build directory.
+ literal_index != 1 ||
+ // When the result is nonempty, appending the slash as a separator is
+ // always OK.
+ !result->empty() ||
+ // If the literal doesn't begin in a slash, appending directly is fine.
+ literal.empty() || literal[0] != '/') {
+ result->append(literal);
+ return;
+ }
+
+ // If we get here, we need to collapse the slash. Assert that the first
+ // substitution should have ended up in the output directory. This should
+ // have already been checked since linker and compiler outputs (which is
+ // what this is used for) should always bein the output directory.
+ DCHECK(SubstitutionIsInOutputDir(ranges[0].type));
+ result->append(&literal[1], literal.size() - 1);
+}
+
+} // namespace
const char kSourceExpansion_Help[] =
"How Source Expansion Works\n"
@@ -118,15 +185,38 @@ const char kSourceExpansion_Help[] =
" //out/Debug/obj/mydirectory/input2.h\n"
" //out/Debug/obj/mydirectory/input2.cc\n";
-SubstitutionWriter::SubstitutionWriter() {
-}
+// static
+void SubstitutionWriter::WriteWithNinjaVariables(
+ const SubstitutionPattern& pattern,
+ const EscapeOptions& escape_options,
+ std::ostream& out) {
+ // The result needs to be quoted as if it was one string, but the $ for
+ // the inserted Ninja variables can't be escaped. So write to a buffer with
+ // no quoting, and then quote the whole thing if necessary.
+ EscapeOptions no_quoting(escape_options);
+ no_quoting.inhibit_quoting = true;
-SubstitutionWriter::~SubstitutionWriter() {
+ bool needs_quotes = false;
+ std::string result;
+ for (size_t i = 0; i < pattern.ranges().size(); i++) {
+ const SubstitutionPattern::Subrange range = pattern.ranges()[i];
+ if (range.type == SUBSTITUTION_LITERAL) {
+ result.append(EscapeString(range.literal, no_quoting, &needs_quotes));
+ } else {
+ result.append("${");
+ result.append(kSubstitutionNinjaNames[range.type]);
+ result.append("}");
+ }
+ }
+
+ if (needs_quotes && !escape_options.inhibit_quoting)
+ out << "\"" << result << "\"";
+ else
+ out << result;
}
// static
void SubstitutionWriter::GetListAsSourceFiles(
- const Settings* settings,
const SubstitutionList& list,
std::vector<SourceFile>* output) {
for (size_t i = 0; i < list.list().size(); i++) {
@@ -145,16 +235,16 @@ void SubstitutionWriter::GetListAsSourceFiles(
}
}
+// static
void SubstitutionWriter::GetListAsOutputFiles(
const Settings* settings,
const SubstitutionList& list,
std::vector<OutputFile>* output) {
std::vector<SourceFile> output_as_sources;
- GetListAsSourceFiles(settings, list, &output_as_sources);
+ GetListAsSourceFiles(list, &output_as_sources);
for (size_t i = 0; i < output_as_sources.size(); i++) {
- output->push_back(OutputFile(
- RebaseSourceAbsolutePath(output_as_sources[i].value(),
- settings->build_settings()->build_dir())));
+ output->push_back(OutputFile(settings->build_settings(),
+ output_as_sources[i]));
}
}
@@ -201,9 +291,7 @@ OutputFile SubstitutionWriter::ApplyPatternToSourceAsOutputFile(
<< "The result of the pattern \""
<< pattern.AsString()
<< "\" was not an absolute path beginning in \"//\".";
- return OutputFile(
- RebaseSourceAbsolutePath(result_as_source.value(),
- settings->build_settings()->build_dir()));
+ return OutputFile(settings->build_settings(), result_as_source);
}
// static
@@ -298,36 +386,6 @@ void SubstitutionWriter::WriteNinjaVariablesForSource(
}
// static
-void SubstitutionWriter::WriteWithNinjaVariables(
- const SubstitutionPattern& pattern,
- const EscapeOptions& escape_options,
- std::ostream& out) {
- // The result needs to be quoted as if it was one string, but the $ for
- // the inserted Ninja variables can't be escaped. So write to a buffer with
- // no quoting, and then quote the whole thing if necessary.
- EscapeOptions no_quoting(escape_options);
- no_quoting.inhibit_quoting = true;
-
- bool needs_quotes = false;
- std::string result;
- for (size_t i = 0; i < pattern.ranges().size(); i++) {
- const SubstitutionPattern::Subrange range = pattern.ranges()[i];
- if (range.type == SUBSTITUTION_LITERAL) {
- result.append(EscapeString(range.literal, no_quoting, &needs_quotes));
- } else {
- result.append("${");
- result.append(kSubstitutionNinjaNames[range.type]);
- result.append("}");
- }
- }
-
- if (needs_quotes && !escape_options.inhibit_quoting)
- out << "\"" << result << "\"";
- else
- out << result;
-}
-
-// static
std::string SubstitutionWriter::GetSourceSubstitution(
const Settings* settings,
const SourceFile& source,
@@ -371,7 +429,9 @@ std::string SubstitutionWriter::GetSourceSubstitution(
break;
default:
- NOTREACHED();
+ NOTREACHED()
+ << "Unsupported substitution for this function: "
+ << kSubstitutionNames[type];
return std::string();
}
@@ -382,3 +442,182 @@ std::string SubstitutionWriter::GetSourceSubstitution(
return to_rebase;
return RebaseSourceAbsolutePath(to_rebase, relative_to);
}
+
+// static
+OutputFile SubstitutionWriter::ApplyPatternToTargetAsOutputFile(
+ const Target* target,
+ const Tool* tool,
+ const SubstitutionPattern& pattern) {
+ std::string result_value;
+ for (size_t i = 0; i < pattern.ranges().size(); i++) {
+ const SubstitutionPattern::Subrange& subrange = pattern.ranges()[i];
+ if (subrange.type == SUBSTITUTION_LITERAL) {
+ result_value.append(subrange.literal);
+ } else {
+ std::string subst;
+ CHECK(GetTargetSubstitution(target, subrange.type, &subst));
+ result_value.append(subst);
+ }
+ }
+ return OutputFile(result_value);
+}
+
+// static
+void SubstitutionWriter::ApplyListToTargetAsOutputFile(
+ const Target* target,
+ const Tool* tool,
+ const SubstitutionList& list,
+ std::vector<OutputFile>* output) {
+ for (size_t i = 0; i < list.list().size(); i++) {
+ output->push_back(ApplyPatternToTargetAsOutputFile(
+ target, tool, list.list()[i]));
+ }
+}
+
+// static
+bool SubstitutionWriter::GetTargetSubstitution(
+ const Target* target,
+ SubstitutionType type,
+ std::string* result) {
+ switch (type) {
+ case SUBSTITUTION_LABEL:
+ // Only include the toolchain for non-default toolchains.
+ *result = target->label().GetUserVisibleName(
+ !target->settings()->is_default());
+ break;
+ case SUBSTITUTION_ROOT_GEN_DIR:
+ *result = GetToolchainGenDirAsOutputFile(target->settings()).value();
+ TrimTrailingSlash(result);
+ break;
+ case SUBSTITUTION_ROOT_OUT_DIR:
+ *result = target->settings()->toolchain_output_subdir().value();
+ TrimTrailingSlash(result);
+ break;
+ case SUBSTITUTION_TARGET_GEN_DIR:
+ *result = GetTargetGenDirAsOutputFile(target).value();
+ TrimTrailingSlash(result);
+ break;
+ case SUBSTITUTION_TARGET_OUT_DIR:
+ *result = GetTargetOutputDirAsOutputFile(target).value();
+ TrimTrailingSlash(result);
+ break;
+ case SUBSTITUTION_TARGET_OUTPUT_NAME:
+ *result = target->GetComputedOutputName(true);
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+// static
+std::string SubstitutionWriter::GetTargetSubstitution(
+ const Target* target,
+ SubstitutionType type) {
+ std::string result;
+ GetTargetSubstitution(target, type, &result);
+ return result;
+}
+
+// static
+OutputFile SubstitutionWriter::ApplyPatternToCompilerAsOutputFile(
+ const Target* target,
+ const SourceFile& source,
+ const SubstitutionPattern& pattern) {
+ OutputFile result;
+ for (size_t i = 0; i < pattern.ranges().size(); i++) {
+ const SubstitutionPattern::Subrange& subrange = pattern.ranges()[i];
+ if (subrange.type == SUBSTITUTION_LITERAL) {
+ AppendLiteralWithPossibleSlashEliding(
+ pattern.ranges(), i, &result.value());
+ } else {
+ result.value().append(
+ GetCompilerSubstitution(target, source, subrange.type));
+ }
+ }
+ return result;
+}
+
+// static
+void SubstitutionWriter::ApplyListToCompilerAsOutputFile(
+ const Target* target,
+ const SourceFile& source,
+ const SubstitutionList& list,
+ std::vector<OutputFile>* output) {
+ for (size_t i = 0; i < list.list().size(); i++) {
+ output->push_back(ApplyPatternToCompilerAsOutputFile(
+ target, source, list.list()[i]));
+ }
+}
+
+// static
+std::string SubstitutionWriter::GetCompilerSubstitution(
+ const Target* target,
+ const SourceFile& source,
+ SubstitutionType type) {
+ // First try the common tool ones.
+ std::string result;
+ if (GetTargetSubstitution(target, type, &result))
+ return result;
+
+ // Fall-through to the source ones.
+ return GetSourceSubstitution(
+ target->settings(), source, type, OUTPUT_RELATIVE,
+ target->settings()->build_settings()->build_dir());
+}
+
+// static
+OutputFile SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
+ const Target* target,
+ const Tool* tool,
+ const SubstitutionPattern& pattern) {
+ OutputFile result;
+ for (size_t i = 0; i < pattern.ranges().size(); i++) {
+ const SubstitutionPattern::Subrange& subrange = pattern.ranges()[i];
+ if (subrange.type == SUBSTITUTION_LITERAL) {
+ AppendLiteralWithPossibleSlashEliding(
+ pattern.ranges(), i, &result.value());
+ } else {
+ result.value().append(GetLinkerSubstitution(target, tool, subrange.type));
+ }
+ }
+ return result;
+}
+
+// static
+void SubstitutionWriter::ApplyListToLinkerAsOutputFile(
+ const Target* target,
+ const Tool* tool,
+ const SubstitutionList& list,
+ std::vector<OutputFile>* output) {
+ for (size_t i = 0; i < list.list().size(); i++) {
+ output->push_back(ApplyPatternToLinkerAsOutputFile(
+ target, tool, list.list()[i]));
+ }
+}
+
+// static
+std::string SubstitutionWriter::GetLinkerSubstitution(
+ const Target* target,
+ const Tool* tool,
+ SubstitutionType type) {
+ // First try the common tool ones.
+ std::string result;
+ if (GetTargetSubstitution(target, type, &result))
+ return result;
+
+ // Fall-through to the linker-specific ones.
+ switch (type) {
+ case SUBSTITUTION_OUTPUT_EXTENSION:
+ // Use the extension provided on the target if nonempty, otherwise
+ // fall back on the default. Note that the target's output extension
+ // does not include the dot but the tool's does.
+ if (target->output_extension().empty())
+ return tool->default_output_extension();
+ return std::string(".") + target->output_extension();
+
+ default:
+ NOTREACHED();
+ return std::string();
+ }
+}
« 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