| // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "tools/gn/ninja_binary_target_writer.h" |
| |
| #include <sstream> |
| |
| #include "base/strings/string_util.h" |
| #include "tools/gn/config_values_extractors.h" |
| #include "tools/gn/deps_iterator.h" |
| #include "tools/gn/filesystem_utils.h" |
| #include "tools/gn/general_tool.h" |
| #include "tools/gn/ninja_c_binary_target_writer.h" |
| #include "tools/gn/ninja_rust_binary_target_writer.h" |
| #include "tools/gn/ninja_utils.h" |
| #include "tools/gn/settings.h" |
| #include "tools/gn/string_utils.h" |
| #include "tools/gn/substitution_writer.h" |
| #include "tools/gn/target.h" |
| |
| namespace { |
| |
| // Returns the proper escape options for writing compiler and linker flags. |
| EscapeOptions GetFlagOptions() { |
| EscapeOptions opts; |
| opts.mode = ESCAPE_NINJA_COMMAND; |
| return opts; |
| } |
| |
| } // namespace |
| |
| NinjaBinaryTargetWriter::NinjaBinaryTargetWriter(const Target* target, |
| std::ostream& out) |
| : NinjaTargetWriter(target, out), |
| rule_prefix_(GetNinjaRulePrefixForToolchain(settings_)) {} |
| |
| NinjaBinaryTargetWriter::~NinjaBinaryTargetWriter() = default; |
| |
| void NinjaBinaryTargetWriter::Run() { |
| if (target_->source_types_used().RustSourceUsed()) { |
| NinjaRustBinaryTargetWriter writer(target_, out_); |
| writer.Run(); |
| return; |
| } |
| |
| NinjaCBinaryTargetWriter writer(target_, out_); |
| writer.Run(); |
| } |
| |
| OutputFile NinjaBinaryTargetWriter::WriteInputsStampAndGetDep() const { |
| CHECK(target_->toolchain()) << "Toolchain not set on target " |
| << target_->label().GetUserVisibleName(true); |
| |
| UniqueVector<const SourceFile*> inputs; |
| for (ConfigValuesIterator iter(target_); !iter.done(); iter.Next()) { |
| for (const auto& input : iter.cur().inputs()) { |
| inputs.push_back(&input); |
| } |
| } |
| |
| if (inputs.size() == 0) |
| return OutputFile(); // No inputs |
| |
| // If we only have one input, return it directly instead of writing a stamp |
| // file for it. |
| if (inputs.size() == 1) |
| return OutputFile(settings_->build_settings(), *inputs[0]); |
| |
| // Make a stamp file. |
| OutputFile stamp_file = |
| GetBuildDirForTargetAsOutputFile(target_, BuildDirType::OBJ); |
| stamp_file.value().append(target_->label().name()); |
| stamp_file.value().append(".inputs.stamp"); |
| |
| out_ << "build "; |
| path_output_.WriteFile(out_, stamp_file); |
| out_ << ": " << GetNinjaRulePrefixForToolchain(settings_) |
| << GeneralTool::kGeneralToolStamp; |
| |
| // File inputs. |
| for (const auto* input : inputs) { |
| out_ << " "; |
| path_output_.WriteFile(out_, *input); |
| } |
| |
| out_ << std::endl; |
| return stamp_file; |
| } |
| |
| void NinjaBinaryTargetWriter::WriteSourceSetStamp( |
| const std::vector<OutputFile>& object_files) { |
| // The stamp rule for source sets is generally not used, since targets that |
| // depend on this will reference the object files directly. However, writing |
| // this rule allows the user to type the name of the target and get a build |
| // which can be convenient for development. |
| UniqueVector<OutputFile> extra_object_files; |
| UniqueVector<const Target*> linkable_deps; |
| UniqueVector<const Target*> non_linkable_deps; |
| GetDeps(&extra_object_files, &linkable_deps, &non_linkable_deps); |
| |
| // The classifier should never put extra object files in a source sets: any |
| // source sets that we depend on should appear in our non-linkable deps |
| // instead. |
| DCHECK(extra_object_files.empty()); |
| |
| std::vector<OutputFile> order_only_deps; |
| for (auto* dep : non_linkable_deps) |
| order_only_deps.push_back(dep->dependency_output_file()); |
| |
| WriteStampForTarget(object_files, order_only_deps); |
| } |
| |
| void NinjaBinaryTargetWriter::GetDeps( |
| UniqueVector<OutputFile>* extra_object_files, |
| UniqueVector<const Target*>* linkable_deps, |
| UniqueVector<const Target*>* non_linkable_deps) const { |
| // Normal public/private deps. |
| for (const auto& pair : target_->GetDeps(Target::DEPS_LINKED)) { |
| ClassifyDependency(pair.ptr, extra_object_files, linkable_deps, |
| non_linkable_deps); |
| } |
| |
| // Inherited libraries. |
| for (auto* inherited_target : target_->inherited_libraries().GetOrdered()) { |
| ClassifyDependency(inherited_target, extra_object_files, linkable_deps, |
| non_linkable_deps); |
| } |
| |
| // Data deps. |
| for (const auto& data_dep_pair : target_->data_deps()) |
| non_linkable_deps->push_back(data_dep_pair.ptr); |
| } |
| |
| void NinjaBinaryTargetWriter::ClassifyDependency( |
| const Target* dep, |
| UniqueVector<OutputFile>* extra_object_files, |
| UniqueVector<const Target*>* linkable_deps, |
| UniqueVector<const Target*>* non_linkable_deps) const { |
| // Only the following types of outputs have libraries linked into them: |
| // EXECUTABLE |
| // SHARED_LIBRARY |
| // _complete_ STATIC_LIBRARY |
| // |
| // Child deps of intermediate static libraries get pushed up the |
| // dependency tree until one of these is reached, and source sets |
| // don't link at all. |
| bool can_link_libs = target_->IsFinal(); |
| |
| if (dep->output_type() == Target::SOURCE_SET || |
| // If a complete static library depends on an incomplete static library, |
| // manually link in the object files of the dependent library as if it |
| // were a source set. This avoids problems with braindead tools such as |
| // ar which don't properly link dependent static libraries. |
| (target_->complete_static_lib() && |
| (dep->output_type() == Target::STATIC_LIBRARY && |
| !dep->complete_static_lib()))) { |
| // Source sets have their object files linked into final targets |
| // (shared libraries, executables, loadable modules, and complete static |
| // libraries). Intermediate static libraries and other source sets |
| // just forward the dependency, otherwise the files in the source |
| // set can easily get linked more than once which will cause |
| // multiple definition errors. |
| if (can_link_libs) |
| AddSourceSetFiles(dep, extra_object_files); |
| |
| // Add the source set itself as a non-linkable dependency on the current |
| // target. This will make sure that anything the source set's stamp file |
| // depends on (like data deps) are also built before the current target |
| // can be complete. Otherwise, these will be skipped since this target |
| // will depend only on the source set's object files. |
| non_linkable_deps->push_back(dep); |
| } else if (target_->output_type() == Target::RUST_LIBRARY && |
| dep->IsLinkable()) { |
| // Rust libraries aren't final, but need to have the link lines of all |
| // transitive deps specified. |
| linkable_deps->push_back(dep); |
| } else if (target_->complete_static_lib() && dep->IsFinal()) { |
| non_linkable_deps->push_back(dep); |
| } else if (can_link_libs && dep->IsLinkable()) { |
| linkable_deps->push_back(dep); |
| } else { |
| non_linkable_deps->push_back(dep); |
| } |
| } |
| |
| void NinjaBinaryTargetWriter::AddSourceSetFiles( |
| const Target* source_set, |
| UniqueVector<OutputFile>* obj_files) const { |
| // Just add all sources to the list. |
| for (const auto& source : source_set->sources()) { |
| obj_files->push_back(OutputFile(settings_->build_settings(), source)); |
| } |
| } |
| |
| void NinjaBinaryTargetWriter::WriteCompilerBuildLine( |
| const SourceFile& source, |
| const std::vector<OutputFile>& extra_deps, |
| const std::vector<OutputFile>& order_only_deps, |
| const char* tool_name, |
| const std::vector<OutputFile>& outputs) { |
| out_ << "build"; |
| path_output_.WriteFiles(out_, outputs); |
| |
| out_ << ": " << rule_prefix_ << tool_name; |
| out_ << " "; |
| path_output_.WriteFile(out_, source); |
| |
| if (!extra_deps.empty()) { |
| out_ << " |"; |
| path_output_.WriteFiles(out_, extra_deps); |
| } |
| |
| if (!order_only_deps.empty()) { |
| out_ << " ||"; |
| path_output_.WriteFiles(out_, order_only_deps); |
| } |
| out_ << std::endl; |
| } |
| |
| void NinjaBinaryTargetWriter::WriteLinkerFlags( |
| std::ostream& out, |
| const Tool* tool, |
| const SourceFile* optional_def_file) { |
| if (tool->AsC()) { |
| // First the ldflags from the target and its config. |
| RecursiveTargetConfigStringsToStream(target_, &ConfigValues::ldflags, |
| GetFlagOptions(), out); |
| } |
| |
| // Followed by library search paths that have been recursively pushed |
| // through the dependency tree. |
| const OrderedSet<SourceDir> all_lib_dirs = target_->all_lib_dirs(); |
| if (!all_lib_dirs.empty()) { |
| // Since we're passing these on the command line to the linker and not |
| // to Ninja, we need to do shell escaping. |
| PathOutput lib_path_output(path_output_.current_dir(), |
| settings_->build_settings()->root_path_utf8(), |
| ESCAPE_NINJA_COMMAND); |
| for (size_t i = 0; i < all_lib_dirs.size(); i++) { |
| out << " " << tool->lib_dir_switch(); |
| lib_path_output.WriteDir(out, all_lib_dirs[i], |
| PathOutput::DIR_NO_LAST_SLASH); |
| } |
| } |
| |
| if (optional_def_file) { |
| out_ << " /DEF:"; |
| path_output_.WriteFile(out, *optional_def_file); |
| } |
| } |
| |
| void NinjaBinaryTargetWriter::WriteLibs(std::ostream& out, const Tool* tool) { |
| // Libraries that have been recursively pushed through the dependency tree. |
| EscapeOptions lib_escape_opts; |
| lib_escape_opts.mode = ESCAPE_NINJA_COMMAND; |
| const OrderedSet<LibFile> all_libs = target_->all_libs(); |
| const std::string framework_ending(".framework"); |
| for (size_t i = 0; i < all_libs.size(); i++) { |
| const LibFile& lib_file = all_libs[i]; |
| const std::string& lib_value = lib_file.value(); |
| if (lib_file.is_source_file()) { |
| out << " " << tool->linker_arg(); |
| path_output_.WriteFile(out, lib_file.source_file()); |
| } else if (base::EndsWith(lib_value, framework_ending, |
| base::CompareCase::INSENSITIVE_ASCII)) { |
| // Special-case libraries ending in ".framework" to support Mac: Add the |
| // -framework switch and don't add the extension to the output. |
| out << " " << tool->framework_switch(); |
| EscapeStringToStream( |
| out, lib_value.substr(0, lib_value.size() - framework_ending.size()), |
| lib_escape_opts); |
| } else { |
| out << " " << tool->lib_switch(); |
| EscapeStringToStream(out, lib_value, lib_escape_opts); |
| } |
| } |
| } |