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