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