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