mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-11-24 22:36:09 +03:00
609 lines
20 KiB
C++
609 lines
20 KiB
C++
// Copyright 2014 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "tools/gn/substitution_writer.h"
|
|
|
|
#include "tools/gn/build_settings.h"
|
|
#include "tools/gn/escape.h"
|
|
#include "tools/gn/filesystem_utils.h"
|
|
#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 {
|
|
|
|
// Sets the given directory string to the destination, trimming any trailing
|
|
// slash from the directory (SourceDirs and OutputFiles representing
|
|
// directories will end in a trailing slash). If the directory is empty,
|
|
// it will be replaced with a ".".
|
|
void SetDirOrDotWithNoSlash(const std::string& dir, std::string* dest) {
|
|
if (!dir.empty() && dir[dir.size() - 1] == '/')
|
|
dest->assign(dir.data(), dir.size() - 1);
|
|
else
|
|
dest->assign(dir);
|
|
|
|
if (dest->empty())
|
|
dest->push_back('.');
|
|
}
|
|
|
|
} // namespace
|
|
|
|
const char kSourceExpansion_Help[] =
|
|
R"(How Source Expansion Works
|
|
|
|
Source expansion is used for the action_foreach and copy target types to map
|
|
source file names to output file names or arguments.
|
|
|
|
To perform source expansion in the outputs, GN maps every entry in the
|
|
sources to every entry in the outputs list, producing the cross product of
|
|
all combinations, expanding placeholders (see below).
|
|
|
|
Source expansion in the args works similarly, but performing the placeholder
|
|
substitution produces a different set of arguments for each invocation of the
|
|
script.
|
|
|
|
If no placeholders are found, the outputs or args list will be treated as a
|
|
static list of literal file names that do not depend on the sources.
|
|
|
|
See "gn help copy" and "gn help action_foreach" for more on how this is
|
|
applied.
|
|
|
|
Placeholders
|
|
|
|
This section discusses only placeholders for actions. There are other
|
|
placeholders used in the definition of tools. See "gn help tool" for those.
|
|
|
|
{{source}}
|
|
The name of the source file including directory (*). This will generally
|
|
be used for specifying inputs to a script in the "args" variable.
|
|
"//foo/bar/baz.txt" => "../../foo/bar/baz.txt"
|
|
|
|
{{source_file_part}}
|
|
The file part of the source including the extension.
|
|
"//foo/bar/baz.txt" => "baz.txt"
|
|
|
|
{{source_name_part}}
|
|
The filename part of the source file with no directory or extension. This
|
|
will generally be used for specifying a transformation from a source file
|
|
to a destination file with the same name but different extension.
|
|
"//foo/bar/baz.txt" => "baz"
|
|
|
|
{{source_dir}}
|
|
The directory (*) containing the source file with no trailing slash.
|
|
"//foo/bar/baz.txt" => "../../foo/bar"
|
|
|
|
{{source_root_relative_dir}}
|
|
The path to the source file's directory relative to the source root, with
|
|
no leading "//" or trailing slashes. If the path is system-absolute,
|
|
(beginning in a single slash) this will just return the path with no
|
|
trailing slash. This value will always be the same, regardless of whether
|
|
it appears in the "outputs" or "args" section.
|
|
"//foo/bar/baz.txt" => "foo/bar"
|
|
|
|
{{source_gen_dir}}
|
|
The generated file directory (*) corresponding to the source file's path.
|
|
This will be different than the target's generated file directory if the
|
|
source file is in a different directory than the BUILD.gn file.
|
|
"//foo/bar/baz.txt" => "gen/foo/bar"
|
|
|
|
{{source_out_dir}}
|
|
The object file directory (*) corresponding to the source file's path,
|
|
relative to the build directory. this us be different than the target's
|
|
out directory if the source file is in a different directory than the
|
|
build.gn file.
|
|
"//foo/bar/baz.txt" => "obj/foo/bar"
|
|
|
|
{{source_target_relative}}
|
|
The path to the source file relative to the target's directory. This will
|
|
generally be used for replicating the source directory layout in the
|
|
output directory. This can only be used in actions and it is an error to
|
|
use in process_file_template where there is no "target".
|
|
"//foo/bar/baz.txt" => "baz.txt"
|
|
|
|
(*) Note on directories
|
|
|
|
Paths containing directories (except the source_root_relative_dir) will be
|
|
different depending on what context the expansion is evaluated in. Generally
|
|
it should "just work" but it means you can't concatenate strings containing
|
|
these values with reasonable results.
|
|
|
|
Details: source expansions can be used in the "outputs" variable, the "args"
|
|
variable, and in calls to "process_file_template". The "args" are passed to a
|
|
script which is run from the build directory, so these directories will
|
|
relative to the build directory for the script to find. In the other cases,
|
|
the directories will be source- absolute (begin with a "//") because the
|
|
results of those expansions will be handled by GN internally.
|
|
|
|
Examples
|
|
|
|
Non-varying outputs:
|
|
action("hardcoded_outputs") {
|
|
sources = [ "input1.idl", "input2.idl" ]
|
|
outputs = [ "$target_out_dir/output1.dat",
|
|
"$target_out_dir/output2.dat" ]
|
|
}
|
|
The outputs in this case will be the two literal files given.
|
|
|
|
Varying outputs:
|
|
action_foreach("varying_outputs") {
|
|
sources = [ "input1.idl", "input2.idl" ]
|
|
outputs = [ "{{source_gen_dir}}/{{source_name_part}}.h",
|
|
"{{source_gen_dir}}/{{source_name_part}}.cc" ]
|
|
}
|
|
Performing source expansion will result in the following output names:
|
|
//out/Debug/obj/mydirectory/input1.h
|
|
//out/Debug/obj/mydirectory/input1.cc
|
|
//out/Debug/obj/mydirectory/input2.h
|
|
//out/Debug/obj/mydirectory/input2.cc
|
|
)";
|
|
|
|
// 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 (const auto& range : pattern.ranges()) {
|
|
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 SubstitutionList& list,
|
|
std::vector<SourceFile>* output) {
|
|
for (const auto& pattern : list.list()) {
|
|
CHECK(pattern.ranges().size() == 1 &&
|
|
pattern.ranges()[0].type == SUBSTITUTION_LITERAL)
|
|
<< "The substitution pattern \""
|
|
<< pattern.AsString()
|
|
<< "\" was expected to be a literal with no {{substitutions}}.";
|
|
const std::string& literal = pattern.ranges()[0].literal;
|
|
CHECK(literal.size() >= 1 && literal[0] == '/')
|
|
<< "The result of the pattern \""
|
|
<< pattern.AsString()
|
|
<< "\" was not an absolute path.";
|
|
output->push_back(SourceFile(literal));
|
|
}
|
|
}
|
|
|
|
// static
|
|
void SubstitutionWriter::GetListAsOutputFiles(
|
|
const Settings* settings,
|
|
const SubstitutionList& list,
|
|
std::vector<OutputFile>* output) {
|
|
std::vector<SourceFile> output_as_sources;
|
|
GetListAsSourceFiles(list, &output_as_sources);
|
|
for (const auto& file : output_as_sources)
|
|
output->push_back(OutputFile(settings->build_settings(), file));
|
|
}
|
|
|
|
// static
|
|
SourceFile SubstitutionWriter::ApplyPatternToSource(
|
|
const Target* target,
|
|
const Settings* settings,
|
|
const SubstitutionPattern& pattern,
|
|
const SourceFile& source) {
|
|
std::string result_value = ApplyPatternToSourceAsString(
|
|
target, settings, pattern, source);
|
|
CHECK(!result_value.empty() && result_value[0] == '/')
|
|
<< "The result of the pattern \""
|
|
<< pattern.AsString()
|
|
<< "\" was not a path beginning in \"/\" or \"//\".";
|
|
return SourceFile(SourceFile::SWAP_IN, &result_value);
|
|
}
|
|
|
|
// static
|
|
std::string SubstitutionWriter::ApplyPatternToSourceAsString(
|
|
const Target* target,
|
|
const Settings* settings,
|
|
const SubstitutionPattern& pattern,
|
|
const SourceFile& source) {
|
|
std::string result_value;
|
|
for (const auto& subrange : pattern.ranges()) {
|
|
if (subrange.type == SUBSTITUTION_LITERAL) {
|
|
result_value.append(subrange.literal);
|
|
} else {
|
|
result_value.append(
|
|
GetSourceSubstitution(target, settings, source, subrange.type,
|
|
OUTPUT_ABSOLUTE, SourceDir()));
|
|
}
|
|
}
|
|
return result_value;
|
|
}
|
|
|
|
// static
|
|
OutputFile SubstitutionWriter::ApplyPatternToSourceAsOutputFile(
|
|
const Target* target,
|
|
const Settings* settings,
|
|
const SubstitutionPattern& pattern,
|
|
const SourceFile& source) {
|
|
SourceFile result_as_source = ApplyPatternToSource(
|
|
target, settings, pattern, source);
|
|
return OutputFile(settings->build_settings(), result_as_source);
|
|
}
|
|
|
|
// static
|
|
void SubstitutionWriter::ApplyListToSource(
|
|
const Target* target,
|
|
const Settings* settings,
|
|
const SubstitutionList& list,
|
|
const SourceFile& source,
|
|
std::vector<SourceFile>* output) {
|
|
for (const auto& item : list.list())
|
|
output->push_back(ApplyPatternToSource(target, settings, item, source));
|
|
}
|
|
|
|
// static
|
|
void SubstitutionWriter::ApplyListToSourceAsString(
|
|
const Target* target,
|
|
const Settings* settings,
|
|
const SubstitutionList& list,
|
|
const SourceFile& source,
|
|
std::vector<std::string>* output) {
|
|
for (const auto& item : list.list())
|
|
output->push_back(ApplyPatternToSourceAsString(
|
|
target, settings, item, source));
|
|
}
|
|
|
|
// static
|
|
void SubstitutionWriter::ApplyListToSourceAsOutputFile(
|
|
const Target* target,
|
|
const Settings* settings,
|
|
const SubstitutionList& list,
|
|
const SourceFile& source,
|
|
std::vector<OutputFile>* output) {
|
|
for (const auto& item : list.list())
|
|
output->push_back(ApplyPatternToSourceAsOutputFile(
|
|
target, settings, item, source));
|
|
}
|
|
|
|
// static
|
|
void SubstitutionWriter::ApplyListToSources(
|
|
const Target* target,
|
|
const Settings* settings,
|
|
const SubstitutionList& list,
|
|
const std::vector<SourceFile>& sources,
|
|
std::vector<SourceFile>* output) {
|
|
output->clear();
|
|
for (const auto& source : sources)
|
|
ApplyListToSource(target, settings, list, source, output);
|
|
}
|
|
|
|
// static
|
|
void SubstitutionWriter::ApplyListToSourcesAsString(
|
|
const Target* target,
|
|
const Settings* settings,
|
|
const SubstitutionList& list,
|
|
const std::vector<SourceFile>& sources,
|
|
std::vector<std::string>* output) {
|
|
output->clear();
|
|
for (const auto& source : sources)
|
|
ApplyListToSourceAsString(target, settings, list, source, output);
|
|
}
|
|
|
|
// static
|
|
void SubstitutionWriter::ApplyListToSourcesAsOutputFile(
|
|
const Target* target,
|
|
const Settings* settings,
|
|
const SubstitutionList& list,
|
|
const std::vector<SourceFile>& sources,
|
|
std::vector<OutputFile>* output) {
|
|
output->clear();
|
|
for (const auto& source : sources)
|
|
ApplyListToSourceAsOutputFile(target, settings, list, source, output);
|
|
}
|
|
|
|
// static
|
|
void SubstitutionWriter::WriteNinjaVariablesForSource(
|
|
const Target* target,
|
|
const Settings* settings,
|
|
const SourceFile& source,
|
|
const std::vector<SubstitutionType>& types,
|
|
const EscapeOptions& escape_options,
|
|
std::ostream& out) {
|
|
for (const auto& type : types) {
|
|
// Don't write SOURCE since that just maps to Ninja's $in variable, which
|
|
// is implicit in the rule. RESPONSE_FILE_NAME is written separately
|
|
// only when writing target rules since it can never be used in any
|
|
// other context (like process_file_template).
|
|
if (type != SUBSTITUTION_SOURCE && type != SUBSTITUTION_RSP_FILE_NAME) {
|
|
out << " " << kSubstitutionNinjaNames[type] << " = ";
|
|
EscapeStringToStream(
|
|
out,
|
|
GetSourceSubstitution(target, settings, source, type,
|
|
OUTPUT_RELATIVE,
|
|
settings->build_settings()->build_dir()),
|
|
escape_options);
|
|
out << std::endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
// static
|
|
std::string SubstitutionWriter::GetSourceSubstitution(
|
|
const Target* target,
|
|
const Settings* settings,
|
|
const SourceFile& source,
|
|
SubstitutionType type,
|
|
OutputStyle output_style,
|
|
const SourceDir& relative_to) {
|
|
std::string to_rebase;
|
|
switch (type) {
|
|
case SUBSTITUTION_SOURCE:
|
|
if (source.is_system_absolute())
|
|
return source.value();
|
|
to_rebase = source.value();
|
|
break;
|
|
|
|
case SUBSTITUTION_SOURCE_NAME_PART:
|
|
return FindFilenameNoExtension(&source.value()).as_string();
|
|
|
|
case SUBSTITUTION_SOURCE_FILE_PART:
|
|
return source.GetName();
|
|
|
|
case SUBSTITUTION_SOURCE_DIR:
|
|
if (source.is_system_absolute())
|
|
return DirectoryWithNoLastSlash(source.GetDir());
|
|
to_rebase = DirectoryWithNoLastSlash(source.GetDir());
|
|
break;
|
|
|
|
case SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR:
|
|
if (source.is_system_absolute())
|
|
return DirectoryWithNoLastSlash(source.GetDir());
|
|
return RebasePath(
|
|
DirectoryWithNoLastSlash(source.GetDir()), SourceDir("//"),
|
|
settings->build_settings()->root_path_utf8());
|
|
|
|
case SUBSTITUTION_SOURCE_GEN_DIR:
|
|
to_rebase = DirectoryWithNoLastSlash(GetSubBuildDirAsSourceDir(
|
|
BuildDirContext(settings), source.GetDir(), BuildDirType::GEN));
|
|
break;
|
|
|
|
case SUBSTITUTION_SOURCE_OUT_DIR:
|
|
to_rebase = DirectoryWithNoLastSlash(GetSubBuildDirAsSourceDir(
|
|
BuildDirContext(settings), source.GetDir(), BuildDirType::OBJ));
|
|
break;
|
|
|
|
case SUBSTITUTION_SOURCE_TARGET_RELATIVE:
|
|
if (target) {
|
|
return RebasePath(source.value(), target->label().dir(),
|
|
settings->build_settings()->root_path_utf8());
|
|
}
|
|
NOTREACHED()
|
|
<< "Cannot use substitution " << kSubstitutionNames[type]
|
|
<< " without target";
|
|
return std::string();
|
|
|
|
default:
|
|
NOTREACHED()
|
|
<< "Unsupported substitution for this function: "
|
|
<< kSubstitutionNames[type];
|
|
return std::string();
|
|
}
|
|
|
|
// If we get here, the result is a path that should be made relative or
|
|
// absolute according to the output_style. Other cases (just file name or
|
|
// extension extraction) will have been handled via early return above.
|
|
if (output_style == OUTPUT_ABSOLUTE)
|
|
return to_rebase;
|
|
return RebasePath(to_rebase, relative_to,
|
|
settings->build_settings()->root_path_utf8());
|
|
}
|
|
|
|
// static
|
|
OutputFile SubstitutionWriter::ApplyPatternToTargetAsOutputFile(
|
|
const Target* target,
|
|
const Tool* tool,
|
|
const SubstitutionPattern& pattern) {
|
|
std::string result_value;
|
|
for (const auto& subrange : pattern.ranges()) {
|
|
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 (const auto& item : list.list())
|
|
output->push_back(ApplyPatternToTargetAsOutputFile(target, tool, item));
|
|
}
|
|
|
|
// 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_LABEL_NAME:
|
|
*result = target->label().name();
|
|
break;
|
|
case SUBSTITUTION_ROOT_GEN_DIR:
|
|
SetDirOrDotWithNoSlash(
|
|
GetBuildDirAsOutputFile(BuildDirContext(target),
|
|
BuildDirType::GEN).value(),
|
|
result);
|
|
break;
|
|
case SUBSTITUTION_ROOT_OUT_DIR:
|
|
SetDirOrDotWithNoSlash(
|
|
target->settings()->toolchain_output_subdir().value(),
|
|
result);
|
|
break;
|
|
case SUBSTITUTION_TARGET_GEN_DIR:
|
|
SetDirOrDotWithNoSlash(
|
|
GetBuildDirForTargetAsOutputFile(target, BuildDirType::GEN).value(),
|
|
result);
|
|
break;
|
|
case SUBSTITUTION_TARGET_OUT_DIR:
|
|
SetDirOrDotWithNoSlash(
|
|
GetBuildDirForTargetAsOutputFile(target, BuildDirType::OBJ).value(),
|
|
result);
|
|
break;
|
|
case SUBSTITUTION_TARGET_OUTPUT_NAME:
|
|
*result = target->GetComputedOutputName();
|
|
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 (const auto& subrange : pattern.ranges()) {
|
|
if (subrange.type == SUBSTITUTION_LITERAL) {
|
|
result.value().append(subrange.literal);
|
|
} 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 (const auto& item : list.list())
|
|
output->push_back(ApplyPatternToCompilerAsOutputFile(target, source, item));
|
|
}
|
|
|
|
// 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, 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 (const auto& subrange : pattern.ranges()) {
|
|
if (subrange.type == SUBSTITUTION_LITERAL) {
|
|
result.value().append(subrange.literal);
|
|
} 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 (const auto& item : list.list())
|
|
output->push_back(ApplyPatternToLinkerAsOutputFile(target, tool, item));
|
|
}
|
|
|
|
// 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_DIR:
|
|
// Use the target's value if there is one (it will have no expansion
|
|
// patterns since it can directly use GN variables to compute whatever
|
|
// path it wants), or the tool's default (which will contain further
|
|
// expansions).
|
|
if (target->output_dir().is_null()) {
|
|
return ApplyPatternToLinkerAsOutputFile(
|
|
target, tool, tool->default_output_dir()).value();
|
|
}
|
|
SetDirOrDotWithNoSlash(RebasePath(
|
|
target->output_dir().value(),
|
|
target->settings()->build_settings()->build_dir()),
|
|
&result);
|
|
return result;
|
|
|
|
case SUBSTITUTION_OUTPUT_EXTENSION:
|
|
// Use the extension provided on the target if specified, 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_set())
|
|
return tool->default_output_extension();
|
|
if (target->output_extension().empty())
|
|
return std::string(); // Explicitly set to no extension.
|
|
return std::string(".") + target->output_extension();
|
|
|
|
default:
|
|
NOTREACHED();
|
|
return std::string();
|
|
}
|
|
}
|