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