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