Pārlūkot izejas kodu

Introduce cc_api_library

Introduce cc_api_library, which reflects imported API from other
inner-tree. This cc_api_library module type will later refer from
the other ninja module which generates stub library from the interface
description.

Tested environment :
* original libc definition has been removed temporarily, to ensure that
  imported api stub library is being used from build
* Added new definition of libc as below
 cc_api_library {
  name: "libc",
  arch: {
    x86: {
      src: "libs/x86/libc.so",
    },
    x86_64: {
      src: "libs/x86_64/libc.so",
    },
  },
  header_libs: [
    "libc_headers",
  ],
  export_header_lib_headers: ["libc_headers"],
  min_sdk_version: "9",
  vendor_available: true,
 }

Bug: 236087698
Test: `ALLOW_MISSING_DEPENDENCIES=true m vendorimage` succeeded
Change-Id: I67070b0f3561aa2afd73b6c1c0fdf4255218baac
Kiyoung Kim 1 gadu atpakaļ
vecāks
revīzija
487689eaee
8 mainītis faili ar 447 papildinājumiem un 32 dzēšanām
  1. 58 0
      apex/apex_test.go
  2. 88 25
      cc/cc.go
  3. 87 0
      cc/library_stub.go
  4. 110 0
      cc/library_stub_test.go
  5. 3 0
      cc/testing.go
  6. 1 0
      multitree/Android.bp
  7. 88 0
      multitree/api_imports.go
  8. 12 7
      rust/rust.go

+ 58 - 0
apex/apex_test.go

@@ -26,6 +26,7 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
@@ -9546,6 +9547,63 @@ func TestUpdatableApexEnforcesAppUpdatability(t *testing.T) {
 	}
 }
 
+func TestApexBuildsAgainstApiSurfaceStubLibraries(t *testing.T) {
+	bp := `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			native_shared_libs: ["libfoo"],
+			min_sdk_version: "29",
+		}
+		apex_key {
+			name: "myapex.key",
+		}
+		cc_library {
+			name: "libfoo",
+			shared_libs: ["libc"],
+			apex_available: ["myapex"],
+			min_sdk_version: "29",
+		}
+		cc_api_library {
+			name: "libc",
+			src: "libc.so",
+			min_sdk_version: "29",
+			recovery_available: true,
+		}
+		api_imports {
+			name: "api_imports",
+			shared_libs: [
+				"libc",
+			],
+			header_libs: [],
+		}
+		`
+	result := testApex(t, bp)
+
+	hasDep := func(m android.Module, wantDep android.Module) bool {
+		t.Helper()
+		var found bool
+		result.VisitDirectDeps(m, func(dep blueprint.Module) {
+			if dep == wantDep {
+				found = true
+			}
+		})
+		return found
+	}
+
+	libfooApexVariant := result.ModuleForTests("libfoo", "android_arm64_armv8-a_shared_apex29").Module()
+	libcApexVariant := result.ModuleForTests("libc.apiimport", "android_arm64_armv8-a_shared_apex29").Module()
+
+	android.AssertBoolEquals(t, "apex variant should link against API surface stub libraries", true, hasDep(libfooApexVariant, libcApexVariant))
+
+	// libfoo core variant should be buildable in the same inner tree since
+	// certain mcombo files might build system and apexes in the same inner tree
+	// libfoo core variant should link against source libc
+	libfooCoreVariant := result.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
+	libcCoreVariant := result.ModuleForTests("libc.apiimport", "android_arm64_armv8-a_shared").Module()
+	android.AssertBoolEquals(t, "core variant should link against source libc", true, hasDep(libfooCoreVariant, libcCoreVariant))
+}
+
 func TestMain(m *testing.M) {
 	os.Exit(m.Run())
 }

+ 88 - 25
cc/cc.go

@@ -31,6 +31,7 @@ import (
 	"android/soong/cc/config"
 	"android/soong/fuzz"
 	"android/soong/genrule"
+	"android/soong/multitree"
 	"android/soong/snapshot"
 )
 
@@ -2184,6 +2185,24 @@ func AddSharedLibDependenciesWithVersions(ctx android.BottomUpMutatorContext, mo
 	}
 }
 
+func GetApiImports(c LinkableInterface, actx android.BottomUpMutatorContext) multitree.ApiImportInfo {
+	apiImportInfo := multitree.ApiImportInfo{}
+
+	if c.Device() {
+		var apiImportModule []blueprint.Module
+		if actx.OtherModuleExists("api_imports") {
+			apiImportModule = actx.AddDependency(c, nil, "api_imports")
+			if len(apiImportModule) > 0 && apiImportModule[0] != nil {
+				apiInfo := actx.OtherModuleProvider(apiImportModule[0], multitree.ApiImportsProvider).(multitree.ApiImportInfo)
+				apiImportInfo = apiInfo
+				actx.SetProvider(multitree.ApiImportsProvider, apiInfo)
+			}
+		}
+	}
+
+	return apiImportInfo
+}
+
 func GetSnapshot(c LinkableInterface, snapshotInfo **SnapshotInfo, actx android.BottomUpMutatorContext) SnapshotInfo {
 	// Only device modules with BOARD_VNDK_VERSION uses snapshot.  Others use the zero value of
 	// SnapshotInfo, which provides no mappings.
@@ -2209,8 +2228,8 @@ func GetSnapshot(c LinkableInterface, snapshotInfo **SnapshotInfo, actx android.
 	return **snapshotInfo
 }
 
-func RewriteSnapshotLib(lib string, snapshotMap map[string]string) string {
-	if snapshot, ok := snapshotMap[lib]; ok {
+func GetReplaceModuleName(lib string, replaceMap map[string]string) string {
+	if snapshot, ok := replaceMap[lib]; ok {
 		return snapshot
 	}
 
@@ -2221,13 +2240,18 @@ func RewriteSnapshotLib(lib string, snapshotMap map[string]string) string {
 // of names:
 //
 // 1. Name of an NDK library that refers to a prebuilt module.
-//    For each of these, it adds the name of the prebuilt module (which will be in
-//    prebuilts/ndk) to the list of nonvariant libs.
+//
+//	For each of these, it adds the name of the prebuilt module (which will be in
+//	prebuilts/ndk) to the list of nonvariant libs.
+//
 // 2. Name of an NDK library that refers to an ndk_library module.
-//    For each of these, it adds the name of the ndk_library module to the list of
-//    variant libs.
+//
+//	For each of these, it adds the name of the ndk_library module to the list of
+//	variant libs.
+//
 // 3. Anything else (so anything that isn't an NDK library).
-//    It adds these to the nonvariantLibs list.
+//
+//	It adds these to the nonvariantLibs list.
 //
 // The caller can then know to add the variantLibs dependencies differently from the
 // nonvariantLibs
@@ -2239,11 +2263,11 @@ func RewriteLibs(c LinkableInterface, snapshotInfo **SnapshotInfo, actx android.
 		// strip #version suffix out
 		name, _ := StubsLibNameAndVersion(entry)
 		if c.InRecovery() {
-			nonvariantLibs = append(nonvariantLibs, RewriteSnapshotLib(entry, GetSnapshot(c, snapshotInfo, actx).SharedLibs))
+			nonvariantLibs = append(nonvariantLibs, GetReplaceModuleName(entry, GetSnapshot(c, snapshotInfo, actx).SharedLibs))
 		} else if c.UseSdk() && inList(name, *getNDKKnownLibs(config)) {
 			variantLibs = append(variantLibs, name+ndkLibrarySuffix)
 		} else if c.UseVndk() {
-			nonvariantLibs = append(nonvariantLibs, RewriteSnapshotLib(entry, GetSnapshot(c, snapshotInfo, actx).SharedLibs))
+			nonvariantLibs = append(nonvariantLibs, GetReplaceModuleName(entry, GetSnapshot(c, snapshotInfo, actx).SharedLibs))
 		} else {
 			// put name#version back
 			nonvariantLibs = append(nonvariantLibs, entry)
@@ -2252,6 +2276,25 @@ func RewriteLibs(c LinkableInterface, snapshotInfo **SnapshotInfo, actx android.
 	return nonvariantLibs, variantLibs
 }
 
+func updateDepsWithApiImports(deps Deps, apiImports multitree.ApiImportInfo) Deps {
+	for idx, lib := range deps.SharedLibs {
+		deps.SharedLibs[idx] = GetReplaceModuleName(lib, apiImports.SharedLibs)
+	}
+
+	for idx, lib := range deps.LateSharedLibs {
+		deps.LateSharedLibs[idx] = GetReplaceModuleName(lib, apiImports.SharedLibs)
+	}
+
+	for idx, lib := range deps.RuntimeLibs {
+		deps.RuntimeLibs[idx] = GetReplaceModuleName(lib, apiImports.SharedLibs)
+	}
+
+	for idx, lib := range deps.HeaderLibs {
+		deps.HeaderLibs[idx] = GetReplaceModuleName(lib, apiImports.HeaderLibs)
+	}
+	return deps
+}
+
 func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) {
 	if !c.Enabled() {
 		return
@@ -2267,6 +2310,9 @@ func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) {
 
 	deps := c.deps(ctx)
 
+	apiImportInfo := GetApiImports(c, actx)
+	deps = updateDepsWithApiImports(deps, apiImportInfo)
+
 	c.Properties.AndroidMkSystemSharedLibs = deps.SystemSharedLibs
 
 	var snapshotInfo *SnapshotInfo
@@ -2279,7 +2325,7 @@ func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) {
 		deps.ReexportSharedLibHeaders, _ = RewriteLibs(c, &snapshotInfo, actx, ctx.Config(), deps.ReexportSharedLibHeaders)
 
 		for idx, lib := range deps.RuntimeLibs {
-			deps.RuntimeLibs[idx] = RewriteSnapshotLib(lib, GetSnapshot(c, &snapshotInfo, actx).SharedLibs)
+			deps.RuntimeLibs[idx] = GetReplaceModuleName(lib, GetSnapshot(c, &snapshotInfo, actx).SharedLibs)
 		}
 	}
 
@@ -2289,7 +2335,7 @@ func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) {
 			depTag.reexportFlags = true
 		}
 
-		lib = RewriteSnapshotLib(lib, GetSnapshot(c, &snapshotInfo, actx).HeaderLibs)
+		lib = GetReplaceModuleName(lib, GetSnapshot(c, &snapshotInfo, actx).HeaderLibs)
 
 		if c.IsStubs() {
 			actx.AddFarVariationDependencies(append(ctx.Target().Variations(), c.ImageVariation()),
@@ -2302,10 +2348,20 @@ func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) {
 	if c.isNDKStubLibrary() {
 		// NDK stubs depend on their implementation because the ABI dumps are
 		// generated from the implementation library.
-		actx.AddFarVariationDependencies(append(ctx.Target().Variations(),
-			c.ImageVariation(),
-			blueprint.Variation{Mutator: "link", Variation: "shared"},
-		), stubImplementation, c.BaseModuleName())
+		apiImportName := c.BaseModuleName() + multitree.GetApiImportSuffix()
+
+		// If original library exists as imported API, set dependency on the imported library
+		if actx.OtherModuleExists(apiImportName) {
+			actx.AddFarVariationDependencies(append(ctx.Target().Variations(),
+				c.ImageVariation(),
+				blueprint.Variation{Mutator: "link", Variation: "shared"},
+			), stubImplementation, apiImportName)
+		} else {
+			actx.AddFarVariationDependencies(append(ctx.Target().Variations(),
+				c.ImageVariation(),
+				blueprint.Variation{Mutator: "link", Variation: "shared"},
+			), stubImplementation, c.BaseModuleName())
+		}
 	}
 
 	// sysprop_library has to support both C++ and Java. So sysprop_library internally creates one
@@ -2321,7 +2377,7 @@ func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) {
 			lib = impl
 		}
 
-		lib = RewriteSnapshotLib(lib, GetSnapshot(c, &snapshotInfo, actx).StaticLibs)
+		lib = GetReplaceModuleName(lib, GetSnapshot(c, &snapshotInfo, actx).StaticLibs)
 
 		actx.AddVariationDependencies([]blueprint.Variation{
 			{Mutator: "link", Variation: "static"},
@@ -2341,7 +2397,7 @@ func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) {
 			lib = impl
 		}
 
-		lib = RewriteSnapshotLib(lib, GetSnapshot(c, &snapshotInfo, actx).StaticLibs)
+		lib = GetReplaceModuleName(lib, GetSnapshot(c, &snapshotInfo, actx).StaticLibs)
 
 		actx.AddVariationDependencies([]blueprint.Variation{
 			{Mutator: "link", Variation: "static"},
@@ -2355,7 +2411,7 @@ func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) {
 		depTag := libraryDependencyTag{Kind: staticLibraryDependency, staticUnwinder: true}
 		actx.AddVariationDependencies([]blueprint.Variation{
 			{Mutator: "link", Variation: "static"},
-		}, depTag, RewriteSnapshotLib(staticUnwinder(actx), GetSnapshot(c, &snapshotInfo, actx).StaticLibs))
+		}, depTag, GetReplaceModuleName(staticUnwinder(actx), GetSnapshot(c, &snapshotInfo, actx).StaticLibs))
 	}
 
 	// shared lib names without the #version suffix
@@ -2387,14 +2443,14 @@ func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) {
 		depTag := libraryDependencyTag{Kind: staticLibraryDependency, Order: lateLibraryDependency}
 		actx.AddVariationDependencies([]blueprint.Variation{
 			{Mutator: "link", Variation: "static"},
-		}, depTag, RewriteSnapshotLib(lib, GetSnapshot(c, &snapshotInfo, actx).StaticLibs))
+		}, depTag, GetReplaceModuleName(lib, GetSnapshot(c, &snapshotInfo, actx).StaticLibs))
 	}
 
 	for _, lib := range deps.UnexportedStaticLibs {
 		depTag := libraryDependencyTag{Kind: staticLibraryDependency, Order: lateLibraryDependency, unexportedSymbols: true}
 		actx.AddVariationDependencies([]blueprint.Variation{
 			{Mutator: "link", Variation: "static"},
-		}, depTag, RewriteSnapshotLib(lib, GetSnapshot(c, &snapshotInfo, actx).StaticLibs))
+		}, depTag, GetReplaceModuleName(lib, GetSnapshot(c, &snapshotInfo, actx).StaticLibs))
 	}
 
 	for _, lib := range deps.LateSharedLibs {
@@ -2435,11 +2491,11 @@ func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) {
 	actx.AddVariationDependencies(crtVariations, objDepTag, deps.ObjFiles...)
 	for _, crt := range deps.CrtBegin {
 		actx.AddVariationDependencies(crtVariations, CrtBeginDepTag,
-			RewriteSnapshotLib(crt, GetSnapshot(c, &snapshotInfo, actx).Objects))
+			GetReplaceModuleName(crt, GetSnapshot(c, &snapshotInfo, actx).Objects))
 	}
 	for _, crt := range deps.CrtEnd {
 		actx.AddVariationDependencies(crtVariations, CrtEndDepTag,
-			RewriteSnapshotLib(crt, GetSnapshot(c, &snapshotInfo, actx).Objects))
+			GetReplaceModuleName(crt, GetSnapshot(c, &snapshotInfo, actx).Objects))
 	}
 	if deps.DynamicLinker != "" {
 		actx.AddDependency(c, dynamicLinkerDepTag, deps.DynamicLinker)
@@ -2464,7 +2520,7 @@ func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) {
 			actx.AddVariationDependencies([]blueprint.Variation{
 				c.ImageVariation(),
 				{Mutator: "link", Variation: "shared"},
-			}, vndkExtDepTag, RewriteSnapshotLib(vndkdep.getVndkExtendsModuleName(), GetSnapshot(c, &snapshotInfo, actx).SharedLibs))
+			}, vndkExtDepTag, GetReplaceModuleName(vndkdep.getVndkExtendsModuleName(), GetSnapshot(c, &snapshotInfo, actx).SharedLibs))
 		}
 	}
 }
@@ -3186,6 +3242,11 @@ func MakeLibName(ctx android.ModuleContext, c LinkableInterface, ccDep LinkableI
 
 			return baseName + snapshotPrebuilt.SnapshotAndroidMkSuffix()
 		}
+
+		// Remove API import suffix if exists
+		if _, ok := ccDepModule.linker.(*apiLibraryDecorator); ok {
+			libName = strings.TrimSuffix(libName, multitree.GetApiImportSuffix())
+		}
 	}
 
 	if ctx.DeviceConfig().VndkUseCoreVariant() && ccDep.IsVndk() && !ccDep.MustUseVendorVariant() &&
@@ -3521,6 +3582,10 @@ func (c *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
 	if _, ok := c.linker.(prebuiltLinkerInterface); ok {
 		return nil
 	}
+	if _, ok := c.linker.(*apiLibraryDecorator); ok {
+		return nil
+	}
+
 	minSdkVersion := c.MinSdkVersion()
 	if minSdkVersion == "apex_inherit" {
 		return nil
@@ -3638,9 +3703,7 @@ func (c *Module) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
 	}
 }
 
-//
 // Defaults
-//
 type Defaults struct {
 	android.ModuleBase
 	android.DefaultsModuleBase

+ 87 - 0
cc/library_stub.go

@@ -17,6 +17,7 @@ package cc
 import (
 	"android/soong/android"
 	"android/soong/multitree"
+	"strings"
 )
 
 func init() {
@@ -25,10 +26,96 @@ func init() {
 
 func RegisterLibraryStubBuildComponents(ctx android.RegistrationContext) {
 	// cc_api_stub_library shares a lot of ndk_library, and this will be refactored later
+	ctx.RegisterModuleType("cc_api_library", CcApiLibraryFactory)
 	ctx.RegisterModuleType("cc_api_stub_library", CcApiStubLibraryFactory)
 	ctx.RegisterModuleType("cc_api_contribution", CcApiContributionFactory)
 }
 
+// 'cc_api_library' is a module type which is from the exported API surface
+// with C shared library type. The module will replace original module, and
+// offer a link to the module that generates shared library object from the
+// map file.
+type apiLibraryProperties struct {
+	Src *string `android:"arch_variant"`
+}
+
+type apiLibraryDecorator struct {
+	*libraryDecorator
+	properties apiLibraryProperties
+}
+
+func CcApiLibraryFactory() android.Module {
+	module, decorator := NewLibrary(android.DeviceSupported)
+	apiLibraryDecorator := &apiLibraryDecorator{
+		libraryDecorator: decorator,
+	}
+	apiLibraryDecorator.BuildOnlyShared()
+
+	module.stl = nil
+	module.sanitize = nil
+	decorator.disableStripping()
+
+	module.compiler = nil
+	module.linker = apiLibraryDecorator
+	module.installer = nil
+	module.AddProperties(&module.Properties, &apiLibraryDecorator.properties)
+
+	// Mark module as stub, so APEX would not include this stub in the package.
+	module.library.setBuildStubs(true)
+
+	// Prevent default system libs (libc, libm, and libdl) from being linked
+	if apiLibraryDecorator.baseLinker.Properties.System_shared_libs == nil {
+		apiLibraryDecorator.baseLinker.Properties.System_shared_libs = []string{}
+	}
+
+	module.Init()
+
+	return module
+}
+
+func (d *apiLibraryDecorator) Name(basename string) string {
+	return basename + multitree.GetApiImportSuffix()
+}
+
+func (d *apiLibraryDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, objects Objects) android.Path {
+	// Flags reexported from dependencies. (e.g. vndk_prebuilt_shared)
+	d.libraryDecorator.flagExporter.exportIncludes(ctx)
+	d.libraryDecorator.reexportDirs(deps.ReexportedDirs...)
+	d.libraryDecorator.reexportSystemDirs(deps.ReexportedSystemDirs...)
+	d.libraryDecorator.reexportFlags(deps.ReexportedFlags...)
+	d.libraryDecorator.reexportDeps(deps.ReexportedDeps...)
+	d.libraryDecorator.addExportedGeneratedHeaders(deps.ReexportedGeneratedHeaders...)
+	d.libraryDecorator.flagExporter.setProvider(ctx)
+
+	in := android.PathForModuleSrc(ctx, *d.properties.Src)
+
+	d.unstrippedOutputFile = in
+	libName := d.libraryDecorator.getLibName(ctx) + flags.Toolchain.ShlibSuffix()
+
+	tocFile := android.PathForModuleOut(ctx, libName+".toc")
+	d.tocFile = android.OptionalPathForPath(tocFile)
+	TransformSharedObjectToToc(ctx, in, tocFile)
+
+	ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
+		SharedLibrary: in,
+		Target:        ctx.Target(),
+
+		TableOfContents: d.tocFile,
+	})
+
+	return in
+}
+
+func (d *apiLibraryDecorator) availableFor(what string) bool {
+	// Stub from API surface should be available for any APEX.
+	return true
+}
+
+func (d *apiLibraryDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
+	d.libraryDecorator.libName = strings.TrimSuffix(ctx.ModuleName(), multitree.GetApiImportSuffix())
+	return d.libraryDecorator.linkerFlags(ctx, flags)
+}
+
 func CcApiStubLibraryFactory() android.Module {
 	module, decorator := NewLibrary(android.DeviceSupported)
 	apiStubDecorator := &apiStubDecorator{

+ 110 - 0
cc/library_stub_test.go

@@ -22,6 +22,8 @@ import (
 
 	"android/soong/android"
 	"android/soong/multitree"
+
+	"github.com/google/blueprint"
 )
 
 func TestCcApiStubLibraryOutputFiles(t *testing.T) {
@@ -106,3 +108,111 @@ func TestApiSurfaceOutputs(t *testing.T) {
 	android.AssertStringEquals(t, "name", "foo.mysdk", api_surface_gen_rule_args["name"])
 	android.AssertStringEquals(t, "symbol_file", "foo.map.txt", api_surface_gen_rule_args["symbol_file"])*/
 }
+
+func hasDirectDependency(t *testing.T, ctx *android.TestResult, from android.Module, to android.Module) bool {
+	t.Helper()
+	var found bool
+	ctx.VisitDirectDeps(from, func(dep blueprint.Module) {
+		if dep == to {
+			found = true
+		}
+	})
+	return found
+}
+
+func TestApiLibraryReplacesExistingModule(t *testing.T) {
+	bp := `
+		cc_library {
+			name: "libfoo",
+			shared_libs: ["libbar"],
+		}
+
+		cc_library {
+			name: "libbar",
+		}
+
+		cc_api_library {
+			name: "libbar",
+			src: "libbar.so",
+		}
+
+		api_imports {
+			name: "api_imports",
+			shared_libs: [
+				"libbar",
+			],
+			header_libs: [],
+		}
+	`
+
+	ctx := prepareForCcTest.RunTestWithBp(t, bp)
+
+	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
+	libbar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module()
+	libbarApiImport := ctx.ModuleForTests("libbar.apiimport", "android_arm64_armv8-a_shared").Module()
+
+	android.AssertBoolEquals(t, "original library should not be linked", false, hasDirectDependency(t, ctx, libfoo, libbar))
+	android.AssertBoolEquals(t, "Stub library from API surface should be linked", true, hasDirectDependency(t, ctx, libfoo, libbarApiImport))
+}
+
+func TestApiLibraryDoNotRequireOriginalModule(t *testing.T) {
+	bp := `
+		cc_library {
+			name: "libfoo",
+			shared_libs: ["libbar"],
+		}
+
+		cc_api_library {
+			name: "libbar",
+			src: "libbar.so",
+		}
+
+		api_imports {
+			name: "api_imports",
+			shared_libs: [
+				"libbar",
+			],
+			header_libs: [],
+		}
+	`
+
+	ctx := prepareForCcTest.RunTestWithBp(t, bp)
+
+	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
+	libbarApiImport := ctx.ModuleForTests("libbar.apiimport", "android_arm64_armv8-a_shared").Module()
+
+	android.AssertBoolEquals(t, "Stub library from API surface should be linked", true, hasDirectDependency(t, ctx, libfoo, libbarApiImport))
+}
+
+func TestApiLibraryShouldNotReplaceWithoutApiImport(t *testing.T) {
+	bp := `
+		cc_library {
+			name: "libfoo",
+			shared_libs: ["libbar"],
+		}
+
+		cc_library {
+			name: "libbar",
+		}
+
+		cc_api_library {
+			name: "libbar",
+			src: "libbar.so",
+		}
+
+		api_imports {
+			name: "api_imports",
+			shared_libs: [],
+			header_libs: [],
+		}
+	`
+
+	ctx := prepareForCcTest.RunTestWithBp(t, bp)
+
+	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
+	libbar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module()
+	libbarApiImport := ctx.ModuleForTests("libbar.apiimport", "android_arm64_armv8-a_shared").Module()
+
+	android.AssertBoolEquals(t, "original library should be linked", true, hasDirectDependency(t, ctx, libfoo, libbar))
+	android.AssertBoolEquals(t, "Stub library from API surface should not be linked", false, hasDirectDependency(t, ctx, libfoo, libbarApiImport))
+}

+ 3 - 0
cc/testing.go

@@ -20,6 +20,7 @@ import (
 
 	"android/soong/android"
 	"android/soong/genrule"
+	"android/soong/multitree"
 	"android/soong/snapshot"
 )
 
@@ -31,6 +32,8 @@ func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
 	RegisterLibraryHeadersBuildComponents(ctx)
 	RegisterLibraryStubBuildComponents(ctx)
 
+	multitree.RegisterApiImportsModule(ctx)
+
 	ctx.RegisterModuleType("cc_benchmark", BenchmarkFactory)
 	ctx.RegisterModuleType("cc_object", ObjectFactory)
 	ctx.RegisterModuleType("cc_genrule", GenRuleFactory)

+ 1 - 0
multitree/Android.bp

@@ -10,6 +10,7 @@ bootstrap_go_package {
         "soong-android",
     ],
     srcs: [
+        "api_imports.go",
         "api_surface.go",
         "export.go",
         "metadata.go",

+ 88 - 0
multitree/api_imports.go

@@ -0,0 +1,88 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package multitree
+
+import (
+	"android/soong/android"
+
+	"github.com/google/blueprint"
+)
+
+var (
+	apiImportNameSuffix = ".apiimport"
+)
+
+func init() {
+	RegisterApiImportsModule(android.InitRegistrationContext)
+}
+
+func RegisterApiImportsModule(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("api_imports", apiImportsFactory)
+}
+
+type ApiImports struct {
+	android.ModuleBase
+	properties apiImportsProperties
+}
+
+type apiImportsProperties struct {
+	Shared_libs []string // List of C shared libraries from API surfaces
+	Header_libs []string // List of C header libraries from API surfaces
+}
+
+// 'api_imports' is a module which describes modules available from API surfaces.
+// This module is required to get the list of all imported API modules, because
+// it is discouraged to loop and fetch all modules from its type information. The
+// only module with name 'api_imports' will be used from the build.
+func apiImportsFactory() android.Module {
+	module := &ApiImports{}
+	module.AddProperties(&module.properties)
+	android.InitAndroidModule(module)
+	return module
+}
+
+func (imports *ApiImports) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// ApiImport module does not generate any build actions
+}
+
+type ApiImportInfo struct {
+	SharedLibs, HeaderLibs map[string]string
+}
+
+var ApiImportsProvider = blueprint.NewMutatorProvider(ApiImportInfo{}, "deps")
+
+// Store module lists into ApiImportInfo and share it over mutator provider.
+func (imports *ApiImports) DepsMutator(ctx android.BottomUpMutatorContext) {
+	generateNameMapWithSuffix := func(names []string) map[string]string {
+		moduleNameMap := make(map[string]string)
+		for _, name := range names {
+			moduleNameMap[name] = name + apiImportNameSuffix
+		}
+
+		return moduleNameMap
+	}
+
+	sharedLibs := generateNameMapWithSuffix(imports.properties.Shared_libs)
+	headerLibs := generateNameMapWithSuffix(imports.properties.Header_libs)
+
+	ctx.SetProvider(ApiImportsProvider, ApiImportInfo{
+		SharedLibs: sharedLibs,
+		HeaderLibs: headerLibs,
+	})
+}
+
+func GetApiImportSuffix() string {
+	return apiImportNameSuffix
+}

+ 12 - 7
rust/rust.go

@@ -1380,6 +1380,11 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) {
 	var commonDepVariations []blueprint.Variation
 	var snapshotInfo *cc.SnapshotInfo
 
+	apiImportInfo := cc.GetApiImports(mod, actx)
+	for idx, lib := range deps.SharedLibs {
+		deps.SharedLibs[idx] = cc.GetReplaceModuleName(lib, apiImportInfo.SharedLibs)
+	}
+
 	if ctx.Os() == android.Android {
 		deps.SharedLibs, _ = cc.RewriteLibs(mod, &snapshotInfo, actx, ctx.Config(), deps.SharedLibs)
 	}
@@ -1400,7 +1405,7 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) {
 	rlibDepVariations = append(rlibDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: rlibVariation})
 	for _, lib := range deps.Rlibs {
 		depTag := rlibDepTag
-		lib = cc.RewriteSnapshotLib(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).Rlibs)
+		lib = cc.GetReplaceModuleName(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).Rlibs)
 
 		actx.AddVariationDependencies(rlibDepVariations, depTag, lib)
 	}
@@ -1438,7 +1443,7 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) {
 		if mod.compiler.stdLinkage(ctx) == RlibLinkage {
 			for _, lib := range deps.Stdlibs {
 				depTag := rlibDepTag
-				lib = cc.RewriteSnapshotLib(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).Rlibs)
+				lib = cc.GetReplaceModuleName(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).Rlibs)
 
 				actx.AddVariationDependencies(append(commonDepVariations, []blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}}...),
 					depTag, lib)
@@ -1462,7 +1467,7 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) {
 
 	for _, lib := range deps.WholeStaticLibs {
 		depTag := cc.StaticDepTag(true)
-		lib = cc.RewriteSnapshotLib(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).StaticLibs)
+		lib = cc.GetReplaceModuleName(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).StaticLibs)
 
 		actx.AddVariationDependencies([]blueprint.Variation{
 			{Mutator: "link", Variation: "static"},
@@ -1471,7 +1476,7 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) {
 
 	for _, lib := range deps.StaticLibs {
 		depTag := cc.StaticDepTag(false)
-		lib = cc.RewriteSnapshotLib(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).StaticLibs)
+		lib = cc.GetReplaceModuleName(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).StaticLibs)
 
 		actx.AddVariationDependencies([]blueprint.Variation{
 			{Mutator: "link", Variation: "static"},
@@ -1483,11 +1488,11 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) {
 	crtVariations := cc.GetCrtVariations(ctx, mod)
 	for _, crt := range deps.CrtBegin {
 		actx.AddVariationDependencies(crtVariations, cc.CrtBeginDepTag,
-			cc.RewriteSnapshotLib(crt, cc.GetSnapshot(mod, &snapshotInfo, actx).Objects))
+			cc.GetReplaceModuleName(crt, cc.GetSnapshot(mod, &snapshotInfo, actx).Objects))
 	}
 	for _, crt := range deps.CrtEnd {
 		actx.AddVariationDependencies(crtVariations, cc.CrtEndDepTag,
-			cc.RewriteSnapshotLib(crt, cc.GetSnapshot(mod, &snapshotInfo, actx).Objects))
+			cc.GetReplaceModuleName(crt, cc.GetSnapshot(mod, &snapshotInfo, actx).Objects))
 	}
 
 	if mod.sourceProvider != nil {
@@ -1510,7 +1515,7 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) {
 
 // addRlibDependency will add an rlib dependency, rewriting to the snapshot library if available.
 func addRlibDependency(actx android.BottomUpMutatorContext, lib string, mod *Module, snapshotInfo *cc.SnapshotInfo, variations []blueprint.Variation) {
-	lib = cc.RewriteSnapshotLib(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).Rlibs)
+	lib = cc.GetReplaceModuleName(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).Rlibs)
 	actx.AddVariationDependencies(variations, rlibDepTag, lib)
 }