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:
parent
2098eb8c2a
commit
25fc6a9cc9
|
@ -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"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
22
apex/apex.go
22
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
|
// 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})
|
||||||
|
|
|
@ -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
112
cc/cc.go
|
@ -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()
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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("")
|
||||||
|
|
Loading…
Reference in New Issue