Ensure all --extern crates are listed as dependencies in Ninja.
Any Rust crate B that is visible for use as a dependency of another
Rust crate A gets added to the compile of A with an `--extern` flag,
such as `--extern B`. This ensures that B is accessible from A, so that
it can use the functions and types in B.
When A makes use of something in B, it means A needs to be rebuilt when
B changes, however. This is not expressed to ninja through the
`externs` set. Currently, if A deps on B deps on C, and B is an
order-only dependency (such as a group), ninja does not know that A
should be rebuilt when C is changed. For direct dependencies,
ninja hears about them since rlibs are also collected as linkable_deps
in NinjaBinaryTargetWriter::ClassifyDependency(). But transitive
dependencies need to be propagated to the set of implicit dependencies
of a target in order to cause it to rebuild, if the dependency can be
accessed from the target.
We ensure transitive deps get a dependency relationship in ninja if
and only if they appear in `externs`, since transitive dependencies
that are not visible to a crate can not require it to be recompiled.
Bug: 282
Change-Id: I9f30274c65cccabcaed0181d7fe3b95539fb674b
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/13380
Reviewed-by: Tyler Mandry <tmandry@google.com>
Reviewed-by: Brett Wilson <brettw@chromium.org>
Commit-Queue: Brett Wilson <brettw@chromium.org>
diff --git a/src/gn/ninja_rust_binary_target_writer.cc b/src/gn/ninja_rust_binary_target_writer.cc
index f3ff21f..9263a58 100644
--- a/src/gn/ninja_rust_binary_target_writer.cc
+++ b/src/gn/ninja_rust_binary_target_writer.cc
@@ -187,6 +187,12 @@
if (dep->source_types_used().RustSourceUsed() &&
RustValues::IsRustLibrary(dep)) {
transitive_crates.push_back({dep, has_direct_access});
+ // If the current crate can directly acccess the `dep` crate, then the
+ // current crate needs an implicit dependency on `dep` so it will be
+ // rebuilt if `dep` changes.
+ if (has_direct_access) {
+ implicit_deps.push_back(dep->dependency_output_file());
+ }
}
}
diff --git a/src/gn/ninja_rust_binary_target_writer_unittest.cc b/src/gn/ninja_rust_binary_target_writer_unittest.cc
index 68d5970..d224193 100644
--- a/src/gn/ninja_rust_binary_target_writer_unittest.cc
+++ b/src/gn/ninja_rust_binary_target_writer_unittest.cc
@@ -457,7 +457,8 @@
"target_output_name = bar\n"
"\n"
"build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/source.rs "
- "../../foo/main.rs obj/foo/libdirect.so obj/bar/libmylib.so\n"
+ "../../foo/main.rs obj/foo/libdirect.so obj/bar/libmylib.so "
+ "obj/baz/libinside.rlib\n"
" source_file_part = main.rs\n"
" source_name_part = main\n"
" externs = --extern direct=obj/foo/libdirect.so "
@@ -518,6 +519,13 @@
EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
}
+ // A group produces an order-only dependency in ninja:
+ // https://ninja-build.org/manual.html#ref_dependencies.
+ //
+ // If a crate D inside the group is visible to a crate C depending on the
+ // group, the crate C needs to be rebuilt when D is changed. The group
+ // dependency does not guarantee that it would, so we test that C has an
+ // indirect dependency on D through this group.
Target group(setup.settings(), Label(SourceDir("//baz/"), "group"));
group.set_output_type(Target::GROUP);
group.visibility().SetPublic();
@@ -543,6 +551,10 @@
NinjaRustBinaryTargetWriter writer(&rlib, out);
writer.Run();
+ // libmymacro.so is inside the obj/baz/group, so would be built before
+ // libmylib.rlib. However it must also cause libmylib.rlib to be recompiled
+ // when changed, so we expect an implicit dependency (appearing after `|` on
+ // the build line) from libmylib.rlib to libmymacro.so.
const char expected[] =
"crate_name = mylib\n"
"crate_type = rlib\n"
@@ -555,7 +567,7 @@
"target_output_name = libmylib\n"
"\n"
"build obj/bar/libmylib.rlib: rust_rlib ../../bar/lib.rs | "
- "../../bar/mylib.rs ../../bar/lib.rs || "
+ "../../bar/mylib.rs ../../bar/lib.rs obj/bar/libmymacro.so || "
"obj/baz/group.stamp\n"
" source_file_part = lib.rs\n"
" source_name_part = lib\n"
@@ -597,7 +609,8 @@
"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"
+ "../../foo/source.rs ../../foo/main.rs "
+ "obj/bar/libmylib.rlib obj/bar/libmymacro.so\n"
" source_file_part = main.rs\n"
" source_name_part = main\n"
" externs = --extern mylib=obj/bar/libmylib.rlib "
@@ -1055,7 +1068,8 @@
"obj/staticlib/libstaticlib.a "
"obj/dylib/libdylib.so "
"obj/pub_in_staticlib/libpub_in_staticlib.rlib "
- "obj/priv_in_staticlib/libpriv_in_staticlib.rlib || "
+ "obj/priv_in_staticlib/libpriv_in_staticlib.rlib "
+ "obj/pub_in_dylib/libpub_in_dylib.rlib || "
"obj/pub_sset_in_staticlib/pub_sset_in_staticlib.stamp "
"obj/priv_sset_in_staticlib/priv_sset_in_staticlib.stamp\n"
" source_file_part = main.rs\n"