diff options
author | Jiyong Park <jiyong@google.com> | 2018-12-04 14:07:56 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2018-12-04 14:07:56 +0000 |
commit | ce16f3bac8ed6719a52cfa77463b8df78ad0c7b5 (patch) | |
tree | c06828e382ec2ffb38fb05a8523f5a853ab64c9a | |
parent | 3a33fe535eb6ee4f67b9840cf28a33e3d9edbdee (diff) | |
parent | 25fc6a9cc9b665882f6a422462cbc5d0670af87c (diff) | |
download | build_soong-ce16f3bac8ed6719a52cfa77463b8df78ad0c7b5.tar.gz build_soong-ce16f3bac8ed6719a52cfa77463b8df78ad0c7b5.tar.bz2 build_soong-ce16f3bac8ed6719a52cfa77463b8df78ad0c7b5.zip |
Merge "Stubs variant is used when building for APEX"
-rw-r--r-- | Android.bp | 3 | ||||
-rw-r--r-- | android/apex.go | 64 | ||||
-rw-r--r-- | apex/apex.go | 22 | ||||
-rw-r--r-- | apex/apex_test.go | 369 | ||||
-rw-r--r-- | cc/cc.go | 112 | ||||
-rw-r--r-- | cc/cc_test.go | 2 | ||||
-rw-r--r-- | cc/library.go | 59 |
7 files changed, 589 insertions, 42 deletions
@@ -353,6 +353,9 @@ bootstrap_go_package { "apex/apex.go", "apex/key.go", ], + testSrcs: [ + "apex/apex_test.go", + ], pluginFor: ["soong_build"], } diff --git a/android/apex.go b/android/apex.go index dae88cec..3a191cf6 100644 --- a/android/apex.go +++ b/android/apex.go @@ -14,6 +14,8 @@ package android +import "sync" + // ApexModule is the interface that a module type is expected to implement if // the module has to be built differently depending on whether the module // is destined for an apex or not (installed to one of the regular partitions). @@ -94,6 +96,68 @@ func (m *ApexModuleBase) IsInstallableToApex() bool { return false } +// This structure maps a module name to the set of APEX bundle names that the module +// should be built for. Examples: +// +// ...["foo"]["bar"] == true: module foo is directly depended on by APEX bar +// ...["foo"]["bar"] == false: module foo is indirectly depended on by APEX bar +// ...["foo"]["bar"] doesn't exist: foo is not built for APEX bar +// ...["foo"] doesn't exist: foo is not built for any APEX +func apexBundleNamesMap(config Config) map[string]map[string]bool { + return config.Once("apexBundleNames", func() interface{} { + return make(map[string]map[string]bool) + }).(map[string]map[string]bool) +} + +var bundleNamesMapMutex sync.Mutex + +// Mark that a module named moduleName should be built for an apex named bundleName +// directDep should be set to true if the module is a direct dependency of the apex. +func BuildModuleForApexBundle(ctx BaseModuleContext, moduleName string, bundleName string, directDep bool) { + bundleNamesMapMutex.Lock() + defer bundleNamesMapMutex.Unlock() + bundleNames, ok := apexBundleNamesMap(ctx.Config())[moduleName] + if !ok { + bundleNames = make(map[string]bool) + apexBundleNamesMap(ctx.Config())[moduleName] = bundleNames + } + bundleNames[bundleName] = bundleNames[bundleName] || directDep +} + +// Returns the list of apex bundle names that the module named moduleName +// should be built for. +func GetApexBundlesForModule(ctx BaseModuleContext, moduleName string) map[string]bool { + bundleNamesMapMutex.Lock() + defer bundleNamesMapMutex.Unlock() + return apexBundleNamesMap(ctx.Config())[moduleName] +} + +// Tests if moduleName is directly depended on by bundleName (i.e. referenced in +// native_shared_libs, etc.) +func DirectlyInApex(config Config, bundleName string, moduleName string) bool { + bundleNamesMapMutex.Lock() + defer bundleNamesMapMutex.Unlock() + if bundleNames, ok := apexBundleNamesMap(config)[moduleName]; ok { + return bundleNames[bundleName] + } + return false +} + +// Tests if moduleName is directly depended on by any APEX. If this returns true, +// that means the module is part of the platform. +func DirectlyInAnyApex(config Config, moduleName string) bool { + bundleNamesMapMutex.Lock() + defer bundleNamesMapMutex.Unlock() + if bundleNames, ok := apexBundleNamesMap(config)[moduleName]; ok { + for bn := range bundleNames { + if bundleNames[bn] { + return true + } + } + } + return false +} + func InitApexModule(m ApexModule) { base := m.apexModuleBase() base.canHaveApexVariants = true diff --git a/apex/apex.go b/apex/apex.go index 8a652db0..52fa561d 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -128,13 +128,6 @@ func init() { }) } -// maps a module name to set of apex bundle names that the module should be built for -func apexBundleNamesFor(config android.Config) map[string]map[string]bool { - return config.Once("apexBundleNames", func() interface{} { - return make(map[string]map[string]bool) - }).(map[string]map[string]bool) -} - // Mark the direct and transitive dependencies of apex bundles so that they // can be built for the apex bundles. func apexDepsMutator(mctx android.TopDownMutatorContext) { @@ -143,12 +136,9 @@ func apexDepsMutator(mctx android.TopDownMutatorContext) { mctx.WalkDeps(func(child, parent android.Module) bool { if am, ok := child.(android.ApexModule); ok && am.CanHaveApexVariants() { moduleName := mctx.OtherModuleName(am) + "-" + am.Target().String() - bundleNames, ok := apexBundleNamesFor(mctx.Config())[moduleName] - if !ok { - bundleNames = make(map[string]bool) - apexBundleNamesFor(mctx.Config())[moduleName] = bundleNames - } - bundleNames[apexBundleName] = true + // If the parent is apexBundle, this child is directly depended. + _, directDep := parent.(*apexBundle) + android.BuildModuleForApexBundle(mctx, moduleName, apexBundleName, directDep) return true } else { return false @@ -161,7 +151,8 @@ func apexDepsMutator(mctx android.TopDownMutatorContext) { func apexMutator(mctx android.BottomUpMutatorContext) { if am, ok := mctx.Module().(android.ApexModule); ok && am.CanHaveApexVariants() { moduleName := mctx.ModuleName() + "-" + am.Target().String() - if bundleNames, ok := apexBundleNamesFor(mctx.Config())[moduleName]; ok { + bundleNames := android.GetApexBundlesForModule(mctx, moduleName) + if len(bundleNames) > 0 { variations := []string{"platform"} for bn := range bundleNames { variations = append(variations, bn) @@ -495,6 +486,9 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { // indirect dependencies if am, ok := child.(android.ApexModule); ok && am.CanHaveApexVariants() && am.IsInstallableToApex() { if cc, ok := child.(*cc.Module); ok { + if cc.IsStubs() || cc.HasStubsVariants() { + return false + } depName := ctx.OtherModuleName(child) fileToCopy, dirInApex := getCopyManifestForNativeLibrary(cc) filesInfo = append(filesInfo, apexFile{fileToCopy, depName, cc.Arch().ArchType, dirInApex, nativeSharedLib}) diff --git a/apex/apex_test.go b/apex/apex_test.go new file mode 100644 index 00000000..41d8455d --- /dev/null +++ b/apex/apex_test.go @@ -0,0 +1,369 @@ +// Copyright 2018 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package apex + +import ( + "android/soong/android" + "android/soong/cc" + "io/ioutil" + "os" + "strings" + "testing" +) + +func testApex(t *testing.T, bp string) *android.TestContext { + config, buildDir := setup(t) + defer teardown(buildDir) + + ctx := android.NewTestArchContext() + ctx.RegisterModuleType("apex", android.ModuleFactoryAdaptor(apexBundleFactory)) + ctx.RegisterModuleType("apex_key", android.ModuleFactoryAdaptor(apexKeyFactory)) + + ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) { + ctx.TopDown("apex_deps", apexDepsMutator) + ctx.BottomUp("apex", apexMutator) + }) + + ctx.RegisterModuleType("cc_library", android.ModuleFactoryAdaptor(cc.LibraryFactory)) + ctx.RegisterModuleType("cc_library_shared", android.ModuleFactoryAdaptor(cc.LibrarySharedFactory)) + ctx.RegisterModuleType("cc_object", android.ModuleFactoryAdaptor(cc.ObjectFactory)) + ctx.RegisterModuleType("toolchain_library", android.ModuleFactoryAdaptor(cc.ToolchainLibraryFactory)) + ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) { + ctx.BottomUp("link", cc.LinkageMutator).Parallel() + ctx.BottomUp("version", cc.VersionMutator).Parallel() + ctx.BottomUp("begin", cc.BeginMutator).Parallel() + }) + + ctx.Register() + + bp = bp + ` + toolchain_library { + name: "libcompiler_rt-extras", + src: "", + } + + toolchain_library { + name: "libatomic", + src: "", + } + + toolchain_library { + name: "libgcc", + src: "", + } + + toolchain_library { + name: "libclang_rt.builtins-aarch64-android", + src: "", + } + + toolchain_library { + name: "libclang_rt.builtins-arm-android", + src: "", + } + + cc_object { + name: "crtbegin_so", + stl: "none", + } + + cc_object { + name: "crtend_so", + stl: "none", + } + + ` + + ctx.MockFileSystem(map[string][]byte{ + "Android.bp": []byte(bp), + "testkey.avbpubkey": nil, + "testkey.pem": nil, + "build/target/product/security": nil, + "apex_manifest.json": nil, + "system/sepolicy/apex/myapex-file_contexts": nil, + "mylib.cpp": nil, + }) + _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) + android.FailIfErrored(t, errs) + _, errs = ctx.PrepareBuildActions(config) + android.FailIfErrored(t, errs) + + return ctx +} + +func setup(t *testing.T) (config android.Config, buildDir string) { + buildDir, err := ioutil.TempDir("", "soong_apex_test") + if err != nil { + t.Fatal(err) + } + + config = android.TestArchConfig(buildDir, nil) + + return +} + +func teardown(buildDir string) { + os.RemoveAll(buildDir) +} + +// ensure that 'result' contains 'expected' +func ensureContains(t *testing.T, result string, expected string) { + if !strings.Contains(result, expected) { + t.Errorf("%q is not found in %q", expected, result) + } +} + +// ensures that 'result' does not contain 'notExpected' +func ensureNotContains(t *testing.T, result string, notExpected string) { + if strings.Contains(result, notExpected) { + t.Errorf("%q is found in %q", notExpected, result) + } +} + +func ensureListContains(t *testing.T, result []string, expected string) { + if !android.InList(expected, result) { + t.Errorf("%q is not found in %v", expected, result) + } +} + +func ensureListNotContains(t *testing.T, result []string, notExpected string) { + if android.InList(notExpected, result) { + t.Errorf("%q is found in %v", notExpected, result) + } +} + +// Minimal test +func TestBasicApex(t *testing.T) { + ctx := testApex(t, ` + apex { + name: "myapex", + key: "myapex.key", + native_shared_libs: ["mylib"], + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + cc_library { + name: "mylib", + srcs: ["mylib.cpp"], + shared_libs: ["mylib2"], + system_shared_libs: [], + stl: "none", + } + + cc_library { + name: "mylib2", + srcs: ["mylib.cpp"], + system_shared_libs: [], + stl: "none", + } + `) + + apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule") + copyCmds := apexRule.Args["copy_commands"] + + // Ensure that main rule creates an output + ensureContains(t, apexRule.Output.String(), "myapex.apex.unsigned") + + // Ensure that apex variant is created for the direct dep + ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_shared_myapex") + + // Ensure that apex variant is created for the indirect dep + ensureListContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_shared_myapex") + + // Ensure that both direct and indirect deps are copied into apex + ensureContains(t, copyCmds, "image/lib64/mylib.so") + ensureContains(t, copyCmds, "image/lib64/mylib2.so") +} + +func TestApexWithStubs(t *testing.T) { + ctx := testApex(t, ` + apex { + name: "myapex", + key: "myapex.key", + native_shared_libs: ["mylib", "mylib3"], + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + cc_library { + name: "mylib", + srcs: ["mylib.cpp"], + shared_libs: ["mylib2", "mylib3"], + system_shared_libs: [], + stl: "none", + } + + cc_library { + name: "mylib2", + srcs: ["mylib.cpp"], + system_shared_libs: [], + stl: "none", + stubs: { + versions: ["1", "2", "3"], + }, + } + + cc_library { + name: "mylib3", + srcs: ["mylib.cpp"], + system_shared_libs: [], + stl: "none", + stubs: { + versions: ["10", "11", "12"], + }, + } + `) + + apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule") + copyCmds := apexRule.Args["copy_commands"] + + // Ensure that direct non-stubs dep is always included + ensureContains(t, copyCmds, "image/lib64/mylib.so") + + // Ensure that indirect stubs dep is not included + ensureNotContains(t, copyCmds, "image/lib64/mylib2.so") + + // Ensure that direct stubs dep is included + ensureContains(t, copyCmds, "image/lib64/mylib3.so") + + mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_myapex").Rule("ld").Args["libFlags"] + + // Ensure that mylib is linking with the latest version of stubs for mylib2 + ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_3_myapex/mylib2.so") + // ... and not linking to the non-stub (impl) variant of mylib2 + ensureNotContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_myapex/mylib2.so") + + // Ensure that mylib is linking with the non-stub (impl) of mylib3 (because mylib3 is in the same apex) + ensureContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_shared_myapex/mylib3.so") + // .. and not linking to the stubs variant of mylib3 + ensureNotContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_shared_12_myapex/mylib3.so") +} + +func TestApexWithSystemLibsStubs(t *testing.T) { + ctx := testApex(t, ` + apex { + name: "myapex", + key: "myapex.key", + native_shared_libs: ["mylib", "mylib_shared", "libdl", "libm"], + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + cc_library { + name: "mylib", + srcs: ["mylib.cpp"], + shared_libs: ["libdl#27"], + stl: "none", + } + + cc_library_shared { + name: "mylib_shared", + srcs: ["mylib.cpp"], + shared_libs: ["libdl#27"], + stl: "none", + } + + cc_library { + name: "libc", + no_libgcc: true, + nocrt: true, + system_shared_libs: [], + stl: "none", + stubs: { + versions: ["27", "28", "29"], + }, + } + + cc_library { + name: "libm", + no_libgcc: true, + nocrt: true, + system_shared_libs: [], + stl: "none", + stubs: { + versions: ["27", "28", "29"], + }, + } + + cc_library { + name: "libdl", + no_libgcc: true, + nocrt: true, + system_shared_libs: [], + stl: "none", + stubs: { + versions: ["27", "28", "29"], + }, + } + `) + + apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule") + copyCmds := apexRule.Args["copy_commands"] + + // Ensure that mylib, libm, libdl are included. + ensureContains(t, copyCmds, "image/lib64/mylib.so") + ensureContains(t, copyCmds, "image/lib64/libm.so") + ensureContains(t, copyCmds, "image/lib64/libdl.so") + + // Ensure that libc is not included (since it has stubs and not listed in native_shared_libs) + ensureNotContains(t, copyCmds, "image/lib64/libc.so") + + mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_myapex").Rule("ld").Args["libFlags"] + mylibCFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_myapex").Rule("cc").Args["cFlags"] + mylibSharedCFlags := ctx.ModuleForTests("mylib_shared", "android_arm64_armv8-a_shared_myapex").Rule("cc").Args["cFlags"] + + // For dependency to libc + // Ensure that mylib is linking with the latest version of stubs + ensureContains(t, mylibLdFlags, "libc/android_arm64_armv8-a_shared_29_myapex/libc.so") + // ... and not linking to the non-stub (impl) variant + ensureNotContains(t, mylibLdFlags, "libc/android_arm64_armv8-a_shared_myapex/libc.so") + // ... Cflags from stub is correctly exported to mylib + ensureContains(t, mylibCFlags, "__LIBC_API__=29") + ensureContains(t, mylibSharedCFlags, "__LIBC_API__=29") + + // For dependency to libm + // Ensure that mylib is linking with the non-stub (impl) variant + ensureContains(t, mylibLdFlags, "libm/android_arm64_armv8-a_shared_myapex/libm.so") + // ... and not linking to the stub variant + ensureNotContains(t, mylibLdFlags, "libm/android_arm64_armv8-a_shared_29_myapex/libm.so") + // ... and is not compiling with the stub + ensureNotContains(t, mylibCFlags, "__LIBM_API__=29") + ensureNotContains(t, mylibSharedCFlags, "__LIBM_API__=29") + + // For dependency to libdl + // Ensure that mylib is linking with the specified version of stubs + ensureContains(t, mylibLdFlags, "libdl/android_arm64_armv8-a_shared_27_myapex/libdl.so") + // ... and not linking to the other versions of stubs + ensureNotContains(t, mylibLdFlags, "libdl/android_arm64_armv8-a_shared_28_myapex/libdl.so") + ensureNotContains(t, mylibLdFlags, "libdl/android_arm64_armv8-a_shared_29_myapex/libdl.so") + // ... and not linking to the non-stub (impl) variant + ensureNotContains(t, mylibLdFlags, "libdl/android_arm64_armv8-a_shared_myapex/libdl.so") + // ... Cflags from stub is correctly exported to mylib + ensureContains(t, mylibCFlags, "__LIBDL_API__=27") + ensureContains(t, mylibSharedCFlags, "__LIBDL_API__=27") +} @@ -39,7 +39,7 @@ func init() { ctx.BottomUp("vndk", vndkMutator).Parallel() ctx.BottomUp("ndk_api", ndkApiMutator).Parallel() ctx.BottomUp("test_per_src", testPerSrcMutator).Parallel() - ctx.BottomUp("version", versionMutator).Parallel() + ctx.BottomUp("version", VersionMutator).Parallel() ctx.BottomUp("begin", BeginMutator).Parallel() }) @@ -248,6 +248,7 @@ type ModuleContextIntf interface { getVndkExtendsModuleName() string isPgoCompile() bool useClangLld(actx ModuleContext) bool + isApex() bool } type ModuleContext interface { @@ -308,6 +309,8 @@ type dependencyTag struct { library bool reexportFlags bool + + explicitlyVersioned bool } var ( @@ -511,6 +514,20 @@ func (c *Module) onlyInRecovery() bool { return c.ModuleBase.InstallInRecovery() } +func (c *Module) IsStubs() bool { + if library, ok := c.linker.(*libraryDecorator); ok { + return library.buildStubs() + } + return false +} + +func (c *Module) HasStubsVariants() bool { + if library, ok := c.linker.(*libraryDecorator); ok { + return len(library.Properties.Stubs.Versions) > 0 + } + return false +} + type baseModuleContext struct { android.BaseContext moduleContextImpl @@ -649,6 +666,11 @@ func (ctx *moduleContextImpl) getVndkExtendsModuleName() string { return ctx.mod.getVndkExtendsModuleName() } +// Tests if this module is built for APEX +func (ctx *moduleContextImpl) isApex() bool { + return ctx.mod.ApexName() != "" +} + func newBaseModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module { return &Module{ hod: hod, @@ -1081,6 +1103,30 @@ func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) { {Mutator: "link", Variation: "static"}, }, lateStaticDepTag, deps.LateStaticLibs...) + addSharedLibDependencies := func(depTag dependencyTag, name string, version string) { + var variations []blueprint.Variation + variations = append(variations, blueprint.Variation{Mutator: "link", Variation: "shared"}) + versionVariantAvail := ctx.Os() == android.Android && !ctx.useVndk() && !c.inRecovery() + if version != "" && versionVariantAvail { + // Version is explicitly specified. i.e. libFoo#30 + variations = append(variations, blueprint.Variation{Mutator: "version", Variation: version}) + depTag.explicitlyVersioned = true + } + actx.AddVariationDependencies(variations, depTag, name) + + // If the version is not specified, add dependency to the latest stubs library. + // The stubs library will be used when the depending module is built for APEX and + // the dependent module is not in the same APEX. + latestVersion := latestStubsVersionFor(actx.Config(), name) + if version == "" && latestVersion != "" && versionVariantAvail { + actx.AddVariationDependencies([]blueprint.Variation{ + {Mutator: "link", Variation: "shared"}, + {Mutator: "version", Variation: latestVersion}, + }, depTag, name) + // Note that depTag.explicitlyVersioned is false in this case. + } + } + // shared lib names without the #version suffix var sharedLibNames []string @@ -1091,29 +1137,17 @@ func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) { if inList(lib, deps.ReexportSharedLibHeaders) { depTag = sharedExportDepTag } - var variations []blueprint.Variation - variations = append(variations, blueprint.Variation{Mutator: "link", Variation: "shared"}) - if version != "" && ctx.Os() == android.Android && !ctx.useVndk() && !c.inRecovery() { - variations = append(variations, blueprint.Variation{Mutator: "version", Variation: version}) - } - actx.AddVariationDependencies(variations, depTag, name) + addSharedLibDependencies(depTag, name, version) } for _, lib := range deps.LateSharedLibs { - name, version := stubsLibNameAndVersion(lib) - if inList(name, sharedLibNames) { + if inList(lib, sharedLibNames) { // This is to handle the case that some of the late shared libs (libc, libdl, libm, ...) // are added also to SharedLibs with version (e.g., libc#10). If not skipped, we will be // linking against both the stubs lib and the non-stubs lib at the same time. continue } - depTag := lateSharedDepTag - var variations []blueprint.Variation - variations = append(variations, blueprint.Variation{Mutator: "link", Variation: "shared"}) - if version != "" && ctx.Os() == android.Android && !ctx.useVndk() && !c.inRecovery() { - variations = append(variations, blueprint.Variation{Mutator: "version", Variation: version}) - } - actx.AddVariationDependencies(variations, depTag, name) + addSharedLibDependencies(lateSharedDepTag, lib, "") } actx.AddVariationDependencies([]blueprint.Variation{ @@ -1372,7 +1406,53 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps { return } } + + // Extract explicitlyVersioned field from the depTag and reset it inside the struct. + // Otherwise, sharedDepTag and lateSharedDepTag with explicitlyVersioned set to true + // won't be matched to sharedDepTag and lateSharedDepTag. + explicitlyVersioned := false + if t, ok := depTag.(dependencyTag); ok { + explicitlyVersioned = t.explicitlyVersioned + t.explicitlyVersioned = false + depTag = t + } + if t, ok := depTag.(dependencyTag); ok && t.library { + if dependentLibrary, ok := ccDep.linker.(*libraryDecorator); ok { + depIsStubs := dependentLibrary.buildStubs() + depHasStubs := ccDep.HasStubsVariants() + depNameWithTarget := depName + "-" + ccDep.Target().String() + depInSameApex := android.DirectlyInApex(ctx.Config(), c.ApexName(), depNameWithTarget) + depInPlatform := !android.DirectlyInAnyApex(ctx.Config(), depNameWithTarget) + + var useThisDep bool + if depIsStubs && explicitlyVersioned { + // Always respect dependency to the versioned stubs (i.e. libX#10) + useThisDep = true + } else if !depHasStubs { + // Use non-stub variant if that is the only choice + // (i.e. depending on a lib without stubs.version property) + useThisDep = true + } else if c.IsForPlatform() { + // If not building for APEX, use stubs only when it is from + // an APEX (and not from platform) + useThisDep = (depInPlatform != depIsStubs) + if c.inRecovery() { + // However, for recovery modules, since there is no APEX there, + // always link to non-stub variant + useThisDep = !depIsStubs + } + } else { + // If building for APEX, use stubs only when it is not from + // the same APEX + useThisDep = (depInSameApex != depIsStubs) + } + + if !useThisDep { + return // stop processing this dep + } + } + if i, ok := ccDep.linker.(exportedFlagsProducer); ok { flags := i.exportedFlags() deps := i.exportedFlagsDeps() diff --git a/cc/cc_test.go b/cc/cc_test.go index 41f633a9..e368fb34 100644 --- a/cc/cc_test.go +++ b/cc/cc_test.go @@ -66,7 +66,7 @@ func createTestContext(t *testing.T, config android.Config, bp string) *android. ctx.BottomUp("image", imageMutator).Parallel() ctx.BottomUp("link", LinkageMutator).Parallel() ctx.BottomUp("vndk", vndkMutator).Parallel() - ctx.BottomUp("version", versionMutator).Parallel() + ctx.BottomUp("version", VersionMutator).Parallel() ctx.BottomUp("begin", BeginMutator).Parallel() }) ctx.Register() diff --git a/cc/library.go b/cc/library.go index 8b8fe023..822274a1 100644 --- a/cc/library.go +++ b/cc/library.go @@ -16,6 +16,8 @@ package cc import ( "regexp" + "sort" + "strconv" "strings" "sync" @@ -981,30 +983,65 @@ func LinkageMutator(mctx android.BottomUpMutatorContext) { } } +// maps a module name to the list of stubs versions available for the module +func stubsVersionsFor(config android.Config) map[string][]string { + return config.Once("stubVersions", func() interface{} { + return make(map[string][]string) + }).(map[string][]string) +} + +var stubsVersionsLock sync.Mutex + +func latestStubsVersionFor(config android.Config, name string) string { + versions, ok := stubsVersionsFor(config)[name] + if ok && len(versions) > 0 { + // the versions are alreay sorted in ascending order + return versions[len(versions)-1] + } + return "" +} + // Version mutator splits a module into the mandatory non-stubs variant -// (which is named "impl") and zero or more stubs variants. -func versionMutator(mctx android.BottomUpMutatorContext) { +// (which is unnamed) and zero or more stubs variants. +func VersionMutator(mctx android.BottomUpMutatorContext) { if mctx.Os() != android.Android { return } if m, ok := mctx.Module().(*Module); ok && !m.inRecovery() && m.linker != nil { - if library, ok := m.linker.(*libraryDecorator); ok && library.buildShared() { - versions := []string{""} + if library, ok := m.linker.(*libraryDecorator); ok && library.buildShared() && + len(library.Properties.Stubs.Versions) > 0 { + versions := []string{} for _, v := range library.Properties.Stubs.Versions { + if _, err := strconv.Atoi(v); err != nil { + mctx.PropertyErrorf("versions", "%q is not a number", v) + } versions = append(versions, v) } + sort.Slice(versions, func(i, j int) bool { + left, _ := strconv.Atoi(versions[i]) + right, _ := strconv.Atoi(versions[j]) + return left < right + }) + + // save the list of versions for later use + copiedVersions := make([]string, len(versions)) + copy(copiedVersions, versions) + stubsVersionsLock.Lock() + defer stubsVersionsLock.Unlock() + stubsVersionsFor(mctx.Config())[mctx.ModuleName()] = copiedVersions + + // "" is for the non-stubs variant + versions = append(versions, "") + modules := mctx.CreateVariations(versions...) for i, m := range modules { l := m.(*Module).linker.(*libraryDecorator) - if i == 0 { - l.MutatedProperties.BuildStubs = false - continue + if versions[i] != "" { + l.MutatedProperties.BuildStubs = true + l.MutatedProperties.StubsVersion = versions[i] + m.(*Module).Properties.HideFromMake = true } - // Mark that this variant is for stubs. - l.MutatedProperties.BuildStubs = true - l.MutatedProperties.StubsVersion = versions[i] - m.(*Module).Properties.HideFromMake = true } } else { mctx.CreateVariations("") |