summaryrefslogtreecommitdiff
path: root/android/apex.go
diff options
context:
space:
mode:
Diffstat (limited to 'android/apex.go')
-rw-r--r--android/apex.go313
1 files changed, 278 insertions, 35 deletions
diff --git a/android/apex.go b/android/apex.go
index 17df76240..30152db29 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -15,12 +15,35 @@
package android
import (
+ "fmt"
"sort"
+ "strconv"
+ "strings"
"sync"
"github.com/google/blueprint"
)
+const (
+ SdkVersion_Android10 = 29
+)
+
+type ApexInfo struct {
+ // Name of the apex variant that this module is mutated into
+ ApexName string
+
+ MinSdkVersion int
+ Updatable bool
+}
+
+// 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).
@@ -38,11 +61,16 @@ import (
// respectively.
type ApexModule interface {
Module
+ DepIsInSameApex
+
apexModuleBase() *ApexModuleBase
- // Marks that this module should be built for the APEX of the specified name.
+ // Marks that this module should be built for the specified APEXes.
// Call this before apex.apexMutator is run.
- BuildForApex(apexName string)
+ BuildForApexes(apexes []ApexInfo)
+
+ // Returns the APEXes that this module will be built for
+ ApexVariations() []ApexInfo
// Returns the name of APEX that this module will be built for. Empty string
// is returned when 'IsForPlatform() == true'. Note that a module can be
@@ -68,17 +96,58 @@ type ApexModule interface {
IsInstallableToApex() bool
// Mutate this module into one or more variants each of which is built
- // for an APEX marked via BuildForApex().
- CreateApexVariations(mctx BottomUpMutatorContext) []blueprint.Module
-
- // Sets the name of the apex variant of this module. Called inside
- // CreateApexVariations.
- setApexName(apexName string)
+ // for an APEX marked via BuildForApexes().
+ 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
}
type ApexProperties struct {
- // Name of the apex variant that this module is mutated into
- ApexName string `blueprint:"mutated"`
+ // 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".
+ // Default is ["//apex_available:platform"].
+ Apex_available []string
+
+ Info ApexInfo `blueprint:"mutated"`
+
+ NotAvailableForPlatform 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
@@ -89,31 +158,46 @@ type ApexModuleBase struct {
canHaveApexVariants bool
apexVariationsLock sync.Mutex // protects apexVariations during parallel apexDepsMutator
- apexVariations []string
+ apexVariations []ApexInfo
}
func (m *ApexModuleBase) apexModuleBase() *ApexModuleBase {
return m
}
-func (m *ApexModuleBase) BuildForApex(apexName string) {
+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) BuildForApexes(apexes []ApexInfo) {
m.apexVariationsLock.Lock()
defer m.apexVariationsLock.Unlock()
- if !InList(apexName, m.apexVariations) {
- m.apexVariations = append(m.apexVariations, apexName)
+nextApex:
+ for _, apex := range apexes {
+ for _, v := range m.apexVariations {
+ if v.ApexName == apex.ApexName {
+ continue nextApex
+ }
+ }
+ m.apexVariations = append(m.apexVariations, apex)
}
}
-func (m *ApexModuleBase) ApexName() string {
- return m.ApexProperties.ApexName
+func (m *ApexModuleBase) ApexVariations() []ApexInfo {
+ return m.apexVariations
}
-func (m *ApexModuleBase) IsForPlatform() bool {
- return m.ApexProperties.ApexName == ""
+func (m *ApexModuleBase) ApexName() string {
+ return m.ApexProperties.Info.ApexName
}
-func (m *ApexModuleBase) setApexName(apexName string) {
- m.ApexProperties.ApexName = apexName
+func (m *ApexModuleBase) IsForPlatform() bool {
+ return m.ApexProperties.Info.ApexName == ""
}
func (m *ApexModuleBase) CanHaveApexVariants() bool {
@@ -125,18 +209,94 @@ func (m *ApexModuleBase) IsInstallableToApex() bool {
return false
}
-func (m *ApexModuleBase) CreateApexVariations(mctx BottomUpMutatorContext) []blueprint.Module {
+const (
+ AvailableToPlatform = "//apex_available:platform"
+ AvailableToAnyApex = "//apex_available:anyapex"
+)
+
+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))
+}
+
+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 {
+ 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].ApexName < a[j].ApexName }
+
+func (m *ApexModuleBase) CreateApexVariations(mctx BottomUpMutatorContext) []Module {
if len(m.apexVariations) > 0 {
- sort.Strings(m.apexVariations)
- variations := []string{""} // Original variation for platform
- variations = append(variations, m.apexVariations...)
+ m.checkApexAvailableProperty(mctx)
+
+ sort.Sort(byApexName(m.apexVariations))
+ variations := []string{}
+ variations = append(variations, "") // Original variation for platform
+ for _, apex := range m.apexVariations {
+ variations = append(variations, apex.ApexName)
+ }
+
+ defaultVariation := ""
+ mctx.SetDefaultDependencyVariation(&defaultVariation)
modules := mctx.CreateVariations(variations...)
- for i, m := range modules {
- if i == 0 {
- continue
+ for i, mod := range modules {
+ platformVariation := i == 0
+ if platformVariation && !mctx.Host() && !mod.(ApexModule).AvailableFor(AvailableToPlatform) {
+ mod.SkipInstall()
+ }
+ if !platformVariation {
+ mod.(ApexModule).apexModuleBase().ApexProperties.Info = m.apexVariations[i-1]
}
- m.(ApexModule).setApexName(variations[i])
}
return modules
}
@@ -160,18 +320,28 @@ func apexNamesMap() map[string]map[string]bool {
}
// Update the map to mark that a module named moduleName is directly or indirectly
-// depended on by an APEX named apexName. Directly depending means that a module
+// 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(apexName string, moduleName string, directDep bool) {
+func UpdateApexDependency(apexes []ApexInfo, moduleName string, directDep bool) {
apexNamesMapMutex.Lock()
defer apexNamesMapMutex.Unlock()
- apexNames, ok := apexNamesMap()[moduleName]
- if !ok {
- apexNames = make(map[string]bool)
- apexNamesMap()[moduleName] = apexNames
+ for _, apex := range apexes {
+ apexesForModule, ok := apexNamesMap()[moduleName]
+ if !ok {
+ apexesForModule = make(map[string]bool)
+ apexNamesMap()[moduleName] = apexesForModule
+ }
+ apexesForModule[apex.ApexName] = apexesForModule[apex.ApexName] || directDep
+ }
+}
+
+// TODO(b/146393795): remove this when b/146393795 is fixed
+func ClearApexDependency() {
+ m := apexNamesMap()
+ for k := range m {
+ delete(m, k)
}
- apexNames[apexName] = apexNames[apexName] || directDep
}
// Tests whether a module named moduleName is directly depended on by an APEX
@@ -234,3 +404,76 @@ func InitApexModule(m ApexModule) {
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(),
+ },
+ })
+}