From 76b06f3973462a5c4d1702e94426aa33874a0af2 Mon Sep 17 00:00:00 2001 From: Kiyoung Kim Date: Mon, 6 Feb 2023 22:08:13 +0900 Subject: [PATCH] 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 --- apex/apex_test.go | 183 +++++++++++++++++++++++++++---- cc/cc.go | 231 +++++++++++++++++++++++++++------------ cc/library_stub.go | 141 ++++++++++++++++-------- cc/library_stub_test.go | 194 ++++++++++++-------------------- cc/sdk.go | 23 ++-- multitree/api_imports.go | 13 ++- 6 files changed, 520 insertions(+), 265 deletions(-) diff --git a/apex/apex_test.go b/apex/apex_test.go index b7febe17f..0415a6518 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -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) { diff --git a/cc/cc.go b/cc/cc.go index c07d836a6..0e88c5686 100644 --- a/cc/cc.go +++ b/cc/cc.go @@ -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 diff --git a/cc/library_stub.go b/cc/library_stub.go index 08a5eb6ae..18d3f210c 100644 --- a/cc/library_stub.go +++ b/cc/library_stub.go @@ -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 } diff --git a/cc/library_stub_test.go b/cc/library_stub_test.go index 868447a8f..528577a21 100644 --- a/cc/library_stub_test.go +++ b/cc/library_stub_test.go @@ -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) diff --git a/cc/sdk.go b/cc/sdk.go index 23dd0cb2d..4f361eb16 100644 --- a/cc/sdk.go +++ b/cc/sdk.go @@ -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 diff --git a/multitree/api_imports.go b/multitree/api_imports.go index 6674d3e57..07ec7bc10 100644 --- a/multitree/api_imports.go +++ b/multitree/api_imports.go @@ -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, }) }