Support non-Rust libs and lib_dirs for Rust targets
This is necessary for mixed C/C++ and Rust projects.
Change-Id: Iadf9e8d6f2a90ca43fb304b54f4049ed18fdd252
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/5941
Commit-Queue: Petr Hosek <phosek@google.com>
Reviewed-by: Brett Wilson <brettw@chromium.org>
diff --git a/tools/gn/c_tool.cc b/tools/gn/c_tool.cc
index 59452fb..29d7850 100644
--- a/tools/gn/c_tool.cc
+++ b/tools/gn/c_tool.cc
@@ -20,6 +20,10 @@
CTool::CTool(const char* n)
: Tool(n), depsformat_(DEPS_GCC), precompiled_header_type_(PCH_NONE) {
CHECK(ValidateName(n));
+ set_framework_switch("-framework ");
+ set_lib_dir_switch("-L");
+ set_lib_switch("-l");
+ set_linker_arg("");
}
CTool::~CTool() = default;
diff --git a/tools/gn/c_tool.h b/tools/gn/c_tool.h
index 07dfcbe..f9b2653 100644
--- a/tools/gn/c_tool.h
+++ b/tools/gn/c_tool.h
@@ -69,18 +69,6 @@
precompiled_header_type_ = pch_type;
}
- const std::string& lib_switch() const { return lib_switch_; }
- void set_lib_switch(std::string s) {
- DCHECK(!complete_);
- lib_switch_ = std::move(s);
- }
-
- const std::string& lib_dir_switch() const { return lib_dir_switch_; }
- void set_lib_dir_switch(std::string s) {
- DCHECK(!complete_);
- lib_dir_switch_ = std::move(s);
- }
-
// Should match files in the outputs() if nonempty.
const SubstitutionPattern& link_output() const { return link_output_; }
void set_link_output(SubstitutionPattern link_out) {
@@ -126,8 +114,6 @@
DepsFormat depsformat_;
PrecompiledHeaderType precompiled_header_type_;
- std::string lib_switch_;
- std::string lib_dir_switch_;
SubstitutionPattern link_output_;
SubstitutionPattern depend_output_;
diff --git a/tools/gn/ninja_binary_target_writer.cc b/tools/gn/ninja_binary_target_writer.cc
index 3cf7c48..2bbb676 100644
--- a/tools/gn/ninja_binary_target_writer.cc
+++ b/tools/gn/ninja_binary_target_writer.cc
@@ -19,6 +19,17 @@
#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),
@@ -205,4 +216,63 @@
path_output_.WriteFiles(out_, order_only_deps);
}
out_ << std::endl;
-}
\ No newline at end of file
+}
+
+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);
+ }
+ }
+}
diff --git a/tools/gn/ninja_binary_target_writer.h b/tools/gn/ninja_binary_target_writer.h
index 3274867..c38ee4c 100644
--- a/tools/gn/ninja_binary_target_writer.h
+++ b/tools/gn/ninja_binary_target_writer.h
@@ -57,6 +57,11 @@
const char* tool_name,
const std::vector<OutputFile>& outputs);
+ void WriteLinkerFlags(std::ostream& out,
+ const Tool* tool,
+ const SourceFile* optional_def_file);
+ void WriteLibs(std::ostream& out, const Tool* tool);
+
virtual void AddSourceSetFiles(const Target* source_set,
UniqueVector<OutputFile>* obj_files) const;
diff --git a/tools/gn/ninja_c_binary_target_writer.cc b/tools/gn/ninja_c_binary_target_writer.cc
index 04698b5..594fec5 100644
--- a/tools/gn/ninja_c_binary_target_writer.cc
+++ b/tools/gn/ninja_c_binary_target_writer.cc
@@ -525,8 +525,12 @@
if (target_->output_type() == Target::EXECUTABLE ||
target_->output_type() == Target::SHARED_LIBRARY ||
target_->output_type() == Target::LOADABLE_MODULE) {
- WriteLinkerFlags(optional_def_file);
- WriteLibs();
+ out_ << " ldflags =";
+ WriteLinkerFlags(out_, tool_, optional_def_file);
+ out_ << std::endl;
+ out_ << " libs =";
+ WriteLibs(out_, tool_);
+ out_ << std::endl;
} else if (target_->output_type() == Target::STATIC_LIBRARY) {
out_ << " arflags =";
RecursiveTargetConfigStringsToStream(target_, &ConfigValues::arflags,
@@ -537,68 +541,6 @@
WriteSolibs(solibs);
}
-void NinjaCBinaryTargetWriter::WriteLinkerFlags(
- const SourceFile* optional_def_file) {
- out_ << " ldflags =";
-
- // 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);
- }
-
- out_ << std::endl;
-}
-
-void NinjaCBinaryTargetWriter::WriteLibs() {
- out_ << " libs =";
-
- // 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_ << " ";
- 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_ << " -framework ";
- 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);
- }
- }
- out_ << std::endl;
-}
-
void NinjaCBinaryTargetWriter::WriteOutputSubstitutions() {
out_ << " output_extension = "
<< SubstitutionWriter::GetLinkerSubstitution(
diff --git a/tools/gn/ninja_c_binary_target_writer.h b/tools/gn/ninja_c_binary_target_writer.h
index 445198f..a9a612a 100644
--- a/tools/gn/ninja_c_binary_target_writer.h
+++ b/tools/gn/ninja_c_binary_target_writer.h
@@ -82,8 +82,6 @@
void WriteLinkerStuff(const std::vector<OutputFile>& object_files,
const std::vector<SourceFile>& other_files,
const OutputFile& input_dep);
- void WriteLinkerFlags(const SourceFile* optional_def_file);
- void WriteLibs();
void WriteOutputSubstitutions();
void WriteSolibs(const std::vector<OutputFile>& solibs);
diff --git a/tools/gn/ninja_rust_binary_target_writer.cc b/tools/gn/ninja_rust_binary_target_writer.cc
index 47b3fb2..cd81b43 100644
--- a/tools/gn/ninja_rust_binary_target_writer.cc
+++ b/tools/gn/ninja_rust_binary_target_writer.cc
@@ -6,7 +6,9 @@
#include <sstream>
+#include "base/strings/string_util.h"
#include "tools/gn/deps_iterator.h"
+#include "tools/gn/filesystem_utils.h"
#include "tools/gn/general_tool.h"
#include "tools/gn/ninja_target_command_util.h"
#include "tools/gn/ninja_utils.h"
@@ -145,9 +147,12 @@
std::vector<OutputFile> rustdeps;
std::vector<OutputFile> nonrustdeps;
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->dependency_output_file());
@@ -167,7 +172,6 @@
std::copy(non_linkable_deps.begin(), non_linkable_deps.end(),
std::back_inserter(extern_deps));
WriteExterns(extern_deps);
-
WriteRustdeps(rustdeps, nonrustdeps);
}
}
@@ -220,10 +224,9 @@
void NinjaRustBinaryTargetWriter::WriteRustdeps(
const std::vector<OutputFile>& rustdeps,
const std::vector<OutputFile>& nonrustdeps) {
- if (rustdeps.empty() && nonrustdeps.empty())
- return;
-
out_ << " rustdeps =";
+
+ // Rust dependencies.
for (const auto& rustdep : rustdeps) {
out_ << " -Ldependency=";
path_output_.WriteDir(
@@ -231,11 +234,27 @@
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& rustdep : nonrustdeps) {
out_ << " -Lnative=";
path_output_.WriteDir(
out_, rustdep.AsSourceFile(settings_->build_settings()).GetDir(),
PathOutput::DIR_NO_LAST_SLASH);
+ std::string_view file = FindFilenameNoExtension(&rustdep.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_, rustdep);
+ }
}
+
+ WriteLinkerFlags(out_, tool_, nullptr);
+ WriteLibs(out_, tool_);
out_ << std::endl;
}
diff --git a/tools/gn/ninja_rust_binary_target_writer_unittest.cc b/tools/gn/ninja_rust_binary_target_writer_unittest.cc
index f9027f5..ed2e547 100644
--- a/tools/gn/ninja_rust_binary_target_writer_unittest.cc
+++ b/tools/gn/ninja_rust_binary_target_writer_unittest.cc
@@ -90,9 +90,10 @@
"\n"
"build obj/foo/foo_bar: rustc ../../foo/main.rs | ../../foo/input3.rs "
"../../foo/main.rs ../../foo/input1.rs ../../foo/input2.rs || "
- "obj/foo/sources.stamp\n";
+ "obj/foo/sources.stamp\n"
+ " rustdeps =\n";
std::string out_str = out.str();
- EXPECT_EQ(expected, out_str) << out_str;
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
}
}
@@ -130,7 +131,8 @@
"target_output_name = mylib\n"
"\n"
"build obj/bar/libmylib.rlib: rustc ../../bar/lib.rs | "
- "../../bar/mylib.rs ../../bar/lib.rs\n";
+ "../../bar/mylib.rs ../../bar/lib.rs\n"
+ " rustdeps =\n";
std::string out_str = out.str();
EXPECT_EQ(expected, out_str) << out_str;
}
@@ -299,7 +301,7 @@
"build obj/foo/foo_bar: rustc ../../foo/main.rs | ../../foo/source.rs "
"../../foo/main.rs obj/bar/libmylib.rlib obj/foo/libstatic.a\n"
" externs = --extern mylib=obj/bar/libmylib.rlib\n"
- " rustdeps = -Ldependency=obj/bar -Lnative=obj/foo\n";
+ " rustdeps = -Ldependency=obj/bar -Lnative=obj/foo -lstatic\n";
std::string out_str = out.str();
EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
}
@@ -334,7 +336,7 @@
"\n"
"build obj/foo/foo_bar: rustc ../../foo/main.rs | ../../foo/source.rs "
"../../foo/main.rs obj/foo/libstatic.a\n"
- " rustdeps = -Lnative=obj/foo\n";
+ " rustdeps = -Lnative=obj/foo -lstatic\n";
std::string out_str = out.str();
EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
}
@@ -386,9 +388,53 @@
"\n"
"build obj/foo/foo_bar.exe: rustc ../../foo/main.rs | ../../foo/input3.rs "
"../../foo/main.rs ../../foo/input1.rs ../../foo/input2.rs || "
- "obj/foo/sources.stamp\n";
+ "obj/foo/sources.stamp\n"
+ " rustdeps =\n";
std::string out_str = out.str();
- EXPECT_EQ(expected, out_str) << out_str;
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+}
+
+TEST_F(NinjaRustBinaryTargetWriterTest, LibsAndLibDirs) {
+ Err err;
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::EXECUTABLE);
+ target.visibility().SetPublic();
+ SourceFile main("//foo/main.rs");
+ target.sources().push_back(SourceFile("//foo/input.rs"));
+ target.sources().push_back(main);
+ target.source_types_used().Set(SourceFile::SOURCE_RS);
+ target.set_output_dir(SourceDir("//out/Debug/foo/"));
+ target.config_values().libs().push_back(LibFile("quux"));
+ target.config_values().lib_dirs().push_back(SourceDir("//baz/"));
+ target.rust_values().set_crate_root(main);
+ target.rust_values().crate_name() = "foo_bar";
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = foo_bar\n"
+ "crate_type = bin\n"
+ "output_dir = foo\n"
+ "rustc_output_extension = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build obj/foo/foo_bar: rustc ../../foo/main.rs | ../../foo/input.rs "
+ "../../foo/main.rs\n"
+ " rustdeps = -Lnative=../../baz -lquux\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
}
}
@@ -426,7 +472,8 @@
"target_output_name = mymacro\n"
"\n"
"build obj/bar/libmymacro.so: rustc ../../bar/lib.rs | "
- "../../bar/mylib.rs ../../bar/lib.rs\n";
+ "../../bar/mylib.rs ../../bar/lib.rs\n"
+ " rustdeps =\n";
std::string out_str = out.str();
EXPECT_EQ(expected, out_str) << out_str;
}
@@ -462,7 +509,8 @@
"\n"
"build obj/foo/foo_bar: rustc ../../foo/main.rs | ../../foo/source.rs "
"../../foo/main.rs || obj/bar/libmymacro.so\n"
- " externs = --extern mymacro=obj/bar/libmymacro.so\n";
+ " externs = --extern mymacro=obj/bar/libmymacro.so\n"
+ " rustdeps = -Ldependency=obj/bar\n";
std::string out_str = out.str();
EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
}
@@ -502,7 +550,8 @@
"target_output_name = mylib\n"
"\n"
"build obj/bar/libmylib.rlib: rustc ../../bar/lib.rs | "
- "../../bar/mylib.rs ../../bar/lib.rs\n";
+ "../../bar/mylib.rs ../../bar/lib.rs\n"
+ " rustdeps =\n";
std::string out_str = out.str();
EXPECT_EQ(expected, out_str) << out_str;
}
diff --git a/tools/gn/rust_tool.cc b/tools/gn/rust_tool.cc
index e4e127d..563b20b 100644
--- a/tools/gn/rust_tool.cc
+++ b/tools/gn/rust_tool.cc
@@ -11,6 +11,11 @@
RustTool::RustTool(const char* n) : Tool(n), rlib_output_extension_(".rlib") {
CHECK(ValidateName(n));
+ // TODO: should these be settable in toolchain definition?
+ set_framework_switch("-lframework=");
+ set_lib_dir_switch("-Lnative=");
+ set_lib_switch("-l");
+ set_linker_arg("-Clink-arg=");
}
RustTool::~RustTool() = default;
@@ -115,7 +120,6 @@
if (!ReadOutputsPatternList(scope, "outputs", &outputs_, err)) {
return false;
}
-
return true;
}
diff --git a/tools/gn/tool.h b/tools/gn/tool.h
index b963ebf..ae9dd61 100644
--- a/tools/gn/tool.h
+++ b/tools/gn/tool.h
@@ -123,6 +123,30 @@
description_ = std::move(desc);
}
+ const std::string& framework_switch() const { return framework_switch_; }
+ void set_framework_switch(std::string s) {
+ DCHECK(!complete_);
+ framework_switch_ = std::move(s);
+ }
+
+ const std::string& lib_switch() const { return lib_switch_; }
+ void set_lib_switch(std::string s) {
+ DCHECK(!complete_);
+ lib_switch_ = std::move(s);
+ }
+
+ const std::string& lib_dir_switch() const { return lib_dir_switch_; }
+ void set_lib_dir_switch(std::string s) {
+ DCHECK(!complete_);
+ lib_dir_switch_ = std::move(s);
+ }
+
+ const std::string& linker_arg() const { return linker_arg_; }
+ void set_linker_arg(std::string s) {
+ DCHECK(!complete_);
+ linker_arg_ = std::move(s);
+ }
+
const SubstitutionList& outputs() const { return outputs_; }
void set_outputs(SubstitutionList out) {
DCHECK(!complete_);
@@ -226,6 +250,10 @@
SubstitutionPattern default_output_dir_;
SubstitutionPattern depfile_;
SubstitutionPattern description_;
+ std::string framework_switch_;
+ std::string lib_switch_;
+ std::string lib_dir_switch_;
+ std::string linker_arg_;
SubstitutionList outputs_;
SubstitutionList runtime_outputs_;
std::string output_prefix_;