diff options
Diffstat (limited to 'android')
| -rw-r--r-- | android/apex.go | 126 | ||||
| -rw-r--r-- | android/apex_test.go | 111 | ||||
| -rw-r--r-- | android/module.go | 3 | ||||
| -rw-r--r-- | android/testing.go | 2 |
4 files changed, 225 insertions, 17 deletions
diff --git a/android/apex.go b/android/apex.go index 100beb012..83a7fe1f4 100644 --- a/android/apex.go +++ b/android/apex.go @@ -34,6 +34,17 @@ type ApexInfo struct { MinSdkVersion int Updatable bool + RequiredSdks SdkRefs + + InApexes []string +} + +func (i ApexInfo) mergedName() string { + name := "apex" + strconv.Itoa(i.MinSdkVersion) + for _, sdk := range i.RequiredSdks { + name += "_" + sdk.Name + "_" + sdk.Version + } + return name } // Extracted from ApexModule to make it easier to define custom subsets of the @@ -69,17 +80,20 @@ type ApexModule interface { // Call this before apex.apexMutator is run. BuildForApex(apex ApexInfo) - // Returns the APEXes that this module will be built for - ApexVariations() []ApexInfo - // Returns the name of APEX variation that this module will be built for. - //Empty string is returned when 'IsForPlatform() == true'. Note that a - // module can be included in multiple APEXes, in which case, the module - // is mutated into multiple modules each of which for an APEX. This method - // returns the name of the APEX that a variant module is for. + // Empty string is returned when 'IsForPlatform() == true'. Note that a + // module can beincluded in multiple APEXes, in which case, the module + // is mutated into one or more variants, each of which is for one or + // more APEXes. This method returns the name of the APEX variation of + // the module. // Call this after apex.apexMutator is run. ApexVariationName() string + // Returns the name of the APEX modules that this variant of this module + // is present in. + // Call this after apex.apexMutator is run. + InApexes() []string + // Tests whether this module will be built for the platform or not. // This is a shortcut for ApexVariationName() == "" IsForPlatform() bool @@ -128,6 +142,15 @@ type ApexModule interface { // Returns nil if this module supports sdkVersion // Otherwise, returns error with reason ShouldSupportSdkVersion(ctx BaseModuleContext, sdkVersion int) error + + // Returns true if this module needs a unique variation per apex, for example if + // use_apex_name_macro is set. + UniqueApexVariations() bool + + // UpdateUniqueApexVariationsForDeps sets m.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(mctx BottomUpMutatorContext) } type ApexProperties struct { @@ -144,6 +167,8 @@ type ApexProperties struct { Info ApexInfo `blueprint:"mutated"` NotAvailableForPlatform bool `blueprint:"mutated"` + + UniqueApexVariationsForDeps bool `blueprint:"mutated"` } // Marker interface that identifies dependencies that are excluded from APEX @@ -179,6 +204,22 @@ func (m *ApexModuleBase) TestFor() []string { return nil } +func (m *ApexModuleBase) UniqueApexVariations() bool { + return false +} + +func (m *ApexModuleBase) UpdateUniqueApexVariationsForDeps(mctx BottomUpMutatorContext) { + mctx.VisitDirectDeps(func(dep Module) { + if depApexModule, ok := dep.(ApexModule); ok { + if depApexModule.DepIsInSameApex(mctx, depApexModule) && + (depApexModule.UniqueApexVariations() || + depApexModule.apexModuleBase().ApexProperties.UniqueApexVariationsForDeps) { + m.ApexProperties.UniqueApexVariationsForDeps = true + } + } + }) +} + func (m *ApexModuleBase) BuildForApex(apex ApexInfo) { m.apexVariationsLock.Lock() defer m.apexVariationsLock.Unlock() @@ -190,14 +231,14 @@ func (m *ApexModuleBase) BuildForApex(apex ApexInfo) { m.apexVariations = append(m.apexVariations, apex) } -func (m *ApexModuleBase) ApexVariations() []ApexInfo { - return m.apexVariations -} - func (m *ApexModuleBase) ApexVariationName() string { return m.ApexProperties.Info.ApexVariationName } +func (m *ApexModuleBase) InApexes() []string { + return m.ApexProperties.Info.InApexes +} + func (m *ApexModuleBase) IsForPlatform() bool { return m.ApexProperties.Info.ApexVariationName == "" } @@ -278,14 +319,45 @@ 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(apexVariations []ApexInfo) (merged []ApexInfo, aliases [][2]string) { + sort.Sort(byApexName(apexVariations)) + seen := make(map[string]int) + for _, apexInfo := range apexVariations { + apexName := apexInfo.ApexVariationName + mergedName := apexInfo.mergedName() + if index, exists := seen[mergedName]; exists { + merged[index].InApexes = append(merged[index].InApexes, apexName) + merged[index].Updatable = merged[index].Updatable || apexInfo.Updatable + } else { + seen[mergedName] = len(merged) + apexInfo.ApexVariationName = apexInfo.mergedName() + apexInfo.InApexes = CopyOf(apexInfo.InApexes) + merged = append(merged, apexInfo) + } + aliases = append(aliases, [2]string{apexName, mergedName}) + } + return merged, aliases +} + func (m *ApexModuleBase) CreateApexVariations(mctx BottomUpMutatorContext) []Module { if len(m.apexVariations) > 0 { m.checkApexAvailableProperty(mctx) - sort.Sort(byApexName(m.apexVariations)) + var apexVariations []ApexInfo + var aliases [][2]string + if !mctx.Module().(ApexModule).UniqueApexVariations() && !m.ApexProperties.UniqueApexVariationsForDeps { + apexVariations, aliases = mergeApexVariations(m.apexVariations) + } else { + apexVariations = m.apexVariations + } + + sort.Sort(byApexName(apexVariations)) variations := []string{} variations = append(variations, "") // Original variation for platform - for _, apex := range m.apexVariations { + for _, apex := range apexVariations { variations = append(variations, apex.ApexVariationName) } @@ -302,9 +374,14 @@ func (m *ApexModuleBase) CreateApexVariations(mctx BottomUpMutatorContext) []Mod mod.MakeUninstallable() } if !platformVariation { - mod.(ApexModule).apexModuleBase().ApexProperties.Info = m.apexVariations[i-1] + mod.(ApexModule).apexModuleBase().ApexProperties.Info = apexVariations[i-1] } } + + for _, alias := range aliases { + mctx.CreateAliasVariation(alias[0], alias[1]) + } + return modules } return nil @@ -339,6 +416,9 @@ func UpdateApexDependency(apex ApexInfo, moduleName string, directDep bool) { apexNamesMap()[moduleName] = apexesForModule } apexesForModule[apex.ApexVariationName] = apexesForModule[apex.ApexVariationName] || directDep + for _, apexName := range apex.InApexes { + apexesForModule[apexName] = apexesForModule[apex.ApexVariationName] || directDep + } } // TODO(b/146393795): remove this when b/146393795 is fixed @@ -354,12 +434,26 @@ func ClearApexDependency() { func DirectlyInApex(apexName string, moduleName string) bool { apexNamesMapMutex.Lock() defer apexNamesMapMutex.Unlock() - if apexNames, ok := apexNamesMap()[moduleName]; ok { - return apexNames[apexName] + if apexNamesForModule, ok := apexNamesMap()[moduleName]; ok { + return apexNamesForModule[apexName] } return false } +// Tests whether a module named moduleName is directly depended on by all APEXes +// in a list of apexNames. +func DirectlyInAllApexes(apexNames []string, moduleName string) bool { + apexNamesMapMutex.Lock() + defer apexNamesMapMutex.Unlock() + for _, apexName := range apexNames { + apexNamesForModule := apexNamesMap()[moduleName] + if !apexNamesForModule[apexName] { + return false + } + } + return true +} + type hostContext interface { Host() bool } diff --git a/android/apex_test.go b/android/apex_test.go new file mode 100644 index 000000000..db02833e7 --- /dev/null +++ b/android/apex_test.go @@ -0,0 +1,111 @@ +// Copyright 2020 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package android + +import ( + "reflect" + "testing" +) + +func Test_mergeApexVariations(t *testing.T) { + tests := []struct { + name string + in []ApexInfo + wantMerged []ApexInfo + wantAliases [][2]string + }{ + { + name: "single", + in: []ApexInfo{ + {"foo", 10000, false, nil, []string{"foo"}}, + }, + wantMerged: []ApexInfo{ + {"apex10000", 10000, false, nil, []string{"foo"}}, + }, + wantAliases: [][2]string{ + {"foo", "apex10000"}, + }, + }, + { + name: "merge", + in: []ApexInfo{ + {"foo", 10000, false, SdkRefs{{"baz", "1"}}, []string{"foo"}}, + {"bar", 10000, false, SdkRefs{{"baz", "1"}}, []string{"bar"}}, + }, + wantMerged: []ApexInfo{ + {"apex10000_baz_1", 10000, false, SdkRefs{{"baz", "1"}}, []string{"bar", "foo"}}, + }, + wantAliases: [][2]string{ + {"bar", "apex10000_baz_1"}, + {"foo", "apex10000_baz_1"}, + }, + }, + { + name: "don't merge version", + in: []ApexInfo{ + {"foo", 10000, false, nil, []string{"foo"}}, + {"bar", 30, false, nil, []string{"bar"}}, + }, + wantMerged: []ApexInfo{ + {"apex30", 30, false, nil, []string{"bar"}}, + {"apex10000", 10000, false, nil, []string{"foo"}}, + }, + wantAliases: [][2]string{ + {"bar", "apex30"}, + {"foo", "apex10000"}, + }, + }, + { + name: "merge updatable", + in: []ApexInfo{ + {"foo", 10000, false, nil, []string{"foo"}}, + {"bar", 10000, true, nil, []string{"bar"}}, + }, + wantMerged: []ApexInfo{ + {"apex10000", 10000, true, nil, []string{"bar", "foo"}}, + }, + wantAliases: [][2]string{ + {"bar", "apex10000"}, + {"foo", "apex10000"}, + }, + }, + { + name: "don't merge sdks", + in: []ApexInfo{ + {"foo", 10000, false, SdkRefs{{"baz", "1"}}, []string{"foo"}}, + {"bar", 10000, false, SdkRefs{{"baz", "2"}}, []string{"bar"}}, + }, + wantMerged: []ApexInfo{ + {"apex10000_baz_2", 10000, false, SdkRefs{{"baz", "2"}}, []string{"bar"}}, + {"apex10000_baz_1", 10000, false, SdkRefs{{"baz", "1"}}, []string{"foo"}}, + }, + wantAliases: [][2]string{ + {"bar", "apex10000_baz_2"}, + {"foo", "apex10000_baz_1"}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotMerged, gotAliases := mergeApexVariations(tt.in) + if !reflect.DeepEqual(gotMerged, tt.wantMerged) { + t.Errorf("mergeApexVariations() gotMerged = %v, want %v", gotMerged, tt.wantMerged) + } + if !reflect.DeepEqual(gotAliases, tt.wantAliases) { + t.Errorf("mergeApexVariations() gotAliases = %v, want %v", gotAliases, tt.wantAliases) + } + }) + } +} diff --git a/android/module.go b/android/module.go index b689a87f2..0eb8bc908 100644 --- a/android/module.go +++ b/android/module.go @@ -1769,6 +1769,9 @@ func (b *baseModuleContext) VisitDirectDepsBlueprint(visit func(blueprint.Module func (b *baseModuleContext) VisitDirectDeps(visit func(Module)) { b.bp.VisitDirectDeps(func(module blueprint.Module) { + if module == nil { + panic("Unexpected nil module in VisitDirectDeps") + } if aModule := b.validateAndroidModule(module, b.strictVisitDeps); aModule != nil { visit(aModule) } diff --git a/android/testing.go b/android/testing.go index f32d745c2..8a9134c75 100644 --- a/android/testing.go +++ b/android/testing.go @@ -394,7 +394,7 @@ func FailIfNoMatchingErrors(t *testing.T, pattern string, errs []error) { if !found { t.Errorf("missing the expected error %q (checked %d error(s))", pattern, len(errs)) for i, err := range errs { - t.Errorf("errs[%d] = %s", i, err) + t.Errorf("errs[%d] = %q", i, err) } } } |