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