rust: Add stub support for rust_ffi modules
This adds stubs support for rust_ffi and rust_ffi_shared modules. Usage
should match current cc usage. The stubs generator leveraged is the cc
stubs generator.
Bug: 203478530
Test: m blueprint_tests
Change-Id: I043b9714a357cd5fe17c183ccdf86900f5172e0e
diff --git a/rust/library.go b/rust/library.go
index 9912c94..49169ac 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -25,6 +25,7 @@
"android/soong/android"
"android/soong/cc"
+ cc_config "android/soong/cc/config"
)
var (
@@ -79,9 +80,13 @@
// 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 @@
// 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 @@
// 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 @@
BuildOnlyShared()
toc() android.OptionalPath
+
+ IsStubsImplementationRequired() bool
+ setAPIListCoverageXMLPath(out android.ModuleOutPath)
+
+ libraryProperties() LibraryCompilerProperties
}
func (library *libraryDecorator) nativeCoverage() bool {
@@ -276,58 +314,63 @@
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 @@
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 @@
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 @@
module.compiler = library
+ module.SetDefaultableHook(CheckRustLibraryProperties)
return module, library
}
@@ -641,7 +705,11 @@
}
// 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 @@
}
// 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 @@
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 @@
TransitiveStaticLibrariesForOrdering: depSet,
})
}
+ cc.AddStubDependencyProviders(ctx)
library.flagExporter.setProvider(ctx)
@@ -707,6 +774,53 @@
}
}
+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 e5fd5e0..1198fcc 100644
--- a/rust/library_test.go
+++ b/rust/library_test.go
@@ -482,3 +482,289 @@
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 2332b52..557bdc3 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -162,9 +162,36 @@
// 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 @@
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) 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 (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 (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) 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) 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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
}
// 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 @@
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 0ce1b66..2082b52 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -80,7 +80,6 @@
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 @@
llndk: {
symbol_file: "liblog.map.txt",
},
+ stubs: {
+ symbol_file: "liblog.map.txt",
+ versions: [
+ "29",
+ "30",
+ ],
+ },
}
cc_library {
name: "libprotobuf-cpp-full",