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