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..c0fc7fcfedfd9667c26de0984cf2fca47c7f92d1 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); |
+ // That may have generated a name the same as the short name of the |
+ // target which we already wrote. |
+ if (medium_name != label.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); |
+ |
+ out_ << "build " << escaped << ": phony "; |
+ path_output_.WriteFile(out_, target_file); |
+ out_ << std::endl; |
+} |