• 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() const53 OutputFile NinjaBinaryTargetWriter::WriteInputsStampAndGetDep() const {
54   CHECK(target_->toolchain()) << "Toolchain not set on target "
55                               << target_->label().GetUserVisibleName(true);
56 
57   UniqueVector<const SourceFile*> inputs;
58   for (ConfigValuesIterator iter(target_); !iter.done(); iter.Next()) {
59     for (const auto& input : iter.cur().inputs()) {
60       inputs.push_back(&input);
61     }
62   }
63 
64   if (inputs.size() == 0)
65     return OutputFile();  // No inputs
66 
67   // If we only have one input, return it directly instead of writing a stamp
68   // file for it.
69   if (inputs.size() == 1)
70     return OutputFile(settings_->build_settings(), *inputs[0]);
71 
72   // Make a stamp file.
73   OutputFile stamp_file =
74       GetBuildDirForTargetAsOutputFile(target_, BuildDirType::OBJ);
75   stamp_file.value().append(target_->label().name());
76   stamp_file.value().append(".inputs.stamp");
77 
78   out_ << "build ";
79   path_output_.WriteFile(out_, stamp_file);
80   out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
81        << GeneralTool::kGeneralToolStamp;
82 
83   // File inputs.
84   for (const auto* input : inputs) {
85     out_ << " ";
86     path_output_.WriteFile(out_, *input);
87   }
88 
89   out_ << std::endl;
90   return stamp_file;
91 }
92 
WriteSourceSetStamp(const std::vector<OutputFile> & object_files)93 void NinjaBinaryTargetWriter::WriteSourceSetStamp(
94     const std::vector<OutputFile>& object_files) {
95   // The stamp rule for source sets is generally not used, since targets that
96   // depend on this will reference the object files directly. However, writing
97   // this rule allows the user to type the name of the target and get a build
98   // which can be convenient for development.
99   UniqueVector<OutputFile> extra_object_files;
100   UniqueVector<const Target*> linkable_deps;
101   UniqueVector<const Target*> non_linkable_deps;
102   UniqueVector<const Target*> framework_deps;
103   GetDeps(&extra_object_files, &linkable_deps, &non_linkable_deps,
104           &framework_deps);
105 
106   // The classifier should never put extra object files in a source sets: any
107   // source sets that we depend on should appear in our non-linkable deps
108   // instead.
109   DCHECK(extra_object_files.empty());
110 
111   std::vector<OutputFile> order_only_deps;
112   for (auto* dep : non_linkable_deps)
113     order_only_deps.push_back(dep->dependency_output_file());
114 
115   WriteStampForTarget(object_files, order_only_deps);
116 }
117 
GetDeps(UniqueVector<OutputFile> * extra_object_files,UniqueVector<const Target * > * linkable_deps,UniqueVector<const Target * > * non_linkable_deps,UniqueVector<const Target * > * framework_deps) const118 void NinjaBinaryTargetWriter::GetDeps(
119     UniqueVector<OutputFile>* extra_object_files,
120     UniqueVector<const Target*>* linkable_deps,
121     UniqueVector<const Target*>* non_linkable_deps,
122     UniqueVector<const Target*>* framework_deps) const {
123   // Normal public/private deps.
124   for (const auto& pair : target_->GetDeps(Target::DEPS_LINKED)) {
125     ClassifyDependency(pair.ptr, extra_object_files, linkable_deps,
126                        non_linkable_deps, framework_deps);
127   }
128 
129   // Inherited libraries.
130   for (auto* inherited_target : target_->inherited_libraries().GetOrdered()) {
131     ClassifyDependency(inherited_target, extra_object_files, linkable_deps,
132                        non_linkable_deps, framework_deps);
133   }
134 
135   // Data deps.
136   for (const auto& data_dep_pair : target_->data_deps())
137     non_linkable_deps->push_back(data_dep_pair.ptr);
138 }
139 
ClassifyDependency(const Target * dep,UniqueVector<OutputFile> * extra_object_files,UniqueVector<const Target * > * linkable_deps,UniqueVector<const Target * > * non_linkable_deps,UniqueVector<const Target * > * framework_deps) const140 void NinjaBinaryTargetWriter::ClassifyDependency(
141     const Target* dep,
142     UniqueVector<OutputFile>* extra_object_files,
143     UniqueVector<const Target*>* linkable_deps,
144     UniqueVector<const Target*>* non_linkable_deps,
145     UniqueVector<const Target*>* framework_deps) const {
146   // Only the following types of outputs have libraries linked into them:
147   //  EXECUTABLE
148   //  SHARED_LIBRARY
149   //  _complete_ STATIC_LIBRARY
150   //
151   // Child deps of intermediate static libraries get pushed up the
152   // dependency tree until one of these is reached, and source sets
153   // don't link at all.
154   bool can_link_libs = target_->IsFinal();
155 
156   if (dep->output_type() == Target::SOURCE_SET ||
157       // If a complete static library depends on an incomplete static library,
158       // manually link in the object files of the dependent library as if it
159       // were a source set. This avoids problems with braindead tools such as
160       // ar which don't properly link dependent 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, 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     non_linkable_deps->push_back(dep);
179   } else if (target_->output_type() == Target::RUST_LIBRARY &&
180              dep->IsLinkable()) {
181     // Rust libraries aren't final, but need to have the link lines of all
182     // transitive deps specified.
183     linkable_deps->push_back(dep);
184   } else if (target_->complete_static_lib() && dep->IsFinal()) {
185     non_linkable_deps->push_back(dep);
186   } else if (can_link_libs && dep->IsLinkable()) {
187     linkable_deps->push_back(dep);
188   } else if (dep->output_type() == Target::CREATE_BUNDLE &&
189              dep->bundle_data().is_framework()) {
190     framework_deps->push_back(dep);
191   } else {
192     non_linkable_deps->push_back(dep);
193   }
194 }
195 
AddSourceSetFiles(const Target * source_set,UniqueVector<OutputFile> * obj_files) const196 void NinjaBinaryTargetWriter::AddSourceSetFiles(
197     const Target* source_set,
198     UniqueVector<OutputFile>* obj_files) const {
199   // Just add all sources to the list.
200   for (const auto& source : source_set->sources()) {
201     obj_files->push_back(OutputFile(settings_->build_settings(), source));
202   }
203 }
204 
WriteCompilerBuildLine(const SourceFile & source,const std::vector<OutputFile> & extra_deps,const std::vector<OutputFile> & order_only_deps,const char * tool_name,const std::vector<OutputFile> & outputs)205 void NinjaBinaryTargetWriter::WriteCompilerBuildLine(
206     const SourceFile& source,
207     const std::vector<OutputFile>& extra_deps,
208     const std::vector<OutputFile>& order_only_deps,
209     const char* tool_name,
210     const std::vector<OutputFile>& outputs) {
211   out_ << "build";
212   path_output_.WriteFiles(out_, outputs);
213 
214   out_ << ": " << rule_prefix_ << tool_name;
215   out_ << " ";
216   path_output_.WriteFile(out_, source);
217 
218   if (!extra_deps.empty()) {
219     out_ << " |";
220     path_output_.WriteFiles(out_, extra_deps);
221   }
222 
223   if (!order_only_deps.empty()) {
224     out_ << " ||";
225     path_output_.WriteFiles(out_, order_only_deps);
226   }
227   out_ << std::endl;
228 }
229 
WriteLinkerFlags(std::ostream & out,const Tool * tool,const SourceFile * optional_def_file)230 void NinjaBinaryTargetWriter::WriteLinkerFlags(
231     std::ostream& out,
232     const Tool* tool,
233     const SourceFile* optional_def_file) {
234   if (tool->AsC()) {
235     // First the ldflags from the target and its config.
236     RecursiveTargetConfigStringsToStream(target_, &ConfigValues::ldflags,
237                                        GetFlagOptions(), out);
238   }
239 
240   // Followed by library search paths that have been recursively pushed
241   // through the dependency tree.
242   const OrderedSet<SourceDir> all_lib_dirs = target_->all_lib_dirs();
243   if (!all_lib_dirs.empty()) {
244     // Since we're passing these on the command line to the linker and not
245     // to Ninja, we need to do shell escaping.
246     PathOutput lib_path_output(path_output_.current_dir(),
247                                settings_->build_settings()->root_path_utf8(),
248                                ESCAPE_NINJA_COMMAND);
249     for (size_t i = 0; i < all_lib_dirs.size(); i++) {
250       out << " " << tool->lib_dir_switch();
251       lib_path_output.WriteDir(out, all_lib_dirs[i],
252                                PathOutput::DIR_NO_LAST_SLASH);
253     }
254   }
255 
256   const auto& all_framework_dirs = target_->all_framework_dirs();
257   if (!all_framework_dirs.empty()) {
258     // Since we're passing these on the command line to the linker and not
259     // to Ninja, we need to do shell escaping.
260     PathOutput framework_path_output(
261         path_output_.current_dir(),
262         settings_->build_settings()->root_path_utf8(), ESCAPE_NINJA_COMMAND);
263     for (size_t i = 0; i < all_framework_dirs.size(); i++) {
264       out << " " << tool->framework_dir_switch();
265       framework_path_output.WriteDir(out, all_framework_dirs[i],
266                                      PathOutput::DIR_NO_LAST_SLASH);
267     }
268   }
269 
270   if (optional_def_file) {
271     out_ << " /DEF:";
272     path_output_.WriteFile(out, *optional_def_file);
273   }
274 }
275 
WriteLibs(std::ostream & out,const Tool * tool)276 void NinjaBinaryTargetWriter::WriteLibs(std::ostream& out, const Tool* tool) {
277   // Libraries that have been recursively pushed through the dependency tree.
278   EscapeOptions lib_escape_opts;
279   lib_escape_opts.mode = ESCAPE_NINJA_COMMAND;
280   const OrderedSet<LibFile> all_libs = target_->all_libs();
281   for (size_t i = 0; i < all_libs.size(); i++) {
282     const LibFile& lib_file = all_libs[i];
283     const std::string& lib_value = lib_file.value();
284     std::string_view framework_name = GetFrameworkName(lib_value);
285     if (lib_file.is_source_file()) {
286       out << " " << tool->linker_arg();
287       path_output_.WriteFile(out, lib_file.source_file());
288     } else if (!framework_name.empty()) {
289       // Special-case libraries ending in ".framework" to support Mac: Add the
290       // -framework switch and don't add the extension to the output.
291       // TODO(crbug.com/gn/119): remove this once all code has been ported to
292       // use "frameworks" and "framework_dirs" instead.
293       out << " " << tool->framework_switch();
294       EscapeStringToStream(out, framework_name, lib_escape_opts);
295     } else {
296       out << " " << tool->lib_switch();
297       EscapeStringToStream(out, lib_value, lib_escape_opts);
298     }
299   }
300 }
301 
WriteFrameworks(std::ostream & out,const Tool * tool)302 void NinjaBinaryTargetWriter::WriteFrameworks(std::ostream& out,
303                                               const Tool* tool) {
304   FrameworksWriter writer(tool->framework_switch());
305 
306   // Frameworks that have been recursively pushed through the dependency tree.
307   const auto& all_frameworks = target_->all_frameworks();
308   for (size_t i = 0; i < all_frameworks.size(); i++) {
309     writer(all_frameworks[i], out);
310   }
311 }
312