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