summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/gn/command_desc.cc2
-rw-r--r--tools/gn/file_template.cc189
-rw-r--r--tools/gn/file_template.h54
-rw-r--r--tools/gn/file_template_unittest.cc79
-rw-r--r--tools/gn/filesystem_utils.cc21
-rw-r--r--tools/gn/function_get_path_info.cc48
-rw-r--r--tools/gn/function_get_path_info_unittest.cc22
-rw-r--r--tools/gn/function_get_target_outputs.cc13
-rw-r--r--tools/gn/function_process_file_template.cc22
-rw-r--r--tools/gn/ninja_action_target_writer.cc14
-rw-r--r--tools/gn/ninja_action_target_writer_unittest.cc2
-rw-r--r--tools/gn/ninja_copy_target_writer.cc2
-rw-r--r--tools/gn/ninja_target_writer.cc2
13 files changed, 333 insertions, 137 deletions
diff --git a/tools/gn/command_desc.cc b/tools/gn/command_desc.cc
index d178233..4491920 100644
--- a/tools/gn/command_desc.cc
+++ b/tools/gn/command_desc.cc
@@ -276,7 +276,7 @@ void PrintOutputs(const Target* target, bool display_header) {
std::vector<std::string> output_strings;
FileTemplate file_template = FileTemplate::GetForTargetOutputs(target);
for (size_t i = 0; i < target->sources().size(); i++)
- file_template.ApplyString(target->sources()[i].value(), &output_strings);
+ file_template.Apply(target->sources()[i], &output_strings);
std::sort(output_strings.begin(), output_strings.end());
for (size_t i = 0; i < output_strings.size(); i++) {
diff --git a/tools/gn/file_template.cc b/tools/gn/file_template.cc
index b00eb39..2809920 100644
--- a/tools/gn/file_template.cc
+++ b/tools/gn/file_template.cc
@@ -15,6 +15,10 @@
const char FileTemplate::kSource[] = "{{source}}";
const char FileTemplate::kSourceNamePart[] = "{{source_name_part}}";
const char FileTemplate::kSourceFilePart[] = "{{source_file_part}}";
+const char FileTemplate::kSourceDir[] = "{{source_dir}}";
+const char FileTemplate::kRootRelDir[] = "{{source_root_relative_dir}}";
+const char FileTemplate::kSourceGenDir[] = "{{source_gen_dir}}";
+const char FileTemplate::kSourceOutDir[] = "{{source_out_dir}}";
const char kSourceExpansion_Help[] =
"How Source Expansion Works\n"
@@ -44,17 +48,47 @@ const char kSourceExpansion_Help[] =
" directory (which is the current directory when running compilers\n"
" and scripts). This will generally be used for specifying inputs\n"
" to a script in the \"args\" variable.\n"
+ " \"//foo/bar/baz.txt\" => \"../../foo/bar/baz.txt\"\n"
"\n"
" {{source_file_part}}\n"
- " The file part of the source including the extension. For the\n"
- " source \"foo/bar.txt\" the source file part will be \"bar.txt\".\n"
+ " The file part of the source including the extension.\n"
+ " \"//foo/bar/baz.txt\" => \"baz.txt\"\n"
"\n"
" {{source_name_part}}\n"
" The filename part of the source file with no directory or\n"
" extension. This will generally be used for specifying a\n"
" transformation from a soruce file to a destination file with the\n"
- " same name but different extension. For the source \"foo/bar.txt\"\n"
- " the source name part will be \"bar\".\n"
+ " same name but different extension.\n"
+ " \"//foo/bar/baz.txt\" => \"baz\"\n"
+ "\n"
+ " {{source_dir}}\n"
+ " The directory containing the source file, relative to the build\n"
+ " directory, with no trailing slash.\n"
+ " \"//foo/bar/baz.txt\" => \"../../foo/bar\"\n"
+ "\n"
+ " {{source_root_relative_dir}}\n"
+ " The path to the source file's directory relative to the source\n"
+ " root, with no leading \"//\" or trailing slashes. If the path is\n"
+ " system-absolute, (beginning in a single slash) this will just\n"
+ " return the path with no trailing slash.\n"
+ " \"//foo/bar/baz.txt\" => \"foo/bar\"\n"
+ "\n"
+ " {{source_gen_dir}}\n"
+ " The generated file directory corresponding to the source file's\n"
+ " path, relative to the build directory. This will be different than\n"
+ " the target's generated file directory if the source file is in a\n"
+ " different directory than the build.gn file. If the input path is\n"
+ " system absolute, this will return the root generated file\n"
+ " directory."
+ " \"//foo/bar/baz.txt\" => \"gen/foo/bar\"\n"
+ "\n"
+ " {{source_out_dir}}\n"
+ " The object file directory corresponding to the source file's\n"
+ " path, relative to the build directory. this us be different than\n"
+ " the target's out directory if the source file is in a different\n"
+ " directory than the build.gn file. if the input path is system\n"
+ " absolute, this will return the root generated file directory.\n"
+ " \"//foo/bar/baz.txt\" => \"obj/foo/bar\"\n"
"\n"
"Examples\n"
"\n"
@@ -78,21 +112,26 @@ const char kSourceExpansion_Help[] =
" //out/Debug/obj/mydirectory/input2.h\n"
" //out/Debug/obj/mydirectory/input2.cc\n";
-FileTemplate::FileTemplate(const Value& t, Err* err)
- : has_substitutions_(false) {
+FileTemplate::FileTemplate(const Settings* settings, const Value& t, Err* err)
+ : settings_(settings),
+ has_substitutions_(false) {
std::fill(types_required_, &types_required_[Subrange::NUM_TYPES], false);
ParseInput(t, err);
}
-FileTemplate::FileTemplate(const std::vector<std::string>& t)
- : has_substitutions_(false) {
+FileTemplate::FileTemplate(const Settings* settings,
+ const std::vector<std::string>& t)
+ : settings_(settings),
+ has_substitutions_(false) {
std::fill(types_required_, &types_required_[Subrange::NUM_TYPES], false);
for (size_t i = 0; i < t.size(); i++)
ParseOneTemplateString(t[i]);
}
-FileTemplate::FileTemplate(const std::vector<SourceFile>& t)
- : has_substitutions_(false) {
+FileTemplate::FileTemplate(const Settings* settings,
+ const std::vector<SourceFile>& t)
+ : settings_(settings),
+ has_substitutions_(false) {
std::fill(types_required_, &types_required_[Subrange::NUM_TYPES], false);
for (size_t i = 0; i < t.size(); i++)
ParseOneTemplateString(t[i].value());
@@ -107,7 +146,7 @@ FileTemplate FileTemplate::GetForTargetOutputs(const Target* target) {
std::vector<std::string> output_template_args;
for (size_t i = 0; i < outputs.size(); i++)
output_template_args.push_back(outputs[i].value());
- return FileTemplate(output_template_args);
+ return FileTemplate(target->settings(), output_template_args);
}
bool FileTemplate::IsTypeUsed(Subrange::Type type) const {
@@ -115,37 +154,16 @@ bool FileTemplate::IsTypeUsed(Subrange::Type type) const {
return types_required_[type];
}
-void FileTemplate::Apply(const Value& sources,
- const ParseNode* origin,
- std::vector<Value>* dest,
- Err* err) const {
- if (!sources.VerifyTypeIs(Value::LIST, err))
- return;
- dest->reserve(sources.list_value().size() * templates_.container().size());
-
- // Temporary holding place, allocate outside to re-use- buffer.
- std::vector<std::string> string_output;
-
- const std::vector<Value>& sources_list = sources.list_value();
- for (size_t i = 0; i < sources_list.size(); i++) {
- string_output.clear();
- if (!sources_list[i].VerifyTypeIs(Value::STRING, err))
- return;
-
- ApplyString(sources_list[i].string_value(), &string_output);
- for (size_t out_i = 0; out_i < string_output.size(); out_i++)
- dest->push_back(Value(origin, string_output[out_i]));
- }
-}
-
-void FileTemplate::ApplyString(const std::string& str,
- std::vector<std::string>* output) const {
+void FileTemplate::Apply(const SourceFile& source,
+ std::vector<std::string>* output) const {
// Compute all substitutions needed so we can just do substitutions below.
// We skip the LITERAL one since that varies each time.
std::string subst[Subrange::NUM_TYPES];
for (int i = 1; i < Subrange::NUM_TYPES; i++) {
- if (types_required_[i])
- subst[i] = GetSubstitution(str, static_cast<Subrange::Type>(i));
+ if (types_required_[i]) {
+ subst[i] =
+ GetSubstitution(settings_, source, static_cast<Subrange::Type>(i));
+ }
}
size_t first_output_index = output->size();
@@ -209,13 +227,15 @@ void FileTemplate::WriteWithNinjaExpansions(std::ostream& out) const {
void FileTemplate::WriteNinjaVariablesForSubstitution(
std::ostream& out,
- const std::string& source,
+ const Settings* settings,
+ const SourceFile& source,
const EscapeOptions& escape_options) const {
for (int i = 1; i < Subrange::NUM_TYPES; i++) {
if (types_required_[i]) {
Subrange::Type type = static_cast<Subrange::Type>(i);
out << " " << GetNinjaVariableNameForType(type) << " = ";
- EscapeStringToStream(out, GetSubstitution(source, type), escape_options);
+ EscapeStringToStream(out, GetSubstitution(settings, source, type),
+ escape_options);
out << std::endl;
}
}
@@ -230,6 +250,15 @@ const char* FileTemplate::GetNinjaVariableNameForType(Subrange::Type type) {
return "source_name_part";
case Subrange::FILE_PART:
return "source_file_part";
+ case Subrange::SOURCE_DIR:
+ return "source_dir";
+ case Subrange::ROOT_RELATIVE_DIR:
+ return "source_root_rel_dir";
+ case Subrange::SOURCE_GEN_DIR:
+ return "source_gen_dir";
+ case Subrange::SOURCE_OUT_DIR:
+ return "source_out_dir";
+
default:
NOTREACHED();
}
@@ -237,15 +266,47 @@ const char* FileTemplate::GetNinjaVariableNameForType(Subrange::Type type) {
}
// static
-std::string FileTemplate::GetSubstitution(const std::string& source,
+std::string FileTemplate::GetSubstitution(const Settings* settings,
+ const SourceFile& source,
Subrange::Type type) {
switch (type) {
case Subrange::SOURCE:
- return source;
+ if (source.is_system_absolute())
+ return source.value();
+ return RebaseSourceAbsolutePath(source.value(),
+ settings->build_settings()->build_dir());
+
case Subrange::NAME_PART:
- return FindFilenameNoExtension(&source).as_string();
+ return FindFilenameNoExtension(&source.value()).as_string();
+
case Subrange::FILE_PART:
- return FindFilename(&source).as_string();
+ return source.GetName();
+
+ case Subrange::SOURCE_DIR:
+ if (source.is_system_absolute())
+ return DirectoryWithNoLastSlash(source.GetDir());
+ return RebaseSourceAbsolutePath(
+ DirectoryWithNoLastSlash(source.GetDir()),
+ settings->build_settings()->build_dir());
+
+ case Subrange::ROOT_RELATIVE_DIR:
+ if (source.is_system_absolute())
+ return DirectoryWithNoLastSlash(source.GetDir());
+ return RebaseSourceAbsolutePath(
+ DirectoryWithNoLastSlash(source.GetDir()), SourceDir("//"));
+
+ case Subrange::SOURCE_GEN_DIR:
+ return RebaseSourceAbsolutePath(
+ DirectoryWithNoLastSlash(
+ GetGenDirForSourceDir(settings, source.GetDir())),
+ settings->build_settings()->build_dir());
+
+ case Subrange::SOURCE_OUT_DIR:
+ return RebaseSourceAbsolutePath(
+ DirectoryWithNoLastSlash(
+ GetOutputDirForSourceDir(settings, source.GetDir())),
+ settings->build_settings()->build_dir());
+
default:
NOTREACHED();
}
@@ -288,30 +349,34 @@ void FileTemplate::ParseOneTemplateString(const std::string& str) {
Subrange(Subrange::LITERAL, str.substr(cur, next - cur)));
}
+ // Given the name of the string constant and enum for a template parameter,
+ // checks for it and stores it. Writing this as a function requires passing
+ // the entire state of this function as arguments, so this actually ends
+ // up being more clear.
+ #define IF_MATCH_THEN_STORE(const_name, enum_name) \
+ if (str.compare(next, arraysize(const_name) - 1, const_name) == 0) { \
+ t.container().push_back(Subrange(Subrange::enum_name)); \
+ types_required_[Subrange::enum_name] = true; \
+ has_substitutions_ = true; \
+ cur = next + arraysize(const_name) - 1; \
+ }
+
// Decode the template param.
- if (str.compare(next, arraysize(kSource) - 1, kSource) == 0) {
- t.container().push_back(Subrange(Subrange::SOURCE));
- types_required_[Subrange::SOURCE] = true;
- has_substitutions_ = true;
- cur = next + arraysize(kSource) - 1;
- } else if (str.compare(next, arraysize(kSourceNamePart) - 1,
- kSourceNamePart) == 0) {
- t.container().push_back(Subrange(Subrange::NAME_PART));
- types_required_[Subrange::NAME_PART] = true;
- has_substitutions_ = true;
- cur = next + arraysize(kSourceNamePart) - 1;
- } else if (str.compare(next, arraysize(kSourceFilePart) - 1,
- kSourceFilePart) == 0) {
- t.container().push_back(Subrange(Subrange::FILE_PART));
- types_required_[Subrange::FILE_PART] = true;
- has_substitutions_ = true;
- cur = next + arraysize(kSourceFilePart) - 1;
- } else {
+ IF_MATCH_THEN_STORE(kSource, SOURCE)
+ else IF_MATCH_THEN_STORE(kSourceNamePart, NAME_PART)
+ else IF_MATCH_THEN_STORE(kSourceFilePart, FILE_PART)
+ else IF_MATCH_THEN_STORE(kSourceDir, SOURCE_DIR)
+ else IF_MATCH_THEN_STORE(kRootRelDir, ROOT_RELATIVE_DIR)
+ else IF_MATCH_THEN_STORE(kSourceGenDir, SOURCE_GEN_DIR)
+ else IF_MATCH_THEN_STORE(kSourceOutDir, SOURCE_OUT_DIR)
+ else {
// If it's not a match, treat it like a one-char literal (this will be
// rare, so it's not worth the bother to add to the previous literal) so
// we can keep going.
t.container().push_back(Subrange(Subrange::LITERAL, "{"));
cur = next + 1;
}
+
+ #undef IF_MATCH_THEN_STORE
}
}
diff --git a/tools/gn/file_template.h b/tools/gn/file_template.h
index 30b40da..b04a6c7 100644
--- a/tools/gn/file_template.h
+++ b/tools/gn/file_template.h
@@ -14,6 +14,7 @@
struct EscapeOptions;
class ParseNode;
+class Settings;
class SourceFile;
class Target;
@@ -30,20 +31,17 @@ extern const char kSourceExpansion_Help[];
class FileTemplate {
public:
struct Subrange {
+ // See the help in the .cc file for what these mean.
enum Type {
LITERAL = 0,
- // {{source}} -> expands to be the source file name relative to the build
- // root dir.
- SOURCE,
-
- // {{source_name_part}} -> file name without extension or directory.
- // Maps "foo/bar.txt" to "bar".
- NAME_PART,
-
- // {{source_file_part}} -> file name including extension but no directory.
- // Maps "foo/bar.txt" to "bar.txt".
- FILE_PART,
+ SOURCE, // {{source}}
+ NAME_PART, // {{source_name_part}}
+ FILE_PART, // {{source_file_part}}
+ SOURCE_DIR, // {{source_dir}}
+ ROOT_RELATIVE_DIR, // {{root_relative_dir}}
+ SOURCE_GEN_DIR, // {{source_gen_dir}}
+ SOURCE_OUT_DIR, // {{source_out_dir}}
NUM_TYPES // Must be last
};
@@ -60,9 +58,9 @@ class FileTemplate {
// Constructs a template from the given value. On error, the err will be
// set. In this case you should not use this object.
- FileTemplate(const Value& t, Err* err);
- FileTemplate(const std::vector<std::string>& t);
- FileTemplate(const std::vector<SourceFile>& t);
+ FileTemplate(const Settings* settings, const Value& t, Err* err);
+ FileTemplate(const Settings* settings, const std::vector<std::string>& t);
+ FileTemplate(const Settings* settings, const std::vector<SourceFile>& t);
~FileTemplate();
@@ -76,18 +74,10 @@ class FileTemplate {
// Returns true if there are any substitutions.
bool has_substitutions() const { return has_substitutions_; }
- // Applies this template to the given list of sources, appending all
- // results to the given dest list. The sources must be a list for the
- // one that takes a value as an input, otherwise the given error will be set.
- void Apply(const Value& sources,
- const ParseNode* origin,
- std::vector<Value>* dest,
- Err* err) const;
-
- // Low-level version of Apply that handles one source file. The results
- // will be *appended* to the output.
- void ApplyString(const std::string& input,
- std::vector<std::string>* output) const;
+ // Applies the template to one source file. The results will be *appended* to
+ // the output.
+ void Apply(const SourceFile& source,
+ std::vector<std::string>* output) const;
// Writes a string representing the template with Ninja variables for the
// substitutions, and the literals escaped for Ninja consumption.
@@ -115,7 +105,8 @@ class FileTemplate {
// (see GetWithNinjaExpansions).
void WriteNinjaVariablesForSubstitution(
std::ostream& out,
- const std::string& source,
+ const Settings* settings,
+ const SourceFile& source,
const EscapeOptions& escape_options) const;
// Returns the Ninja variable name used by the above Ninja functions to
@@ -124,13 +115,18 @@ class FileTemplate {
// Extracts the given type of substitution from the given source. The source
// should be the file name relative to the output directory.
- static std::string GetSubstitution(const std::string& source,
+ static std::string GetSubstitution(const Settings* settings,
+ const SourceFile& source,
Subrange::Type type);
// Known template types, these include the "{{ }}"
static const char kSource[];
static const char kSourceNamePart[];
static const char kSourceFilePart[];
+ static const char kSourceDir[];
+ static const char kRootRelDir[];
+ static const char kSourceGenDir[];
+ static const char kSourceOutDir[];
private:
typedef base::StackVector<Subrange, 8> Template;
@@ -141,6 +137,8 @@ class FileTemplate {
// Parses a template string and adds it to the templates_ list.
void ParseOneTemplateString(const std::string& str);
+ const Settings* settings_;
+
TemplateVector templates_;
// The corresponding value is set to true if the given subrange type is
diff --git a/tools/gn/file_template_unittest.cc b/tools/gn/file_template_unittest.cc
index 8e3bf40..d10791e 100644
--- a/tools/gn/file_template_unittest.cc
+++ b/tools/gn/file_template_unittest.cc
@@ -7,58 +7,62 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "tools/gn/escape.h"
#include "tools/gn/file_template.h"
+#include "tools/gn/test_with_scope.h"
TEST(FileTemplate, Static) {
+ TestWithScope setup;
+
std::vector<std::string> templates;
templates.push_back("something_static");
- FileTemplate t(templates);
+ FileTemplate t(setup.settings(), templates);
EXPECT_FALSE(t.has_substitutions());
std::vector<std::string> result;
- t.ApplyString("", &result);
- ASSERT_EQ(1u, result.size());
- EXPECT_EQ("something_static", result[0]);
-
- result.clear();
- t.ApplyString("lalala", &result);
+ t.Apply(SourceFile("//foo/bar"), &result);
ASSERT_EQ(1u, result.size());
EXPECT_EQ("something_static", result[0]);
}
TEST(FileTemplate, Typical) {
+ TestWithScope setup;
+
std::vector<std::string> templates;
templates.push_back("foo/{{source_name_part}}.cc");
templates.push_back("foo/{{source_name_part}}.h");
- FileTemplate t(templates);
+ FileTemplate t(setup.settings(), templates);
EXPECT_TRUE(t.has_substitutions());
std::vector<std::string> result;
- t.ApplyString("sources/ha.idl", &result);
+ t.Apply(SourceFile("//sources/ha.idl"), &result);
ASSERT_EQ(2u, result.size());
EXPECT_EQ("foo/ha.cc", result[0]);
EXPECT_EQ("foo/ha.h", result[1]);
}
TEST(FileTemplate, Weird) {
+ TestWithScope setup;
+
std::vector<std::string> templates;
templates.push_back("{{{source}}{{source}}{{");
- FileTemplate t(templates);
+ FileTemplate t(setup.settings(), templates);
EXPECT_TRUE(t.has_substitutions());
std::vector<std::string> result;
- t.ApplyString("foo/lalala.c", &result);
+ t.Apply(SourceFile("//foo/lalala.c"), &result);
ASSERT_EQ(1u, result.size());
- EXPECT_EQ("{foo/lalala.cfoo/lalala.c{{", result[0]);
+ EXPECT_EQ("{../../foo/lalala.c../../foo/lalala.c{{", result[0]);
}
TEST(FileTemplate, NinjaExpansions) {
+ TestWithScope setup;
+
std::vector<std::string> templates;
templates.push_back("-i");
templates.push_back("{{source}}");
templates.push_back("--out=foo bar\"{{source_name_part}}\".o");
templates.push_back(""); // Test empty string.
- FileTemplate t(templates);
+ FileTemplate t(setup.settings(), templates);
std::ostringstream out;
t.WriteWithNinjaExpansions(out);
@@ -79,21 +83,64 @@ TEST(FileTemplate, NinjaExpansions) {
}
TEST(FileTemplate, NinjaVariables) {
+ TestWithScope setup;
+
std::vector<std::string> templates;
templates.push_back("-i");
templates.push_back("{{source}}");
templates.push_back("--out=foo bar\"{{source_name_part}}\".o");
+ templates.push_back("{{source_file_part}}");
+ templates.push_back("{{source_dir}}");
+ templates.push_back("{{source_root_relative_dir}}");
+ templates.push_back("{{source_gen_dir}}");
+ templates.push_back("{{source_out_dir}}");
- FileTemplate t(templates);
+ FileTemplate t(setup.settings(), templates);
std::ostringstream out;
EscapeOptions options;
options.mode = ESCAPE_NINJA_COMMAND;
- t.WriteNinjaVariablesForSubstitution(out, "../../foo/bar.txt", options);
+ t.WriteNinjaVariablesForSubstitution(out, setup.settings(),
+ SourceFile("//foo/bar.txt"), options);
// Just the variables used above should be written.
EXPECT_EQ(
" source = ../../foo/bar.txt\n"
- " source_name_part = bar\n",
+ " source_name_part = bar\n"
+ " source_file_part = bar.txt\n"
+ " source_dir = ../../foo\n"
+ " source_root_rel_dir = foo\n"
+ " source_gen_dir = gen/foo\n"
+ " source_out_dir = obj/foo\n",
out.str());
}
+
+// Tests in isolation different types of substitutions and that the right
+// things are generated.
+TEST(FileTemplate, Substitutions) {
+ TestWithScope setup;
+
+ #define GetSubst(str, what) \
+ FileTemplate::GetSubstitution(setup.settings(), \
+ SourceFile(str), \
+ FileTemplate::Subrange::what)
+
+ // Try all possible templates with a normal looking string.
+ EXPECT_EQ("../../foo/bar/baz.txt", GetSubst("//foo/bar/baz.txt", SOURCE));
+ EXPECT_EQ("baz", GetSubst("//foo/bar/baz.txt", NAME_PART));
+ EXPECT_EQ("baz.txt", GetSubst("//foo/bar/baz.txt", FILE_PART));
+ EXPECT_EQ("../../foo/bar", GetSubst("//foo/bar/baz.txt", SOURCE_DIR));
+ EXPECT_EQ("foo/bar", GetSubst("//foo/bar/baz.txt", ROOT_RELATIVE_DIR));
+ EXPECT_EQ("gen/foo/bar", GetSubst("//foo/bar/baz.txt", SOURCE_GEN_DIR));
+ EXPECT_EQ("obj/foo/bar", GetSubst("//foo/bar/baz.txt", SOURCE_OUT_DIR));
+
+ // Operations on an absolute path.
+ EXPECT_EQ("/baz.txt", GetSubst("/baz.txt", SOURCE));
+ EXPECT_EQ("/.", GetSubst("/baz.txt", SOURCE_DIR));
+ EXPECT_EQ("gen", GetSubst("/baz.txt", SOURCE_GEN_DIR));
+ EXPECT_EQ("obj", GetSubst("/baz.txt", SOURCE_OUT_DIR));
+
+ EXPECT_EQ(".", GetSubst("//baz.txt", ROOT_RELATIVE_DIR));
+
+ #undef GetSubst
+}
diff --git a/tools/gn/filesystem_utils.cc b/tools/gn/filesystem_utils.cc
index 4cfefa9..ca45625 100644
--- a/tools/gn/filesystem_utils.cc
+++ b/tools/gn/filesystem_utils.cc
@@ -720,10 +720,12 @@ SourceDir GetOutputDirForSourceDir(const Settings* settings,
toolchain.SwapValue(&ret);
ret.append("obj/");
- // The source dir should be source-absolute, so we trim off the two leading
- // slashes to append to the toolchain object directory.
- DCHECK(source_dir.is_source_absolute());
- ret.append(&source_dir.value()[2], source_dir.value().size() - 2);
+ if (source_dir.is_source_absolute()) {
+ // The source dir is source-absolute, so we trim off the two leading
+ // slashes to append to the toolchain object directory.
+ ret.append(&source_dir.value()[2], source_dir.value().size() - 2);
+ }
+ // (Put system-absolute stuff in the root obj directory.)
return SourceDir(SourceDir::SWAP_IN, &ret);
}
@@ -735,10 +737,13 @@ SourceDir GetGenDirForSourceDir(const Settings* settings,
std::string ret;
toolchain.SwapValue(&ret);
- // The source dir should be source-absolute, so we trim off the two leading
- // slashes to append to the toolchain object directory.
- DCHECK(source_dir.is_source_absolute());
- ret.append(&source_dir.value()[2], source_dir.value().size() - 2);
+ if (source_dir.is_source_absolute()) {
+ // The source dir should be source-absolute, so we trim off the two leading
+ // slashes to append to the toolchain object directory.
+ DCHECK(source_dir.is_source_absolute());
+ ret.append(&source_dir.value()[2], source_dir.value().size() - 2);
+ }
+ // (Put system-absolute stuff in the root gen directory.)
return SourceDir(SourceDir::SWAP_IN, &ret);
}
diff --git a/tools/gn/function_get_path_info.cc b/tools/gn/function_get_path_info.cc
index 23f690c..ea6651c 100644
--- a/tools/gn/function_get_path_info.cc
+++ b/tools/gn/function_get_path_info.cc
@@ -20,9 +20,25 @@ enum What {
WHAT_EXTENSION,
WHAT_DIR,
WHAT_ABSPATH,
+ WHAT_GEN_DIR,
+ WHAT_OUT_DIR,
};
-std::string GetOnePathInfo(const SourceDir& current_dir,
+// Returns the directory containing the input (resolving it against the
+// |current_dir|), regardless of whether the input is a directory or a file.
+SourceDir DirForInput(const SourceDir& current_dir,
+ const std::string& input_string) {
+ if (!input_string.empty() && input_string[input_string.size() - 1] == '/') {
+ // Input is a directory.
+ return current_dir.ResolveRelativeDir(input_string);
+ }
+
+ // Input is a directory.
+ return current_dir.ResolveRelativeFile(input_string).GetDir();
+}
+
+std::string GetOnePathInfo(const Settings* settings,
+ const SourceDir& current_dir,
What what,
const Value& input,
Err* err) {
@@ -62,6 +78,16 @@ std::string GetOnePathInfo(const SourceDir& current_dir,
return std::string("//.");
return dir_incl_slash.substr(0, dir_incl_slash.size() - 1).as_string();
}
+ case WHAT_GEN_DIR: {
+ return DirectoryWithNoLastSlash(
+ GetGenDirForSourceDir(settings,
+ DirForInput(current_dir, input_string)));
+ }
+ case WHAT_OUT_DIR: {
+ return DirectoryWithNoLastSlash(
+ GetOutputDirForSourceDir(settings,
+ DirForInput(current_dir, input_string)));
+ }
case WHAT_ABSPATH: {
if (!input_string.empty() && input_string[input_string.size() - 1] == '/')
return current_dir.ResolveRelativeDir(input_string).value();
@@ -123,6 +149,16 @@ const char kGetPathInfo_Help[] =
" will be appended such that it is always legal to append a slash\n"
" and a filename and get a valid path.\n"
"\n"
+ " \"out_dir\"\n"
+ " The output file directory corresponding to the path of the\n"
+ " given file, not including a trailing slash.\n"
+ " \"//foo/bar/baz.txt\" => \"//out/Default/obj/foo/bar\"\n"
+
+ " \"gen_dir\"\n"
+ " The generated file directory corresponding to the path of the\n"
+ " given file, not including a trailing slash.\n"
+ " \"//foo/bar/baz.txt\" => \"//out/Default/gen/foo/bar\"\n"
+ "\n"
" \"abspath\"\n"
" The full absolute path name to the file or directory. It will be\n"
" resolved relative to the currebt directory, and then the source-\n"
@@ -168,6 +204,10 @@ Value RunGetPathInfo(Scope* scope,
what = WHAT_EXTENSION;
} else if (args[1].string_value() == "dir") {
what = WHAT_DIR;
+ } else if (args[1].string_value() == "out_dir") {
+ what = WHAT_OUT_DIR;
+ } else if (args[1].string_value() == "gen_dir") {
+ what = WHAT_GEN_DIR;
} else if (args[1].string_value() == "abspath") {
what = WHAT_ABSPATH;
} else {
@@ -177,13 +217,15 @@ Value RunGetPathInfo(Scope* scope,
const SourceDir& current_dir = scope->GetSourceDir();
if (args[0].type() == Value::STRING) {
- return Value(function, GetOnePathInfo(current_dir, what, args[0], err));
+ return Value(function, GetOnePathInfo(scope->settings(), current_dir, what,
+ args[0], err));
} else if (args[0].type() == Value::LIST) {
const std::vector<Value>& input_list = args[0].list_value();
Value result(function, Value::LIST);
for (size_t i = 0; i < input_list.size(); i++) {
result.list_value().push_back(Value(function,
- GetOnePathInfo(current_dir, what, input_list[i], err)));
+ GetOnePathInfo(scope->settings(), current_dir, what,
+ input_list[i], err)));
if (err->has_error())
return Value();
}
diff --git a/tools/gn/function_get_path_info_unittest.cc b/tools/gn/function_get_path_info_unittest.cc
index 12bdc2a..1756c0c 100644
--- a/tools/gn/function_get_path_info_unittest.cc
+++ b/tools/gn/function_get_path_info_unittest.cc
@@ -87,3 +87,25 @@ TEST_F(GetPathInfoTest, AbsPath) {
EXPECT_EQ("/foo/", Call("/foo/", "abspath"));
EXPECT_EQ("/", Call("/", "abspath"));
}
+
+// Note build dir is "//out/Debug/".
+TEST_F(GetPathInfoTest, OutDir) {
+ EXPECT_EQ("//out/Debug/obj/src/foo/foo", Call("foo/bar.txt", "out_dir"));
+ EXPECT_EQ("//out/Debug/obj/src/foo/bar", Call("bar/", "out_dir"));
+ EXPECT_EQ("//out/Debug/obj/src/foo", Call(".", "out_dir"));
+ EXPECT_EQ("//out/Debug/obj/src/foo", Call("bar", "out_dir"));
+ EXPECT_EQ("//out/Debug/obj/foo", Call("//foo/bar.txt", "out_dir"));
+ // System paths go into the root obj directory.
+ EXPECT_EQ("//out/Debug/obj", Call("/foo/bar.txt", "out_dir"));
+}
+
+// Note build dir is "//out/Debug/".
+TEST_F(GetPathInfoTest, GenDir) {
+ EXPECT_EQ("//out/Debug/gen/src/foo/foo", Call("foo/bar.txt", "gen_dir"));
+ EXPECT_EQ("//out/Debug/gen/src/foo/bar", Call("bar/", "gen_dir"));
+ EXPECT_EQ("//out/Debug/gen/src/foo", Call(".", "gen_dir"));
+ EXPECT_EQ("//out/Debug/gen/src/foo", Call("bar", "gen_dir"));
+ EXPECT_EQ("//out/Debug/gen/foo", Call("//foo/bar.txt", "gen_dir"));
+ // System paths go into the root obj directory.
+ EXPECT_EQ("//out/Debug/gen", Call("/foo/bar.txt", "gen_dir"));
+}
diff --git a/tools/gn/function_get_target_outputs.cc b/tools/gn/function_get_target_outputs.cc
index 370fee9..be014f2 100644
--- a/tools/gn/function_get_target_outputs.cc
+++ b/tools/gn/function_get_target_outputs.cc
@@ -15,7 +15,7 @@ namespace functions {
namespace {
-void GetOutputsForTarget(const BuildSettings* build_settings,
+void GetOutputsForTarget(const Settings* settings,
const Target* target,
std::vector<std::string>* ret) {
switch (target->output_type()) {
@@ -31,10 +31,10 @@ void GetOutputsForTarget(const BuildSettings* build_settings,
case Target::ACTION_FOREACH: {
// Action_foreach: return the result of the template in the outputs.
- FileTemplate file_template(target->action_values().outputs());
+ FileTemplate file_template(settings, target->action_values().outputs());
const std::vector<SourceFile>& sources = target->sources();
for (size_t i = 0; i < sources.size(); i++)
- file_template.ApplyString(sources[i].value(), ret);
+ file_template.Apply(sources[i], ret);
break;
}
@@ -50,11 +50,12 @@ void GetOutputsForTarget(const BuildSettings* build_settings,
case Target::GROUP:
case Target::SOURCE_SET: {
// These return the stamp file, which is computed by the NinjaHelper.
- NinjaHelper helper(build_settings);
+ NinjaHelper helper(settings->build_settings());
OutputFile output_file = helper.GetTargetOutputFile(target);
// The output file is relative to the build dir.
- std::string absolute_output_file = build_settings->build_dir().value();
+ std::string absolute_output_file =
+ settings->build_settings()->build_dir().value();
absolute_output_file.append(output_file.value());
ret->push_back(absolute_output_file);
@@ -168,7 +169,7 @@ Value RunGetTargetOutputs(Scope* scope,
}
std::vector<std::string> files;
- GetOutputsForTarget(scope->settings()->build_settings(), target, &files);
+ GetOutputsForTarget(scope->settings(), target, &files);
Value ret(function, Value::LIST);
ret.list_value().reserve(files.size());
diff --git a/tools/gn/function_process_file_template.cc b/tools/gn/function_process_file_template.cc
index 5d4c9d7..061c901 100644
--- a/tools/gn/function_process_file_template.cc
+++ b/tools/gn/function_process_file_template.cc
@@ -5,6 +5,10 @@
#include "tools/gn/file_template.h"
#include "tools/gn/functions.h"
#include "tools/gn/parse_tree.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/settings.h"
+#include "tools/gn/target.h"
+#include "tools/gn/value_extractors.h"
namespace functions {
@@ -67,12 +71,26 @@ Value RunProcessFileTemplate(Scope* scope,
return Value();
}
- FileTemplate file_template(args[1], err);
+ FileTemplate file_template(scope->settings(), args[1], err);
if (err->has_error())
return Value();
+ Target::FileList input_files;
+ if (!ExtractListOfRelativeFiles(scope->settings()->build_settings(), args[0],
+ scope->GetSourceDir(), &input_files, err))
+ return Value();
+
Value ret(function, Value::LIST);
- file_template.Apply(args[0], function, &ret.list_value(), err);
+
+ // Temporary holding place, allocate outside to re-use buffer.
+ std::vector<std::string> string_output;
+
+ for (size_t i = 0; i < input_files.size(); i++) {
+ string_output.clear();
+ file_template.Apply(input_files[i], &string_output);
+ for (size_t out_i = 0; out_i < string_output.size(); out_i++)
+ ret.list_value().push_back(Value(function, string_output[out_i]));
+ }
return ret;
}
diff --git a/tools/gn/ninja_action_target_writer.cc b/tools/gn/ninja_action_target_writer.cc
index 7f14e74..6a632e7 100644
--- a/tools/gn/ninja_action_target_writer.cc
+++ b/tools/gn/ninja_action_target_writer.cc
@@ -23,7 +23,8 @@ NinjaActionTargetWriter::~NinjaActionTargetWriter() {
}
void NinjaActionTargetWriter::Run() {
- FileTemplate args_template(target_->action_values().args());
+ FileTemplate args_template(target_->settings(),
+ target_->action_values().args());
std::string custom_rule_name = WriteRuleDefinition(args_template);
// Collect our deps to pass as "extra hard dependencies" for input deps. This
@@ -136,14 +137,11 @@ std::string NinjaActionTargetWriter::WriteRuleDefinition(
void NinjaActionTargetWriter::WriteArgsSubstitutions(
const SourceFile& source,
const FileTemplate& args_template) {
- std::ostringstream source_file_stream;
- path_output_no_escaping_.WriteFile(source_file_stream, source);
-
EscapeOptions template_escape_options;
template_escape_options.mode = ESCAPE_NINJA_COMMAND;
args_template.WriteNinjaVariablesForSubstitution(
- out_, source_file_stream.str(), template_escape_options);
+ out_, target_->settings(), source, template_escape_options);
}
void NinjaActionTargetWriter::WriteSourceRules(
@@ -208,7 +206,7 @@ void NinjaActionTargetWriter::WriteOutputFilesForBuildLine(
const SourceFile& source,
std::vector<OutputFile>* output_files) {
std::vector<std::string> output_template_result;
- output_template.ApplyString(source.value(), &output_template_result);
+ output_template.Apply(source, &output_template_result);
for (size_t out_i = 0; out_i < output_template_result.size(); out_i++) {
OutputFile output_path(output_template_result[out_i]);
output_files->push_back(output_path);
@@ -219,7 +217,7 @@ void NinjaActionTargetWriter::WriteOutputFilesForBuildLine(
void NinjaActionTargetWriter::WriteDepfile(const SourceFile& source) {
std::vector<std::string> result;
- GetDepfileTemplate().ApplyString(source.value(), &result);
+ GetDepfileTemplate().Apply(source, &result);
path_output_.WriteFile(out_, OutputFile(result[0]));
}
@@ -229,5 +227,5 @@ FileTemplate NinjaActionTargetWriter::GetDepfileTemplate() const {
RemovePrefix(target_->action_values().depfile().value(),
settings_->build_settings()->build_dir().value());
template_args.push_back(depfile_relative_to_build_dir);
- return FileTemplate(template_args);
+ return FileTemplate(settings_, template_args);
}
diff --git a/tools/gn/ninja_action_target_writer_unittest.cc b/tools/gn/ninja_action_target_writer_unittest.cc
index 9899e2c..c6aa5f8 100644
--- a/tools/gn/ninja_action_target_writer_unittest.cc
+++ b/tools/gn/ninja_action_target_writer_unittest.cc
@@ -44,7 +44,7 @@ TEST(NinjaActionTargetWriter, WriteArgsSubstitutions) {
args.push_back("-i");
args.push_back("{{source}}");
args.push_back("--out=foo bar{{source_name_part}}.o");
- FileTemplate args_template(args);
+ FileTemplate args_template(setup.settings(), args);
writer.WriteArgsSubstitutions(SourceFile("//foo/b ar.in"), args_template);
#if defined(OS_WIN)
diff --git a/tools/gn/ninja_copy_target_writer.cc b/tools/gn/ninja_copy_target_writer.cc
index 9ab88e4..4f8534f 100644
--- a/tools/gn/ninja_copy_target_writer.cc
+++ b/tools/gn/ninja_copy_target_writer.cc
@@ -30,7 +30,7 @@ void NinjaCopyTargetWriter::Run() {
// Make the output file from the template.
std::vector<std::string> template_result;
- output_template.ApplyString(input_file.value(), &template_result);
+ output_template.Apply(input_file, &template_result);
CHECK(template_result.size() == 1);
OutputFile output_file(template_result[0]);
diff --git a/tools/gn/ninja_target_writer.cc b/tools/gn/ninja_target_writer.cc
index b6412dc..2383251 100644
--- a/tools/gn/ninja_target_writer.cc
+++ b/tools/gn/ninja_target_writer.cc
@@ -165,5 +165,5 @@ FileTemplate NinjaTargetWriter::GetOutputTemplate() const {
RemovePrefix(outputs[i].value(),
settings_->build_settings()->build_dir().value()));
}
- return FileTemplate(output_template_args);
+ return FileTemplate(target_->settings(), output_template_args);
}