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