diff options
-rw-r--r-- | apex/apex_test.go | 406 | ||||
-rw-r--r-- | cc/builder.go | 19 | ||||
-rw-r--r-- | cc/cc.go | 22 | ||||
-rw-r--r-- | cc/compiler.go | 4 | ||||
-rw-r--r-- | cc/coverage.go | 2 | ||||
-rw-r--r-- | cc/library.go | 21 | ||||
-rw-r--r-- | cc/linkable.go | 4 | ||||
-rw-r--r-- | cc/linker.go | 6 | ||||
-rw-r--r-- | cc/ndk_library.go | 6 | ||||
-rw-r--r-- | cc/stub_library.go | 4 | ||||
-rw-r--r-- | rust/library.go | 162 | ||||
-rw-r--r-- | rust/library_test.go | 286 | ||||
-rw-r--r-- | rust/rust.go | 164 | ||||
-rw-r--r-- | rust/testing.go | 8 |
14 files changed, 1007 insertions, 107 deletions
diff --git a/apex/apex_test.go b/apex/apex_test.go index e1a958268..6796fca0f 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -897,7 +897,11 @@ func TestApexWithStubs(t *testing.T) { apex { name: "myapex", key: "myapex.key", - native_shared_libs: ["mylib", "mylib3"], + native_shared_libs: [ + "mylib", + "mylib3", + "libmylib3_rs", + ], binaries: ["foo.rust"], updatable: false, } @@ -911,7 +915,14 @@ func TestApexWithStubs(t *testing.T) { cc_library { name: "mylib", srcs: ["mylib.cpp"], - shared_libs: ["mylib2", "mylib3#impl", "my_prebuilt_platform_lib", "my_prebuilt_platform_stub_only_lib"], + shared_libs: [ + "mylib2", + "mylib3#impl", + "libmylib2_rs", + "libmylib3_rs#impl", + "my_prebuilt_platform_lib", + "my_prebuilt_platform_stub_only_lib", + ], system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], @@ -929,6 +940,16 @@ func TestApexWithStubs(t *testing.T) { }, } + rust_ffi { + name: "libmylib2_rs", + crate_name: "mylib2", + srcs: ["mylib.rs"], + stubs: { + symbol_file: "mylib2.map.txt", + versions: ["1", "2", "3"], + }, + } + cc_library { name: "mylib3", srcs: ["mylib.cpp"], @@ -942,6 +963,18 @@ func TestApexWithStubs(t *testing.T) { apex_available: [ "myapex" ], } + rust_ffi { + name: "libmylib3_rs", + crate_name: "mylib3", + srcs: ["mylib.rs"], + shared_libs: ["mylib4.from_rust"], + stubs: { + symbol_file: "mylib3.map.txt", + versions: ["10", "11", "12"], + }, + apex_available: [ "myapex" ], + } + cc_library { name: "mylib4", srcs: ["mylib.cpp"], @@ -950,6 +983,14 @@ func TestApexWithStubs(t *testing.T) { apex_available: [ "myapex" ], } + cc_library { + name: "mylib4.from_rust", + srcs: ["mylib.cpp"], + system_shared_libs: [], + stl: "none", + apex_available: [ "myapex" ], + } + cc_prebuilt_library_shared { name: "my_prebuilt_platform_lib", stubs: { @@ -971,7 +1012,10 @@ func TestApexWithStubs(t *testing.T) { rust_binary { name: "foo.rust", srcs: ["foo.rs"], - shared_libs: ["libfoo.shared_from_rust"], + shared_libs: [ + "libfoo.shared_from_rust", + "libfoo_rs.shared_from_rust", + ], prefer_rlib: true, apex_available: ["myapex"], } @@ -986,6 +1030,15 @@ func TestApexWithStubs(t *testing.T) { }, } + rust_ffi { + name: "libfoo_rs.shared_from_rust", + crate_name: "foo_rs", + srcs: ["mylib.rs"], + stubs: { + versions: ["10", "11", "12"], + }, + } + `) apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule") @@ -996,21 +1049,27 @@ func TestApexWithStubs(t *testing.T) { // Ensure that indirect stubs dep is not included ensureNotContains(t, copyCmds, "image.apex/lib64/mylib2.so") + ensureNotContains(t, copyCmds, "image.apex/lib64/libmylib2_rs.so") // Ensure that direct stubs dep is included ensureContains(t, copyCmds, "image.apex/lib64/mylib3.so") + ensureContains(t, copyCmds, "image.apex/lib64/libmylib3_rs.so") mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"] // Ensure that mylib is linking with the latest version of stubs for mylib2 ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_current/mylib2.so") + ensureContains(t, mylibLdFlags, "libmylib2_rs/android_arm64_armv8-a_shared_current/unstripped/libmylib2_rs.so") // ... and not linking to the non-stub (impl) variant of mylib2 ensureNotContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared/mylib2.so") + ensureNotContains(t, mylibLdFlags, "libmylib2_rs/android_arm64_armv8-a_shared/unstripped/libmylib2_rs.so") // Ensure that mylib is linking with the non-stub (impl) of mylib3 (because the dependency is added with mylib3#impl) ensureContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_shared_apex10000/mylib3.so") + ensureContains(t, mylibLdFlags, "libmylib3_rs/android_arm64_armv8-a_shared_apex10000/unstripped/libmylib3_rs.so") // .. and not linking to the stubs variant of mylib3 ensureNotContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_shared_12/mylib3.so") + ensureNotContains(t, mylibLdFlags, "libmylib3_rs/android_arm64_armv8-a_shared_12/unstripped/mylib3.so") // Comment out this test. Now it fails after the optimization of sharing "cflags" in cc/cc.go // is replaced by sharing of "cFlags" in cc/builder.go. @@ -1026,27 +1085,35 @@ func TestApexWithStubs(t *testing.T) { // Ensure that genstub for platform-provided lib is invoked with --systemapi ensureContains(t, ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_shared_3").Rule("genStubSrc").Args["flags"], "--systemapi") + ensureContains(t, ctx.ModuleForTests("libmylib2_rs", "android_arm64_armv8-a_shared_3").Rule("genStubSrc").Args["flags"], "--systemapi") // Ensure that genstub for apex-provided lib is invoked with --apex ensureContains(t, ctx.ModuleForTests("mylib3", "android_arm64_armv8-a_shared_12").Rule("genStubSrc").Args["flags"], "--apex") + ensureContains(t, ctx.ModuleForTests("libmylib3_rs", "android_arm64_armv8-a_shared_12").Rule("genStubSrc").Args["flags"], "--apex") ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{ "lib64/mylib.so", "lib64/mylib3.so", + "lib64/libmylib3_rs.so", "lib64/mylib4.so", + "lib64/mylib4.from_rust.so", "bin/foo.rust", - "lib64/libc++.so", // by the implicit dependency from foo.rust - "lib64/liblog.so", // by the implicit dependency from foo.rust + + "lib64/libstd.dylib.so", // implicit rust ffi dep }) // Ensure that stub dependency from a rust module is not included ensureNotContains(t, copyCmds, "image.apex/lib64/libfoo.shared_from_rust.so") + ensureNotContains(t, copyCmds, "image.apex/lib64/libfoo_rs.shared_from_rust.so") // The rust module is linked to the stub cc library rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000").Rule("rustc").Args["linkFlags"] ensureContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared_current/libfoo.shared_from_rust.so") + ensureContains(t, rustDeps, "libfoo_rs.shared_from_rust/android_arm64_armv8-a_shared_current/unstripped/libfoo_rs.shared_from_rust.so") ensureNotContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared/libfoo.shared_from_rust.so") + ensureNotContains(t, rustDeps, "libfoo_rs.shared_from_rust/android_arm64_armv8-a_shared/unstripped/libfoo_rs.shared_from_rust.so") apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexManifestRule") ensureListContains(t, names(apexManifestRule.Args["requireNativeLibs"]), "libfoo.shared_from_rust.so") + ensureListContains(t, names(apexManifestRule.Args["requireNativeLibs"]), "libfoo_rs.shared_from_rust.so") // Ensure that mylib is linking with the latest version of stubs for my_prebuilt_platform_lib ensureContains(t, mylibLdFlags, "my_prebuilt_platform_lib/android_arm64_armv8-a_shared_current/my_prebuilt_platform_lib.so") @@ -1111,7 +1178,10 @@ func TestApexCanUsePrivateApis(t *testing.T) { cc_library { name: "mylib", srcs: ["mylib.cpp"], - shared_libs: ["mylib2"], + shared_libs: [ + "mylib2", + "libmylib2_rust" + ], system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], @@ -1128,10 +1198,22 @@ func TestApexCanUsePrivateApis(t *testing.T) { }, } + rust_ffi { + name: "libmylib2_rust", + crate_name: "mylib2_rust", + srcs: ["mylib.rs"], + stubs: { + versions: ["1", "2", "3"], + }, + } + rust_binary { name: "foo.rust", srcs: ["foo.rs"], - shared_libs: ["libfoo.shared_from_rust"], + shared_libs: [ + "libfoo.shared_from_rust", + "libmylib_rust.shared_from_rust" + ], prefer_rlib: true, apex_available: ["myapex"], } @@ -1145,6 +1227,15 @@ func TestApexCanUsePrivateApis(t *testing.T) { versions: ["10", "11", "12"], }, } + rust_ffi { + name: "libmylib_rust.shared_from_rust", + crate_name: "mylib_rust", + srcs: ["mylib.rs"], + stubs: { + versions: ["1", "2", "3"], + }, + } + `) apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule") @@ -1152,6 +1243,8 @@ func TestApexCanUsePrivateApis(t *testing.T) { // Ensure that indirect stubs dep is not included ensureNotContains(t, copyCmds, "image.apex/lib64/mylib2.so") + ensureNotContains(t, copyCmds, "image.apex/lib64/libmylib_rust.so") + ensureNotContains(t, copyCmds, "image.apex/lib64/libmylib_rust.shared_from_rust.so") ensureNotContains(t, copyCmds, "image.apex/lib64/libfoo.shared_from_rust.so") // Ensure that we are using non-stub variants of mylib2 and libfoo.shared_from_rust (because @@ -1159,9 +1252,13 @@ func TestApexCanUsePrivateApis(t *testing.T) { mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"] ensureNotContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_current/mylib2.so") ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared/mylib2.so") + ensureNotContains(t, mylibLdFlags, "libmylib2_rust/android_arm64_armv8-a_shared_current/unstripped/libmylib2_rust.so") + ensureContains(t, mylibLdFlags, "libmylib2_rust/android_arm64_armv8-a_shared/unstripped/libmylib2_rust.so") rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000").Rule("rustc").Args["linkFlags"] ensureNotContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared_current/libfoo.shared_from_rust.so") ensureContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared/libfoo.shared_from_rust.so") + ensureNotContains(t, rustDeps, "libmylib_rust.shared_from_rust/android_arm64_armv8-a_shared_current/unstripped/libmylib_rust.shared_from_rust.so") + ensureContains(t, rustDeps, "libmylib_rust.shared_from_rust/android_arm64_armv8-a_shared/unstripped/libmylib_rust.shared_from_rust.so") } func TestApexWithStubsWithMinSdkVersion(t *testing.T) { @@ -1170,7 +1267,11 @@ func TestApexWithStubsWithMinSdkVersion(t *testing.T) { apex { name: "myapex", key: "myapex.key", - native_shared_libs: ["mylib", "mylib3"], + native_shared_libs: [ + "mylib", + "mylib3", + "libmylib3_rust", + ], min_sdk_version: "29", } @@ -1183,7 +1284,12 @@ func TestApexWithStubsWithMinSdkVersion(t *testing.T) { cc_library { name: "mylib", srcs: ["mylib.cpp"], - shared_libs: ["mylib2", "mylib3#impl"], + shared_libs: [ + "mylib2", + "mylib3#impl", + "libmylib2_rust", + "libmylib3_rust#impl", + ], system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], @@ -1203,6 +1309,17 @@ func TestApexWithStubsWithMinSdkVersion(t *testing.T) { min_sdk_version: "28", } + rust_ffi { + name: "libmylib2_rust", + crate_name: "mylib2_rust", + srcs: ["mylib.rs"], + stubs: { + symbol_file: "mylib2.map.txt", + versions: ["28", "29", "30", "current"], + }, + min_sdk_version: "28", + } + cc_library { name: "mylib3", srcs: ["mylib.cpp"], @@ -1217,6 +1334,19 @@ func TestApexWithStubsWithMinSdkVersion(t *testing.T) { min_sdk_version: "28", } + rust_ffi { + name: "libmylib3_rust", + crate_name: "mylib3_rust", + srcs: ["mylib.rs"], + shared_libs: ["libmylib4.from_rust"], + stubs: { + symbol_file: "mylib3.map.txt", + versions: ["28", "29", "30", "current"], + }, + apex_available: [ "myapex" ], + min_sdk_version: "28", + } + cc_library { name: "mylib4", srcs: ["mylib.cpp"], @@ -1225,6 +1355,14 @@ func TestApexWithStubsWithMinSdkVersion(t *testing.T) { apex_available: [ "myapex" ], min_sdk_version: "28", } + + rust_ffi { + name: "libmylib4.from_rust", + crate_name: "mylib4", + srcs: ["mylib.rs"], + apex_available: [ "myapex" ], + min_sdk_version: "28", + } `) apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule") @@ -1232,9 +1370,12 @@ func TestApexWithStubsWithMinSdkVersion(t *testing.T) { // Ensure that direct non-stubs dep is always included ensureContains(t, copyCmds, "image.apex/lib64/mylib.so") + ensureContains(t, copyCmds, "image.apex/lib64/mylib3.so") + ensureContains(t, copyCmds, "image.apex/lib64/libmylib3_rust.so") // Ensure that indirect stubs dep is not included ensureNotContains(t, copyCmds, "image.apex/lib64/mylib2.so") + ensureNotContains(t, copyCmds, "image.apex/lib64/libmylib2_rust.so") // Ensure that direct stubs dep is included ensureContains(t, copyCmds, "image.apex/lib64/mylib3.so") @@ -1243,13 +1384,17 @@ func TestApexWithStubsWithMinSdkVersion(t *testing.T) { // Ensure that mylib is linking with the latest version of stub for mylib2 ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_current/mylib2.so") + ensureContains(t, mylibLdFlags, "libmylib2_rust/android_arm64_armv8-a_shared_current/unstripped/libmylib2_rust.so") // ... and not linking to the non-stub (impl) variant of mylib2 ensureNotContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared/mylib2.so") + ensureNotContains(t, mylibLdFlags, "libmylib2_rust/android_arm64_armv8-a_shared/unstripped/libmylib2_rust.so") // Ensure that mylib is linking with the non-stub (impl) of mylib3 (because the dependency is added with mylib3#impl) ensureContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_shared_apex29/mylib3.so") + ensureContains(t, mylibLdFlags, "libmylib3_rust/android_arm64_armv8-a_shared_apex29/unstripped/libmylib3_rust.so") // .. and not linking to the stubs variant of mylib3 ensureNotContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_shared_29/mylib3.so") + ensureNotContains(t, mylibLdFlags, "libmylib3_rust/android_arm64_armv8-a_shared_29/unstripped/libmylib3_rust.so") // Ensure that stubs libs are built without -include flags mylib2Cflags := ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_shared_29").Rule("cc").Args["cFlags"] @@ -1257,11 +1402,16 @@ func TestApexWithStubsWithMinSdkVersion(t *testing.T) { // Ensure that genstub is invoked with --systemapi ensureContains(t, ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_shared_29").Rule("genStubSrc").Args["flags"], "--systemapi") + ensureContains(t, ctx.ModuleForTests("libmylib2_rust", "android_arm64_armv8-a_shared_29").Rule("cc.genStubSrc").Args["flags"], "--systemapi") ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{ "lib64/mylib.so", "lib64/mylib3.so", + "lib64/libmylib3_rust.so", "lib64/mylib4.so", + "lib64/libmylib4.from_rust.so", + + "lib64/libstd.dylib.so", // by the implicit dependency from foo.rust }) } @@ -1286,7 +1436,10 @@ func TestApex_PlatformUsesLatestStubFromApex(t *testing.T) { cc_library { name: "mylib", srcs: ["mylib.cpp"], - shared_libs: ["libstub"], + shared_libs: [ + "libstub", + "libstub_rust", + ], apex_available: ["myapex"], min_sdk_version: "Z", } @@ -1300,7 +1453,10 @@ func TestApex_PlatformUsesLatestStubFromApex(t *testing.T) { apex { name: "otherapex", key: "myapex.key", - native_shared_libs: ["libstub"], + native_shared_libs: [ + "libstub", + "libstub_rust", + ], min_sdk_version: "29", } @@ -1314,11 +1470,25 @@ func TestApex_PlatformUsesLatestStubFromApex(t *testing.T) { min_sdk_version: "29", } + rust_ffi { + name: "libstub_rust", + crate_name: "stub_rust", + srcs: ["mylib.rs"], + stubs: { + versions: ["29", "Z", "current"], + }, + apex_available: ["otherapex"], + min_sdk_version: "29", + } + // platform module depending on libstub from otherapex should use the latest stub("current") cc_library { name: "libplatform", srcs: ["mylib.cpp"], - shared_libs: ["libstub"], + shared_libs: [ + "libstub", + "libstub_rust", + ], } `, android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { @@ -1331,14 +1501,20 @@ func TestApex_PlatformUsesLatestStubFromApex(t *testing.T) { // Ensure that mylib from myapex is built against the latest stub (current) mylibCflags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_apex10000").Rule("cc").Args["cFlags"] ensureContains(t, mylibCflags, "-D__LIBSTUB_API__=10000 ") + // rust stubs do not emit -D__LIBFOO_API__ flags as this is deprecated behavior for cc stubs + mylibLdflags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"] ensureContains(t, mylibLdflags, "libstub/android_arm64_armv8-a_shared_current/libstub.so ") + ensureContains(t, mylibLdflags, "libstub_rust/android_arm64_armv8-a_shared_current/unstripped/libstub_rust.so ") // Ensure that libplatform is built against latest stub ("current") of mylib3 from the apex libplatformCflags := ctx.ModuleForTests("libplatform", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"] ensureContains(t, libplatformCflags, "-D__LIBSTUB_API__=10000 ") // "current" maps to 10000 + // rust stubs do not emit -D__LIBFOO_API__ flags as this is deprecated behavior for cc stubs + libplatformLdflags := ctx.ModuleForTests("libplatform", "android_arm64_armv8-a_shared").Rule("ld").Args["libFlags"] ensureContains(t, libplatformLdflags, "libstub/android_arm64_armv8-a_shared_current/libstub.so ") + ensureContains(t, libplatformLdflags, "libstub_rust/android_arm64_armv8-a_shared_current/unstripped/libstub_rust.so ") } func TestApexWithExplicitStubsDependency(t *testing.T) { @@ -1360,7 +1536,10 @@ func TestApexWithExplicitStubsDependency(t *testing.T) { cc_library { name: "mylib", srcs: ["mylib.cpp"], - shared_libs: ["libfoo#10"], + shared_libs: [ + "libfoo#10", + "libfoo_rust#10" + ], static_libs: ["libbaz"], system_shared_libs: [], stl: "none", @@ -1378,6 +1557,16 @@ func TestApexWithExplicitStubsDependency(t *testing.T) { }, } + rust_ffi { + name: "libfoo_rust", + crate_name: "foo_rust", + srcs: ["mylib.cpp"], + shared_libs: ["libbar.from_rust"], + stubs: { + versions: ["10", "20", "30"], + }, + } + cc_library { name: "libbar", srcs: ["mylib.cpp"], @@ -1385,6 +1574,13 @@ func TestApexWithExplicitStubsDependency(t *testing.T) { stl: "none", } + cc_library { + name: "libbar.from_rust", + srcs: ["mylib.cpp"], + system_shared_libs: [], + stl: "none", + } + cc_library_static { name: "libbaz", srcs: ["mylib.cpp"], @@ -1403,21 +1599,27 @@ func TestApexWithExplicitStubsDependency(t *testing.T) { // Ensure that indirect stubs dep is not included ensureNotContains(t, copyCmds, "image.apex/lib64/libfoo.so") + ensureNotContains(t, copyCmds, "image.apex/lib64/libfoo_rust.so") // Ensure that dependency of stubs is not included ensureNotContains(t, copyCmds, "image.apex/lib64/libbar.so") + ensureNotContains(t, copyCmds, "image.apex/lib64/libbar.from_rust.so") mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"] // Ensure that mylib is linking with version 10 of libfoo ensureContains(t, mylibLdFlags, "libfoo/android_arm64_armv8-a_shared_10/libfoo.so") + ensureContains(t, mylibLdFlags, "libfoo_rust/android_arm64_armv8-a_shared_10/unstripped/libfoo_rust.so") // ... and not linking to the non-stub (impl) variant of libfoo ensureNotContains(t, mylibLdFlags, "libfoo/android_arm64_armv8-a_shared/libfoo.so") + ensureNotContains(t, mylibLdFlags, "libfoo_rust/android_arm64_armv8-a_shared/unstripped/libfoo_rust.so") libFooStubsLdFlags := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared_10").Rule("ld").Args["libFlags"] + libFooRustStubsLdFlags := ctx.ModuleForTests("libfoo_rust", "android_arm64_armv8-a_shared_10").Rule("ld").Args["libFlags"] // Ensure that libfoo stubs is not linking to libbar (since it is a stubs) ensureNotContains(t, libFooStubsLdFlags, "libbar.so") + ensureNotContains(t, libFooRustStubsLdFlags, "libbar.from_rust.so") fullDepsInfo := strings.Split(android.ContentFromFileRuleForTests(t, ctx, ctx.ModuleForTests("myapex2", "android_common_myapex2").Output("depsinfo/fulllist.txt")), "\n") @@ -1457,7 +1659,11 @@ func TestApexWithRuntimeLibsDependency(t *testing.T) { srcs: ["mylib.cpp"], static_libs: ["libstatic"], shared_libs: ["libshared"], - runtime_libs: ["libfoo", "libbar"], + runtime_libs: [ + "libfoo", + "libbar", + "libfoo_rs", + ], system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], @@ -1473,6 +1679,15 @@ func TestApexWithRuntimeLibsDependency(t *testing.T) { }, } + rust_ffi { + name: "libfoo_rs", + crate_name: "foo_rs", + srcs: ["mylib.rs"], + stubs: { + versions: ["10", "20", "30"], + }, + } + cc_library { name: "libbar", srcs: ["mylib.cpp"], @@ -1524,6 +1739,7 @@ func TestApexWithRuntimeLibsDependency(t *testing.T) { // Ensure that indirect stubs dep is not included ensureNotContains(t, copyCmds, "image.apex/lib64/libfoo.so") + ensureNotContains(t, copyCmds, "image.apex/lib64/libfoo_rs.so") // Ensure that runtime_libs dep in included ensureContains(t, copyCmds, "image.apex/lib64/libbar.so") @@ -1535,6 +1751,7 @@ func TestApexWithRuntimeLibsDependency(t *testing.T) { apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexManifestRule") ensureListEmpty(t, names(apexManifestRule.Args["provideNativeLibs"])) ensureListContains(t, names(apexManifestRule.Args["requireNativeLibs"]), "libfoo.so") + ensureListContains(t, names(apexManifestRule.Args["requireNativeLibs"]), "libfoo_rs.so") } var prepareForTestOfRuntimeApexWithHwasan = android.GroupFixturePreparers( @@ -1766,7 +1983,7 @@ func TestApexWithSystemLibsStubs(t *testing.T) { apex { name: "myapex", key: "myapex.key", - native_shared_libs: ["mylib", "mylib_shared", "libdl", "libm"], + native_shared_libs: ["mylib", "mylib_shared", "libdl", "libm", "libmylib_rs"], updatable: false, } @@ -1785,6 +2002,14 @@ func TestApexWithSystemLibsStubs(t *testing.T) { apex_available: [ "myapex" ], } + rust_ffi { + name: "libmylib_rs", + crate_name: "mylib_rs", + shared_libs: ["libvers#27", "libm#impl"], + srcs: ["mylib.rs"], + apex_available: [ "myapex" ], + } + cc_library_shared { name: "mylib_shared", srcs: ["mylib.cpp"], @@ -1799,20 +2024,38 @@ func TestApexWithSystemLibsStubs(t *testing.T) { stl: "none", bootstrap: true, } + + rust_ffi { + name: "libbootstrap_rs", + srcs: ["mylib.cpp"], + crate_name: "bootstrap_rs", + bootstrap: true, + } + + cc_library { + name: "libvers", + srcs: ["mylib.cpp"], + stl: "none", + stubs: { versions: ["27","30"] }, + } `) apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] - // Ensure that mylib, libm, libdl are included. + // Ensure that mylib, libmylib_rs, libm, libdl, libstd.dylib.so (from Rust) are included. ensureContains(t, copyCmds, "image.apex/lib64/mylib.so") + ensureContains(t, copyCmds, "image.apex/lib64/libmylib_rs.so") ensureContains(t, copyCmds, "image.apex/lib64/bionic/libm.so") ensureContains(t, copyCmds, "image.apex/lib64/bionic/libdl.so") + ensureContains(t, copyCmds, "image.apex/lib64/libstd.dylib.so") - // Ensure that libc is not included (since it has stubs and not listed in native_shared_libs) + // Ensure that libc and liblog (from Rust) is not included (since it has stubs and not listed in native_shared_libs) ensureNotContains(t, copyCmds, "image.apex/lib64/bionic/libc.so") + ensureNotContains(t, copyCmds, "image.apex/lib64/liblog.so") mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"] + mylibRsFlags := ctx.ModuleForTests("libmylib_rs", "android_arm64_armv8-a_shared_apex10000").Rule("rustc").Args["linkFlags"] mylibCFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_apex10000").Rule("cc").Args["cFlags"] mylibSharedCFlags := ctx.ModuleForTests("mylib_shared", "android_arm64_armv8-a_shared_apex10000").Rule("cc").Args["cFlags"] @@ -1846,11 +2089,42 @@ func TestApexWithSystemLibsStubs(t *testing.T) { ensureContains(t, mylibCFlags, "__LIBDL_API__=27") ensureContains(t, mylibSharedCFlags, "__LIBDL_API__=27") + // Rust checks + // For dependency to libc, liblog + // Ensure that libmylib_rs is linking with the latest versions of stubs + ensureContains(t, mylibRsFlags, "libc/android_arm64_armv8-a_shared_current/libc.so") + ensureContains(t, mylibRsFlags, "liblog/android_arm64_armv8-a_shared_current/liblog.so") + // ... and not linking to the non-stub (impl) variants + ensureNotContains(t, mylibRsFlags, "libc/android_arm64_armv8-a_shared/libc.so") + ensureNotContains(t, mylibRsFlags, "liblog/android_arm64_armv8-a_shared/liblog.so") + + // For libm dependency (explicit) + // Ensure that mylib is linking with the non-stub (impl) variant + ensureContains(t, mylibRsFlags, "libm/android_arm64_armv8-a_shared_apex10000/libm.so") + // ... and not linking to the stub variant + ensureNotContains(t, mylibRsFlags, "libm/android_arm64_armv8-a_shared_29/libm.so") + + // For dependency to libvers + // (We do not use libdl#27 as Rust links the system libs implicitly and does + // not currently have a system_shared_libs equivalent to prevent this) + // Ensure that mylib is linking with the specified version of stubs + ensureContains(t, mylibRsFlags, "libvers/android_arm64_armv8-a_shared_27/libvers.so") + // ... and not linking to the other versions of stubs + ensureNotContains(t, mylibRsFlags, "libvers/android_arm64_armv8-a_shared_30/libvers.so") + // ... and not linking to the non-stub (impl) variant + ensureNotContains(t, mylibRsFlags, "libvers/android_arm64_armv8-a_shared_apex10000/libvers.so") + // Ensure that libBootstrap is depending on the platform variant of bionic libs libFlags := ctx.ModuleForTests("libBootstrap", "android_arm64_armv8-a_shared").Rule("ld").Args["libFlags"] ensureContains(t, libFlags, "libc/android_arm64_armv8-a_shared/libc.so") ensureContains(t, libFlags, "libm/android_arm64_armv8-a_shared/libm.so") ensureContains(t, libFlags, "libdl/android_arm64_armv8-a_shared/libdl.so") + + // Ensure that libbootstrap_rs is depending on the platform variant of bionic libs + libRsFlags := ctx.ModuleForTests("libbootstrap_rs", "android_arm64_armv8-a_shared").Rule("rustc").Args["linkFlags"] + ensureContains(t, libRsFlags, "libc/android_arm64_armv8-a_shared/libc.so") + ensureContains(t, libRsFlags, "libm/android_arm64_armv8-a_shared/libm.so") + ensureContains(t, libRsFlags, "libdl/android_arm64_armv8-a_shared/libdl.so") } func TestApexMinSdkVersion_NativeModulesShouldBeBuiltAgainstStubs(t *testing.T) { @@ -1900,7 +2174,7 @@ func TestApexMinSdkVersion_NativeModulesShouldBeBuiltAgainstStubs(t *testing.T) cc_library { name: "liba", - shared_libs: ["libz"], + shared_libs: ["libz", "libz_rs"], system_shared_libs: [], stl: "none", apex_available: [ @@ -1918,6 +2192,15 @@ func TestApexMinSdkVersion_NativeModulesShouldBeBuiltAgainstStubs(t *testing.T) versions: ["28", "30"], }, } + + rust_ffi { + name: "libz_rs", + crate_name: "z_rs", + srcs: ["foo.rs"], + stubs: { + versions: ["28", "30"], + }, + } `) expectLink := func(from, from_variant, to, to_variant string) { @@ -1930,16 +2213,25 @@ func TestApexMinSdkVersion_NativeModulesShouldBeBuiltAgainstStubs(t *testing.T) } // platform liba is linked to non-stub version expectLink("liba", "shared", "libz", "shared") + expectLink("liba", "shared", "unstripped/libz_rs", "shared") // liba in myapex is linked to current expectLink("liba", "shared_apex29", "libz", "shared_current") expectNoLink("liba", "shared_apex29", "libz", "shared_30") expectNoLink("liba", "shared_apex29", "libz", "shared_28") expectNoLink("liba", "shared_apex29", "libz", "shared") + expectLink("liba", "shared_apex29", "unstripped/libz_rs", "shared_current") + expectNoLink("liba", "shared_apex29", "unstripped/libz_rs", "shared_30") + expectNoLink("liba", "shared_apex29", "unstripped/libz_rs", "shared_28") + expectNoLink("liba", "shared_apex29", "unstripped/libz_rs", "shared") // liba in otherapex is linked to current expectLink("liba", "shared_apex30", "libz", "shared_current") expectNoLink("liba", "shared_apex30", "libz", "shared_30") expectNoLink("liba", "shared_apex30", "libz", "shared_28") expectNoLink("liba", "shared_apex30", "libz", "shared") + expectLink("liba", "shared_apex30", "unstripped/libz_rs", "shared_current") + expectNoLink("liba", "shared_apex30", "unstripped/libz_rs", "shared_30") + expectNoLink("liba", "shared_apex30", "unstripped/libz_rs", "shared_28") + expectNoLink("liba", "shared_apex30", "unstripped/libz_rs", "shared") } func TestApexMinSdkVersion_SupportsCodeNames(t *testing.T) { @@ -2159,7 +2451,7 @@ func TestTrackAllowedDeps(t *testing.T) { cc_library { name: "mylib", srcs: ["mylib.cpp"], - shared_libs: ["libbar"], + shared_libs: ["libbar", "libbar_rs"], min_sdk_version: "29", apex_available: ["myapex"], } @@ -2169,6 +2461,13 @@ func TestTrackAllowedDeps(t *testing.T) { stubs: { versions: ["29", "30"] }, } + rust_ffi { + name: "libbar_rs", + crate_name: "bar_rs", + srcs: ["bar.rs"], + stubs: { versions: ["29", "30"] }, + } + cc_library { name: "yourlib", srcs: ["mylib.cpp"], @@ -2191,6 +2490,8 @@ func TestTrackAllowedDeps(t *testing.T) { myapex.Output("depsinfo/flatlist.txt")), "\n") android.AssertStringListContains(t, "deps with stubs should be tracked in depsinfo as external dep", flatlist, "libbar(minSdkVersion:(no version)) (external)") + android.AssertStringListContains(t, "deps with stubs should be tracked in depsinfo as external dep", + flatlist, "libbar_rs(minSdkVersion:(no version)) (external)") android.AssertStringListDoesNotContain(t, "do not track if not available for platform", flatlist, "mylib:(minSdkVersion:29)") android.AssertStringListContains(t, "track platform-available lib", @@ -2227,7 +2528,7 @@ func TestTrackCustomAllowedDepsInvalidDefaultTxt(t *testing.T) { cc_library { name: "mylib", srcs: ["mylib.cpp"], - shared_libs: ["libbar"], + shared_libs: ["libbar", "libbar_rs",], min_sdk_version: "29", apex_available: ["myapex"], } @@ -2237,6 +2538,13 @@ func TestTrackCustomAllowedDepsInvalidDefaultTxt(t *testing.T) { stubs: { versions: ["29", "30"] }, } + rust_ffi { + name: "libbar_rs", + crate_name: "bar_rs", + srcs: ["bar.rs"], + stubs: { versions: ["29", "30"] }, + } + cc_library { name: "yourlib", srcs: ["mylib.cpp"], @@ -2264,6 +2572,8 @@ func TestTrackCustomAllowedDepsInvalidDefaultTxt(t *testing.T) { myapex.Output("depsinfo/flatlist.txt")), "\n") android.AssertStringListContains(t, "deps with stubs should be tracked in depsinfo as external dep", flatlist, "libbar(minSdkVersion:(no version)) (external)") + android.AssertStringListContains(t, "deps with stubs should be tracked in depsinfo as external dep", + flatlist, "libbar_rs(minSdkVersion:(no version)) (external)") android.AssertStringListDoesNotContain(t, "do not track if not available for platform", flatlist, "mylib:(minSdkVersion:29)") android.AssertStringListContains(t, "track platform-available lib", @@ -2300,7 +2610,7 @@ func TestTrackCustomAllowedDepsWithDefaultTxt(t *testing.T) { cc_library { name: "mylib", srcs: ["mylib.cpp"], - shared_libs: ["libbar"], + shared_libs: ["libbar", "libbar_rs"], min_sdk_version: "29", apex_available: ["myapex"], } @@ -2310,6 +2620,13 @@ func TestTrackCustomAllowedDepsWithDefaultTxt(t *testing.T) { stubs: { versions: ["29", "30"] }, } + rust_ffi { + name: "libbar_rs", + crate_name: "bar_rs", + srcs: ["bar.rs"], + stubs: { versions: ["29", "30"] }, + } + cc_library { name: "yourlib", srcs: ["mylib.cpp"], @@ -2338,6 +2655,8 @@ func TestTrackCustomAllowedDepsWithDefaultTxt(t *testing.T) { myapex.Output("depsinfo/flatlist.txt")), "\n") android.AssertStringListContains(t, "deps with stubs should be tracked in depsinfo as external dep", flatlist, "libbar(minSdkVersion:(no version)) (external)") + android.AssertStringListContains(t, "deps with stubs should be tracked in depsinfo as external dep", + flatlist, "libbar_rs(minSdkVersion:(no version)) (external)") android.AssertStringListDoesNotContain(t, "do not track if not available for platform", flatlist, "mylib:(minSdkVersion:29)") android.AssertStringListContains(t, "track platform-available lib", @@ -2372,7 +2691,7 @@ func TestPlatformUsesLatestStubsFromApexes(t *testing.T) { apex { name: "myapex", key: "myapex.key", - native_shared_libs: ["libx"], + native_shared_libs: ["libx", "libx_rs"], updatable: false, } @@ -2392,9 +2711,19 @@ func TestPlatformUsesLatestStubsFromApexes(t *testing.T) { }, } + rust_ffi { + name: "libx_rs", + crate_name: "x_rs", + srcs: ["x.rs"], + apex_available: [ "myapex" ], + stubs: { + versions: ["1", "2"], + }, + } + cc_library { name: "libz", - shared_libs: ["libx"], + shared_libs: ["libx", "libx_rs",], system_shared_libs: [], stl: "none", } @@ -2412,6 +2741,8 @@ func TestPlatformUsesLatestStubsFromApexes(t *testing.T) { } expectLink("libz", "shared", "libx", "shared_current") expectNoLink("libz", "shared", "libx", "shared_2") + expectLink("libz", "shared", "unstripped/libx_rs", "shared_current") + expectNoLink("libz", "shared", "unstripped/libx_rs", "shared_2") expectNoLink("libz", "shared", "libz", "shared_1") expectNoLink("libz", "shared", "libz", "shared") } @@ -2440,11 +2771,18 @@ func TestQApexesUseLatestStubsInBundledBuildsAndHWASAN(t *testing.T) { cc_library { name: "libx", - shared_libs: ["libbar"], + shared_libs: ["libbar", "libbar_rs"], apex_available: [ "myapex" ], min_sdk_version: "29", } + rust_ffi { + name: "libbar_rs", + crate_name: "bar_rs", + srcs: ["bar.rs"], + stubs: { versions: ["29", "30"] }, + } + cc_library { name: "libbar", stubs: { @@ -2460,6 +2798,7 @@ func TestQApexesUseLatestStubsInBundledBuildsAndHWASAN(t *testing.T) { ensureContains(t, libFlags, "android_arm64_armv8-a_"+to_variant+"/"+to+".so") } expectLink("libx", "shared_hwasan_apex29", "libbar", "shared_current") + expectLink("libx", "shared_hwasan_apex29", "unstripped/libbar_rs", "shared_current") } func TestQTargetApexUsesStaticUnwinder(t *testing.T) { @@ -3404,10 +3743,20 @@ func TestStaticLinking(t *testing.T) { apex_available: ["myapex"], } + rust_ffi { + name: "libmylib_rs", + crate_name: "mylib_rs", + srcs: ["mylib.rs"], + stubs: { + versions: ["1", "2", "3"], + }, + apex_available: ["myapex"], + } + cc_binary { name: "not_in_apex", srcs: ["mylib.cpp"], - static_libs: ["mylib"], + static_libs: ["mylib", "libmylib_rs"], static_executable: true, system_shared_libs: [], stl: "none", @@ -3418,6 +3767,7 @@ func TestStaticLinking(t *testing.T) { // Ensure that not_in_apex is linking with the static variant of mylib ensureContains(t, ldFlags, "mylib/android_arm64_armv8-a_static/mylib.a") + ensureContains(t, ldFlags, "generated_rust_staticlib/librustlibs.a") } func TestKeys(t *testing.T) { @@ -8217,8 +8567,6 @@ func TestApexWithJniLibs(t *testing.T) { "lib64/mylib2.so", "lib64/mylib3.so", "lib64/libfoo.rust.so", - "lib64/libc++.so", // auto-added to libfoo.rust by Soong - "lib64/liblog.so", // auto-added to libfoo.rust by Soong }) // b/220397949 @@ -10676,8 +11024,8 @@ func TestAconfigFilesRustDeps(t *testing.T) { mod := ctx.ModuleForTests("myapex", "android_common_myapex") s := mod.Rule("apexRule").Args["copy_commands"] copyCmds := regexp.MustCompile(" *&& *").Split(s, -1) - if len(copyCmds) != 38 { - t.Fatalf("Expected 38 commands, got %d in:\n%s", len(copyCmds), s) + if len(copyCmds) != 34 { + t.Fatalf("Expected 34 commands, got %d in:\n%s", len(copyCmds), s) } ensureListContainsMatch(t, copyCmds, "^cp -f .*/aconfig_flags.pb .*/image.apex/etc/aconfig_flags.pb") diff --git a/cc/builder.go b/cc/builder.go index 56b7139e1..532511628 100644 --- a/cc/builder.go +++ b/cc/builder.go @@ -851,6 +851,25 @@ func genRustStaticlibSrcFile(crateNames []string) string { return strings.Join(lines, "\n") } +func BuildRustStubs(ctx android.ModuleContext, outputFile android.ModuleOutPath, + crtBegin, crtEnd android.Paths, stubObjs Objects, ccFlags Flags) { + + // Instantiate paths + sharedLibs := android.Paths{} + staticLibs := android.Paths{} + lateStaticLibs := android.Paths{} + wholeStaticLibs := android.Paths{} + deps := android.Paths{} + implicitOutputs := android.WritablePaths{} + validations := android.Paths{} + groupLate := false + + builderFlags := flagsToBuilderFlags(ccFlags) + transformObjToDynamicBinary(ctx, stubObjs.objFiles, sharedLibs, staticLibs, + lateStaticLibs, wholeStaticLibs, deps, crtBegin, crtEnd, + groupLate, builderFlags, outputFile, implicitOutputs, validations) +} + // Generate a rule for compiling multiple .o files, plus static libraries, whole static libraries, // and shared libraries, to a shared library (.so) or dynamic executable func transformObjToDynamicBinary(ctx android.ModuleContext, @@ -160,6 +160,8 @@ type LinkableInfo struct { Installable *bool // RelativeInstallPath returns the relative install path for this module. RelativeInstallPath string + // TODO(b/362509506): remove this once all apex_exclude uses are switched to stubs. + RustApexExclude bool } var LinkableInfoProvider = blueprint.NewProvider[*LinkableInfo]() @@ -943,6 +945,11 @@ func IsRuntimeDepTag(depTag blueprint.DependencyTag) bool { return depTag == runtimeDepTag } +func ExcludeInApexDepTag(depTag blueprint.DependencyTag) bool { + ccLibDepTag, ok := depTag.(libraryDependencyTag) + return ok && ccLibDepTag.excludeInApex +} + // Module contains the properties and members used by all C/C++ module types, and implements // the blueprint.Module interface. It delegates to compiler, linker, and installer interfaces // to construct the output file. Behavior can be customized with a Customizer, or "decorator", @@ -1529,6 +1536,10 @@ func (c *Module) HasStubsVariants() bool { return false } +func (c *Module) RustApexExclude() bool { + return false +} + func (c *Module) IsStubsImplementationRequired() bool { if lib := c.library; lib != nil { return lib.IsStubsImplementationRequired() @@ -2360,6 +2371,8 @@ func CreateCommonLinkableInfo(mod VersionedLinkableInterface) *LinkableInfo { OnlyInRecovery: mod.OnlyInRecovery(), Installable: mod.Installable(), RelativeInstallPath: mod.RelativeInstallPath(), + // TODO(b/362509506): remove this once all apex_exclude uses are switched to stubs. + RustApexExclude: mod.RustApexExclude(), } } @@ -3354,9 +3367,12 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps { depFile = sharedLibraryInfo.TableOfContents if !sharedLibraryInfo.IsStubs { - depPaths.directImplementationDeps = append(depPaths.directImplementationDeps, android.OutputFileForModule(ctx, dep, "")) - if info, ok := android.OtherModuleProvider(ctx, dep, ImplementationDepInfoProvider); ok { - depPaths.transitiveImplementationDeps = append(depPaths.transitiveImplementationDeps, info.ImplementationDeps) + // TODO(b/362509506): remove this additional check once all apex_exclude uses are switched to stubs. + if !linkableInfo.RustApexExclude { + depPaths.directImplementationDeps = append(depPaths.directImplementationDeps, android.OutputFileForModule(ctx, dep, "")) + if info, ok := android.OtherModuleProvider(ctx, dep, ImplementationDepInfoProvider); ok { + depPaths.transitiveImplementationDeps = append(depPaths.transitiveImplementationDeps, info.ImplementationDeps) + } } } diff --git a/cc/compiler.go b/cc/compiler.go index 1899f5965..3730bbe66 100644 --- a/cc/compiler.go +++ b/cc/compiler.go @@ -360,7 +360,7 @@ func parseCStd(cStdPtr *string) string { } } -func addTargetFlags(ctx android.ModuleContext, flags Flags, tc config.Toolchain, version string, bpf bool) Flags { +func AddTargetFlags(ctx android.ModuleContext, flags Flags, tc config.Toolchain, version string, bpf bool) Flags { target := "-target " + tc.ClangTriple() if ctx.Os().Class == android.Device { if version == "" || version == "current" { @@ -536,7 +536,7 @@ func (compiler *baseCompiler) compilerFlags(ctx ModuleContext, flags Flags, deps flags.Local.ConlyFlags = config.ClangFilterUnknownCflags(flags.Local.ConlyFlags) flags.Local.LdFlags = config.ClangFilterUnknownCflags(flags.Local.LdFlags) - flags = addTargetFlags(ctx, flags, tc, ctx.minSdkVersion(), Bool(compiler.Properties.Bpf_target)) + flags = AddTargetFlags(ctx, flags, tc, ctx.minSdkVersion(), Bool(compiler.Properties.Bpf_target)) hod := "Host" if ctx.Os().Class == android.Device { diff --git a/cc/coverage.go b/cc/coverage.go index 4e058bdb6..c8ff82b85 100644 --- a/cc/coverage.go +++ b/cc/coverage.go @@ -354,7 +354,7 @@ func (c coverageTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, va } } -func parseSymbolFileForAPICoverage(ctx android.ModuleContext, symbolFile string) android.ModuleOutPath { +func ParseSymbolFileForAPICoverage(ctx android.ModuleContext, symbolFile string) android.ModuleOutPath { apiLevelsJson := android.GetApiLevelsJson(ctx) symbolFilePath := android.PathForModuleSrc(ctx, symbolFile) outputFile := ctx.Module().(LinkableInterface).BaseModuleName() + ".xml" diff --git a/cc/library.go b/cc/library.go index 7b225ecf8..950ea9134 100644 --- a/cc/library.go +++ b/cc/library.go @@ -460,7 +460,7 @@ func (library *libraryDecorator) linkerProps() []interface{} { return props } -func commonLibraryLinkerFlags(ctx android.ModuleContext, flags Flags, +func CommonLibraryLinkerFlags(ctx android.ModuleContext, flags Flags, toolchain config.Toolchain, libName string) Flags { mod, ok := ctx.Module().(LinkableInterface) @@ -511,7 +511,7 @@ func commonLibraryLinkerFlags(ctx android.ModuleContext, flags Flags, // shared library). func (library *libraryDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags { flags = library.baseLinker.linkerFlags(ctx, flags) - flags = commonLibraryLinkerFlags(ctx, flags, ctx.toolchain(), library.getLibName(ctx)) + flags = CommonLibraryLinkerFlags(ctx, flags, ctx.toolchain(), library.getLibName(ctx)) if library.static() { flags.Local.CFlags = append(flags.Local.CFlags, library.StaticProperties.Static.Cflags.GetOrDefault(ctx, nil)...) } else if library.shared() { @@ -558,7 +558,7 @@ func (library *libraryDecorator) compilerFlags(ctx ModuleContext, flags Flags, d flags.Local.CommonFlags = removeInclude(flags.Local.CommonFlags) flags.Local.CFlags = removeInclude(flags.Local.CFlags) - flags = addStubLibraryCompilerFlags(flags) + flags = AddStubLibraryCompilerFlags(flags) } return flags } @@ -735,7 +735,7 @@ func (library *libraryDecorator) compileModuleLibApiStubs(ctx ModuleContext, fla nativeAbiResult.VersionScript) // Parse symbol file to get API list for coverage if library.StubsVersion() == "current" && ctx.PrimaryArch() && !ctx.inRecovery() && !ctx.inProduct() && !ctx.inVendor() { - library.apiListCoverageXmlPath = parseSymbolFileForAPICoverage(ctx, symbolFile) + library.apiListCoverageXmlPath = ParseSymbolFileForAPICoverage(ctx, symbolFile) } return objs @@ -2360,10 +2360,6 @@ func moduleVersionedInterface(module blueprint.Module) VersionedInterface { // setStubsVersions normalizes the versions in the Stubs.Versions property into MutatedProperties.AllStubsVersions. func setStubsVersions(mctx android.BaseModuleContext, module VersionedLinkableInterface) { - // TODO(ivanlozano) remove this when Rust supports stubs - if module.RustLibraryInterface() { - return - } if !module.BuildSharedVariant() || !canBeVersionVariant(module) { return } @@ -2385,10 +2381,6 @@ func (versionTransitionMutator) Split(ctx android.BaseModuleContext) []string { return []string{""} } if m, ok := ctx.Module().(VersionedLinkableInterface); ok { - // TODO(ivanlozano) remove this when Rust supports stubs - if m.RustLibraryInterface() { - return []string{""} - } if m.CcLibraryInterface() && canBeVersionVariant(m) { setStubsVersions(ctx, m) return append(slices.Clone(m.VersionedInterface().AllStubsVersions()), "") @@ -2439,11 +2431,6 @@ func (versionTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, varia m, ok := ctx.Module().(VersionedLinkableInterface) if library := moduleVersionedInterface(ctx.Module()); library != nil && canBeVersionVariant(m) { - // TODO(ivanlozano) remove this when Rust supports stubs - if m.RustLibraryInterface() { - return - } - isLLNDK := m.IsLlndk() isVendorPublicLibrary := m.IsVendorPublicLibrary() diff --git a/cc/linkable.go b/cc/linkable.go index d0fda6459..337b45943 100644 --- a/cc/linkable.go +++ b/cc/linkable.go @@ -84,6 +84,10 @@ type VersionedLinkableInterface interface { SetMinSdkVersion(version string) ApexSdkVersion() android.ApiLevel ImplementationModuleNameForMake(ctx android.BaseModuleContext) string + + // RustApexExclude returns ApexExclude() for Rust modules; always returns false for all non-Rust modules. + // TODO(b/362509506): remove this once all apex_exclude uses are switched to stubs. + RustApexExclude() bool } // LinkableInterface is an interface for a type of module that is linkable in a C++ library. diff --git a/cc/linker.go b/cc/linker.go index 20c2f4a45..f85493726 100644 --- a/cc/linker.go +++ b/cc/linker.go @@ -471,7 +471,7 @@ func CheckSdkVersionAtLeast(ctx ModuleContext, SdkVersion android.ApiLevel) bool // ModuleContext extends BaseModuleContext // BaseModuleContext should know if LLD is used? -func commonLinkerFlags(ctx android.ModuleContext, flags Flags, useClangLld bool, +func CommonLinkerFlags(ctx android.ModuleContext, flags Flags, useClangLld bool, toolchain config.Toolchain, allow_undefined_symbols bool) Flags { hod := "Host" if ctx.Os().Class == android.Device { @@ -480,7 +480,7 @@ func commonLinkerFlags(ctx android.ModuleContext, flags Flags, useClangLld bool, mod, ok := ctx.Module().(LinkableInterface) if !ok { - ctx.ModuleErrorf("trying to add commonLinkerFlags to non-LinkableInterface module.") + ctx.ModuleErrorf("trying to add CommonLinkerFlags to non-LinkableInterface module.") return flags } if useClangLld { @@ -531,7 +531,7 @@ func (linker *baseLinker) linkerFlags(ctx ModuleContext, flags Flags) Flags { toolchain := ctx.toolchain() allow_undefined_symbols := Bool(linker.Properties.Allow_undefined_symbols) - flags = commonLinkerFlags(ctx, flags, linker.useClangLld(ctx), toolchain, + flags = CommonLinkerFlags(ctx, flags, linker.useClangLld(ctx), toolchain, allow_undefined_symbols) if !toolchain.Bionic() && ctx.Os() != android.LinuxMusl { diff --git a/cc/ndk_library.go b/cc/ndk_library.go index 4321bebfd..1f0fc0764 100644 --- a/cc/ndk_library.go +++ b/cc/ndk_library.go @@ -236,7 +236,7 @@ func init() { pctx.StaticVariable("StubLibraryCompilerFlags", strings.Join(stubLibraryCompilerFlags, " ")) } -func addStubLibraryCompilerFlags(flags Flags) Flags { +func AddStubLibraryCompilerFlags(flags Flags) Flags { flags.Global.CFlags = append(flags.Global.CFlags, stubLibraryCompilerFlags...) // All symbols in the stubs library should be visible. if inList("-fvisibility=hidden", flags.Local.CFlags) { @@ -247,7 +247,7 @@ func addStubLibraryCompilerFlags(flags Flags) Flags { func (stub *stubDecorator) compilerFlags(ctx ModuleContext, flags Flags, deps PathDeps) Flags { flags = stub.baseCompiler.compilerFlags(ctx, flags, deps) - return addStubLibraryCompilerFlags(flags) + return AddStubLibraryCompilerFlags(flags) } type NdkApiOutputs struct { @@ -510,7 +510,7 @@ func (c *stubDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) O } } if c.apiLevel.IsCurrent() && ctx.PrimaryArch() { - c.parsedCoverageXmlPath = parseSymbolFileForAPICoverage(ctx, symbolFile) + c.parsedCoverageXmlPath = ParseSymbolFileForAPICoverage(ctx, symbolFile) } return objs } diff --git a/cc/stub_library.go b/cc/stub_library.go index 2291153a2..9d7b5bc1a 100644 --- a/cc/stub_library.go +++ b/cc/stub_library.go @@ -59,10 +59,6 @@ func (s *stubLibraries) GenerateBuildActions(ctx android.SingletonContext) { vendorStubLibraryMap := make(map[string]bool) ctx.VisitAllModules(func(module android.Module) { if m, ok := module.(VersionedLinkableInterface); ok { - // TODO(ivanlozano) remove this when Rust supports stubs - if m.RustLibraryInterface() { - return - } if IsStubTarget(android.OtherModuleProviderOrDefault(ctx, m, LinkableInfoProvider)) { if name := getInstalledFileName(ctx, m); name != "" { stubLibraryMap[name] = true diff --git a/rust/library.go b/rust/library.go index 9912c946e..49169ac08 100644 --- a/rust/library.go +++ b/rust/library.go @@ -25,6 +25,7 @@ import ( "android/soong/android" "android/soong/cc" + cc_config "android/soong/cc/config" ) var ( @@ -79,9 +80,13 @@ type LibraryCompilerProperties struct { // Whether this library is part of the Rust toolchain sysroot. Sysroot *bool - // Exclude this rust_ffi target from being included in APEXes. - // TODO(b/362509506): remove this once stubs are properly supported by rust_ffi targets. + // Deprecated - exclude this rust_ffi target from being included in APEXes. + // TODO(b/362509506): remove this once all apex_exclude uses are switched to stubs. Apex_exclude *bool + + // Generate stubs to make this library accessible to APEXes. + // Can only be set for modules producing shared libraries. + Stubs cc.StubsProperties `android:"arch_variant"` } type LibraryMutatedProperties struct { @@ -109,6 +114,15 @@ type LibraryMutatedProperties struct { // Whether this library variant should be link libstd via rlibs VariantIsStaticStd bool `blueprint:"mutated"` + + // This variant is a stubs lib + BuildStubs bool `blueprint:"mutated"` + // This variant is the latest version + IsLatestVersion bool `blueprint:"mutated"` + // Version of the stubs lib + StubsVersion string `blueprint:"mutated"` + // List of all stubs versions associated with an implementation lib + AllStubsVersions []string `blueprint:"mutated"` } type libraryDecorator struct { @@ -123,9 +137,28 @@ type libraryDecorator struct { // table-of-contents file for cdylib crates to optimize out relinking when possible tocFile android.OptionalPath + + // Path to the file containing the APIs exported by this library + stubsSymbolFilePath android.Path + apiListCoverageXmlPath android.ModuleOutPath + versionScriptPath android.OptionalPath +} + +func (library *libraryDecorator) stubs() bool { + return library.MutatedProperties.BuildStubs +} + +func (library *libraryDecorator) setAPIListCoverageXMLPath(xml android.ModuleOutPath) { + library.apiListCoverageXmlPath = xml +} + +func (library *libraryDecorator) libraryProperties() LibraryCompilerProperties { + return library.Properties } type libraryInterface interface { + cc.VersionedInterface + rlib() bool dylib() bool static() bool @@ -161,6 +194,11 @@ type libraryInterface interface { BuildOnlyShared() toc() android.OptionalPath + + IsStubsImplementationRequired() bool + setAPIListCoverageXMLPath(out android.ModuleOutPath) + + libraryProperties() LibraryCompilerProperties } func (library *libraryDecorator) nativeCoverage() bool { @@ -276,58 +314,63 @@ func (library *libraryDecorator) stdLinkage(device bool) RustLinkage { var _ compiler = (*libraryDecorator)(nil) var _ libraryInterface = (*libraryDecorator)(nil) +var _ cc.VersionedInterface = (*libraryDecorator)(nil) var _ exportedFlagsProducer = (*libraryDecorator)(nil) var _ cc.VersionedInterface = (*libraryDecorator)(nil) func (library *libraryDecorator) HasLLNDKStubs() bool { - // Rust does not support LLNDK yet. + // Rust LLNDK is currently unsupported return false } func (library *libraryDecorator) HasVendorPublicLibrary() bool { - // Rust does not support vendor public library. + // Rust does not support vendor_public_library yet. return false } func (library *libraryDecorator) HasLLNDKHeaders() bool { - // Rust does not support LLNDK yet. + // Rust LLNDK is currently unsupported return false } func (library *libraryDecorator) HasStubsVariants() bool { - return false + // Just having stubs.symbol_file is enough to create a stub variant. In that case + // the stub for the future API level is created. + return library.Properties.Stubs.Symbol_file != nil || + len(library.Properties.Stubs.Versions) > 0 } func (library *libraryDecorator) IsStubsImplementationRequired() bool { - return false + return BoolDefault(library.Properties.Stubs.Implementation_installable, true) } func (library *libraryDecorator) GetAPIListCoverageXMLPath() android.ModuleOutPath { - panic(fmt.Errorf("GetAPIListCoverageXMLPath called on unsupported Rust module")) + return library.apiListCoverageXmlPath } func (library *libraryDecorator) AllStubsVersions() []string { - panic(fmt.Errorf("AllStubsVersions called on unsupported Rust module")) + return library.MutatedProperties.AllStubsVersions } func (library *libraryDecorator) SetAllStubsVersions(versions []string) { - panic(fmt.Errorf("ApexSdkVersion called on unsupported Rust module")) + library.MutatedProperties.AllStubsVersions = versions } func (library *libraryDecorator) SetStubsVersion(version string) { - panic(fmt.Errorf("SetStubsVersion called on unsupported Rust module")) + library.MutatedProperties.StubsVersion = version } func (library *libraryDecorator) SetBuildStubs(isLatest bool) { - panic(fmt.Errorf("SetBuildStubs called on unsupported Rust module")) + library.MutatedProperties.BuildStubs = true + library.MutatedProperties.IsLatestVersion = isLatest } func (library *libraryDecorator) BuildStubs() bool { - return false + return library.MutatedProperties.BuildStubs } func (library *libraryDecorator) ImplementationModuleName(name string) string { - panic(fmt.Errorf("ImplementationModuleName called on unsupported Rust module")) + return name } func (library *libraryDecorator) IsLLNDKMovedToApex() bool { @@ -335,12 +378,20 @@ func (library *libraryDecorator) IsLLNDKMovedToApex() bool { return false } -func (library *libraryDecorator) StubsVersions(ctx android.BaseModuleContext) []string { - panic(fmt.Errorf("StubsVersions called on unsupported Rust module")) +func (library *libraryDecorator) StubsVersion() string { + return library.MutatedProperties.StubsVersion } -func (library *libraryDecorator) StubsVersion() string { - panic(fmt.Errorf("StubsVersions called on unsupported Rust module")) +// stubsVersions implements cc.VersionedInterface. +func (library *libraryDecorator) StubsVersions(ctx android.BaseModuleContext) []string { + if !library.HasStubsVariants() { + return nil + } + + // Future API level is implicitly added if there isn't + versions := cc.AddCurrentVersionIfNotPresent(library.Properties.Stubs.Versions) + cc.NormalizeVersions(ctx, versions) + return versions } // rust_library produces all Rust variants (rust_library_dylib and @@ -420,6 +471,18 @@ func RustFFISharedHostFactory() android.Module { return module.Init() } +func CheckRustLibraryProperties(mctx android.DefaultableHookContext) { + lib := mctx.Module().(*Module).compiler.(libraryInterface) + if !lib.buildShared() { + if lib.libraryProperties().Stubs.Symbol_file != nil || + lib.libraryProperties().Stubs.Implementation_installable != nil || + len(lib.libraryProperties().Stubs.Versions) > 0 { + + mctx.PropertyErrorf("stubs", "stubs properties can only be set for rust_ffi or rust_ffi_shared modules") + } + } +} + func (library *libraryDecorator) BuildOnlyFFI() { library.MutatedProperties.BuildDylib = false // we build rlibs for later static ffi linkage. @@ -479,6 +542,7 @@ func NewRustLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorat module.compiler = library + module.SetDefaultableHook(CheckRustLibraryProperties) return module, library } @@ -641,7 +705,11 @@ func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps Pa } // Call the appropriate builder for this library type - if library.rlib() { + if library.stubs() { + ccFlags := library.getApiStubsCcFlags(ctx) + stubObjs := library.compileModuleLibApiStubs(ctx, ccFlags) + cc.BuildRustStubs(ctx, outputFile, deps.CrtBegin, deps.CrtEnd, stubObjs, ccFlags) + } else if library.rlib() { ret.kytheFile = TransformSrctoRlib(ctx, crateRootPath, deps, flags, outputFile).kytheFile } else if library.dylib() { ret.kytheFile = TransformSrctoDylib(ctx, crateRootPath, deps, flags, outputFile).kytheFile @@ -657,13 +725,13 @@ func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps Pa } // Since we have FFI rlibs, we need to collect their includes as well - if library.static() || library.shared() || library.rlib() { + if library.static() || library.shared() || library.rlib() || library.stubs() { android.SetProvider(ctx, cc.FlagExporterInfoProvider, cc.FlagExporterInfo{ IncludeDirs: android.FirstUniquePaths(library.includeDirs), }) } - if library.shared() { + if library.shared() || library.stubs() { // Optimize out relinking against shared libraries whose interface hasn't changed by // depending on a table of contents file instead of the library itself. tocFile := outputFile.ReplaceExtension(ctx, flags.Toolchain.SharedLibSuffix()[1:]+".toc") @@ -674,9 +742,7 @@ func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps Pa TableOfContents: android.OptionalPathForPath(tocFile), SharedLibrary: outputFile, Target: ctx.Target(), - // TODO: when rust supports stubs uses the stubs state rather than inferring it from - // apex_exclude. - IsStubs: Bool(library.Properties.Apex_exclude), + IsStubs: library.BuildStubs(), }) } @@ -688,6 +754,7 @@ func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps Pa TransitiveStaticLibrariesForOrdering: depSet, }) } + cc.AddStubDependencyProviders(ctx) library.flagExporter.setProvider(ctx) @@ -707,6 +774,53 @@ func (library *libraryDecorator) checkedCrateRootPath() (android.Path, error) { } } +func (library *libraryDecorator) getApiStubsCcFlags(ctx ModuleContext) cc.Flags { + ccFlags := cc.Flags{} + toolchain := cc_config.FindToolchain(ctx.Os(), ctx.Arch()) + + platformSdkVersion := "" + if ctx.Device() { + platformSdkVersion = ctx.Config().PlatformSdkVersion().String() + } + minSdkVersion := cc.MinSdkVersion(ctx.RustModule(), cc.CtxIsForPlatform(ctx), ctx.Device(), platformSdkVersion) + + // Collect common CC compilation flags + ccFlags = cc.CommonLinkerFlags(ctx, ccFlags, true, toolchain, false) + ccFlags = cc.CommonLibraryLinkerFlags(ctx, ccFlags, toolchain, library.getStem(ctx)) + ccFlags = cc.AddStubLibraryCompilerFlags(ccFlags) + ccFlags = cc.AddTargetFlags(ctx, ccFlags, toolchain, minSdkVersion, false) + + return ccFlags +} + +func (library *libraryDecorator) compileModuleLibApiStubs(ctx ModuleContext, ccFlags cc.Flags) cc.Objects { + mod := ctx.RustModule() + + symbolFile := String(library.Properties.Stubs.Symbol_file) + library.stubsSymbolFilePath = android.PathForModuleSrc(ctx, symbolFile) + + apiParams := cc.ApiStubsParams{ + NotInPlatform: mod.NotInPlatform(), + IsNdk: mod.IsNdk(ctx.Config()), + BaseModuleName: mod.BaseModuleName(), + ModuleName: ctx.ModuleName(), + } + flag := cc.GetApiStubsFlags(apiParams) + + nativeAbiResult := cc.ParseNativeAbiDefinition(ctx, symbolFile, + android.ApiLevelOrPanic(ctx, library.MutatedProperties.StubsVersion), flag) + objs := cc.CompileStubLibrary(ctx, ccFlags, nativeAbiResult.StubSrc, mod.getSharedFlags()) + + library.versionScriptPath = android.OptionalPathForPath(nativeAbiResult.VersionScript) + + // Parse symbol file to get API list for coverage + if library.StubsVersion() == "current" && ctx.PrimaryArch() && !mod.InRecovery() && !mod.InProduct() && !mod.InVendor() { + library.apiListCoverageXmlPath = cc.ParseSymbolFileForAPICoverage(ctx, symbolFile) + } + + return objs +} + func (library *libraryDecorator) rustdoc(ctx ModuleContext, flags Flags, deps PathDeps) android.OptionalPath { // rustdoc has builtin support for documenting config specific information diff --git a/rust/library_test.go b/rust/library_test.go index e5fd5e018..1198fcc9f 100644 --- a/rust/library_test.go +++ b/rust/library_test.go @@ -482,3 +482,289 @@ func TestRustVersionScriptPropertyErrors(t *testing.T) { extra_exported_symbols: "libbar.map.txt", }`) } + +func TestStubsVersions(t *testing.T) { + t.Parallel() + bp := ` + rust_ffi { + name: "libfoo", + crate_name: "foo", + srcs: ["foo.rs"], + stubs: { + versions: ["29", "R", "current"], + }, + } + ` + ctx := android.GroupFixturePreparers( + prepareForRustTest, + android.PrepareForTestWithVisibility, + rustMockedFiles.AddToFixture(), + android.FixtureModifyConfigAndContext(func(config android.Config, ctx *android.TestContext) { + config.TestProductVariables.Platform_version_active_codenames = []string{"R"} + })).RunTestWithBp(t, bp) + + variants := ctx.ModuleVariantsForTests("libfoo") + for _, expectedVer := range []string{"29", "R", "current"} { + expectedVariant := "android_arm_armv7-a-neon_shared_" + expectedVer + if !android.InList(expectedVariant, variants) { + t.Errorf("missing expected variant: %q", expectedVariant) + } + } +} + +func TestStubsVersions_NotSorted(t *testing.T) { + t.Parallel() + bp := ` + rust_ffi_shared { + name: "libfoo", + crate_name: "foo", + srcs: ["foo.rs"], + stubs: { + versions: ["29", "current", "R"], + }, + } + ` + fixture := android.GroupFixturePreparers( + prepareForRustTest, + android.PrepareForTestWithVisibility, + rustMockedFiles.AddToFixture(), + + android.FixtureModifyConfigAndContext(func(config android.Config, ctx *android.TestContext) { + config.TestProductVariables.Platform_version_active_codenames = []string{"R"} + })) + + fixture.ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(`"libfoo" .*: versions: not sorted`)).RunTestWithBp(t, bp) +} + +func TestStubsVersions_ParseError(t *testing.T) { + t.Parallel() + bp := ` + rust_ffi_shared { + name: "libfoo", + crate_name: "foo", + srcs: ["foo.rs"], + stubs: { + versions: ["29", "current", "X"], + }, + } + ` + fixture := android.GroupFixturePreparers( + prepareForRustTest, + android.PrepareForTestWithVisibility, + rustMockedFiles.AddToFixture(), + + android.FixtureModifyConfigAndContext(func(config android.Config, ctx *android.TestContext) { + config.TestProductVariables.Platform_version_active_codenames = []string{"R"} + })) + + fixture.ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(`"libfoo" .*: versions: "X" could not be parsed as an integer and is not a recognized codename`)).RunTestWithBp(t, bp) +} + +func TestVersionedStubs(t *testing.T) { + t.Parallel() + bp := ` + rust_ffi_shared { + name: "libFoo", + crate_name: "Foo", + srcs: ["foo.rs"], + stubs: { + symbol_file: "foo.map.txt", + versions: ["1", "2", "3"], + }, + } + + cc_library_shared { + name: "libBar", + srcs: ["bar.c"], + shared_libs: ["libFoo#1"], + } + + rust_library { + name: "libbar_rs", + crate_name: "bar_rs", + srcs: ["bar.rs"], + shared_libs: ["libFoo#1"], + } + rust_ffi { + name: "libbar_ffi_rs", + crate_name: "bar_ffi_rs", + srcs: ["bar.rs"], + shared_libs: ["libFoo#1"], + } + ` + + ctx := android.GroupFixturePreparers( + prepareForRustTest, + android.PrepareForTestWithVisibility, + rustMockedFiles.AddToFixture()).RunTestWithBp(t, bp) + + variants := ctx.ModuleVariantsForTests("libFoo") + expectedVariants := []string{ + "android_arm64_armv8-a_shared", + "android_arm64_armv8-a_shared_1", + "android_arm64_armv8-a_shared_2", + "android_arm64_armv8-a_shared_3", + "android_arm64_armv8-a_shared_current", + "android_arm_armv7-a-neon_shared", + "android_arm_armv7-a-neon_shared_1", + "android_arm_armv7-a-neon_shared_2", + "android_arm_armv7-a-neon_shared_3", + "android_arm_armv7-a-neon_shared_current", + } + variantsMismatch := false + if len(variants) != len(expectedVariants) { + variantsMismatch = true + } else { + for _, v := range expectedVariants { + if !android.InList(v, variants) { + variantsMismatch = false + } + } + } + if variantsMismatch { + t.Errorf("variants of libFoo expected:\n") + for _, v := range expectedVariants { + t.Errorf("%q\n", v) + } + t.Errorf(", but got:\n") + for _, v := range variants { + t.Errorf("%q\n", v) + } + } + + libBarLinkRule := ctx.ModuleForTests("libBar", "android_arm64_armv8-a_shared").Rule("ld") + libBarFlags := libBarLinkRule.Args["libFlags"] + + libBarRsRustcRule := ctx.ModuleForTests("libbar_rs", "android_arm64_armv8-a_dylib").Rule("rustc") + libBarRsFlags := libBarRsRustcRule.Args["linkFlags"] + + libBarFfiRsRustcRule := ctx.ModuleForTests("libbar_ffi_rs", "android_arm64_armv8-a_shared").Rule("rustc") + libBarFfiRsFlags := libBarFfiRsRustcRule.Args["linkFlags"] + + libFoo1StubPath := "libFoo/android_arm64_armv8-a_shared_1/unstripped/libFoo.so" + if !strings.Contains(libBarFlags, libFoo1StubPath) { + t.Errorf("%q is not found in %q", libFoo1StubPath, libBarFlags) + } + if !strings.Contains(libBarRsFlags, libFoo1StubPath) { + t.Errorf("%q is not found in %q", libFoo1StubPath, libBarRsFlags) + } + if !strings.Contains(libBarFfiRsFlags, libFoo1StubPath) { + t.Errorf("%q is not found in %q", libFoo1StubPath, libBarFfiRsFlags) + } +} + +func TestCheckConflictingExplicitVersions(t *testing.T) { + t.Parallel() + bp := ` + cc_library_shared { + name: "libbar", + srcs: ["bar.c"], + shared_libs: ["libfoo", "libfoo#impl"], + } + + rust_ffi_shared { + name: "libfoo", + crate_name: "foo", + srcs: ["foo.rs"], + stubs: { + versions: ["29", "current"], + }, + } + ` + fixture := android.GroupFixturePreparers( + prepareForRustTest, + android.PrepareForTestWithVisibility, + rustMockedFiles.AddToFixture()) + + fixture.ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(`duplicate shared libraries with different explicit versions`)).RunTestWithBp(t, bp) +} + +func TestAddnoOverride64GlobalCflags(t *testing.T) { + t.Parallel() + bp := ` + cc_library_shared { + name: "libclient", + srcs: ["foo.c"], + shared_libs: ["libfoo#1"], + } + + rust_ffi_shared { + name: "libfoo", + crate_name: "foo", + srcs: ["foo.c"], + shared_libs: ["libbar"], + stubs: { + symbol_file: "foo.map.txt", + versions: ["1", "2", "3"], + }, + } + + cc_library_shared { + name: "libbar", + export_include_dirs: ["include/libbar"], + srcs: ["foo.c"], + }` + ctx := android.GroupFixturePreparers( + prepareForRustTest, + android.PrepareForTestWithVisibility, + rustMockedFiles.AddToFixture()).RunTestWithBp(t, bp) + + cFlags := ctx.ModuleForTests("libclient", "android_arm64_armv8-a_shared").Rule("cc").Args["cFlags"] + + if !strings.Contains(cFlags, "${config.NoOverride64GlobalCflags}") { + t.Errorf("expected %q in cflags, got %q", "${config.NoOverride64GlobalCflags}", cFlags) + } +} + +// Make sure the stubs properties can only be used in modules producing shared libs +func TestRustStubsFFIOnly(t *testing.T) { + testRustError(t, "stubs properties", ` + rust_library { + name: "libfoo", + crate_name: "foo", + srcs: ["foo.c"], + shared_libs: ["libbar"], + stubs: { + symbol_file: "foo.map.txt", + }, + } + `) + + testRustError(t, "stubs properties", ` + rust_library { + name: "libfoo", + crate_name: "foo", + srcs: ["foo.c"], + shared_libs: ["libbar"], + stubs: { + versions: ["1"], + }, + } + `) + + testRustError(t, "stubs properties", ` + rust_ffi_static { + name: "libfoo", + crate_name: "foo", + srcs: ["foo.c"], + shared_libs: ["libbar"], + stubs: { + symbol_file: "foo.map.txt", + }, + } + `) + testRustError(t, "stubs properties", ` + rust_ffi_static { + name: "libfoo", + crate_name: "foo", + srcs: ["foo.c"], + shared_libs: ["libbar"], + stubs: { + versions: ["1"], + }, + } + `) +} + +// TODO: When rust_ffi libraries support export_*_lib_headers, +// add a test similar to cc.TestStubsLibReexportsHeaders diff --git a/rust/rust.go b/rust/rust.go index 2332b52ec..557bdc31d 100644 --- a/rust/rust.go +++ b/rust/rust.go @@ -162,9 +162,36 @@ type BaseProperties struct { // Make this module available when building for recovery Recovery_available *bool - // Minimum sdk version that the artifact should support when it runs as part of mainline modules(APEX). + // The API level that this module is built against. The APIs of this API level will be + // visible at build time, but use of any APIs newer than min_sdk_version will render the + // module unloadable on older devices. In the future it will be possible to weakly-link new + // APIs, making the behavior match Java: such modules will load on older devices, but + // calling new APIs on devices that do not support them will result in a crash. + // + // This property has the same behavior as sdk_version does for Java modules. For those + // familiar with Android Gradle, the property behaves similarly to how compileSdkVersion + // does for Java code. + // + // In addition, setting this property causes two variants to be built, one for the platform + // and one for apps. + Sdk_version *string + + // Minimum OS API level supported by this C or C++ module. This property becomes the value + // of the __ANDROID_API__ macro. When the C or C++ module is included in an APEX or an APK, + // this property is also used to ensure that the min_sdk_version of the containing module is + // not older (i.e. less) than this module's min_sdk_version. When not set, this property + // defaults to the value of sdk_version. When this is set to "apex_inherit", this tracks + // min_sdk_version of the containing APEX. When the module + // is not built for an APEX, "apex_inherit" defaults to sdk_version. Min_sdk_version *string + // Variant is an SDK variant created by sdkMutator + IsSdkVariant bool `blueprint:"mutated"` + + // Set by factories of module types that can only be referenced from variants compiled against + // the SDK. + AlwaysSdk bool `blueprint:"mutated"` + HideFromMake bool `blueprint:"mutated"` PreventInstall bool `blueprint:"mutated"` @@ -209,6 +236,9 @@ type Module struct { apexSdkVersion android.ApiLevel transitiveAndroidMkSharedLibs depset.DepSet[string] + + // Shared flags among stubs build rules of this module + sharedFlags cc.SharedFlags } func (mod *Module) Header() bool { @@ -374,7 +404,8 @@ func (mod *Module) IsVndkPrebuiltLibrary() bool { } func (mod *Module) IsVendorPublicLibrary() bool { - return mod.VendorProperties.IsVendorPublicLibrary + // Rust modules do not currently support vendor_public_library + return false } func (mod *Module) SdkAndPlatformVariantVisibleToMake() bool { @@ -383,10 +414,12 @@ func (mod *Module) SdkAndPlatformVariantVisibleToMake() bool { } func (c *Module) IsVndkPrivate() bool { + // Rust modules do not currently support VNDK variants return false } func (c *Module) IsLlndk() bool { + // Rust modules do not currently support LLNDK variants return false } @@ -395,31 +428,34 @@ func (mod *Module) KernelHeadersDecorator() bool { } func (m *Module) NeedsLlndkVariants() bool { + // Rust modules do not currently support LLNDK variants return false } func (m *Module) NeedsVendorPublicLibraryVariants() bool { + // Rust modules do not currently support vendor_public_library return false } func (mod *Module) HasLlndkStubs() bool { + // Rust modules do not currently support LLNDK stubs return false } func (mod *Module) SdkVersion() string { - return "" + return String(mod.Properties.Sdk_version) } func (mod *Module) AlwaysSdk() bool { - return false + return mod.Properties.AlwaysSdk } func (mod *Module) IsSdkVariant() bool { - return false + return mod.Properties.IsSdkVariant } func (mod *Module) SplitPerApiLevel() bool { - return false + return cc.CanUseSdk(mod) && mod.IsCrt() } func (mod *Module) XrefRustFiles() android.Paths { @@ -743,19 +779,42 @@ func (mod *Module) IsNdk(config android.Config) bool { } func (mod *Module) IsStubs() bool { + if lib, ok := mod.compiler.(libraryInterface); ok { + return lib.BuildStubs() + } return false } func (mod *Module) HasStubsVariants() bool { + if lib, ok := mod.compiler.(libraryInterface); ok { + return lib.HasStubsVariants() + } return false } func (mod *Module) ApexSdkVersion() android.ApiLevel { - panic(fmt.Errorf("ApexSdkVersion called on unsupported Rust module: %q", mod.BaseModuleName())) + return mod.apexSdkVersion +} + +func (mod *Module) RustApexExclude() bool { + return mod.ApexExclude() +} + +func (mod *Module) getSharedFlags() *cc.SharedFlags { + shared := &mod.sharedFlags + if shared.FlagsMap == nil { + shared.NumSharedFlags = 0 + shared.FlagsMap = make(map[string]string) + } + return shared } func (mod *Module) ImplementationModuleNameForMake(ctx android.BaseModuleContext) string { - return mod.Name() + name := mod.BaseModuleName() + if versioned, ok := mod.compiler.(cc.VersionedInterface); ok { + name = versioned.ImplementationModuleName(name) + } + return name } func (mod *Module) Multilib() string { @@ -903,7 +962,7 @@ func (mod *Module) SetStl(s string) { } func (mod *Module) SetSdkVersion(s string) { - panic(fmt.Errorf("SetSdkVersion called on unsupported Rust module: %q", mod.BaseModuleName())) + mod.Properties.Sdk_version = StringPtr(s) } func (mod *Module) SetMinSdkVersion(s string) { @@ -1066,6 +1125,10 @@ func (mod *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { linkableInfo.Shared = mod.Shared() linkableInfo.CrateName = mod.CrateName() linkableInfo.ExportedCrateLinkDirs = mod.ExportedCrateLinkDirs() + if lib, ok := mod.compiler.(cc.VersionedInterface); ok { + linkableInfo.StubsVersion = lib.StubsVersion() + } + android.SetProvider(ctx, cc.LinkableInfoProvider, linkableInfo) rustInfo := &RustInfo{ @@ -1108,10 +1171,9 @@ func (mod *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { // does compilation and linking in one step. If this changes in the future, // move this as appropriate. ccInfo.LinkerInfo = &cc.LinkerInfo{ - WholeStaticLibs: mod.compiler.baseCompilerProps().Whole_static_libs, - StaticLibs: mod.compiler.baseCompilerProps().Static_libs, - SharedLibs: mod.compiler.baseCompilerProps().Shared_libs, - UnstrippedOutputFile: mod.UnstrippedOutputFile(), + WholeStaticLibs: mod.compiler.baseCompilerProps().Whole_static_libs, + StaticLibs: mod.compiler.baseCompilerProps().Static_libs, + SharedLibs: mod.compiler.baseCompilerProps().Shared_libs, } android.SetProvider(ctx, cc.CcInfoProvider, ccInfo) @@ -1277,6 +1339,21 @@ func (mod *Module) begin(ctx BaseModuleContext) { if mod.sanitize != nil { mod.sanitize.begin(ctx) } + + if mod.UseSdk() && mod.IsSdkVariant() { + sdkVersion := "" + if ctx.Device() { + sdkVersion = mod.SdkVersion() + } + version, err := cc.NativeApiLevelFromUser(ctx, sdkVersion) + if err != nil { + ctx.PropertyErrorf("sdk_version", err.Error()) + mod.Properties.Sdk_version = nil + } else { + mod.Properties.Sdk_version = StringPtr(version.String()) + } + } + } func (mod *Module) Prebuilt() *android.Prebuilt { @@ -1552,9 +1629,12 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps { sharedLibraryInfo, exportedInfo := cc.ChooseStubOrImpl(ctx, dep) if !sharedLibraryInfo.IsStubs { - depPaths.directImplementationDeps = append(depPaths.directImplementationDeps, android.OutputFileForModule(ctx, dep, "")) - if info, ok := android.OtherModuleProvider(ctx, dep, cc.ImplementationDepInfoProvider); ok { - depPaths.transitiveImplementationDeps = append(depPaths.transitiveImplementationDeps, info.ImplementationDeps) + // TODO(b/362509506): remove this additional check once all apex_exclude uses are switched to stubs. + if !linkableInfo.RustApexExclude { + depPaths.directImplementationDeps = append(depPaths.directImplementationDeps, android.OutputFileForModule(ctx, dep, "")) + if info, ok := android.OtherModuleProvider(ctx, dep, cc.ImplementationDepInfoProvider); ok { + depPaths.transitiveImplementationDeps = append(depPaths.transitiveImplementationDeps, info.ImplementationDeps) + } } } @@ -1922,7 +2002,7 @@ func (mod *Module) HostToolPath() android.OptionalPath { var _ android.ApexModule = (*Module)(nil) // If a module is marked for exclusion from apexes, don't provide apex variants. -// TODO(b/362509506): remove this once stubs are properly supported by rust_ffi targets. +// TODO(b/362509506): remove this once all apex_exclude usages are removed. func (m *Module) CanHaveApexVariants() bool { if m.ApexExclude() { return false @@ -1959,6 +2039,13 @@ func (mod *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVer } // Implements android.ApexModule +func (mod *Module) AlwaysRequiresPlatformApexVariant() bool { + // stub libraries and native bridge libraries are always available to platform + // TODO(b/362509506): remove the ApexExclude() check once all apex_exclude uses are switched to stubs. + return mod.IsStubs() || mod.Target().NativeBridge == android.NativeBridgeEnabled || mod.ApexExclude() +} + +// Implements android.ApexModule func (mod *Module) OutgoingDepIsInSameApex(depTag blueprint.DependencyTag) bool { if depTag == procMacroDepTag || depTag == customBindgenDepTag { return false @@ -1971,18 +2058,55 @@ func (mod *Module) OutgoingDepIsInSameApex(depTag blueprint.DependencyTag) bool return false } + if depTag == cc.StubImplDepTag { + // We don't track from an implementation library to its stubs. + return false + } + + if cc.ExcludeInApexDepTag(depTag) { + return false + } + + // TODO(b/362509506): remove once all apex_exclude uses are switched to stubs. + if mod.ApexExclude() { + return false + } + return true } func (mod *Module) IncomingDepIsInSameApex(depTag blueprint.DependencyTag) bool { - return !mod.ApexExclude() + // TODO(b/362509506): remove once all apex_exclude uses are switched to stubs. + if mod.ApexExclude() { + return false + } + + if mod.HasStubsVariants() { + if cc.IsSharedDepTag(depTag) { + // dynamic dep to a stubs lib crosses APEX boundary + return false + } + if cc.IsRuntimeDepTag(depTag) { + // runtime dep to a stubs lib also crosses APEX boundary + return false + } + if cc.IsHeaderDepTag(depTag) { + return false + } + } + return true } // Overrides ApexModule.IsInstallabeToApex() func (mod *Module) IsInstallableToApex() bool { + // TODO(b/362509506): remove once all apex_exclude uses are switched to stubs. + if mod.ApexExclude() { + return false + } + if mod.compiler != nil { - if lib, ok := mod.compiler.(libraryInterface); ok && (lib.shared() || lib.dylib()) { - return true + if lib, ok := mod.compiler.(libraryInterface); ok { + return (lib.shared() || lib.dylib()) && !lib.BuildStubs() } if _, ok := mod.compiler.(*binaryDecorator); ok { return true diff --git a/rust/testing.go b/rust/testing.go index 0ce1b66f5..2082b524e 100644 --- a/rust/testing.go +++ b/rust/testing.go @@ -80,7 +80,6 @@ func GatherRequiredDepsForTest() string { no_libcrt: true, nocrt: true, system_shared_libs: [], - apex_available: ["//apex_available:platform", "//apex_available:anyapex"], min_sdk_version: "29", vendor_available: true, host_supported: true, @@ -88,6 +87,13 @@ func GatherRequiredDepsForTest() string { llndk: { symbol_file: "liblog.map.txt", }, + stubs: { + symbol_file: "liblog.map.txt", + versions: [ + "29", + "30", + ], + }, } cc_library { name: "libprotobuf-cpp-full", |