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