rust: made-to-order rust staticlibs

Whenever any two Rust static libraries are included
as static libraries anywhere in a CC dependency tree, we sometimes
get duplicate symbol errors. To avoid this, we no longer
directly link multiple rust static libs to CC modules.

Instead, we build rust_ffi_rlib modules and produce the actual
static library that gets linked against the CC module based on
that CC module's full list of Rust rlib dependencies.

This introduces a new static_rlibs property for cc modules to
define the rust_ffi_rlib dependencies, which are then used to
generate the module above.

This CL is intended to deprecate rust_ffi_static. It leaves
rust_ffi_static and rust_ffi static variants in place until
the remaining rust_ffi_static declarations and uses can be
removed. In the meantime, rust_ffi_static produces
rust_ffi_rlib variants as well to make the transition easier.

Bug: 254469782
Test: m # with no changes
Test: m libapexsupport # with static_rlibs
Test: m libunwindstack # with static_rlibs
Test: m netsimd # with static_rlibs, no duplicate symbols
Test: m blueprint_tests # New Soong tests

Change-Id: I47e27ac967ef0cad46d398ebf59d8275929ae28a
diff --git a/rust/rust_test.go b/rust/rust_test.go
index 6d083f6..8b96df8 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -150,15 +150,11 @@
 			host_supported: true,
 			name: "cc_stubs_dep",
 		}
-		rust_ffi_host_static {
+		cc_library_host_static {
 			name: "libstatic",
-			srcs: ["foo.rs"],
-			crate_name: "static",
 		}
-		rust_ffi_host_static {
+		cc_library_host_static {
 			name: "libwholestatic",
-			srcs: ["foo.rs"],
-			crate_name: "wholestatic",
 		}
 		rust_ffi_host_shared {
 			name: "libshared",
@@ -435,6 +431,105 @@
 	}
 }
 
+func TestRustRlibs(t *testing.T) {
+	ctx := testRust(t, `
+		rust_ffi_rlib {
+			name: "libbar",
+			crate_name: "bar",
+			srcs: ["src/lib.rs"],
+			export_include_dirs: ["bar_includes"]
+		}
+
+		rust_ffi_rlib {
+			name: "libfoo",
+			crate_name: "foo",
+			srcs: ["src/lib.rs"],
+			export_include_dirs: ["foo_includes"]
+		}
+
+		cc_library_shared {
+			name: "libcc_shared",
+			srcs:["foo.c"],
+			static_rlibs: ["libbar"],
+		}
+
+		cc_library_static {
+			name: "libcc_static",
+			srcs:["foo.c"],
+			static_rlibs: ["libfoo"],
+		}
+
+		cc_binary {
+			name: "ccBin",
+			srcs:["foo.c"],
+			static_rlibs: ["libbar"],
+			static_libs: ["libcc_static"],
+		}
+		`)
+
+	libbar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_rlib_rlib-std").Rule("rustc")
+	libcc_shared_rustc := ctx.ModuleForTests("libcc_shared", "android_arm64_armv8-a_shared").Rule("rustc")
+	libcc_shared_ld := ctx.ModuleForTests("libcc_shared", "android_arm64_armv8-a_shared").Rule("ld")
+	libcc_shared_cc := ctx.ModuleForTests("libcc_shared", "android_arm64_armv8-a_shared").Rule("cc")
+	ccbin_rustc := ctx.ModuleForTests("ccBin", "android_arm64_armv8-a").Rule("rustc")
+	ccbin_ld := ctx.ModuleForTests("ccBin", "android_arm64_armv8-a").Rule("ld")
+	ccbin_cc := ctx.ModuleForTests("ccBin", "android_arm64_armv8-a").Rule("cc")
+
+	if !strings.Contains(libbar.Args["rustcFlags"], "crate-type=rlib") {
+		t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", "rlib", libbar.Args["rustcFlags"])
+	}
+
+	// Make sure there's a rustc command, and it's producing a staticlib
+	if !strings.Contains(libcc_shared_rustc.Args["rustcFlags"], "crate-type=staticlib") {
+		t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v",
+			"staticlib", libcc_shared_rustc.Args["rustcFlags"])
+	}
+
+	// Make sure the static lib is included in the ld command
+	if !strings.Contains(libcc_shared_ld.Args["libFlags"], "generated_rust_staticlib/liblibcc_shared_rust_staticlib.a") {
+		t.Errorf("missing generated static library in linker step libFlags %#v, libFlags: %#v",
+			"libcc_shared.generated_rust_staticlib.a", libcc_shared_ld.Args["libFlags"])
+	}
+
+	// Make sure the static lib includes are in the cc command
+	if !strings.Contains(libcc_shared_cc.Args["cFlags"], "-Ibar_includes") {
+		t.Errorf("missing rlibs includes, expecting %#v, cFlags: %#v",
+			"-Ibar_includes", libcc_shared_cc.Args["cFlags"])
+	}
+
+	// Make sure there's a rustc command, and it's producing a staticlib
+	if !strings.Contains(ccbin_rustc.Args["rustcFlags"], "crate-type=staticlib") {
+		t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", "staticlib", ccbin_rustc.Args["rustcFlags"])
+	}
+
+	// Make sure the static lib is included in the cc command
+	if !strings.Contains(ccbin_ld.Args["libFlags"], "generated_rust_staticlib/libccBin_rust_staticlib.a") {
+		t.Errorf("missing generated static library in linker step libFlags, expecting %#v, libFlags: %#v",
+			"ccBin.generated_rust_staticlib.a", ccbin_ld.Args["libFlags"])
+	}
+
+	// Make sure the static lib includes are in the ld command
+	if !strings.Contains(ccbin_cc.Args["cFlags"], "-Ibar_includes") {
+		t.Errorf("missing rlibs includes, expecting %#v, cFlags: %#v",
+			"-Ibar_includes", ccbin_cc.Args)
+	}
+
+	// Make sure that direct dependencies and indirect dependencies are
+	// propagating correctly to the generated rlib.
+	if !strings.Contains(ccbin_rustc.Args["libFlags"], "--extern foo=") {
+		t.Errorf("Missing indirect dependency libfoo when writing generated Rust staticlib: %#v", ccbin_rustc.Args["libFlags"])
+	}
+	if !strings.Contains(ccbin_rustc.Args["libFlags"], "--extern bar=") {
+		t.Errorf("Missing direct dependency libbar when writing generated Rust staticlib: %#v", ccbin_rustc.Args["libFlags"])
+	}
+
+	// Test indirect includes propagation
+	if !strings.Contains(ccbin_cc.Args["cFlags"], "-Ifoo_includes") {
+		t.Errorf("missing rlibs includes, expecting %#v, cFlags: %#v",
+			"-Ifoo_includes", ccbin_cc.Args)
+	}
+}
+
 func assertString(t *testing.T, got, expected string) {
 	t.Helper()
 	if got != expected {