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