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