Add ICU message format support
[chromium-blink-merge.git] / tools / gn / ninja_binary_target_writer.cc
blob971f4d87f439bbb3ff4941a263a0603069dab438
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "tools/gn/ninja_binary_target_writer.h"
7 #include <cstring>
8 #include <set>
9 #include <sstream>
11 #include "base/containers/hash_tables.h"
12 #include "base/strings/string_util.h"
13 #include "tools/gn/config_values_extractors.h"
14 #include "tools/gn/deps_iterator.h"
15 #include "tools/gn/err.h"
16 #include "tools/gn/escape.h"
17 #include "tools/gn/filesystem_utils.h"
18 #include "tools/gn/ninja_utils.h"
19 #include "tools/gn/scheduler.h"
20 #include "tools/gn/settings.h"
21 #include "tools/gn/source_file_type.h"
22 #include "tools/gn/string_utils.h"
23 #include "tools/gn/substitution_writer.h"
24 #include "tools/gn/target.h"
26 // Represents a set of tool types. Must be first since it is also shared by
27 // some helper functions in the anonymous namespace below.
28 class NinjaBinaryTargetWriter::SourceFileTypeSet {
29 public:
30 SourceFileTypeSet() {
31 memset(flags_, 0, sizeof(bool) * static_cast<int>(SOURCE_NUMTYPES));
34 void Set(SourceFileType type) {
35 flags_[static_cast<int>(type)] = true;
37 bool Get(SourceFileType type) const {
38 return flags_[static_cast<int>(type)];
41 private:
42 bool flags_[static_cast<int>(SOURCE_NUMTYPES)];
45 namespace {
47 // Returns the proper escape options for writing compiler and linker flags.
48 EscapeOptions GetFlagOptions() {
49 EscapeOptions opts;
50 opts.mode = ESCAPE_NINJA_COMMAND;
52 // Some flag strings are actually multiple flags that expect to be just
53 // added to the command line. We assume that quoting is done by the
54 // buildfiles if it wants such things quoted.
55 opts.inhibit_quoting = true;
57 return opts;
60 struct DefineWriter {
61 DefineWriter() {
62 options.mode = ESCAPE_NINJA_COMMAND;
65 void operator()(const std::string& s, std::ostream& out) const {
66 out << " -D";
67 EscapeStringToStream(out, s, options);
70 EscapeOptions options;
73 struct IncludeWriter {
74 explicit IncludeWriter(PathOutput& path_output) : path_output_(path_output) {
76 ~IncludeWriter() {
79 void operator()(const SourceDir& d, std::ostream& out) const {
80 std::ostringstream path_out;
81 path_output_.WriteDir(path_out, d, PathOutput::DIR_NO_LAST_SLASH);
82 const std::string& path = path_out.str();
83 if (path[0] == '"')
84 out << " \"-I" << path.substr(1);
85 else
86 out << " -I" << path;
89 PathOutput& path_output_;
92 // Computes the set of output files resulting from compiling the given source
93 // file. If the file can be compiled and the tool exists, fills the outputs in
94 // and writes the tool type to computed_tool_type. If the file is not
95 // compilable, returns false.
97 // The target that the source belongs to is passed as an argument. In the case
98 // of linking to source sets, this can be different than the target this class
99 // is currently writing.
101 // The function can succeed with a "NONE" tool type for object files which are
102 // just passed to the output. The output will always be overwritten, not
103 // appended to.
104 bool GetOutputFilesForSource(const Target* target,
105 const SourceFile& source,
106 Toolchain::ToolType* computed_tool_type,
107 std::vector<OutputFile>* outputs) {
108 outputs->clear();
109 *computed_tool_type = Toolchain::TYPE_NONE;
111 SourceFileType file_type = GetSourceFileType(source);
112 if (file_type == SOURCE_UNKNOWN)
113 return false;
114 if (file_type == SOURCE_O) {
115 // Object files just get passed to the output and not compiled.
116 outputs->push_back(
117 OutputFile(target->settings()->build_settings(), source));
118 return true;
121 *computed_tool_type =
122 target->toolchain()->GetToolTypeForSourceType(file_type);
123 if (*computed_tool_type == Toolchain::TYPE_NONE)
124 return false; // No tool for this file (it's a header file or something).
125 const Tool* tool = target->toolchain()->GetTool(*computed_tool_type);
126 if (!tool)
127 return false; // Tool does not apply for this toolchain.file.
129 // Figure out what output(s) this compiler produces.
130 SubstitutionWriter::ApplyListToCompilerAsOutputFile(
131 target, source, tool->outputs(), outputs);
132 return !outputs->empty();
135 // Returns the language-specific prefix/suffix for precomiled header files.
136 const char* GetPCHLangForToolType(Toolchain::ToolType type) {
137 switch (type) {
138 case Toolchain::TYPE_CC:
139 return "c";
140 case Toolchain::TYPE_CXX:
141 return "cc";
142 case Toolchain::TYPE_OBJC:
143 return "m";
144 case Toolchain::TYPE_OBJCXX:
145 return "mm";
146 default:
147 NOTREACHED() << "Not a valid PCH tool type type";
148 return "";
152 // Returns the object files for the precompiled header of the given type (flag
153 // type and tool type must match).
154 void GetWindowsPCHObjectFiles(const Target* target,
155 Toolchain::ToolType tool_type,
156 std::vector<OutputFile>* outputs) {
157 outputs->clear();
159 // Compute the tool. This must use the tool type passed in rather than the
160 // detected file type of the precompiled source file since the same
161 // precompiled source file will be used for separate C/C++ compiles.
162 const Tool* tool = target->toolchain()->GetTool(tool_type);
163 if (!tool)
164 return;
165 SubstitutionWriter::ApplyListToCompilerAsOutputFile(
166 target, target->config_values().precompiled_source(),
167 tool->outputs(), outputs);
169 if (outputs->empty())
170 return;
171 if (outputs->size() > 1)
172 outputs->resize(1); // Only link the first output from the compiler tool.
174 // Need to annotate the obj files with the language type. For example:
175 // obj/foo/target_name.precompile.obj ->
176 // obj/foo/target_name.precompile.cc.obj
177 const char* lang_suffix = GetPCHLangForToolType(tool_type);
178 std::string& output_value = (*outputs)[0].value();
179 size_t extension_offset = FindExtensionOffset(output_value);
180 if (extension_offset == std::string::npos) {
181 NOTREACHED() << "No extension found";
182 } else {
183 DCHECK(extension_offset >= 1);
184 DCHECK(output_value[extension_offset - 1] == '.');
185 output_value.insert(extension_offset - 1, ".");
186 output_value.insert(extension_offset, lang_suffix);
190 // Appends the object files generated by the given source set to the given
191 // output vector.
192 void AddSourceSetObjectFiles(const Target* source_set,
193 UniqueVector<OutputFile>* obj_files) {
194 std::vector<OutputFile> tool_outputs; // Prevent allocation in loop.
195 NinjaBinaryTargetWriter::SourceFileTypeSet used_types;
197 // Compute object files for all sources. Only link the first output from
198 // the tool if there are more than one.
199 for (const auto& source : source_set->sources()) {
200 Toolchain::ToolType tool_type = Toolchain::TYPE_NONE;
201 if (GetOutputFilesForSource(source_set, source, &tool_type, &tool_outputs))
202 obj_files->push_back(tool_outputs[0]);
204 used_types.Set(GetSourceFileType(source));
207 // Precompiled header object files.
208 if (source_set->config_values().has_precompiled_headers()) {
209 if (used_types.Get(SOURCE_C)) {
210 GetWindowsPCHObjectFiles(source_set, Toolchain::TYPE_CC, &tool_outputs);
211 obj_files->Append(tool_outputs.begin(), tool_outputs.end());
213 if (used_types.Get(SOURCE_CPP)) {
214 GetWindowsPCHObjectFiles(source_set, Toolchain::TYPE_CXX, &tool_outputs);
215 obj_files->Append(tool_outputs.begin(), tool_outputs.end());
217 if (used_types.Get(SOURCE_M)) {
218 GetWindowsPCHObjectFiles(source_set, Toolchain::TYPE_OBJC, &tool_outputs);
219 obj_files->Append(tool_outputs.begin(), tool_outputs.end());
221 if (used_types.Get(SOURCE_MM)) {
222 GetWindowsPCHObjectFiles(source_set, Toolchain::TYPE_OBJCXX,
223 &tool_outputs);
224 obj_files->Append(tool_outputs.begin(), tool_outputs.end());
229 } // namespace
231 NinjaBinaryTargetWriter::NinjaBinaryTargetWriter(const Target* target,
232 std::ostream& out)
233 : NinjaTargetWriter(target, out),
234 tool_(target->toolchain()->GetToolForTargetFinalOutput(target)),
235 rule_prefix_(GetNinjaRulePrefixForToolchain(settings_)) {
238 NinjaBinaryTargetWriter::~NinjaBinaryTargetWriter() {
241 void NinjaBinaryTargetWriter::Run() {
242 // Figure out what source types are needed.
243 SourceFileTypeSet used_types;
244 for (const auto& source : target_->sources())
245 used_types.Set(GetSourceFileType(source));
247 WriteCompilerVars(used_types);
249 // The input dependencies will be an order-only dependency. This will cause
250 // Ninja to make sure the inputs are up-to-date before compiling this source,
251 // but changes in the inputs deps won't cause the file to be recompiled.
253 // This is important to prevent changes in unrelated actions that are
254 // upstream of this target from causing everything to be recompiled
256 // Why can we get away with this rather than using implicit deps ("|", which
257 // will force rebuilds when the inputs change)? For source code, the
258 // computed dependencies of all headers will be computed by the compiler,
259 // which will cause source rebuilds if any "real" upstream dependencies
260 // change.
262 // If a .cc file is generated by an input dependency, Ninja will see the
263 // input to the build rule doesn't exist, and that it is an output from a
264 // previous step, and build the previous step first. This is a "real"
265 // dependency and doesn't need | or || to express.
267 // The only case where this rule matters is for the first build where no .d
268 // files exist, and Ninja doesn't know what that source file depends on. In
269 // this case it's sufficient to ensure that the upstream dependencies are
270 // built first. This is exactly what Ninja's order-only dependencies
271 // expresses.
272 OutputFile order_only_dep =
273 WriteInputDepsStampAndGetDep(std::vector<const Target*>());
275 std::vector<OutputFile> pch_obj_files;
276 WritePrecompiledHeaderCommands(used_types, order_only_dep, &pch_obj_files);
278 // Treat all precompiled object files as explicit dependencies of all
279 // compiles. Some notes:
281 // - Technically only the language-specific one is required for any specific
282 // compile, but that's more difficult to express and the additional logic
283 // doesn't buy much reduced parallelism. Just list them all (there's
284 // usually only one anyway).
286 // - Technically the .pch file is the input to the compile, not the
287 // precompiled header's corresponding object file that we're using here.
288 // But Ninja's depslog doesn't support multiple outputs from the
289 // precompiled header compile step (it outputs both the .pch file and a
290 // corresponding .obj file). So we consistently list the .obj file and the
291 // .pch file we really need comes along with it.
292 std::vector<OutputFile> obj_files;
293 std::vector<SourceFile> other_files;
294 WriteSources(pch_obj_files, order_only_dep, &obj_files, &other_files);
296 // Also link all pch object files.
297 obj_files.insert(obj_files.end(), pch_obj_files.begin(), pch_obj_files.end());
299 if (!CheckForDuplicateObjectFiles(obj_files))
300 return;
302 if (target_->output_type() == Target::SOURCE_SET) {
303 WriteSourceSetStamp(obj_files);
304 #ifndef NDEBUG
305 // Verify that the function that separately computes a source set's object
306 // files match the object files just computed.
307 UniqueVector<OutputFile> computed_obj;
308 AddSourceSetObjectFiles(target_, &computed_obj);
309 DCHECK_EQ(obj_files.size(), computed_obj.size());
310 for (const auto& obj : obj_files)
311 DCHECK_NE(static_cast<size_t>(-1), computed_obj.IndexOf(obj));
312 #endif
313 } else {
314 WriteLinkerStuff(obj_files, other_files);
318 void NinjaBinaryTargetWriter::WriteCompilerVars(
319 const SourceFileTypeSet& used_types) {
320 const SubstitutionBits& subst = target_->toolchain()->substitution_bits();
322 // Defines.
323 if (subst.used[SUBSTITUTION_DEFINES]) {
324 out_ << kSubstitutionNinjaNames[SUBSTITUTION_DEFINES] << " =";
325 RecursiveTargetConfigToStream<std::string>(
326 target_, &ConfigValues::defines, DefineWriter(), out_);
327 out_ << std::endl;
330 // Include directories.
331 if (subst.used[SUBSTITUTION_INCLUDE_DIRS]) {
332 out_ << kSubstitutionNinjaNames[SUBSTITUTION_INCLUDE_DIRS] << " =";
333 PathOutput include_path_output(
334 path_output_.current_dir(),
335 settings_->build_settings()->root_path_utf8(),
336 ESCAPE_NINJA_COMMAND);
337 RecursiveTargetConfigToStream<SourceDir>(
338 target_, &ConfigValues::include_dirs,
339 IncludeWriter(include_path_output), out_);
340 out_ << std::endl;
343 bool has_precompiled_headers =
344 target_->config_values().has_precompiled_headers();
346 // Some toolchains pass cflags to the assembler since it's the same command,
347 // and cflags_c might also be sent to the objective C compiler.
349 // TODO(brettw) remove the SOURCE_M from the CFLAGS_C writing once the Chrome
350 // Mac build is updated not to pass cflags_c to .m files.
351 EscapeOptions opts = GetFlagOptions();
352 if (used_types.Get(SOURCE_C) || used_types.Get(SOURCE_CPP) ||
353 used_types.Get(SOURCE_M) || used_types.Get(SOURCE_MM) ||
354 used_types.Get(SOURCE_S) || used_types.Get(SOURCE_ASM)) {
355 WriteOneFlag(SUBSTITUTION_CFLAGS, false, Toolchain::TYPE_NONE,
356 &ConfigValues::cflags, opts);
358 if (used_types.Get(SOURCE_C) || used_types.Get(SOURCE_M) ||
359 used_types.Get(SOURCE_S) || used_types.Get(SOURCE_ASM)) {
360 WriteOneFlag(SUBSTITUTION_CFLAGS_C, has_precompiled_headers,
361 Toolchain::TYPE_CC, &ConfigValues::cflags_c, opts);
363 if (used_types.Get(SOURCE_CPP)) {
364 WriteOneFlag(SUBSTITUTION_CFLAGS_CC, has_precompiled_headers,
365 Toolchain::TYPE_CXX, &ConfigValues::cflags_cc, opts);
367 if (used_types.Get(SOURCE_M)) {
368 WriteOneFlag(SUBSTITUTION_CFLAGS_OBJC, has_precompiled_headers,
369 Toolchain::TYPE_OBJC, &ConfigValues::cflags_objc, opts);
371 if (used_types.Get(SOURCE_MM)) {
372 WriteOneFlag(SUBSTITUTION_CFLAGS_OBJCC, has_precompiled_headers,
373 Toolchain::TYPE_OBJCXX, &ConfigValues::cflags_objcc, opts);
376 WriteSharedVars(subst);
379 void NinjaBinaryTargetWriter::WriteOneFlag(
380 SubstitutionType subst_enum,
381 bool has_precompiled_headers,
382 Toolchain::ToolType tool_type,
383 const std::vector<std::string>& (ConfigValues::* getter)() const,
384 EscapeOptions flag_escape_options) {
385 if (!target_->toolchain()->substitution_bits().used[subst_enum])
386 return;
388 out_ << kSubstitutionNinjaNames[subst_enum] << " =";
390 if (has_precompiled_headers) {
391 const Tool* tool = target_->toolchain()->GetTool(tool_type);
392 if (tool && tool->precompiled_header_type() == Tool::PCH_MSVC) {
393 // Name the .pch file.
394 out_ << " /Fp";
395 path_output_.WriteFile(out_, GetWindowsPCHFile(tool_type));
397 // Enables precompiled headers and names the .h file. It's a string
398 // rather than a file name (so no need to rebase or use path_output_).
399 out_ << " /Yu" << target_->config_values().precompiled_header();
403 RecursiveTargetConfigStringsToStream(target_, getter,
404 flag_escape_options, out_);
405 out_ << std::endl;
408 void NinjaBinaryTargetWriter::WritePrecompiledHeaderCommands(
409 const SourceFileTypeSet& used_types,
410 const OutputFile& order_only_dep,
411 std::vector<OutputFile>* object_files) {
412 if (!target_->config_values().has_precompiled_headers())
413 return;
415 const Tool* tool_c = target_->toolchain()->GetTool(Toolchain::TYPE_CC);
416 if (tool_c &&
417 tool_c->precompiled_header_type() == Tool::PCH_MSVC &&
418 used_types.Get(SOURCE_C)) {
419 WriteWindowsPCHCommand(SUBSTITUTION_CFLAGS_C,
420 Toolchain::TYPE_CC,
421 order_only_dep, object_files);
423 const Tool* tool_cxx = target_->toolchain()->GetTool(Toolchain::TYPE_CXX);
424 if (tool_cxx &&
425 tool_cxx->precompiled_header_type() == Tool::PCH_MSVC &&
426 used_types.Get(SOURCE_CPP)) {
427 WriteWindowsPCHCommand(SUBSTITUTION_CFLAGS_CC,
428 Toolchain::TYPE_CXX,
429 order_only_dep, object_files);
433 void NinjaBinaryTargetWriter::WriteWindowsPCHCommand(
434 SubstitutionType flag_type,
435 Toolchain::ToolType tool_type,
436 const OutputFile& order_only_dep,
437 std::vector<OutputFile>* object_files) {
438 // Compute the object file (it will be language-specific).
439 std::vector<OutputFile> outputs;
440 GetWindowsPCHObjectFiles(target_, tool_type, &outputs);
441 if (outputs.empty())
442 return;
443 object_files->insert(object_files->end(), outputs.begin(), outputs.end());
445 // Build line to compile the file.
446 WriteCompilerBuildLine(target_->config_values().precompiled_source(),
447 std::vector<OutputFile>(), order_only_dep, tool_type,
448 outputs);
450 // This build line needs a custom language-specific flags value. It needs to
451 // include the switch to generate the .pch file in addition to the normal
452 // ones. Rule-specific variables are just indented underneath the rule line,
453 // and this defines the new one in terms of the old value.
454 out_ << " " << kSubstitutionNinjaNames[flag_type] << " =";
455 out_ << " ${" << kSubstitutionNinjaNames[flag_type] << "}";
457 // Append the command to generate the .pch file.
458 out_ << " /Yc" << target_->config_values().precompiled_header();
460 // Write two blank lines to help separate the PCH build lines from the
461 // regular source build lines.
462 out_ << std::endl << std::endl;
465 void NinjaBinaryTargetWriter::WriteSources(
466 const std::vector<OutputFile>& extra_deps,
467 const OutputFile& order_only_dep,
468 std::vector<OutputFile>* object_files,
469 std::vector<SourceFile>* other_files) {
470 object_files->reserve(object_files->size() + target_->sources().size());
472 std::vector<OutputFile> tool_outputs; // Prevent reallocation in loop.
473 for (const auto& source : target_->sources()) {
474 Toolchain::ToolType tool_type = Toolchain::TYPE_NONE;
475 if (!GetOutputFilesForSource(target_, source, &tool_type, &tool_outputs)) {
476 if (GetSourceFileType(source) == SOURCE_DEF)
477 other_files->push_back(source);
478 continue; // No output for this source.
481 if (tool_type != Toolchain::TYPE_NONE) {
482 WriteCompilerBuildLine(source, extra_deps, order_only_dep, tool_type,
483 tool_outputs);
486 // It's theoretically possible for a compiler to produce more than one
487 // output, but we'll only link to the first output.
488 object_files->push_back(tool_outputs[0]);
490 out_ << std::endl;
493 void NinjaBinaryTargetWriter::WriteCompilerBuildLine(
494 const SourceFile& source,
495 const std::vector<OutputFile>& extra_deps,
496 const OutputFile& order_only_dep,
497 Toolchain::ToolType tool_type,
498 const std::vector<OutputFile>& outputs) {
499 out_ << "build";
500 path_output_.WriteFiles(out_, outputs);
502 out_ << ": " << rule_prefix_ << Toolchain::ToolTypeToName(tool_type);
503 out_ << " ";
504 path_output_.WriteFile(out_, source);
506 if (!extra_deps.empty()) {
507 out_ << " |";
508 for (const OutputFile& dep : extra_deps) {
509 out_ << " ";
510 path_output_.WriteFile(out_, dep);
514 if (!order_only_dep.value().empty()) {
515 out_ << " || ";
516 path_output_.WriteFile(out_, order_only_dep);
518 out_ << std::endl;
521 void NinjaBinaryTargetWriter::WriteLinkerStuff(
522 const std::vector<OutputFile>& object_files,
523 const std::vector<SourceFile>& other_files) {
524 std::vector<OutputFile> output_files;
525 SubstitutionWriter::ApplyListToLinkerAsOutputFile(
526 target_, tool_, tool_->outputs(), &output_files);
528 out_ << "build";
529 path_output_.WriteFiles(out_, output_files);
531 out_ << ": " << rule_prefix_
532 << Toolchain::ToolTypeToName(
533 target_->toolchain()->GetToolTypeForTargetFinalOutput(target_));
535 UniqueVector<OutputFile> extra_object_files;
536 UniqueVector<const Target*> linkable_deps;
537 UniqueVector<const Target*> non_linkable_deps;
538 GetDeps(&extra_object_files, &linkable_deps, &non_linkable_deps);
540 // Object files.
541 path_output_.WriteFiles(out_, object_files);
542 path_output_.WriteFiles(out_, extra_object_files);
544 // Dependencies.
545 std::vector<OutputFile> implicit_deps;
546 std::vector<OutputFile> solibs;
547 for (const Target* cur : linkable_deps) {
548 // All linkable deps should have a link output file.
549 DCHECK(!cur->link_output_file().value().empty())
550 << "No link output file for "
551 << target_->label().GetUserVisibleName(false);
553 if (cur->dependency_output_file().value() !=
554 cur->link_output_file().value()) {
555 // This is a shared library with separate link and deps files. Save for
556 // later.
557 implicit_deps.push_back(cur->dependency_output_file());
558 solibs.push_back(cur->link_output_file());
559 } else {
560 // Normal case, just link to this target.
561 out_ << " ";
562 path_output_.WriteFile(out_, cur->link_output_file());
566 const SourceFile* optional_def_file = nullptr;
567 if (!other_files.empty()) {
568 for (const SourceFile& src_file : other_files) {
569 if (GetSourceFileType(src_file) == SOURCE_DEF) {
570 optional_def_file = &src_file;
571 implicit_deps.push_back(
572 OutputFile(settings_->build_settings(), src_file));
573 break; // Only one def file is allowed.
578 // Append implicit dependencies collected above.
579 if (!implicit_deps.empty()) {
580 out_ << " |";
581 path_output_.WriteFiles(out_, implicit_deps);
584 // Append data dependencies as order-only dependencies.
586 // This will include data dependencies and input dependencies (like when
587 // this target depends on an action). Having the data dependencies in this
588 // list ensures that the data is available at runtime when the user builds
589 // this target.
591 // The action dependencies are not strictly necessary in this case. They
592 // should also have been collected via the input deps stamp that each source
593 // file has for an order-only dependency, and since this target depends on
594 // the sources, there is already an implicit order-only dependency. However,
595 // it's extra work to separate these out and there's no disadvantage to
596 // listing them again.
597 WriteOrderOnlyDependencies(non_linkable_deps);
599 // End of the link "build" line.
600 out_ << std::endl;
602 // These go in the inner scope of the link line.
603 WriteLinkerFlags(optional_def_file);
605 WriteLibs();
606 WriteOutputExtension();
607 WriteSolibs(solibs);
610 void NinjaBinaryTargetWriter::WriteLinkerFlags(
611 const SourceFile* optional_def_file) {
612 out_ << " ldflags =";
614 // First the ldflags from the target and its config.
615 EscapeOptions flag_options = GetFlagOptions();
616 RecursiveTargetConfigStringsToStream(target_, &ConfigValues::ldflags,
617 flag_options, out_);
619 // Followed by library search paths that have been recursively pushed
620 // through the dependency tree.
621 const OrderedSet<SourceDir> all_lib_dirs = target_->all_lib_dirs();
622 if (!all_lib_dirs.empty()) {
623 // Since we're passing these on the command line to the linker and not
624 // to Ninja, we need to do shell escaping.
625 PathOutput lib_path_output(path_output_.current_dir(),
626 settings_->build_settings()->root_path_utf8(),
627 ESCAPE_NINJA_COMMAND);
628 for (size_t i = 0; i < all_lib_dirs.size(); i++) {
629 out_ << " " << tool_->lib_dir_switch();
630 lib_path_output.WriteDir(out_, all_lib_dirs[i],
631 PathOutput::DIR_NO_LAST_SLASH);
635 if (optional_def_file) {
636 out_ << " /DEF:";
637 path_output_.WriteFile(out_, *optional_def_file);
640 out_ << std::endl;
643 void NinjaBinaryTargetWriter::WriteLibs() {
644 out_ << " libs =";
646 // Libraries that have been recursively pushed through the dependency tree.
647 EscapeOptions lib_escape_opts;
648 lib_escape_opts.mode = ESCAPE_NINJA_COMMAND;
649 const OrderedSet<std::string> all_libs = target_->all_libs();
650 const std::string framework_ending(".framework");
651 for (size_t i = 0; i < all_libs.size(); i++) {
652 if (settings_->IsMac() &&
653 base::EndsWith(all_libs[i], framework_ending,
654 base::CompareCase::INSENSITIVE_ASCII)) {
655 // Special-case libraries ending in ".framework" on Mac. Add the
656 // -framework switch and don't add the extension to the output.
657 out_ << " -framework ";
658 EscapeStringToStream(out_,
659 all_libs[i].substr(0, all_libs[i].size() - framework_ending.size()),
660 lib_escape_opts);
661 } else {
662 out_ << " " << tool_->lib_switch();
663 EscapeStringToStream(out_, all_libs[i], lib_escape_opts);
666 out_ << std::endl;
669 void NinjaBinaryTargetWriter::WriteOutputExtension() {
670 out_ << " output_extension = ";
671 if (target_->output_extension().empty()) {
672 // Use the default from the tool.
673 out_ << tool_->default_output_extension();
674 } else {
675 // Use the one specified in the target. Note that the one in the target
676 // does not include the leading dot, so add that.
677 out_ << "." << target_->output_extension();
679 out_ << std::endl;
682 void NinjaBinaryTargetWriter::WriteSolibs(
683 const std::vector<OutputFile>& solibs) {
684 if (solibs.empty())
685 return;
687 out_ << " solibs =";
688 path_output_.WriteFiles(out_, solibs);
689 out_ << std::endl;
692 void NinjaBinaryTargetWriter::WriteSourceSetStamp(
693 const std::vector<OutputFile>& object_files) {
694 // The stamp rule for source sets is generally not used, since targets that
695 // depend on this will reference the object files directly. However, writing
696 // this rule allows the user to type the name of the target and get a build
697 // which can be convenient for development.
698 UniqueVector<OutputFile> extra_object_files;
699 UniqueVector<const Target*> linkable_deps;
700 UniqueVector<const Target*> non_linkable_deps;
701 GetDeps(&extra_object_files, &linkable_deps, &non_linkable_deps);
703 // The classifier should never put extra object files in a source set:
704 // any source sets that we depend on should appear in our non-linkable
705 // deps instead.
706 DCHECK(extra_object_files.empty());
708 std::vector<OutputFile> order_only_deps;
709 for (const auto& dep : non_linkable_deps)
710 order_only_deps.push_back(dep->dependency_output_file());
712 WriteStampForTarget(object_files, order_only_deps);
715 void NinjaBinaryTargetWriter::GetDeps(
716 UniqueVector<OutputFile>* extra_object_files,
717 UniqueVector<const Target*>* linkable_deps,
718 UniqueVector<const Target*>* non_linkable_deps) const {
719 // Normal public/private deps.
720 for (const auto& pair : target_->GetDeps(Target::DEPS_LINKED)) {
721 ClassifyDependency(pair.ptr, extra_object_files,
722 linkable_deps, non_linkable_deps);
725 // Inherited libraries.
726 for (const auto& inherited_target :
727 target_->inherited_libraries().GetOrdered()) {
728 ClassifyDependency(inherited_target, extra_object_files,
729 linkable_deps, non_linkable_deps);
732 // Data deps.
733 for (const auto& data_dep_pair : target_->data_deps())
734 non_linkable_deps->push_back(data_dep_pair.ptr);
737 void NinjaBinaryTargetWriter::ClassifyDependency(
738 const Target* dep,
739 UniqueVector<OutputFile>* extra_object_files,
740 UniqueVector<const Target*>* linkable_deps,
741 UniqueVector<const Target*>* non_linkable_deps) const {
742 // Only the following types of outputs have libraries linked into them:
743 // EXECUTABLE
744 // SHARED_LIBRARY
745 // _complete_ STATIC_LIBRARY
747 // Child deps of intermediate static libraries get pushed up the
748 // dependency tree until one of these is reached, and source sets
749 // don't link at all.
750 bool can_link_libs = target_->IsFinal();
752 if (dep->output_type() == Target::SOURCE_SET) {
753 // Source sets have their object files linked into final targets
754 // (shared libraries, executables, and complete static
755 // libraries). Intermediate static libraries and other source sets
756 // just forward the dependency, otherwise the files in the source
757 // set can easily get linked more than once which will cause
758 // multiple definition errors.
759 if (can_link_libs)
760 AddSourceSetObjectFiles(dep, extra_object_files);
762 // Add the source set itself as a non-linkable dependency on the current
763 // target. This will make sure that anything the source set's stamp file
764 // depends on (like data deps) are also built before the current target
765 // can be complete. Otherwise, these will be skipped since this target
766 // will depend only on the source set's object files.
767 non_linkable_deps->push_back(dep);
768 } else if (can_link_libs && dep->IsLinkable()) {
769 linkable_deps->push_back(dep);
770 } else {
771 non_linkable_deps->push_back(dep);
775 void NinjaBinaryTargetWriter::WriteOrderOnlyDependencies(
776 const UniqueVector<const Target*>& non_linkable_deps) {
777 if (!non_linkable_deps.empty()) {
778 out_ << " ||";
780 // Non-linkable targets.
781 for (const auto& non_linkable_dep : non_linkable_deps) {
782 out_ << " ";
783 path_output_.WriteFile(out_, non_linkable_dep->dependency_output_file());
788 OutputFile NinjaBinaryTargetWriter::GetWindowsPCHFile(
789 Toolchain::ToolType tool_type) const {
790 // Use "obj/{dir}/{target_name}_{lang}.pch" which ends up
791 // looking like "obj/chrome/browser/browser.cc.pch"
792 OutputFile ret = GetTargetOutputDirAsOutputFile(target_);
793 ret.value().append(target_->label().name());
794 ret.value().push_back('_');
795 ret.value().append(GetPCHLangForToolType(tool_type));
796 ret.value().append(".pch");
798 return ret;
801 bool NinjaBinaryTargetWriter::CheckForDuplicateObjectFiles(
802 const std::vector<OutputFile>& files) const {
803 base::hash_set<std::string> set;
804 for (const auto& file : files) {
805 if (!set.insert(file.value()).second) {
806 Err err(
807 target_->defined_from(),
808 "Duplicate object file",
809 "The target " + target_->label().GetUserVisibleName(false) +
810 "\ngenerates two object files with the same name:\n " +
811 file.value() + "\n"
812 "\n"
813 "It could be you accidentally have a file listed twice in the\n"
814 "sources. Or, depending on how your toolchain maps sources to\n"
815 "object files, two source files with the same name in different\n"
816 "directories could map to the same object file.\n"
817 "\n"
818 "In the latter case, either rename one of the files or move one of\n"
819 "the sources to a separate source_set to avoid them both being in\n"
820 "the same target.");
821 g_scheduler->FailWithError(err);
822 return false;
825 return true;