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 <stddef.h> | 7 #include <stddef.h> |
8 | 8 |
9 #include <fstream> | 9 #include <fstream> |
10 #include <map> | 10 #include <map> |
11 #include <sstream> | |
11 | 12 |
12 #include "base/command_line.h" | 13 #include "base/command_line.h" |
13 #include "base/files/file_util.h" | 14 #include "base/files/file_util.h" |
14 #include "base/path_service.h" | 15 #include "base/path_service.h" |
15 #include "base/process/process_handle.h" | 16 #include "base/process/process_handle.h" |
16 #include "base/strings/string_util.h" | 17 #include "base/strings/string_util.h" |
17 #include "base/strings/utf_string_conversions.h" | 18 #include "base/strings/utf_string_conversions.h" |
18 #include "build/build_config.h" | 19 #include "build/build_config.h" |
19 #include "tools/gn/build_settings.h" | 20 #include "tools/gn/build_settings.h" |
20 #include "tools/gn/err.h" | 21 #include "tools/gn/err.h" |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
72 } | 73 } |
73 } | 74 } |
74 | 75 |
75 #if defined(OS_WIN) | 76 #if defined(OS_WIN) |
76 return base::WideToUTF8(cmdline.GetCommandLineString()); | 77 return base::WideToUTF8(cmdline.GetCommandLineString()); |
77 #else | 78 #else |
78 return cmdline.GetCommandLineString(); | 79 return cmdline.GetCommandLineString(); |
79 #endif | 80 #endif |
80 } | 81 } |
81 | 82 |
82 OutputFile GetTargetOutputFile(const Target* target) { | |
83 OutputFile result(target->dependency_output_file()); | |
84 | |
85 // The output files may have leading "./" so normalize those away. | |
86 NormalizePath(&result.value()); | |
87 return result; | |
88 } | |
89 | |
90 bool HasOutputIdenticalToLabel(const Target* target, | |
91 const std::string& short_name) { | |
92 if (target->output_type() != Target::ACTION && | |
93 target->output_type() != Target::ACTION_FOREACH) | |
94 return false; | |
95 | |
96 // Rather than convert all outputs to be relative to the build directory | |
97 // and then compare to the short name, convert the short name to look like a | |
98 // file in the output directory since this is only one conversion. | |
99 SourceFile short_name_as_source_file( | |
100 target->settings()->build_settings()->build_dir().value() + short_name); | |
101 | |
102 std::vector<SourceFile> outputs_as_source; | |
103 target->action_values().GetOutputsAsSourceFiles(target, &outputs_as_source); | |
104 for (const SourceFile& output_as_source : outputs_as_source) { | |
105 if (output_as_source == short_name_as_source_file) | |
106 return true; | |
107 } | |
108 return false; | |
109 } | |
110 | |
111 // Given an output that appears more than once, generates an error message | 83 // Given an output that appears more than once, generates an error message |
112 // that describes the problem and which targets generate it. | 84 // that describes the problem and which targets generate it. |
113 Err GetDuplicateOutputError(const std::vector<const Target*>& all_targets, | 85 Err GetDuplicateOutputError(const std::vector<const Target*>& all_targets, |
114 const OutputFile& bad_output) { | 86 const OutputFile& bad_output) { |
115 std::vector<const Target*> matches; | 87 std::vector<const Target*> matches; |
116 for (const Target* target : all_targets) { | 88 for (const Target* target : all_targets) { |
117 for (const auto& output : target->computed_outputs()) { | 89 for (const auto& output : target->computed_outputs()) { |
118 if (output == bad_output) { | 90 if (output == bad_output) { |
119 matches.push_back(target); | 91 matches.push_back(target); |
120 break; | 92 break; |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
171 | 143 |
172 // static | 144 // static |
173 bool NinjaBuildWriter::RunAndWriteFile( | 145 bool NinjaBuildWriter::RunAndWriteFile( |
174 const BuildSettings* build_settings, | 146 const BuildSettings* build_settings, |
175 const std::vector<const Settings*>& all_settings, | 147 const std::vector<const Settings*>& all_settings, |
176 const Toolchain* default_toolchain, | 148 const Toolchain* default_toolchain, |
177 const std::vector<const Target*>& default_toolchain_targets, | 149 const std::vector<const Target*>& default_toolchain_targets, |
178 Err* err) { | 150 Err* err) { |
179 ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, "build.ninja"); | 151 ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, "build.ninja"); |
180 | 152 |
181 base::FilePath ninja_file(build_settings->GetFullPath( | 153 std::stringstream file; |
182 SourceFile(build_settings->build_dir().value() + "build.ninja"))); | 154 std::stringstream depfile; |
183 base::CreateDirectory(ninja_file.DirName()); | |
184 | |
185 std::ofstream file; | |
186 file.open(FilePathToUTF8(ninja_file).c_str(), | |
187 std::ios_base::out | std::ios_base::binary); | |
188 if (file.fail()) { | |
189 *err = Err(Location(), "Couldn't open build.ninja for writing"); | |
190 return false; | |
191 } | |
192 | |
193 std::ofstream depfile; | |
194 depfile.open((FilePathToUTF8(ninja_file) + ".d").c_str(), | |
195 std::ios_base::out | std::ios_base::binary); | |
196 if (depfile.fail()) { | |
197 *err = Err(Location(), "Couldn't open depfile for writing"); | |
198 return false; | |
199 } | |
200 | |
201 NinjaBuildWriter gen(build_settings, all_settings, default_toolchain, | 155 NinjaBuildWriter gen(build_settings, all_settings, default_toolchain, |
202 default_toolchain_targets, file, depfile); | 156 default_toolchain_targets, file, depfile); |
203 return gen.Run(err); | 157 if (!gen.Run(err)) |
158 return false; | |
159 | |
160 base::FilePath ninja_file_name(build_settings->GetFullPath( | |
161 SourceFile(build_settings->build_dir().value() + "build.ninja"))); | |
162 base::FilePath dep_file_name(build_settings->GetFullPath( | |
163 SourceFile(build_settings->build_dir().value() + "build.ninja.d"))); | |
164 base::CreateDirectory(ninja_file_name.DirName()); | |
165 | |
166 if (!WriteFileIfChanged(ninja_file_name, file.str(), err) || | |
167 !WriteFileIfChanged(dep_file_name, depfile.str(), err)) | |
168 return false; | |
169 | |
170 return true; | |
204 } | 171 } |
205 | 172 |
206 void NinjaBuildWriter::WriteNinjaRules() { | 173 void NinjaBuildWriter::WriteNinjaRules() { |
207 out_ << "rule gn\n"; | 174 out_ << "rule gn\n"; |
208 out_ << " command = " << GetSelfInvocationCommand(build_settings_) << "\n"; | 175 out_ << " command = " << GetSelfInvocationCommand(build_settings_) << "\n"; |
209 out_ << " description = Regenerating ninja files\n\n"; | 176 out_ << " description = Regenerating ninja files\n\n"; |
210 | 177 |
211 // This rule will regenerate the ninja files when any input file has changed. | 178 // This rule will regenerate the ninja files when any input file has changed. |
212 out_ << "build build.ninja: gn\n" | 179 out_ << "build build.ninja: gn\n" |
213 << " generator = 1\n" | 180 << " generator = 1\n" |
(...skipping 29 matching lines...) Expand all Loading... | |
243 | 210 |
244 void NinjaBuildWriter::WriteSubninjas() { | 211 void NinjaBuildWriter::WriteSubninjas() { |
245 for (const auto& elem : all_settings_) { | 212 for (const auto& elem : all_settings_) { |
246 out_ << "subninja "; | 213 out_ << "subninja "; |
247 path_output_.WriteFile(out_, GetNinjaFileForToolchain(elem)); | 214 path_output_.WriteFile(out_, GetNinjaFileForToolchain(elem)); |
248 out_ << std::endl; | 215 out_ << std::endl; |
249 } | 216 } |
250 out_ << std::endl; | 217 out_ << std::endl; |
251 } | 218 } |
252 | 219 |
220 namespace { | |
scottmg
2016/05/09 23:31:53
Unusual to have another one here instead of puttin
| |
221 | |
222 struct Counts { | |
223 Counts() : count(0), last_seen(nullptr) {} | |
224 | |
225 // Number of targets of this type. | |
226 int count; | |
227 | |
228 // The last one we encountered. | |
229 const Target* last_seen; | |
230 }; | |
231 | |
232 } // namesoace | |
scottmg
2016/05/09 23:31:52
"namespace"
| |
233 | |
253 bool NinjaBuildWriter::WritePhonyAndAllRules(Err* err) { | 234 bool NinjaBuildWriter::WritePhonyAndAllRules(Err* err) { |
254 std::string all_rules; | |
255 | |
256 // Track rules as we generate them so we don't accidentally write a phony | 235 // Track rules as we generate them so we don't accidentally write a phony |
257 // rule that collides with something else. | 236 // rule that collides with something else. |
258 // GN internally generates an "all" target, so don't duplicate it. | 237 // GN internally generates an "all" target, so don't duplicate it. |
259 std::set<std::string> written_rules; | 238 base::hash_set<std::string> written_rules; |
260 written_rules.insert("all"); | 239 written_rules.insert("all"); |
261 | 240 |
262 // Write phony rules for all uniquely-named targets in the default toolchain. | 241 // Set if we encounter a target named "//:default". |
263 // Don't do other toolchains or we'll get naming conflicts, and if the name | 242 bool default_target_exists = false; |
264 // isn't unique, also skip it. The exception is for the toplevel targets | 243 |
265 // which we also find. | 244 // Targets in the root build file. |
266 std::map<std::string, int> small_name_count; | |
267 std::map<std::string, int> exe_count; | |
268 std::vector<const Target*> toplevel_targets; | 245 std::vector<const Target*> toplevel_targets; |
269 base::hash_set<std::string> target_files; | 246 |
270 for (const auto& target : default_toolchain_targets_) { | 247 // Targets with names matching their toplevel directories. For example |
248 // "//foo:foo". Expect this is the naming scheme for "big components." | |
249 std::vector<const Target*> toplevel_dir_targets; | |
250 | |
251 // Tracks the number of each target with the given short name, as well | |
252 // as the short names of executables (which will be a subset of short_names). | |
253 std::map<std::string, Counts> short_names; | |
254 std::map<std::string, Counts> exes; | |
255 | |
256 for (const Target* target : default_toolchain_targets_) { | |
271 const Label& label = target->label(); | 257 const Label& label = target->label(); |
272 small_name_count[label.name()]++; | 258 const std::string& short_name = label.name(); |
273 | 259 |
274 // Look for targets with a name of the form | 260 if (label.dir().value() == "//" && label.name() == "default") |
275 // dir = "//foo/", name = "foo" | 261 default_target_exists = true; |
276 // i.e. where the target name matches the top level directory. We will | 262 |
277 // always write phony rules for these even if there is another target with | 263 // Count the number of targets with the given short name. |
278 // the same short name. | 264 Counts& short_names_counts = short_names[short_name]; |
265 short_names_counts.count++; | |
266 short_names_counts.last_seen = target; | |
267 | |
268 // Count executables with the given short name. | |
269 if (target->output_type() == Target::EXECUTABLE) { | |
270 Counts& exes_counts = exes[short_name]; | |
271 exes_counts.count++; | |
272 exes_counts.last_seen = target; | |
273 } | |
274 | |
275 // Find targets in "important" directories. | |
279 const std::string& dir_string = label.dir().value(); | 276 const std::string& dir_string = label.dir().value(); |
280 if (dir_string.size() == label.name().size() + 3 && // Size matches. | 277 if (dir_string.size() == 2 && |
278 dir_string[0] == '/' && dir_string[1] == '/') { | |
279 toplevel_targets.push_back(target); | |
280 } else if ( | |
281 dir_string.size() == label.name().size() + 3 && // Size matches. | |
281 dir_string[0] == '/' && dir_string[1] == '/' && // "//" at beginning. | 282 dir_string[0] == '/' && dir_string[1] == '/' && // "//" at beginning. |
282 dir_string[dir_string.size() - 1] == '/' && // "/" at end. | 283 dir_string[dir_string.size() - 1] == '/' && // "/" at end. |
283 dir_string.compare(2, label.name().size(), label.name()) == 0) | 284 dir_string.compare(2, label.name().size(), label.name()) == 0) { |
284 toplevel_targets.push_back(target); | 285 toplevel_dir_targets.push_back(target); |
286 } | |
285 | 287 |
286 // Look for executables; later we will generate phony rules for them | 288 // Add the output files from each target to the written rules so that |
287 // even if there are non-executable targets with the same name. | 289 // we don't write phony rules that collide with anything generated by the |
288 if (target->output_type() == Target::EXECUTABLE) | 290 // build. |
289 exe_count[label.name()]++; | 291 // |
290 | 292 // If at this point there is a collision (no phony rules have been |
291 // Add the files to the list of generated targets so we don't write phony | 293 // generated yet), two targets make the same output so throw an error. |
292 // rules that collide. | |
293 std::string target_file(target->dependency_output_file().value()); | |
294 NormalizePath(&target_file); | |
295 written_rules.insert(target_file); | |
296 } | |
297 | |
298 for (const auto& target : default_toolchain_targets_) { | |
299 const Label& label = target->label(); | |
300 for (const auto& output : target->computed_outputs()) { | 294 for (const auto& output : target->computed_outputs()) { |
301 if (!target_files.insert(output.value()).second) { | 295 // Need to normalize because many toolchain outputs will be preceeded |
296 // with "./". | |
297 std::string output_string(output.value()); | |
298 NormalizePath(&output_string); | |
299 if (!written_rules.insert(output_string).second) { | |
302 *err = GetDuplicateOutputError(default_toolchain_targets_, output); | 300 *err = GetDuplicateOutputError(default_toolchain_targets_, output); |
303 return false; | 301 return false; |
304 } | 302 } |
305 } | 303 } |
304 } | |
306 | 305 |
307 OutputFile target_file = GetTargetOutputFile(target); | 306 // First prefer the short names of toplevel targets. |
307 for (const Target* target : toplevel_targets) { | |
308 if (written_rules.insert(target->label().name()).second) | |
309 WritePhonyRule(target, target->label().name()); | |
310 } | |
311 | |
312 // Next prefer short names of toplevel dir targets. | |
313 for (const Target* target : toplevel_dir_targets) { | |
314 if (written_rules.insert(target->label().name()).second) | |
315 WritePhonyRule(target, target->label().name()); | |
316 } | |
317 | |
318 // Write out the names labels of executables. Many toolchains will produce | |
319 // executables in the root build directory with no extensions, so the names | |
320 // will already exist and this will be a no-op. But on Windows such programs | |
321 // will have extensions, and executables may override the output directory to | |
322 // go into some other place. | |
323 // | |
324 // Putting this after the "toplevel" rules above also means that you can | |
325 // steal the short name from an executable by outputting the executable to | |
326 // a different directory or using a different output name, and writing a | |
327 // toplevel build rule. | |
328 for (const auto& pair : exes) { | |
329 const Counts& counts = pair.second; | |
330 const std::string& short_name = counts.last_seen->label().name(); | |
331 if (counts.count == 1 && written_rules.insert(short_name).second) | |
332 WritePhonyRule(counts.last_seen, short_name); | |
333 } | |
334 | |
335 // Write short names when those names are unique and not already taken. | |
336 for (const auto& pair : short_names) { | |
337 const Counts& counts = pair.second; | |
338 const std::string& short_name = counts.last_seen->label().name(); | |
339 if (counts.count == 1 && written_rules.insert(short_name).second) | |
340 WritePhonyRule(counts.last_seen, short_name); | |
341 } | |
342 | |
343 // Write the label variants of the target name. | |
344 for (const Target* target : default_toolchain_targets_) { | |
345 const Label& label = target->label(); | |
346 | |
308 // Write the long name "foo/bar:baz" for the target "//foo/bar:baz". | 347 // Write the long name "foo/bar:baz" for the target "//foo/bar:baz". |
309 std::string long_name = label.GetUserVisibleName(false); | 348 std::string long_name = label.GetUserVisibleName(false); |
310 base::TrimString(long_name, "/", &long_name); | 349 base::TrimString(long_name, "/", &long_name); |
311 WritePhonyRule(target, target_file, long_name, &written_rules); | 350 if (written_rules.insert(long_name).second) |
351 WritePhonyRule(target, long_name); | |
312 | 352 |
313 // Write the directory name with no target name if they match | 353 // Write the directory name with no target name if they match |
314 // (e.g. "//foo/bar:bar" -> "foo/bar"). | 354 // (e.g. "//foo/bar:bar" -> "foo/bar"). |
315 if (FindLastDirComponent(label.dir()) == label.name()) { | 355 if (FindLastDirComponent(label.dir()) == label.name()) { |
316 std::string medium_name = DirectoryWithNoLastSlash(label.dir()); | 356 std::string medium_name = DirectoryWithNoLastSlash(label.dir()); |
317 base::TrimString(medium_name, "/", &medium_name); | 357 base::TrimString(medium_name, "/", &medium_name); |
318 // That may have generated a name the same as the short name of the | 358 // That may have generated a name the same as the short name of the |
319 // target which we already wrote. | 359 // target which we already wrote. |
320 if (medium_name != label.name()) | 360 if (medium_name != label.name() && |
321 WritePhonyRule(target, target_file, medium_name, &written_rules); | 361 written_rules.insert(medium_name).second) |
362 WritePhonyRule(target, medium_name); | |
322 } | 363 } |
323 | 364 |
324 // Write short names for ones which are either completely unique or there | 365 // Write the short name if no other target shares that short name and |
325 // at least only one of them in the default toolchain that is an exe. | 366 // non of the higher-priority rules above claimed it. |
scottmg
2016/05/09 23:31:53
non->none
| |
326 if (small_name_count[label.name()] == 1 || | 367 if (short_names[label.name()].count == 1 && |
327 (target->output_type() == Target::EXECUTABLE && | 368 written_rules.insert(label.name()).second) |
328 exe_count[label.name()] == 1)) { | 369 WritePhonyRule(target, label.name()); |
329 // It's reasonable to generate output files in the root build directory | |
330 // with the same name as the target. Don't generate phony rules for | |
331 // these cases. | |
332 // | |
333 // All of this does not do the general checking of all target's outputs | |
334 // which may theoretically collide. But it's not very reasonable for | |
335 // a script target named "foo" to generate a file named "bar" with no | |
336 // extension in the root build directory while another target is named | |
337 // "bar". If this does occur, the user is likely to be confused when | |
338 // building "bar" that is builds foo anyway, so you probably just | |
339 // shouldn't do that. | |
340 // | |
341 // We should fix this however, and build up all generated script outputs | |
342 // and check everything against that. There are other edge cases that the | |
343 // current phony rule generator doesn't check. We may need to make a big | |
344 // set of every possible generated file in the build for this purpose. | |
345 if (!HasOutputIdenticalToLabel(target, label.name())) | |
346 WritePhonyRule(target, target_file, label.name(), &written_rules); | |
347 } | |
348 | |
349 if (!all_rules.empty()) | |
350 all_rules.append(" $\n "); | |
351 all_rules.append(target_file.value()); | |
352 } | 370 } |
353 | 371 |
354 // Pick up phony rules for the toplevel targets with non-unique names (which | 372 // Write the autogenerated "all" rule. |
355 // would have been skipped in the above loop). | 373 if (!default_toolchain_targets_.empty()) { |
356 for (const auto& toplevel_target : toplevel_targets) { | 374 out_ << "\nbuild all: phony"; |
357 if (small_name_count[toplevel_target->label().name()] > 1) { | 375 |
358 WritePhonyRule(toplevel_target, toplevel_target->dependency_output_file(), | 376 EscapeOptions ninja_escape; |
359 toplevel_target->label().name(), &written_rules); | 377 ninja_escape.mode = ESCAPE_NINJA; |
378 for (const Target* target : default_toolchain_targets_) { | |
379 out_ << (" $\n "); | |
scottmg
2016/05/09 23:31:53
no ()
| |
380 path_output_.WriteFile(out_, target->dependency_output_file()); | |
360 } | 381 } |
361 } | 382 } |
383 out_ << std::endl; | |
362 | 384 |
363 // Figure out if the BUILD file wants to declare a custom "default" | 385 if (default_target_exists) |
364 // target (rather than building 'all' by default). By convention | 386 out_ << "\ndefault default" << std::endl; |
365 // we use group("default") but it doesn't have to be a group. | 387 else if (!default_toolchain_targets_.empty()) |
366 bool default_target_exists = false; | 388 out_ << "\ndefault all" << std::endl; |
367 for (const auto& target : default_toolchain_targets_) { | |
368 const Label& label = target->label(); | |
369 if (label.dir().value() == "//" && label.name() == "default") | |
370 default_target_exists = true; | |
371 } | |
372 | |
373 if (!all_rules.empty()) { | |
374 out_ << "\nbuild all: phony " << all_rules << std::endl; | |
375 } | |
376 | |
377 if (default_target_exists) { | |
378 out_ << "default default" << std::endl; | |
379 } else if (!all_rules.empty()) { | |
380 out_ << "default all" << std::endl; | |
381 } | |
382 | 389 |
383 return true; | 390 return true; |
384 } | 391 } |
385 | 392 |
386 void NinjaBuildWriter::WritePhonyRule(const Target* target, | 393 void NinjaBuildWriter::WritePhonyRule(const Target* target, |
387 const OutputFile& target_file, | 394 const std::string& phony_name) { |
388 const std::string& phony_name, | |
389 std::set<std::string>* written_rules) { | |
390 if (target_file.value() == phony_name) | |
391 return; // No need for a phony rule. | |
392 | |
393 if (written_rules->find(phony_name) != written_rules->end()) | |
394 return; // Already exists. | |
395 written_rules->insert(phony_name); | |
396 | |
397 EscapeOptions ninja_escape; | 395 EscapeOptions ninja_escape; |
398 ninja_escape.mode = ESCAPE_NINJA; | 396 ninja_escape.mode = ESCAPE_NINJA; |
399 | 397 |
400 // Escape for special chars Ninja will handle. | 398 // Escape for special chars Ninja will handle. |
401 std::string escaped = EscapeString(phony_name, ninja_escape, nullptr); | 399 std::string escaped = EscapeString(phony_name, ninja_escape, nullptr); |
402 | 400 |
403 out_ << "build " << escaped << ": phony "; | 401 out_ << "build " << escaped << ": phony "; |
404 path_output_.WriteFile(out_, target_file); | 402 path_output_.WriteFile(out_, target->dependency_output_file()); |
405 out_ << std::endl; | 403 out_ << std::endl; |
406 } | 404 } |
OLD | NEW |