• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "gn/ninja_binary_target_writer.h"
6 
7 #include <sstream>
8 
9 #include "base/strings/string_util.h"
10 #include "gn/config_values_extractors.h"
11 #include "gn/deps_iterator.h"
12 #include "gn/filesystem_utils.h"
13 #include "gn/general_tool.h"
14 #include "gn/ninja_c_binary_target_writer.h"
15 #include "gn/ninja_rust_binary_target_writer.h"
16 #include "gn/ninja_target_command_util.h"
17 #include "gn/ninja_utils.h"
18 #include "gn/pool.h"
19 #include "gn/settings.h"
20 #include "gn/string_utils.h"
21 #include "gn/substitution_writer.h"
22 #include "gn/target.h"
23 #include "gn/variables.h"
24 
25 namespace {
26 
27 // Returns the proper escape options for writing compiler and linker flags.
GetFlagOptions()28 EscapeOptions GetFlagOptions() {
29   EscapeOptions opts;
30   opts.mode = ESCAPE_NINJA_COMMAND;
31   return opts;
32 }
33 
34 }  // namespace
35 
NinjaBinaryTargetWriter(const Target * target,std::ostream & out)36 NinjaBinaryTargetWriter::NinjaBinaryTargetWriter(const Target* target,
37                                                  std::ostream& out)
38     : NinjaTargetWriter(target, out),
39       rule_prefix_(GetNinjaRulePrefixForToolchain(settings_)) {}
40 
41 NinjaBinaryTargetWriter::~NinjaBinaryTargetWriter() = default;
42 
Run()43 void NinjaBinaryTargetWriter::Run() {
44   if (target_->source_types_used().RustSourceUsed()) {
45     NinjaRustBinaryTargetWriter writer(target_, out_);
46     writer.SetResolvedTargetData(GetResolvedTargetData());
47     writer.SetNinjaOutputs(ninja_outputs_);
48     writer.Run();
49     return;
50   }
51 
52   NinjaCBinaryTargetWriter writer(target_, out_);
53   writer.SetResolvedTargetData(GetResolvedTargetData());
54   writer.SetNinjaOutputs(ninja_outputs_);
55   writer.Run();
56 }
57 
WriteInputsStampAndGetDep(size_t num_stamp_uses) const58 std::vector<OutputFile> NinjaBinaryTargetWriter::WriteInputsStampAndGetDep(
59     size_t num_stamp_uses) const {
60   CHECK(target_->toolchain()) << "Toolchain not set on target "
61                               << target_->label().GetUserVisibleName(true);
62 
63   UniqueVector<const SourceFile*> inputs;
64   for (ConfigValuesIterator iter(target_); !iter.done(); iter.Next()) {
65     for (const auto& input : iter.cur().inputs()) {
66       inputs.push_back(&input);
67     }
68   }
69 
70   if (inputs.size() == 0)
71     return std::vector<OutputFile>();  // No inputs
72 
73   // If we only have one input, return it directly instead of writing a stamp
74   // file for it.
75   if (inputs.size() == 1) {
76     return std::vector<OutputFile>{
77         OutputFile(settings_->build_settings(), *inputs[0])};
78   }
79 
80   std::vector<OutputFile> outs;
81   for (const SourceFile* source : inputs)
82     outs.push_back(OutputFile(settings_->build_settings(), *source));
83 
84   // If there are multiple inputs, but the stamp file would be referenced only
85   // once, don't write it but depend on the inputs directly.
86   if (num_stamp_uses == 1u)
87     return outs;
88 
89   // Make a stamp file.
90   OutputFile stamp_file =
91       GetBuildDirForTargetAsOutputFile(target_, BuildDirType::OBJ);
92   stamp_file.value().append(target_->label().name());
93   stamp_file.value().append(".inputs.stamp");
94 
95   out_ << "build ";
96   WriteOutput(stamp_file);
97 
98   out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
99        << GeneralTool::kGeneralToolStamp;
100 
101   // File inputs.
102   for (const auto* input : inputs) {
103     out_ << " ";
104     path_output_.WriteFile(out_, *input);
105   }
106 
107   out_ << std::endl;
108   return {stamp_file};
109 }
110 
111 NinjaBinaryTargetWriter::ClassifiedDeps
GetClassifiedDeps() const112 NinjaBinaryTargetWriter::GetClassifiedDeps() const {
113   ClassifiedDeps classified_deps;
114 
115   const auto& target_deps = resolved().GetTargetDeps(target_);
116 
117   // Normal public/private deps.
118   for (const Target* dep : target_deps.linked_deps()) {
119     ClassifyDependency(dep, &classified_deps);
120   }
121 
122   // Inherited libraries.
123   for (const auto& inherited : resolved().GetInheritedLibraries(target_)) {
124     ClassifyDependency(inherited.target(), &classified_deps);
125   }
126 
127   // Data deps.
128   for (const Target* data_dep : target_deps.data_deps())
129     classified_deps.non_linkable_deps.push_back(data_dep);
130 
131   return classified_deps;
132 }
133 
ClassifyDependency(const Target * dep,ClassifiedDeps * classified_deps) const134 void NinjaBinaryTargetWriter::ClassifyDependency(
135     const Target* dep,
136     ClassifiedDeps* classified_deps) const {
137   // Only the following types of outputs have libraries linked into them:
138   //  EXECUTABLE
139   //  SHARED_LIBRARY
140   //  _complete_ STATIC_LIBRARY
141   //
142   // Child deps of intermediate static libraries get pushed up the
143   // dependency tree until one of these is reached, and source sets
144   // don't link at all.
145   bool can_link_libs = target_->IsFinal();
146 
147   if (can_link_libs && dep->builds_swift_module())
148     classified_deps->swiftmodule_deps.push_back(dep);
149 
150   if (target_->source_types_used().RustSourceUsed() &&
151       (target_->output_type() == Target::RUST_LIBRARY ||
152        target_->output_type() == Target::STATIC_LIBRARY) &&
153       dep->IsLinkable()) {
154     // Rust libraries and static libraries aren't final, but need to have the
155     // link lines of all transitive deps specified.
156     classified_deps->linkable_deps.push_back(dep);
157   } else if (dep->output_type() == Target::SOURCE_SET ||
158              // If a complete static library depends on an incomplete static
159              // library, manually link in the object files of the dependent
160              // library as if it were a source set. This avoids problems with
161              // braindead tools such as ar which don't properly link dependent
162              // static libraries.
163              (target_->complete_static_lib() &&
164               (dep->output_type() == Target::STATIC_LIBRARY &&
165                !dep->complete_static_lib()))) {
166     // Source sets have their object files linked into final targets
167     // (shared libraries, executables, loadable modules, and complete static
168     // libraries). Intermediate static libraries and other source sets
169     // just forward the dependency, otherwise the files in the source
170     // set can easily get linked more than once which will cause
171     // multiple definition errors.
172     if (can_link_libs)
173       AddSourceSetFiles(dep, &classified_deps->extra_object_files);
174 
175     // Add the source set itself as a non-linkable dependency on the current
176     // target. This will make sure that anything the source set's stamp file
177     // depends on (like data deps) are also built before the current target
178     // can be complete. Otherwise, these will be skipped since this target
179     // will depend only on the source set's object files.
180     classified_deps->non_linkable_deps.push_back(dep);
181   } else if (target_->complete_static_lib() && dep->IsFinal()) {
182     classified_deps->non_linkable_deps.push_back(dep);
183   } else if (can_link_libs && dep->IsLinkable()) {
184     classified_deps->linkable_deps.push_back(dep);
185   } else if (dep->output_type() == Target::CREATE_BUNDLE &&
186              dep->bundle_data().is_framework()) {
187     classified_deps->framework_deps.push_back(dep);
188   } else {
189     classified_deps->non_linkable_deps.push_back(dep);
190   }
191 }
192 
AddSourceSetFiles(const Target * source_set,UniqueVector<OutputFile> * obj_files) const193 void NinjaBinaryTargetWriter::AddSourceSetFiles(
194     const Target* source_set,
195     UniqueVector<OutputFile>* obj_files) const {
196   std::vector<OutputFile> tool_outputs;  // Prevent allocation in loop.
197 
198   // Compute object files for all sources. Only link the first output from
199   // the tool if there are more than one.
200   for (const auto& source : source_set->sources()) {
201     const char* tool_name = Tool::kToolNone;
202     if (source_set->GetOutputFilesForSource(source, &tool_name, &tool_outputs))
203       obj_files->push_back(tool_outputs[0]);
204   }
205 
206   // Swift files may generate one object file per module or one per source file
207   // depending on how the compiler is invoked (whole module optimization).
208   if (source_set->source_types_used().SwiftSourceUsed()) {
209     std::vector<OutputFile> outputs;
210     source_set->swift_values().GetOutputs(source_set, &outputs);
211 
212     for (const OutputFile& output : outputs) {
213       SourceFile output_as_source =
214           output.AsSourceFile(source_set->settings()->build_settings());
215       if (output_as_source.IsObjectType()) {
216         obj_files->push_back(output);
217       }
218     }
219   }
220 
221   // Add MSVC precompiled header object files. GCC .gch files are not object
222   // files so they are omitted.
223   if (source_set->config_values().has_precompiled_headers()) {
224     if (source_set->source_types_used().Get(SourceFile::SOURCE_C)) {
225       const CTool* tool = source_set->toolchain()->GetToolAsC(CTool::kCToolCc);
226       if (tool && tool->precompiled_header_type() == CTool::PCH_MSVC) {
227         GetPCHOutputFiles(source_set, CTool::kCToolCc, &tool_outputs);
228         obj_files->Append(tool_outputs.begin(), tool_outputs.end());
229       }
230     }
231     if (source_set->source_types_used().Get(SourceFile::SOURCE_CPP)) {
232       const CTool* tool = source_set->toolchain()->GetToolAsC(CTool::kCToolCxx);
233       if (tool && tool->precompiled_header_type() == CTool::PCH_MSVC) {
234         GetPCHOutputFiles(source_set, CTool::kCToolCxx, &tool_outputs);
235         obj_files->Append(tool_outputs.begin(), tool_outputs.end());
236       }
237     }
238     if (source_set->source_types_used().Get(SourceFile::SOURCE_M)) {
239       const CTool* tool =
240           source_set->toolchain()->GetToolAsC(CTool::kCToolObjC);
241       if (tool && tool->precompiled_header_type() == CTool::PCH_MSVC) {
242         GetPCHOutputFiles(source_set, CTool::kCToolObjC, &tool_outputs);
243         obj_files->Append(tool_outputs.begin(), tool_outputs.end());
244       }
245     }
246     if (source_set->source_types_used().Get(SourceFile::SOURCE_MM)) {
247       const CTool* tool =
248           source_set->toolchain()->GetToolAsC(CTool::kCToolObjCxx);
249       if (tool && tool->precompiled_header_type() == CTool::PCH_MSVC) {
250         GetPCHOutputFiles(source_set, CTool::kCToolObjCxx, &tool_outputs);
251         obj_files->Append(tool_outputs.begin(), tool_outputs.end());
252       }
253     }
254   }
255 }
256 
WriteCompilerBuildLine(const std::vector<SourceFile> & sources,const std::vector<OutputFile> & extra_deps,const std::vector<OutputFile> & order_only_deps,const char * tool_name,const std::vector<OutputFile> & outputs,bool can_write_source_info,bool restat_output_allowed)257 void NinjaBinaryTargetWriter::WriteCompilerBuildLine(
258     const std::vector<SourceFile>& sources,
259     const std::vector<OutputFile>& extra_deps,
260     const std::vector<OutputFile>& order_only_deps,
261     const char* tool_name,
262     const std::vector<OutputFile>& outputs,
263     bool can_write_source_info,
264     bool restat_output_allowed) {
265   out_ << "build";
266   WriteOutputs(outputs);
267 
268   out_ << ": " << rule_prefix_ << tool_name;
269   path_output_.WriteFiles(out_, sources);
270 
271   if (!extra_deps.empty()) {
272     out_ << " |";
273     path_output_.WriteFiles(out_, extra_deps);
274   }
275 
276   if (!order_only_deps.empty()) {
277     out_ << " ||";
278     path_output_.WriteFiles(out_, order_only_deps);
279   }
280   out_ << std::endl;
281 
282   if (!sources.empty() && can_write_source_info) {
283     out_ << "  " << "source_file_part = " << sources[0].GetName();
284     out_ << std::endl;
285     out_ << "  " << "source_name_part = "
286          << FindFilenameNoExtension(&sources[0].value());
287     out_ << std::endl;
288   }
289 
290   if (restat_output_allowed) {
291     out_ << "  restat = 1" << std::endl;
292   }
293 }
294 
WriteCustomLinkerFlags(std::ostream & out,const Tool * tool)295 void NinjaBinaryTargetWriter::WriteCustomLinkerFlags(std::ostream& out,
296                                                      const Tool* tool) {
297   if (tool->AsC() || (tool->AsRust() && tool->AsRust()->MayLink())) {
298     // First the ldflags from the target and its config.
299     RecursiveTargetConfigStringsToStream(kRecursiveWriterKeepDuplicates,
300                                          target_, &ConfigValues::ldflags,
301                                          GetFlagOptions(), out);
302   }
303 }
304 
WriteLibrarySearchPath(std::ostream & out,const Tool * tool)305 void NinjaBinaryTargetWriter::WriteLibrarySearchPath(std::ostream& out,
306                                                      const Tool* tool) {
307   // Write library search paths that have been recursively pushed
308   // through the dependency tree.
309   const auto& all_lib_dirs = resolved().GetLinkedLibraryDirs(target_);
310   if (!all_lib_dirs.empty()) {
311     // Since we're passing these on the command line to the linker and not
312     // to Ninja, we need to do shell escaping.
313     PathOutput lib_path_output(path_output_.current_dir(),
314                                settings_->build_settings()->root_path_utf8(),
315                                ESCAPE_NINJA_COMMAND);
316     for (size_t i = 0; i < all_lib_dirs.size(); i++) {
317       out << " " << tool->lib_dir_switch();
318       lib_path_output.WriteDir(out, all_lib_dirs[i],
319                                PathOutput::DIR_NO_LAST_SLASH);
320     }
321   }
322 
323   const auto& all_framework_dirs = resolved().GetLinkedFrameworkDirs(target_);
324   if (!all_framework_dirs.empty()) {
325     // Since we're passing these on the command line to the linker and not
326     // to Ninja, we need to do shell escaping.
327     PathOutput framework_path_output(
328         path_output_.current_dir(),
329         settings_->build_settings()->root_path_utf8(), ESCAPE_NINJA_COMMAND);
330     for (size_t i = 0; i < all_framework_dirs.size(); i++) {
331       out << " " << tool->framework_dir_switch();
332       framework_path_output.WriteDir(out, all_framework_dirs[i],
333                                      PathOutput::DIR_NO_LAST_SLASH);
334     }
335   }
336 }
337 
WriteLinkerFlags(std::ostream & out,const Tool * tool,const SourceFile * optional_def_file)338 void NinjaBinaryTargetWriter::WriteLinkerFlags(
339     std::ostream& out,
340     const Tool* tool,
341     const SourceFile* optional_def_file) {
342   // First any ldflags
343   WriteCustomLinkerFlags(out, tool);
344   // Then the library search path
345   WriteLibrarySearchPath(out, tool);
346 
347   if (optional_def_file) {
348     out_ << " /DEF:";
349     path_output_.WriteFile(out, *optional_def_file);
350   }
351 }
352 
WriteLibs(std::ostream & out,const Tool * tool)353 void NinjaBinaryTargetWriter::WriteLibs(std::ostream& out, const Tool* tool) {
354   // Libraries that have been recursively pushed through the dependency tree.
355   // Since we're passing these on the command line to the linker and not
356   // to Ninja, we need to do shell escaping.
357   PathOutput lib_path_output(path_output_.current_dir(),
358                              settings_->build_settings()->root_path_utf8(),
359                              ESCAPE_NINJA_COMMAND);
360   EscapeOptions lib_escape_opts;
361   lib_escape_opts.mode = ESCAPE_NINJA_COMMAND;
362   const auto& all_libs = resolved().GetLinkedLibraries(target_);
363   for (size_t i = 0; i < all_libs.size(); i++) {
364     const LibFile& lib_file = all_libs[i];
365     const std::string& lib_value = lib_file.value();
366     if (lib_file.is_source_file()) {
367       out << " " << tool->linker_arg();
368       lib_path_output.WriteFile(out, lib_file.source_file());
369     } else {
370       out << " " << tool->lib_switch();
371       EscapeStringToStream(out, lib_value, lib_escape_opts);
372     }
373   }
374 }
375 
WriteFrameworks(std::ostream & out,const Tool * tool)376 void NinjaBinaryTargetWriter::WriteFrameworks(std::ostream& out,
377                                               const Tool* tool) {
378   // Frameworks that have been recursively pushed through the dependency tree.
379   FrameworksWriter writer(tool->framework_switch());
380   const auto& all_frameworks = resolved().GetLinkedFrameworks(target_);
381   for (size_t i = 0; i < all_frameworks.size(); i++) {
382     writer(all_frameworks[i], out);
383   }
384 
385   FrameworksWriter weak_writer(tool->weak_framework_switch());
386   const auto& all_weak_frameworks = resolved().GetLinkedWeakFrameworks(target_);
387   for (size_t i = 0; i < all_weak_frameworks.size(); i++) {
388     weak_writer(all_weak_frameworks[i], out);
389   }
390 }
391 
WriteSwiftModules(std::ostream & out,const Tool * tool,const std::vector<OutputFile> & swiftmodules)392 void NinjaBinaryTargetWriter::WriteSwiftModules(
393     std::ostream& out,
394     const Tool* tool,
395     const std::vector<OutputFile>& swiftmodules) {
396   // Since we're passing these on the command line to the linker and not
397   // to Ninja, we need to do shell escaping.
398   PathOutput swiftmodule_path_output(
399       path_output_.current_dir(), settings_->build_settings()->root_path_utf8(),
400       ESCAPE_NINJA_COMMAND);
401 
402   for (const OutputFile& swiftmodule : swiftmodules) {
403     out << " " << tool->swiftmodule_switch();
404     swiftmodule_path_output.WriteFile(out, swiftmodule);
405   }
406 }
407 
WritePool(std::ostream & out)408 void NinjaBinaryTargetWriter::WritePool(std::ostream& out) {
409   if (target_->pool().ptr) {
410     out << "  pool = ";
411     out << target_->pool().ptr->GetNinjaName(
412         settings_->default_toolchain_label());
413     out << std::endl;
414   }
415 }
416