• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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