Merge "Documenting android/apex.go"

This commit is contained in:
Treehugger Robot 2020-11-23 00:44:35 +00:00 committed by Gerrit Code Review
commit b26070efef
2 changed files with 298 additions and 216 deletions

View File

@ -25,32 +25,54 @@ import (
) )
var ( var (
// This is the sdk version when APEX was first introduced
SdkVersion_Android10 = uncheckedFinalApiLevel(29) 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 { type ApexInfo struct {
// Name of the apex variation that this module is mutated into, or "" for // Name of the apex variation that this module (i.e. the apex variant of the module) is
// a platform variant. Note that a module can be included in multiple APEXes, // mutated into, or "" for a platform (i.e. non-APEX) variant. Note that a module can be
// in which case, the module is mutated into one or more variants, each of // included in multiple APEXes, in which case, the module is mutated into one or more
// which is for one or more APEXes. // 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 ApexVariationName string
// Serialized ApiLevel. Use via MinSdkVersion() method. Cannot be stored in // Serialized ApiLevel that this module has to support at minimum. Should be accessed via
// its struct form because this is cloned into properties structs, and // MinSdkVersion() method. Cannot be stored in its struct form because this is cloned into
// ApiLevel has private members. // properties structs, and ApiLevel has private members.
MinSdkVersionStr string MinSdkVersionStr string
// True if the module comes from an updatable APEX. // True if this module comes from an updatable apexBundle.
Updatable bool Updatable bool
// The list of SDK modules that the containing apexBundle depends on.
RequiredSdks SdkRefs 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 ApexContents []*ApexContents
} }
var ApexInfoProvider = blueprint.NewMutatorProvider(ApexInfo{}, "apex") 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 { func (i ApexInfo) mergedName(ctx PathContext) string {
name := "apex" + strconv.Itoa(i.MinSdkVersion(ctx).FinalOrFutureInt()) name := "apex" + strconv.Itoa(i.MinSdkVersion(ctx).FinalOrFutureInt())
for _, sdk := range i.RequiredSdks { for _, sdk := range i.RequiredSdks {
@ -59,14 +81,19 @@ func (i ApexInfo) mergedName(ctx PathContext) string {
return name return name
} }
func (this *ApexInfo) MinSdkVersion(ctx PathContext) ApiLevel { // MinSdkVersion gives the api level that this module has to support at minimum. This is from the
return ApiLevelOrPanic(ctx, this.MinSdkVersionStr) // 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 { func (i ApexInfo) IsForPlatform() bool {
return i.ApexVariationName == "" 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 { func (i ApexInfo) InApex(apex string) bool {
for _, a := range i.InApexes { for _, a := range i.InApexes {
if a == apex { if a == apex {
@ -76,102 +103,111 @@ func (i ApexInfo) InApex(apex string) bool {
return false return false
} }
// ApexTestForInfo stores the contents of APEXes for which this module is a test and thus has // ApexTestForInfo stores the contents of APEXes for which this module is a test - although this
// access to APEX internals. // module is not part of the APEX - and thus has access to APEX internals.
type ApexTestForInfo struct { type ApexTestForInfo struct {
ApexContents []*ApexContents ApexContents []*ApexContents
} }
var ApexTestForInfoProvider = blueprint.NewMutatorProvider(ApexTestForInfo{}, "apex_test_for") var ApexTestForInfoProvider = blueprint.NewMutatorProvider(ApexTestForInfo{}, "apex_test_for")
// Extracted from ApexModule to make it easier to define custom subsets of the // DepIsInSameApex defines an interface that should be used to determine whether a given dependency
// ApexModule interface and improve code navigation within the IDE. // 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 { type DepIsInSameApex interface {
// DepIsInSameApex tests if the other module 'dep' is installed to the same // DepIsInSameApex tests if the other module 'dep' is considered as part of the same APEX as
// APEX as this module // 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 DepIsInSameApex(ctx BaseModuleContext, dep Module) bool
} }
// ApexModule is the interface that a module type is expected to implement if // ApexModule is the interface that a module type is expected to implement if the module has to be
// the module has to be built differently depending on whether the module // built differently depending on whether the module is destined for an APEX or not (i.e., installed
// is destined for an apex or not (installed to one of the regular partitions). // to one of the regular partitions).
// //
// Native shared libraries are one such module type; when it is built for an // Native shared libraries are one such module type; when it is built for an APEX, it should depend
// APEX, it should depend only on stable interfaces such as NDK, stable AIDL, // only on stable interfaces such as NDK, stable AIDL, or C APIs from other APEXes.
// or C APIs from other APEXs.
// //
// A module implementing this interface will be mutated into multiple // A module implementing this interface will be mutated into multiple variations by apex.apexMutator
// variations by apex.apexMutator if it is directly or indirectly included // if it is directly or indirectly included in one or more APEXes. Specifically, if a module is
// in one or more APEXs. Specifically, if a module is included in apex.foo and // included in apex.foo and apex.bar then three apex variants are created: platform, 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.)
// apex.bar. The platform variant is for the regular partitions // while the other two are for the APEXs, respectively. The latter two variations can be merged (see
// (e.g., /system or /vendor, etc.) while the other two are for the APEXs, // mergedName) when the two APEXes have the same min_sdk_version requirement.
// respectively.
type ApexModule interface { type ApexModule interface {
Module Module
DepIsInSameApex DepIsInSameApex
apexModuleBase() *ApexModuleBase apexModuleBase() *ApexModuleBase
// Marks that this module should be built for the specified APEX. // Marks that this module should be built for the specified APEX. Call this BEFORE
// Call this before apex.apexMutator is run. // apex.apexMutator is run.
BuildForApex(apex ApexInfo) BuildForApex(apex ApexInfo)
// Returns true if this module is present in any APEXes // Returns true if this module is present in any APEX either directly or indirectly. Call
// directly or indirectly. // this after apex.apexMutator is run.
// Call this after apex.apexMutator is run.
InAnyApex() bool InAnyApex() bool
// Returns true if this module is directly in any APEXes. // Returns true if this module is directly in any APEX. Call this AFTER apex.apexMutator is
// Call this after apex.apexMutator is run. // run.
DirectlyInAnyApex() bool DirectlyInAnyApex() bool
// Returns true if any variant of this module is directly in any APEXes. // Returns true in the primary variant of a module if _any_ variant of the module is
// Call this after apex.apexMutator is run. // 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 AnyVariantDirectlyInAnyApex() bool
// Tests if this module could have APEX variants. APEX variants are // Tests if this module could have APEX variants. Even when a module type implements
// created only for the modules that returns true here. This is useful // ApexModule interface, APEX variants are created only for the module instances that return
// for not creating APEX variants for certain types of shared libraries // true here. This is useful for not creating APEX variants for certain types of shared
// such as NDK stubs. // libraries such as NDK stubs.
CanHaveApexVariants() bool CanHaveApexVariants() bool
// Tests if this module can be installed to APEX as a file. For example, // Tests if this module can be installed to APEX as a file. For example, this would return
// this would return true for shared libs while return false for static // true for shared libs while return false for static libs because static libs are not
// libs. // installable module (but it can still be mutated for APEX)
IsInstallableToApex() bool 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 AvailableFor(what string) bool
// Return true if this module is not available to platform (i.e. apex_available // Returns true if this module is not available to platform (i.e. apex_available property
// property doesn't have "//apex_available:platform"), or shouldn't be available // doesn't have "//apex_available:platform"), or shouldn't be available to platform, which
// to platform, which is the case when this module depends on other module that // is the case when this module depends on other module that isn't available to platform.
// isn't available to platform.
NotAvailableForPlatform() bool 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. // check-platform-availability mutator in the apex package.
SetNotAvailableForPlatform() SetNotAvailableForPlatform()
// List of APEXes that this module tests. The module has access to // Returns the list of APEXes that this module is a test for. The module has access to the
// the private part of the listed APEXes even when it is not included in the // private part of the listed APEXes even when it is not included in the APEXes. This by
// APEXes. // 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 TestFor() []string
// Returns nil if this module supports sdkVersion // Returns nil (success) if this module should support the given sdk version. Returns an
// Otherwise, returns error with reason // 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 ShouldSupportSdkVersion(ctx BaseModuleContext, sdkVersion ApiLevel) error
// Returns true if this module needs a unique variation per apex, for example if // Returns true if this module needs a unique variation per apex, effectively disabling the
// use_apex_name_macro is set. // 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 UniqueApexVariations() bool
} }
// Properties that are common to all module types implementing ApexModule interface.
type ApexProperties struct { type ApexProperties struct {
// Availability of this module in APEXes. Only the listed APEXes can contain // Availability of this module in APEXes. Only the listed APEXes can contain this module. If
// this module. If the module has stubs then other APEXes and the platform may // the module has stubs then other APEXes and the platform may access it through them
// access it through them (subject to visibility). // (subject to visibility).
// //
// "//apex_available:anyapex" is a pseudo APEX name that matches to any APEX. // "//apex_available:anyapex" is a pseudo APEX name that matches to any APEX.
// "//apex_available:platform" refers to non-APEX partitions like "system.img". // "//apex_available:platform" refers to non-APEX partitions like "system.img".
@ -179,29 +215,23 @@ type ApexProperties struct {
// Default is ["//apex_available:platform"]. // Default is ["//apex_available:platform"].
Apex_available []string Apex_available []string
// AnyVariantDirectlyInAnyApex is true in the primary variant of a module if _any_ variant // See ApexModule.InAnyApex()
// 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.
InAnyApex bool `blueprint:"mutated"` 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"` NotAvailableForPlatform bool `blueprint:"mutated"`
// See ApexModule.UniqueApexVariants()
UniqueApexVariationsForDeps bool `blueprint:"mutated"` UniqueApexVariationsForDeps bool `blueprint:"mutated"`
} }
// Marker interface that identifies dependencies that are excluded from APEX // Marker interface that identifies dependencies that are excluded from APEX contents.
// contents.
type ExcludeFromApexContentsTag interface { type ExcludeFromApexContentsTag interface {
blueprint.DependencyTag blueprint.DependencyTag
@ -209,85 +239,122 @@ type ExcludeFromApexContentsTag interface {
ExcludeFromApexContents() ExcludeFromApexContents()
} }
// Marker interface that identifies dependencies that should inherit the DirectlyInAnyApex // Marker interface that identifies dependencies that should inherit the DirectlyInAnyApex state
// state from the parent to the child. For example, stubs libraries are marked as // from the parent to the child. For example, stubs libraries are marked as DirectlyInAnyApex if
// DirectlyInAnyApex if their implementation is in an apex. // their implementation is in an apex.
type CopyDirectlyInAnyApexTag interface { type CopyDirectlyInAnyApexTag interface {
blueprint.DependencyTag blueprint.DependencyTag
// Method that differentiates this interface from others.
CopyDirectlyInAnyApex() 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(). // modules are expected to include this struct and call InitApexModule().
type ApexModuleBase struct { type ApexModuleBase struct {
ApexProperties ApexProperties ApexProperties ApexProperties
canHaveApexVariants bool canHaveApexVariants bool
apexVariationsLock sync.Mutex // protects apexVariations during parallel apexDepsMutator apexInfos []ApexInfo
apexVariations []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 { func (m *ApexModuleBase) apexModuleBase() *ApexModuleBase {
return m return m
} }
// Implements ApexModule
func (m *ApexModuleBase) ApexAvailable() []string { func (m *ApexModuleBase) ApexAvailable() []string {
return m.ApexProperties.Apex_available return m.ApexProperties.Apex_available
} }
func (m *ApexModuleBase) TestFor() []string { // Implements ApexModule
// To be implemented by concrete types inheriting ApexModuleBase
return nil
}
func (m *ApexModuleBase) UniqueApexVariations() bool {
return false
}
func (m *ApexModuleBase) BuildForApex(apex ApexInfo) { func (m *ApexModuleBase) BuildForApex(apex ApexInfo) {
m.apexVariationsLock.Lock() m.apexInfosLock.Lock()
defer m.apexVariationsLock.Unlock() defer m.apexInfosLock.Unlock()
for _, v := range m.apexVariations { for _, v := range m.apexInfos {
if v.ApexVariationName == apex.ApexVariationName { if v.ApexVariationName == apex.ApexVariationName {
return return
} }
} }
m.apexVariations = append(m.apexVariations, apex) m.apexInfos = append(m.apexInfos, apex)
}
func (m *ApexModuleBase) DirectlyInAnyApex() bool {
return m.ApexProperties.DirectlyInAnyApex
}
func (m *ApexModuleBase) AnyVariantDirectlyInAnyApex() bool {
return m.ApexProperties.AnyVariantDirectlyInAnyApex
} }
// Implements ApexModule
func (m *ApexModuleBase) InAnyApex() bool { func (m *ApexModuleBase) InAnyApex() bool {
return m.ApexProperties.InAnyApex 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 { func (m *ApexModuleBase) CanHaveApexVariants() bool {
return m.canHaveApexVariants return m.canHaveApexVariants
} }
// Implements ApexModule
func (m *ApexModuleBase) IsInstallableToApex() bool { func (m *ApexModuleBase) IsInstallableToApex() bool {
// should be overriden if needed // If needed, this will bel overridden by concrete types inheriting
// ApexModuleBase
return false 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 ( const (
AvailableToPlatform = "//apex_available:platform" AvailableToPlatform = "//apex_available:platform"
AvailableToAnyApex = "//apex_available:anyapex" AvailableToAnyApex = "//apex_available:anyapex"
AvailableToGkiApex = "com.android.gki.*" 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 { func CheckAvailableForApex(what string, apex_available []string) bool {
if len(apex_available) == 0 { 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 what == AvailableToPlatform
} }
return InList(what, apex_available) || 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)) (strings.HasPrefix(what, "com.android.gki.") && InList(AvailableToGkiApex, apex_available))
} }
// Implements ApexModule
func (m *ApexModuleBase) AvailableFor(what string) bool { func (m *ApexModuleBase) AvailableFor(what string) bool {
return CheckAvailableForApex(what, m.ApexProperties.Apex_available) return CheckAvailableForApex(what, m.ApexProperties.Apex_available)
} }
// Implements ApexModule
func (m *ApexModuleBase) NotAvailableForPlatform() bool { func (m *ApexModuleBase) NotAvailableForPlatform() bool {
return m.ApexProperties.NotAvailableForPlatform return m.ApexProperties.NotAvailableForPlatform
} }
// Implements ApexModule
func (m *ApexModuleBase) SetNotAvailableForPlatform() { func (m *ApexModuleBase) SetNotAvailableForPlatform() {
m.ApexProperties.NotAvailableForPlatform = true m.ApexProperties.NotAvailableForPlatform = true
} }
func (m *ApexModuleBase) DepIsInSameApex(ctx BaseModuleContext, dep Module) bool { // This function makes sure that the apex_available property is valid
// 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) checkApexAvailableProperty(mctx BaseModuleContext) { func (m *ApexModuleBase) checkApexAvailableProperty(mctx BaseModuleContext) {
for _, n := range m.ApexProperties.Apex_available { for _, n := range m.ApexProperties.Apex_available {
if n == AvailableToPlatform || n == AvailableToAnyApex || n == AvailableToGkiApex { 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) 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 } 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 // 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. It returns the reduced list of variations and a list of aliases from the original
// variation names to the new variation names. // variation names to the new variation names.
func mergeApexVariations(ctx PathContext, apexVariations []ApexInfo) (merged []ApexInfo, aliases [][2]string) { func mergeApexVariations(ctx PathContext, apexInfos []ApexInfo) (merged []ApexInfo, aliases [][2]string) {
sort.Sort(byApexName(apexVariations)) sort.Sort(byApexName(apexInfos))
seen := make(map[string]int) seen := make(map[string]int)
for _, apexInfo := range apexVariations { for _, apexInfo := range apexInfos {
apexName := apexInfo.ApexVariationName apexName := apexInfo.ApexVariationName
mergedName := apexInfo.mergedName(ctx) mergedName := apexInfo.mergedName(ctx)
if index, exists := seen[mergedName]; exists { if index, exists := seen[mergedName]; exists {
// Variants having the same mergedName are deduped
merged[index].InApexes = append(merged[index].InApexes, apexName) merged[index].InApexes = append(merged[index].InApexes, apexName)
merged[index].ApexContents = append(merged[index].ApexContents, apexInfo.ApexContents...) merged[index].ApexContents = append(merged[index].ApexContents, apexInfo.ApexContents...)
merged[index].Updatable = merged[index].Updatable || apexInfo.Updatable merged[index].Updatable = merged[index].Updatable || apexInfo.Updatable
} else { } else {
seen[mergedName] = len(merged) seen[mergedName] = len(merged)
apexInfo.ApexVariationName = apexInfo.mergedName(ctx) apexInfo.ApexVariationName = mergedName
apexInfo.InApexes = CopyOf(apexInfo.InApexes) apexInfo.InApexes = CopyOf(apexInfo.InApexes)
apexInfo.ApexContents = append([]*ApexContents(nil), apexInfo.ApexContents...) apexInfo.ApexContents = append([]*ApexContents(nil), apexInfo.ApexContents...)
merged = append(merged, apexInfo) merged = append(merged, apexInfo)
@ -356,75 +421,78 @@ func mergeApexVariations(ctx PathContext, apexVariations []ApexInfo) (merged []A
return merged, aliases 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 { func CreateApexVariations(mctx BottomUpMutatorContext, module ApexModule) []Module {
base := module.apexModuleBase() base := module.apexModuleBase()
if len(base.apexVariations) > 0 {
base.checkApexAvailableProperty(mctx)
var apexVariations []ApexInfo // Shortcut
var aliases [][2]string if len(base.apexInfos) == 0 {
if !mctx.Module().(ApexModule).UniqueApexVariations() && !base.ApexProperties.UniqueApexVariationsForDeps { return nil
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
} }
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 // UpdateUniqueApexVariationsForDeps sets UniqueApexVariationsForDeps if any dependencies that are
// that are in the same APEX have unique APEX variations so that the module can link against // in the same APEX have unique APEX variations so that the module can link against the right
// the right variant. // variant.
func UpdateUniqueApexVariationsForDeps(mctx BottomUpMutatorContext, am ApexModule) { func UpdateUniqueApexVariationsForDeps(mctx BottomUpMutatorContext, am ApexModule) {
// anyInSameApex returns true if the two ApexInfo lists contain any values in an InApexes list // anyInSameApex returns true if the two ApexInfo lists contain any values in an InApexes
// in common. It is used instead of DepIsInSameApex because it needs to determine if the dep // list in common. It is used instead of DepIsInSameApex because it needs to determine if
// is in the same APEX due to being directly included, not only if it is included _because_ it // the dep is in the same APEX due to being directly included, not only if it is included
// is a dependency. // _because_ it is a dependency.
anyInSameApex := func(a, b []ApexInfo) bool { anyInSameApex := func(a, b []ApexInfo) bool {
collectApexes := func(infos []ApexInfo) []string { collectApexes := func(infos []ApexInfo) []string {
var ret []string var ret []string
@ -446,9 +514,10 @@ func UpdateUniqueApexVariationsForDeps(mctx BottomUpMutatorContext, am ApexModul
return false return false
} }
// If any of the dependencies requires unique apex variations, so does this module.
mctx.VisitDirectDeps(func(dep Module) { mctx.VisitDirectDeps(func(dep Module) {
if depApexModule, ok := dep.(ApexModule); ok { 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.UniqueApexVariations() ||
depApexModule.apexModuleBase().ApexProperties.UniqueApexVariationsForDeps) { depApexModule.apexModuleBase().ApexProperties.UniqueApexVariationsForDeps) {
am.apexModuleBase().ApexProperties.UniqueApexVariationsForDeps = true 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 // UpdateDirectlyInAnyApex uses the final module to store if any variant of this module is directly
// module is directly in any APEX, and then copies the final value to all the modules. // in any APEX, and then copies the final value to all the modules. It also copies the
// It also copies the DirectlyInAnyApex value to any direct dependencies with a // DirectlyInAnyApex value to any direct dependencies with a CopyDirectlyInAnyApexTag dependency
// CopyDirectlyInAnyApexTag dependency tag. // tag.
func UpdateDirectlyInAnyApex(mctx BottomUpMutatorContext, am ApexModule) { func UpdateDirectlyInAnyApex(mctx BottomUpMutatorContext, am ApexModule) {
base := am.apexModuleBase() base := am.apexModuleBase()
// Copy DirectlyInAnyApex and InAnyApex from any direct dependencies with a // Copy DirectlyInAnyApex and InAnyApex from any direct dependencies with a
@ -475,14 +544,13 @@ func UpdateDirectlyInAnyApex(mctx BottomUpMutatorContext, am ApexModule) {
if base.ApexProperties.DirectlyInAnyApex { if base.ApexProperties.DirectlyInAnyApex {
// Variants of a module are always visited sequentially in order, so it is safe to // Variants of a module are always visited sequentially in order, so it is safe to
// write to another variant of this module. // write to another variant of this module. For a BottomUpMutator the
// For a BottomUpMutator the PrimaryModule() is visited first and FinalModule() is // PrimaryModule() is visited first and FinalModule() is visited last.
// visited last.
mctx.FinalModule().(ApexModule).apexModuleBase().ApexProperties.AnyVariantDirectlyInAnyApex = true mctx.FinalModule().(ApexModule).apexModuleBase().ApexProperties.AnyVariantDirectlyInAnyApex = true
} }
// If this is the FinalModule (last visited module) copy AnyVariantDirectlyInAnyApex to // If this is the FinalModule (last visited module) copy
// all the other variants // AnyVariantDirectlyInAnyApex to all the other variants
if am == mctx.FinalModule().(ApexModule) { if am == mctx.FinalModule().(ApexModule) {
mctx.VisitAllModuleVariants(func(variant Module) { mctx.VisitAllModuleVariants(func(variant Module) {
variant.(ApexModule).apexModuleBase().ApexProperties.AnyVariantDirectlyInAnyApex = 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 type ApexMembership int
const ( const (
@ -499,20 +568,21 @@ const (
directlyInApex directlyInApex
) )
// Each apexBundle has an apexContents, and modules in that apex have a provider containing the // ApexContents gives an information about member modules of an apexBundle. Each apexBundle has an
// apexContents of each apexBundle they are part of. // apexContents, and modules in that apex have a provider containing the apexContents of each
// apexBundle they are part of.
type ApexContents struct { type ApexContents struct {
ApexName string // map from a module name to its membership to this apexBUndle
contents map[string]ApexMembership contents map[string]ApexMembership
} }
func NewApexContents(name string, contents map[string]ApexMembership) *ApexContents { func NewApexContents(contents map[string]ApexMembership) *ApexContents {
return &ApexContents{ return &ApexContents{
ApexName: name,
contents: contents, contents: contents,
} }
} }
// Updates an existing membership by adding a new direct (or indirect) membership
func (i ApexMembership) Add(direct bool) ApexMembership { func (i ApexMembership) Add(direct bool) ApexMembership {
if direct || i == directlyInApex { if direct || i == directlyInApex {
return directlyInApex return directlyInApex
@ -520,6 +590,10 @@ func (i ApexMembership) Add(direct bool) ApexMembership {
return indirectlyInApex 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 { func (i ApexMembership) merge(other ApexMembership) ApexMembership {
if other == directlyInApex || i == directlyInApex { if other == directlyInApex || i == directlyInApex {
return directlyInApex return directlyInApex
@ -531,16 +605,19 @@ func (i ApexMembership) merge(other ApexMembership) ApexMembership {
return notInApex return notInApex
} }
func (ac *ApexContents) DirectlyInApex(name string) bool { // Tests whether a module named moduleName is directly included in the apexBundle where this
return ac.contents[name] == directlyInApex // ApexContents is tagged.
func (ac *ApexContents) DirectlyInApex(moduleName string) bool {
return ac.contents[moduleName] == directlyInApex
} }
func (ac *ApexContents) InApex(name string) bool { // Tests whether a module named moduleName is included in the apexBundle where this ApexContent is
return ac.contents[name] != notInApex // 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 // Tests whether a module named moduleName is directly depended on by all APEXes in an ApexInfo.
// in an ApexInfo.
func DirectlyInAllApexes(apexInfo ApexInfo, moduleName string) bool { func DirectlyInAllApexes(apexInfo ApexInfo, moduleName string) bool {
for _, contents := range apexInfo.ApexContents { for _, contents := range apexInfo.ApexContents {
if !contents.DirectlyInApex(moduleName) { if !contents.DirectlyInApex(moduleName) {
@ -550,12 +627,15 @@ func DirectlyInAllApexes(apexInfo ApexInfo, moduleName string) bool {
return true return true
} }
func InitApexModule(m ApexModule) { ////////////////////////////////////////////////////////////////////////////////////////////////////
base := m.apexModuleBase() //Below are routines for extra safety checks.
base.canHaveApexVariants = true //
// BuildDepsInfoLists is to flatten the dependency graph for an apexBundle into a text file
m.AddProperties(&base.ApexProperties) // (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. // A dependency info for a single ApexModule, either direct or transitive.
type ApexModuleDepInfo struct { type ApexModuleDepInfo struct {
@ -705,7 +785,8 @@ type UpdatableModule interface {
WalkPayloadDeps(ctx ModuleContext, do PayloadDepsCallback) 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) { func CheckMinSdkVersion(m UpdatableModule, ctx ModuleContext, minSdkVersion ApiLevel) {
// do not enforce min_sdk_version for host // do not enforce min_sdk_version for host
if ctx.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 { m.WalkPayloadDeps(ctx, func(ctx ModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool {
if externalDep { if externalDep {
// external deps are outside the payload boundary, which is "stable" interface. // external deps are outside the payload boundary, which is "stable"
// We don't have to check min_sdk_version for external dependencies. // interface. We don't have to check min_sdk_version for external
// dependencies.
return false return false
} }
if am, ok := from.(DepIsInSameApex); ok && !am.DepIsInSameApex(ctx, to) { if am, ok := from.(DepIsInSameApex); ok && !am.DepIsInSameApex(ctx, to) {

View File

@ -709,7 +709,7 @@ func apexDepsMutator(mctx android.TopDownMutatorContext) {
return true return true
}) })
apexContents := android.NewApexContents(mctx.ModuleName(), contents) apexContents := android.NewApexContents(contents)
mctx.SetProvider(ApexBundleInfoProvider, ApexBundleInfo{ mctx.SetProvider(ApexBundleInfoProvider, ApexBundleInfo{
Contents: apexContents, Contents: apexContents,
}) })