diff options
Diffstat (limited to 'android/apex.go')
| -rw-r--r-- | android/apex.go | 291 |
1 files changed, 222 insertions, 69 deletions
diff --git a/android/apex.go b/android/apex.go index c9b4a0b7c..ecab8e3fe 100644 --- a/android/apex.go +++ b/android/apex.go @@ -16,6 +16,7 @@ package android import ( "fmt" + "slices" "sort" "strconv" "strings" @@ -36,11 +37,7 @@ var ( // Accessible via `ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)` type ApexInfo struct { // 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 this name and the - // Soong module name of the APEX can be different. That happens when there is - // `override_apex` that overrides `apex`. In that case, both Soong modules have the same - // apex variation name which usually is `com.android.foo`. This name is also the `name` - // in the path `/apex/<name>` where this apex is activated on at runtime. + // mutated into, or "" for a platform (i.e. non-APEX) variant. // // Also 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 @@ -84,9 +81,21 @@ type ApexInfo struct { // // See Prebuilt.ApexInfoMutator for more information. ForPrebuiltApex bool + + // Returns the name of the test apexes that this module is included in. + TestApexes []string + + // Returns the name of the overridden apex (com.android.foo) + BaseApexName string } -var ApexInfoProvider = blueprint.NewMutatorProvider(ApexInfo{}, "apex") +// AllApexInfo holds the ApexInfo of all apexes that include this module. +type AllApexInfo struct { + ApexInfos []ApexInfo +} + +var ApexInfoProvider = blueprint.NewMutatorProvider[ApexInfo]("apex_mutate") +var AllApexInfoProvider = blueprint.NewMutatorProvider[*AllApexInfo]("apex_info") func (i ApexInfo) AddJSONData(d *map[string]interface{}) { (*d)["Apex"] = map[string]interface{}{ @@ -105,7 +114,7 @@ func (i ApexInfo) AddJSONData(d *map[string]interface{}) { // are configured to have the same alias variation named apex29. Whether platform APIs is allowed // or not also matters; if two APEXes don't have the same allowance, they get different names and // thus wouldn't be merged. -func (i ApexInfo) mergedName(ctx PathContext) string { +func (i ApexInfo) mergedName() string { name := "apex" + strconv.Itoa(i.MinSdkVersion.FinalOrFutureInt()) return name } @@ -142,7 +151,14 @@ type ApexTestForInfo struct { ApexContents []*ApexContents } -var ApexTestForInfoProvider = blueprint.NewMutatorProvider(ApexTestForInfo{}, "apex_test_for") +var ApexTestForInfoProvider = blueprint.NewMutatorProvider[ApexTestForInfo]("apex_test_for") + +// ApexBundleInfo contains information about the dependencies of an apex +type ApexBundleInfo struct { + Contents *ApexContents +} + +var ApexBundleInfoProvider = blueprint.NewMutatorProvider[ApexBundleInfo]("apex_info") // 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 @@ -287,6 +303,9 @@ type ApexProperties struct { // See ApexModule.UniqueApexVariants() UniqueApexVariationsForDeps bool `blueprint:"mutated"` + + // The test apexes that includes this apex variant + TestApexes []string `blueprint:"mutated"` } // Marker interface that identifies dependencies that are excluded from APEX contents. @@ -334,7 +353,8 @@ type SkipApexAllowedDependenciesCheck interface { // 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 + ApexProperties ApexProperties + apexPropertiesLock sync.Mutex // protects ApexProperties during parallel apexDirectlyInAnyMutator canHaveApexVariants bool @@ -429,6 +449,11 @@ func (m *ApexModuleBase) TestFor() []string { return nil } +// Returns the test apexes that this module is included in. +func (m *ApexModuleBase) TestApexes() []string { + return m.ApexProperties.TestApexes +} + // Implements ApexModule func (m *ApexModuleBase) UniqueApexVariations() bool { // If needed, this will bel overridden by concrete types inheriting @@ -451,6 +476,14 @@ const ( AvailableToGkiApex = "com.android.gki.*" ) +var ( + AvailableToRecognziedWildcards = []string{ + AvailableToPlatform, + AvailableToAnyApex, + AvailableToGkiApex, + } +) + // 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 @@ -517,17 +550,10 @@ func AvailableToSameApexes(mod1, mod2 ApexModule) bool { return true } -type byApexName []ApexInfo - -func (a byApexName) Len() int { return len(a) } -func (a byApexName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a byApexName) Less(i, j int) bool { return a[i].ApexVariationName < a[j].ApexVariationName } - // mergeApexVariations deduplicates apex variations that would build identically into a common // variation. It returns the reduced list of variations and a list of aliases from the original // variation names to the new variation names. -func mergeApexVariations(ctx PathContext, apexInfos []ApexInfo) (merged []ApexInfo, aliases [][2]string) { - sort.Sort(byApexName(apexInfos)) +func mergeApexVariations(apexInfos []ApexInfo) (merged []ApexInfo, aliases [][2]string) { seen := make(map[string]int) for _, apexInfo := range apexInfos { // If this is for a prebuilt apex then use the actual name of the apex variation to prevent this @@ -541,7 +567,7 @@ func mergeApexVariations(ctx PathContext, apexInfos []ApexInfo) (merged []ApexIn // this one into it, otherwise create a new merged ApexInfo from this one and save it away so // other ApexInfo instances can be merged into it. variantName := apexInfo.ApexVariationName - mergedName := apexInfo.mergedName(ctx) + mergedName := apexInfo.mergedName() if index, exists := seen[mergedName]; exists { // Variants having the same mergedName are deduped merged[index].InApexVariants = append(merged[index].InApexVariants, variantName) @@ -551,12 +577,14 @@ func mergeApexVariations(ctx PathContext, apexInfos []ApexInfo) (merged []ApexIn // Platform APIs is allowed for this module only when all APEXes containing // the module are with `use_platform_apis: true`. merged[index].UsePlatformApis = merged[index].UsePlatformApis && apexInfo.UsePlatformApis + merged[index].TestApexes = append(merged[index].TestApexes, apexInfo.TestApexes...) } else { seen[mergedName] = len(merged) apexInfo.ApexVariationName = mergedName apexInfo.InApexVariants = CopyOf(apexInfo.InApexVariants) apexInfo.InApexModules = CopyOf(apexInfo.InApexModules) apexInfo.ApexContents = append([]*ApexContents(nil), apexInfo.ApexContents...) + apexInfo.TestApexes = CopyOf(apexInfo.TestApexes) merged = append(merged, apexInfo) } aliases = append(aliases, [2]string{variantName, mergedName}) @@ -564,68 +592,137 @@ func mergeApexVariations(ctx PathContext, apexInfos []ApexInfo) (merged []ApexIn 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 { +// IncomingApexTransition is called by apexTransitionMutator.IncomingTransition on modules that can be in apexes. +// The incomingVariation can be either the name of an apex if the dependency is coming directly from an apex +// module, or it can be the name of an apex variation (e.g. apex10000) if it is coming from another module that +// is in the apex. +func IncomingApexTransition(ctx IncomingTransitionContext, incomingVariation string) string { + module := ctx.Module().(ApexModule) + base := module.apexModuleBase() + + var apexInfos []ApexInfo + if allApexInfos, ok := ModuleProvider(ctx, AllApexInfoProvider); ok { + apexInfos = allApexInfos.ApexInfos + } + + // Dependencies from platform variations go to the platform variation. + if incomingVariation == "" { + return "" + } + + if len(apexInfos) == 0 { + if ctx.IsAddingDependency() { + // If this module has no apex variations we can't do any mapping on the incoming variation, just return it + // and let the caller get a "missing variant" error. + return incomingVariation + } else { + // If this module has no apex variations the use the platform variation. + return "" + } + } + + // Convert the list of apex infos into from the AllApexInfoProvider into the merged list + // of apex variations and the aliases from apex names to apex variations. + var aliases [][2]string + if !module.UniqueApexVariations() && !base.ApexProperties.UniqueApexVariationsForDeps { + apexInfos, aliases = mergeApexVariations(apexInfos) + } + + // Check if the incoming variation matches an apex name, and if so use the corresponding + // apex variation. + aliasIndex := slices.IndexFunc(aliases, func(alias [2]string) bool { + return alias[0] == incomingVariation + }) + if aliasIndex >= 0 { + return aliases[aliasIndex][1] + } + + // Check if the incoming variation matches an apex variation. + apexIndex := slices.IndexFunc(apexInfos, func(info ApexInfo) bool { + return info.ApexVariationName == incomingVariation + }) + if apexIndex >= 0 { + return incomingVariation + } + + return "" +} + +func MutateApexTransition(ctx BaseModuleContext, variation string) { + module := ctx.Module().(ApexModule) base := module.apexModuleBase() + platformVariation := variation == "" + + var apexInfos []ApexInfo + if allApexInfos, ok := ModuleProvider(ctx, AllApexInfoProvider); ok { + apexInfos = allApexInfos.ApexInfos + } // Shortcut - if len(base.apexInfos) == 0 { - return nil + if len(apexInfos) == 0 { + return } // Do some validity checks. // TODO(jiyong): is this the right place? - base.checkApexAvailableProperty(mctx) + base.checkApexAvailableProperty(ctx) - 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 + if !module.UniqueApexVariations() && !base.ApexProperties.UniqueApexVariationsForDeps { + apexInfos, _ = mergeApexVariations(apexInfos) } - // base.apexInfos is only needed to propagate the list of apexes from apexInfoMutator 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()]) + inApex = inApex.merge(apexContents.contents[ctx.ModuleName()]) } } base.ApexProperties.InAnyApex = true base.ApexProperties.DirectlyInAnyApex = inApex == directlyInApex - defaultVariation := "" - mctx.SetDefaultDependencyVariation(&defaultVariation) + if platformVariation && !ctx.Host() && !module.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 + module.MakeUninstallable() + } + if !platformVariation { + var thisApexInfo ApexInfo - 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]) + apexIndex := slices.IndexFunc(apexInfos, func(info ApexInfo) bool { + return info.ApexVariationName == variation + }) + if apexIndex >= 0 { + thisApexInfo = apexInfos[apexIndex] + } else { + panic(fmt.Errorf("failed to find apexInfo for incoming variation %q", variation)) } + + SetProvider(ctx, ApexInfoProvider, thisApexInfo) } - for _, alias := range aliases { - mctx.CreateAliasVariation(alias[0], alias[1]) + // Set the value of TestApexes in every single apex variant. + // This allows each apex variant to be aware of the test apexes in the user provided apex_available. + var testApexes []string + for _, a := range apexInfos { + testApexes = append(testApexes, a.TestApexes...) } + base.ApexProperties.TestApexes = testApexes - return modules +} + +func ApexInfoMutator(ctx TopDownMutatorContext, module ApexModule) { + base := module.apexModuleBase() + if len(base.apexInfos) > 0 { + apexInfos := slices.Clone(base.apexInfos) + slices.SortFunc(apexInfos, func(a, b ApexInfo) int { + return strings.Compare(a.ApexVariationName, b.ApexVariationName) + }) + SetProvider(ctx, AllApexInfoProvider, &AllApexInfo{apexInfos}) + // base.apexInfos is only needed to propagate the list of apexes from the apex module to its + // contents within apexInfoMutator. Clear it so it doesn't accidentally get used later. + base.apexInfos = nil + } } // UpdateUniqueApexVariationsForDeps sets UniqueApexVariationsForDeps if any dependencies that are @@ -636,13 +733,16 @@ func UpdateUniqueApexVariationsForDeps(mctx BottomUpMutatorContext, am ApexModul // InApexVariants list in common. It is used instead of DepIsInSameApex because it needs to // determine if the dep is in the same APEX due to being directly included, not only if it // is included _because_ it is a dependency. - anyInSameApex := func(a, b []ApexInfo) bool { - collectApexes := func(infos []ApexInfo) []string { - var ret []string - for _, info := range infos { - ret = append(ret, info.InApexVariants...) + anyInSameApex := func(a, b ApexModule) bool { + collectApexes := func(m ApexModule) []string { + if allApexInfo, ok := OtherModuleProvider(mctx, m, AllApexInfoProvider); ok { + var ret []string + for _, info := range allApexInfo.ApexInfos { + ret = append(ret, info.InApexVariants...) + } + return ret } - return ret + return nil } aApexes := collectApexes(a) @@ -660,7 +760,7 @@ func UpdateUniqueApexVariationsForDeps(mctx BottomUpMutatorContext, am ApexModul // 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().apexInfos, am.apexModuleBase().apexInfos) && + if anyInSameApex(depApexModule, am) && (depApexModule.UniqueApexVariations() || depApexModule.apexModuleBase().ApexProperties.UniqueApexVariationsForDeps) { am.apexModuleBase().ApexProperties.UniqueApexVariationsForDeps = true @@ -671,18 +771,22 @@ 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. +// DirectlyInAnyApex value to any transitive 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 + // Copy DirectlyInAnyApex and InAnyApex from any transitive dependencies with a // CopyDirectlyInAnyApexTag dependency tag. - mctx.VisitDirectDeps(func(dep Module) { - if _, ok := mctx.OtherModuleDependencyTag(dep).(CopyDirectlyInAnyApexTag); ok { - depBase := dep.(ApexModule).apexModuleBase() + mctx.WalkDeps(func(child, parent Module) bool { + if _, ok := mctx.OtherModuleDependencyTag(child).(CopyDirectlyInAnyApexTag); ok { + depBase := child.(ApexModule).apexModuleBase() + depBase.apexPropertiesLock.Lock() + defer depBase.apexPropertiesLock.Unlock() depBase.ApexProperties.DirectlyInAnyApex = base.ApexProperties.DirectlyInAnyApex depBase.ApexProperties.InAnyApex = base.ApexProperties.InAnyApex + return true } + return false }) if base.ApexProperties.DirectlyInAnyApex { @@ -909,3 +1013,52 @@ func CheckMinSdkVersion(ctx ModuleContext, minSdkVersion ApiLevel, walk WalkPayl return true }) } + +// Construct ApiLevel object from min_sdk_version string value +func MinSdkVersionFromValue(ctx EarlyModuleContext, value string) ApiLevel { + if value == "" { + return NoneApiLevel + } + apiLevel, err := ApiLevelFromUser(ctx, value) + if err != nil { + ctx.PropertyErrorf("min_sdk_version", "%s", err.Error()) + return NoneApiLevel + } + return apiLevel +} + +// Implemented by apexBundle. +type ApexTestInterface interface { + // Return true if the apex bundle is an apex_test + IsTestApex() bool +} + +var ApexExportsInfoProvider = blueprint.NewProvider[ApexExportsInfo]() + +// ApexExportsInfo contains information about the artifacts provided by apexes to dexpreopt and hiddenapi +type ApexExportsInfo struct { + // Canonical name of this APEX. Used to determine the path to the activated APEX on + // device (/apex/<apex_name>) + ApexName string + + // Path to the image profile file on host (or empty, if profile is not generated). + ProfilePathOnHost Path + + // Map from the apex library name (without prebuilt_ prefix) to the dex file path on host + LibraryNameToDexJarPathOnHost map[string]Path +} + +var PrebuiltInfoProvider = blueprint.NewProvider[PrebuiltInfo]() + +// contents of prebuilt_info.json +type PrebuiltInfo struct { + // Name of the apex, without the prebuilt_ prefix + Name string + + Is_prebuilt bool + + // This is relative to root of the workspace. + // In case of mainline modules, this file contains the build_id that was used + // to generate the mainline module prebuilt. + Prebuilt_info_file_path string `json:",omitempty"` +} |