diff --git a/apex/apex_test.go b/apex/apex_test.go index c94bbbb32..707cc632a 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -9788,30 +9788,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) @@ -9827,17 +9882,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, }) }