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