• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 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_c_binary_target_writer.h"
6 
7 #include <stddef.h>
8 #include <string.h>
9 
10 #include <cstring>
11 #include <set>
12 #include <sstream>
13 
14 #include "base/strings/string_util.h"
15 #include "gn/c_substitution_type.h"
16 #include "gn/config_values_extractors.h"
17 #include "gn/deps_iterator.h"
18 #include "gn/err.h"
19 #include "gn/escape.h"
20 #include "gn/filesystem_utils.h"
21 #include "gn/general_tool.h"
22 #include "gn/ninja_target_command_util.h"
23 #include "gn/ninja_utils.h"
24 #include "gn/pool.h"
25 #include "gn/scheduler.h"
26 #include "gn/settings.h"
27 #include "gn/string_utils.h"
28 #include "gn/substitution_writer.h"
29 #include "gn/target.h"
30 
31 struct ModuleDep {
ModuleDepModuleDep32   ModuleDep(const SourceFile* modulemap,
33             const std::string& module_name,
34             const OutputFile& pcm,
35             bool is_self)
36       : modulemap(modulemap),
37         module_name(module_name),
38         pcm(pcm),
39         is_self(is_self) {}
40 
41   // The input module.modulemap source file.
42   const SourceFile* modulemap;
43 
44   // The internal module name, in GN this is the target's label.
45   std::string module_name;
46 
47   // The compiled version of the module.
48   OutputFile pcm;
49 
50   // Is this the module for the current target.
51   bool is_self;
52 };
53 
54 namespace {
55 
56 // Returns the proper escape options for writing compiler and linker flags.
GetFlagOptions()57 EscapeOptions GetFlagOptions() {
58   EscapeOptions opts;
59   opts.mode = ESCAPE_NINJA_COMMAND;
60   return opts;
61 }
62 
63 // Returns the language-specific lang recognized by gcc’s -x flag for
64 // precompiled header files.
GetPCHLangForToolType(const char * name)65 const char* GetPCHLangForToolType(const char* name) {
66   if (name == CTool::kCToolCc)
67     return "c-header";
68   if (name == CTool::kCToolCxx)
69     return "c++-header";
70   if (name == CTool::kCToolObjC)
71     return "objective-c-header";
72   if (name == CTool::kCToolObjCxx)
73     return "objective-c++-header";
74   NOTREACHED() << "Not a valid PCH tool type: " << name;
75   return "";
76 }
77 
GetModuleMapFromTargetSources(const Target * target)78 const SourceFile* GetModuleMapFromTargetSources(const Target* target) {
79   for (const SourceFile& sf : target->sources()) {
80     if (sf.IsModuleMapType())
81       return &sf;
82   }
83   return nullptr;
84 }
85 
GetModuleDepsInformation(const Target * target,const ResolvedTargetData & resolved)86 std::vector<ModuleDep> GetModuleDepsInformation(
87     const Target* target,
88     const ResolvedTargetData& resolved) {
89   std::vector<ModuleDep> ret;
90 
91   auto add = [&ret](const Target* t, bool is_self) {
92     const SourceFile* modulemap = GetModuleMapFromTargetSources(t);
93     CHECK(modulemap);
94 
95     std::string label;
96     CHECK(SubstitutionWriter::GetTargetSubstitution(
97         t, &SubstitutionLabelNoToolchain, &label));
98 
99     const char* tool_type;
100     std::vector<OutputFile> modulemap_outputs;
101     CHECK(
102         t->GetOutputFilesForSource(*modulemap, &tool_type, &modulemap_outputs));
103     // Must be only one .pcm from .modulemap.
104     CHECK(modulemap_outputs.size() == 1u);
105     ret.emplace_back(modulemap, label, modulemap_outputs[0], is_self);
106   };
107 
108   if (target->source_types_used().Get(SourceFile::SOURCE_MODULEMAP)) {
109     add(target, true);
110   }
111 
112   for (const Target* dep : resolved.GetLinkedDeps(target)) {
113     // Having a .modulemap source means that the dependency is modularized.
114     if (dep->source_types_used().Get(SourceFile::SOURCE_MODULEMAP)) {
115       add(dep, false);
116     }
117   }
118 
119   return ret;
120 }
121 
122 }  // namespace
123 
NinjaCBinaryTargetWriter(const Target * target,std::ostream & out)124 NinjaCBinaryTargetWriter::NinjaCBinaryTargetWriter(const Target* target,
125                                                    std::ostream& out)
126     : NinjaBinaryTargetWriter(target, out),
127       tool_(target->toolchain()->GetToolForTargetFinalOutputAsC(target)) {}
128 
129 NinjaCBinaryTargetWriter::~NinjaCBinaryTargetWriter() = default;
130 
Run()131 void NinjaCBinaryTargetWriter::Run() {
132   std::vector<ModuleDep> module_dep_info =
133       GetModuleDepsInformation(target_, resolved());
134 
135   WriteCompilerVars(module_dep_info);
136 
137   size_t num_stamp_uses = target_->sources().size();
138 
139   std::vector<OutputFile> input_deps =
140       WriteInputsStampAndGetDep(num_stamp_uses);
141 
142   // The input dependencies will be an order-only dependency. This will cause
143   // Ninja to make sure the inputs are up to date before compiling this source,
144   // but changes in the inputs deps won't cause the file to be recompiled.
145   //
146   // This is important to prevent changes in unrelated actions that are
147   // upstream of this target from causing everything to be recompiled.
148   //
149   // Why can we get away with this rather than using implicit deps ("|", which
150   // will force rebuilds when the inputs change)? For source code, the
151   // computed dependencies of all headers will be computed by the compiler,
152   // which will cause source rebuilds if any "real" upstream dependencies
153   // change.
154   //
155   // If a .cc file is generated by an input dependency, Ninja will see the
156   // input to the build rule doesn't exist, and that it is an output from a
157   // previous step, and build the previous step first. This is a "real"
158   // dependency and doesn't need | or || to express.
159   //
160   // The only case where this rule matters is for the first build where no .d
161   // files exist, and Ninja doesn't know what that source file depends on. In
162   // this case it's sufficient to ensure that the upstream dependencies are
163   // built first. This is exactly what Ninja's order-only dependencies
164   // expresses.
165   //
166   // The order only deps are referenced by each source file compile,
167   // but also by PCH compiles.  The latter are annoying to count, so omit
168   // them here.  This means that binary targets with a single source file
169   // that also use PCH files won't have a stamp file even though having
170   // one would make output ninja file size a bit lower. That's ok, binary
171   // targets with a single source are rare.
172   std::vector<OutputFile> order_only_deps = WriteInputDepsStampAndGetDep(
173       std::vector<const Target*>(), num_stamp_uses);
174 
175   // For GCC builds, the .gch files are not object files, but still need to be
176   // added as explicit dependencies below. The .gch output files are placed in
177   // |pch_other_files|. This is to prevent linking against them.
178   std::vector<OutputFile> pch_obj_files;
179   std::vector<OutputFile> pch_other_files;
180   WritePCHCommands(input_deps, order_only_deps, &pch_obj_files,
181                    &pch_other_files);
182   std::vector<OutputFile>* pch_files =
183       !pch_obj_files.empty() ? &pch_obj_files : &pch_other_files;
184 
185   // Treat all pch output files as explicit dependencies of all
186   // compiles that support them. Some notes:
187   //
188   //  - On Windows, the .pch file is the input to the compile, not the
189   //    precompiled header's corresponding object file that we're using here.
190   //    But Ninja's depslog doesn't support multiple outputs from the
191   //    precompiled header compile step (it outputs both the .pch file and a
192   //    corresponding .obj file). So we consistently list the .obj file and the
193   //    .pch file we really need comes along with it.
194   //
195   //  - GCC .gch files are not object files, therefore they are not added to the
196   //    object file list.
197   std::vector<OutputFile> obj_files;
198   std::vector<OutputFile> extra_files;
199   std::vector<SourceFile> other_files;
200   std::vector<OutputFile>* stamp_files = &obj_files;  // default
201   if (!target_->source_types_used().SwiftSourceUsed()) {
202     WriteSources(*pch_files, input_deps, order_only_deps, module_dep_info,
203                  &obj_files, &other_files);
204   } else {
205     stamp_files = &extra_files;  // Swift generates more than object files
206     WriteSwiftSources(input_deps, order_only_deps, &obj_files, &extra_files);
207   }
208 
209   // Link all MSVC pch object files. The vector will be empty on GCC toolchains.
210   obj_files.insert(obj_files.end(), pch_obj_files.begin(), pch_obj_files.end());
211   if (!CheckForDuplicateObjectFiles(obj_files))
212     return;
213 
214   if (target_->output_type() == Target::SOURCE_SET) {
215     WriteSourceSetStamp(*stamp_files);
216 #ifndef NDEBUG
217     // Verify that the function that separately computes a source set's object
218     // files match the object files just computed.
219     UniqueVector<OutputFile> computed_obj;
220     AddSourceSetFiles(target_, &computed_obj);
221     DCHECK_EQ(obj_files.size(), computed_obj.size());
222     for (const auto& obj : obj_files)
223       DCHECK(computed_obj.Contains(obj));
224 #endif
225   } else {
226     WriteLinkerStuff(obj_files, other_files, input_deps);
227   }
228 }
229 
WriteCompilerVars(const std::vector<ModuleDep> & module_dep_info)230 void NinjaCBinaryTargetWriter::WriteCompilerVars(
231     const std::vector<ModuleDep>& module_dep_info) {
232   const SubstitutionBits& subst = target_->toolchain()->substitution_bits();
233 
234   WriteCCompilerVars(subst, /*indent=*/false,
235                      /*respect_source_types_used=*/true);
236 
237   if (!module_dep_info.empty()) {
238     // TODO(scottmg): Currently clang modules only working for C++.
239     if (target_->source_types_used().Get(SourceFile::SOURCE_CPP) ||
240         target_->source_types_used().Get(SourceFile::SOURCE_MODULEMAP)) {
241       WriteModuleDepsSubstitution(&CSubstitutionModuleDeps, module_dep_info,
242                                   true);
243       WriteModuleDepsSubstitution(&CSubstitutionModuleDepsNoSelf,
244                                   module_dep_info, false);
245     }
246   }
247 
248   WriteSharedVars(subst);
249 }
250 
WriteModuleDepsSubstitution(const Substitution * substitution,const std::vector<ModuleDep> & module_dep_info,bool include_self)251 void NinjaCBinaryTargetWriter::WriteModuleDepsSubstitution(
252     const Substitution* substitution,
253     const std::vector<ModuleDep>& module_dep_info,
254     bool include_self) {
255   if (target_->toolchain()->substitution_bits().used.count(substitution)) {
256     EscapeOptions options;
257     options.mode = ESCAPE_NINJA_COMMAND;
258 
259     out_ << substitution->ninja_name << " = -Xclang ";
260     EscapeStringToStream(out_, "-fmodules-embed-all-files", options);
261 
262     for (const auto& module_dep : module_dep_info) {
263       if (!module_dep.is_self || include_self) {
264         out_ << " ";
265         EscapeStringToStream(out_, "-fmodule-file=", options);
266         path_output_.WriteFile(out_, module_dep.pcm);
267       }
268     }
269 
270     out_ << std::endl;
271   }
272 }
273 
WritePCHCommands(const std::vector<OutputFile> & input_deps,const std::vector<OutputFile> & order_only_deps,std::vector<OutputFile> * object_files,std::vector<OutputFile> * other_files)274 void NinjaCBinaryTargetWriter::WritePCHCommands(
275     const std::vector<OutputFile>& input_deps,
276     const std::vector<OutputFile>& order_only_deps,
277     std::vector<OutputFile>* object_files,
278     std::vector<OutputFile>* other_files) {
279   if (!target_->config_values().has_precompiled_headers())
280     return;
281 
282   const CTool* tool_c = target_->toolchain()->GetToolAsC(CTool::kCToolCc);
283   if (tool_c && tool_c->precompiled_header_type() != CTool::PCH_NONE &&
284       target_->source_types_used().Get(SourceFile::SOURCE_C)) {
285     WritePCHCommand(&CSubstitutionCFlagsC, CTool::kCToolCc,
286                     tool_c->precompiled_header_type(), input_deps,
287                     order_only_deps, object_files, other_files);
288   }
289   const CTool* tool_cxx = target_->toolchain()->GetToolAsC(CTool::kCToolCxx);
290   if (tool_cxx && tool_cxx->precompiled_header_type() != CTool::PCH_NONE &&
291       target_->source_types_used().Get(SourceFile::SOURCE_CPP)) {
292     WritePCHCommand(&CSubstitutionCFlagsCc, CTool::kCToolCxx,
293                     tool_cxx->precompiled_header_type(), input_deps,
294                     order_only_deps, object_files, other_files);
295   }
296 
297   const CTool* tool_objc = target_->toolchain()->GetToolAsC(CTool::kCToolObjC);
298   if (tool_objc && tool_objc->precompiled_header_type() == CTool::PCH_GCC &&
299       target_->source_types_used().Get(SourceFile::SOURCE_M)) {
300     WritePCHCommand(&CSubstitutionCFlagsObjC, CTool::kCToolObjC,
301                     tool_objc->precompiled_header_type(), input_deps,
302                     order_only_deps, object_files, other_files);
303   }
304 
305   const CTool* tool_objcxx =
306       target_->toolchain()->GetToolAsC(CTool::kCToolObjCxx);
307   if (tool_objcxx && tool_objcxx->precompiled_header_type() == CTool::PCH_GCC &&
308       target_->source_types_used().Get(SourceFile::SOURCE_MM)) {
309     WritePCHCommand(&CSubstitutionCFlagsObjCc, CTool::kCToolObjCxx,
310                     tool_objcxx->precompiled_header_type(), input_deps,
311                     order_only_deps, object_files, other_files);
312   }
313 }
314 
WritePCHCommand(const Substitution * flag_type,const char * tool_name,CTool::PrecompiledHeaderType header_type,const std::vector<OutputFile> & input_deps,const std::vector<OutputFile> & order_only_deps,std::vector<OutputFile> * object_files,std::vector<OutputFile> * other_files)315 void NinjaCBinaryTargetWriter::WritePCHCommand(
316     const Substitution* flag_type,
317     const char* tool_name,
318     CTool::PrecompiledHeaderType header_type,
319     const std::vector<OutputFile>& input_deps,
320     const std::vector<OutputFile>& order_only_deps,
321     std::vector<OutputFile>* object_files,
322     std::vector<OutputFile>* other_files) {
323   switch (header_type) {
324     case CTool::PCH_MSVC:
325       WriteWindowsPCHCommand(flag_type, tool_name, input_deps, order_only_deps,
326                              object_files);
327       break;
328     case CTool::PCH_GCC:
329       WriteGCCPCHCommand(flag_type, tool_name, input_deps, order_only_deps,
330                          other_files);
331       break;
332     case CTool::PCH_NONE:
333       NOTREACHED() << "Cannot write a PCH command with no PCH header type";
334       break;
335   }
336 }
337 
WriteGCCPCHCommand(const Substitution * flag_type,const char * tool_name,const std::vector<OutputFile> & input_deps,const std::vector<OutputFile> & order_only_deps,std::vector<OutputFile> * gch_files)338 void NinjaCBinaryTargetWriter::WriteGCCPCHCommand(
339     const Substitution* flag_type,
340     const char* tool_name,
341     const std::vector<OutputFile>& input_deps,
342     const std::vector<OutputFile>& order_only_deps,
343     std::vector<OutputFile>* gch_files) {
344   // Compute the pch output file (it will be language-specific).
345   std::vector<OutputFile> outputs;
346   GetPCHOutputFiles(target_, tool_name, &outputs);
347   if (outputs.empty())
348     return;
349 
350   gch_files->insert(gch_files->end(), outputs.begin(), outputs.end());
351 
352   std::vector<OutputFile> extra_deps;
353   std::copy(input_deps.begin(), input_deps.end(),
354             std::back_inserter(extra_deps));
355 
356   // Build line to compile the file.
357   WriteCompilerBuildLine({target_->config_values().precompiled_source()},
358                          extra_deps, order_only_deps, tool_name, outputs);
359 
360   // This build line needs a custom language-specific flags value. Rule-specific
361   // variables are just indented underneath the rule line.
362   out_ << "  " << flag_type->ninja_name << " =";
363 
364   // Each substitution flag is overwritten in the target rule to replace the
365   // implicitly generated -include flag with the -x <header lang> flag required
366   // for .gch targets.
367   EscapeOptions opts = GetFlagOptions();
368   if (tool_name == CTool::kCToolCc) {
369     RecursiveTargetConfigStringsToStream(kRecursiveWriterKeepDuplicates,
370                                          target_, &ConfigValues::cflags_c, opts,
371                                          out_);
372   } else if (tool_name == CTool::kCToolCxx) {
373     RecursiveTargetConfigStringsToStream(kRecursiveWriterKeepDuplicates,
374                                          target_, &ConfigValues::cflags_cc,
375                                          opts, out_);
376   } else if (tool_name == CTool::kCToolObjC) {
377     RecursiveTargetConfigStringsToStream(kRecursiveWriterKeepDuplicates,
378                                          target_, &ConfigValues::cflags_objc,
379                                          opts, out_);
380   } else if (tool_name == CTool::kCToolObjCxx) {
381     RecursiveTargetConfigStringsToStream(kRecursiveWriterKeepDuplicates,
382                                          target_, &ConfigValues::cflags_objcc,
383                                          opts, out_);
384   }
385 
386   // Append the command to specify the language of the .gch file.
387   out_ << " -x " << GetPCHLangForToolType(tool_name);
388 
389   // Write two blank lines to help separate the PCH build lines from the
390   // regular source build lines.
391   out_ << std::endl << std::endl;
392 }
393 
WriteWindowsPCHCommand(const Substitution * flag_type,const char * tool_name,const std::vector<OutputFile> & input_deps,const std::vector<OutputFile> & order_only_deps,std::vector<OutputFile> * object_files)394 void NinjaCBinaryTargetWriter::WriteWindowsPCHCommand(
395     const Substitution* flag_type,
396     const char* tool_name,
397     const std::vector<OutputFile>& input_deps,
398     const std::vector<OutputFile>& order_only_deps,
399     std::vector<OutputFile>* object_files) {
400   // Compute the pch output file (it will be language-specific).
401   std::vector<OutputFile> outputs;
402   GetPCHOutputFiles(target_, tool_name, &outputs);
403   if (outputs.empty())
404     return;
405 
406   object_files->insert(object_files->end(), outputs.begin(), outputs.end());
407 
408   std::vector<OutputFile> extra_deps;
409   std::copy(input_deps.begin(), input_deps.end(),
410             std::back_inserter(extra_deps));
411 
412   // Build line to compile the file.
413   WriteCompilerBuildLine({target_->config_values().precompiled_source()},
414                          extra_deps, order_only_deps, tool_name, outputs);
415 
416   // This build line needs a custom language-specific flags value. Rule-specific
417   // variables are just indented underneath the rule line.
418   out_ << "  " << flag_type->ninja_name << " =";
419 
420   // Append the command to generate the .pch file.
421   // This adds the value to the existing flag instead of overwriting it.
422   out_ << " ${" << flag_type->ninja_name << "}";
423   out_ << " /Yc" << target_->config_values().precompiled_header();
424 
425   // Write two blank lines to help separate the PCH build lines from the
426   // regular source build lines.
427   out_ << std::endl << std::endl;
428 }
429 
WriteSources(const std::vector<OutputFile> & pch_deps,const std::vector<OutputFile> & input_deps,const std::vector<OutputFile> & order_only_deps,const std::vector<ModuleDep> & module_dep_info,std::vector<OutputFile> * object_files,std::vector<SourceFile> * other_files)430 void NinjaCBinaryTargetWriter::WriteSources(
431     const std::vector<OutputFile>& pch_deps,
432     const std::vector<OutputFile>& input_deps,
433     const std::vector<OutputFile>& order_only_deps,
434     const std::vector<ModuleDep>& module_dep_info,
435     std::vector<OutputFile>* object_files,
436     std::vector<SourceFile>* other_files) {
437   DCHECK(!target_->source_types_used().SwiftSourceUsed());
438   object_files->reserve(object_files->size() + target_->sources().size());
439 
440   std::vector<OutputFile> tool_outputs;  // Prevent reallocation in loop.
441   std::vector<OutputFile> deps;
442   for (const auto& source : target_->sources()) {
443     DCHECK_NE(source.GetType(), SourceFile::SOURCE_SWIFT);
444 
445     // Clear the vector but maintain the max capacity to prevent reallocations.
446     deps.resize(0);
447     const char* tool_name = Tool::kToolNone;
448     if (!target_->GetOutputFilesForSource(source, &tool_name, &tool_outputs)) {
449       if (source.IsDefType())
450         other_files->push_back(source);
451       continue;  // No output for this source.
452     }
453 
454     std::copy(input_deps.begin(), input_deps.end(), std::back_inserter(deps));
455 
456     if (tool_name != Tool::kToolNone) {
457       // Only include PCH deps that correspond to the tool type, for instance,
458       // do not specify target_name.precompile.cc.obj (a CXX PCH file) as a dep
459       // for the output of a C tool type.
460       //
461       // This makes the assumption that pch_deps only contains pch output files
462       // with the naming scheme specified in GetWindowsPCHObjectExtension or
463       // GetGCCPCHOutputExtension.
464       const CTool* tool = target_->toolchain()->GetToolAsC(tool_name);
465       if (tool->precompiled_header_type() != CTool::PCH_NONE) {
466         for (const auto& dep : pch_deps) {
467           const std::string& output_value = dep.value();
468           size_t extension_offset = FindExtensionOffset(output_value);
469           if (extension_offset == std::string::npos)
470             continue;
471           std::string output_extension;
472           if (tool->precompiled_header_type() == CTool::PCH_MSVC) {
473             output_extension = GetWindowsPCHObjectExtension(
474                 tool_name, output_value.substr(extension_offset - 1));
475           } else if (tool->precompiled_header_type() == CTool::PCH_GCC) {
476             output_extension = GetGCCPCHOutputExtension(tool_name);
477           }
478           if (output_value.compare(
479                   output_value.size() - output_extension.size(),
480                   output_extension.size(), output_extension) == 0) {
481             deps.push_back(dep);
482           }
483         }
484       }
485 
486       for (const auto& module_dep : module_dep_info) {
487         if (tool_outputs[0] != module_dep.pcm)
488           deps.push_back(module_dep.pcm);
489       }
490 
491       WriteCompilerBuildLine({source}, deps, order_only_deps, tool_name,
492                              tool_outputs);
493       WritePool(out_);
494     }
495 
496     // It's theoretically possible for a compiler to produce more than one
497     // output, but we'll only link to the first output.
498     if (!source.IsModuleMapType()) {
499       object_files->push_back(tool_outputs[0]);
500     }
501   }
502 
503   out_ << std::endl;
504 }
505 
WriteSwiftSources(const std::vector<OutputFile> & input_deps,const std::vector<OutputFile> & order_only_deps,std::vector<OutputFile> * object_files,std::vector<OutputFile> * output_files)506 void NinjaCBinaryTargetWriter::WriteSwiftSources(
507     const std::vector<OutputFile>& input_deps,
508     const std::vector<OutputFile>& order_only_deps,
509     std::vector<OutputFile>* object_files,
510     std::vector<OutputFile>* output_files) {
511   DCHECK(target_->builds_swift_module());
512   target_->swift_values().GetOutputs(target_, output_files);
513 
514   const BuildSettings* build_settings = settings_->build_settings();
515   for (const OutputFile& output : *output_files) {
516     const SourceFile output_as_source = output.AsSourceFile(build_settings);
517     if (output_as_source.IsObjectType()) {
518       object_files->push_back(output);
519     }
520   }
521 
522   UniqueVector<OutputFile> swift_order_only_deps;
523   swift_order_only_deps.reserve(order_only_deps.size());
524   swift_order_only_deps.Append(order_only_deps.begin(), order_only_deps.end());
525 
526   for (const Target* swiftmodule :
527        resolved().GetSwiftModuleDependencies(target_)) {
528     swift_order_only_deps.push_back(swiftmodule->dependency_output_file());
529   }
530 
531   const Tool* tool = target_->swift_values().GetTool(target_);
532   WriteCompilerBuildLine(target_->sources(), input_deps,
533                          swift_order_only_deps.vector(), tool->name(),
534                          *output_files,
535                          /*can_write_source_info=*/false,
536                          /*restat_output_allowed=*/true);
537 
538   out_ << std::endl;
539 }
540 
WriteSourceSetStamp(const std::vector<OutputFile> & object_files)541 void NinjaCBinaryTargetWriter::WriteSourceSetStamp(
542     const std::vector<OutputFile>& object_files) {
543   // The stamp rule for source sets is generally not used, since targets that
544   // depend on this will reference the object files directly. However, writing
545   // this rule allows the user to type the name of the target and get a build
546   // which can be convenient for development.
547   ClassifiedDeps classified_deps = GetClassifiedDeps();
548 
549   // The classifier should never put extra object files in a source sets: any
550   // source sets that we depend on should appear in our non-linkable deps
551   // instead.
552   DCHECK(classified_deps.extra_object_files.empty());
553 
554   std::vector<OutputFile> order_only_deps;
555   for (auto* dep : classified_deps.non_linkable_deps)
556     order_only_deps.push_back(dep->dependency_output_file());
557 
558   WriteStampForTarget(object_files, order_only_deps);
559 }
560 
WriteLinkerStuff(const std::vector<OutputFile> & object_files,const std::vector<SourceFile> & other_files,const std::vector<OutputFile> & input_deps)561 void NinjaCBinaryTargetWriter::WriteLinkerStuff(
562     const std::vector<OutputFile>& object_files,
563     const std::vector<SourceFile>& other_files,
564     const std::vector<OutputFile>& input_deps) {
565   std::vector<OutputFile> output_files;
566   SubstitutionWriter::ApplyListToLinkerAsOutputFile(
567       target_, tool_, tool_->outputs(), &output_files);
568 
569   out_ << "build";
570   WriteOutputs(output_files);
571 
572   out_ << ": " << rule_prefix_
573        << Tool::GetToolTypeForTargetFinalOutput(target_);
574 
575   ClassifiedDeps classified_deps = GetClassifiedDeps();
576 
577   // Object files.
578   path_output_.WriteFiles(out_, object_files);
579   path_output_.WriteFiles(out_, classified_deps.extra_object_files);
580 
581   // Dependencies.
582   std::vector<OutputFile> implicit_deps;
583   std::vector<OutputFile> solibs;
584   for (const Target* cur : classified_deps.linkable_deps) {
585     // All linkable deps should have a link output file.
586     DCHECK(!cur->link_output_file().value().empty())
587         << "No link output file for "
588         << target_->label().GetUserVisibleName(false);
589 
590     if (cur->output_type() == Target::RUST_LIBRARY ||
591         cur->output_type() == Target::RUST_PROC_MACRO)
592       continue;
593 
594     if (cur->dependency_output_file().value() !=
595         cur->link_output_file().value()) {
596       // This is a shared library with separate link and deps files. Save for
597       // later.
598       implicit_deps.push_back(cur->dependency_output_file());
599       solibs.push_back(cur->link_output_file());
600     } else {
601       // Normal case, just link to this target.
602       out_ << " ";
603       path_output_.WriteFile(out_, cur->link_output_file());
604     }
605   }
606 
607   const SourceFile* optional_def_file = nullptr;
608   if (!other_files.empty()) {
609     for (const SourceFile& src_file : other_files) {
610       if (src_file.IsDefType()) {
611         optional_def_file = &src_file;
612         implicit_deps.push_back(
613             OutputFile(settings_->build_settings(), src_file));
614         break;  // Only one def file is allowed.
615       }
616     }
617   }
618 
619   // Libraries specified by paths.
620   for (const auto& lib : resolved().GetLinkedLibraries(target_)) {
621     if (lib.is_source_file()) {
622       implicit_deps.push_back(
623           OutputFile(settings_->build_settings(), lib.source_file()));
624     }
625   }
626 
627   // If any target creates a framework bundle, then treat it as an implicit
628   // dependency via the .stamp file. This is a pessimisation as it is not
629   // always necessary to relink the current target if one of the framework
630   // is regenerated, but it ensure that if one of the framework API changes,
631   // any dependent target will relink it (see crbug.com/1037607).
632   for (const Target* dep : classified_deps.framework_deps) {
633     implicit_deps.push_back(dep->dependency_output_file());
634   }
635 
636   // The input dependency is only needed if there are no object files, as the
637   // dependency is normally provided transitively by the source files.
638   std::copy(input_deps.begin(), input_deps.end(),
639             std::back_inserter(implicit_deps));
640 
641   // Any C++ target which depends on a Rust .rlib has to depend on its entire
642   // tree of transitive rlibs found inside the linking target (which excludes
643   // rlibs only depended on inside a shared library dependency).
644   std::vector<OutputFile> transitive_rustlibs;
645   if (target_->IsFinal()) {
646     for (const auto& inherited : resolved().GetInheritedLibraries(target_)) {
647       const Target* dep = inherited.target();
648       if (dep->output_type() == Target::RUST_LIBRARY) {
649         transitive_rustlibs.push_back(dep->dependency_output_file());
650         implicit_deps.push_back(dep->dependency_output_file());
651       }
652     }
653   }
654 
655   // Swift modules from dependencies (and possibly self).
656   std::vector<OutputFile> swiftmodules;
657   if (target_->IsFinal()) {
658     for (const Target* dep : classified_deps.swiftmodule_deps) {
659       swiftmodules.push_back(dep->swift_values().module_output_file());
660       implicit_deps.push_back(dep->swift_values().module_output_file());
661     }
662     if (target_->builds_swift_module()) {
663       swiftmodules.push_back(target_->swift_values().module_output_file());
664       implicit_deps.push_back(target_->swift_values().module_output_file());
665     }
666   }
667 
668   // Append implicit dependencies collected above.
669   if (!implicit_deps.empty()) {
670     out_ << " |";
671     path_output_.WriteFiles(out_, implicit_deps);
672   }
673 
674   // Append data dependencies as order-only dependencies.
675   //
676   // This will include data dependencies and input dependencies (like when
677   // this target depends on an action). Having the data dependencies in this
678   // list ensures that the data is available at runtime when the user builds
679   // this target.
680   //
681   // The action dependencies are not strictly necessary in this case. They
682   // should also have been collected via the input deps stamp that each source
683   // file has for an order-only dependency, and since this target depends on
684   // the sources, there is already an implicit order-only dependency. However,
685   // it's extra work to separate these out and there's no disadvantage to
686   // listing them again.
687   WriteOrderOnlyDependencies(classified_deps.non_linkable_deps);
688 
689   // End of the link "build" line.
690   out_ << std::endl;
691 
692   // The remaining things go in the inner scope of the link line.
693   if (target_->output_type() == Target::EXECUTABLE ||
694       target_->output_type() == Target::SHARED_LIBRARY ||
695       target_->output_type() == Target::LOADABLE_MODULE) {
696     out_ << "  ldflags =";
697     WriteLinkerFlags(out_, tool_, optional_def_file);
698     out_ << std::endl;
699     out_ << "  libs =";
700     WriteLibs(out_, tool_);
701     out_ << std::endl;
702     out_ << "  frameworks =";
703     WriteFrameworks(out_, tool_);
704     out_ << std::endl;
705     out_ << "  swiftmodules =";
706     WriteSwiftModules(out_, tool_, swiftmodules);
707     out_ << std::endl;
708   } else if (target_->output_type() == Target::STATIC_LIBRARY) {
709     out_ << "  arflags =";
710     RecursiveTargetConfigStringsToStream(kRecursiveWriterKeepDuplicates,
711                                          target_, &ConfigValues::arflags,
712                                          GetFlagOptions(), out_);
713     out_ << std::endl;
714   }
715   WriteOutputSubstitutions();
716   WriteLibsList("solibs", solibs);
717   WriteLibsList("rlibs", transitive_rustlibs);
718 
719   const Toolchain *toolchain = target_->toolchain();
720   if (toolchain == nullptr || toolchain->GetToolAsC(CTool::kCToolSolink) == nullptr) {
721     return;
722   }
723   int toolchain_whole_status = toolchain->GetToolAsC(CTool::kCToolSolink)->toolchain_whole_status();
724   if (toolchain_whole_status == 0) {
725     WriteWholeArchive(toolchain_whole_status);
726   } else if (toolchain_whole_status == 1){
727     WriteNoWholeArchive(toolchain_whole_status);
728   }
729   return;
730 }
731 
WriteWholeArchive(int toolchain_whole_status)732 void NinjaCBinaryTargetWriter::WriteWholeArchive(int toolchain_whole_status) {
733 
734   if (target_->whole_archive_deps().size() == 0) {
735     return;
736   }
737   std::string result;
738   for (const auto& file : target_->whole_archive_deps()) {
739     std::string label = file.label.GetUserVisibleName(false);
740     size_t sep = label.find(":");
741     if (sep == std::string::npos) {
742       continue;
743     }
744     result +=  label.substr(sep + 1) + ".a";
745     result += " ";
746   }
747 
748   out_ << "  toolchain_whole_status = "
749        << toolchain_whole_status;
750   out_ << std::endl;
751 
752   out_ << "  whole-archive = "
753        << result;
754   out_ << std::endl;
755 }
756 
WriteNoWholeArchive(int toolchain_whole_status)757 void NinjaCBinaryTargetWriter::WriteNoWholeArchive(int toolchain_whole_status) {
758   if (target_->no_whole_archive_deps().size() == 0) {
759     return;
760   }
761   std::string result;
762   for (const auto& file : target_->no_whole_archive_deps()) {
763     std::string label = file.label.GetUserVisibleName(false);
764     size_t sep = label.find(":");
765     if (sep == std::string::npos) {
766       continue;
767     }
768     result +=  label.substr(sep + 1) + ".a";
769     result += " ";
770   }
771 
772   out_ << "  toolchain_whole_status = "
773        << toolchain_whole_status;
774   out_ << std::endl;
775 
776   out_ << "  no-whole-archive = "
777        << result;
778   out_ << std::endl;
779 }
780 
WriteOutputSubstitutions()781 void NinjaCBinaryTargetWriter::WriteOutputSubstitutions() {
782   out_ << "  output_extension = "
783        << SubstitutionWriter::GetLinkerSubstitution(
784               target_, tool_, &SubstitutionOutputExtension);
785   out_ << std::endl;
786   out_ << "  output_dir = "
787        << SubstitutionWriter::GetLinkerSubstitution(target_, tool_,
788                                                     &SubstitutionOutputDir);
789   out_ << std::endl;
790 }
791 
WriteLibsList(const std::string & label,const std::vector<OutputFile> & libs)792 void NinjaCBinaryTargetWriter::WriteLibsList(
793     const std::string& label,
794     const std::vector<OutputFile>& libs) {
795   if (libs.empty())
796     return;
797 
798   out_ << "  " << label << " =";
799   PathOutput output(path_output_.current_dir(),
800                     settings_->build_settings()->root_path_utf8(),
801                     ESCAPE_NINJA_COMMAND);
802   output.WriteFiles(out_, libs);
803   out_ << std::endl;
804 }
805 
WriteOrderOnlyDependencies(const UniqueVector<const Target * > & non_linkable_deps)806 void NinjaCBinaryTargetWriter::WriteOrderOnlyDependencies(
807     const UniqueVector<const Target*>& non_linkable_deps) {
808   if (!non_linkable_deps.empty()) {
809     out_ << " ||";
810 
811     // Non-linkable targets.
812     for (auto* non_linkable_dep : non_linkable_deps) {
813       out_ << " ";
814       path_output_.WriteFile(out_, non_linkable_dep->dependency_output_file());
815     }
816   }
817 }
818 
CheckForDuplicateObjectFiles(const std::vector<OutputFile> & files) const819 bool NinjaCBinaryTargetWriter::CheckForDuplicateObjectFiles(
820     const std::vector<OutputFile>& files) const {
821   std::set<std::string> set;
822   for (const auto& file : files) {
823     if (!set.insert(file.value()).second) {
824       Err err(
825           target_->defined_from(), "Duplicate object file",
826           "The target " + target_->label().GetUserVisibleName(false) +
827               "\ngenerates two object files with the same name:\n  " +
828               file.value() +
829               "\n"
830               "\n"
831               "It could be you accidentally have a file listed twice in the\n"
832               "sources. Or, depending on how your toolchain maps sources to\n"
833               "object files, two source files with the same name in different\n"
834               "directories could map to the same object file.\n"
835               "\n"
836               "In the latter case, either rename one of the files or move one "
837               "of\n"
838               "the sources to a separate source_set to avoid them both being "
839               "in\n"
840               "the same target.");
841       g_scheduler->FailWithError(err);
842       return false;
843     }
844   }
845   return true;
846 }
847