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" |
(...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
179 out_ << "subninja "; | 179 out_ << "subninja "; |
180 path_output_.WriteFile(out_, GetNinjaFileForToolchain(elem)); | 180 path_output_.WriteFile(out_, GetNinjaFileForToolchain(elem)); |
181 out_ << std::endl; | 181 out_ << std::endl; |
182 } | 182 } |
183 out_ << std::endl; | 183 out_ << std::endl; |
184 } | 184 } |
185 | 185 |
186 bool NinjaBuildWriter::WritePhonyAndAllRules(Err* err) { | 186 bool NinjaBuildWriter::WritePhonyAndAllRules(Err* err) { |
187 std::string all_rules; | 187 std::string all_rules; |
188 | 188 |
| 189 // Track rules as we generate them so we don't accidentally write a phony |
| 190 // rule that collides with something else. |
| 191 // GN internally generates an "all" target, so don't duplicate it. |
| 192 std::set<std::string> written_rules; |
| 193 written_rules.insert("all"); |
| 194 |
189 // Write phony rules for all uniquely-named targets in the default toolchain. | 195 // Write phony rules for all uniquely-named targets in the default toolchain. |
190 // Don't do other toolchains or we'll get naming conflicts, and if the name | 196 // Don't do other toolchains or we'll get naming conflicts, and if the name |
191 // isn't unique, also skip it. The exception is for the toplevel targets | 197 // isn't unique, also skip it. The exception is for the toplevel targets |
192 // which we also find. | 198 // which we also find. |
193 std::map<std::string, int> small_name_count; | 199 std::map<std::string, int> small_name_count; |
194 std::map<std::string, int> exe_count; | 200 std::map<std::string, int> exe_count; |
195 std::vector<const Target*> toplevel_targets; | 201 std::vector<const Target*> toplevel_targets; |
196 base::hash_set<std::string> target_files; | 202 base::hash_set<std::string> target_files; |
197 for (const auto& target : default_toolchain_targets_) { | 203 for (const auto& target : default_toolchain_targets_) { |
198 const Label& label = target->label(); | 204 const Label& label = target->label(); |
199 small_name_count[label.name()]++; | 205 small_name_count[label.name()]++; |
200 | 206 |
201 // Look for targets with a name of the form | 207 // Look for targets with a name of the form |
202 // dir = "//foo/", name = "foo" | 208 // dir = "//foo/", name = "foo" |
203 // i.e. where the target name matches the top level directory. We will | 209 // i.e. where the target name matches the top level directory. We will |
204 // always write phony rules for these even if there is another target with | 210 // always write phony rules for these even if there is another target with |
205 // the same short name. | 211 // the same short name. |
206 const std::string& dir_string = label.dir().value(); | 212 const std::string& dir_string = label.dir().value(); |
207 if (dir_string.size() == label.name().size() + 3 && // Size matches. | 213 if (dir_string.size() == label.name().size() + 3 && // Size matches. |
208 dir_string[0] == '/' && dir_string[1] == '/' && // "//" at beginning. | 214 dir_string[0] == '/' && dir_string[1] == '/' && // "//" at beginning. |
209 dir_string[dir_string.size() - 1] == '/' && // "/" at end. | 215 dir_string[dir_string.size() - 1] == '/' && // "/" at end. |
210 dir_string.compare(2, label.name().size(), label.name()) == 0) | 216 dir_string.compare(2, label.name().size(), label.name()) == 0) |
211 toplevel_targets.push_back(target); | 217 toplevel_targets.push_back(target); |
212 | 218 |
213 // Look for executables; later we will generate phony rules for them | 219 // Look for executables; later we will generate phony rules for them |
214 // even if there are non-executable targets with the same name. | 220 // even if there are non-executable targets with the same name. |
215 if (target->output_type() == Target::EXECUTABLE) | 221 if (target->output_type() == Target::EXECUTABLE) |
216 exe_count[label.name()]++; | 222 exe_count[label.name()]++; |
| 223 |
| 224 // Add the files to the list of generated targets so we don't write phony |
| 225 // rules that collide. |
| 226 std::string target_file(target->dependency_output_file().value()); |
| 227 NormalizePath(&target_file); |
| 228 written_rules.insert(target_file); |
217 } | 229 } |
218 | 230 |
219 for (const auto& target : default_toolchain_targets_) { | 231 for (const auto& target : default_toolchain_targets_) { |
220 const Label& label = target->label(); | 232 const Label& label = target->label(); |
221 OutputFile target_file(target->dependency_output_file()); | 233 OutputFile target_file(target->dependency_output_file()); |
222 // The output files may have leading "./" so normalize those away. | 234 // The output files may have leading "./" so normalize those away. |
223 NormalizePath(&target_file.value()); | 235 NormalizePath(&target_file.value()); |
224 if (!target_files.insert(target_file.value()).second) { | 236 if (!target_files.insert(target_file.value()).second) { |
225 *err = Err(Location(), "Duplicate rules for " + target_file.value()); | 237 *err = Err(Location(), "Duplicate rules for " + target_file.value()); |
226 return false; | 238 return false; |
227 } | 239 } |
228 | 240 |
229 // Write the long name "foo/bar:baz" for the target "//foo/bar:baz". | 241 // Write the long name "foo/bar:baz" for the target "//foo/bar:baz". |
230 std::string long_name = label.GetUserVisibleName(false); | 242 std::string long_name = label.GetUserVisibleName(false); |
231 base::TrimString(long_name, "/", &long_name); | 243 base::TrimString(long_name, "/", &long_name); |
232 WritePhonyRule(target, target_file, long_name); | 244 WritePhonyRule(target, target_file, long_name, &written_rules); |
233 | 245 |
234 // Write the directory name with no target name if they match | 246 // Write the directory name with no target name if they match |
235 // (e.g. "//foo/bar:bar" -> "foo/bar"). | 247 // (e.g. "//foo/bar:bar" -> "foo/bar"). |
236 if (FindLastDirComponent(label.dir()) == label.name()) { | 248 if (FindLastDirComponent(label.dir()) == label.name()) { |
237 std::string medium_name = DirectoryWithNoLastSlash(label.dir()); | 249 std::string medium_name = DirectoryWithNoLastSlash(label.dir()); |
238 base::TrimString(medium_name, "/", &medium_name); | 250 base::TrimString(medium_name, "/", &medium_name); |
239 // That may have generated a name the same as the short name of the | 251 // That may have generated a name the same as the short name of the |
240 // target which we already wrote. | 252 // target which we already wrote. |
241 if (medium_name != label.name()) | 253 if (medium_name != label.name()) |
242 WritePhonyRule(target, target_file, medium_name); | 254 WritePhonyRule(target, target_file, medium_name, &written_rules); |
243 } | 255 } |
244 | 256 |
245 // Write short names for ones which are either completely unique or there | 257 // Write short names for ones which are either completely unique or there |
246 // at least only one of them in the default toolchain that is an exe. | 258 // at least only one of them in the default toolchain that is an exe. |
247 if (small_name_count[label.name()] == 1 || | 259 if (small_name_count[label.name()] == 1 || |
248 (target->output_type() == Target::EXECUTABLE && | 260 (target->output_type() == Target::EXECUTABLE && |
249 exe_count[label.name()] == 1)) { | 261 exe_count[label.name()] == 1)) { |
250 WritePhonyRule(target, target_file, label.name()); | 262 WritePhonyRule(target, target_file, label.name(), &written_rules); |
251 } | 263 } |
252 | 264 |
253 if (!all_rules.empty()) | 265 if (!all_rules.empty()) |
254 all_rules.append(" $\n "); | 266 all_rules.append(" $\n "); |
255 all_rules.append(target_file.value()); | 267 all_rules.append(target_file.value()); |
256 } | 268 } |
257 | 269 |
258 // Pick up phony rules for the toplevel targets with non-unique names (which | 270 // Pick up phony rules for the toplevel targets with non-unique names (which |
259 // would have been skipped in the above loop). | 271 // would have been skipped in the above loop). |
260 for (const auto& toplevel_target : toplevel_targets) { | 272 for (const auto& toplevel_target : toplevel_targets) { |
261 if (small_name_count[toplevel_target->label().name()] > 1) { | 273 if (small_name_count[toplevel_target->label().name()] > 1) { |
262 WritePhonyRule(toplevel_target, toplevel_target->dependency_output_file(), | 274 WritePhonyRule(toplevel_target, toplevel_target->dependency_output_file(), |
263 toplevel_target->label().name()); | 275 toplevel_target->label().name(), &written_rules); |
264 } | 276 } |
265 } | 277 } |
266 | 278 |
267 // Figure out if the BUILD file wants to declare a custom "default" | 279 // Figure out if the BUILD file wants to declare a custom "default" |
268 // target (rather than building 'all' by default). By convention | 280 // target (rather than building 'all' by default). By convention |
269 // we use group("default") but it doesn't have to be a group. | 281 // we use group("default") but it doesn't have to be a group. |
270 bool default_target_exists = false; | 282 bool default_target_exists = false; |
271 for (const auto& target : default_toolchain_targets_) { | 283 for (const auto& target : default_toolchain_targets_) { |
272 const Label& label = target->label(); | 284 const Label& label = target->label(); |
273 if (label.dir().value() == "//" && label.name() == "default") | 285 if (label.dir().value() == "//" && label.name() == "default") |
274 default_target_exists = true; | 286 default_target_exists = true; |
275 } | 287 } |
276 | 288 |
277 if (!all_rules.empty()) { | 289 if (!all_rules.empty()) { |
278 out_ << "\nbuild all: phony " << all_rules << std::endl; | 290 out_ << "\nbuild all: phony " << all_rules << std::endl; |
279 } | 291 } |
280 | 292 |
281 if (default_target_exists) { | 293 if (default_target_exists) { |
282 out_ << "default default" << std::endl; | 294 out_ << "default default" << std::endl; |
283 } else if (!all_rules.empty()) { | 295 } else if (!all_rules.empty()) { |
284 out_ << "default all" << std::endl; | 296 out_ << "default all" << std::endl; |
285 } | 297 } |
286 | 298 |
287 return true; | 299 return true; |
288 } | 300 } |
289 | 301 |
290 void NinjaBuildWriter::WritePhonyRule(const Target* target, | 302 void NinjaBuildWriter::WritePhonyRule(const Target* target, |
291 const OutputFile& target_file, | 303 const OutputFile& target_file, |
292 const std::string& phony_name) { | 304 const std::string& phony_name, |
| 305 std::set<std::string>* written_rules) { |
293 if (target_file.value() == phony_name) | 306 if (target_file.value() == phony_name) |
294 return; // No need for a phony rule. | 307 return; // No need for a phony rule. |
295 | 308 |
| 309 if (written_rules->find(phony_name) != written_rules->end()) |
| 310 return; // Already exists. |
| 311 written_rules->insert(phony_name); |
| 312 |
296 EscapeOptions ninja_escape; | 313 EscapeOptions ninja_escape; |
297 ninja_escape.mode = ESCAPE_NINJA; | 314 ninja_escape.mode = ESCAPE_NINJA; |
298 | 315 |
299 // Escape for special chars Ninja will handle. | 316 // Escape for special chars Ninja will handle. |
300 std::string escaped = EscapeString(phony_name, ninja_escape, nullptr); | 317 std::string escaped = EscapeString(phony_name, ninja_escape, nullptr); |
301 | 318 |
302 out_ << "build " << escaped << ": phony "; | 319 out_ << "build " << escaped << ": phony "; |
303 path_output_.WriteFile(out_, target_file); | 320 path_output_.WriteFile(out_, target_file); |
304 out_ << std::endl; | 321 out_ << std::endl; |
305 } | 322 } |
OLD | NEW |