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/cc/builder.go b/cc/builder.go
index 56b7139..5325116 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -851,6 +851,25 @@
 	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,
diff --git a/cc/cc.go b/cc/cc.go
index 0db5c40..57b5e3c 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -160,6 +160,8 @@
 	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 @@
 	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 @@
 	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 @@
 		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 @@
 				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 1899f59..3730bbe 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -360,7 +360,7 @@
 	}
 }
 
-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 @@
 	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 4e058bd..c8ff82b 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -354,7 +354,7 @@
 	}
 }
 
-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 7b225ec..950ea91 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -460,7 +460,7 @@
 	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 @@
 // 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 @@
 		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 @@
 		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 @@
 
 // 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 @@
 		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 @@
 
 	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 d0fda64..337b459 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -84,6 +84,10 @@
 	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 20c2f4a..f854937 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -471,7 +471,7 @@
 
 // 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 @@
 
 	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 @@
 	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 4321beb..1f0fc07 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -236,7 +236,7 @@
 	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 (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 @@
 		}
 	}
 	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 2291153..9d7b5bc 100644
--- a/cc/stub_library.go
+++ b/cc/stub_library.go
@@ -59,10 +59,6 @@
 	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