aede88c1c7
APEX variants that share the same SDK version and updatability almost always use identical command line arguments to build but with different intermediates directories. This causes unnecessary build time and disk space for duplicated work. Deduplicate APEX variants that would build identically. Create aliases from the per-APEX variations to the new shared variations so that the APEX modules can continue to depend on them via the APEX name as the variation. This has one significant change in behavior. Before this change, if an APEX had two libraries in its direct dependencies and one of those libraries depended on the other, and the second library had stubs, then the first library would depend on the implementation of the second library and not the stubs. After this change, if the first library is also present in a second APEX but the second library is not, then the common variant shared between the two APEXes would use the stubs, not the implementation. In a correctly configured set of build rules this change will be irrelevant, because if the compilation worked for the second APEX using stubs then it will work for the common variant using stubs. However, if an incorrect change to the build rules is made this could lead to confusing errors, as a previously-working common variant could suddenly stop building when a module is added to a new APEX without its dependencies that require implementation APIs to compile. This change reduces the number of modules in an AOSP arm64-userdebug build by 3% (52242 to 50586), reduces the number of variants of the libcutils module from 74 to 53, and reduces the number of variants of the massive libart[d] modules from 44 to 32. This relands I0529837476a253c32b3dfb98dcccf107427c742c with a fix to always mark permissions XML files of java_sdk_library modules as unique per apex since they contain the APEX filename, and a fix to UpdateUniqueApexVariationsForDeps to check ApexInfo.InApexes instead of DepIsInSameApex to check if two modules are in the same apex to account for a module that depends on another in a way that doesn't normally include the dependency in the APEX (e.g. a libs property), but the dependency is directly included in the APEX. Bug: 164216768 Test: go test ./build/soong/apex/... Change-Id: I2ae170601f764e5b88d0be2e0e6adc84e3a4d9cc
725 lines
25 KiB
Go
725 lines
25 KiB
Go
// 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 android
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/google/blueprint"
|
|
)
|
|
|
|
const (
|
|
SdkVersion_Android10 = 29
|
|
)
|
|
|
|
type ApexInfo struct {
|
|
// Name of the apex variation that this module is mutated into
|
|
ApexVariationName string
|
|
|
|
MinSdkVersion int
|
|
Updatable bool
|
|
RequiredSdks SdkRefs
|
|
|
|
InApexes []string
|
|
}
|
|
|
|
func (i ApexInfo) mergedName() string {
|
|
name := "apex" + strconv.Itoa(i.MinSdkVersion)
|
|
for _, sdk := range i.RequiredSdks {
|
|
name += "_" + sdk.Name + "_" + sdk.Version
|
|
}
|
|
return name
|
|
}
|
|
|
|
// Extracted from ApexModule to make it easier to define custom subsets of the
|
|
// ApexModule interface and improve code navigation within the IDE.
|
|
type DepIsInSameApex interface {
|
|
// DepIsInSameApex tests if the other module 'dep' is installed to the same
|
|
// APEX as this module
|
|
DepIsInSameApex(ctx BaseModuleContext, dep Module) bool
|
|
}
|
|
|
|
// 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).
|
|
//
|
|
// Native shared libraries are one such module type; when it is built for an
|
|
// APEX, it should depend only on stable interfaces such as NDK, stable AIDL,
|
|
// or C APIs from other APEXs.
|
|
//
|
|
// A module implementing this interface will be mutated into multiple
|
|
// variations by apex.apexMutator if it is directly or indirectly included
|
|
// in one or more APEXs. Specifically, if a module is included in apex.foo and
|
|
// apex.bar then three apex variants are created: platform, apex.foo and
|
|
// apex.bar. The platform variant is for the regular partitions
|
|
// (e.g., /system or /vendor, etc.) while the other two are for the APEXs,
|
|
// respectively.
|
|
type ApexModule interface {
|
|
Module
|
|
DepIsInSameApex
|
|
|
|
apexModuleBase() *ApexModuleBase
|
|
|
|
// Marks that this module should be built for the specified APEX.
|
|
// Call this before apex.apexMutator is run.
|
|
BuildForApex(apex ApexInfo)
|
|
|
|
// Returns the name of APEX variation that this module will be built for.
|
|
// Empty string is returned when 'IsForPlatform() == true'. Note that a
|
|
// module can beincluded in multiple APEXes, in which case, the module
|
|
// is mutated into one or more variants, each of which is for one or
|
|
// more APEXes. This method returns the name of the APEX variation of
|
|
// the module.
|
|
// Call this after apex.apexMutator is run.
|
|
ApexVariationName() string
|
|
|
|
// Returns the name of the APEX modules that this variant of this module
|
|
// is present in.
|
|
// Call this after apex.apexMutator is run.
|
|
InApexes() []string
|
|
|
|
// Tests whether this module will be built for the platform or not.
|
|
// This is a shortcut for ApexVariationName() == ""
|
|
IsForPlatform() bool
|
|
|
|
// Tests if this module could have APEX variants. APEX variants are
|
|
// created only for the modules that returns true here. This is useful
|
|
// for not creating APEX variants for certain types of shared libraries
|
|
// such as NDK stubs.
|
|
CanHaveApexVariants() bool
|
|
|
|
// Tests if this module can be installed to APEX as a file. For example,
|
|
// this would return true for shared libs while return false for static
|
|
// libs.
|
|
IsInstallableToApex() bool
|
|
|
|
// Mutate this module into one or more variants each of which is built
|
|
// for an APEX marked via BuildForApex().
|
|
CreateApexVariations(mctx BottomUpMutatorContext) []Module
|
|
|
|
// Tests if this module is available for the specified APEX or ":platform"
|
|
AvailableFor(what string) bool
|
|
|
|
// Return true if this module is not available to platform (i.e. apex_available
|
|
// property doesn't have "//apex_available:platform"), or shouldn't be available
|
|
// to platform, which is the case when this module depends on other module that
|
|
// isn't available to platform.
|
|
NotAvailableForPlatform() bool
|
|
|
|
// Mark that this module is not available to platform. Set by the
|
|
// check-platform-availability mutator in the apex package.
|
|
SetNotAvailableForPlatform()
|
|
|
|
// Returns the highest version which is <= maxSdkVersion.
|
|
// For example, with maxSdkVersion is 10 and versionList is [9,11]
|
|
// it returns 9 as string
|
|
ChooseSdkVersion(versionList []string, maxSdkVersion int) (string, error)
|
|
|
|
// Tests if the module comes from an updatable APEX.
|
|
Updatable() bool
|
|
|
|
// List of APEXes that this module tests. The module has access to
|
|
// the private part of the listed APEXes even when it is not included in the
|
|
// APEXes.
|
|
TestFor() []string
|
|
|
|
// Returns nil if this module supports sdkVersion
|
|
// Otherwise, returns error with reason
|
|
ShouldSupportSdkVersion(ctx BaseModuleContext, sdkVersion int) error
|
|
|
|
// Returns true if this module needs a unique variation per apex, for example if
|
|
// use_apex_name_macro is set.
|
|
UniqueApexVariations() bool
|
|
|
|
// UpdateUniqueApexVariationsForDeps sets UniqueApexVariationsForDeps if any dependencies
|
|
// that are in the same APEX have unique APEX variations so that the module can link against
|
|
// the right variant.
|
|
UpdateUniqueApexVariationsForDeps(mctx BottomUpMutatorContext)
|
|
}
|
|
|
|
type ApexProperties struct {
|
|
// Availability of this module in APEXes. Only the listed APEXes can contain
|
|
// this module. If the module has stubs then other APEXes and the platform may
|
|
// access it through them (subject to visibility).
|
|
//
|
|
// "//apex_available:anyapex" is a pseudo APEX name that matches to any APEX.
|
|
// "//apex_available:platform" refers to non-APEX partitions like "system.img".
|
|
// "com.android.gki.*" matches any APEX module name with the prefix "com.android.gki.".
|
|
// Default is ["//apex_available:platform"].
|
|
Apex_available []string
|
|
|
|
Info ApexInfo `blueprint:"mutated"`
|
|
|
|
NotAvailableForPlatform bool `blueprint:"mutated"`
|
|
|
|
UniqueApexVariationsForDeps bool `blueprint:"mutated"`
|
|
}
|
|
|
|
// Marker interface that identifies dependencies that are excluded from APEX
|
|
// contents.
|
|
type ExcludeFromApexContentsTag interface {
|
|
blueprint.DependencyTag
|
|
|
|
// Method that differentiates this interface from others.
|
|
ExcludeFromApexContents()
|
|
}
|
|
|
|
// Provides default implementation for the ApexModule interface. APEX-aware
|
|
// modules are expected to include this struct and call InitApexModule().
|
|
type ApexModuleBase struct {
|
|
ApexProperties ApexProperties
|
|
|
|
canHaveApexVariants bool
|
|
|
|
apexVariationsLock sync.Mutex // protects apexVariations during parallel apexDepsMutator
|
|
apexVariations []ApexInfo
|
|
}
|
|
|
|
func (m *ApexModuleBase) apexModuleBase() *ApexModuleBase {
|
|
return m
|
|
}
|
|
|
|
func (m *ApexModuleBase) ApexAvailable() []string {
|
|
return m.ApexProperties.Apex_available
|
|
}
|
|
|
|
func (m *ApexModuleBase) TestFor() []string {
|
|
// To be implemented by concrete types inheriting ApexModuleBase
|
|
return nil
|
|
}
|
|
|
|
func (m *ApexModuleBase) UniqueApexVariations() bool {
|
|
return false
|
|
}
|
|
|
|
func (m *ApexModuleBase) UpdateUniqueApexVariationsForDeps(mctx BottomUpMutatorContext) {
|
|
// anyInSameApex returns true if the two ApexInfo lists contain any values in an InApexes list
|
|
// in common. It is used instead of DepIsInSameApex because it needs to determine if the dep
|
|
// is in the same APEX due to being directly included, not only if it is included _because_ it
|
|
// is a dependency.
|
|
anyInSameApex := func(a, b []ApexInfo) bool {
|
|
collectApexes := func(infos []ApexInfo) []string {
|
|
var ret []string
|
|
for _, info := range infos {
|
|
ret = append(ret, info.InApexes...)
|
|
}
|
|
return ret
|
|
}
|
|
|
|
aApexes := collectApexes(a)
|
|
bApexes := collectApexes(b)
|
|
sort.Strings(bApexes)
|
|
for _, aApex := range aApexes {
|
|
index := sort.SearchStrings(bApexes, aApex)
|
|
if index < len(bApexes) && bApexes[index] == aApex {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
mctx.VisitDirectDeps(func(dep Module) {
|
|
if depApexModule, ok := dep.(ApexModule); ok {
|
|
if anyInSameApex(depApexModule.apexModuleBase().apexVariations, m.apexVariations) &&
|
|
(depApexModule.UniqueApexVariations() ||
|
|
depApexModule.apexModuleBase().ApexProperties.UniqueApexVariationsForDeps) {
|
|
m.ApexProperties.UniqueApexVariationsForDeps = true
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func (m *ApexModuleBase) BuildForApex(apex ApexInfo) {
|
|
m.apexVariationsLock.Lock()
|
|
defer m.apexVariationsLock.Unlock()
|
|
for _, v := range m.apexVariations {
|
|
if v.ApexVariationName == apex.ApexVariationName {
|
|
return
|
|
}
|
|
}
|
|
m.apexVariations = append(m.apexVariations, apex)
|
|
}
|
|
|
|
func (m *ApexModuleBase) ApexVariationName() string {
|
|
return m.ApexProperties.Info.ApexVariationName
|
|
}
|
|
|
|
func (m *ApexModuleBase) InApexes() []string {
|
|
return m.ApexProperties.Info.InApexes
|
|
}
|
|
|
|
func (m *ApexModuleBase) IsForPlatform() bool {
|
|
return m.ApexProperties.Info.ApexVariationName == ""
|
|
}
|
|
|
|
func (m *ApexModuleBase) CanHaveApexVariants() bool {
|
|
return m.canHaveApexVariants
|
|
}
|
|
|
|
func (m *ApexModuleBase) IsInstallableToApex() bool {
|
|
// should be overriden if needed
|
|
return false
|
|
}
|
|
|
|
const (
|
|
AvailableToPlatform = "//apex_available:platform"
|
|
AvailableToAnyApex = "//apex_available:anyapex"
|
|
AvailableToGkiApex = "com.android.gki.*"
|
|
)
|
|
|
|
func CheckAvailableForApex(what string, apex_available []string) bool {
|
|
if len(apex_available) == 0 {
|
|
// apex_available defaults to ["//apex_available:platform"],
|
|
// which means 'available to the platform but no apexes'.
|
|
return what == AvailableToPlatform
|
|
}
|
|
return InList(what, apex_available) ||
|
|
(what != AvailableToPlatform && InList(AvailableToAnyApex, apex_available)) ||
|
|
(strings.HasPrefix(what, "com.android.gki.") && InList(AvailableToGkiApex, apex_available))
|
|
}
|
|
|
|
func (m *ApexModuleBase) AvailableFor(what string) bool {
|
|
return CheckAvailableForApex(what, m.ApexProperties.Apex_available)
|
|
}
|
|
|
|
func (m *ApexModuleBase) NotAvailableForPlatform() bool {
|
|
return m.ApexProperties.NotAvailableForPlatform
|
|
}
|
|
|
|
func (m *ApexModuleBase) SetNotAvailableForPlatform() {
|
|
m.ApexProperties.NotAvailableForPlatform = true
|
|
}
|
|
|
|
func (m *ApexModuleBase) DepIsInSameApex(ctx BaseModuleContext, dep Module) bool {
|
|
// By default, if there is a dependency from A to B, we try to include both in the same APEX,
|
|
// unless B is explicitly from outside of the APEX (i.e. a stubs lib). Thus, returning true.
|
|
// This is overridden by some module types like apex.ApexBundle, cc.Module, java.Module, etc.
|
|
return true
|
|
}
|
|
|
|
func (m *ApexModuleBase) ChooseSdkVersion(versionList []string, maxSdkVersion int) (string, error) {
|
|
for i := range versionList {
|
|
ver, _ := strconv.Atoi(versionList[len(versionList)-i-1])
|
|
if ver <= maxSdkVersion {
|
|
return versionList[len(versionList)-i-1], nil
|
|
}
|
|
}
|
|
return "", fmt.Errorf("not found a version(<=%d) in versionList: %v", maxSdkVersion, versionList)
|
|
}
|
|
|
|
func (m *ApexModuleBase) checkApexAvailableProperty(mctx BaseModuleContext) {
|
|
for _, n := range m.ApexProperties.Apex_available {
|
|
if n == AvailableToPlatform || n == AvailableToAnyApex || n == AvailableToGkiApex {
|
|
continue
|
|
}
|
|
if !mctx.OtherModuleExists(n) && !mctx.Config().AllowMissingDependencies() {
|
|
mctx.PropertyErrorf("apex_available", "%q is not a valid module name", n)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (m *ApexModuleBase) Updatable() bool {
|
|
return m.ApexProperties.Info.Updatable
|
|
}
|
|
|
|
type byApexName []ApexInfo
|
|
|
|
func (a byApexName) Len() int { return len(a) }
|
|
func (a byApexName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
func (a byApexName) Less(i, j int) bool { return a[i].ApexVariationName < a[j].ApexVariationName }
|
|
|
|
// mergeApexVariations deduplicates APEX variations that would build identically into a common
|
|
// variation. It returns the reduced list of variations and a list of aliases from the original
|
|
// variation names to the new variation names.
|
|
func mergeApexVariations(apexVariations []ApexInfo) (merged []ApexInfo, aliases [][2]string) {
|
|
sort.Sort(byApexName(apexVariations))
|
|
seen := make(map[string]int)
|
|
for _, apexInfo := range apexVariations {
|
|
apexName := apexInfo.ApexVariationName
|
|
mergedName := apexInfo.mergedName()
|
|
if index, exists := seen[mergedName]; exists {
|
|
merged[index].InApexes = append(merged[index].InApexes, apexName)
|
|
merged[index].Updatable = merged[index].Updatable || apexInfo.Updatable
|
|
} else {
|
|
seen[mergedName] = len(merged)
|
|
apexInfo.ApexVariationName = apexInfo.mergedName()
|
|
apexInfo.InApexes = CopyOf(apexInfo.InApexes)
|
|
merged = append(merged, apexInfo)
|
|
}
|
|
aliases = append(aliases, [2]string{apexName, mergedName})
|
|
}
|
|
return merged, aliases
|
|
}
|
|
|
|
func (m *ApexModuleBase) CreateApexVariations(mctx BottomUpMutatorContext) []Module {
|
|
if len(m.apexVariations) > 0 {
|
|
m.checkApexAvailableProperty(mctx)
|
|
|
|
var apexVariations []ApexInfo
|
|
var aliases [][2]string
|
|
if !mctx.Module().(ApexModule).UniqueApexVariations() && !m.ApexProperties.UniqueApexVariationsForDeps {
|
|
apexVariations, aliases = mergeApexVariations(m.apexVariations)
|
|
} else {
|
|
apexVariations = m.apexVariations
|
|
}
|
|
|
|
sort.Sort(byApexName(apexVariations))
|
|
variations := []string{}
|
|
variations = append(variations, "") // Original variation for platform
|
|
for _, apex := range apexVariations {
|
|
variations = append(variations, apex.ApexVariationName)
|
|
}
|
|
|
|
defaultVariation := ""
|
|
mctx.SetDefaultDependencyVariation(&defaultVariation)
|
|
|
|
modules := mctx.CreateVariations(variations...)
|
|
for i, mod := range modules {
|
|
platformVariation := i == 0
|
|
if platformVariation && !mctx.Host() && !mod.(ApexModule).AvailableFor(AvailableToPlatform) {
|
|
// Do not install the module for platform, but still allow it to output
|
|
// uninstallable AndroidMk entries in certain cases when they have
|
|
// side effects.
|
|
mod.MakeUninstallable()
|
|
}
|
|
if !platformVariation {
|
|
mod.(ApexModule).apexModuleBase().ApexProperties.Info = apexVariations[i-1]
|
|
}
|
|
}
|
|
|
|
for _, alias := range aliases {
|
|
mctx.CreateAliasVariation(alias[0], alias[1])
|
|
}
|
|
|
|
return modules
|
|
}
|
|
return nil
|
|
}
|
|
|
|
var apexData OncePer
|
|
var apexNamesMapMutex sync.Mutex
|
|
var apexNamesKey = NewOnceKey("apexNames")
|
|
|
|
// This structure maintains the global mapping in between modules and APEXes.
|
|
// Examples:
|
|
//
|
|
// apexNamesMap()["foo"]["bar"] == true: module foo is directly depended on by APEX bar
|
|
// apexNamesMap()["foo"]["bar"] == false: module foo is indirectly depended on by APEX bar
|
|
// apexNamesMap()["foo"]["bar"] doesn't exist: foo is not built for APEX bar
|
|
func apexNamesMap() map[string]map[string]bool {
|
|
return apexData.Once(apexNamesKey, func() interface{} {
|
|
return make(map[string]map[string]bool)
|
|
}).(map[string]map[string]bool)
|
|
}
|
|
|
|
// Update the map to mark that a module named moduleName is directly or indirectly
|
|
// depended on by the specified APEXes. Directly depending means that a module
|
|
// is explicitly listed in the build definition of the APEX via properties like
|
|
// native_shared_libs, java_libs, etc.
|
|
func UpdateApexDependency(apex ApexInfo, moduleName string, directDep bool) {
|
|
apexNamesMapMutex.Lock()
|
|
defer apexNamesMapMutex.Unlock()
|
|
apexesForModule, ok := apexNamesMap()[moduleName]
|
|
if !ok {
|
|
apexesForModule = make(map[string]bool)
|
|
apexNamesMap()[moduleName] = apexesForModule
|
|
}
|
|
apexesForModule[apex.ApexVariationName] = apexesForModule[apex.ApexVariationName] || directDep
|
|
for _, apexName := range apex.InApexes {
|
|
apexesForModule[apexName] = apexesForModule[apex.ApexVariationName] || directDep
|
|
}
|
|
}
|
|
|
|
// TODO(b/146393795): remove this when b/146393795 is fixed
|
|
func ClearApexDependency() {
|
|
m := apexNamesMap()
|
|
for k := range m {
|
|
delete(m, k)
|
|
}
|
|
}
|
|
|
|
// Tests whether a module named moduleName is directly depended on by an APEX
|
|
// named apexName.
|
|
func DirectlyInApex(apexName string, moduleName string) bool {
|
|
apexNamesMapMutex.Lock()
|
|
defer apexNamesMapMutex.Unlock()
|
|
if apexNamesForModule, ok := apexNamesMap()[moduleName]; ok {
|
|
return apexNamesForModule[apexName]
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Tests whether a module named moduleName is directly depended on by all APEXes
|
|
// in a list of apexNames.
|
|
func DirectlyInAllApexes(apexNames []string, moduleName string) bool {
|
|
apexNamesMapMutex.Lock()
|
|
defer apexNamesMapMutex.Unlock()
|
|
for _, apexName := range apexNames {
|
|
apexNamesForModule := apexNamesMap()[moduleName]
|
|
if !apexNamesForModule[apexName] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
type hostContext interface {
|
|
Host() bool
|
|
}
|
|
|
|
// Tests whether a module named moduleName is directly depended on by any APEX.
|
|
func DirectlyInAnyApex(ctx hostContext, moduleName string) bool {
|
|
if ctx.Host() {
|
|
// Host has no APEX.
|
|
return false
|
|
}
|
|
apexNamesMapMutex.Lock()
|
|
defer apexNamesMapMutex.Unlock()
|
|
if apexNames, ok := apexNamesMap()[moduleName]; ok {
|
|
for an := range apexNames {
|
|
if apexNames[an] {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Tests whether a module named module is depended on (including both
|
|
// direct and indirect dependencies) by any APEX.
|
|
func InAnyApex(moduleName string) bool {
|
|
apexNamesMapMutex.Lock()
|
|
defer apexNamesMapMutex.Unlock()
|
|
apexNames, ok := apexNamesMap()[moduleName]
|
|
return ok && len(apexNames) > 0
|
|
}
|
|
|
|
func GetApexesForModule(moduleName string) []string {
|
|
ret := []string{}
|
|
apexNamesMapMutex.Lock()
|
|
defer apexNamesMapMutex.Unlock()
|
|
if apexNames, ok := apexNamesMap()[moduleName]; ok {
|
|
for an := range apexNames {
|
|
ret = append(ret, an)
|
|
}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func InitApexModule(m ApexModule) {
|
|
base := m.apexModuleBase()
|
|
base.canHaveApexVariants = true
|
|
|
|
m.AddProperties(&base.ApexProperties)
|
|
}
|
|
|
|
// A dependency info for a single ApexModule, either direct or transitive.
|
|
type ApexModuleDepInfo struct {
|
|
// Name of the dependency
|
|
To string
|
|
// List of dependencies To belongs to. Includes APEX itself, if a direct dependency.
|
|
From []string
|
|
// Whether the dependency belongs to the final compiled APEX.
|
|
IsExternal bool
|
|
// min_sdk_version of the ApexModule
|
|
MinSdkVersion string
|
|
}
|
|
|
|
// A map of a dependency name to its ApexModuleDepInfo
|
|
type DepNameToDepInfoMap map[string]ApexModuleDepInfo
|
|
|
|
type ApexBundleDepsInfo struct {
|
|
flatListPath OutputPath
|
|
fullListPath OutputPath
|
|
}
|
|
|
|
type ApexBundleDepsInfoIntf interface {
|
|
Updatable() bool
|
|
FlatListPath() Path
|
|
FullListPath() Path
|
|
}
|
|
|
|
func (d *ApexBundleDepsInfo) FlatListPath() Path {
|
|
return d.flatListPath
|
|
}
|
|
|
|
func (d *ApexBundleDepsInfo) FullListPath() Path {
|
|
return d.fullListPath
|
|
}
|
|
|
|
// Generate two module out files:
|
|
// 1. FullList with transitive deps and their parents in the dep graph
|
|
// 2. FlatList with a flat list of transitive deps
|
|
func (d *ApexBundleDepsInfo) BuildDepsInfoLists(ctx ModuleContext, minSdkVersion string, depInfos DepNameToDepInfoMap) {
|
|
var fullContent strings.Builder
|
|
var flatContent strings.Builder
|
|
|
|
fmt.Fprintf(&flatContent, "%s(minSdkVersion:%s):\\n", ctx.ModuleName(), minSdkVersion)
|
|
for _, key := range FirstUniqueStrings(SortedStringKeys(depInfos)) {
|
|
info := depInfos[key]
|
|
toName := fmt.Sprintf("%s(minSdkVersion:%s)", info.To, info.MinSdkVersion)
|
|
if info.IsExternal {
|
|
toName = toName + " (external)"
|
|
}
|
|
fmt.Fprintf(&fullContent, "%s <- %s\\n", toName, strings.Join(SortedUniqueStrings(info.From), ", "))
|
|
fmt.Fprintf(&flatContent, " %s\\n", toName)
|
|
}
|
|
|
|
d.fullListPath = PathForModuleOut(ctx, "depsinfo", "fulllist.txt").OutputPath
|
|
ctx.Build(pctx, BuildParams{
|
|
Rule: WriteFile,
|
|
Description: "Full Dependency Info",
|
|
Output: d.fullListPath,
|
|
Args: map[string]string{
|
|
"content": fullContent.String(),
|
|
},
|
|
})
|
|
|
|
d.flatListPath = PathForModuleOut(ctx, "depsinfo", "flatlist.txt").OutputPath
|
|
ctx.Build(pctx, BuildParams{
|
|
Rule: WriteFile,
|
|
Description: "Flat Dependency Info",
|
|
Output: d.flatListPath,
|
|
Args: map[string]string{
|
|
"content": flatContent.String(),
|
|
},
|
|
})
|
|
}
|
|
|
|
// TODO(b/158059172): remove minSdkVersion allowlist
|
|
var minSdkVersionAllowlist = map[string]int{
|
|
"adbd": 30,
|
|
"android.net.ipsec.ike": 30,
|
|
"androidx-constraintlayout_constraintlayout-solver": 30,
|
|
"androidx.annotation_annotation": 28,
|
|
"androidx.arch.core_core-common": 28,
|
|
"androidx.collection_collection": 28,
|
|
"androidx.lifecycle_lifecycle-common": 28,
|
|
"apache-commons-compress": 29,
|
|
"bouncycastle_ike_digests": 30,
|
|
"brotli-java": 29,
|
|
"captiveportal-lib": 28,
|
|
"flatbuffer_headers": 30,
|
|
"framework-permission": 30,
|
|
"framework-statsd": 30,
|
|
"gemmlowp_headers": 30,
|
|
"ike-internals": 30,
|
|
"kotlinx-coroutines-android": 28,
|
|
"kotlinx-coroutines-core": 28,
|
|
"libadb_crypto": 30,
|
|
"libadb_pairing_auth": 30,
|
|
"libadb_pairing_connection": 30,
|
|
"libadb_pairing_server": 30,
|
|
"libadb_protos": 30,
|
|
"libadb_tls_connection": 30,
|
|
"libadbconnection_client": 30,
|
|
"libadbconnection_server": 30,
|
|
"libadbd_core": 30,
|
|
"libadbd_services": 30,
|
|
"libadbd": 30,
|
|
"libapp_processes_protos_lite": 30,
|
|
"libasyncio": 30,
|
|
"libbrotli": 30,
|
|
"libbuildversion": 30,
|
|
"libcrypto_static": 30,
|
|
"libcrypto_utils": 30,
|
|
"libdiagnose_usb": 30,
|
|
"libeigen": 30,
|
|
"liblz4": 30,
|
|
"libmdnssd": 30,
|
|
"libneuralnetworks_common": 30,
|
|
"libneuralnetworks_headers": 30,
|
|
"libneuralnetworks": 30,
|
|
"libprocpartition": 30,
|
|
"libprotobuf-java-lite": 30,
|
|
"libprotoutil": 30,
|
|
"libqemu_pipe": 30,
|
|
"libstats_jni": 30,
|
|
"libstatslog_statsd": 30,
|
|
"libstatsmetadata": 30,
|
|
"libstatspull": 30,
|
|
"libstatssocket": 30,
|
|
"libsync": 30,
|
|
"libtextclassifier_hash_headers": 30,
|
|
"libtextclassifier_hash_static": 30,
|
|
"libtflite_kernel_utils": 30,
|
|
"libwatchdog": 29,
|
|
"libzstd": 30,
|
|
"metrics-constants-protos": 28,
|
|
"net-utils-framework-common": 29,
|
|
"permissioncontroller-statsd": 28,
|
|
"philox_random_headers": 30,
|
|
"philox_random": 30,
|
|
"service-permission": 30,
|
|
"service-statsd": 30,
|
|
"statsd-aidl-ndk_platform": 30,
|
|
"statsd": 30,
|
|
"tensorflow_headers": 30,
|
|
"xz-java": 29,
|
|
}
|
|
|
|
// Function called while walking an APEX's payload dependencies.
|
|
//
|
|
// Return true if the `to` module should be visited, false otherwise.
|
|
type PayloadDepsCallback func(ctx ModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool
|
|
|
|
// UpdatableModule represents updatable APEX/APK
|
|
type UpdatableModule interface {
|
|
Module
|
|
WalkPayloadDeps(ctx ModuleContext, do PayloadDepsCallback)
|
|
}
|
|
|
|
// CheckMinSdkVersion checks if every dependency of an updatable module sets min_sdk_version accordingly
|
|
func CheckMinSdkVersion(m UpdatableModule, ctx ModuleContext, minSdkVersion int) {
|
|
// do not enforce min_sdk_version for host
|
|
if ctx.Host() {
|
|
return
|
|
}
|
|
|
|
// do not enforce for coverage build
|
|
if ctx.Config().IsEnvTrue("EMMA_INSTRUMENT") || ctx.DeviceConfig().NativeCoverageEnabled() || ctx.DeviceConfig().ClangCoverageEnabled() {
|
|
return
|
|
}
|
|
|
|
// do not enforce deps.min_sdk_version if APEX/APK doesn't set min_sdk_version or
|
|
// min_sdk_version is not finalized (e.g. current or codenames)
|
|
if minSdkVersion == FutureApiLevel {
|
|
return
|
|
}
|
|
|
|
m.WalkPayloadDeps(ctx, func(ctx ModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool {
|
|
if externalDep {
|
|
// external deps are outside the payload boundary, which is "stable" interface.
|
|
// We don't have to check min_sdk_version for external dependencies.
|
|
return false
|
|
}
|
|
if am, ok := from.(DepIsInSameApex); ok && !am.DepIsInSameApex(ctx, to) {
|
|
return false
|
|
}
|
|
if err := to.ShouldSupportSdkVersion(ctx, minSdkVersion); err != nil {
|
|
toName := ctx.OtherModuleName(to)
|
|
if ver, ok := minSdkVersionAllowlist[toName]; !ok || ver > minSdkVersion {
|
|
ctx.OtherModuleErrorf(to, "should support min_sdk_version(%v) for %q: %v. Dependency path: %s",
|
|
minSdkVersion, ctx.ModuleName(), err.Error(), ctx.GetPathString(false))
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
})
|
|
}
|