All accessible crates should be specified by a public --extern
Currently direct dependencies are added to the rustc command line as
`--extern cratename:path/to/rlib/file` which tells rustc how to resolve
any references to the crate. In compiler terms, it adds the dependency
crate to the currently-being-compiled crate's "extern prelude", making
it available throughout the crate.
GN uses public_deps to express that a dependency should be available
not just in the current crate but in anything that depends on it. That
means a crate that is reachable from the currently-compiling-crate
from a direct dependency + a chain-of-public_deps should also be
accessible to be used. To do so, it needs to appear in the extern
prelude by passing it to --extern.
Currently all transitive dependencies are represented on the command-
line via `-Ldependency=path/to/rlib/dir` in order for rustc to find
the crates, but this does not allow the currently-compiling-crate to
use them itself.
To resolve this issue, we add any crates publicly-accessible to the
list of externs, to be listed with the `--extern` flag. We also continue
to list them with `-Ldependency` in order for rustc to also find them
when used inside a dependency.
This is of special interest for dynamic libraries. If a rust dylib
depends on crate A via public_deps and crate B via deps, then a
target that makes use of the dylib should also gain access to crate
A without linking A into its own link target. This is now supported
as depending on the dylib would gain `--extern thedylib:path` as
well as `--extern A:path` on the rustc command line.
Along the way, we ensure that rust crate aliases written in the
currently-compiling-crate will apply not just to direct dependencies
but also to transitive public dependencies, and we add a unit test
for this case.
An example BUILD.gn that works after this change, but not without it:
rust_library("rust_repro_inner_lib") {
sources = ["rust_repro_inner_lib.rs"]
crate_root = "rust_repro_inner_lib.rs"
}
rust_library("rust_repro_dylib") {
sources = ["rust_repro_dylib.rs"]
crate_root = "rust_repro_dylib.rs"
public_deps = [":rust_repro_inner_lib"]
}
shared_library("rust_repro_dylib_so") {
public_deps = [":rust_repro_dylib"]
}
rust_library("rust_repro_lib") {
sources = ["rust_repro_lib.rs"]
crate_root = "rust_repro_lib.rs"
public_deps = [":rust_repro_dylib_so"]
}
executable("rust_repro_bin") {
sources = ["rust_repro_bin.rs"]
crate_root = "rust_repro_bin.rs"
deps = [ ":rust_repro_lib" ]
}
Without this change, rust_repro_bin.rs is unable to call functions in
rust_repro_dylib or rust_repro_inner_lib, though they are in the set of
accessible dependencies.
Bug: 276
Change-Id: I772b4728a848d9e54ffc876383990df2bf66bd42
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/13060
Reviewed-by: Brett Wilson <brettw@chromium.org>
Commit-Queue: Brett Wilson <brettw@google.com>
diff --git a/src/gn/ninja_c_binary_target_writer.cc b/src/gn/ninja_c_binary_target_writer.cc
index 5b518b3..3321191 100644
--- a/src/gn/ninja_c_binary_target_writer.cc
+++ b/src/gn/ninja_c_binary_target_writer.cc
@@ -779,7 +779,8 @@
// entire tree of transitive rlibs.
std::vector<OutputFile> transitive_rustlibs;
if (target_->IsFinal()) {
- for (const auto* dep : target_->rust_transitive_libs().GetOrdered()) {
+ for (const auto* dep :
+ target_->rust_transitive_inherited_libs().GetOrdered()) {
if (dep->output_type() == Target::RUST_LIBRARY) {
transitive_rustlibs.push_back(dep->dependency_output_file());
implicit_deps.push_back(dep->dependency_output_file());
diff --git a/src/gn/ninja_rust_binary_target_writer.cc b/src/gn/ninja_rust_binary_target_writer.cc
index d894681..0fd26c0 100644
--- a/src/gn/ninja_rust_binary_target_writer.cc
+++ b/src/gn/ninja_rust_binary_target_writer.cc
@@ -13,8 +13,8 @@
#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/rust_values.h"
+#include "gn/substitution_writer.h"
#include "gn/target.h"
namespace {
@@ -113,8 +113,8 @@
size_t num_stamp_uses = target_->sources().size();
- std::vector<OutputFile> input_deps = WriteInputsStampAndGetDep(
- num_stamp_uses);
+ std::vector<OutputFile> input_deps =
+ WriteInputsStampAndGetDep(num_stamp_uses);
WriteCompilerVars();
@@ -176,11 +176,17 @@
}
}
- // Bubble up the full list of transitive rlib dependencies.
- std::vector<OutputFile> transitive_rustlibs;
- for (const auto* dep : target_->rust_transitive_libs().GetOrdered()) {
- if (dep->source_types_used().RustSourceUsed()) {
- transitive_rustlibs.push_back(dep->dependency_output_file());
+ // Collect the full transitive set of rust libraries that this target depends
+ // on, and the public flag represents if the target has direct access to the
+ // dependency through a chain of public_deps.
+ std::vector<ExternCrate> transitive_crates;
+ for (const auto& [dep, has_direct_access] :
+ target_->rust_transitive_inherited_libs().GetOrderedAndPublicFlag()) {
+ // We will tell rustc to look for crate metadata for any rust crate
+ // dependencies except cdylibs, as they have no metadata present.
+ if (dep->source_types_used().RustSourceUsed() &&
+ RustValues::IsRustLibrary(dep)) {
+ transitive_crates.push_back({dep, has_direct_access});
}
}
@@ -196,8 +202,7 @@
std::copy(classified_deps.non_linkable_deps.begin(),
classified_deps.non_linkable_deps.end(),
std::back_inserter(extern_deps));
- WriteExterns(extern_deps);
- WriteRustdeps(transitive_rustlibs, rustdeps, nonrustdeps);
+ WriteExternsAndDeps(extern_deps, transitive_crates, rustdeps, nonrustdeps);
WriteSourcesAndInputs();
}
@@ -237,7 +242,8 @@
out_ << " sources =";
for (const auto& source : target_->sources()) {
out_ << " ";
- path_output_.WriteFile(out_, OutputFile(settings_->build_settings(), source));
+ path_output_.WriteFile(out_,
+ OutputFile(settings_->build_settings(), source));
}
for (const auto& data : target_->config_values().inputs()) {
out_ << " ";
@@ -246,67 +252,99 @@
out_ << std::endl;
}
-void NinjaRustBinaryTargetWriter::WriteExterns(
- const std::vector<const Target*>& deps) {
+void NinjaRustBinaryTargetWriter::WriteExternsAndDeps(
+ const std::vector<const Target*>& deps,
+ const std::vector<ExternCrate>& transitive_rust_deps,
+ const std::vector<OutputFile>& rustdeps,
+ const std::vector<OutputFile>& nonrustdeps) {
+ // Writes a external LibFile which comes from user-specified externs, and may
+ // be either a string or a SourceFile.
+ auto write_extern_lib_file = [this](std::string_view crate_name,
+ LibFile lib_file) {
+ out_ << " --extern ";
+ out_ << crate_name;
+ out_ << "=";
+ if (lib_file.is_source_file()) {
+ path_output_.WriteFile(out_, lib_file.source_file());
+ } else {
+ EscapeOptions escape_opts_command;
+ escape_opts_command.mode = ESCAPE_NINJA_COMMAND;
+ EscapeStringToStream(out_, lib_file.value(), escape_opts_command);
+ }
+ };
+ // Writes an external OutputFile which comes from a dependency of the current
+ // target.
+ auto write_extern_target = [this](const Target& dep) {
+ std::string_view crate_name;
+ const auto& aliased_deps = target_->rust_values().aliased_deps();
+ if (auto it = aliased_deps.find(dep.label()); it != aliased_deps.end()) {
+ crate_name = it->second;
+ } else {
+ crate_name = dep.rust_values().crate_name();
+ }
+
+ out_ << " --extern ";
+ out_ << crate_name;
+ out_ << "=";
+ path_output_.WriteFile(out_, dep.dependency_output_file());
+ };
+
+ // Write accessible crates with `--extern` to add them to the extern prelude.
out_ << " externs =";
- for (const Target* target : deps) {
- if (target->output_type() == Target::RUST_LIBRARY ||
- target->output_type() == Target::RUST_PROC_MACRO ||
- (target->source_types_used().RustSourceUsed() &&
- target->rust_values().InferredCrateType(target) ==
- RustValues::CRATE_DYLIB)) {
- 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()) << "=";
+ // Tracking to avoid emitted the same lib twice. We track it instead of
+ // pre-emptively constructing a UniqueVector since we would have to also store
+ // the crate name, and in the future the public-ness.
+ std::unordered_set<OutputFile> emitted_rust_libs;
+ // TODO: We defer private dependencies to -Ldependency until --extern priv is
+ // stabilized.
+ UniqueVector<SourceDir> private_extern_dirs;
+
+ // Walk the transitive closure of all rust dependencies.
+ //
+ // For dependencies that are meant to be accessible we pass them to --extern
+ // in order to add them to the crate's extern prelude.
+ //
+ // For all transitive dependencies, we add them to `private_extern_dirs` in
+ // order to generate a -Ldependency switch that points to them. This ensures
+ // that rustc can find them if they are used by other dependencies. For
+ // example:
+ //
+ // A -> C --public--> D
+ // -> B --private-> D
+ //
+ // Here A has direct access to D, but B and C also make use of D, and they
+ // will only search the paths specified to -Ldependency, thus D needs to
+ // appear as both a --extern (for A) and -Ldependency (for B and C).
+ for (const auto& crate : transitive_rust_deps) {
+ const OutputFile& rust_lib = crate.target->dependency_output_file();
+ if (emitted_rust_libs.count(rust_lib) == 0) {
+ if (crate.has_direct_access) {
+ write_extern_target(*crate.target);
}
- path_output_.WriteFile(out_, target->dependency_output_file());
+ emitted_rust_libs.insert(rust_lib);
}
+ private_extern_dirs.push_back(
+ rust_lib.AsSourceFile(settings_->build_settings()).GetDir());
}
- EscapeOptions extern_escape_opts;
- extern_escape_opts.mode = ESCAPE_NINJA_COMMAND;
-
+ // Add explicitly specified externs from the GN target.
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);
- }
+ for (const auto& [crate_name, lib_file] : cur.externs()) {
+ write_extern_lib_file(crate_name, lib_file);
}
}
out_ << std::endl;
-}
-
-void NinjaRustBinaryTargetWriter::WriteRustdeps(
- const std::vector<OutputFile>& transitive_rustdeps,
- const std::vector<OutputFile>& rustdeps,
- const std::vector<OutputFile>& nonrustdeps) {
out_ << " rustdeps =";
- // Rust dependencies.
- UniqueVector<SourceDir> transitive_rustdep_dirs;
- for (const auto& rustdep : transitive_rustdeps) {
- // TODO switch to using --extern priv: after stabilization
- transitive_rustdep_dirs.push_back(
- rustdep.AsSourceFile(settings_->build_settings()).GetDir());
- }
- for (const auto& rustdepdir : transitive_rustdep_dirs) {
+ for (const SourceDir& dir : private_extern_dirs) {
+ // TODO: switch to using `--extern priv:name` after stabilization.
out_ << " -Ldependency=";
- path_output_.WriteDir(out_, rustdepdir, PathOutput::DIR_NO_LAST_SLASH);
+ path_output_.WriteDir(out_, dir, PathOutput::DIR_NO_LAST_SLASH);
}
- EscapeOptions lib_escape_opts;
- lib_escape_opts.mode = ESCAPE_NINJA_COMMAND;
-
// Non-Rust native dependencies.
UniqueVector<SourceDir> nonrustdep_dirs;
for (const auto& nonrustdep : nonrustdeps) {
diff --git a/src/gn/ninja_rust_binary_target_writer.h b/src/gn/ninja_rust_binary_target_writer.h
index 0319c86..69ec916 100644
--- a/src/gn/ninja_rust_binary_target_writer.h
+++ b/src/gn/ninja_rust_binary_target_writer.h
@@ -6,9 +6,9 @@
#define TOOLS_GN_NINJA_RUST_BINARY_TARGET_WRITER_H_
#include "gn/ninja_binary_target_writer.h"
+#include "gn/output_file.h"
#include "gn/rust_tool.h"
-
-struct EscapeOptions;
+#include "gn/target.h"
// Writes a .ninja file for a binary target type (an executable, a shared
// library, or a static library).
@@ -20,13 +20,18 @@
void Run() override;
private:
+ struct ExternCrate {
+ const Target* target;
+ bool has_direct_access;
+ };
+
void WriteCompilerVars();
void WriteSources(const OutputFile& input_dep,
const std::vector<OutputFile>& order_only_deps);
- void WriteExterns(const std::vector<const Target*>& deps);
- void WriteRustdeps(const std::vector<OutputFile>& transitive_rustdeps,
- const std::vector<OutputFile>& rustdeps,
- const std::vector<OutputFile>& nonrustdeps);
+ void WriteExternsAndDeps(const std::vector<const Target*>& deps,
+ const std::vector<ExternCrate>& transitive_rust_deps,
+ const std::vector<OutputFile>& rustdeps,
+ const std::vector<OutputFile>& nonrustdeps);
// Unlike C/C++, Rust compiles all sources of a crate in one command.
// Write a ninja variable `sources` that contains all sources and input files.
void WriteSourcesAndInputs();
diff --git a/src/gn/ninja_rust_binary_target_writer_unittest.cc b/src/gn/ninja_rust_binary_target_writer_unittest.cc
index b4b71d6..29f9756 100644
--- a/src/gn/ninja_rust_binary_target_writer_unittest.cc
+++ b/src/gn/ninja_rust_binary_target_writer_unittest.cc
@@ -73,29 +73,115 @@
}
}
+// Accessible dependencies appear as --extern switches for rustc, so that the
+// target crate can make use of them whether transitive or not. Transitive
+// dependencies can be accessible if they are in the public_deps of a direct
+// dependency, or part of a chain of public_deps from a direct dependency. Any
+// dependencies used by other crate dependencies also must appear, but are
+// pointed to by -Ldependency as they are not available for use from the target
+// crate. In the future they may move to `--extern priv:` when explicit private
+// dependencies are stabilized.
TEST_F(NinjaRustBinaryTargetWriterTest, RlibDeps) {
Err err;
TestWithScope setup;
- Target rlib(setup.settings(), Label(SourceDir("//bar/"), "mylib"));
- rlib.set_output_type(Target::RUST_LIBRARY);
- rlib.visibility().SetPublic();
- SourceFile barlib("//bar/lib.rs");
- rlib.sources().push_back(SourceFile("//bar/mylib.rs"));
- rlib.sources().push_back(barlib);
- rlib.source_types_used().Set(SourceFile::SOURCE_RS);
- rlib.rust_values().set_crate_root(barlib);
- rlib.rust_values().crate_name() = "mylib";
- rlib.SetToolchain(setup.toolchain());
- ASSERT_TRUE(rlib.OnResolved(&err));
+ Target private_rlib(setup.settings(), Label(SourceDir("//baz/"), "privatelib"));
+ private_rlib.set_output_type(Target::RUST_LIBRARY);
+ private_rlib.visibility().SetPublic();
+ SourceFile bazlib("//baz/lib.rs");
+ private_rlib.sources().push_back(SourceFile("//baz/privatelib.rs"));
+ private_rlib.sources().push_back(bazlib);
+ private_rlib.source_types_used().Set(SourceFile::SOURCE_RS);
+ private_rlib.rust_values().set_crate_root(bazlib);
+ private_rlib.rust_values().crate_name() = "privatecrate";
+ private_rlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(private_rlib.OnResolved(&err));
{
std::ostringstream out;
- NinjaRustBinaryTargetWriter writer(&rlib, out);
+ NinjaRustBinaryTargetWriter writer(&private_rlib, out);
writer.Run();
const char expected[] =
- "crate_name = mylib\n"
+ "crate_name = privatecrate\n"
+ "crate_type = rlib\n"
+ "output_extension = .rlib\n"
+ "output_dir = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/baz\n"
+ "target_output_name = libprivatelib\n"
+ "\n"
+ "build obj/baz/libprivatelib.rlib: rust_rlib ../../baz/lib.rs | "
+ "../../baz/privatelib.rs ../../baz/lib.rs\n"
+ " externs =\n"
+ " rustdeps =\n"
+ " ldflags =\n"
+ " sources = ../../baz/privatelib.rs ../../baz/lib.rs\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+
+ Target far_public_rlib(setup.settings(),
+ Label(SourceDir("//far/"), "farlib"));
+ far_public_rlib.set_output_type(Target::RUST_LIBRARY);
+ far_public_rlib.visibility().SetPublic();
+ SourceFile farlib("//far/lib.rs");
+ far_public_rlib.sources().push_back(SourceFile("//far/farlib.rs"));
+ far_public_rlib.sources().push_back(farlib);
+ far_public_rlib.source_types_used().Set(SourceFile::SOURCE_RS);
+ far_public_rlib.rust_values().set_crate_root(farlib);
+ far_public_rlib.rust_values().crate_name() = "farcrate";
+ far_public_rlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(far_public_rlib.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&far_public_rlib, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = farcrate\n"
+ "crate_type = rlib\n"
+ "output_extension = .rlib\n"
+ "output_dir = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/far\n"
+ "target_output_name = libfarlib\n"
+ "\n"
+ "build obj/far/libfarlib.rlib: rust_rlib ../../far/lib.rs | "
+ "../../far/farlib.rs ../../far/lib.rs\n"
+ " externs =\n"
+ " rustdeps =\n"
+ " ldflags =\n"
+ " sources = ../../far/farlib.rs ../../far/lib.rs\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+
+ Target public_rlib(setup.settings(), Label(SourceDir("//bar/"), "publiclib"));
+ public_rlib.set_output_type(Target::RUST_LIBRARY);
+ public_rlib.visibility().SetPublic();
+ SourceFile barlib("//bar/lib.rs");
+ public_rlib.sources().push_back(SourceFile("//bar/publiclib.rs"));
+ public_rlib.sources().push_back(barlib);
+ public_rlib.source_types_used().Set(SourceFile::SOURCE_RS);
+ public_rlib.rust_values().set_crate_root(barlib);
+ public_rlib.rust_values().crate_name() = "publiccrate";
+ public_rlib.public_deps().push_back(LabelTargetPair(&far_public_rlib));
+ public_rlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(public_rlib.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&public_rlib, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = publiccrate\n"
"crate_type = rlib\n"
"output_extension = .rlib\n"
"output_dir = \n"
@@ -103,41 +189,42 @@
"rustenv =\n"
"root_out_dir = .\n"
"target_out_dir = obj/bar\n"
- "target_output_name = libmylib\n"
+ "target_output_name = libpubliclib\n"
"\n"
- "build obj/bar/libmylib.rlib: rust_rlib ../../bar/lib.rs | "
- "../../bar/mylib.rs ../../bar/lib.rs\n"
- " externs =\n"
- " rustdeps =\n"
+ "build obj/bar/libpubliclib.rlib: rust_rlib ../../bar/lib.rs | "
+ "../../bar/publiclib.rs ../../bar/lib.rs obj/far/libfarlib.rlib\n"
+ " externs = --extern farcrate=obj/far/libfarlib.rlib\n"
+ " rustdeps = -Ldependency=obj/far\n"
" ldflags =\n"
- " sources = ../../bar/mylib.rs ../../bar/lib.rs\n";
+ " sources = ../../bar/publiclib.rs ../../bar/lib.rs\n";
std::string out_str = out.str();
EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
}
- Target another_rlib(setup.settings(), Label(SourceDir("//foo/"), "direct"));
- another_rlib.set_output_type(Target::RUST_LIBRARY);
- another_rlib.visibility().SetPublic();
+ Target rlib(setup.settings(), Label(SourceDir("//foo/"), "direct"));
+ rlib.set_output_type(Target::RUST_LIBRARY);
+ rlib.visibility().SetPublic();
SourceFile lib("//foo/main.rs");
- another_rlib.sources().push_back(SourceFile("//foo/direct.rs"));
- another_rlib.sources().push_back(lib);
- another_rlib.source_types_used().Set(SourceFile::SOURCE_RS);
- another_rlib.rust_values().set_crate_root(lib);
- another_rlib.rust_values().crate_name() = "direct";
- another_rlib.SetToolchain(setup.toolchain());
- another_rlib.public_deps().push_back(LabelTargetPair(&rlib));
- ASSERT_TRUE(another_rlib.OnResolved(&err));
+ rlib.sources().push_back(SourceFile("//foo/direct.rs"));
+ rlib.sources().push_back(lib);
+ rlib.source_types_used().Set(SourceFile::SOURCE_RS);
+ rlib.rust_values().set_crate_root(lib);
+ rlib.rust_values().crate_name() = "direct";
+ rlib.SetToolchain(setup.toolchain());
+ rlib.public_deps().push_back(LabelTargetPair(&public_rlib));
+ rlib.private_deps().push_back(LabelTargetPair(&private_rlib));
+ ASSERT_TRUE(rlib.OnResolved(&err));
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ Target target(setup.settings(), Label(SourceDir("//main/"), "main"));
target.set_output_type(Target::EXECUTABLE);
target.visibility().SetPublic();
- SourceFile main("//foo/main.rs");
- target.sources().push_back(SourceFile("//foo/source.rs"));
+ SourceFile main("//main/main.rs");
+ target.sources().push_back(SourceFile("//main/source.rs"));
target.sources().push_back(main);
target.source_types_used().Set(SourceFile::SOURCE_RS);
target.rust_values().set_crate_root(main);
- target.rust_values().crate_name() = "foo_bar";
- target.private_deps().push_back(LabelTargetPair(&another_rlib));
+ target.rust_values().crate_name() = "main_crate";
+ target.private_deps().push_back(LabelTargetPair(&rlib));
target.SetToolchain(setup.toolchain());
ASSERT_TRUE(target.OnResolved(&err));
@@ -147,22 +234,25 @@
writer.Run();
const char expected[] =
- "crate_name = foo_bar\n"
+ "crate_name = main_crate\n"
"crate_type = bin\n"
"output_extension = \n"
"output_dir = \n"
"rustflags =\n"
"rustenv =\n"
"root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = bar\n"
+ "target_out_dir = obj/main\n"
+ "target_output_name = main\n"
"\n"
- "build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/source.rs "
- "../../foo/main.rs obj/foo/libdirect.rlib\n"
- " externs = --extern direct=obj/foo/libdirect.rlib\n"
- " rustdeps = -Ldependency=obj/foo -Ldependency=obj/bar\n"
+ "build ./main_crate: rust_bin ../../main/main.rs | "
+ "../../main/source.rs ../../main/main.rs obj/foo/libdirect.rlib\n"
+ " externs = --extern direct=obj/foo/libdirect.rlib "
+ "--extern publiccrate=obj/bar/libpubliclib.rlib "
+ "--extern farcrate=obj/far/libfarlib.rlib\n"
+ " rustdeps = -Ldependency=obj/foo -Ldependency=obj/bar "
+ "-Ldependency=obj/far -Ldependency=obj/baz\n"
" ldflags =\n"
- " sources = ../../foo/source.rs ../../foo/main.rs\n";
+ " sources = ../../main/source.rs ../../main/main.rs\n";
std::string out_str = out.str();
EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
}
@@ -172,6 +262,82 @@
Err err;
TestWithScope setup;
+ Target private_inside_dylib(setup.settings(), Label(SourceDir("//faz/"), "private_inside"));
+ private_inside_dylib.set_output_type(Target::RUST_LIBRARY);
+ private_inside_dylib.visibility().SetPublic();
+ SourceFile fazlib("//faz/lib.rs");
+ private_inside_dylib.sources().push_back(SourceFile("//faz/private_inside.rs"));
+ private_inside_dylib.sources().push_back(fazlib);
+ private_inside_dylib.source_types_used().Set(SourceFile::SOURCE_RS);
+ private_inside_dylib.rust_values().set_crate_root(fazlib);
+ private_inside_dylib.rust_values().crate_name() = "private_inside";
+ private_inside_dylib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(private_inside_dylib.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&private_inside_dylib, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = private_inside\n"
+ "crate_type = rlib\n"
+ "output_extension = .rlib\n"
+ "output_dir = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/faz\n"
+ "target_output_name = libprivate_inside\n"
+ "\n"
+ "build obj/faz/libprivate_inside.rlib: rust_rlib ../../faz/lib.rs | "
+ "../../faz/private_inside.rs ../../faz/lib.rs\n"
+ " externs =\n"
+ " rustdeps =\n"
+ " ldflags =\n"
+ " sources = ../../faz/private_inside.rs ../../faz/lib.rs\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+
+ Target inside_dylib(setup.settings(), Label(SourceDir("//baz/"), "inside"));
+ inside_dylib.set_output_type(Target::RUST_LIBRARY);
+ inside_dylib.visibility().SetPublic();
+ SourceFile bazlib("//baz/lib.rs");
+ inside_dylib.sources().push_back(SourceFile("//baz/inside.rs"));
+ inside_dylib.sources().push_back(bazlib);
+ inside_dylib.source_types_used().Set(SourceFile::SOURCE_RS);
+ inside_dylib.rust_values().set_crate_root(bazlib);
+ inside_dylib.rust_values().crate_name() = "inside";
+ inside_dylib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(inside_dylib.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&inside_dylib, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = inside\n"
+ "crate_type = rlib\n"
+ "output_extension = .rlib\n"
+ "output_dir = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/baz\n"
+ "target_output_name = libinside\n"
+ "\n"
+ "build obj/baz/libinside.rlib: rust_rlib ../../baz/lib.rs | "
+ "../../baz/inside.rs ../../baz/lib.rs\n"
+ " externs =\n"
+ " rustdeps =\n"
+ " ldflags =\n"
+ " sources = ../../baz/inside.rs ../../baz/lib.rs\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+
Target dylib(setup.settings(), Label(SourceDir("//bar/"), "mylib"));
dylib.set_output_type(Target::SHARED_LIBRARY);
dylib.visibility().SetPublic();
@@ -179,9 +345,11 @@
dylib.sources().push_back(SourceFile("//bar/mylib.rs"));
dylib.sources().push_back(barlib);
dylib.source_types_used().Set(SourceFile::SOURCE_RS);
- dylib.rust_values().set_crate_type(RustValues::CRATE_DYLIB); // TODO
+ dylib.rust_values().set_crate_type(RustValues::CRATE_DYLIB); // TODO
dylib.rust_values().set_crate_root(barlib);
dylib.rust_values().crate_name() = "mylib";
+ dylib.public_deps().push_back(LabelTargetPair(&inside_dylib));
+ dylib.private_deps().push_back(LabelTargetPair(&private_inside_dylib));
dylib.SetToolchain(setup.toolchain());
ASSERT_TRUE(dylib.OnResolved(&err));
@@ -202,9 +370,11 @@
"target_output_name = libmylib\n"
"\n"
"build obj/bar/libmylib.so: rust_dylib ../../bar/lib.rs | "
- "../../bar/mylib.rs ../../bar/lib.rs\n"
- " externs =\n"
- " rustdeps =\n"
+ "../../bar/mylib.rs ../../bar/lib.rs "
+ "obj/baz/libinside.rlib obj/faz/libprivate_inside.rlib\n"
+ " externs = --extern inside=obj/baz/libinside.rlib "
+ "--extern private_inside=obj/faz/libprivate_inside.rlib\n"
+ " rustdeps = -Ldependency=obj/baz -Ldependency=obj/faz\n"
" ldflags =\n"
" sources = ../../bar/mylib.rs ../../bar/lib.rs\n";
std::string out_str = out.str();
@@ -222,7 +392,7 @@
another_dylib.rust_values().set_crate_root(lib);
another_dylib.rust_values().crate_name() = "direct";
another_dylib.SetToolchain(setup.toolchain());
- another_dylib.private_deps().push_back(LabelTargetPair(&dylib));
+ another_dylib.public_deps().push_back(LabelTargetPair(&dylib));
ASSERT_TRUE(another_dylib.OnResolved(&err));
Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
@@ -256,8 +426,11 @@
"\n"
"build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/source.rs "
"../../foo/main.rs obj/foo/libdirect.so\n"
- " externs = --extern direct=obj/foo/libdirect.so\n"
- " rustdeps = -Ldependency=obj/foo -Ldependency=obj/bar\n"
+ " externs = --extern direct=obj/foo/libdirect.so "
+ "--extern mylib=obj/bar/libmylib.so "
+ "--extern inside=obj/baz/libinside.rlib\n"
+ " rustdeps = -Ldependency=obj/foo -Ldependency=obj/bar "
+ "-Ldependency=obj/baz -Ldependency=obj/faz\n"
" ldflags =\n"
" sources = ../../foo/source.rs ../../foo/main.rs\n";
std::string out_str = out.str();
@@ -385,8 +558,10 @@
"target_output_name = bar\n"
"\n"
"build ./foo_bar: rust_bin ../../foo/main.rs | "
- "../../foo/source.rs ../../foo/main.rs obj/bar/libmylib.rlib\n"
- " externs = --extern mylib=obj/bar/libmylib.rlib\n"
+ "../../foo/source.rs ../../foo/main.rs obj/bar/libmylib.rlib || "
+ "obj/baz/group.stamp\n"
+ " externs = --extern mylib=obj/bar/libmylib.rlib "
+ "--extern mymacro=obj/bar/libmymacro.so\n"
" rustdeps = -Ldependency=obj/bar\n"
" ldflags =\n"
" sources = ../../foo/source.rs ../../foo/main.rs\n";
@@ -399,17 +574,42 @@
Err err;
TestWithScope setup;
- Target another_rlib(setup.settings(), Label(SourceDir("//foo/"), "direct"));
- another_rlib.set_output_type(Target::RUST_LIBRARY);
- another_rlib.visibility().SetPublic();
- SourceFile lib("//foo/lib.rs");
- another_rlib.sources().push_back(SourceFile("//foo/direct.rs"));
- another_rlib.sources().push_back(lib);
- another_rlib.source_types_used().Set(SourceFile::SOURCE_RS);
- another_rlib.rust_values().set_crate_root(lib);
- another_rlib.rust_values().crate_name() = "direct";
- another_rlib.SetToolchain(setup.toolchain());
- ASSERT_TRUE(another_rlib.OnResolved(&err));
+ Target transitive(setup.settings(), Label(SourceDir("//faz/"), "transitive"));
+ transitive.set_output_type(Target::RUST_LIBRARY);
+ transitive.visibility().SetPublic();
+ SourceFile transitive_lib("//faz/transitive/lib.rs");
+ transitive.sources().push_back(SourceFile("//faz/transitive/transitive.rs"));
+ transitive.sources().push_back(transitive_lib);
+ transitive.source_types_used().Set(SourceFile::SOURCE_RS);
+ transitive.rust_values().set_crate_root(transitive_lib);
+ transitive.rust_values().crate_name() = "transitive";
+ transitive.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(transitive.OnResolved(&err));
+
+ Target rlib(setup.settings(), Label(SourceDir("//baz/"), "mylib"));
+ rlib.set_output_type(Target::RUST_LIBRARY);
+ rlib.visibility().SetPublic();
+ SourceFile barlib("//baz/bar/lib.rs");
+ rlib.sources().push_back(SourceFile("//baz/bar/mylib.rs"));
+ rlib.sources().push_back(barlib);
+ rlib.source_types_used().Set(SourceFile::SOURCE_RS);
+ rlib.rust_values().set_crate_root(barlib);
+ rlib.rust_values().crate_name() = "mylib";
+ rlib.SetToolchain(setup.toolchain());
+ rlib.public_deps().push_back(LabelTargetPair(&transitive));
+ ASSERT_TRUE(rlib.OnResolved(&err));
+
+ Target direct(setup.settings(), Label(SourceDir("//bar/"), "direct"));
+ direct.set_output_type(Target::RUST_LIBRARY);
+ direct.visibility().SetPublic();
+ SourceFile direct_lib("//bar/direct/lib.rs");
+ direct.sources().push_back(SourceFile("//bar/direct/direct.rs"));
+ direct.sources().push_back(direct_lib);
+ direct.source_types_used().Set(SourceFile::SOURCE_RS);
+ direct.rust_values().set_crate_root(direct_lib);
+ direct.rust_values().crate_name() = "direct";
+ direct.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(direct.OnResolved(&err));
Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
target.set_output_type(Target::EXECUTABLE);
@@ -420,8 +620,13 @@
target.source_types_used().Set(SourceFile::SOURCE_RS);
target.rust_values().set_crate_root(main);
target.rust_values().crate_name() = "foo_bar";
- target.rust_values().aliased_deps()[another_rlib.label()] = "direct_renamed";
- target.private_deps().push_back(LabelTargetPair(&another_rlib));
+ // A direct dependency is renamed.
+ target.rust_values().aliased_deps()[direct.label()] = "direct_renamed";
+ // A transitive public dependency, through `rlib`, is renamed.
+ target.rust_values().aliased_deps()[transitive.label()] =
+ "transitive_renamed";
+ target.private_deps().push_back(LabelTargetPair(&direct));
+ target.private_deps().push_back(LabelTargetPair(&rlib));
target.SetToolchain(setup.toolchain());
ASSERT_TRUE(target.OnResolved(&err));
@@ -442,9 +647,12 @@
"target_output_name = bar\n"
"\n"
"build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/source.rs "
- "../../foo/main.rs obj/foo/libdirect.rlib\n"
- " externs = --extern direct_renamed=obj/foo/libdirect.rlib\n"
- " rustdeps = -Ldependency=obj/foo\n"
+ "../../foo/main.rs obj/bar/libdirect.rlib obj/baz/libmylib.rlib\n"
+ " externs = --extern direct_renamed=obj/bar/libdirect.rlib "
+ "--extern mylib=obj/baz/libmylib.rlib "
+ "--extern transitive_renamed=obj/faz/libtransitive.rlib\n"
+ " rustdeps = -Ldependency=obj/bar -Ldependency=obj/baz "
+ "-Ldependency=obj/faz\n"
" ldflags =\n"
" sources = ../../foo/source.rs ../../foo/main.rs\n";
std::string out_str = out.str();
@@ -725,22 +933,39 @@
}
}
+// Test that neither public nor private rust dependencies of a proc-macro are
+// transitively acquired as accessible dependencies by users of the macro. But
+// the macro itself is listed as an accessible dependency (via --extern).
TEST_F(NinjaRustBinaryTargetWriterTest, RustProcMacro) {
Err err;
TestWithScope setup;
- Target procmacrodep(setup.settings(),
- Label(SourceDir("//baz/"), "mymacrodep"));
- procmacrodep.set_output_type(Target::RUST_LIBRARY);
- procmacrodep.visibility().SetPublic();
- SourceFile bazlib("//baz/lib.rs");
- procmacrodep.sources().push_back(SourceFile("//baz/mylib.rs"));
- procmacrodep.sources().push_back(bazlib);
- procmacrodep.source_types_used().Set(SourceFile::SOURCE_RS);
- procmacrodep.rust_values().set_crate_root(bazlib);
- procmacrodep.rust_values().crate_name() = "mymacrodep";
- procmacrodep.SetToolchain(setup.toolchain());
- ASSERT_TRUE(procmacrodep.OnResolved(&err));
+ Target procmacropublicdep(
+ setup.settings(), Label(SourceDir("//baz/public/"), "mymacropublicdep"));
+ procmacropublicdep.set_output_type(Target::RUST_LIBRARY);
+ procmacropublicdep.visibility().SetPublic();
+ SourceFile publicbazlib("//baz/public/lib.rs");
+ procmacropublicdep.sources().push_back(SourceFile("//baz/public/mylib.rs"));
+ procmacropublicdep.sources().push_back(publicbazlib);
+ procmacropublicdep.source_types_used().Set(SourceFile::SOURCE_RS);
+ procmacropublicdep.rust_values().set_crate_root(publicbazlib);
+ procmacropublicdep.rust_values().crate_name() = "publicdep";
+ procmacropublicdep.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(procmacropublicdep.OnResolved(&err));
+
+ Target procmacroprivatedep(
+ setup.settings(),
+ Label(SourceDir("//baz/private/"), "mymacroprivatedep"));
+ procmacroprivatedep.set_output_type(Target::RUST_LIBRARY);
+ procmacroprivatedep.visibility().SetPublic();
+ SourceFile privatebazlib("//baz/private/lib.rs");
+ procmacroprivatedep.sources().push_back(SourceFile("//baz/private/mylib.rs"));
+ procmacroprivatedep.sources().push_back(privatebazlib);
+ procmacroprivatedep.source_types_used().Set(SourceFile::SOURCE_RS);
+ procmacroprivatedep.rust_values().set_crate_root(privatebazlib);
+ procmacroprivatedep.rust_values().crate_name() = "privatedep";
+ procmacroprivatedep.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(procmacroprivatedep.OnResolved(&err));
Target procmacro(setup.settings(), Label(SourceDir("//bar/"), "mymacro"));
procmacro.set_output_type(Target::RUST_PROC_MACRO);
@@ -754,7 +979,8 @@
procmacro.rust_values().set_crate_type(RustValues::CRATE_PROC_MACRO);
// Add a dependency to the procmacro so we can be sure its output
// directory is not propagated downstream beyond the proc macro.
- procmacro.private_deps().push_back(LabelTargetPair(&procmacrodep));
+ procmacro.public_deps().push_back(LabelTargetPair(&procmacropublicdep));
+ procmacro.private_deps().push_back(LabelTargetPair(&procmacroprivatedep));
procmacro.SetToolchain(setup.toolchain());
ASSERT_TRUE(procmacro.OnResolved(&err));
@@ -775,9 +1001,14 @@
"target_output_name = libmymacro\n"
"\n"
"build obj/bar/libmymacro.so: rust_macro ../../bar/lib.rs | "
- "../../bar/mylib.rs ../../bar/lib.rs obj/baz/libmymacrodep.rlib\n"
- " externs = --extern mymacrodep=obj/baz/libmymacrodep.rlib\n"
- " rustdeps = -Ldependency=obj/baz\n"
+ "../../bar/mylib.rs ../../bar/lib.rs "
+ "obj/baz/public/libmymacropublicdep.rlib "
+ "obj/baz/private/libmymacroprivatedep.rlib\n"
+ " externs = "
+ "--extern publicdep=obj/baz/public/libmymacropublicdep.rlib "
+ "--extern privatedep=obj/baz/private/libmymacroprivatedep.rlib\n"
+ " rustdeps = -Ldependency=obj/baz/public "
+ "-Ldependency=obj/baz/private\n"
" ldflags =\n"
" sources = ../../bar/mylib.rs ../../bar/lib.rs\n";
std::string out_str = out.str();
@@ -989,7 +1220,8 @@
writer.Run();
const char expected[] =
- "build obj/foo/bar.inputs.stamp: stamp ../../foo/config.json ../../foo/template.h\n"
+ "build obj/foo/bar.inputs.stamp: stamp ../../foo/config.json "
+ "../../foo/template.h\n"
"crate_name = foo_bar\n"
"crate_type = bin\n"
"output_extension = \n"
@@ -1083,8 +1315,8 @@
"build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/source.rs "
"../../foo/main.rs obj/bar/libmylib.so\n"
" externs =\n"
- " rustdeps = -Ldependency=obj/bar -Lnative=obj/bar "
- "-Clink-arg=-Bdynamic -Clink-arg=obj/bar/libmylib.so\n"
+ " rustdeps = -Lnative=obj/bar -Clink-arg=-Bdynamic "
+ "-Clink-arg=obj/bar/libmylib.so\n"
" ldflags =\n"
" sources = ../../foo/source.rs ../../foo/main.rs\n";
std::string out_str = out.str();
diff --git a/src/gn/target.cc b/src/gn/target.cc
index 508ad7e..c491897 100644
--- a/src/gn/target.cc
+++ b/src/gn/target.cc
@@ -769,38 +769,40 @@
if (dep->output_type() == STATIC_LIBRARY ||
dep->output_type() == SHARED_LIBRARY ||
- dep->output_type() == RUST_LIBRARY) {
- rust_transitive_libs_.Append(dep, is_public);
+ dep->output_type() == RUST_LIBRARY ||
+ dep->output_type() == GROUP) {
+ // Here we have: `this` --[depends-on]--> `dep`
+ //
+ // The `this` target has direct access to `dep` since its a direct
+ // dependency, regardless of the edge being a public_dep or not, so we pass
+ // true for public-ness. Whereas, anything depending on `this` can only gain
+ // direct access to `dep` if the edge between `this` and `dep` is public,
+ // so we pass `is_public`.
+ rust_transitive_inherited_libs_.Append(dep, true);
+ rust_transitive_inheritable_libs_.Append(dep, is_public);
- // Propagate public dependent libraries.
- for (const auto& transitive :
- dep->rust_transitive_libs_.GetOrderedAndPublicFlag()) {
- if (transitive.second) {
- rust_transitive_libs_.Append(transitive.first, is_public);
- }
- }
+ rust_transitive_inherited_libs_.AppendInherited(
+ dep->rust_transitive_inheritable_libs(), true);
+ rust_transitive_inheritable_libs_.AppendInherited(
+ dep->rust_transitive_inheritable_libs(), is_public);
+ } else if (dep->output_type() == RUST_PROC_MACRO) {
+ // Proc-macros are inherited as a transitive dependency, but the things they
+ // depend on can't be used elsewhere, as the proc macro is not linked into
+ // the target (as it's only used during compilation).
+ rust_transitive_inherited_libs_.Append(dep, true);
+ rust_transitive_inheritable_libs_.Append(dep, is_public);
}
- // Rust libraries (those meant for consumption by another Rust target) are
- // handled the same way, whether static or dynamic.
if (dep->output_type() == RUST_LIBRARY ||
RustValues::InferredCrateType(dep) == RustValues::CRATE_DYLIB) {
- rust_transitive_libs_.AppendInherited(dep->rust_transitive_libs_,
- is_public);
-
- // If there is a transitive dependency that is not a rust library, place it
- // in the normal location
- for (const auto& inherited :
- rust_transitive_libs_.GetOrderedAndPublicFlag()) {
- if (!RustValues::IsRustLibrary(inherited.first)) {
- inherited_libraries_.Append(inherited.first, inherited.second);
+ // Transitive dependencies behind a library (shared or static), that would
+ // be consumed by rustc, gets bumped up to this target.
+ for (const auto& [inherited, inherited_is_public] :
+ rust_transitive_inheritable_libs_.GetOrderedAndPublicFlag()) {
+ if (!RustValues::IsRustLibrary(inherited)) {
+ inherited_libraries_.Append(inherited, inherited_is_public);
}
}
- } else if (dep->output_type() == RUST_PROC_MACRO) {
- // We will need to specify the path to find a procedural macro,
- // but have no need to specify the paths to find its dependencies
- // as the procedural macro is now a complete .so.
- rust_transitive_libs_.Append(dep, is_public);
} else if (dep->output_type() == SHARED_LIBRARY) {
// Shared library dependendencies are inherited across public shared
// library boundaries.
@@ -828,8 +830,6 @@
// The current target isn't linked, so propagate linked deps and
// libraries up the dependency tree.
inherited_libraries_.AppendInherited(dep->inherited_libraries(), is_public);
- rust_transitive_libs_.AppendInherited(dep->rust_transitive_libs_,
- is_public);
} else if (dep->complete_static_lib()) {
// Inherit only final targets through _complete_ static libraries.
//
diff --git a/src/gn/target.h b/src/gn/target.h
index d88f0a5..de076a8 100644
--- a/src/gn/target.h
+++ b/src/gn/target.h
@@ -316,9 +316,11 @@
bool has_rust_values() const { return rust_values_.get(); }
// Transitive closure of libraries that are depended on by this target
- InheritedLibraries& rust_transitive_libs() { return rust_transitive_libs_; }
- const InheritedLibraries& rust_transitive_libs() const {
- return rust_transitive_libs_;
+ const InheritedLibraries& rust_transitive_inherited_libs() const {
+ return rust_transitive_inherited_libs_;
+ }
+ const InheritedLibraries& rust_transitive_inheritable_libs() const {
+ return rust_transitive_inheritable_libs_;
}
const UniqueVector<SourceDir>& all_lib_dirs() const { return all_lib_dirs_; }
@@ -520,8 +522,18 @@
// Used for Rust targets.
std::unique_ptr<RustValues> rust_values_;
- // Used by all targets, only useful to generate Rust targets though.
- InheritedLibraries rust_transitive_libs_;
+ // Used by all targets, only useful to generate Rust targets though. These
+ // present 2 different views of the public flags:
+ //
+ // Lists all transitive libraries, and for each one the public bit says if
+ // there is a public chain such that this target can make direct use of the
+ // lib. For each library marked public: "I have access to these targets."
+ InheritedLibraries rust_transitive_inherited_libs_;
+ // Lists all transitive libraries, and for each one the public bit says if a
+ // target depending on this target would inherit the libraries as public too.
+ // For each library marked public: "If you depend on me, you get access to
+ // these targets."
+ InheritedLibraries rust_transitive_inheritable_libs_;
// User for Swift targets.
std::unique_ptr<SwiftValues> swift_values_;