Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "tools/gn/ninja_build_writer.h" | 5 #include "tools/gn/ninja_build_writer.h" |
| 6 | 6 |
| 7 #include <fstream> | 7 #include <fstream> |
| 8 #include <map> | 8 #include <map> |
| 9 | 9 |
| 10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
| 11 #include "base/files/file_util.h" | 11 #include "base/files/file_util.h" |
| 12 #include "base/path_service.h" | 12 #include "base/path_service.h" |
| 13 #include "base/process/process_handle.h" | 13 #include "base/process/process_handle.h" |
| 14 #include "base/strings/string_util.h" | 14 #include "base/strings/string_util.h" |
| 15 #include "base/strings/utf_string_conversions.h" | 15 #include "base/strings/utf_string_conversions.h" |
| 16 #include "build/build_config.h" | 16 #include "build/build_config.h" |
| 17 #include "tools/gn/build_settings.h" | 17 #include "tools/gn/build_settings.h" |
| 18 #include "tools/gn/err.h" | |
| 18 #include "tools/gn/escape.h" | 19 #include "tools/gn/escape.h" |
| 19 #include "tools/gn/filesystem_utils.h" | 20 #include "tools/gn/filesystem_utils.h" |
| 20 #include "tools/gn/input_file_manager.h" | 21 #include "tools/gn/input_file_manager.h" |
| 21 #include "tools/gn/ninja_utils.h" | 22 #include "tools/gn/ninja_utils.h" |
| 22 #include "tools/gn/scheduler.h" | 23 #include "tools/gn/scheduler.h" |
| 23 #include "tools/gn/target.h" | 24 #include "tools/gn/target.h" |
| 24 #include "tools/gn/trace.h" | 25 #include "tools/gn/trace.h" |
| 25 | 26 |
| 26 #if defined(OS_WIN) | 27 #if defined(OS_WIN) |
| 27 #include <windows.h> | 28 #include <windows.h> |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 84 default_toolchain_(default_toolchain), | 85 default_toolchain_(default_toolchain), |
| 85 default_toolchain_targets_(default_toolchain_targets), | 86 default_toolchain_targets_(default_toolchain_targets), |
| 86 out_(out), | 87 out_(out), |
| 87 dep_out_(dep_out), | 88 dep_out_(dep_out), |
| 88 path_output_(build_settings->build_dir(), ESCAPE_NINJA) { | 89 path_output_(build_settings->build_dir(), ESCAPE_NINJA) { |
| 89 } | 90 } |
| 90 | 91 |
| 91 NinjaBuildWriter::~NinjaBuildWriter() { | 92 NinjaBuildWriter::~NinjaBuildWriter() { |
| 92 } | 93 } |
| 93 | 94 |
| 94 void NinjaBuildWriter::Run() { | 95 bool NinjaBuildWriter::Run() { |
|
brettw
2014/09/26 18:01:20
Yeah, this should probably take an Err* out param.
| |
| 95 WriteNinjaRules(); | 96 WriteNinjaRules(); |
| 96 WriteLinkPool(); | 97 WriteLinkPool(); |
| 97 WriteSubninjas(); | 98 WriteSubninjas(); |
| 98 WritePhonyAndAllRules(); | 99 return WritePhonyAndAllRules(); |
| 99 } | 100 } |
| 100 | 101 |
| 101 // static | 102 // static |
| 102 bool NinjaBuildWriter::RunAndWriteFile( | 103 bool NinjaBuildWriter::RunAndWriteFile( |
| 103 const BuildSettings* build_settings, | 104 const BuildSettings* build_settings, |
| 104 const std::vector<const Settings*>& all_settings, | 105 const std::vector<const Settings*>& all_settings, |
| 105 const Toolchain* default_toolchain, | 106 const Toolchain* default_toolchain, |
| 106 const std::vector<const Target*>& default_toolchain_targets) { | 107 const std::vector<const Target*>& default_toolchain_targets) { |
| 107 ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, "build.ninja"); | 108 ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, "build.ninja"); |
| 108 | 109 |
| 109 base::FilePath ninja_file(build_settings->GetFullPath( | 110 base::FilePath ninja_file(build_settings->GetFullPath( |
| 110 SourceFile(build_settings->build_dir().value() + "build.ninja"))); | 111 SourceFile(build_settings->build_dir().value() + "build.ninja"))); |
| 111 base::CreateDirectory(ninja_file.DirName()); | 112 base::CreateDirectory(ninja_file.DirName()); |
| 112 | 113 |
| 113 std::ofstream file; | 114 std::ofstream file; |
| 114 file.open(FilePathToUTF8(ninja_file).c_str(), | 115 file.open(FilePathToUTF8(ninja_file).c_str(), |
| 115 std::ios_base::out | std::ios_base::binary); | 116 std::ios_base::out | std::ios_base::binary); |
| 116 if (file.fail()) | 117 if (file.fail()) |
| 117 return false; | 118 return false; |
| 118 | 119 |
| 119 std::ofstream depfile; | 120 std::ofstream depfile; |
| 120 depfile.open((FilePathToUTF8(ninja_file) + ".d").c_str(), | 121 depfile.open((FilePathToUTF8(ninja_file) + ".d").c_str(), |
| 121 std::ios_base::out | std::ios_base::binary); | 122 std::ios_base::out | std::ios_base::binary); |
| 122 if (depfile.fail()) | 123 if (depfile.fail()) |
| 123 return false; | 124 return false; |
| 124 | 125 |
| 125 NinjaBuildWriter gen(build_settings, all_settings, default_toolchain, | 126 NinjaBuildWriter gen(build_settings, all_settings, default_toolchain, |
| 126 default_toolchain_targets, file, depfile); | 127 default_toolchain_targets, file, depfile); |
| 127 gen.Run(); | 128 return gen.Run(); |
| 128 return true; | |
| 129 } | 129 } |
| 130 | 130 |
| 131 void NinjaBuildWriter::WriteNinjaRules() { | 131 void NinjaBuildWriter::WriteNinjaRules() { |
| 132 out_ << "rule gn\n"; | 132 out_ << "rule gn\n"; |
| 133 out_ << " command = " << GetSelfInvocationCommand(build_settings_) << "\n"; | 133 out_ << " command = " << GetSelfInvocationCommand(build_settings_) << "\n"; |
| 134 out_ << " description = Regenerating ninja files\n\n"; | 134 out_ << " description = Regenerating ninja files\n\n"; |
| 135 | 135 |
| 136 // This rule will regenerate the ninja files when any input file has changed. | 136 // This rule will regenerate the ninja files when any input file has changed. |
| 137 out_ << "build build.ninja: gn\n" | 137 out_ << "build build.ninja: gn\n" |
| 138 << " generator = 1\n" | 138 << " generator = 1\n" |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 164 | 164 |
| 165 void NinjaBuildWriter::WriteSubninjas() { | 165 void NinjaBuildWriter::WriteSubninjas() { |
| 166 for (size_t i = 0; i < all_settings_.size(); i++) { | 166 for (size_t i = 0; i < all_settings_.size(); i++) { |
| 167 out_ << "subninja "; | 167 out_ << "subninja "; |
| 168 path_output_.WriteFile(out_, GetNinjaFileForToolchain(all_settings_[i])); | 168 path_output_.WriteFile(out_, GetNinjaFileForToolchain(all_settings_[i])); |
| 169 out_ << std::endl; | 169 out_ << std::endl; |
| 170 } | 170 } |
| 171 out_ << std::endl; | 171 out_ << std::endl; |
| 172 } | 172 } |
| 173 | 173 |
| 174 void NinjaBuildWriter::WritePhonyAndAllRules() { | 174 bool NinjaBuildWriter::WritePhonyAndAllRules() { |
| 175 std::string all_rules; | 175 std::string all_rules; |
| 176 | 176 |
| 177 // Write phony rules for all uniquely-named targets in the default toolchain. | 177 // Write phony rules for all uniquely-named targets in the default toolchain. |
| 178 // Don't do other toolchains or we'll get naming conflicts, and if the name | 178 // Don't do other toolchains or we'll get naming conflicts, and if the name |
| 179 // isn't unique, also skip it. The exception is for the toplevel targets | 179 // isn't unique, also skip it. The exception is for the toplevel targets |
| 180 // which we also find. | 180 // which we also find. |
| 181 std::map<std::string, int> small_name_count; | 181 std::map<std::string, int> small_name_count; |
| 182 std::vector<const Target*> toplevel_targets; | 182 std::vector<const Target*> toplevel_targets; |
| 183 base::hash_set<std::string> target_files; | |
| 183 for (size_t i = 0; i < default_toolchain_targets_.size(); i++) { | 184 for (size_t i = 0; i < default_toolchain_targets_.size(); i++) { |
| 184 const Target* target = default_toolchain_targets_[i]; | 185 const Target* target = default_toolchain_targets_[i]; |
| 185 const Label& label = target->label(); | 186 const Label& label = target->label(); |
| 186 small_name_count[label.name()]++; | 187 small_name_count[label.name()]++; |
| 187 | 188 |
| 188 // Look for targets with a name of the form | 189 // Look for targets with a name of the form |
| 189 // dir = "//foo/", name = "foo" | 190 // dir = "//foo/", name = "foo" |
| 190 // i.e. where the target name matches the top level directory. We will | 191 // i.e. where the target name matches the top level directory. We will |
| 191 // always write phony rules for these even if there is another target with | 192 // always write phony rules for these even if there is another target with |
| 192 // the same short name. | 193 // the same short name. |
| 193 const std::string& dir_string = label.dir().value(); | 194 const std::string& dir_string = label.dir().value(); |
| 194 if (dir_string.size() == label.name().size() + 3 && // Size matches. | 195 if (dir_string.size() == label.name().size() + 3 && // Size matches. |
| 195 dir_string[0] == '/' && dir_string[1] == '/' && // "//" at beginning. | 196 dir_string[0] == '/' && dir_string[1] == '/' && // "//" at beginning. |
| 196 dir_string[dir_string.size() - 1] == '/' && // "/" at end. | 197 dir_string[dir_string.size() - 1] == '/' && // "/" at end. |
| 197 dir_string.compare(2, label.name().size(), label.name()) == 0) | 198 dir_string.compare(2, label.name().size(), label.name()) == 0) |
| 198 toplevel_targets.push_back(target); | 199 toplevel_targets.push_back(target); |
| 199 } | 200 } |
| 200 | 201 |
| 201 for (size_t i = 0; i < default_toolchain_targets_.size(); i++) { | 202 for (size_t i = 0; i < default_toolchain_targets_.size(); i++) { |
| 202 const Target* target = default_toolchain_targets_[i]; | 203 const Target* target = default_toolchain_targets_[i]; |
| 203 const Label& label = target->label(); | 204 const Label& label = target->label(); |
| 204 OutputFile target_file(target->dependency_output_file()); | 205 OutputFile target_file(target->dependency_output_file()); |
| 205 // The output files may have leading "./" so normalize those away. | 206 // The output files may have leading "./" so normalize those away. |
| 206 NormalizePath(&target_file.value()); | 207 NormalizePath(&target_file.value()); |
| 208 if (!target_files.insert(target_file.value()).second) { | |
| 209 Err(Location(), "Duplicate rules for " + target_file.value()) | |
| 210 .PrintToStdout(); | |
| 211 return false; | |
| 212 } | |
| 207 | 213 |
| 208 // Write the long name "foo/bar:baz" for the target "//foo/bar:baz". | 214 // Write the long name "foo/bar:baz" for the target "//foo/bar:baz". |
| 209 std::string long_name = label.GetUserVisibleName(false); | 215 std::string long_name = label.GetUserVisibleName(false); |
| 210 base::TrimString(long_name, "/", &long_name); | 216 base::TrimString(long_name, "/", &long_name); |
| 211 WritePhonyRule(target, target_file, long_name); | 217 WritePhonyRule(target, target_file, long_name); |
| 212 | 218 |
| 213 // Write the directory name with no target name if they match | 219 // Write the directory name with no target name if they match |
| 214 // (e.g. "//foo/bar:bar" -> "foo/bar"). | 220 // (e.g. "//foo/bar:bar" -> "foo/bar"). |
| 215 if (FindLastDirComponent(label.dir()) == label.name()) { | 221 if (FindLastDirComponent(label.dir()) == label.name()) { |
| 216 std::string medium_name = DirectoryWithNoLastSlash(label.dir()); | 222 std::string medium_name = DirectoryWithNoLastSlash(label.dir()); |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 237 const Target* target = toplevel_targets[i]; | 243 const Target* target = toplevel_targets[i]; |
| 238 WritePhonyRule(target, target->dependency_output_file(), | 244 WritePhonyRule(target, target->dependency_output_file(), |
| 239 target->label().name()); | 245 target->label().name()); |
| 240 } | 246 } |
| 241 } | 247 } |
| 242 | 248 |
| 243 if (!all_rules.empty()) { | 249 if (!all_rules.empty()) { |
| 244 out_ << "\nbuild all: phony " << all_rules << std::endl; | 250 out_ << "\nbuild all: phony " << all_rules << std::endl; |
| 245 out_ << "default all" << std::endl; | 251 out_ << "default all" << std::endl; |
| 246 } | 252 } |
| 253 return true; | |
| 247 } | 254 } |
| 248 | 255 |
| 249 void NinjaBuildWriter::WritePhonyRule(const Target* target, | 256 void NinjaBuildWriter::WritePhonyRule(const Target* target, |
| 250 const OutputFile& target_file, | 257 const OutputFile& target_file, |
| 251 const std::string& phony_name) { | 258 const std::string& phony_name) { |
| 252 if (target_file.value() == phony_name) | 259 if (target_file.value() == phony_name) |
| 253 return; // No need for a phony rule. | 260 return; // No need for a phony rule. |
| 254 | 261 |
| 255 EscapeOptions ninja_escape; | 262 EscapeOptions ninja_escape; |
| 256 ninja_escape.mode = ESCAPE_NINJA; | 263 ninja_escape.mode = ESCAPE_NINJA; |
| 257 | 264 |
| 258 // Escape for special chars Ninja will handle. | 265 // Escape for special chars Ninja will handle. |
| 259 std::string escaped = EscapeString(phony_name, ninja_escape, NULL); | 266 std::string escaped = EscapeString(phony_name, ninja_escape, NULL); |
| 260 | 267 |
| 261 out_ << "build " << escaped << ": phony "; | 268 out_ << "build " << escaped << ": phony "; |
| 262 path_output_.WriteFile(out_, target_file); | 269 path_output_.WriteFile(out_, target_file); |
| 263 out_ << std::endl; | 270 out_ << std::endl; |
| 264 } | 271 } |
| OLD | NEW |