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