Implement API surface import with APEX stub

Implement APEX stub of API surface so any stub can be replaced with API
surface when APEX stub interface is required.

Unlike other stub interface, APEX stub can be decided if it should be
used after APEX postdeps mutator analyzes which modules should be
included in which APEX. To cover this, APEX stub is being added to the
dependency if the dependency should not be covered with LLNDK or NDK
stub, and APEX stub exists. From depsToPaths, if dependency to both
original module and API library exists, then it choose one of the
dependency and ignore the other.

To cover this logic, a new property is added to the api_surface :
apex_libs. This is introduced as it is difficult to
gather all api library with apex stub before DepsMutator.

Bug: 264963986
Test: cf_x86_64_phone_vendor build succeeded
Change-Id: I9f0b1f70968e32eba94d3e0d7bb1f4bb29ff2438
This commit is contained in:
Kiyoung Kim 2023-02-06 22:08:13 +09:00
parent 8ecedd4dfc
commit 76b06f3973
6 changed files with 520 additions and 265 deletions

View file

@ -9722,30 +9722,85 @@ func TestApexBuildsAgainstApiSurfaceStubLibraries(t *testing.T) {
apex {
name: "myapex",
key: "myapex.key",
native_shared_libs: ["libfoo"],
native_shared_libs: ["libbaz"],
binaries: ["binfoo"],
min_sdk_version: "29",
}
apex_key {
name: "myapex.key",
}
cc_library {
name: "libfoo",
shared_libs: ["libc"],
cc_binary {
name: "binfoo",
shared_libs: ["libbar", "libbaz", "libqux",],
apex_available: ["myapex"],
min_sdk_version: "29",
recovery_available: false,
}
cc_library {
name: "libbar",
srcs: ["libbar.cc"],
stubs: {
symbol_file: "libbar.map.txt",
versions: [
"29",
],
},
}
cc_library {
name: "libbaz",
srcs: ["libbaz.cc"],
apex_available: ["myapex"],
min_sdk_version: "29",
stubs: {
symbol_file: "libbaz.map.txt",
versions: [
"29",
],
},
}
cc_api_library {
name: "libc",
src: "libc.so",
name: "libbar",
src: "libbar_stub.so",
min_sdk_version: "29",
recovery_available: true,
variants: ["apex.29"],
}
cc_api_variant {
name: "libbar",
variant: "apex",
version: "29",
src: "libbar_apex_29.so",
}
cc_api_library {
name: "libbaz",
src: "libbaz_stub.so",
min_sdk_version: "29",
variants: ["apex.29"],
}
cc_api_variant {
name: "libbaz",
variant: "apex",
version: "29",
src: "libbaz_apex_29.so",
}
cc_api_library {
name: "libqux",
src: "libqux_stub.so",
min_sdk_version: "29",
variants: ["apex.29"],
}
cc_api_variant {
name: "libqux",
variant: "apex",
version: "29",
src: "libqux_apex_29.so",
}
api_imports {
name: "api_imports",
shared_libs: [
"libc",
apex_shared_libs: [
"libbar",
"libbaz",
"libqux",
],
header_libs: [],
}
`
result := testApex(t, bp)
@ -9761,17 +9816,107 @@ func TestApexBuildsAgainstApiSurfaceStubLibraries(t *testing.T) {
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()
// Library defines stubs and cc_api_library should be used with cc_api_library
binfooApexVariant := result.ModuleForTests("binfoo", "android_arm64_armv8-a_apex29").Module()
libbarCoreVariant := result.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module()
libbarApiImportCoreVariant := result.ModuleForTests("libbar.apiimport", "android_arm64_armv8-a_shared").Module()
android.AssertBoolEquals(t, "apex variant should link against API surface stub libraries", true, hasDep(libfooApexVariant, libcApexVariant))
android.AssertBoolEquals(t, "apex variant should link against API surface stub libraries", true, hasDep(binfooApexVariant, libbarApiImportCoreVariant))
android.AssertBoolEquals(t, "apex variant should link against original library if exists", true, hasDep(binfooApexVariant, libbarCoreVariant))
// 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))
binFooCFlags := result.ModuleForTests("binfoo", "android_arm64_armv8-a_apex29").Rule("ld").Args["libFlags"]
android.AssertStringDoesContain(t, "binfoo should link against APEX variant", binFooCFlags, "libbar.apex.29.apiimport.so")
android.AssertStringDoesNotContain(t, "binfoo should not link against cc_api_library itself", binFooCFlags, "libbar.apiimport.so")
android.AssertStringDoesNotContain(t, "binfoo should not link against original definition", binFooCFlags, "libbar.so")
// Library defined in the same APEX should be linked with original definition instead of cc_api_library
libbazApexVariant := result.ModuleForTests("libbaz", "android_arm64_armv8-a_shared_apex29").Module()
libbazApiImportCoreVariant := result.ModuleForTests("libbaz.apiimport", "android_arm64_armv8-a_shared").Module()
android.AssertBoolEquals(t, "apex variant should link against API surface stub libraries even from same APEX", true, hasDep(binfooApexVariant, libbazApiImportCoreVariant))
android.AssertBoolEquals(t, "apex variant should link against original library if exists", true, hasDep(binfooApexVariant, libbazApexVariant))
android.AssertStringDoesContain(t, "binfoo should link against APEX variant", binFooCFlags, "libbaz.so")
android.AssertStringDoesNotContain(t, "binfoo should not link against cc_api_library itself", binFooCFlags, "libbaz.apiimport.so")
android.AssertStringDoesNotContain(t, "binfoo should not link against original definition", binFooCFlags, "libbaz.apex.29.apiimport.so")
// cc_api_library defined without original library should be linked with cc_api_library
libquxApiImportApexVariant := result.ModuleForTests("libqux.apiimport", "android_arm64_armv8-a_shared").Module()
android.AssertBoolEquals(t, "apex variant should link against API surface stub libraries even original library definition does not exist", true, hasDep(binfooApexVariant, libquxApiImportApexVariant))
android.AssertStringDoesContain(t, "binfoo should link against APEX variant", binFooCFlags, "libqux.apex.29.apiimport.so")
}
func TestPlatformBinaryBuildsAgainstApiSurfaceStubLibraries(t *testing.T) {
bp := `
apex {
name: "myapex",
key: "myapex.key",
native_shared_libs: ["libbar"],
min_sdk_version: "29",
}
apex_key {
name: "myapex.key",
}
cc_binary {
name: "binfoo",
shared_libs: ["libbar"],
recovery_available: false,
}
cc_library {
name: "libbar",
srcs: ["libbar.cc"],
apex_available: ["myapex"],
min_sdk_version: "29",
stubs: {
symbol_file: "libbar.map.txt",
versions: [
"29",
],
},
}
cc_api_library {
name: "libbar",
src: "libbar_stub.so",
variants: ["apex.29"],
}
cc_api_variant {
name: "libbar",
variant: "apex",
version: "29",
src: "libbar_apex_29.so",
}
api_imports {
name: "api_imports",
apex_shared_libs: [
"libbar",
],
}
`
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
}
// Library defines stubs and cc_api_library should be used with cc_api_library
binfooApexVariant := result.ModuleForTests("binfoo", "android_arm64_armv8-a").Module()
libbarCoreVariant := result.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module()
libbarApiImportCoreVariant := result.ModuleForTests("libbar.apiimport", "android_arm64_armv8-a_shared").Module()
android.AssertBoolEquals(t, "apex variant should link against API surface stub libraries", true, hasDep(binfooApexVariant, libbarApiImportCoreVariant))
android.AssertBoolEquals(t, "apex variant should link against original library if exists", true, hasDep(binfooApexVariant, libbarCoreVariant))
binFooCFlags := result.ModuleForTests("binfoo", "android_arm64_armv8-a").Rule("ld").Args["libFlags"]
android.AssertStringDoesContain(t, "binfoo should link against APEX variant", binFooCFlags, "libbar.apex.29.apiimport.so")
android.AssertStringDoesNotContain(t, "binfoo should not link against cc_api_library itself", binFooCFlags, "libbar.apiimport.so")
android.AssertStringDoesNotContain(t, "binfoo should not link against original definition", binFooCFlags, "libbar.so")
}
func TestTrimmedApex(t *testing.T) {

231
cc/cc.go
View file

@ -2416,6 +2416,27 @@ func rewriteLibsForApiImports(c LinkableInterface, libs []string, replaceList ma
return nonVariantLibs, variantLibs
}
func (c *Module) shouldUseApiSurface() bool {
if c.Os() == android.Android && c.Target().NativeBridge != android.NativeBridgeEnabled {
if GetImageVariantType(c) == vendorImageVariant || GetImageVariantType(c) == productImageVariant {
// LLNDK Variant
return true
}
if c.Properties.IsSdkVariant {
// NDK Variant
return true
}
if c.isImportedApiLibrary() {
// API Library should depend on API headers
return true
}
}
return false
}
func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) {
if !c.Enabled() {
return
@ -2435,7 +2456,7 @@ func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) {
apiNdkLibs := []string{}
apiLateNdkLibs := []string{}
if ctx.Os() == android.Android && c.Target().NativeBridge != android.NativeBridgeEnabled {
if c.shouldUseApiSurface() {
deps.SharedLibs, apiNdkLibs = rewriteLibsForApiImports(c, deps.SharedLibs, apiImportInfo.SharedLibs, ctx.Config())
deps.LateSharedLibs, apiLateNdkLibs = rewriteLibsForApiImports(c, deps.LateSharedLibs, apiImportInfo.SharedLibs, ctx.Config())
deps.SystemSharedLibs, _ = rewriteLibsForApiImports(c, deps.SystemSharedLibs, apiImportInfo.SharedLibs, ctx.Config())
@ -2466,7 +2487,7 @@ func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) {
}
// Check header lib replacement from API surface first, and then check again with VSDK
if ctx.Os() == android.Android && c.Target().NativeBridge != android.NativeBridgeEnabled {
if c.shouldUseApiSurface() {
lib = GetReplaceModuleName(lib, apiImportInfo.HeaderLibs)
}
lib = GetReplaceModuleName(lib, GetSnapshot(c, &snapshotInfo, actx).HeaderLibs)
@ -2550,12 +2571,22 @@ func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) {
}
name, version := StubsLibNameAndVersion(lib)
if apiLibraryName, ok := apiImportInfo.SharedLibs[name]; ok && !ctx.OtherModuleExists(name) {
name = apiLibraryName
}
sharedLibNames = append(sharedLibNames, name)
variations := []blueprint.Variation{
{Mutator: "link", Variation: "shared"},
}
AddSharedLibDependenciesWithVersions(ctx, c, variations, depTag, name, version, false)
if _, ok := apiImportInfo.ApexSharedLibs[name]; !ok || ctx.OtherModuleExists(name) {
AddSharedLibDependenciesWithVersions(ctx, c, variations, depTag, name, version, false)
}
if apiLibraryName, ok := apiImportInfo.ApexSharedLibs[name]; ok {
AddSharedLibDependenciesWithVersions(ctx, c, variations, depTag, apiLibraryName, version, false)
}
}
for _, lib := range deps.LateStaticLibs {
@ -2898,10 +2929,58 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
c.apexSdkVersion = findApexSdkVersion(ctx, apexInfo)
skipModuleList := map[string]bool{}
var apiImportInfo multitree.ApiImportInfo
hasApiImportInfo := false
ctx.VisitDirectDeps(func(dep android.Module) {
if dep.Name() == "api_imports" {
apiImportInfo = ctx.OtherModuleProvider(dep, multitree.ApiImportsProvider).(multitree.ApiImportInfo)
hasApiImportInfo = true
}
})
if hasApiImportInfo {
targetStubModuleList := map[string]string{}
targetOrigModuleList := map[string]string{}
// Search for dependency which both original module and API imported library with APEX stub exists
ctx.VisitDirectDeps(func(dep android.Module) {
depName := ctx.OtherModuleName(dep)
if apiLibrary, ok := apiImportInfo.ApexSharedLibs[depName]; ok {
targetStubModuleList[apiLibrary] = depName
}
})
ctx.VisitDirectDeps(func(dep android.Module) {
depName := ctx.OtherModuleName(dep)
if origLibrary, ok := targetStubModuleList[depName]; ok {
targetOrigModuleList[origLibrary] = depName
}
})
// Decide which library should be used between original and API imported library
ctx.VisitDirectDeps(func(dep android.Module) {
depName := ctx.OtherModuleName(dep)
if apiLibrary, ok := targetOrigModuleList[depName]; ok {
if shouldUseStubForApex(ctx, dep) {
skipModuleList[depName] = true
} else {
skipModuleList[apiLibrary] = true
}
}
})
}
ctx.VisitDirectDeps(func(dep android.Module) {
depName := ctx.OtherModuleName(dep)
depTag := ctx.OtherModuleDependencyTag(dep)
if _, ok := skipModuleList[depName]; ok {
// skip this module because original module or API imported module matching with this should be used instead.
return
}
if depTag == android.DarwinUniversalVariantTag {
depPaths.DarwinSecondArchOutput = dep.(*Module).OutputFile()
return
@ -3237,21 +3316,8 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
return depPaths
}
// ChooseStubOrImpl determines whether a given dependency should be redirected to the stub variant
// of the dependency or not, and returns the SharedLibraryInfo and FlagExporterInfo for the right
// dependency. The stub variant is selected when the dependency crosses a boundary where each side
// has different level of updatability. For example, if a library foo in an APEX depends on a
// library bar which provides stable interface and exists in the platform, foo uses the stub variant
// of bar. If bar doesn't provide a stable interface (i.e. buildStubs() == false) or is in the
// same APEX as foo, the non-stub variant of bar is used.
func ChooseStubOrImpl(ctx android.ModuleContext, dep android.Module) (SharedLibraryInfo, FlagExporterInfo) {
func shouldUseStubForApex(ctx android.ModuleContext, dep android.Module) bool {
depName := ctx.OtherModuleName(dep)
depTag := ctx.OtherModuleDependencyTag(dep)
libDepTag, ok := depTag.(libraryDependencyTag)
if !ok || !libDepTag.shared() {
panic(fmt.Errorf("Unexpected dependency tag: %T", depTag))
}
thisModule, ok := ctx.Module().(android.ApexModule)
if !ok {
panic(fmt.Errorf("Not an APEX module: %q", ctx.ModuleName()))
@ -3266,62 +3332,93 @@ func ChooseStubOrImpl(ctx android.ModuleContext, dep android.Module) (SharedLibr
bootstrap = linkable.Bootstrap()
}
apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
useStubs := false
if lib := moduleLibraryInterface(dep); lib.buildStubs() && useVndk { // LLNDK
if !apexInfo.IsForPlatform() {
// For platform libraries, use current version of LLNDK
// If this is for use_vendor apex we will apply the same rules
// of apex sdk enforcement below to choose right version.
useStubs = true
}
} else if apexInfo.IsForPlatform() || apexInfo.UsePlatformApis {
// If not building for APEX or the containing APEX allows the use of
// platform APIs, use stubs only when it is from an APEX (and not from
// platform) However, for host, ramdisk, vendor_ramdisk, recovery or
// bootstrap modules, always link to non-stub variant
isNotInPlatform := dep.(android.ApexModule).NotInPlatform()
isApexImportedApiLibrary := false
if cc, ok := dep.(*Module); ok {
if apiLibrary, ok := cc.linker.(*apiLibraryDecorator); ok {
if apiLibrary.hasApexStubs() {
isApexImportedApiLibrary = true
}
}
}
useStubs = (isNotInPlatform || isApexImportedApiLibrary) && !bootstrap
if useStubs {
// Another exception: if this module is a test for an APEX, then
// it is linked with the non-stub variant of a module in the APEX
// as if this is part of the APEX.
testFor := ctx.Provider(android.ApexTestForInfoProvider).(android.ApexTestForInfo)
for _, apexContents := range testFor.ApexContents {
if apexContents.DirectlyInApex(depName) {
useStubs = false
break
}
}
}
if useStubs {
// Yet another exception: If this module and the dependency are
// available to the same APEXes then skip stubs between their
// platform variants. This complements the test_for case above,
// which avoids the stubs on a direct APEX library dependency, by
// avoiding stubs for indirect test dependencies as well.
//
// TODO(b/183882457): This doesn't work if the two libraries have
// only partially overlapping apex_available. For that test_for
// modules would need to be split into APEX variants and resolved
// separately for each APEX they have access to.
if !isApexImportedApiLibrary && android.AvailableToSameApexes(thisModule, dep.(android.ApexModule)) {
useStubs = false
}
}
} else {
// If building for APEX, use stubs when the parent is in any APEX that
// the child is not in.
useStubs = !android.DirectlyInAllApexes(apexInfo, depName)
}
return useStubs
}
// ChooseStubOrImpl determines whether a given dependency should be redirected to the stub variant
// of the dependency or not, and returns the SharedLibraryInfo and FlagExporterInfo for the right
// dependency. The stub variant is selected when the dependency crosses a boundary where each side
// has different level of updatability. For example, if a library foo in an APEX depends on a
// library bar which provides stable interface and exists in the platform, foo uses the stub variant
// of bar. If bar doesn't provide a stable interface (i.e. buildStubs() == false) or is in the
// same APEX as foo, the non-stub variant of bar is used.
func ChooseStubOrImpl(ctx android.ModuleContext, dep android.Module) (SharedLibraryInfo, FlagExporterInfo) {
depTag := ctx.OtherModuleDependencyTag(dep)
libDepTag, ok := depTag.(libraryDependencyTag)
if !ok || !libDepTag.shared() {
panic(fmt.Errorf("Unexpected dependency tag: %T", depTag))
}
sharedLibraryInfo := ctx.OtherModuleProvider(dep, SharedLibraryInfoProvider).(SharedLibraryInfo)
depExporterInfo := ctx.OtherModuleProvider(dep, FlagExporterInfoProvider).(FlagExporterInfo)
sharedLibraryStubsInfo := ctx.OtherModuleProvider(dep, SharedLibraryStubsProvider).(SharedLibraryStubsInfo)
apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
if !libDepTag.explicitlyVersioned && len(sharedLibraryStubsInfo.SharedStubLibraries) > 0 {
useStubs := false
if lib := moduleLibraryInterface(dep); lib.buildStubs() && useVndk { // LLNDK
if !apexInfo.IsForPlatform() {
// For platform libraries, use current version of LLNDK
// If this is for use_vendor apex we will apply the same rules
// of apex sdk enforcement below to choose right version.
useStubs = true
}
} else if apexInfo.IsForPlatform() || apexInfo.UsePlatformApis {
// If not building for APEX or the containing APEX allows the use of
// platform APIs, use stubs only when it is from an APEX (and not from
// platform) However, for host, ramdisk, vendor_ramdisk, recovery or
// bootstrap modules, always link to non-stub variant
useStubs = dep.(android.ApexModule).NotInPlatform() && !bootstrap
if useStubs {
// Another exception: if this module is a test for an APEX, then
// it is linked with the non-stub variant of a module in the APEX
// as if this is part of the APEX.
testFor := ctx.Provider(android.ApexTestForInfoProvider).(android.ApexTestForInfo)
for _, apexContents := range testFor.ApexContents {
if apexContents.DirectlyInApex(depName) {
useStubs = false
break
}
}
}
if useStubs {
// Yet another exception: If this module and the dependency are
// available to the same APEXes then skip stubs between their
// platform variants. This complements the test_for case above,
// which avoids the stubs on a direct APEX library dependency, by
// avoiding stubs for indirect test dependencies as well.
//
// TODO(b/183882457): This doesn't work if the two libraries have
// only partially overlapping apex_available. For that test_for
// modules would need to be split into APEX variants and resolved
// separately for each APEX they have access to.
if android.AvailableToSameApexes(thisModule, dep.(android.ApexModule)) {
useStubs = false
}
}
} else {
// If building for APEX, use stubs when the parent is in any APEX that
// the child is not in.
useStubs = !android.DirectlyInAllApexes(apexInfo, depName)
}
// when to use (unspecified) stubs, use the latest one.
if useStubs {
if shouldUseStubForApex(ctx, dep) {
stubs := sharedLibraryStubsInfo.SharedStubLibraries
toUse := stubs[len(stubs)-1]
sharedLibraryInfo = toUse.SharedLibraryInfo

View file

@ -23,7 +23,8 @@ import (
)
var (
ndkVariantRegex = regexp.MustCompile("ndk\\.([a-zA-Z0-9]+)")
ndkVariantRegex = regexp.MustCompile("ndk\\.([a-zA-Z0-9]+)")
stubVariantRegex = regexp.MustCompile("apex\\.([a-zA-Z0-9]+)")
)
func init() {
@ -60,6 +61,12 @@ func updateImportedLibraryDependency(ctx android.BottomUpMutatorContext) {
variantName := BuildApiVariantName(m.BaseModuleName(), targetVariant, "")
ctx.AddDependency(m, nil, variantName)
}
} else if m.IsStubs() {
targetVariant := "apex." + m.StubsVersion()
if inList(targetVariant, apiLibrary.properties.Variants) {
variantName := BuildApiVariantName(m.BaseModuleName(), targetVariant, "")
ctx.AddDependency(m, nil, variantName)
}
}
}
@ -153,15 +160,15 @@ func (d *apiLibraryDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps
in = android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), src)
}
// LLNDK variant
if m.UseVndk() && d.hasLLNDKStubs() {
apiVariantModule := BuildApiVariantName(m.BaseModuleName(), "llndk", "")
libName := m.BaseModuleName() + multitree.GetApiImportSuffix()
load_cc_variant := func(apiVariantModule string) {
var mod android.Module
ctx.VisitDirectDeps(func(depMod android.Module) {
if depMod.Name() == apiVariantModule {
mod = depMod
libName = apiVariantModule
}
})
@ -184,37 +191,17 @@ func (d *apiLibraryDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps
}
}
}
}
if m.UseVndk() && d.hasLLNDKStubs() {
// LLNDK variant
load_cc_variant(BuildApiVariantName(m.BaseModuleName(), "llndk", ""))
} else if m.IsSdkVariant() {
// NDK Variant
apiVariantModule := BuildApiVariantName(m.BaseModuleName(), "ndk", m.StubsVersion())
var mod android.Module
ctx.VisitDirectDeps(func(depMod android.Module) {
if depMod.Name() == apiVariantModule {
mod = depMod
}
})
if mod != nil {
variantMod, ok := mod.(*CcApiVariant)
if ok {
in = variantMod.Src()
// Copy NDK properties to cc_api_library module
d.libraryDecorator.flagExporter.Properties.Export_include_dirs = append(
d.libraryDecorator.flagExporter.Properties.Export_include_dirs,
variantMod.exportProperties.Export_include_dirs...)
// Export headers as system include dirs if specified. Mostly for libc
if Bool(variantMod.exportProperties.Export_headers_as_system) {
d.libraryDecorator.flagExporter.Properties.Export_system_include_dirs = append(
d.libraryDecorator.flagExporter.Properties.Export_system_include_dirs,
d.libraryDecorator.flagExporter.Properties.Export_include_dirs...)
d.libraryDecorator.flagExporter.Properties.Export_include_dirs = nil
}
}
}
load_cc_variant(BuildApiVariantName(m.BaseModuleName(), "ndk", m.StubsVersion()))
} else if m.IsStubs() {
// APEX Variant
load_cc_variant(BuildApiVariantName(m.BaseModuleName(), "apex", m.StubsVersion()))
}
// Flags reexported from dependencies. (e.g. vndk_prebuilt_shared)
@ -237,20 +224,58 @@ func (d *apiLibraryDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps
d.libraryDecorator.flagExporter.setProvider(ctx)
d.unstrippedOutputFile = in
libName := d.libraryDecorator.getLibName(ctx) + flags.Toolchain.ShlibSuffix()
libName += flags.Toolchain.ShlibSuffix()
tocFile := android.PathForModuleOut(ctx, libName+".toc")
d.tocFile = android.OptionalPathForPath(tocFile)
TransformSharedObjectToToc(ctx, in, tocFile)
outputFile := android.PathForModuleOut(ctx, libName)
// TODO(b/270485584) This copies with a new name, just to avoid conflict with prebuilts.
// We can just use original input if there is any way to avoid name conflict without copy.
ctx.Build(pctx, android.BuildParams{
Rule: android.Cp,
Description: "API surface imported library",
Input: in,
Output: outputFile,
Args: map[string]string{
"cpFlags": "-L",
},
})
ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
SharedLibrary: in,
SharedLibrary: outputFile,
Target: ctx.Target(),
TableOfContents: d.tocFile,
})
return in
d.shareStubs(ctx)
return outputFile
}
// Share additional information about stub libraries with provider
func (d *apiLibraryDecorator) shareStubs(ctx ModuleContext) {
stubs := ctx.GetDirectDepsWithTag(stubImplDepTag)
if len(stubs) > 0 {
var stubsInfo []SharedStubLibrary
for _, stub := range stubs {
stubInfo := ctx.OtherModuleProvider(stub, SharedLibraryInfoProvider).(SharedLibraryInfo)
flagInfo := ctx.OtherModuleProvider(stub, FlagExporterInfoProvider).(FlagExporterInfo)
stubsInfo = append(stubsInfo, SharedStubLibrary{
Version: moduleLibraryInterface(stub).stubsVersion(),
SharedLibraryInfo: stubInfo,
FlagExporterInfo: flagInfo,
})
}
ctx.SetProvider(SharedLibraryStubsProvider, SharedLibraryStubsInfo{
SharedStubLibraries: stubsInfo,
IsLLNDK: ctx.IsLlndk(),
})
}
}
func (d *apiLibraryDecorator) availableFor(what string) bool {
@ -258,6 +283,19 @@ func (d *apiLibraryDecorator) availableFor(what string) bool {
return true
}
func (d *apiLibraryDecorator) hasApexStubs() bool {
for _, variant := range d.properties.Variants {
if strings.HasPrefix(variant, "apex") {
return true
}
}
return false
}
func (d *apiLibraryDecorator) hasStubsVariants() bool {
return d.hasApexStubs()
}
func (d *apiLibraryDecorator) stubsVersions(ctx android.BaseMutatorContext) []string {
m, ok := ctx.Module().(*Module)
@ -265,14 +303,8 @@ func (d *apiLibraryDecorator) stubsVersions(ctx android.BaseMutatorContext) []st
return nil
}
if d.hasLLNDKStubs() && m.UseVndk() {
// LLNDK libraries only need a single stubs variant.
return []string{android.FutureApiLevel.String()}
}
// TODO(b/244244438) Create more version information for NDK and APEX variations
// NDK variants
if m.IsSdkVariant() {
// TODO(b/249193999) Do not check if module has NDK stubs once all NDK cc_api_library contains ndk variant of cc_api_variant.
if d.hasNDKStubs() {
@ -280,6 +312,17 @@ func (d *apiLibraryDecorator) stubsVersions(ctx android.BaseMutatorContext) []st
}
}
if d.hasLLNDKStubs() && m.UseVndk() {
// LLNDK libraries only need a single stubs variant.
return []string{android.FutureApiLevel.String()}
}
stubsVersions := d.getStubVersions()
if len(stubsVersions) != 0 {
return stubsVersions
}
if m.MinSdkVersion() == "" {
return nil
}
@ -319,6 +362,18 @@ func (d *apiLibraryDecorator) getNdkVersions() []string {
return ndkVersions
}
func (d *apiLibraryDecorator) getStubVersions() []string {
stubVersions := []string{}
for _, variant := range d.properties.Variants {
if match := stubVariantRegex.FindStringSubmatch(variant); len(match) == 2 {
stubVersions = append(stubVersions, match[1])
}
}
return stubVersions
}
// 'cc_api_headers' is similar with 'cc_api_library', but which replaces
// header libraries. The module will replace any dependencies to existing
// original header libraries.
@ -433,7 +488,7 @@ func BuildApiVariantName(baseName string, variant string, version string) string
// Implement ImageInterface to generate image variants
func (v *CcApiVariant) ImageMutatorBegin(ctx android.BaseModuleContext) {}
func (v *CcApiVariant) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
return String(v.properties.Variant) == "ndk"
return inList(String(v.properties.Variant), []string{"ndk", "apex"})
}
func (v *CcApiVariant) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool { return false }
func (v *CcApiVariant) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool { return false }

View file

@ -41,6 +41,7 @@ func TestApiLibraryReplacesExistingModule(t *testing.T) {
cc_library {
name: "libfoo",
shared_libs: ["libbar"],
vendor_available: true,
}
cc_library {
@ -49,6 +50,7 @@ func TestApiLibraryReplacesExistingModule(t *testing.T) {
cc_api_library {
name: "libbar",
vendor_available: true,
src: "libbar.so",
}
@ -57,7 +59,6 @@ func TestApiLibraryReplacesExistingModule(t *testing.T) {
shared_libs: [
"libbar",
],
header_libs: [],
}
`
@ -67,8 +68,14 @@ func TestApiLibraryReplacesExistingModule(t *testing.T) {
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))
android.AssertBoolEquals(t, "original library should be linked with non-stub variant", true, hasDirectDependency(t, ctx, libfoo, libbar))
android.AssertBoolEquals(t, "Stub library from API surface should be not linked with non-stub variant", false, hasDirectDependency(t, ctx, libfoo, libbarApiImport))
libfooVendor := ctx.ModuleForTests("libfoo", "android_vendor.29_arm64_armv8-a_shared").Module()
libbarApiImportVendor := ctx.ModuleForTests("libbar.apiimport", "android_vendor.29_arm64_armv8-a_shared").Module()
android.AssertBoolEquals(t, "original library should not be linked", false, hasDirectDependency(t, ctx, libfooVendor, libbar))
android.AssertBoolEquals(t, "Stub library from API surface should be linked", true, hasDirectDependency(t, ctx, libfooVendor, libbarApiImportVendor))
}
func TestApiLibraryDoNotRequireOriginalModule(t *testing.T) {
@ -76,11 +83,13 @@ func TestApiLibraryDoNotRequireOriginalModule(t *testing.T) {
cc_library {
name: "libfoo",
shared_libs: ["libbar"],
vendor: true,
}
cc_api_library {
name: "libbar",
src: "libbar.so",
vendor_available: true,
}
api_imports {
@ -88,14 +97,13 @@ func TestApiLibraryDoNotRequireOriginalModule(t *testing.T) {
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()
libfoo := ctx.ModuleForTests("libfoo", "android_vendor.29_arm64_armv8-a_shared").Module()
libbarApiImport := ctx.ModuleForTests("libbar.apiimport", "android_vendor.29_arm64_armv8-a_shared").Module()
android.AssertBoolEquals(t, "Stub library from API surface should be linked", true, hasDirectDependency(t, ctx, libfoo, libbarApiImport))
}
@ -105,143 +113,36 @@ func TestApiLibraryShouldNotReplaceWithoutApiImport(t *testing.T) {
cc_library {
name: "libfoo",
shared_libs: ["libbar"],
vendor_available: true,
}
cc_library {
name: "libbar",
vendor_available: true,
}
cc_api_library {
name: "libbar",
src: "libbar.so",
vendor_available: true,
}
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()
libfoo := ctx.ModuleForTests("libfoo", "android_vendor.29_arm64_armv8-a_shared").Module()
libbar := ctx.ModuleForTests("libbar", "android_vendor.29_arm64_armv8-a_shared").Module()
libbarApiImport := ctx.ModuleForTests("libbar.apiimport", "android_vendor.29_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))
}
func TestApiHeaderReplacesExistingModule(t *testing.T) {
bp := `
cc_library {
name: "libfoo",
header_libs: ["libfoo_headers"],
}
cc_api_library {
name: "libfoo",
header_libs: ["libfoo_headers"],
src: "libfoo.so",
}
cc_library_headers {
name: "libfoo_headers",
}
cc_api_headers {
name: "libfoo_headers",
}
api_imports {
name: "api_imports",
shared_libs: [
"libfoo",
],
header_libs: [
"libfoo_headers",
],
}
`
ctx := prepareForCcTest.RunTestWithBp(t, bp)
libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
libfooApiImport := ctx.ModuleForTests("libfoo.apiimport", "android_arm64_armv8-a_shared").Module()
libfooHeader := ctx.ModuleForTests("libfoo_headers", "android_arm64_armv8-a").Module()
libfooHeaderApiImport := ctx.ModuleForTests("libfoo_headers.apiimport", "android_arm64_armv8-a").Module()
android.AssertBoolEquals(t, "original header should not be used for original library", false, hasDirectDependency(t, ctx, libfoo, libfooHeader))
android.AssertBoolEquals(t, "Header from API surface should be used for original library", true, hasDirectDependency(t, ctx, libfoo, libfooHeaderApiImport))
android.AssertBoolEquals(t, "original header should not be used for library imported from API surface", false, hasDirectDependency(t, ctx, libfooApiImport, libfooHeader))
android.AssertBoolEquals(t, "Header from API surface should be used for library imported from API surface", true, hasDirectDependency(t, ctx, libfooApiImport, libfooHeaderApiImport))
}
func TestApiHeadersDoNotRequireOriginalModule(t *testing.T) {
bp := `
cc_library {
name: "libfoo",
header_libs: ["libfoo_headers"],
}
cc_api_headers {
name: "libfoo_headers",
}
api_imports {
name: "api_imports",
shared_libs: [
"libfoo",
],
header_libs: [
"libfoo_headers",
],
}
`
ctx := prepareForCcTest.RunTestWithBp(t, bp)
libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
libfooHeaderApiImport := ctx.ModuleForTests("libfoo_headers.apiimport", "android_arm64_armv8-a").Module()
android.AssertBoolEquals(t, "Header from API surface should be used for original library", true, hasDirectDependency(t, ctx, libfoo, libfooHeaderApiImport))
}
func TestApiHeadersShouldNotReplaceWithoutApiImport(t *testing.T) {
bp := `
cc_library {
name: "libfoo",
header_libs: ["libfoo_headers"],
}
cc_library_headers {
name: "libfoo_headers",
}
cc_api_headers {
name: "libfoo_headers",
}
api_imports {
name: "api_imports",
shared_libs: [
"libfoo",
],
header_libs: [],
}
`
ctx := prepareForCcTest.RunTestWithBp(t, bp)
libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
libfooHeader := ctx.ModuleForTests("libfoo_headers", "android_arm64_armv8-a").Module()
libfooHeaderApiImport := ctx.ModuleForTests("libfoo_headers.apiimport", "android_arm64_armv8-a").Module()
android.AssertBoolEquals(t, "original header should be used for original library", true, hasDirectDependency(t, ctx, libfoo, libfooHeader))
android.AssertBoolEquals(t, "Header from API surface should not be used for original library", false, hasDirectDependency(t, ctx, libfoo, libfooHeaderApiImport))
}
func TestExportDirFromStubLibrary(t *testing.T) {
bp := `
cc_library {
@ -330,7 +231,7 @@ func TestApiLibraryWithLlndkVariant(t *testing.T) {
android.AssertBoolEquals(t, "Stub library variant from API surface should be linked", true, hasDirectDependency(t, ctx, libbarApiImport, libbarApiVariant))
binFooLibFlags := ctx.ModuleForTests("binfoo", "android_vendor.29_arm64_armv8-a").Rule("ld").Args["libFlags"]
android.AssertStringDoesContain(t, "Vendor binary should be linked with LLNDK variant source", binFooLibFlags, "libbar_llndk.so")
android.AssertStringDoesContain(t, "Vendor binary should be linked with LLNDK variant source", binFooLibFlags, "libbar.llndk.apiimport.so")
binFooCFlags := ctx.ModuleForTests("binfoo", "android_vendor.29_arm64_armv8-a").Rule("cc").Args["cFlags"]
android.AssertStringDoesContain(t, "Vendor binary should include headers from the LLNDK variant source", binFooCFlags, "-Ilibbar_llndk_include")
@ -354,6 +255,17 @@ func TestApiLibraryWithNdkVariant(t *testing.T) {
stl: "c++_shared",
}
cc_binary {
name: "binqux",
srcs: ["binfoo.cc"],
shared_libs: ["libbar"],
}
cc_library {
name: "libbar",
srcs: ["libbar.cc"],
}
cc_api_library {
name: "libbar",
// TODO(b/244244438) Remove src property once all variants are implemented.
@ -417,10 +329,14 @@ func TestApiLibraryWithNdkVariant(t *testing.T) {
android.AssertBoolEquals(t, "Stub library from API surface should not be linked with different version", false, hasDirectDependency(t, ctx, binbaz, libbarApiImportv29))
binFooLibFlags := ctx.ModuleForTests("binfoo", "android_arm64_armv8-a_sdk").Rule("ld").Args["libFlags"]
android.AssertStringDoesContain(t, "Binary using sdk should be linked with NDK variant source", binFooLibFlags, "libbar_ndk_29.so")
android.AssertStringDoesContain(t, "Binary using sdk should be linked with NDK variant source", binFooLibFlags, "libbar.ndk.29.apiimport.so")
binFooCFlags := ctx.ModuleForTests("binfoo", "android_arm64_armv8-a_sdk").Rule("cc").Args["cFlags"]
android.AssertStringDoesContain(t, "Binary using sdk should include headers from the NDK variant source", binFooCFlags, "-Ilibbar_ndk_29_include")
binQux := ctx.ModuleForTests("binqux", "android_arm64_armv8-a").Module()
android.AssertBoolEquals(t, "NDK Stub library from API surface should not be linked with nonSdk binary", false,
(hasDirectDependency(t, ctx, binQux, libbarApiImportv30) || hasDirectDependency(t, ctx, binQux, libbarApiImportv29)))
}
func TestApiLibraryWithMultipleVariants(t *testing.T) {
@ -440,6 +356,11 @@ func TestApiLibraryWithMultipleVariants(t *testing.T) {
shared_libs: ["libbar"],
}
cc_library {
name: "libbar",
srcs: ["libbar.cc"],
}
cc_api_library {
name: "libbar",
// TODO(b/244244438) Remove src property once all variants are implemented.
@ -450,6 +371,9 @@ func TestApiLibraryWithMultipleVariants(t *testing.T) {
"ndk.29",
"ndk.30",
"ndk.current",
"apex.29",
"apex.30",
"apex.current",
],
}
@ -477,6 +401,30 @@ func TestApiLibraryWithMultipleVariants(t *testing.T) {
export_include_dirs: ["libbar_ndk_current_include"]
}
cc_api_variant {
name: "libbar",
variant: "apex",
version: "29",
src: "libbar_apex_29.so",
export_include_dirs: ["libbar_apex_29_include"]
}
cc_api_variant {
name: "libbar",
variant: "apex",
version: "30",
src: "libbar_apex_30.so",
export_include_dirs: ["libbar_apex_30_include"]
}
cc_api_variant {
name: "libbar",
variant: "apex",
version: "current",
src: "libbar_apex_current.so",
export_include_dirs: ["libbar_apex_current_include"]
}
cc_api_variant {
name: "libbar",
variant: "llndk",
@ -489,7 +437,9 @@ func TestApiLibraryWithMultipleVariants(t *testing.T) {
shared_libs: [
"libbar",
],
header_libs: [],
apex_shared_libs: [
"libbar",
],
}
`
ctx := prepareForCcTest.RunTestWithBp(t, bp)

View file

@ -62,21 +62,26 @@ func sdkMutator(ctx android.BottomUpMutatorContext) {
} else if isCcModule && ccModule.isImportedApiLibrary() {
apiLibrary, _ := ccModule.linker.(*apiLibraryDecorator)
if apiLibrary.hasNDKStubs() && ccModule.canUseSdk() {
variations := []string{"sdk"}
if apiLibrary.hasApexStubs() {
variations = append(variations, "")
}
// Handle cc_api_library module with NDK stubs and variants only which can use SDK
modules := ctx.CreateVariations("", "sdk")
modules := ctx.CreateVariations(variations...)
// Mark the SDK variant.
modules[1].(*Module).Properties.IsSdkVariant = true
// SDK variant is not supposed to be installed
modules[1].(*Module).Properties.PreventInstall = true
modules[0].(*Module).Properties.IsSdkVariant = true
if ctx.Config().UnbundledBuildApps() {
// For an unbundled apps build, hide the platform variant from Make.
modules[0].(*Module).Properties.HideFromMake = true
if apiLibrary.hasApexStubs() {
// For an unbundled apps build, hide the platform variant from Make.
modules[1].(*Module).Properties.HideFromMake = true
modules[1].(*Module).Properties.PreventInstall = true
}
} else {
// For a platform build, mark the SDK variant so that it gets a ".sdk" suffix when
// exposed to Make.
modules[1].(*Module).Properties.SdkAndPlatformVariantVisibleToMake = true
modules[0].(*Module).Properties.SdkAndPlatformVariantVisibleToMake = true
// SDK variant is not supposed to be installed
modules[0].(*Module).Properties.PreventInstall = true
}
} else {
ccModule.Properties.Sdk_version = nil

View file

@ -40,8 +40,9 @@ type ApiImports struct {
}
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
Shared_libs []string // List of C shared libraries from API surfaces
Header_libs []string // List of C header libraries from API surfaces
Apex_shared_libs []string // List of C shared libraries with APEX stubs
}
// 'api_imports' is a module which describes modules available from API surfaces.
@ -60,7 +61,7 @@ func (imports *ApiImports) GenerateAndroidBuildActions(ctx android.ModuleContext
}
type ApiImportInfo struct {
SharedLibs, HeaderLibs map[string]string
SharedLibs, HeaderLibs, ApexSharedLibs map[string]string
}
var ApiImportsProvider = blueprint.NewMutatorProvider(ApiImportInfo{}, "deps")
@ -78,10 +79,12 @@ func (imports *ApiImports) DepsMutator(ctx android.BottomUpMutatorContext) {
sharedLibs := generateNameMapWithSuffix(imports.properties.Shared_libs)
headerLibs := generateNameMapWithSuffix(imports.properties.Header_libs)
apexSharedLibs := generateNameMapWithSuffix(imports.properties.Apex_shared_libs)
ctx.SetProvider(ApiImportsProvider, ApiImportInfo{
SharedLibs: sharedLibs,
HeaderLibs: headerLibs,
SharedLibs: sharedLibs,
HeaderLibs: headerLibs,
ApexSharedLibs: apexSharedLibs,
})
}