| 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();
|
| + }
|
| +}
|
|
|