Stubs variant is used when building for APEX

When a native module is built for an APEX and is depending on a native
library having stubs (i.e. stubs.versions property is set), the stubs
variant is used unless the dependent lib is directly included in the
same APEX with the depending module.

Example:

apex {
    name: "myapex",
    native_shared_libs: ["libX", "libY"],
}

cc_library {
    name: "libX",
    shared_libs: ["libY", "libZ"],
}

cc_library {
    name: "libY",
    stubs: { versions: ["1", "2"], },
}

cc_library {
    name: "libZ",
    stubs: { versions: ["1", "2"], },
}

In this case, libX is linking to the impl variant of libY (that provides
private APIs) while libY is linking to the version 2 stubs of libZ. This is
because libY is directly included in the same apex via
native_shared_libs property, but libZ isn't.

Bug: 112672359
Test: apex_test added
Change-Id: If9871b70dc74a06bd828dd4cd1aeebd2e68b837c
This commit is contained in:
Jiyong Park 2018-11-18 18:02:45 +09:00
parent 2098eb8c2a
commit 25fc6a9cc9
7 changed files with 589 additions and 42 deletions

View File

@ -353,6 +353,9 @@ bootstrap_go_package {
"apex/apex.go", "apex/apex.go",
"apex/key.go", "apex/key.go",
], ],
testSrcs: [
"apex/apex_test.go",
],
pluginFor: ["soong_build"], pluginFor: ["soong_build"],
} }

View File

@ -14,6 +14,8 @@
package android package android
import "sync"
// ApexModule is the interface that a module type is expected to implement if // 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 // 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). // 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 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) { func InitApexModule(m ApexModule) {
base := m.apexModuleBase() base := m.apexModuleBase()
base.canHaveApexVariants = true base.canHaveApexVariants = true

View File

@ -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 // Mark the direct and transitive dependencies of apex bundles so that they
// can be built for the apex bundles. // can be built for the apex bundles.
func apexDepsMutator(mctx android.TopDownMutatorContext) { func apexDepsMutator(mctx android.TopDownMutatorContext) {
@ -143,12 +136,9 @@ func apexDepsMutator(mctx android.TopDownMutatorContext) {
mctx.WalkDeps(func(child, parent android.Module) bool { mctx.WalkDeps(func(child, parent android.Module) bool {
if am, ok := child.(android.ApexModule); ok && am.CanHaveApexVariants() { if am, ok := child.(android.ApexModule); ok && am.CanHaveApexVariants() {
moduleName := mctx.OtherModuleName(am) + "-" + am.Target().String() moduleName := mctx.OtherModuleName(am) + "-" + am.Target().String()
bundleNames, ok := apexBundleNamesFor(mctx.Config())[moduleName] // If the parent is apexBundle, this child is directly depended.
if !ok { _, directDep := parent.(*apexBundle)
bundleNames = make(map[string]bool) android.BuildModuleForApexBundle(mctx, moduleName, apexBundleName, directDep)
apexBundleNamesFor(mctx.Config())[moduleName] = bundleNames
}
bundleNames[apexBundleName] = true
return true return true
} else { } else {
return false return false
@ -161,7 +151,8 @@ func apexDepsMutator(mctx android.TopDownMutatorContext) {
func apexMutator(mctx android.BottomUpMutatorContext) { func apexMutator(mctx android.BottomUpMutatorContext) {
if am, ok := mctx.Module().(android.ApexModule); ok && am.CanHaveApexVariants() { if am, ok := mctx.Module().(android.ApexModule); ok && am.CanHaveApexVariants() {
moduleName := mctx.ModuleName() + "-" + am.Target().String() 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"} variations := []string{"platform"}
for bn := range bundleNames { for bn := range bundleNames {
variations = append(variations, bn) variations = append(variations, bn)
@ -495,6 +486,9 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
// indirect dependencies // indirect dependencies
if am, ok := child.(android.ApexModule); ok && am.CanHaveApexVariants() && am.IsInstallableToApex() { if am, ok := child.(android.ApexModule); ok && am.CanHaveApexVariants() && am.IsInstallableToApex() {
if cc, ok := child.(*cc.Module); ok { if cc, ok := child.(*cc.Module); ok {
if cc.IsStubs() || cc.HasStubsVariants() {
return false
}
depName := ctx.OtherModuleName(child) depName := ctx.OtherModuleName(child)
fileToCopy, dirInApex := getCopyManifestForNativeLibrary(cc) fileToCopy, dirInApex := getCopyManifestForNativeLibrary(cc)
filesInfo = append(filesInfo, apexFile{fileToCopy, depName, cc.Arch().ArchType, dirInApex, nativeSharedLib}) filesInfo = append(filesInfo, apexFile{fileToCopy, depName, cc.Arch().ArchType, dirInApex, nativeSharedLib})

369
apex/apex_test.go Normal file
View File

@ -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")
}

112
cc/cc.go
View File

@ -39,7 +39,7 @@ func init() {
ctx.BottomUp("vndk", vndkMutator).Parallel() ctx.BottomUp("vndk", vndkMutator).Parallel()
ctx.BottomUp("ndk_api", ndkApiMutator).Parallel() ctx.BottomUp("ndk_api", ndkApiMutator).Parallel()
ctx.BottomUp("test_per_src", testPerSrcMutator).Parallel() ctx.BottomUp("test_per_src", testPerSrcMutator).Parallel()
ctx.BottomUp("version", versionMutator).Parallel() ctx.BottomUp("version", VersionMutator).Parallel()
ctx.BottomUp("begin", BeginMutator).Parallel() ctx.BottomUp("begin", BeginMutator).Parallel()
}) })
@ -248,6 +248,7 @@ type ModuleContextIntf interface {
getVndkExtendsModuleName() string getVndkExtendsModuleName() string
isPgoCompile() bool isPgoCompile() bool
useClangLld(actx ModuleContext) bool useClangLld(actx ModuleContext) bool
isApex() bool
} }
type ModuleContext interface { type ModuleContext interface {
@ -308,6 +309,8 @@ type dependencyTag struct {
library bool library bool
reexportFlags bool reexportFlags bool
explicitlyVersioned bool
} }
var ( var (
@ -511,6 +514,20 @@ func (c *Module) onlyInRecovery() bool {
return c.ModuleBase.InstallInRecovery() 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 { type baseModuleContext struct {
android.BaseContext android.BaseContext
moduleContextImpl moduleContextImpl
@ -649,6 +666,11 @@ func (ctx *moduleContextImpl) getVndkExtendsModuleName() string {
return ctx.mod.getVndkExtendsModuleName() 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 { func newBaseModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module {
return &Module{ return &Module{
hod: hod, hod: hod,
@ -1081,6 +1103,30 @@ func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) {
{Mutator: "link", Variation: "static"}, {Mutator: "link", Variation: "static"},
}, lateStaticDepTag, deps.LateStaticLibs...) }, 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 // shared lib names without the #version suffix
var sharedLibNames []string var sharedLibNames []string
@ -1091,29 +1137,17 @@ func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) {
if inList(lib, deps.ReexportSharedLibHeaders) { if inList(lib, deps.ReexportSharedLibHeaders) {
depTag = sharedExportDepTag depTag = sharedExportDepTag
} }
var variations []blueprint.Variation addSharedLibDependencies(depTag, name, version)
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)
} }
for _, lib := range deps.LateSharedLibs { for _, lib := range deps.LateSharedLibs {
name, version := stubsLibNameAndVersion(lib) if inList(lib, sharedLibNames) {
if inList(name, sharedLibNames) {
// This is to handle the case that some of the late shared libs (libc, libdl, libm, ...) // 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 // 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. // linking against both the stubs lib and the non-stubs lib at the same time.
continue continue
} }
depTag := lateSharedDepTag addSharedLibDependencies(lateSharedDepTag, lib, "")
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)
} }
actx.AddVariationDependencies([]blueprint.Variation{ actx.AddVariationDependencies([]blueprint.Variation{
@ -1372,7 +1406,53 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
return 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 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 { if i, ok := ccDep.linker.(exportedFlagsProducer); ok {
flags := i.exportedFlags() flags := i.exportedFlags()
deps := i.exportedFlagsDeps() deps := i.exportedFlagsDeps()

View File

@ -66,7 +66,7 @@ func createTestContext(t *testing.T, config android.Config, bp string) *android.
ctx.BottomUp("image", imageMutator).Parallel() ctx.BottomUp("image", imageMutator).Parallel()
ctx.BottomUp("link", LinkageMutator).Parallel() ctx.BottomUp("link", LinkageMutator).Parallel()
ctx.BottomUp("vndk", vndkMutator).Parallel() ctx.BottomUp("vndk", vndkMutator).Parallel()
ctx.BottomUp("version", versionMutator).Parallel() ctx.BottomUp("version", VersionMutator).Parallel()
ctx.BottomUp("begin", BeginMutator).Parallel() ctx.BottomUp("begin", BeginMutator).Parallel()
}) })
ctx.Register() ctx.Register()

View File

@ -16,6 +16,8 @@ package cc
import ( import (
"regexp" "regexp"
"sort"
"strconv"
"strings" "strings"
"sync" "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 // Version mutator splits a module into the mandatory non-stubs variant
// (which is named "impl") and zero or more stubs variants. // (which is unnamed) and zero or more stubs variants.
func versionMutator(mctx android.BottomUpMutatorContext) { func VersionMutator(mctx android.BottomUpMutatorContext) {
if mctx.Os() != android.Android { if mctx.Os() != android.Android {
return return
} }
if m, ok := mctx.Module().(*Module); ok && !m.inRecovery() && m.linker != nil { if m, ok := mctx.Module().(*Module); ok && !m.inRecovery() && m.linker != nil {
if library, ok := m.linker.(*libraryDecorator); ok && library.buildShared() { if library, ok := m.linker.(*libraryDecorator); ok && library.buildShared() &&
versions := []string{""} len(library.Properties.Stubs.Versions) > 0 {
versions := []string{}
for _, v := range library.Properties.Stubs.Versions { 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) 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...) modules := mctx.CreateVariations(versions...)
for i, m := range modules { for i, m := range modules {
l := m.(*Module).linker.(*libraryDecorator) l := m.(*Module).linker.(*libraryDecorator)
if i == 0 { if versions[i] != "" {
l.MutatedProperties.BuildStubs = false l.MutatedProperties.BuildStubs = true
continue 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 { } else {
mctx.CreateVariations("") mctx.CreateVariations("")