From e4758ed844113d65d8428d34f97d710224b6b4e8 Mon Sep 17 00:00:00 2001 From: Jiyong Park Date: Wed, 18 Nov 2020 01:34:22 +0900 Subject: [PATCH] Documenting android/apex.go Mostly documentation changes, but includes a few refactorings like changing the variable names, reording functions, reordering statements in logical order, etc. Bug: 173472337 Test: m Change-Id: I000c76e818722ed06bac03d9de87588b23552b08 --- android/apex.go | 512 ++++++++++++++++++++++++++++-------------------- apex/apex.go | 2 +- 2 files changed, 298 insertions(+), 216 deletions(-) diff --git a/android/apex.go b/android/apex.go index 276f7a4bd..a4ff0f97a 100644 --- a/android/apex.go +++ b/android/apex.go @@ -25,32 +25,54 @@ import ( ) var ( + // This is the sdk version when APEX was first introduced SdkVersion_Android10 = uncheckedFinalApiLevel(29) ) -// ApexInfo describes the metadata common to all modules in an apexBundle. +// ApexInfo describes the metadata about one or more apexBundles that an apex variant of a module is +// part of. When an apex variant is created, the variant is associated with one apexBundle. But +// when multiple apex variants are merged for deduping (see mergeApexVariations), this holds the +// information about the apexBundles that are merged together. +// Accessible via `ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)` type ApexInfo struct { - // Name of the apex variation that this module is mutated into, or "" for - // a platform variant. Note that a module can be included in multiple APEXes, - // in which case, the module is mutated into one or more variants, each of - // which is for one or more APEXes. + // Name of the apex variation that this module (i.e. the apex variant of the module) is + // mutated into, or "" for a platform (i.e. non-APEX) variant. Note that a module can be + // included in multiple APEXes, in which case, the module is mutated into one or more + // variants, each of which is for an APEX. The variants then can later be deduped if they + // don't need to be compiled differently. This is an optimization done in + // mergeApexVariations. ApexVariationName string - // Serialized ApiLevel. Use via MinSdkVersion() method. Cannot be stored in - // its struct form because this is cloned into properties structs, and - // ApiLevel has private members. + // Serialized ApiLevel that this module has to support at minimum. Should be accessed via + // MinSdkVersion() method. Cannot be stored in its struct form because this is cloned into + // properties structs, and ApiLevel has private members. MinSdkVersionStr string - // True if the module comes from an updatable APEX. - Updatable bool + // True if this module comes from an updatable apexBundle. + Updatable bool + + // The list of SDK modules that the containing apexBundle depends on. RequiredSdks SdkRefs - InApexes []string + // List of apexBundles that this apex variant of the module is associated with. Initially, + // the size of this list is one because one apex variant is associated with one apexBundle. + // When multiple apex variants are merged in mergeApexVariations, ApexInfo struct of the + // merged variant holds the list of apexBundles that are merged together. + InApexes []string + + // Pointers to the ApexContents struct each of which is for apexBundle modules that this + // module is part of. The ApexContents gives information about which modules the apexBundle + // has and whether a module became part of the apexBundle via a direct dependency or not. ApexContents []*ApexContents } var ApexInfoProvider = blueprint.NewMutatorProvider(ApexInfo{}, "apex") +// mergedName gives the name of the alias variation that will be used when multiple apex variations +// of a module can be deduped into one variation. For example, if libfoo is included in both apex.a +// and apex.b, and if the two APEXes have the same min_sdk_version (say 29), then libfoo doesn't +// have to be built twice, but only once. In that case, the two apex variations apex.a and apex.b +// are configured to have the same alias variation named apex29. func (i ApexInfo) mergedName(ctx PathContext) string { name := "apex" + strconv.Itoa(i.MinSdkVersion(ctx).FinalOrFutureInt()) for _, sdk := range i.RequiredSdks { @@ -59,14 +81,19 @@ func (i ApexInfo) mergedName(ctx PathContext) string { return name } -func (this *ApexInfo) MinSdkVersion(ctx PathContext) ApiLevel { - return ApiLevelOrPanic(ctx, this.MinSdkVersionStr) +// MinSdkVersion gives the api level that this module has to support at minimum. This is from the +// min_sdk_version property of the containing apexBundle. +func (i ApexInfo) MinSdkVersion(ctx PathContext) ApiLevel { + return ApiLevelOrPanic(ctx, i.MinSdkVersionStr) } +// IsForPlatform tells whether this module is for the platform or not. If false is returned, it +// means that this apex variant of the module is built for an APEX. func (i ApexInfo) IsForPlatform() bool { return i.ApexVariationName == "" } +// InApex tells whether this apex variant of the module is part of the given apexBundle or not. func (i ApexInfo) InApex(apex string) bool { for _, a := range i.InApexes { if a == apex { @@ -76,102 +103,111 @@ func (i ApexInfo) InApex(apex string) bool { return false } -// ApexTestForInfo stores the contents of APEXes for which this module is a test and thus has -// access to APEX internals. +// ApexTestForInfo stores the contents of APEXes for which this module is a test - although this +// module is not part of the APEX - and thus has access to APEX internals. type ApexTestForInfo struct { ApexContents []*ApexContents } var ApexTestForInfoProvider = blueprint.NewMutatorProvider(ApexTestForInfo{}, "apex_test_for") -// Extracted from ApexModule to make it easier to define custom subsets of the -// ApexModule interface and improve code navigation within the IDE. +// DepIsInSameApex defines an interface that should be used to determine whether a given dependency +// should be considered as part of the same APEX as the current module or not. Note: this was +// 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 tests if the other module 'dep' is considered as part of the same APEX as + // this module. For example, a static lib dependency usually returns true here, while a + // shared lib dependency to a stub library returns false. 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). +// 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 (i.e., 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. +// 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 APEXes. // -// 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. +// 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 APEXes. 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. The latter two variations can be merged (see +// mergedName) when the two APEXes have the same min_sdk_version requirement. 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. + // Marks that this module should be built for the specified APEX. Call this BEFORE + // apex.apexMutator is run. BuildForApex(apex ApexInfo) - // Returns true if this module is present in any APEXes - // directly or indirectly. - // Call this after apex.apexMutator is run. + // Returns true if this module is present in any APEX either directly or indirectly. Call + // this after apex.apexMutator is run. InAnyApex() bool - // Returns true if this module is directly in any APEXes. - // Call this after apex.apexMutator is run. + // Returns true if this module is directly in any APEX. Call this AFTER apex.apexMutator is + // run. DirectlyInAnyApex() bool - // Returns true if any variant of this module is directly in any APEXes. - // Call this after apex.apexMutator is run. + // Returns true in the primary variant of a module if _any_ variant of the module is + // directly in any apex. This includes host, arch, asan, etc. variants. It is unused in any + // variant that is not the primary variant. Ideally this wouldn't be used, as it incorrectly + // mixes arch variants if only one arch is in an apex, but a few places depend on it, for + // example when an ASAN variant is created before the apexMutator. Call this after + // apex.apexMutator is run. AnyVariantDirectlyInAnyApex() 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. + // Tests if this module could have APEX variants. Even when a module type implements + // ApexModule interface, APEX variants are created only for the module instances that return + // 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. + // 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 because static libs are not + // installable module (but it can still be mutated for APEX) IsInstallableToApex() bool - // Tests if this module is available for the specified APEX or ":platform" + // Tests if this module is available for the specified APEX or ":platform". This is from the + // apex_available property of the module. 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. + // Returns 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 + // Marks that this module is not available to platform. Set by the // check-platform-availability mutator in the apex package. SetNotAvailableForPlatform() - // 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. + // Returns the list of APEXes that this module is a test for. The module has access to the + // private part of the listed APEXes even when it is not included in the APEXes. This by + // default returns nil. A module type should override the default implementation. For + // example, cc_test module type returns the value of test_for here. TestFor() []string - // Returns nil if this module supports sdkVersion - // Otherwise, returns error with reason + // Returns nil (success) if this module should support the given sdk version. Returns an + // error if not. No default implementation is provided for this method. A module type + // implementing this interface should provide an implementation. A module supports an sdk + // version when the module's min_sdk_version is equal to or less than the given sdk version. ShouldSupportSdkVersion(ctx BaseModuleContext, sdkVersion ApiLevel) error - // Returns true if this module needs a unique variation per apex, for example if - // use_apex_name_macro is set. + // Returns true if this module needs a unique variation per apex, effectively disabling the + // deduping. This is turned on when, for example if use_apex_name_macro is set so that each + // apex variant should be built with different macro definitions. UniqueApexVariations() bool } +// Properties that are common to all module types implementing ApexModule interface. 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). + // 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". @@ -179,29 +215,23 @@ type ApexProperties struct { // Default is ["//apex_available:platform"]. Apex_available []string - // AnyVariantDirectlyInAnyApex is true in the primary variant of a module if _any_ variant - // of the module is directly in any apex. This includes host, arch, asan, etc. variants. - // It is unused in any variant that is not the primary variant. - // Ideally this wouldn't be used, as it incorrectly mixes arch variants if only one arch - // is in an apex, but a few places depend on it, for example when an ASAN variant is - // created before the apexMutator. - AnyVariantDirectlyInAnyApex bool `blueprint:"mutated"` - - // DirectlyInAnyApex is true if any APEX variant (including the "" variant used for the - // platform) of this module is directly in any APEX. - DirectlyInAnyApex bool `blueprint:"mutated"` - - // DirectlyInAnyApex is true if any APEX variant (including the "" variant used for the - // platform) of this module is directly or indirectly in any APEX. + // See ApexModule.InAnyApex() InAnyApex bool `blueprint:"mutated"` + // See ApexModule.DirectlyInAnyApex() + DirectlyInAnyApex bool `blueprint:"mutated"` + + // See ApexModule.AnyVariantDirectlyInAnyApex() + AnyVariantDirectlyInAnyApex bool `blueprint:"mutated"` + + // See ApexModule.NotAvailableForPlatform() NotAvailableForPlatform bool `blueprint:"mutated"` + // See ApexModule.UniqueApexVariants() UniqueApexVariationsForDeps bool `blueprint:"mutated"` } -// Marker interface that identifies dependencies that are excluded from APEX -// contents. +// Marker interface that identifies dependencies that are excluded from APEX contents. type ExcludeFromApexContentsTag interface { blueprint.DependencyTag @@ -209,85 +239,122 @@ type ExcludeFromApexContentsTag interface { ExcludeFromApexContents() } -// Marker interface that identifies dependencies that should inherit the DirectlyInAnyApex -// state from the parent to the child. For example, stubs libraries are marked as -// DirectlyInAnyApex if their implementation is in an apex. +// Marker interface that identifies dependencies that should inherit the DirectlyInAnyApex state +// from the parent to the child. For example, stubs libraries are marked as DirectlyInAnyApex if +// their implementation is in an apex. type CopyDirectlyInAnyApexTag interface { blueprint.DependencyTag + // Method that differentiates this interface from others. CopyDirectlyInAnyApex() } -// Provides default implementation for the ApexModule interface. APEX-aware +// ApexModuleBase provides the 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 + apexInfos []ApexInfo + apexInfosLock sync.Mutex // protects apexInfos during parallel apexDepsMutator } +// Initializes ApexModuleBase struct. Not calling this (even when inheriting from ApexModuleBase) +// prevents the module from being mutated for apexBundle. +func InitApexModule(m ApexModule) { + base := m.apexModuleBase() + base.canHaveApexVariants = true + + m.AddProperties(&base.ApexProperties) +} + +// Implements ApexModule func (m *ApexModuleBase) apexModuleBase() *ApexModuleBase { return m } +// Implements ApexModule 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 -} - +// Implements ApexModule func (m *ApexModuleBase) BuildForApex(apex ApexInfo) { - m.apexVariationsLock.Lock() - defer m.apexVariationsLock.Unlock() - for _, v := range m.apexVariations { + m.apexInfosLock.Lock() + defer m.apexInfosLock.Unlock() + for _, v := range m.apexInfos { if v.ApexVariationName == apex.ApexVariationName { return } } - m.apexVariations = append(m.apexVariations, apex) -} - -func (m *ApexModuleBase) DirectlyInAnyApex() bool { - return m.ApexProperties.DirectlyInAnyApex -} - -func (m *ApexModuleBase) AnyVariantDirectlyInAnyApex() bool { - return m.ApexProperties.AnyVariantDirectlyInAnyApex + m.apexInfos = append(m.apexInfos, apex) } +// Implements ApexModule func (m *ApexModuleBase) InAnyApex() bool { return m.ApexProperties.InAnyApex } +// Implements ApexModule +func (m *ApexModuleBase) DirectlyInAnyApex() bool { + return m.ApexProperties.DirectlyInAnyApex +} + +// Implements ApexModule +func (m *ApexModuleBase) AnyVariantDirectlyInAnyApex() bool { + return m.ApexProperties.AnyVariantDirectlyInAnyApex +} + +// Implements ApexModule func (m *ApexModuleBase) CanHaveApexVariants() bool { return m.canHaveApexVariants } +// Implements ApexModule func (m *ApexModuleBase) IsInstallableToApex() bool { - // should be overriden if needed + // If needed, this will bel overridden by concrete types inheriting + // ApexModuleBase return false } +// Implements ApexModule +func (m *ApexModuleBase) TestFor() []string { + // If needed, this will be overridden by concrete types inheriting + // ApexModuleBase + return nil +} + +// Implements ApexModule +func (m *ApexModuleBase) UniqueApexVariations() bool { + // If needed, this will bel overridden by concrete types inheriting + // ApexModuleBase + return false +} + +// Implements ApexModule +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 +} + const ( AvailableToPlatform = "//apex_available:platform" AvailableToAnyApex = "//apex_available:anyapex" AvailableToGkiApex = "com.android.gki.*" ) +// CheckAvailableForApex provides the default algorithm for checking the apex availability. When the +// availability is empty, it defaults to ["//apex_available:platform"] which means "available to the +// platform but not available to any APEX". When the list is not empty, `what` is matched against +// the list. If there is any matching element in the list, thus function returns true. The special +// availability "//apex_available:anyapex" matches with anything except for +// "//apex_available:platform". 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) || @@ -295,25 +362,22 @@ func CheckAvailableForApex(what string, apex_available []string) bool { (strings.HasPrefix(what, "com.android.gki.") && InList(AvailableToGkiApex, apex_available)) } +// Implements ApexModule func (m *ApexModuleBase) AvailableFor(what string) bool { return CheckAvailableForApex(what, m.ApexProperties.Apex_available) } +// Implements ApexModule func (m *ApexModuleBase) NotAvailableForPlatform() bool { return m.ApexProperties.NotAvailableForPlatform } +// Implements ApexModule 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 -} - +// This function makes sure that the apex_available property is valid func (m *ApexModuleBase) checkApexAvailableProperty(mctx BaseModuleContext) { for _, n := range m.ApexProperties.Apex_available { if n == AvailableToPlatform || n == AvailableToAnyApex || n == AvailableToGkiApex { @@ -331,22 +395,23 @@ 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 +// 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(ctx PathContext, apexVariations []ApexInfo) (merged []ApexInfo, aliases [][2]string) { - sort.Sort(byApexName(apexVariations)) +func mergeApexVariations(ctx PathContext, apexInfos []ApexInfo) (merged []ApexInfo, aliases [][2]string) { + sort.Sort(byApexName(apexInfos)) seen := make(map[string]int) - for _, apexInfo := range apexVariations { + for _, apexInfo := range apexInfos { apexName := apexInfo.ApexVariationName mergedName := apexInfo.mergedName(ctx) if index, exists := seen[mergedName]; exists { + // Variants having the same mergedName are deduped merged[index].InApexes = append(merged[index].InApexes, apexName) merged[index].ApexContents = append(merged[index].ApexContents, apexInfo.ApexContents...) merged[index].Updatable = merged[index].Updatable || apexInfo.Updatable } else { seen[mergedName] = len(merged) - apexInfo.ApexVariationName = apexInfo.mergedName(ctx) + apexInfo.ApexVariationName = mergedName apexInfo.InApexes = CopyOf(apexInfo.InApexes) apexInfo.ApexContents = append([]*ApexContents(nil), apexInfo.ApexContents...) merged = append(merged, apexInfo) @@ -356,75 +421,78 @@ func mergeApexVariations(ctx PathContext, apexVariations []ApexInfo) (merged []A return merged, aliases } +// CreateApexVariations mutates a given module into multiple apex variants each of which is for an +// apexBundle (and/or the platform) where the module is part of. func CreateApexVariations(mctx BottomUpMutatorContext, module ApexModule) []Module { base := module.apexModuleBase() - if len(base.apexVariations) > 0 { - base.checkApexAvailableProperty(mctx) - var apexVariations []ApexInfo - var aliases [][2]string - if !mctx.Module().(ApexModule).UniqueApexVariations() && !base.ApexProperties.UniqueApexVariationsForDeps { - apexVariations, aliases = mergeApexVariations(mctx, base.apexVariations) - } else { - apexVariations = base.apexVariations - } - // base.apexVariations is only needed to propagate the list of apexes from - // apexDepsMutator to apexMutator. It is no longer accurate after - // mergeApexVariations, and won't be copied to all but the first created - // variant. Clear it so it doesn't accidentally get used later. - base.apexVariations = nil - - 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) - - var inApex ApexMembership - for _, a := range apexVariations { - for _, apexContents := range a.ApexContents { - inApex = inApex.merge(apexContents.contents[mctx.ModuleName()]) - } - } - - base.ApexProperties.InAnyApex = true - base.ApexProperties.DirectlyInAnyApex = inApex == directlyInApex - - 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 { - mctx.SetVariationProvider(mod, ApexInfoProvider, apexVariations[i-1]) - } - } - - for _, alias := range aliases { - mctx.CreateAliasVariation(alias[0], alias[1]) - } - - return modules + // Shortcut + if len(base.apexInfos) == 0 { + return nil } - return nil + + // Do some validity checks. + // TODO(jiyong): is this the right place? + base.checkApexAvailableProperty(mctx) + + var apexInfos []ApexInfo + var aliases [][2]string + if !mctx.Module().(ApexModule).UniqueApexVariations() && !base.ApexProperties.UniqueApexVariationsForDeps { + apexInfos, aliases = mergeApexVariations(mctx, base.apexInfos) + } else { + apexInfos = base.apexInfos + } + // base.apexInfos is only needed to propagate the list of apexes from apexDepsMutator to + // apexMutator. It is no longer accurate after mergeApexVariations, and won't be copied to + // all but the first created variant. Clear it so it doesn't accidentally get used later. + base.apexInfos = nil + sort.Sort(byApexName(apexInfos)) + + var inApex ApexMembership + for _, a := range apexInfos { + for _, apexContents := range a.ApexContents { + inApex = inApex.merge(apexContents.contents[mctx.ModuleName()]) + } + } + base.ApexProperties.InAnyApex = true + base.ApexProperties.DirectlyInAnyApex = inApex == directlyInApex + + defaultVariation := "" + mctx.SetDefaultDependencyVariation(&defaultVariation) + + variations := []string{defaultVariation} + for _, a := range apexInfos { + variations = append(variations, a.ApexVariationName) + } + 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. TODO(jiyong): move this routine to somewhere else + mod.MakeUninstallable() + } + if !platformVariation { + mctx.SetVariationProvider(mod, ApexInfoProvider, apexInfos[i-1]) + } + } + + for _, alias := range aliases { + mctx.CreateAliasVariation(alias[0], alias[1]) + } + + return modules } -// 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 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. func UpdateUniqueApexVariationsForDeps(mctx BottomUpMutatorContext, am ApexModule) { - // 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 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 @@ -446,9 +514,10 @@ func UpdateUniqueApexVariationsForDeps(mctx BottomUpMutatorContext, am ApexModul return false } + // If any of the dependencies requires unique apex variations, so does this module. mctx.VisitDirectDeps(func(dep Module) { if depApexModule, ok := dep.(ApexModule); ok { - if anyInSameApex(depApexModule.apexModuleBase().apexVariations, am.apexModuleBase().apexVariations) && + if anyInSameApex(depApexModule.apexModuleBase().apexInfos, am.apexModuleBase().apexInfos) && (depApexModule.UniqueApexVariations() || depApexModule.apexModuleBase().ApexProperties.UniqueApexVariationsForDeps) { am.apexModuleBase().ApexProperties.UniqueApexVariationsForDeps = true @@ -457,10 +526,10 @@ func UpdateUniqueApexVariationsForDeps(mctx BottomUpMutatorContext, am ApexModul }) } -// UpdateDirectlyInAnyApex uses the final module to store if any variant of this -// module is directly in any APEX, and then copies the final value to all the modules. -// It also copies the DirectlyInAnyApex value to any direct dependencies with a -// CopyDirectlyInAnyApexTag dependency tag. +// UpdateDirectlyInAnyApex uses the final module to store if any variant of this module is directly +// in any APEX, and then copies the final value to all the modules. It also copies the +// DirectlyInAnyApex value to any direct dependencies with a CopyDirectlyInAnyApexTag dependency +// tag. func UpdateDirectlyInAnyApex(mctx BottomUpMutatorContext, am ApexModule) { base := am.apexModuleBase() // Copy DirectlyInAnyApex and InAnyApex from any direct dependencies with a @@ -475,14 +544,13 @@ func UpdateDirectlyInAnyApex(mctx BottomUpMutatorContext, am ApexModule) { if base.ApexProperties.DirectlyInAnyApex { // Variants of a module are always visited sequentially in order, so it is safe to - // write to another variant of this module. - // For a BottomUpMutator the PrimaryModule() is visited first and FinalModule() is - // visited last. + // write to another variant of this module. For a BottomUpMutator the + // PrimaryModule() is visited first and FinalModule() is visited last. mctx.FinalModule().(ApexModule).apexModuleBase().ApexProperties.AnyVariantDirectlyInAnyApex = true } - // If this is the FinalModule (last visited module) copy AnyVariantDirectlyInAnyApex to - // all the other variants + // If this is the FinalModule (last visited module) copy + // AnyVariantDirectlyInAnyApex to all the other variants if am == mctx.FinalModule().(ApexModule) { mctx.VisitAllModuleVariants(func(variant Module) { variant.(ApexModule).apexModuleBase().ApexProperties.AnyVariantDirectlyInAnyApex = @@ -491,6 +559,7 @@ func UpdateDirectlyInAnyApex(mctx BottomUpMutatorContext, am ApexModule) { } } +// ApexMembership tells how a module became part of an APEX. type ApexMembership int const ( @@ -499,20 +568,21 @@ const ( directlyInApex ) -// Each apexBundle has an apexContents, and modules in that apex have a provider containing the -// apexContents of each apexBundle they are part of. +// ApexContents gives an information about member modules of an apexBundle. Each apexBundle has an +// apexContents, and modules in that apex have a provider containing the apexContents of each +// apexBundle they are part of. type ApexContents struct { - ApexName string + // map from a module name to its membership to this apexBUndle contents map[string]ApexMembership } -func NewApexContents(name string, contents map[string]ApexMembership) *ApexContents { +func NewApexContents(contents map[string]ApexMembership) *ApexContents { return &ApexContents{ - ApexName: name, contents: contents, } } +// Updates an existing membership by adding a new direct (or indirect) membership func (i ApexMembership) Add(direct bool) ApexMembership { if direct || i == directlyInApex { return directlyInApex @@ -520,6 +590,10 @@ func (i ApexMembership) Add(direct bool) ApexMembership { return indirectlyInApex } +// Merges two membership into one. Merging is needed because a module can be a part of an apexBundle +// in many different paths. For example, it could be dependend on by the apexBundle directly, but at +// the same time, there might be an indirect dependency to the module. In that case, the more +// specific dependency (the direct one) is chosen. func (i ApexMembership) merge(other ApexMembership) ApexMembership { if other == directlyInApex || i == directlyInApex { return directlyInApex @@ -531,16 +605,19 @@ func (i ApexMembership) merge(other ApexMembership) ApexMembership { return notInApex } -func (ac *ApexContents) DirectlyInApex(name string) bool { - return ac.contents[name] == directlyInApex +// Tests whether a module named moduleName is directly included in the apexBundle where this +// ApexContents is tagged. +func (ac *ApexContents) DirectlyInApex(moduleName string) bool { + return ac.contents[moduleName] == directlyInApex } -func (ac *ApexContents) InApex(name string) bool { - return ac.contents[name] != notInApex +// Tests whether a module named moduleName is included in the apexBundle where this ApexContent is +// tagged. +func (ac *ApexContents) InApex(moduleName string) bool { + return ac.contents[moduleName] != notInApex } -// Tests whether a module named moduleName is directly depended on by all APEXes -// in an ApexInfo. +// Tests whether a module named moduleName is directly depended on by all APEXes in an ApexInfo. func DirectlyInAllApexes(apexInfo ApexInfo, moduleName string) bool { for _, contents := range apexInfo.ApexContents { if !contents.DirectlyInApex(moduleName) { @@ -550,12 +627,15 @@ func DirectlyInAllApexes(apexInfo ApexInfo, moduleName string) bool { return true } -func InitApexModule(m ApexModule) { - base := m.apexModuleBase() - base.canHaveApexVariants = true - - m.AddProperties(&base.ApexProperties) -} +//////////////////////////////////////////////////////////////////////////////////////////////////// +//Below are routines for extra safety checks. +// +// BuildDepsInfoLists is to flatten the dependency graph for an apexBundle into a text file +// (actually two in slightly different formats). The files are mostly for debugging, for example to +// see why a certain module is included in an APEX via which dependency path. +// +// CheckMinSdkVersion is to make sure that all modules in an apexBundle satisfy the min_sdk_version +// requirement of the apexBundle. // A dependency info for a single ApexModule, either direct or transitive. type ApexModuleDepInfo struct { @@ -705,7 +785,8 @@ type UpdatableModule interface { WalkPayloadDeps(ctx ModuleContext, do PayloadDepsCallback) } -// CheckMinSdkVersion checks if every dependency of an updatable module sets min_sdk_version accordingly +// CheckMinSdkVersion checks if every dependency of an updatable module sets min_sdk_version +// accordingly func CheckMinSdkVersion(m UpdatableModule, ctx ModuleContext, minSdkVersion ApiLevel) { // do not enforce min_sdk_version for host if ctx.Host() { @@ -725,8 +806,9 @@ func CheckMinSdkVersion(m UpdatableModule, ctx ModuleContext, minSdkVersion ApiL 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. + // 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) { diff --git a/apex/apex.go b/apex/apex.go index ce323ca4d..dff0855a5 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -709,7 +709,7 @@ func apexDepsMutator(mctx android.TopDownMutatorContext) { return true }) - apexContents := android.NewApexContents(mctx.ModuleName(), contents) + apexContents := android.NewApexContents(contents) mctx.SetProvider(ApexBundleInfoProvider, ApexBundleInfo{ Contents: apexContents, })