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_;