Index: tools/gn/file_template.cc |
diff --git a/tools/gn/file_template.cc b/tools/gn/file_template.cc |
index b00eb391738b1fff05d0141e58623e4a676dabbf..014b5e5608e264921ce75fac166650f37ff84f62 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 us be different than\n" |
cjhopman
2014/06/18 18:13:30
nit: s/this/This
cjhopman
2014/06/18 18:13:30
"this us"? s/us/may or s/us/will maybe?
|
+ " 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" |
cjhopman
2014/06/18 18:13:30
s/if/If
|
+ " 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" |
cjhopman
2014/06/18 18:13:30
capitalization in this paragraph too
|
+ " 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 |
} |
} |