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