1 // Copyright 2019 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_c_binary_target_writer.h"
6
7 #include <stddef.h>
8 #include <string.h>
9
10 #include <cstring>
11 #include <set>
12 #include <sstream>
13
14 #include "base/strings/string_util.h"
15 #include "gn/c_substitution_type.h"
16 #include "gn/config_values_extractors.h"
17 #include "gn/deps_iterator.h"
18 #include "gn/err.h"
19 #include "gn/escape.h"
20 #include "gn/filesystem_utils.h"
21 #include "gn/general_tool.h"
22 #include "gn/ninja_target_command_util.h"
23 #include "gn/ninja_utils.h"
24 #include "gn/pool.h"
25 #include "gn/scheduler.h"
26 #include "gn/settings.h"
27 #include "gn/string_utils.h"
28 #include "gn/substitution_writer.h"
29 #include "gn/target.h"
30
31 struct ModuleDep {
ModuleDepModuleDep32 ModuleDep(const SourceFile* modulemap,
33 const std::string& module_name,
34 const OutputFile& pcm,
35 bool is_self)
36 : modulemap(modulemap),
37 module_name(module_name),
38 pcm(pcm),
39 is_self(is_self) {}
40
41 // The input module.modulemap source file.
42 const SourceFile* modulemap;
43
44 // The internal module name, in GN this is the target's label.
45 std::string module_name;
46
47 // The compiled version of the module.
48 OutputFile pcm;
49
50 // Is this the module for the current target.
51 bool is_self;
52 };
53
54 namespace {
55
56 // Returns the proper escape options for writing compiler and linker flags.
GetFlagOptions()57 EscapeOptions GetFlagOptions() {
58 EscapeOptions opts;
59 opts.mode = ESCAPE_NINJA_COMMAND;
60 return opts;
61 }
62
63 // Returns the language-specific lang recognized by gcc’s -x flag for
64 // precompiled header files.
GetPCHLangForToolType(const char * name)65 const char* GetPCHLangForToolType(const char* name) {
66 if (name == CTool::kCToolCc)
67 return "c-header";
68 if (name == CTool::kCToolCxx)
69 return "c++-header";
70 if (name == CTool::kCToolObjC)
71 return "objective-c-header";
72 if (name == CTool::kCToolObjCxx)
73 return "objective-c++-header";
74 NOTREACHED() << "Not a valid PCH tool type: " << name;
75 return "";
76 }
77
GetModuleMapFromTargetSources(const Target * target)78 const SourceFile* GetModuleMapFromTargetSources(const Target* target) {
79 for (const SourceFile& sf : target->sources()) {
80 if (sf.IsModuleMapType())
81 return &sf;
82 }
83 return nullptr;
84 }
85
GetModuleDepsInformation(const Target * target,const ResolvedTargetData & resolved)86 std::vector<ModuleDep> GetModuleDepsInformation(
87 const Target* target,
88 const ResolvedTargetData& resolved) {
89 std::vector<ModuleDep> ret;
90
91 auto add = [&ret](const Target* t, bool is_self) {
92 const SourceFile* modulemap = GetModuleMapFromTargetSources(t);
93 CHECK(modulemap);
94
95 std::string label;
96 CHECK(SubstitutionWriter::GetTargetSubstitution(
97 t, &SubstitutionLabelNoToolchain, &label));
98
99 const char* tool_type;
100 std::vector<OutputFile> modulemap_outputs;
101 CHECK(
102 t->GetOutputFilesForSource(*modulemap, &tool_type, &modulemap_outputs));
103 // Must be only one .pcm from .modulemap.
104 CHECK(modulemap_outputs.size() == 1u);
105 ret.emplace_back(modulemap, label, modulemap_outputs[0], is_self);
106 };
107
108 if (target->source_types_used().Get(SourceFile::SOURCE_MODULEMAP)) {
109 add(target, true);
110 }
111
112 for (const Target* dep : resolved.GetLinkedDeps(target)) {
113 // Having a .modulemap source means that the dependency is modularized.
114 if (dep->source_types_used().Get(SourceFile::SOURCE_MODULEMAP)) {
115 add(dep, false);
116 }
117 }
118
119 return ret;
120 }
121
122 } // namespace
123
NinjaCBinaryTargetWriter(const Target * target,std::ostream & out)124 NinjaCBinaryTargetWriter::NinjaCBinaryTargetWriter(const Target* target,
125 std::ostream& out)
126 : NinjaBinaryTargetWriter(target, out),
127 tool_(target->toolchain()->GetToolForTargetFinalOutputAsC(target)) {}
128
129 NinjaCBinaryTargetWriter::~NinjaCBinaryTargetWriter() = default;
130
Run()131 void NinjaCBinaryTargetWriter::Run() {
132 std::vector<ModuleDep> module_dep_info =
133 GetModuleDepsInformation(target_, resolved());
134
135 WriteCompilerVars(module_dep_info);
136
137 size_t num_stamp_uses = target_->sources().size();
138
139 std::vector<OutputFile> input_deps =
140 WriteInputsStampAndGetDep(num_stamp_uses);
141
142 // The input dependencies will be an order-only dependency. This will cause
143 // Ninja to make sure the inputs are up to date before compiling this source,
144 // but changes in the inputs deps won't cause the file to be recompiled.
145 //
146 // This is important to prevent changes in unrelated actions that are
147 // upstream of this target from causing everything to be recompiled.
148 //
149 // Why can we get away with this rather than using implicit deps ("|", which
150 // will force rebuilds when the inputs change)? For source code, the
151 // computed dependencies of all headers will be computed by the compiler,
152 // which will cause source rebuilds if any "real" upstream dependencies
153 // change.
154 //
155 // If a .cc file is generated by an input dependency, Ninja will see the
156 // input to the build rule doesn't exist, and that it is an output from a
157 // previous step, and build the previous step first. This is a "real"
158 // dependency and doesn't need | or || to express.
159 //
160 // The only case where this rule matters is for the first build where no .d
161 // files exist, and Ninja doesn't know what that source file depends on. In
162 // this case it's sufficient to ensure that the upstream dependencies are
163 // built first. This is exactly what Ninja's order-only dependencies
164 // expresses.
165 //
166 // The order only deps are referenced by each source file compile,
167 // but also by PCH compiles. The latter are annoying to count, so omit
168 // them here. This means that binary targets with a single source file
169 // that also use PCH files won't have a stamp file even though having
170 // one would make output ninja file size a bit lower. That's ok, binary
171 // targets with a single source are rare.
172 std::vector<OutputFile> order_only_deps = WriteInputDepsStampAndGetDep(
173 std::vector<const Target*>(), num_stamp_uses);
174
175 // For GCC builds, the .gch files are not object files, but still need to be
176 // added as explicit dependencies below. The .gch output files are placed in
177 // |pch_other_files|. This is to prevent linking against them.
178 std::vector<OutputFile> pch_obj_files;
179 std::vector<OutputFile> pch_other_files;
180 WritePCHCommands(input_deps, order_only_deps, &pch_obj_files,
181 &pch_other_files);
182 std::vector<OutputFile>* pch_files =
183 !pch_obj_files.empty() ? &pch_obj_files : &pch_other_files;
184
185 // Treat all pch output files as explicit dependencies of all
186 // compiles that support them. Some notes:
187 //
188 // - On Windows, the .pch file is the input to the compile, not the
189 // precompiled header's corresponding object file that we're using here.
190 // But Ninja's depslog doesn't support multiple outputs from the
191 // precompiled header compile step (it outputs both the .pch file and a
192 // corresponding .obj file). So we consistently list the .obj file and the
193 // .pch file we really need comes along with it.
194 //
195 // - GCC .gch files are not object files, therefore they are not added to the
196 // object file list.
197 std::vector<OutputFile> obj_files;
198 std::vector<OutputFile> extra_files;
199 std::vector<SourceFile> other_files;
200 std::vector<OutputFile>* stamp_files = &obj_files; // default
201 if (!target_->source_types_used().SwiftSourceUsed()) {
202 WriteSources(*pch_files, input_deps, order_only_deps, module_dep_info,
203 &obj_files, &other_files);
204 } else {
205 stamp_files = &extra_files; // Swift generates more than object files
206 WriteSwiftSources(input_deps, order_only_deps, &obj_files, &extra_files);
207 }
208
209 // Link all MSVC pch object files. The vector will be empty on GCC toolchains.
210 obj_files.insert(obj_files.end(), pch_obj_files.begin(), pch_obj_files.end());
211 if (!CheckForDuplicateObjectFiles(obj_files))
212 return;
213
214 if (target_->output_type() == Target::SOURCE_SET) {
215 WriteSourceSetStamp(*stamp_files);
216 #ifndef NDEBUG
217 // Verify that the function that separately computes a source set's object
218 // files match the object files just computed.
219 UniqueVector<OutputFile> computed_obj;
220 AddSourceSetFiles(target_, &computed_obj);
221 DCHECK_EQ(obj_files.size(), computed_obj.size());
222 for (const auto& obj : obj_files)
223 DCHECK(computed_obj.Contains(obj));
224 #endif
225 } else {
226 WriteLinkerStuff(obj_files, other_files, input_deps);
227 }
228 }
229
WriteCompilerVars(const std::vector<ModuleDep> & module_dep_info)230 void NinjaCBinaryTargetWriter::WriteCompilerVars(
231 const std::vector<ModuleDep>& module_dep_info) {
232 const SubstitutionBits& subst = target_->toolchain()->substitution_bits();
233
234 WriteCCompilerVars(subst, /*indent=*/false,
235 /*respect_source_types_used=*/true);
236
237 if (!module_dep_info.empty()) {
238 // TODO(scottmg): Currently clang modules only working for C++.
239 if (target_->source_types_used().Get(SourceFile::SOURCE_CPP) ||
240 target_->source_types_used().Get(SourceFile::SOURCE_MODULEMAP)) {
241 WriteModuleDepsSubstitution(&CSubstitutionModuleDeps, module_dep_info,
242 true);
243 WriteModuleDepsSubstitution(&CSubstitutionModuleDepsNoSelf,
244 module_dep_info, false);
245 }
246 }
247
248 WriteSharedVars(subst);
249 }
250
WriteModuleDepsSubstitution(const Substitution * substitution,const std::vector<ModuleDep> & module_dep_info,bool include_self)251 void NinjaCBinaryTargetWriter::WriteModuleDepsSubstitution(
252 const Substitution* substitution,
253 const std::vector<ModuleDep>& module_dep_info,
254 bool include_self) {
255 if (target_->toolchain()->substitution_bits().used.count(substitution)) {
256 EscapeOptions options;
257 options.mode = ESCAPE_NINJA_COMMAND;
258
259 out_ << substitution->ninja_name << " = -Xclang ";
260 EscapeStringToStream(out_, "-fmodules-embed-all-files", options);
261
262 for (const auto& module_dep : module_dep_info) {
263 if (!module_dep.is_self || include_self) {
264 out_ << " ";
265 EscapeStringToStream(out_, "-fmodule-file=", options);
266 path_output_.WriteFile(out_, module_dep.pcm);
267 }
268 }
269
270 out_ << std::endl;
271 }
272 }
273
WritePCHCommands(const std::vector<OutputFile> & input_deps,const std::vector<OutputFile> & order_only_deps,std::vector<OutputFile> * object_files,std::vector<OutputFile> * other_files)274 void NinjaCBinaryTargetWriter::WritePCHCommands(
275 const std::vector<OutputFile>& input_deps,
276 const std::vector<OutputFile>& order_only_deps,
277 std::vector<OutputFile>* object_files,
278 std::vector<OutputFile>* other_files) {
279 if (!target_->config_values().has_precompiled_headers())
280 return;
281
282 const CTool* tool_c = target_->toolchain()->GetToolAsC(CTool::kCToolCc);
283 if (tool_c && tool_c->precompiled_header_type() != CTool::PCH_NONE &&
284 target_->source_types_used().Get(SourceFile::SOURCE_C)) {
285 WritePCHCommand(&CSubstitutionCFlagsC, CTool::kCToolCc,
286 tool_c->precompiled_header_type(), input_deps,
287 order_only_deps, object_files, other_files);
288 }
289 const CTool* tool_cxx = target_->toolchain()->GetToolAsC(CTool::kCToolCxx);
290 if (tool_cxx && tool_cxx->precompiled_header_type() != CTool::PCH_NONE &&
291 target_->source_types_used().Get(SourceFile::SOURCE_CPP)) {
292 WritePCHCommand(&CSubstitutionCFlagsCc, CTool::kCToolCxx,
293 tool_cxx->precompiled_header_type(), input_deps,
294 order_only_deps, object_files, other_files);
295 }
296
297 const CTool* tool_objc = target_->toolchain()->GetToolAsC(CTool::kCToolObjC);
298 if (tool_objc && tool_objc->precompiled_header_type() == CTool::PCH_GCC &&
299 target_->source_types_used().Get(SourceFile::SOURCE_M)) {
300 WritePCHCommand(&CSubstitutionCFlagsObjC, CTool::kCToolObjC,
301 tool_objc->precompiled_header_type(), input_deps,
302 order_only_deps, object_files, other_files);
303 }
304
305 const CTool* tool_objcxx =
306 target_->toolchain()->GetToolAsC(CTool::kCToolObjCxx);
307 if (tool_objcxx && tool_objcxx->precompiled_header_type() == CTool::PCH_GCC &&
308 target_->source_types_used().Get(SourceFile::SOURCE_MM)) {
309 WritePCHCommand(&CSubstitutionCFlagsObjCc, CTool::kCToolObjCxx,
310 tool_objcxx->precompiled_header_type(), input_deps,
311 order_only_deps, object_files, other_files);
312 }
313 }
314
WritePCHCommand(const Substitution * flag_type,const char * tool_name,CTool::PrecompiledHeaderType header_type,const std::vector<OutputFile> & input_deps,const std::vector<OutputFile> & order_only_deps,std::vector<OutputFile> * object_files,std::vector<OutputFile> * other_files)315 void NinjaCBinaryTargetWriter::WritePCHCommand(
316 const Substitution* flag_type,
317 const char* tool_name,
318 CTool::PrecompiledHeaderType header_type,
319 const std::vector<OutputFile>& input_deps,
320 const std::vector<OutputFile>& order_only_deps,
321 std::vector<OutputFile>* object_files,
322 std::vector<OutputFile>* other_files) {
323 switch (header_type) {
324 case CTool::PCH_MSVC:
325 WriteWindowsPCHCommand(flag_type, tool_name, input_deps, order_only_deps,
326 object_files);
327 break;
328 case CTool::PCH_GCC:
329 WriteGCCPCHCommand(flag_type, tool_name, input_deps, order_only_deps,
330 other_files);
331 break;
332 case CTool::PCH_NONE:
333 NOTREACHED() << "Cannot write a PCH command with no PCH header type";
334 break;
335 }
336 }
337
WriteGCCPCHCommand(const Substitution * flag_type,const char * tool_name,const std::vector<OutputFile> & input_deps,const std::vector<OutputFile> & order_only_deps,std::vector<OutputFile> * gch_files)338 void NinjaCBinaryTargetWriter::WriteGCCPCHCommand(
339 const Substitution* flag_type,
340 const char* tool_name,
341 const std::vector<OutputFile>& input_deps,
342 const std::vector<OutputFile>& order_only_deps,
343 std::vector<OutputFile>* gch_files) {
344 // Compute the pch output file (it will be language-specific).
345 std::vector<OutputFile> outputs;
346 GetPCHOutputFiles(target_, tool_name, &outputs);
347 if (outputs.empty())
348 return;
349
350 gch_files->insert(gch_files->end(), outputs.begin(), outputs.end());
351
352 std::vector<OutputFile> extra_deps;
353 std::copy(input_deps.begin(), input_deps.end(),
354 std::back_inserter(extra_deps));
355
356 // Build line to compile the file.
357 WriteCompilerBuildLine({target_->config_values().precompiled_source()},
358 extra_deps, order_only_deps, tool_name, outputs);
359
360 // This build line needs a custom language-specific flags value. Rule-specific
361 // variables are just indented underneath the rule line.
362 out_ << " " << flag_type->ninja_name << " =";
363
364 // Each substitution flag is overwritten in the target rule to replace the
365 // implicitly generated -include flag with the -x <header lang> flag required
366 // for .gch targets.
367 EscapeOptions opts = GetFlagOptions();
368 if (tool_name == CTool::kCToolCc) {
369 RecursiveTargetConfigStringsToStream(kRecursiveWriterKeepDuplicates,
370 target_, &ConfigValues::cflags_c, opts,
371 out_);
372 } else if (tool_name == CTool::kCToolCxx) {
373 RecursiveTargetConfigStringsToStream(kRecursiveWriterKeepDuplicates,
374 target_, &ConfigValues::cflags_cc,
375 opts, out_);
376 } else if (tool_name == CTool::kCToolObjC) {
377 RecursiveTargetConfigStringsToStream(kRecursiveWriterKeepDuplicates,
378 target_, &ConfigValues::cflags_objc,
379 opts, out_);
380 } else if (tool_name == CTool::kCToolObjCxx) {
381 RecursiveTargetConfigStringsToStream(kRecursiveWriterKeepDuplicates,
382 target_, &ConfigValues::cflags_objcc,
383 opts, out_);
384 }
385
386 // Append the command to specify the language of the .gch file.
387 out_ << " -x " << GetPCHLangForToolType(tool_name);
388
389 // Write two blank lines to help separate the PCH build lines from the
390 // regular source build lines.
391 out_ << std::endl << std::endl;
392 }
393
WriteWindowsPCHCommand(const Substitution * flag_type,const char * tool_name,const std::vector<OutputFile> & input_deps,const std::vector<OutputFile> & order_only_deps,std::vector<OutputFile> * object_files)394 void NinjaCBinaryTargetWriter::WriteWindowsPCHCommand(
395 const Substitution* flag_type,
396 const char* tool_name,
397 const std::vector<OutputFile>& input_deps,
398 const std::vector<OutputFile>& order_only_deps,
399 std::vector<OutputFile>* object_files) {
400 // Compute the pch output file (it will be language-specific).
401 std::vector<OutputFile> outputs;
402 GetPCHOutputFiles(target_, tool_name, &outputs);
403 if (outputs.empty())
404 return;
405
406 object_files->insert(object_files->end(), outputs.begin(), outputs.end());
407
408 std::vector<OutputFile> extra_deps;
409 std::copy(input_deps.begin(), input_deps.end(),
410 std::back_inserter(extra_deps));
411
412 // Build line to compile the file.
413 WriteCompilerBuildLine({target_->config_values().precompiled_source()},
414 extra_deps, order_only_deps, tool_name, outputs);
415
416 // This build line needs a custom language-specific flags value. Rule-specific
417 // variables are just indented underneath the rule line.
418 out_ << " " << flag_type->ninja_name << " =";
419
420 // Append the command to generate the .pch file.
421 // This adds the value to the existing flag instead of overwriting it.
422 out_ << " ${" << flag_type->ninja_name << "}";
423 out_ << " /Yc" << target_->config_values().precompiled_header();
424
425 // Write two blank lines to help separate the PCH build lines from the
426 // regular source build lines.
427 out_ << std::endl << std::endl;
428 }
429
WriteSources(const std::vector<OutputFile> & pch_deps,const std::vector<OutputFile> & input_deps,const std::vector<OutputFile> & order_only_deps,const std::vector<ModuleDep> & module_dep_info,std::vector<OutputFile> * object_files,std::vector<SourceFile> * other_files)430 void NinjaCBinaryTargetWriter::WriteSources(
431 const std::vector<OutputFile>& pch_deps,
432 const std::vector<OutputFile>& input_deps,
433 const std::vector<OutputFile>& order_only_deps,
434 const std::vector<ModuleDep>& module_dep_info,
435 std::vector<OutputFile>* object_files,
436 std::vector<SourceFile>* other_files) {
437 DCHECK(!target_->source_types_used().SwiftSourceUsed());
438 object_files->reserve(object_files->size() + target_->sources().size());
439
440 std::vector<OutputFile> tool_outputs; // Prevent reallocation in loop.
441 std::vector<OutputFile> deps;
442 for (const auto& source : target_->sources()) {
443 DCHECK_NE(source.GetType(), SourceFile::SOURCE_SWIFT);
444
445 // Clear the vector but maintain the max capacity to prevent reallocations.
446 deps.resize(0);
447 const char* tool_name = Tool::kToolNone;
448 if (!target_->GetOutputFilesForSource(source, &tool_name, &tool_outputs)) {
449 if (source.IsDefType())
450 other_files->push_back(source);
451 continue; // No output for this source.
452 }
453
454 std::copy(input_deps.begin(), input_deps.end(), std::back_inserter(deps));
455
456 if (tool_name != Tool::kToolNone) {
457 // Only include PCH deps that correspond to the tool type, for instance,
458 // do not specify target_name.precompile.cc.obj (a CXX PCH file) as a dep
459 // for the output of a C tool type.
460 //
461 // This makes the assumption that pch_deps only contains pch output files
462 // with the naming scheme specified in GetWindowsPCHObjectExtension or
463 // GetGCCPCHOutputExtension.
464 const CTool* tool = target_->toolchain()->GetToolAsC(tool_name);
465 if (tool->precompiled_header_type() != CTool::PCH_NONE) {
466 for (const auto& dep : pch_deps) {
467 const std::string& output_value = dep.value();
468 size_t extension_offset = FindExtensionOffset(output_value);
469 if (extension_offset == std::string::npos)
470 continue;
471 std::string output_extension;
472 if (tool->precompiled_header_type() == CTool::PCH_MSVC) {
473 output_extension = GetWindowsPCHObjectExtension(
474 tool_name, output_value.substr(extension_offset - 1));
475 } else if (tool->precompiled_header_type() == CTool::PCH_GCC) {
476 output_extension = GetGCCPCHOutputExtension(tool_name);
477 }
478 if (output_value.compare(
479 output_value.size() - output_extension.size(),
480 output_extension.size(), output_extension) == 0) {
481 deps.push_back(dep);
482 }
483 }
484 }
485
486 for (const auto& module_dep : module_dep_info) {
487 if (tool_outputs[0] != module_dep.pcm)
488 deps.push_back(module_dep.pcm);
489 }
490
491 WriteCompilerBuildLine({source}, deps, order_only_deps, tool_name,
492 tool_outputs);
493 WritePool(out_);
494 }
495
496 // It's theoretically possible for a compiler to produce more than one
497 // output, but we'll only link to the first output.
498 if (!source.IsModuleMapType()) {
499 object_files->push_back(tool_outputs[0]);
500 }
501 }
502
503 out_ << std::endl;
504 }
505
WriteSwiftSources(const std::vector<OutputFile> & input_deps,const std::vector<OutputFile> & order_only_deps,std::vector<OutputFile> * object_files,std::vector<OutputFile> * output_files)506 void NinjaCBinaryTargetWriter::WriteSwiftSources(
507 const std::vector<OutputFile>& input_deps,
508 const std::vector<OutputFile>& order_only_deps,
509 std::vector<OutputFile>* object_files,
510 std::vector<OutputFile>* output_files) {
511 DCHECK(target_->builds_swift_module());
512 target_->swift_values().GetOutputs(target_, output_files);
513
514 const BuildSettings* build_settings = settings_->build_settings();
515 for (const OutputFile& output : *output_files) {
516 const SourceFile output_as_source = output.AsSourceFile(build_settings);
517 if (output_as_source.IsObjectType()) {
518 object_files->push_back(output);
519 }
520 }
521
522 UniqueVector<OutputFile> swift_order_only_deps;
523 swift_order_only_deps.reserve(order_only_deps.size());
524 swift_order_only_deps.Append(order_only_deps.begin(), order_only_deps.end());
525
526 for (const Target* swiftmodule :
527 resolved().GetSwiftModuleDependencies(target_)) {
528 swift_order_only_deps.push_back(swiftmodule->dependency_output_file());
529 }
530
531 const Tool* tool = target_->swift_values().GetTool(target_);
532 WriteCompilerBuildLine(target_->sources(), input_deps,
533 swift_order_only_deps.vector(), tool->name(),
534 *output_files,
535 /*can_write_source_info=*/false,
536 /*restat_output_allowed=*/true);
537
538 out_ << std::endl;
539 }
540
WriteSourceSetStamp(const std::vector<OutputFile> & object_files)541 void NinjaCBinaryTargetWriter::WriteSourceSetStamp(
542 const std::vector<OutputFile>& object_files) {
543 // The stamp rule for source sets is generally not used, since targets that
544 // depend on this will reference the object files directly. However, writing
545 // this rule allows the user to type the name of the target and get a build
546 // which can be convenient for development.
547 ClassifiedDeps classified_deps = GetClassifiedDeps();
548
549 // The classifier should never put extra object files in a source sets: any
550 // source sets that we depend on should appear in our non-linkable deps
551 // instead.
552 DCHECK(classified_deps.extra_object_files.empty());
553
554 std::vector<OutputFile> order_only_deps;
555 for (auto* dep : classified_deps.non_linkable_deps)
556 order_only_deps.push_back(dep->dependency_output_file());
557
558 WriteStampForTarget(object_files, order_only_deps);
559 }
560
WriteLinkerStuff(const std::vector<OutputFile> & object_files,const std::vector<SourceFile> & other_files,const std::vector<OutputFile> & input_deps)561 void NinjaCBinaryTargetWriter::WriteLinkerStuff(
562 const std::vector<OutputFile>& object_files,
563 const std::vector<SourceFile>& other_files,
564 const std::vector<OutputFile>& input_deps) {
565 std::vector<OutputFile> output_files;
566 SubstitutionWriter::ApplyListToLinkerAsOutputFile(
567 target_, tool_, tool_->outputs(), &output_files);
568
569 out_ << "build";
570 WriteOutputs(output_files);
571
572 out_ << ": " << rule_prefix_
573 << Tool::GetToolTypeForTargetFinalOutput(target_);
574
575 ClassifiedDeps classified_deps = GetClassifiedDeps();
576
577 // Object files.
578 path_output_.WriteFiles(out_, object_files);
579 path_output_.WriteFiles(out_, classified_deps.extra_object_files);
580
581 // Dependencies.
582 std::vector<OutputFile> implicit_deps;
583 std::vector<OutputFile> solibs;
584 for (const Target* cur : classified_deps.linkable_deps) {
585 // All linkable deps should have a link output file.
586 DCHECK(!cur->link_output_file().value().empty())
587 << "No link output file for "
588 << target_->label().GetUserVisibleName(false);
589
590 if (cur->output_type() == Target::RUST_LIBRARY ||
591 cur->output_type() == Target::RUST_PROC_MACRO)
592 continue;
593
594 if (cur->dependency_output_file().value() !=
595 cur->link_output_file().value()) {
596 // This is a shared library with separate link and deps files. Save for
597 // later.
598 implicit_deps.push_back(cur->dependency_output_file());
599 solibs.push_back(cur->link_output_file());
600 } else {
601 // Normal case, just link to this target.
602 out_ << " ";
603 path_output_.WriteFile(out_, cur->link_output_file());
604 }
605 }
606
607 const SourceFile* optional_def_file = nullptr;
608 if (!other_files.empty()) {
609 for (const SourceFile& src_file : other_files) {
610 if (src_file.IsDefType()) {
611 optional_def_file = &src_file;
612 implicit_deps.push_back(
613 OutputFile(settings_->build_settings(), src_file));
614 break; // Only one def file is allowed.
615 }
616 }
617 }
618
619 // Libraries specified by paths.
620 for (const auto& lib : resolved().GetLinkedLibraries(target_)) {
621 if (lib.is_source_file()) {
622 implicit_deps.push_back(
623 OutputFile(settings_->build_settings(), lib.source_file()));
624 }
625 }
626
627 // If any target creates a framework bundle, then treat it as an implicit
628 // dependency via the .stamp file. This is a pessimisation as it is not
629 // always necessary to relink the current target if one of the framework
630 // is regenerated, but it ensure that if one of the framework API changes,
631 // any dependent target will relink it (see crbug.com/1037607).
632 for (const Target* dep : classified_deps.framework_deps) {
633 implicit_deps.push_back(dep->dependency_output_file());
634 }
635
636 // The input dependency is only needed if there are no object files, as the
637 // dependency is normally provided transitively by the source files.
638 std::copy(input_deps.begin(), input_deps.end(),
639 std::back_inserter(implicit_deps));
640
641 // Any C++ target which depends on a Rust .rlib has to depend on its entire
642 // tree of transitive rlibs found inside the linking target (which excludes
643 // rlibs only depended on inside a shared library dependency).
644 std::vector<OutputFile> transitive_rustlibs;
645 if (target_->IsFinal()) {
646 for (const auto& inherited : resolved().GetInheritedLibraries(target_)) {
647 const Target* dep = inherited.target();
648 if (dep->output_type() == Target::RUST_LIBRARY) {
649 transitive_rustlibs.push_back(dep->dependency_output_file());
650 implicit_deps.push_back(dep->dependency_output_file());
651 }
652 }
653 }
654
655 // Swift modules from dependencies (and possibly self).
656 std::vector<OutputFile> swiftmodules;
657 if (target_->IsFinal()) {
658 for (const Target* dep : classified_deps.swiftmodule_deps) {
659 swiftmodules.push_back(dep->swift_values().module_output_file());
660 implicit_deps.push_back(dep->swift_values().module_output_file());
661 }
662 if (target_->builds_swift_module()) {
663 swiftmodules.push_back(target_->swift_values().module_output_file());
664 implicit_deps.push_back(target_->swift_values().module_output_file());
665 }
666 }
667
668 // Append implicit dependencies collected above.
669 if (!implicit_deps.empty()) {
670 out_ << " |";
671 path_output_.WriteFiles(out_, implicit_deps);
672 }
673
674 // Append data dependencies as order-only dependencies.
675 //
676 // This will include data dependencies and input dependencies (like when
677 // this target depends on an action). Having the data dependencies in this
678 // list ensures that the data is available at runtime when the user builds
679 // this target.
680 //
681 // The action dependencies are not strictly necessary in this case. They
682 // should also have been collected via the input deps stamp that each source
683 // file has for an order-only dependency, and since this target depends on
684 // the sources, there is already an implicit order-only dependency. However,
685 // it's extra work to separate these out and there's no disadvantage to
686 // listing them again.
687 WriteOrderOnlyDependencies(classified_deps.non_linkable_deps);
688
689 // End of the link "build" line.
690 out_ << std::endl;
691
692 // The remaining things go in the inner scope of the link line.
693 if (target_->output_type() == Target::EXECUTABLE ||
694 target_->output_type() == Target::SHARED_LIBRARY ||
695 target_->output_type() == Target::LOADABLE_MODULE) {
696 out_ << " ldflags =";
697 WriteLinkerFlags(out_, tool_, optional_def_file);
698 out_ << std::endl;
699 out_ << " libs =";
700 WriteLibs(out_, tool_);
701 out_ << std::endl;
702 out_ << " frameworks =";
703 WriteFrameworks(out_, tool_);
704 out_ << std::endl;
705 out_ << " swiftmodules =";
706 WriteSwiftModules(out_, tool_, swiftmodules);
707 out_ << std::endl;
708 } else if (target_->output_type() == Target::STATIC_LIBRARY) {
709 out_ << " arflags =";
710 RecursiveTargetConfigStringsToStream(kRecursiveWriterKeepDuplicates,
711 target_, &ConfigValues::arflags,
712 GetFlagOptions(), out_);
713 out_ << std::endl;
714 }
715 WriteOutputSubstitutions();
716 WriteLibsList("solibs", solibs);
717 WriteLibsList("rlibs", transitive_rustlibs);
718
719 const Toolchain *toolchain = target_->toolchain();
720 if (toolchain == nullptr || toolchain->GetToolAsC(CTool::kCToolSolink) == nullptr) {
721 return;
722 }
723 int toolchain_whole_status = toolchain->GetToolAsC(CTool::kCToolSolink)->toolchain_whole_status();
724 if (toolchain_whole_status == 0) {
725 WriteWholeArchive(toolchain_whole_status);
726 } else if (toolchain_whole_status == 1){
727 WriteNoWholeArchive(toolchain_whole_status);
728 }
729 return;
730 }
731
WriteWholeArchive(int toolchain_whole_status)732 void NinjaCBinaryTargetWriter::WriteWholeArchive(int toolchain_whole_status) {
733
734 if (target_->whole_archive_deps().size() == 0) {
735 return;
736 }
737 std::string result;
738 for (const auto& file : target_->whole_archive_deps()) {
739 std::string label = file.label.GetUserVisibleName(false);
740 size_t sep = label.find(":");
741 if (sep == std::string::npos) {
742 continue;
743 }
744 result += label.substr(sep + 1) + ".a";
745 result += " ";
746 }
747
748 out_ << " toolchain_whole_status = "
749 << toolchain_whole_status;
750 out_ << std::endl;
751
752 out_ << " whole-archive = "
753 << result;
754 out_ << std::endl;
755 }
756
WriteNoWholeArchive(int toolchain_whole_status)757 void NinjaCBinaryTargetWriter::WriteNoWholeArchive(int toolchain_whole_status) {
758 if (target_->no_whole_archive_deps().size() == 0) {
759 return;
760 }
761 std::string result;
762 for (const auto& file : target_->no_whole_archive_deps()) {
763 std::string label = file.label.GetUserVisibleName(false);
764 size_t sep = label.find(":");
765 if (sep == std::string::npos) {
766 continue;
767 }
768 result += label.substr(sep + 1) + ".a";
769 result += " ";
770 }
771
772 out_ << " toolchain_whole_status = "
773 << toolchain_whole_status;
774 out_ << std::endl;
775
776 out_ << " no-whole-archive = "
777 << result;
778 out_ << std::endl;
779 }
780
WriteOutputSubstitutions()781 void NinjaCBinaryTargetWriter::WriteOutputSubstitutions() {
782 out_ << " output_extension = "
783 << SubstitutionWriter::GetLinkerSubstitution(
784 target_, tool_, &SubstitutionOutputExtension);
785 out_ << std::endl;
786 out_ << " output_dir = "
787 << SubstitutionWriter::GetLinkerSubstitution(target_, tool_,
788 &SubstitutionOutputDir);
789 out_ << std::endl;
790 }
791
WriteLibsList(const std::string & label,const std::vector<OutputFile> & libs)792 void NinjaCBinaryTargetWriter::WriteLibsList(
793 const std::string& label,
794 const std::vector<OutputFile>& libs) {
795 if (libs.empty())
796 return;
797
798 out_ << " " << label << " =";
799 PathOutput output(path_output_.current_dir(),
800 settings_->build_settings()->root_path_utf8(),
801 ESCAPE_NINJA_COMMAND);
802 output.WriteFiles(out_, libs);
803 out_ << std::endl;
804 }
805
WriteOrderOnlyDependencies(const UniqueVector<const Target * > & non_linkable_deps)806 void NinjaCBinaryTargetWriter::WriteOrderOnlyDependencies(
807 const UniqueVector<const Target*>& non_linkable_deps) {
808 if (!non_linkable_deps.empty()) {
809 out_ << " ||";
810
811 // Non-linkable targets.
812 for (auto* non_linkable_dep : non_linkable_deps) {
813 out_ << " ";
814 path_output_.WriteFile(out_, non_linkable_dep->dependency_output_file());
815 }
816 }
817 }
818
CheckForDuplicateObjectFiles(const std::vector<OutputFile> & files) const819 bool NinjaCBinaryTargetWriter::CheckForDuplicateObjectFiles(
820 const std::vector<OutputFile>& files) const {
821 std::set<std::string> set;
822 for (const auto& file : files) {
823 if (!set.insert(file.value()).second) {
824 Err err(
825 target_->defined_from(), "Duplicate object file",
826 "The target " + target_->label().GetUserVisibleName(false) +
827 "\ngenerates two object files with the same name:\n " +
828 file.value() +
829 "\n"
830 "\n"
831 "It could be you accidentally have a file listed twice in the\n"
832 "sources. Or, depending on how your toolchain maps sources to\n"
833 "object files, two source files with the same name in different\n"
834 "directories could map to the same object file.\n"
835 "\n"
836 "In the latter case, either rename one of the files or move one "
837 "of\n"
838 "the sources to a separate source_set to avoid them both being "
839 "in\n"
840 "the same target.");
841 g_scheduler->FailWithError(err);
842 return false;
843 }
844 }
845 return true;
846 }
847