| // Copyright 2019 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 "gn/ninja_rust_binary_target_writer.h" |
| |
| #include <sstream> |
| |
| #include "base/strings/string_util.h" |
| #include "gn/deps_iterator.h" |
| #include "gn/filesystem_utils.h" |
| #include "gn/general_tool.h" |
| #include "gn/ninja_target_command_util.h" |
| #include "gn/ninja_utils.h" |
| #include "gn/rust_substitution_type.h" |
| #include "gn/substitution_writer.h" |
| #include "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; |
| } |
| |
| void WriteVar(const char* name, |
| const std::string& value, |
| EscapeOptions opts, |
| std::ostream& out) { |
| out << name << " = "; |
| EscapeStringToStream(out, value, opts); |
| out << std::endl; |
| } |
| |
| void WriteCrateVars(const Target* target, |
| const Tool* tool, |
| EscapeOptions opts, |
| std::ostream& out) { |
| WriteVar(kRustSubstitutionCrateName.ninja_name, |
| target->rust_values().crate_name(), opts, out); |
| |
| std::string crate_type; |
| switch (target->rust_values().crate_type()) { |
| // Auto-select the crate type for executables, static libraries, and rlibs. |
| case RustValues::CRATE_AUTO: { |
| switch (target->output_type()) { |
| case Target::EXECUTABLE: |
| crate_type = "bin"; |
| break; |
| case Target::STATIC_LIBRARY: |
| crate_type = "staticlib"; |
| break; |
| case Target::RUST_LIBRARY: |
| crate_type = "rlib"; |
| break; |
| case Target::RUST_PROC_MACRO: |
| crate_type = "proc-macro"; |
| break; |
| default: |
| NOTREACHED(); |
| } |
| break; |
| } |
| case RustValues::CRATE_BIN: |
| crate_type = "bin"; |
| break; |
| case RustValues::CRATE_CDYLIB: |
| crate_type = "cdylib"; |
| break; |
| case RustValues::CRATE_DYLIB: |
| crate_type = "dylib"; |
| break; |
| case RustValues::CRATE_PROC_MACRO: |
| crate_type = "proc-macro"; |
| break; |
| case RustValues::CRATE_RLIB: |
| crate_type = "rlib"; |
| break; |
| case RustValues::CRATE_STATICLIB: |
| crate_type = "staticlib"; |
| break; |
| default: |
| NOTREACHED(); |
| } |
| WriteVar(kRustSubstitutionCrateType.ninja_name, crate_type, opts, out); |
| |
| WriteVar(SubstitutionOutputExtension.ninja_name, |
| SubstitutionWriter::GetLinkerSubstitution( |
| target, tool, &SubstitutionOutputExtension), |
| opts, out); |
| WriteVar(SubstitutionOutputDir.ninja_name, |
| SubstitutionWriter::GetLinkerSubstitution(target, tool, |
| &SubstitutionOutputDir), |
| opts, out); |
| } |
| |
| } // namespace |
| |
| NinjaRustBinaryTargetWriter::NinjaRustBinaryTargetWriter(const Target* target, |
| std::ostream& out) |
| : NinjaBinaryTargetWriter(target, out), |
| tool_(target->toolchain()->GetToolForTargetFinalOutputAsRust(target)) {} |
| |
| NinjaRustBinaryTargetWriter::~NinjaRustBinaryTargetWriter() = default; |
| |
| // TODO(juliehockett): add inherited library support? and IsLinkable support? |
| // for c-cross-compat |
| void NinjaRustBinaryTargetWriter::Run() { |
| OutputFile input_dep = WriteInputsStampAndGetDep(); |
| |
| // The input dependencies will be an order-only dependency. This will cause |
| // Ninja to make sure the inputs are up to date before compiling this source, |
| // but changes in the inputs deps won't cause the file to be recompiled. See |
| // the comment on NinjaCBinaryTargetWriter::Run for more detailed explanation. |
| size_t num_stamp_uses = target_->sources().size(); |
| std::vector<OutputFile> order_only_deps = WriteInputDepsStampAndGetDep( |
| std::vector<const Target*>(), num_stamp_uses); |
| |
| // Public rust_library deps go in a --extern rlibs, public non-rust deps go in |
| // -Ldependency rustdeps, and non-public source_sets get passed in as normal |
| // source files |
| UniqueVector<OutputFile> deps; |
| AddSourceSetFiles(target_, &deps); |
| if (target_->output_type() == Target::SOURCE_SET) { |
| WriteSharedVars(target_->toolchain()->substitution_bits()); |
| WriteSourceSetStamp(deps.vector()); |
| } else { |
| WriteCompilerVars(); |
| UniqueVector<const Target*> linkable_deps; |
| UniqueVector<const Target*> non_linkable_deps; |
| UniqueVector<const Target*> framework_deps; |
| GetDeps(&deps, &linkable_deps, &non_linkable_deps, &framework_deps); |
| |
| if (!input_dep.value().empty()) |
| order_only_deps.push_back(input_dep); |
| |
| std::vector<OutputFile> rustdeps; |
| std::vector<OutputFile> nonrustdeps; |
| for (const auto* framework_dep : framework_deps) { |
| order_only_deps.push_back(framework_dep->dependency_output_file()); |
| } |
| for (const auto* non_linkable_dep : non_linkable_deps) { |
| if (non_linkable_dep->source_types_used().RustSourceUsed() && |
| non_linkable_dep->output_type() != Target::SOURCE_SET) { |
| rustdeps.push_back(non_linkable_dep->dependency_output_file()); |
| } |
| order_only_deps.push_back(non_linkable_dep->dependency_output_file()); |
| } |
| for (const auto* linkable_dep : linkable_deps) { |
| if (linkable_dep->source_types_used().RustSourceUsed()) { |
| rustdeps.push_back(linkable_dep->link_output_file()); |
| } else { |
| nonrustdeps.push_back(linkable_dep->link_output_file()); |
| } |
| deps.push_back(linkable_dep->dependency_output_file()); |
| } |
| |
| // Rust libraries specified by paths. |
| for (ConfigValuesIterator iter(target_); !iter.done(); iter.Next()) { |
| const ConfigValues& cur = iter.cur(); |
| for (const auto& e : cur.externs()) { |
| if (e.second.is_source_file()) { |
| deps.push_back(OutputFile(settings_->build_settings(), |
| e.second.source_file())); |
| } |
| } |
| } |
| |
| std::vector<OutputFile> tool_outputs; |
| SubstitutionWriter::ApplyListToLinkerAsOutputFile( |
| target_, tool_, tool_->outputs(), &tool_outputs); |
| WriteCompilerBuildLine(target_->rust_values().crate_root(), deps.vector(), |
| order_only_deps, tool_->name(), tool_outputs); |
| |
| std::vector<const Target*> extern_deps(linkable_deps.vector()); |
| std::copy(non_linkable_deps.begin(), non_linkable_deps.end(), |
| std::back_inserter(extern_deps)); |
| WriteExterns(extern_deps); |
| WriteRustdeps(rustdeps, nonrustdeps); |
| } |
| } |
| |
| void NinjaRustBinaryTargetWriter::WriteCompilerVars() { |
| const SubstitutionBits& subst = target_->toolchain()->substitution_bits(); |
| |
| EscapeOptions opts = GetFlagOptions(); |
| WriteCrateVars(target_, tool_, opts, out_); |
| |
| WriteOneFlag(target_, &kRustSubstitutionRustFlags, false, Tool::kToolNone, |
| &ConfigValues::rustflags, opts, path_output_, out_); |
| |
| WriteOneFlag(target_, &kRustSubstitutionRustEnv, false, Tool::kToolNone, |
| &ConfigValues::rustenv, opts, path_output_, out_); |
| |
| WriteSharedVars(subst); |
| } |
| |
| void NinjaRustBinaryTargetWriter::WriteExterns( |
| const std::vector<const Target*>& deps) { |
| out_ << " externs ="; |
| |
| for (const Target* target : deps) { |
| if (target->output_type() == Target::RUST_LIBRARY || |
| target->output_type() == Target::RUST_PROC_MACRO) { |
| out_ << " --extern "; |
| const auto& renamed_dep = |
| target_->rust_values().aliased_deps().find(target->label()); |
| if (renamed_dep != target_->rust_values().aliased_deps().end()) { |
| out_ << renamed_dep->second << "="; |
| } else { |
| out_ << std::string(target->rust_values().crate_name()) << "="; |
| } |
| path_output_.WriteFile(out_, target->dependency_output_file()); |
| } |
| } |
| |
| EscapeOptions extern_escape_opts; |
| extern_escape_opts.mode = ESCAPE_NINJA_COMMAND; |
| |
| for (ConfigValuesIterator iter(target_); !iter.done(); iter.Next()) { |
| const ConfigValues& cur = iter.cur(); |
| for (const auto& e : cur.externs()) { |
| out_ << " --extern " << std::string(e.first) << "="; |
| if (e.second.is_source_file()) { |
| path_output_.WriteFile(out_, e.second.source_file()); |
| } else { |
| EscapeStringToStream(out_, e.second.value(), extern_escape_opts); |
| } |
| } |
| } |
| |
| out_ << std::endl; |
| } |
| |
| void NinjaRustBinaryTargetWriter::WriteRustdeps( |
| const std::vector<OutputFile>& rustdeps, |
| const std::vector<OutputFile>& nonrustdeps) { |
| out_ << " rustdeps ="; |
| |
| // Rust dependencies. |
| for (const auto& rustdep : rustdeps) { |
| out_ << " -Ldependency="; |
| path_output_.WriteDir( |
| out_, rustdep.AsSourceFile(settings_->build_settings()).GetDir(), |
| PathOutput::DIR_NO_LAST_SLASH); |
| } |
| |
| EscapeOptions lib_escape_opts; |
| lib_escape_opts.mode = ESCAPE_NINJA_COMMAND; |
| const std::string_view lib_prefix("lib"); |
| |
| // Non-Rust native dependencies. |
| for (const auto& nonrustdep : nonrustdeps) { |
| out_ << " -Lnative="; |
| path_output_.WriteDir( |
| out_, nonrustdep.AsSourceFile(settings_->build_settings()).GetDir(), |
| PathOutput::DIR_NO_LAST_SLASH); |
| std::string_view file = FindFilenameNoExtension(&nonrustdep.value()); |
| if (!file.compare(0, lib_prefix.size(), lib_prefix)) { |
| out_ << " -l"; |
| EscapeStringToStream(out_, file.substr(lib_prefix.size()), |
| lib_escape_opts); |
| } else { |
| out_ << " -Clink-arg="; |
| path_output_.WriteFile(out_, nonrustdep); |
| } |
| } |
| |
| WriteLinkerFlags(out_, tool_, nullptr); |
| WriteLibs(out_, tool_); |
| out_ << std::endl; |
| } |