Chromium Code Reviews| Index: tools/gn/ninja_build_writer.cc |
| diff --git a/tools/gn/ninja_build_writer.cc b/tools/gn/ninja_build_writer.cc |
| index 11b36e6b591e74a85753f2998aad2dcfe003dc36..ad37f942bdf336322abfe11c46a56ff310a694ba 100644 |
| --- a/tools/gn/ninja_build_writer.cc |
| +++ b/tools/gn/ninja_build_writer.cc |
| @@ -11,6 +11,7 @@ |
| #include "base/file_util.h" |
| #include "base/path_service.h" |
| #include "base/process/process_handle.h" |
| +#include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "build/build_config.h" |
| #include "tools/gn/build_settings.h" |
| @@ -179,30 +180,87 @@ void NinjaBuildWriter::WritePhonyAndAllRules() { |
| // Write phony rules for all uniquely-named targets in the default toolchain. |
| // Don't do other toolchains or we'll get naming conflicts, and if the name |
| - // isn't unique, also skip it. |
| + // isn't unique, also skip it. The exception is for the toplevel targets |
| + // which we also find. |
| std::map<std::string, int> small_name_count; |
| - for (size_t i = 0; i < default_toolchain_targets_.size(); i++) |
| - small_name_count[default_toolchain_targets_[i]->label().name()]++; |
| - |
| + std::vector<const Target*> toplevel_targets; |
| for (size_t i = 0; i < default_toolchain_targets_.size(); i++) { |
| const Target* target = default_toolchain_targets_[i]; |
| + const Label& label = target->label(); |
| + small_name_count[label.name()]++; |
| + |
| + // Look for targets with a name of the form |
| + // dir = "//foo/", name = "foo" |
| + // i.e. where the target name matches the top level directory. We will |
| + // always write phony rules for these even if there is another target with |
| + // the same short name. |
| + const std::string& dir_string = label.dir().value(); |
| + if (dir_string.size() == label.name().size() + 3 && // Size matches. |
| + dir_string[0] == '/' && dir_string[1] == '/' && // "//" at beginning. |
| + dir_string[dir_string.size() - 1] == '/' && // "/" at end. |
| + dir_string.compare(2, label.name().size(), label.name()) == 0) |
| + toplevel_targets.push_back(target); |
| + } |
| + for (size_t i = 0; i < default_toolchain_targets_.size(); i++) { |
| + const Target* target = default_toolchain_targets_[i]; |
| + const Label& label = target->label(); |
| OutputFile target_file = helper_.GetTargetOutputFile(target); |
| - if (target_file.value() != target->label().name() && |
| - small_name_count[default_toolchain_targets_[i]->label().name()] == 1) { |
| - out_ << "build " << target->label().name() << ": phony "; |
| - path_output_.WriteFile(out_, target_file); |
| - out_ << std::endl; |
| + |
| + // Write the long name "foo/bar:baz" for the target "//foo/bar:baz". |
| + std::string long_name = label.GetUserVisibleName(false); |
| + base::TrimString(long_name, "/", &long_name); |
| + WritePhonyRule(target, target_file, long_name); |
| + |
| + // Write the directory name with no target name if they match |
| + // (e.g. "//foo/bar:bar" -> "foo/bar"). |
| + if (FindLastDirComponent(label.dir()) == label.name()) { |
| + std::string medium_name = DirectoryWithNoLastSlash(label.dir()); |
| + base::TrimString(medium_name, "/", &medium_name); |
| + WritePhonyRule(target, target_file, medium_name); |
| } |
| + // Write short names for ones which are unique. |
| + if (small_name_count[label.name()] == 1) |
| + WritePhonyRule(target, target_file, label.name()); |
| + |
| if (!all_rules.empty()) |
| all_rules.append(" $\n "); |
| all_rules.append(target_file.value()); |
| } |
| + // Pick up phony rules for the toplevel targets with non-unique names (which |
| + // would have been skipped in the above loop). |
| + for (size_t i = 0; i < toplevel_targets.size(); i++) { |
| + if (small_name_count[toplevel_targets[i]->label().name()] > 1) { |
| + const Target* target = toplevel_targets[i]; |
| + WritePhonyRule(target, helper_.GetTargetOutputFile(target), |
| + target->label().name()); |
| + } |
| + } |
| + |
| if (!all_rules.empty()) { |
| out_ << "\nbuild all: phony " << all_rules << std::endl; |
| out_ << "default all" << std::endl; |
| } |
| } |
| +void NinjaBuildWriter::WritePhonyRule(const Target* target, |
| + const OutputFile& target_file, |
| + const std::string& phony_name) { |
| + if (target_file.value() == phony_name) |
| + return; // No need for a phony rule. |
| + |
| + EscapeOptions ninja_escape; |
| + ninja_escape.mode = ESCAPE_NINJA; |
| + |
| + // Escape for special chars Ninja will handle. |
| + std::string escaped = EscapeString(phony_name, ninja_escape, NULL); |
| + |
| + // Additionally escape colon which is meaningful in this context. |
| + ReplaceSubstringsAfterOffset(&escaped, 0, ":", "$:"); |
|
scottmg
2014/04/28 18:45:21
shouldn't escape_ninja be doing this?
|
| + |
| + out_ << "build " << escaped << ": phony "; |
| + path_output_.WriteFile(out_, target_file); |
| + out_ << std::endl; |
| +} |