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
3 // found in the LICENSE file.
4
5 #include "gn/ninja_build_writer.h"
6
7 #include <stddef.h>
8
9 #include <fstream>
10 #include <map>
11 #include <set>
12 #include <sstream>
13
14 #include "base/command_line.h"
15 #include "base/files/file_util.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "gn/build_settings.h"
19 #include "gn/builder.h"
20 #include "gn/err.h"
21 #include "gn/escape.h"
22 #include "gn/filesystem_utils.h"
23 #include "gn/input_file_manager.h"
24 #include "gn/loader.h"
25 #include "gn/ninja_utils.h"
26 #include "gn/pool.h"
27 #include "gn/scheduler.h"
28 #include "gn/string_atom.h"
29 #include "gn/switches.h"
30 #include "gn/target.h"
31 #include "gn/trace.h"
32 #include "util/atomic_write.h"
33 #include "util/build_config.h"
34 #include "util/exe_path.h"
35
36 #if defined(OS_WIN)
37 #include <windows.h>
38 #endif
39
40 namespace {
41
42 struct Counts {
Counts__anon91cdaad10111::Counts43 Counts() : count(0), last_seen(nullptr) {}
44
45 // Number of targets of this type.
46 int count;
47
48 // The last one we encountered.
49 const Target* last_seen;
50 };
51
52 } // namespace
53
GetSelfInvocationCommandLine(const BuildSettings * build_settings)54 base::CommandLine GetSelfInvocationCommandLine(
55 const BuildSettings* build_settings) {
56 const base::FilePath build_path =
57 build_settings->build_dir().Resolve(build_settings->root_path());
58
59 base::FilePath exe_path = GetExePath();
60 if (build_path.IsAbsolute())
61 exe_path = MakeAbsoluteFilePathRelativeIfPossible(build_path, exe_path);
62
63 base::CommandLine cmdline(exe_path.NormalizePathSeparatorsTo('/'));
64
65 // Use "." for the directory to generate. When Ninja runs the command it
66 // will have the build directory as the current one. Coding it explicitly
67 // will cause everything to get confused if the user renames the directory.
68 cmdline.AppendArg("gen");
69 cmdline.AppendArg(".");
70
71 base::FilePath root_path = build_settings->root_path();
72 if (build_path.IsAbsolute())
73 root_path = MakeAbsoluteFilePathRelativeIfPossible(build_path, root_path);
74
75 cmdline.AppendSwitchPath(std::string("--") + switches::kRoot,
76 root_path.NormalizePathSeparatorsTo('/'));
77 // Successful automatic invocations shouldn't print output.
78 cmdline.AppendSwitch(std::string("-") + switches::kQuiet);
79
80 EscapeOptions escape_shell;
81 escape_shell.mode = ESCAPE_NINJA_COMMAND;
82 #if defined(OS_WIN)
83 // The command line code quoting varies by platform. We have one string,
84 // possibly with spaces, that we want to quote. The Windows command line
85 // quotes again, so we don't want quoting. The Posix one doesn't.
86 escape_shell.inhibit_quoting = true;
87 #endif
88
89 // If both --root and --dotfile are passed, make sure the --dotfile is
90 // made relative to the build dir here.
91 base::FilePath dotfile_path = build_settings->dotfile_name();
92 if (!dotfile_path.empty()) {
93 if (build_path.IsAbsolute()) {
94 dotfile_path =
95 MakeAbsoluteFilePathRelativeIfPossible(build_path, dotfile_path);
96 }
97 cmdline.AppendSwitchPath(std::string("--") + switches::kDotfile,
98 dotfile_path.NormalizePathSeparatorsTo('/'));
99 }
100
101 const base::CommandLine& our_cmdline =
102 *base::CommandLine::ForCurrentProcess();
103 const base::CommandLine::SwitchMap& switches = our_cmdline.GetSwitches();
104 for (base::CommandLine::SwitchMap::const_iterator i = switches.begin();
105 i != switches.end(); ++i) {
106 // Only write arguments we haven't already written. Always skip "args"
107 // since those will have been written to the file and will be used
108 // implicitly in the future. Keeping --args would mean changes to the file
109 // would be ignored.
110 if (i->first != switches::kQuiet && i->first != switches::kRoot &&
111 i->first != switches::kDotfile && i->first != switches::kArgs) {
112 std::string escaped_value =
113 EscapeString(FilePathToUTF8(i->second), escape_shell, nullptr);
114 cmdline.AppendSwitch(i->first, escaped_value);
115 }
116 }
117
118 // Add the regeneration switch if not already present. This is so that when
119 // the regeneration is invoked by ninja, the gen command is aware that it is a
120 // regeneration invocation and not an user invocation. This allows the gen
121 // command to elide ninja post processing steps that ninja will perform
122 // itself.
123 if (!cmdline.HasSwitch(switches::kRegeneration)) {
124 cmdline.AppendSwitch(switches::kRegeneration);
125 }
126
127 return cmdline;
128 }
129
130 namespace {
131
GetSelfInvocationCommand(const BuildSettings * build_settings)132 std::string GetSelfInvocationCommand(const BuildSettings* build_settings) {
133 base::CommandLine cmdline = GetSelfInvocationCommandLine(build_settings);
134 #if defined(OS_WIN)
135 return base::UTF16ToUTF8(cmdline.GetCommandLineString());
136 #else
137 return cmdline.GetCommandLineString();
138 #endif
139 }
140
141 // Given an output that appears more than once, generates an error message
142 // that describes the problem and which targets generate it.
GetDuplicateOutputError(const std::vector<const Target * > & all_targets,const OutputFile & bad_output)143 Err GetDuplicateOutputError(const std::vector<const Target*>& all_targets,
144 const OutputFile& bad_output) {
145 std::vector<const Target*> matches;
146 for (const Target* target : all_targets) {
147 for (const auto& output : target->computed_outputs()) {
148 if (output == bad_output) {
149 matches.push_back(target);
150 break;
151 }
152 }
153 }
154
155 // There should always be at least two targets generating this file for this
156 // function to be called in the first place.
157 DCHECK(matches.size() >= 2);
158 std::string matches_string;
159 for (const Target* target : matches)
160 matches_string += " " + target->label().GetUserVisibleName(true) + "\n";
161
162 Err result(matches[0]->defined_from(), "Duplicate output file.",
163 "Two or more targets generate the same output:\n " +
164 bad_output.value() +
165 "\n\n"
166 "This is can often be fixed by changing one of the target "
167 "names, or by \n"
168 "setting an output_name on one of them.\n"
169 "\nCollisions:\n" +
170 matches_string);
171 for (size_t i = 1; i < matches.size(); i++)
172 result.AppendSubErr(Err(matches[i]->defined_from(), "Collision."));
173 return result;
174 }
175
176 // Given two toolchains with the same name, generates an error message
177 // that describes the problem.
GetDuplicateToolchainError(const SourceFile & source_file,const Toolchain * previous_toolchain,const Toolchain * toolchain)178 Err GetDuplicateToolchainError(const SourceFile& source_file,
179 const Toolchain* previous_toolchain,
180 const Toolchain* toolchain) {
181 Err result(
182 toolchain->defined_from(), "Duplicate toolchain.",
183 "Two or more toolchains write to the same directory:\n " +
184 source_file.GetDir().value() +
185 "\n\n"
186 "This can be fixed by making sure that distinct toolchains have\n"
187 "distinct names.\n");
188 result.AppendSubErr(
189 Err(previous_toolchain->defined_from(), "Previous toolchain."));
190 return result;
191 }
192
193 } // namespace
194
NinjaBuildWriter(const BuildSettings * build_settings,const std::unordered_map<const Settings *,const Toolchain * > & used_toolchains,const std::vector<const Target * > & all_targets,const Toolchain * default_toolchain,const std::vector<const Target * > & default_toolchain_targets,std::ostream & out,std::ostream & dep_out)195 NinjaBuildWriter::NinjaBuildWriter(
196 const BuildSettings* build_settings,
197 const std::unordered_map<const Settings*, const Toolchain*>&
198 used_toolchains,
199 const std::vector<const Target*>& all_targets,
200 const Toolchain* default_toolchain,
201 const std::vector<const Target*>& default_toolchain_targets,
202 std::ostream& out,
203 std::ostream& dep_out)
204 : build_settings_(build_settings),
205 used_toolchains_(used_toolchains),
206 all_targets_(all_targets),
207 default_toolchain_(default_toolchain),
208 default_toolchain_targets_(default_toolchain_targets),
209 out_(out),
210 dep_out_(dep_out),
211 path_output_(build_settings->build_dir(),
212 build_settings->root_path_utf8(),
213 ESCAPE_NINJA) {}
214
215 NinjaBuildWriter::~NinjaBuildWriter() = default;
216
Run(Err * err)217 bool NinjaBuildWriter::Run(Err* err) {
218 WriteNinjaRules();
219 WriteAllPools();
220 return WriteSubninjas(err) && WritePhonyAndAllRules(err);
221 }
222
223 // static
RunAndWriteFile(const BuildSettings * build_settings,const Builder & builder,Err * err)224 bool NinjaBuildWriter::RunAndWriteFile(const BuildSettings* build_settings,
225 const Builder& builder,
226 Err* err) {
227 ScopedTrace trace(TraceItem::TRACE_FILE_WRITE_NINJA, "build.ninja");
228
229 std::vector<const Target*> all_targets = builder.GetAllResolvedTargets();
230 std::unordered_map<const Settings*, const Toolchain*> used_toolchains;
231
232 // Find the default toolchain info.
233 Label default_toolchain_label = builder.loader()->GetDefaultToolchain();
234 const Settings* default_toolchain_settings =
235 builder.loader()->GetToolchainSettings(default_toolchain_label);
236 const Toolchain* default_toolchain =
237 builder.GetToolchain(default_toolchain_label);
238
239 // Most targets will be in the default toolchain. Add it at the beginning and
240 // skip adding it to the list every time in the loop.
241 used_toolchains[default_toolchain_settings] = default_toolchain;
242
243 std::vector<const Target*> default_toolchain_targets;
244 default_toolchain_targets.reserve(all_targets.size());
245 for (const Target* target : all_targets) {
246 if (target->settings() == default_toolchain_settings) {
247 default_toolchain_targets.push_back(target);
248 // The default toolchain will already have been added to the used
249 // settings array.
250 } else if (used_toolchains.find(target->settings()) ==
251 used_toolchains.end()) {
252 used_toolchains[target->settings()] =
253 builder.GetToolchain(target->settings()->toolchain_label());
254 }
255 }
256
257 std::stringstream file;
258 std::stringstream depfile;
259 NinjaBuildWriter gen(build_settings, used_toolchains, all_targets,
260 default_toolchain, default_toolchain_targets, file,
261 depfile);
262 if (!gen.Run(err))
263 return false;
264
265 // Unconditionally write the build.ninja. Ninja's build-out-of-date
266 // checking will re-run GN when any build input is newer than build.ninja, so
267 // any time the build is updated, build.ninja's timestamp needs to updated
268 // also, even if the contents haven't been changed.
269 base::FilePath ninja_file_name(build_settings->GetFullPath(
270 SourceFile(build_settings->build_dir().value() + "build.ninja")));
271 base::CreateDirectory(ninja_file_name.DirName());
272 std::string ninja_contents = file.str();
273 if (util::WriteFileAtomically(ninja_file_name, ninja_contents.data(),
274 static_cast<int>(ninja_contents.size())) !=
275 static_cast<int>(ninja_contents.size()))
276 return false;
277
278 // Dep file listing build dependencies.
279 base::FilePath dep_file_name(build_settings->GetFullPath(
280 SourceFile(build_settings->build_dir().value() + "build.ninja.d")));
281 std::string dep_contents = depfile.str();
282 if (util::WriteFileAtomically(dep_file_name, dep_contents.data(),
283 static_cast<int>(dep_contents.size())) !=
284 static_cast<int>(dep_contents.size()))
285 return false;
286
287 // Finally, write the empty build.ninja.stamp file. This is the output
288 // expected by the first of the two ninja rules used to accomplish
289 // regeneration.
290
291 base::FilePath stamp_file_name(build_settings->GetFullPath(
292 SourceFile(build_settings->build_dir().value() + "build.ninja.stamp")));
293 std::string stamp_contents;
294 if (util::WriteFileAtomically(stamp_file_name, stamp_contents.data(),
295 static_cast<int>(stamp_contents.size())) !=
296 static_cast<int>(stamp_contents.size()))
297 return false;
298
299 return true;
300 }
301
302 // static
ExtractRegenerationCommands(std::istream & build_ninja_in)303 std::string NinjaBuildWriter::ExtractRegenerationCommands(
304 std::istream& build_ninja_in) {
305 std::ostringstream out;
306 int num_blank_lines = 0;
307 for (std::string line; std::getline(build_ninja_in, line);) {
308 out << line << '\n';
309 if (line.empty())
310 ++num_blank_lines;
311 // Warning: Significant magic number. Represents the number of blank lines
312 // in the ninja rules written by NinjaBuildWriter::WriteNinjaRules below.
313 if (num_blank_lines == 4)
314 return out.str();
315 }
316 return std::string{};
317 }
318
WriteNinjaRules()319 void NinjaBuildWriter::WriteNinjaRules() {
320 out_ << "ninja_required_version = "
321 << build_settings_->ninja_required_version().Describe() << "\n\n";
322 out_ << "rule gn\n";
323 out_ << " command = " << GetSelfInvocationCommand(build_settings_) << "\n";
324 // Putting gn rule to console pool for colorful output on regeneration
325 out_ << " pool = console\n";
326 out_ << " description = Regenerating ninja files\n\n";
327
328 // A comment is left in the build.ninja explaining the two statement setup to
329 // avoid confusion, since build.ninja is written earlier than the ninja rules
330 // might make someone think.
331 out_ << "# The 'gn' rule also writes build.ninja, unbeknownst to ninja. The\n"
332 << "# build.ninja edge is separate to prevent ninja from deleting it\n"
333 << "# (due to depfile usage) if interrupted. gn uses atomic writes to\n"
334 << "# ensure that build.ninja is always valid even if interrupted.\n"
335 << "build build.ninja.stamp: gn\n"
336 << " generator = 1\n"
337 << " depfile = build.ninja.d\n"
338 << "\n"
339 << "build build.ninja: phony build.ninja.stamp\n"
340 << " generator = 1\n";
341
342 // Input build files. These go in the ".d" file. If we write them
343 // as dependencies in the .ninja file itself, ninja will expect
344 // the files to exist and will error if they don't. When files are
345 // listed in a depfile, missing files are ignored.
346 dep_out_ << "build.ninja.stamp:";
347
348 // Other files read by the build.
349 std::vector<base::FilePath> other_files = g_scheduler->GetGenDependencies();
350
351 const InputFileManager* input_file_manager =
352 g_scheduler->input_file_manager();
353
354 VectorSetSorter<base::FilePath> sorter(
355 input_file_manager->GetInputFileCount() + other_files.size());
356
357 input_file_manager->AddAllPhysicalInputFileNamesToVectorSetSorter(&sorter);
358 sorter.Add(other_files.begin(), other_files.end());
359
360 const base::FilePath build_path =
361 build_settings_->build_dir().Resolve(build_settings_->root_path());
362
363 EscapeOptions depfile_escape;
364 depfile_escape.mode = ESCAPE_DEPFILE;
365 auto item_callback = [this, &depfile_escape,
366 &build_path](const base::FilePath& input_file) {
367 const base::FilePath file =
368 MakeAbsoluteFilePathRelativeIfPossible(build_path, input_file);
369 dep_out_ << " ";
370 EscapeStringToStream(dep_out_,
371 FilePathToUTF8(file.NormalizePathSeparatorsTo('/')),
372 depfile_escape);
373 };
374
375 sorter.IterateOver(item_callback);
376
377 out_ << std::endl;
378 }
379
WriteAllPools()380 void NinjaBuildWriter::WriteAllPools() {
381 // Compute the pools referenced by all tools of all used toolchains.
382 std::set<const Pool*> used_pools;
383 for (const auto& pair : used_toolchains_) {
384 for (const auto& tool : pair.second->tools()) {
385 if (tool.second->pool().ptr)
386 used_pools.insert(tool.second->pool().ptr);
387 }
388 }
389
390 for (const Target* target : all_targets_) {
391 if (target->IsBinary() || target->output_type() == Target::ACTION ||
392 target->output_type() == Target::ACTION_FOREACH) {
393 const LabelPtrPair<Pool>& pool = target->pool();
394 if (pool.ptr)
395 used_pools.insert(pool.ptr);
396 }
397 }
398
399 // Write pools sorted by their name, to make output deterministic.
400 std::vector<const Pool*> sorted_pools(used_pools.begin(), used_pools.end());
401 auto pool_name = [this](const Pool* pool) {
402 return pool->GetNinjaName(default_toolchain_->label());
403 };
404 std::sort(sorted_pools.begin(), sorted_pools.end(),
405 [&pool_name](const Pool* a, const Pool* b) {
406 return pool_name(a) < pool_name(b);
407 });
408 for (const Pool* pool : sorted_pools) {
409 std::string name = pool_name(pool);
410 if (name == "console")
411 continue;
412 out_ << "pool " << name << std::endl
413 << " depth = " << pool->depth() << std::endl
414 << std::endl;
415 }
416 }
417
WriteSubninjas(Err * err)418 bool NinjaBuildWriter::WriteSubninjas(Err* err) {
419 // Write toolchains sorted by their name, to make output deterministic.
420 std::vector<std::pair<const Settings*, const Toolchain*>> sorted_settings(
421 used_toolchains_.begin(), used_toolchains_.end());
422 std::sort(sorted_settings.begin(), sorted_settings.end(),
423 [this](const std::pair<const Settings*, const Toolchain*>& a,
424 const std::pair<const Settings*, const Toolchain*>& b) {
425 // Always put the default toolchain first.
426 if (b.second == default_toolchain_)
427 return false;
428 if (a.second == default_toolchain_)
429 return true;
430 return GetNinjaFileForToolchain(a.first) <
431 GetNinjaFileForToolchain(b.first);
432 });
433
434 SourceFile previous_subninja;
435 const Toolchain* previous_toolchain = nullptr;
436
437 for (const auto& pair : sorted_settings) {
438 SourceFile subninja = GetNinjaFileForToolchain(pair.first);
439
440 // Since the toolchains are sorted, comparing to the previous subninja is
441 // enough to find duplicates.
442 if (subninja == previous_subninja) {
443 *err =
444 GetDuplicateToolchainError(subninja, previous_toolchain, pair.second);
445 return false;
446 }
447
448 out_ << "subninja ";
449 path_output_.WriteFile(out_, subninja);
450 out_ << std::endl;
451 previous_subninja = subninja;
452 previous_toolchain = pair.second;
453 }
454 out_ << std::endl;
455 return true;
456 }
457
458 const char kNinjaRules_Help[] =
459 R"(Ninja build rules
460
461 The "all" and "default" rules
462
463 All generated targets (see "gn help execution") will be added to an implicit
464 build rule called "all" so "ninja all" will always compile everything. The
465 default rule will be used by Ninja if no specific target is specified (just
466 typing "ninja"). If there is a target named "default" in the root build file,
467 it will be the default build rule, otherwise the implicit "all" rule will be
468 used.
469
470 Phony rules
471
472 GN generates Ninja "phony" rules for targets in the default toolchain. The
473 phony rules can collide with each other and with the names of generated files
474 so are generated with the following priority:
475
476 1. Actual files generated by the build always take precedence.
477
478 2. Targets in the toplevel //BUILD.gn file.
479
480 3. Targets in toplevel directories matching the names of the directories.
481 So "ninja foo" can be used to compile "//foo:foo". This only applies to
482 the first level of directories since usually these are the most
483 important (so this won't apply to "//foo/bar:bar").
484
485 4. The short names of executables if there is only one executable with that
486 short name. Use "ninja doom_melon" to compile the
487 "//tools/fruit:doom_melon" executable.
488
489 Note that for Apple platforms, create_bundle targets with a product_type
490 of "com.apple.product-type.application" are considered as executable
491 for this rule (as they define application bundles).
492
493 5. The short names of all targets if there is only one target with that
494 short name.
495
496 6. Full label name with no leading slashes. So you can use
497 "ninja tools/fruit:doom_melon" to build "//tools/fruit:doom_melon".
498
499 7. Labels with an implicit name part (when the short names match the
500 directory). So you can use "ninja foo/bar" to compile "//foo/bar:bar".
501
502 These "phony" rules are provided only for running Ninja since this matches
503 people's historical expectations for building. For consistency with the rest
504 of the program, GN introspection commands accept explicit labels.
505
506 To explicitly compile a target in a non-default toolchain, you must give
507 Ninja the exact name of the output file relative to the build directory.
508 )";
509
WritePhonyAndAllRules(Err * err)510 bool NinjaBuildWriter::WritePhonyAndAllRules(Err* err) {
511 // Track rules as we generate them so we don't accidentally write a phony
512 // rule that collides with something else.
513 // GN internally generates an "all" target, so don't duplicate it.
514 std::set<StringAtom, StringAtom::PtrCompare> written_rules;
515 written_rules.insert(StringAtom("all"));
516
517 // Set if we encounter a target named "//:default".
518 const Target* default_target = nullptr;
519
520 // Targets in the root build file.
521 std::vector<const Target*> toplevel_targets;
522
523 // Targets with names matching their toplevel directories. For example
524 // "//foo:foo". Expect this is the naming scheme for "big components."
525 std::vector<const Target*> toplevel_dir_targets;
526
527 // Tracks the number of each target with the given short name, as well
528 // as the short names of executables (which will be a subset of short_names).
529 std::map<std::string, Counts> short_names;
530 std::map<std::string, Counts> exes;
531
532 // ----------------------------------------------------
533 // If you change this algorithm, update the help above!
534 // ----------------------------------------------------
535
536 for (const Target* target : default_toolchain_targets_) {
537 const Label& label = target->label();
538 const std::string& short_name = label.name();
539
540 if (label.dir() == build_settings_->root_target_label().dir() &&
541 short_name == "default")
542 default_target = target;
543
544 // Count the number of targets with the given short name.
545 Counts& short_names_counts = short_names[short_name];
546 short_names_counts.count++;
547 short_names_counts.last_seen = target;
548
549 // Count executables with the given short name.
550 if (target->output_type() == Target::EXECUTABLE) {
551 Counts& exes_counts = exes[short_name];
552 exes_counts.count++;
553 exes_counts.last_seen = target;
554 }
555
556 if (target->output_type() == Target::CREATE_BUNDLE &&
557 target->bundle_data().is_application()) {
558 Counts& exes_counts = exes[short_name];
559 exes_counts.count++;
560 exes_counts.last_seen = target;
561 }
562
563 // Find targets in "important" directories.
564 const std::string& dir_string = label.dir().value();
565 if (dir_string.size() == 2 && dir_string[0] == '/' &&
566 dir_string[1] == '/') {
567 toplevel_targets.push_back(target);
568 } else if (dir_string.size() == label.name().size() + 3 && // Size matches.
569 dir_string[0] == '/' &&
570 dir_string[1] == '/' && // "//" at beginning.
571 dir_string[dir_string.size() - 1] == '/' && // "/" at end.
572 dir_string.compare(2, label.name().size(), label.name()) == 0) {
573 toplevel_dir_targets.push_back(target);
574 }
575
576 // Add the output files from each target to the written rules so that
577 // we don't write phony rules that collide with anything generated by the
578 // build.
579 //
580 // If at this point there is a collision (no phony rules have been
581 // generated yet), two targets make the same output so throw an error.
582 for (const auto& output : target->computed_outputs()) {
583 // Need to normalize because many toolchain outputs will be preceded
584 // with "./".
585 std::string output_string(output.value());
586 NormalizePath(&output_string);
587 const StringAtom output_string_atom(output_string);
588
589 if (!written_rules.insert(output_string_atom).second) {
590 *err = GetDuplicateOutputError(default_toolchain_targets_, output);
591 return false;
592 }
593 }
594 }
595
596 // First prefer the short names of toplevel targets.
597 for (const Target* target : toplevel_targets) {
598 if (written_rules.insert(target->label().name_atom()).second)
599 WritePhonyRule(target, target->label().name_atom());
600 }
601
602 // Next prefer short names of toplevel dir targets.
603 for (const Target* target : toplevel_dir_targets) {
604 if (written_rules.insert(target->label().name_atom()).second)
605 WritePhonyRule(target, target->label().name_atom());
606 }
607
608 // Write out the names labels of executables. Many toolchains will produce
609 // executables in the root build directory with no extensions, so the names
610 // will already exist and this will be a no-op. But on Windows such programs
611 // will have extensions, and executables may override the output directory to
612 // go into some other place.
613 //
614 // Putting this after the "toplevel" rules above also means that you can
615 // steal the short name from an executable by outputting the executable to
616 // a different directory or using a different output name, and writing a
617 // toplevel build rule.
618 for (const auto& pair : exes) {
619 const Counts& counts = pair.second;
620 const StringAtom& short_name = counts.last_seen->label().name_atom();
621 if (counts.count == 1 && written_rules.insert(short_name).second)
622 WritePhonyRule(counts.last_seen, short_name);
623 }
624
625 // Write short names when those names are unique and not already taken.
626 for (const auto& pair : short_names) {
627 const Counts& counts = pair.second;
628 const StringAtom& short_name = counts.last_seen->label().name_atom();
629 if (counts.count == 1 && written_rules.insert(short_name).second)
630 WritePhonyRule(counts.last_seen, short_name);
631 }
632
633 // Write the label variants of the target name.
634 for (const Target* target : default_toolchain_targets_) {
635 const Label& label = target->label();
636
637 // Write the long name "foo/bar:baz" for the target "//foo/bar:baz".
638 std::string long_name = label.GetUserVisibleName(false);
639 base::TrimString(long_name, "/", &long_name);
640 const StringAtom long_name_atom(long_name);
641 if (written_rules.insert(long_name_atom).second)
642 WritePhonyRule(target, long_name_atom);
643
644 // Write the directory name with no target name if they match
645 // (e.g. "//foo/bar:bar" -> "foo/bar").
646 if (FindLastDirComponent(label.dir()) == label.name()) {
647 std::string medium_name = DirectoryWithNoLastSlash(label.dir());
648 base::TrimString(medium_name, "/", &medium_name);
649 const StringAtom medium_name_atom(medium_name);
650
651 // That may have generated a name the same as the short name of the
652 // target which we already wrote.
653 if (medium_name != label.name() &&
654 written_rules.insert(medium_name_atom).second)
655 WritePhonyRule(target, medium_name_atom);
656 }
657 }
658
659 // Write the autogenerated "all" rule.
660 if (!default_toolchain_targets_.empty()) {
661 out_ << "\nbuild all: phony";
662
663 EscapeOptions ninja_escape;
664 ninja_escape.mode = ESCAPE_NINJA;
665 for (const Target* target : default_toolchain_targets_) {
666 out_ << " $\n ";
667 path_output_.WriteFile(out_, target->dependency_output_file());
668 }
669 }
670 out_ << std::endl;
671
672 if (default_target) {
673 // Use the short name when available
674 if (written_rules.find(StringAtom("default")) != written_rules.end()) {
675 out_ << "\ndefault default" << std::endl;
676 } else {
677 out_ << "\ndefault ";
678 path_output_.WriteFile(out_, default_target->dependency_output_file());
679 out_ << std::endl;
680 }
681 } else if (!default_toolchain_targets_.empty()) {
682 out_ << "\ndefault all" << std::endl;
683 }
684
685 return true;
686 }
687
WritePhonyRule(const Target * target,std::string_view phony_name)688 void NinjaBuildWriter::WritePhonyRule(const Target* target,
689 std::string_view phony_name) {
690 EscapeOptions ninja_escape;
691 ninja_escape.mode = ESCAPE_NINJA;
692
693 // Escape for special chars Ninja will handle.
694 std::string escaped = EscapeString(phony_name, ninja_escape, nullptr);
695
696 out_ << "build " << escaped << ": phony ";
697 path_output_.WriteFile(out_, target->dependency_output_file());
698 out_ << std::endl;
699 }
700