• 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_target_writer.h"
6 
7 #include <sstream>
8 
9 #include "base/files/file_util.h"
10 #include "base/strings/string_util.h"
11 #include "gn/c_substitution_type.h"
12 #include "gn/config_values_extractors.h"
13 #include "gn/err.h"
14 #include "gn/escape.h"
15 #include "gn/filesystem_utils.h"
16 #include "gn/general_tool.h"
17 #include "gn/ninja_action_target_writer.h"
18 #include "gn/ninja_binary_target_writer.h"
19 #include "gn/ninja_bundle_data_target_writer.h"
20 #include "gn/ninja_copy_target_writer.h"
21 #include "gn/ninja_create_bundle_target_writer.h"
22 #include "gn/ninja_generated_file_target_writer.h"
23 #include "gn/ninja_group_target_writer.h"
24 #include "gn/ninja_target_command_util.h"
25 #include "gn/ninja_utils.h"
26 #include "gn/output_file.h"
27 #include "gn/rust_substitution_type.h"
28 #include "gn/scheduler.h"
29 #include "gn/string_output_buffer.h"
30 #include "gn/string_utils.h"
31 #include "gn/substitution_writer.h"
32 #include "gn/target.h"
33 #include "gn/trace.h"
34 
NinjaTargetWriter(const Target * target,std::ostream & out)35 NinjaTargetWriter::NinjaTargetWriter(const Target* target, std::ostream& out)
36     : settings_(target->settings()),
37       target_(target),
38       out_(out),
39       path_output_(settings_->build_settings()->build_dir(),
40                    settings_->build_settings()->root_path_utf8(),
41                    ESCAPE_NINJA) {}
42 
SetResolvedTargetData(ResolvedTargetData * resolved)43 void NinjaTargetWriter::SetResolvedTargetData(ResolvedTargetData* resolved) {
44   if (resolved) {
45     resolved_owned_.reset();
46     resolved_ptr_ = resolved;
47   }
48 }
49 
SetNinjaOutputs(std::vector<OutputFile> * ninja_outputs)50 void NinjaTargetWriter::SetNinjaOutputs(
51     std::vector<OutputFile>* ninja_outputs) {
52   ninja_outputs_ = ninja_outputs;
53 }
54 
GetResolvedTargetData()55 ResolvedTargetData* NinjaTargetWriter::GetResolvedTargetData() {
56   return const_cast<ResolvedTargetData*>(&resolved());
57 }
58 
resolved() const59 const ResolvedTargetData& NinjaTargetWriter::resolved() const {
60   if (!resolved_ptr_) {
61     resolved_owned_ = std::make_unique<ResolvedTargetData>();
62     resolved_ptr_ = resolved_owned_.get();
63   }
64   return *resolved_ptr_;
65 }
66 
67 NinjaTargetWriter::~NinjaTargetWriter() = default;
68 
WriteOutput(const OutputFile & output) const69 void NinjaTargetWriter::WriteOutput(const OutputFile& output) const {
70   path_output_.WriteFile(out_, output);
71   if (ninja_outputs_)
72     ninja_outputs_->push_back(output);
73 }
74 
WriteOutput(OutputFile && output) const75 void NinjaTargetWriter::WriteOutput(OutputFile&& output) const {
76   path_output_.WriteFile(out_, output);
77   if (ninja_outputs_)
78     ninja_outputs_->push_back(std::move(output));
79 }
80 
WriteOutputs(const std::vector<OutputFile> & outputs) const81 void NinjaTargetWriter::WriteOutputs(
82     const std::vector<OutputFile>& outputs) const {
83   path_output_.WriteFiles(out_, outputs);
84   if (ninja_outputs_)
85     ninja_outputs_->insert(ninja_outputs_->end(), outputs.begin(),
86                            outputs.end());
87 }
88 
WriteOutputs(std::vector<OutputFile> && outputs) const89 void NinjaTargetWriter::WriteOutputs(std::vector<OutputFile>&& outputs) const {
90   path_output_.WriteFiles(out_, outputs);
91   if (ninja_outputs_) {
92     for (auto& output : outputs)
93       ninja_outputs_->push_back(std::move(output));
94   }
95 }
96 
97 // static
RunAndWriteFile(const Target * target,ResolvedTargetData * resolved,std::vector<OutputFile> * ninja_outputs)98 std::string NinjaTargetWriter::RunAndWriteFile(
99     const Target* target,
100     ResolvedTargetData* resolved,
101     std::vector<OutputFile>* ninja_outputs) {
102   const Settings* settings = target->settings();
103 
104   ScopedTrace trace(TraceItem::TRACE_FILE_WRITE_NINJA,
105                     target->label().GetUserVisibleName(false));
106   trace.SetToolchain(settings->toolchain_label());
107 
108   if (g_scheduler->verbose_logging())
109     g_scheduler->Log("Computing", target->label().GetUserVisibleName(true));
110 
111   // It's ridiculously faster to write to a string and then write that to
112   // disk in one operation than to use an fstream here.
113   StringOutputBuffer storage;
114   std::ostream rules(&storage);
115 
116   // Call out to the correct sub-type of writer. Binary targets need to be
117   // written to separate files for compiler flag scoping, but other target
118   // types can have their rules coalesced.
119   //
120   // In ninja, if a rule uses a variable (like $include_dirs) it will use
121   // the value set by indenting it under the build line or it takes the value
122   // from the end of the invoking scope (otherwise the current file). It does
123   // not copy the value from what it was when the build line was encountered.
124   // To avoid writing lots of duplicate rules for defines and cflags, etc. on
125   // each source file build line, we use separate .ninja files with the shared
126   // variables set at the top.
127   //
128   // Groups and actions don't use this type of flag, they make unique rules
129   // or write variables scoped under each build line. As a result, they don't
130   // need the separate files.
131   bool needs_file_write = false;
132   if (target->output_type() == Target::BUNDLE_DATA) {
133     NinjaBundleDataTargetWriter writer(target, rules);
134     writer.SetResolvedTargetData(resolved);
135     writer.SetNinjaOutputs(ninja_outputs);
136     writer.Run();
137   } else if (target->output_type() == Target::CREATE_BUNDLE) {
138     NinjaCreateBundleTargetWriter writer(target, rules);
139     writer.SetResolvedTargetData(resolved);
140     writer.SetNinjaOutputs(ninja_outputs);
141     writer.Run();
142   } else if (target->output_type() == Target::COPY_FILES) {
143     NinjaCopyTargetWriter writer(target, rules);
144     writer.SetResolvedTargetData(resolved);
145     writer.SetNinjaOutputs(ninja_outputs);
146     writer.Run();
147   } else if (target->output_type() == Target::ACTION ||
148              target->output_type() == Target::ACTION_FOREACH) {
149     NinjaActionTargetWriter writer(target, rules);
150     writer.SetResolvedTargetData(resolved);
151     writer.SetNinjaOutputs(ninja_outputs);
152     writer.Run();
153   } else if (target->output_type() == Target::GROUP) {
154     NinjaGroupTargetWriter writer(target, rules);
155     writer.SetResolvedTargetData(resolved);
156     writer.SetNinjaOutputs(ninja_outputs);
157     writer.Run();
158   } else if (target->output_type() == Target::GENERATED_FILE) {
159     NinjaGeneratedFileTargetWriter writer(target, rules);
160     writer.SetResolvedTargetData(resolved);
161     writer.SetNinjaOutputs(ninja_outputs);
162     writer.Run();
163   } else if (target->IsBinary()) {
164     needs_file_write = true;
165     NinjaBinaryTargetWriter writer(target, rules);
166     writer.SetResolvedTargetData(resolved);
167     writer.SetNinjaOutputs(ninja_outputs);
168     writer.Run();
169   } else {
170     CHECK(0) << "Output type of target not handled.";
171   }
172 
173   if (needs_file_write) {
174     // Write the ninja file.
175     SourceFile ninja_file = GetNinjaFileForTarget(target);
176     base::FilePath full_ninja_file =
177         settings->build_settings()->GetFullPath(ninja_file);
178     storage.WriteToFileIfChanged(full_ninja_file, nullptr);
179 
180     EscapeOptions options;
181     options.mode = ESCAPE_NINJA;
182 
183     // Return the subninja command to load the rules file.
184     std::string result = "subninja ";
185     result.append(EscapeString(
186         OutputFile(target->settings()->build_settings(), ninja_file).value(),
187         options, nullptr));
188     result.push_back('\n');
189     return result;
190   }
191 
192   // No separate file required, just return the rules.
193   return storage.str();
194 }
195 
WriteEscapedSubstitution(const Substitution * type)196 void NinjaTargetWriter::WriteEscapedSubstitution(const Substitution* type) {
197   EscapeOptions opts;
198   opts.mode = ESCAPE_NINJA;
199 
200   out_ << type->ninja_name << " = ";
201   EscapeStringToStream(
202       out_, SubstitutionWriter::GetTargetSubstitution(target_, type), opts);
203   out_ << std::endl;
204 }
205 
WriteSharedVars(const SubstitutionBits & bits)206 void NinjaTargetWriter::WriteSharedVars(const SubstitutionBits& bits) {
207   bool written_anything = false;
208 
209   // Target label.
210   if (bits.used.count(&SubstitutionLabel)) {
211     WriteEscapedSubstitution(&SubstitutionLabel);
212     written_anything = true;
213   }
214 
215   // Target label name.
216   if (bits.used.count(&SubstitutionLabelName)) {
217     WriteEscapedSubstitution(&SubstitutionLabelName);
218     written_anything = true;
219   }
220 
221   // Target label name without toolchain.
222   if (bits.used.count(&SubstitutionLabelNoToolchain)) {
223     WriteEscapedSubstitution(&SubstitutionLabelNoToolchain);
224     written_anything = true;
225   }
226 
227   // Root gen dir.
228   if (bits.used.count(&SubstitutionRootGenDir)) {
229     WriteEscapedSubstitution(&SubstitutionRootGenDir);
230     written_anything = true;
231   }
232 
233   // Root out dir.
234   if (bits.used.count(&SubstitutionRootOutDir)) {
235     WriteEscapedSubstitution(&SubstitutionRootOutDir);
236     written_anything = true;
237   }
238 
239   // Target gen dir.
240   if (bits.used.count(&SubstitutionTargetGenDir)) {
241     WriteEscapedSubstitution(&SubstitutionTargetGenDir);
242     written_anything = true;
243   }
244 
245   // Target out dir.
246   if (bits.used.count(&SubstitutionTargetOutDir)) {
247     WriteEscapedSubstitution(&SubstitutionTargetOutDir);
248     written_anything = true;
249   }
250 
251   // Target output name.
252   if (bits.used.count(&SubstitutionTargetOutputName)) {
253     WriteEscapedSubstitution(&SubstitutionTargetOutputName);
254     written_anything = true;
255   }
256 
257   // If we wrote any vars, separate them from the rest of the file that follows
258   // with a blank line.
259   if (written_anything)
260     out_ << std::endl;
261 }
262 
WriteCCompilerVars(const SubstitutionBits & bits,bool indent,bool respect_source_used)263 void NinjaTargetWriter::WriteCCompilerVars(const SubstitutionBits& bits,
264                                            bool indent,
265                                            bool respect_source_used) {
266   // Defines.
267   if (bits.used.count(&CSubstitutionDefines)) {
268     if (indent)
269       out_ << "  ";
270     out_ << CSubstitutionDefines.ninja_name << " =";
271     RecursiveTargetConfigToStream<std::string>(kRecursiveWriterSkipDuplicates,
272                                                target_, &ConfigValues::defines,
273                                                DefineWriter(), out_);
274     out_ << std::endl;
275   }
276 
277   // Framework search path.
278   if (bits.used.count(&CSubstitutionFrameworkDirs)) {
279     const Tool* tool = target_->toolchain()->GetTool(CTool::kCToolLink);
280 
281     if (indent)
282       out_ << "  ";
283     out_ << CSubstitutionFrameworkDirs.ninja_name << " =";
284     PathOutput framework_dirs_output(
285         path_output_.current_dir(),
286         settings_->build_settings()->root_path_utf8(), ESCAPE_NINJA_COMMAND);
287     RecursiveTargetConfigToStream<SourceDir>(
288         kRecursiveWriterSkipDuplicates, target_, &ConfigValues::framework_dirs,
289         FrameworkDirsWriter(framework_dirs_output,
290                             tool->framework_dir_switch()),
291         out_);
292     out_ << std::endl;
293   }
294 
295   // Include directories.
296   if (bits.used.count(&CSubstitutionIncludeDirs)) {
297     if (indent)
298       out_ << "  ";
299     out_ << CSubstitutionIncludeDirs.ninja_name << " =";
300     PathOutput include_path_output(
301         path_output_.current_dir(),
302         settings_->build_settings()->root_path_utf8(), ESCAPE_NINJA_COMMAND);
303     RecursiveTargetConfigToStream<SourceDir>(
304         kRecursiveWriterSkipDuplicates, target_, &ConfigValues::include_dirs,
305         IncludeWriter(include_path_output), out_);
306     out_ << std::endl;
307   }
308 
309   bool has_precompiled_headers =
310       target_->config_values().has_precompiled_headers();
311 
312   EscapeOptions opts;
313   opts.mode = ESCAPE_NINJA_COMMAND;
314   if (respect_source_used
315           ? target_->source_types_used().Get(SourceFile::SOURCE_S)
316           : bits.used.count(&CSubstitutionAsmFlags)) {
317     WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
318                  &CSubstitutionAsmFlags, false, Tool::kToolNone,
319                  &ConfigValues::asmflags, opts, path_output_, out_, true,
320                  indent);
321   }
322   if (respect_source_used
323           ? (target_->source_types_used().Get(SourceFile::SOURCE_C) ||
324              target_->source_types_used().Get(SourceFile::SOURCE_CPP) ||
325              target_->source_types_used().Get(SourceFile::SOURCE_M) ||
326              target_->source_types_used().Get(SourceFile::SOURCE_MM) ||
327              target_->source_types_used().Get(SourceFile::SOURCE_MODULEMAP))
328           : bits.used.count(&CSubstitutionCFlags)) {
329     WriteOneFlag(kRecursiveWriterKeepDuplicates, target_, &CSubstitutionCFlags,
330                  false, Tool::kToolNone, &ConfigValues::cflags, opts,
331                  path_output_, out_, true, indent);
332   }
333   if (respect_source_used
334           ? target_->source_types_used().Get(SourceFile::SOURCE_C)
335           : bits.used.count(&CSubstitutionCFlagsC)) {
336     WriteOneFlag(kRecursiveWriterKeepDuplicates, target_, &CSubstitutionCFlagsC,
337                  has_precompiled_headers, CTool::kCToolCc,
338                  &ConfigValues::cflags_c, opts, path_output_, out_, true,
339                  indent);
340   }
341   if (respect_source_used
342           ? (target_->source_types_used().Get(SourceFile::SOURCE_CPP) ||
343              target_->source_types_used().Get(SourceFile::SOURCE_MODULEMAP))
344           : bits.used.count(&CSubstitutionCFlagsCc)) {
345     WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
346                  &CSubstitutionCFlagsCc, has_precompiled_headers,
347                  CTool::kCToolCxx, &ConfigValues::cflags_cc, opts, path_output_,
348                  out_, true, indent);
349   }
350   if (respect_source_used
351           ? target_->source_types_used().Get(SourceFile::SOURCE_M)
352           : bits.used.count(&CSubstitutionCFlagsObjC)) {
353     WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
354                  &CSubstitutionCFlagsObjC, has_precompiled_headers,
355                  CTool::kCToolObjC, &ConfigValues::cflags_objc, opts,
356                  path_output_, out_, true, indent);
357   }
358   if (respect_source_used
359           ? target_->source_types_used().Get(SourceFile::SOURCE_MM)
360           : bits.used.count(&CSubstitutionCFlagsObjCc)) {
361     WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
362                  &CSubstitutionCFlagsObjCc, has_precompiled_headers,
363                  CTool::kCToolObjCxx, &ConfigValues::cflags_objcc, opts,
364                  path_output_, out_, true, indent);
365   }
366   if (target_->source_types_used().SwiftSourceUsed() || !respect_source_used) {
367     if (bits.used.count(&CSubstitutionSwiftModuleName)) {
368       if (indent)
369         out_ << "  ";
370       out_ << CSubstitutionSwiftModuleName.ninja_name << " = ";
371       EscapeStringToStream(out_, target_->swift_values().module_name(), opts);
372       out_ << std::endl;
373     }
374 
375     if (bits.used.count(&CSubstitutionSwiftBridgeHeader)) {
376       if (indent)
377         out_ << "  ";
378       out_ << CSubstitutionSwiftBridgeHeader.ninja_name << " = ";
379       if (!target_->swift_values().bridge_header().is_null()) {
380         path_output_.WriteFile(out_, target_->swift_values().bridge_header());
381       } else {
382         out_ << R"("")";
383       }
384       out_ << std::endl;
385     }
386 
387     if (bits.used.count(&CSubstitutionSwiftModuleDirs)) {
388       // Uniquify the list of swiftmodule dirs (in case multiple swiftmodules
389       // are generated in the same directory).
390       UniqueVector<SourceDir> swiftmodule_dirs;
391       for (const Target* dep : resolved().GetSwiftModuleDependencies(target_))
392         swiftmodule_dirs.push_back(dep->swift_values().module_output_dir());
393 
394       if (indent)
395         out_ << "  ";
396       out_ << CSubstitutionSwiftModuleDirs.ninja_name << " =";
397       PathOutput swiftmodule_path_output(
398           path_output_.current_dir(),
399           settings_->build_settings()->root_path_utf8(), ESCAPE_NINJA_COMMAND);
400       IncludeWriter swiftmodule_path_writer(swiftmodule_path_output);
401       for (const SourceDir& swiftmodule_dir : swiftmodule_dirs) {
402         swiftmodule_path_writer(swiftmodule_dir, out_);
403       }
404       out_ << std::endl;
405     }
406 
407     WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
408                  &CSubstitutionSwiftFlags, false, CTool::kCToolSwift,
409                  &ConfigValues::swiftflags, opts, path_output_, out_, true,
410                  indent);
411   }
412 }
413 
WriteRustCompilerVars(const SubstitutionBits & bits,bool indent,bool always_write)414 void NinjaTargetWriter::WriteRustCompilerVars(const SubstitutionBits& bits,
415                                               bool indent,
416                                               bool always_write) {
417   EscapeOptions opts;
418   opts.mode = ESCAPE_NINJA_COMMAND;
419 
420   if (bits.used.count(&kRustSubstitutionRustFlags) || always_write) {
421     WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
422                  &kRustSubstitutionRustFlags, false, Tool::kToolNone,
423                  &ConfigValues::rustflags, opts, path_output_, out_, true,
424                  indent);
425   }
426 
427   if (bits.used.count(&kRustSubstitutionRustEnv) || always_write) {
428     WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
429                  &kRustSubstitutionRustEnv, false, Tool::kToolNone,
430                  &ConfigValues::rustenv, opts, path_output_, out_, true,
431                  indent);
432   }
433 }
434 
WriteInputDepsStampAndGetDep(const std::vector<const Target * > & additional_hard_deps,size_t num_stamp_uses) const435 std::vector<OutputFile> NinjaTargetWriter::WriteInputDepsStampAndGetDep(
436     const std::vector<const Target*>& additional_hard_deps,
437     size_t num_stamp_uses) const {
438   CHECK(target_->toolchain()) << "Toolchain not set on target "
439                               << target_->label().GetUserVisibleName(true);
440 
441   // ----------
442   // Collect all input files that are input deps of this target. Knowing the
443   // number before writing allows us to either skip writing the input deps
444   // stamp or optimize it. Use pointers to avoid copies here.
445   std::vector<const SourceFile*> input_deps_sources;
446   input_deps_sources.reserve(32);
447 
448   // Actions get implicit dependencies on the script itself.
449   if (target_->output_type() == Target::ACTION ||
450       target_->output_type() == Target::ACTION_FOREACH)
451     input_deps_sources.push_back(&target_->action_values().script());
452 
453   // Input files are only considered for non-binary targets which use an
454   // implicit dependency instead. The implicit dependency in this case is
455   // handled separately by the binary target writer.
456   if (!target_->IsBinary()) {
457     for (ConfigValuesIterator iter(target_); !iter.done(); iter.Next()) {
458       for (const auto& input : iter.cur().inputs())
459         input_deps_sources.push_back(&input);
460     }
461   }
462 
463   // For an action (where we run a script only once) the sources are the same
464   // as the inputs. For action_foreach, the sources will be operated on
465   // separately so don't handle them here.
466   if (target_->output_type() == Target::ACTION) {
467     for (const auto& source : target_->sources())
468       input_deps_sources.push_back(&source);
469   }
470 
471   // ----------
472   // Collect all target input dependencies of this target as was done for the
473   // files above.
474   std::vector<const Target*> input_deps_targets;
475   input_deps_targets.reserve(32);
476 
477   // Hard dependencies that are direct or indirect dependencies.
478   // These are large (up to 100s), hence why we check other
479   const TargetSet& hard_deps = resolved().GetHardDeps(target_);
480   for (const Target* target : hard_deps) {
481     // BUNDLE_DATA should normally be treated as a data-only dependency
482     // (see Target::IsDataOnly()). Only the CREATE_BUNDLE target, that actually
483     // consumes this data, needs to have the BUNDLE_DATA as an input dependency.
484     if (target->output_type() != Target::BUNDLE_DATA ||
485         target_->output_type() == Target::CREATE_BUNDLE)
486       input_deps_targets.push_back(target);
487   }
488 
489   // Additional hard dependencies passed in. These are usually empty or small,
490   // and we don't want to duplicate the explicit hard deps of the target.
491   for (const Target* target : additional_hard_deps) {
492     if (!hard_deps.contains(target))
493       input_deps_targets.push_back(target);
494   }
495 
496   // Toolchain dependencies. These must be resolved before doing anything.
497   // This just writes all toolchain deps for simplicity. If we find that
498   // toolchains often have more than one dependency, we could consider writing
499   // a toolchain-specific stamp file and only include the stamp here.
500   // Note that these are usually empty/small.
501   const LabelTargetVector& toolchain_deps = target_->toolchain()->deps();
502   for (const auto& toolchain_dep : toolchain_deps) {
503     // This could theoretically duplicate dependencies already in the list,
504     // but it shouldn't happen in practice, is inconvenient to check for,
505     // and only results in harmless redundant dependencies listed.
506     input_deps_targets.push_back(toolchain_dep.ptr);
507   }
508 
509   // ---------
510   // Write the outputs.
511 
512   if (input_deps_sources.size() + input_deps_targets.size() == 0)
513     return std::vector<OutputFile>();  // No input dependencies.
514 
515   // If we're only generating one input dependency, return it directly instead
516   // of writing a stamp file for it.
517   if (input_deps_sources.size() == 1 && input_deps_targets.size() == 0)
518     return std::vector<OutputFile>{
519         OutputFile(settings_->build_settings(), *input_deps_sources[0])};
520   if (input_deps_sources.size() == 0 && input_deps_targets.size() == 1) {
521     const OutputFile& dep = input_deps_targets[0]->dependency_output_file();
522     DCHECK(!dep.value().empty());
523     return std::vector<OutputFile>{dep};
524   }
525 
526   std::vector<OutputFile> outs;
527   // File input deps.
528   for (const SourceFile* source : input_deps_sources)
529     outs.push_back(OutputFile(settings_->build_settings(), *source));
530   // Target input deps. Sort by label so the output is deterministic (otherwise
531   // some of the targets will have gone through std::sets which will have
532   // sorted them by pointer).
533   std::sort(
534       input_deps_targets.begin(), input_deps_targets.end(),
535       [](const Target* a, const Target* b) { return a->label() < b->label(); });
536   for (auto* dep : input_deps_targets) {
537     DCHECK(!dep->dependency_output_file().value().empty());
538     outs.push_back(dep->dependency_output_file());
539   }
540 
541   // If there are multiple inputs, but the stamp file would be referenced only
542   // once, don't write it but depend on the inputs directly.
543   if (num_stamp_uses == 1u)
544     return outs;
545 
546   // Make a stamp file.
547   OutputFile input_stamp_file =
548       GetBuildDirForTargetAsOutputFile(target_, BuildDirType::OBJ);
549   input_stamp_file.value().append(target_->label().name());
550   input_stamp_file.value().append(".inputdeps.stamp");
551 
552   out_ << "build ";
553   WriteOutput(input_stamp_file);
554 
555   out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
556        << GeneralTool::kGeneralToolStamp;
557   path_output_.WriteFiles(out_, outs);
558 
559   out_ << "\n";
560   return std::vector<OutputFile>{input_stamp_file};
561 }
562 
WriteStampForTarget(const std::vector<OutputFile> & files,const std::vector<OutputFile> & order_only_deps)563 void NinjaTargetWriter::WriteStampForTarget(
564     const std::vector<OutputFile>& files,
565     const std::vector<OutputFile>& order_only_deps) {
566   const OutputFile& stamp_file = target_->dependency_output_file();
567 
568   // First validate that the target's dependency is a stamp file. Otherwise,
569   // we shouldn't have gotten here!
570   CHECK(base::EndsWithCaseInsensitiveASCII(stamp_file.value(), ".stamp"))
571       << "Output should end in \".stamp\" for stamp file output. Instead got: "
572       << "\"" << stamp_file.value() << "\"";
573 
574   out_ << "build ";
575   WriteOutput(std::move(stamp_file));
576 
577   out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
578        << GeneralTool::kGeneralToolStamp;
579   path_output_.WriteFiles(out_, files);
580 
581   if (!order_only_deps.empty()) {
582     out_ << " ||";
583     path_output_.WriteFiles(out_, order_only_deps);
584   }
585   out_ << std::endl;
586 }
587