diff options
Diffstat (limited to 'android/apex.go')
| -rw-r--r-- | android/apex.go | 313 |
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(), + }, + }) +} |