diff options
author | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-19 16:50:52 +0000 |
---|---|---|
committer | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-19 16:50:52 +0000 |
commit | e9bf4fcb470f7220306c39827fc3fd9be0a27741 (patch) | |
tree | 59d2c4de7ab1964d6c421dd014b629ce9bd961e1 /tools/gn | |
parent | 252e6603f3012cdc8de4a212999ef62660769be7 (diff) | |
download | chromium_src-e9bf4fcb470f7220306c39827fc3fd9be0a27741.zip chromium_src-e9bf4fcb470f7220306c39827fc3fd9be0a27741.tar.gz chromium_src-e9bf4fcb470f7220306c39827fc3fd9be0a27741.tar.bz2 |
Add directory extraction to GN path handling.
Some of the mojo templates want to put generated files in the directory
corresponding to the source file, rather than the directory containing the
BUILD file. Previously, this required putting the BUILD files in the same
directory.
This patch adds the ability to specify the generated file directory
corresponding to the input file in the action_foreach file templates so this
can be expressed naturally.
For parity, it also adds qquerying for this information via get_path_info.
BUG=
R=cjhopman@chromium.org
Review URL: https://codereview.chromium.org/334333005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@278402 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools/gn')
-rw-r--r-- | tools/gn/command_desc.cc | 2 | ||||
-rw-r--r-- | tools/gn/file_template.cc | 189 | ||||
-rw-r--r-- | tools/gn/file_template.h | 54 | ||||
-rw-r--r-- | tools/gn/file_template_unittest.cc | 79 | ||||
-rw-r--r-- | tools/gn/filesystem_utils.cc | 21 | ||||
-rw-r--r-- | tools/gn/function_get_path_info.cc | 48 | ||||
-rw-r--r-- | tools/gn/function_get_path_info_unittest.cc | 22 | ||||
-rw-r--r-- | tools/gn/function_get_target_outputs.cc | 13 | ||||
-rw-r--r-- | tools/gn/function_process_file_template.cc | 22 | ||||
-rw-r--r-- | tools/gn/ninja_action_target_writer.cc | 14 | ||||
-rw-r--r-- | tools/gn/ninja_action_target_writer_unittest.cc | 2 | ||||
-rw-r--r-- | tools/gn/ninja_copy_target_writer.cc | 2 | ||||
-rw-r--r-- | tools/gn/ninja_target_writer.cc | 2 |
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); } |