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(Err* err) { |
95 WriteNinjaRules(); | 96 WriteNinjaRules(); |
96 WriteLinkPool(); | 97 WriteLinkPool(); |
97 WriteSubninjas(); | 98 WriteSubninjas(); |
98 WritePhonyAndAllRules(); | 99 return WritePhonyAndAllRules(err); |
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, |
| 108 Err* err) { |
107 ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, "build.ninja"); | 109 ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, "build.ninja"); |
108 | 110 |
109 base::FilePath ninja_file(build_settings->GetFullPath( | 111 base::FilePath ninja_file(build_settings->GetFullPath( |
110 SourceFile(build_settings->build_dir().value() + "build.ninja"))); | 112 SourceFile(build_settings->build_dir().value() + "build.ninja"))); |
111 base::CreateDirectory(ninja_file.DirName()); | 113 base::CreateDirectory(ninja_file.DirName()); |
112 | 114 |
113 std::ofstream file; | 115 std::ofstream file; |
114 file.open(FilePathToUTF8(ninja_file).c_str(), | 116 file.open(FilePathToUTF8(ninja_file).c_str(), |
115 std::ios_base::out | std::ios_base::binary); | 117 std::ios_base::out | std::ios_base::binary); |
116 if (file.fail()) | 118 if (file.fail()) { |
| 119 *err = Err(Location(), "Couldn't open build.ninja for writing"); |
117 return false; | 120 return false; |
| 121 } |
118 | 122 |
119 std::ofstream depfile; | 123 std::ofstream depfile; |
120 depfile.open((FilePathToUTF8(ninja_file) + ".d").c_str(), | 124 depfile.open((FilePathToUTF8(ninja_file) + ".d").c_str(), |
121 std::ios_base::out | std::ios_base::binary); | 125 std::ios_base::out | std::ios_base::binary); |
122 if (depfile.fail()) | 126 if (depfile.fail()) { |
| 127 *err = Err(Location(), "Couldn't open depfile for writing"); |
123 return false; | 128 return false; |
| 129 } |
124 | 130 |
125 NinjaBuildWriter gen(build_settings, all_settings, default_toolchain, | 131 NinjaBuildWriter gen(build_settings, all_settings, default_toolchain, |
126 default_toolchain_targets, file, depfile); | 132 default_toolchain_targets, file, depfile); |
127 gen.Run(); | 133 return gen.Run(err); |
128 return true; | |
129 } | 134 } |
130 | 135 |
131 void NinjaBuildWriter::WriteNinjaRules() { | 136 void NinjaBuildWriter::WriteNinjaRules() { |
132 out_ << "rule gn\n"; | 137 out_ << "rule gn\n"; |
133 out_ << " command = " << GetSelfInvocationCommand(build_settings_) << "\n"; | 138 out_ << " command = " << GetSelfInvocationCommand(build_settings_) << "\n"; |
134 out_ << " description = Regenerating ninja files\n\n"; | 139 out_ << " description = Regenerating ninja files\n\n"; |
135 | 140 |
136 // This rule will regenerate the ninja files when any input file has changed. | 141 // This rule will regenerate the ninja files when any input file has changed. |
137 out_ << "build build.ninja: gn\n" | 142 out_ << "build build.ninja: gn\n" |
138 << " generator = 1\n" | 143 << " generator = 1\n" |
(...skipping 25 matching lines...) Expand all Loading... |
164 | 169 |
165 void NinjaBuildWriter::WriteSubninjas() { | 170 void NinjaBuildWriter::WriteSubninjas() { |
166 for (size_t i = 0; i < all_settings_.size(); i++) { | 171 for (size_t i = 0; i < all_settings_.size(); i++) { |
167 out_ << "subninja "; | 172 out_ << "subninja "; |
168 path_output_.WriteFile(out_, GetNinjaFileForToolchain(all_settings_[i])); | 173 path_output_.WriteFile(out_, GetNinjaFileForToolchain(all_settings_[i])); |
169 out_ << std::endl; | 174 out_ << std::endl; |
170 } | 175 } |
171 out_ << std::endl; | 176 out_ << std::endl; |
172 } | 177 } |
173 | 178 |
174 void NinjaBuildWriter::WritePhonyAndAllRules() { | 179 bool NinjaBuildWriter::WritePhonyAndAllRules(Err* err) { |
175 std::string all_rules; | 180 std::string all_rules; |
176 | 181 |
177 // Write phony rules for all uniquely-named targets in the default toolchain. | 182 // 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 | 183 // 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 | 184 // isn't unique, also skip it. The exception is for the toplevel targets |
180 // which we also find. | 185 // which we also find. |
181 std::map<std::string, int> small_name_count; | 186 std::map<std::string, int> small_name_count; |
182 std::vector<const Target*> toplevel_targets; | 187 std::vector<const Target*> toplevel_targets; |
| 188 base::hash_set<std::string> target_files; |
183 for (size_t i = 0; i < default_toolchain_targets_.size(); i++) { | 189 for (size_t i = 0; i < default_toolchain_targets_.size(); i++) { |
184 const Target* target = default_toolchain_targets_[i]; | 190 const Target* target = default_toolchain_targets_[i]; |
185 const Label& label = target->label(); | 191 const Label& label = target->label(); |
186 small_name_count[label.name()]++; | 192 small_name_count[label.name()]++; |
187 | 193 |
188 // Look for targets with a name of the form | 194 // Look for targets with a name of the form |
189 // dir = "//foo/", name = "foo" | 195 // dir = "//foo/", name = "foo" |
190 // i.e. where the target name matches the top level directory. We will | 196 // 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 | 197 // always write phony rules for these even if there is another target with |
192 // the same short name. | 198 // the same short name. |
193 const std::string& dir_string = label.dir().value(); | 199 const std::string& dir_string = label.dir().value(); |
194 if (dir_string.size() == label.name().size() + 3 && // Size matches. | 200 if (dir_string.size() == label.name().size() + 3 && // Size matches. |
195 dir_string[0] == '/' && dir_string[1] == '/' && // "//" at beginning. | 201 dir_string[0] == '/' && dir_string[1] == '/' && // "//" at beginning. |
196 dir_string[dir_string.size() - 1] == '/' && // "/" at end. | 202 dir_string[dir_string.size() - 1] == '/' && // "/" at end. |
197 dir_string.compare(2, label.name().size(), label.name()) == 0) | 203 dir_string.compare(2, label.name().size(), label.name()) == 0) |
198 toplevel_targets.push_back(target); | 204 toplevel_targets.push_back(target); |
199 } | 205 } |
200 | 206 |
201 for (size_t i = 0; i < default_toolchain_targets_.size(); i++) { | 207 for (size_t i = 0; i < default_toolchain_targets_.size(); i++) { |
202 const Target* target = default_toolchain_targets_[i]; | 208 const Target* target = default_toolchain_targets_[i]; |
203 const Label& label = target->label(); | 209 const Label& label = target->label(); |
204 OutputFile target_file(target->dependency_output_file()); | 210 OutputFile target_file(target->dependency_output_file()); |
205 // The output files may have leading "./" so normalize those away. | 211 // The output files may have leading "./" so normalize those away. |
206 NormalizePath(&target_file.value()); | 212 NormalizePath(&target_file.value()); |
| 213 if (!target_files.insert(target_file.value()).second) { |
| 214 *err = Err(Location(), "Duplicate rules for " + target_file.value()); |
| 215 return false; |
| 216 } |
207 | 217 |
208 // Write the long name "foo/bar:baz" for the target "//foo/bar:baz". | 218 // Write the long name "foo/bar:baz" for the target "//foo/bar:baz". |
209 std::string long_name = label.GetUserVisibleName(false); | 219 std::string long_name = label.GetUserVisibleName(false); |
210 base::TrimString(long_name, "/", &long_name); | 220 base::TrimString(long_name, "/", &long_name); |
211 WritePhonyRule(target, target_file, long_name); | 221 WritePhonyRule(target, target_file, long_name); |
212 | 222 |
213 // Write the directory name with no target name if they match | 223 // Write the directory name with no target name if they match |
214 // (e.g. "//foo/bar:bar" -> "foo/bar"). | 224 // (e.g. "//foo/bar:bar" -> "foo/bar"). |
215 if (FindLastDirComponent(label.dir()) == label.name()) { | 225 if (FindLastDirComponent(label.dir()) == label.name()) { |
216 std::string medium_name = DirectoryWithNoLastSlash(label.dir()); | 226 std::string medium_name = DirectoryWithNoLastSlash(label.dir()); |
(...skipping 20 matching lines...) Expand all Loading... |
237 const Target* target = toplevel_targets[i]; | 247 const Target* target = toplevel_targets[i]; |
238 WritePhonyRule(target, target->dependency_output_file(), | 248 WritePhonyRule(target, target->dependency_output_file(), |
239 target->label().name()); | 249 target->label().name()); |
240 } | 250 } |
241 } | 251 } |
242 | 252 |
243 if (!all_rules.empty()) { | 253 if (!all_rules.empty()) { |
244 out_ << "\nbuild all: phony " << all_rules << std::endl; | 254 out_ << "\nbuild all: phony " << all_rules << std::endl; |
245 out_ << "default all" << std::endl; | 255 out_ << "default all" << std::endl; |
246 } | 256 } |
| 257 return true; |
247 } | 258 } |
248 | 259 |
249 void NinjaBuildWriter::WritePhonyRule(const Target* target, | 260 void NinjaBuildWriter::WritePhonyRule(const Target* target, |
250 const OutputFile& target_file, | 261 const OutputFile& target_file, |
251 const std::string& phony_name) { | 262 const std::string& phony_name) { |
252 if (target_file.value() == phony_name) | 263 if (target_file.value() == phony_name) |
253 return; // No need for a phony rule. | 264 return; // No need for a phony rule. |
254 | 265 |
255 EscapeOptions ninja_escape; | 266 EscapeOptions ninja_escape; |
256 ninja_escape.mode = ESCAPE_NINJA; | 267 ninja_escape.mode = ESCAPE_NINJA; |
257 | 268 |
258 // Escape for special chars Ninja will handle. | 269 // Escape for special chars Ninja will handle. |
259 std::string escaped = EscapeString(phony_name, ninja_escape, NULL); | 270 std::string escaped = EscapeString(phony_name, ninja_escape, NULL); |
260 | 271 |
261 out_ << "build " << escaped << ": phony "; | 272 out_ << "build " << escaped << ": phony "; |
262 path_output_.WriteFile(out_, target_file); | 273 path_output_.WriteFile(out_, target_file); |
263 out_ << std::endl; | 274 out_ << std::endl; |
264 } | 275 } |
OLD | NEW |