Merge "Fix boot jar handling when both source and prebuilt APEXes and modules are present."

This commit is contained in:
Martin Stjernholm 2021-01-29 13:15:02 +00:00 committed by Gerrit Code Review
commit c4e17317d1
4 changed files with 264 additions and 16 deletions

View file

@ -111,6 +111,19 @@ func (i ApexInfo) InApex(apex string) bool {
return false
}
// InApexByBaseName tells whether this apex variant of the module is part of the given APEX or not,
// where the APEX is specified by its canonical base name, i.e. typically beginning with
// "com.android.". In particular this function doesn't differentiate between source and prebuilt
// APEXes, where the latter may have "prebuilt_" prefixes.
func (i ApexInfo) InApexByBaseName(apex string) bool {
for _, a := range i.InApexes {
if RemoveOptionalPrebuiltPrefix(a) == apex {
return true
}
}
return false
}
// 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 {

View file

@ -4398,6 +4398,215 @@ func TestPrebuiltExportDexImplementationJars(t *testing.T) {
})
}
func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) {
transform := func(config *dexpreopt.GlobalConfig) {
config.BootJars = android.CreateTestConfiguredJarList([]string{"myapex:libfoo"})
}
checkBootDexJarPath := func(ctx *android.TestContext, bootDexJarPath string) {
s := ctx.SingletonForTests("dex_bootjars")
foundLibfooJar := false
for _, output := range s.AllOutputs() {
if strings.HasSuffix(output, "/libfoo.jar") {
foundLibfooJar = true
buildRule := s.Output(output)
actual := android.NormalizePathForTesting(buildRule.Input)
if actual != bootDexJarPath {
t.Errorf("Incorrect boot dex jar path '%s', expected '%s'", actual, bootDexJarPath)
}
}
}
if !foundLibfooJar {
t.Errorf("Rule for libfoo.jar missing in dex_bootjars singleton outputs")
}
}
t.Run("prebuilt only", func(t *testing.T) {
bp := `
prebuilt_apex {
name: "myapex",
arch: {
arm64: {
src: "myapex-arm64.apex",
},
arm: {
src: "myapex-arm.apex",
},
},
exported_java_libs: ["libfoo"],
}
java_import {
name: "libfoo",
jars: ["libfoo.jar"],
apex_available: ["myapex"],
}
`
ctx := testDexpreoptWithApexes(t, bp, "", transform)
checkBootDexJarPath(ctx, ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
})
t.Run("prebuilt with source library preferred", func(t *testing.T) {
bp := `
prebuilt_apex {
name: "myapex",
arch: {
arm64: {
src: "myapex-arm64.apex",
},
arm: {
src: "myapex-arm.apex",
},
},
exported_java_libs: ["libfoo"],
}
java_import {
name: "libfoo",
jars: ["libfoo.jar"],
apex_available: ["myapex"],
}
java_library {
name: "libfoo",
srcs: ["foo/bar/MyClass.java"],
apex_available: ["myapex"],
}
`
// In this test the source (java_library) libfoo is active since the
// prebuilt (java_import) defaults to prefer:false. However the
// prebuilt_apex module always depends on the prebuilt, and so it doesn't
// find the dex boot jar in it. We either need to disable the source libfoo
// or make the prebuilt libfoo preferred.
testDexpreoptWithApexes(t, bp, "failed to find a dex jar path for module 'libfoo'", transform)
})
t.Run("prebuilt library preferred with source", func(t *testing.T) {
bp := `
prebuilt_apex {
name: "myapex",
arch: {
arm64: {
src: "myapex-arm64.apex",
},
arm: {
src: "myapex-arm.apex",
},
},
exported_java_libs: ["libfoo"],
}
java_import {
name: "libfoo",
prefer: true,
jars: ["libfoo.jar"],
apex_available: ["myapex"],
}
java_library {
name: "libfoo",
srcs: ["foo/bar/MyClass.java"],
apex_available: ["myapex"],
}
`
ctx := testDexpreoptWithApexes(t, bp, "", transform)
checkBootDexJarPath(ctx, ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
})
t.Run("prebuilt with source apex preferred", func(t *testing.T) {
bp := `
apex {
name: "myapex",
key: "myapex.key",
java_libs: ["libfoo"],
}
apex_key {
name: "myapex.key",
public_key: "testkey.avbpubkey",
private_key: "testkey.pem",
}
prebuilt_apex {
name: "myapex",
arch: {
arm64: {
src: "myapex-arm64.apex",
},
arm: {
src: "myapex-arm.apex",
},
},
exported_java_libs: ["libfoo"],
}
java_import {
name: "libfoo",
jars: ["libfoo.jar"],
apex_available: ["myapex"],
}
java_library {
name: "libfoo",
srcs: ["foo/bar/MyClass.java"],
apex_available: ["myapex"],
}
`
ctx := testDexpreoptWithApexes(t, bp, "", transform)
checkBootDexJarPath(ctx, ".intermediates/libfoo/android_common_apex10000/aligned/libfoo.jar")
})
t.Run("prebuilt preferred with source apex disabled", func(t *testing.T) {
bp := `
apex {
name: "myapex",
enabled: false,
key: "myapex.key",
java_libs: ["libfoo"],
}
apex_key {
name: "myapex.key",
public_key: "testkey.avbpubkey",
private_key: "testkey.pem",
}
prebuilt_apex {
name: "myapex",
arch: {
arm64: {
src: "myapex-arm64.apex",
},
arm: {
src: "myapex-arm.apex",
},
},
exported_java_libs: ["libfoo"],
}
java_import {
name: "libfoo",
prefer: true,
jars: ["libfoo.jar"],
apex_available: ["myapex"],
}
java_library {
name: "libfoo",
srcs: ["foo/bar/MyClass.java"],
apex_available: ["myapex"],
}
`
ctx := testDexpreoptWithApexes(t, bp, "", transform)
checkBootDexJarPath(ctx, ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
})
}
func TestApexWithTests(t *testing.T) {
ctx, config := testApex(t, `
apex_test {
@ -6002,10 +6211,11 @@ func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, transformDexpreopt
"build/make/target/product/security": nil,
"apex_manifest.json": nil,
"AndroidManifest.xml": nil,
"system/sepolicy/apex/myapex-file_contexts": nil,
"system/sepolicy/apex/some-updatable-apex-file_contexts": nil,
"system/sepolicy/apex/some-non-updatable-apex-file_contexts": nil,
"system/sepolicy/apex/com.android.art.debug-file_contexts": nil,
"framework/aidl/a.aidl": nil,
"framework/aidl/a.aidl": nil,
}
cc.GatherRequiredFilesForTest(fs)

View file

@ -49,14 +49,36 @@ func populateMapFromConfiguredJarList(ctx android.SingletonContext, moduleToApex
return true
}
// isActiveModule returns true if the given module should be considered for boot
// jars, i.e. if it's enabled and the preferred one in case of source and
// prebuilt alternatives.
func isActiveModule(module android.Module) bool {
if !module.Enabled() {
return false
}
if module.IsReplacedByPrebuilt() {
// A source module that has been replaced by a prebuilt counterpart.
return false
}
if prebuilt, ok := module.(android.PrebuiltInterface); ok {
if p := prebuilt.Prebuilt(); p != nil {
return p.UsePrebuilt()
}
}
return true
}
func (b *bootJarsSingleton) GenerateBuildActions(ctx android.SingletonContext) {
config := ctx.Config()
if config.SkipBootJarsCheck() {
return
}
// Populate a map from module name to APEX from the boot jars. If there is a problem
// such as duplicate modules then fail and return immediately.
// Populate a map from module name to APEX from the boot jars. If there is a
// problem such as duplicate modules then fail and return immediately. Note
// that both module and APEX names are tracked by base names here, so we need
// to be careful to remove "prebuilt_" prefixes when comparing them with
// actual modules and APEX bundles.
moduleToApex := make(map[string]string)
if !populateMapFromConfiguredJarList(ctx, moduleToApex, config.NonUpdatableBootJars(), "BootJars") ||
!populateMapFromConfiguredJarList(ctx, moduleToApex, config.UpdatableBootJars(), "UpdatableBootJars") {
@ -69,10 +91,14 @@ func (b *bootJarsSingleton) GenerateBuildActions(ctx android.SingletonContext) {
// Scan all the modules looking for the module/apex variants corresponding to the
// boot jars.
ctx.VisitAllModules(func(module android.Module) {
name := ctx.ModuleName(module)
if !isActiveModule(module) {
return
}
name := android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName(module))
if apex, ok := moduleToApex[name]; ok {
apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
if (apex == "platform" && apexInfo.IsForPlatform()) || apexInfo.InApex(apex) {
if (apex == "platform" && apexInfo.IsForPlatform()) || apexInfo.InApexByBaseName(apex) {
// The module name/apex variant should be unique in the system but double check
// just in case something has gone wrong.
if existing, ok := nameToApexVariant[name]; ok {

View file

@ -486,7 +486,7 @@ func getBootImageJar(ctx android.SingletonContext, image *bootImageConfig, modul
// A platform variant is required but this is for an apex so ignore it.
return -1, nil
}
} else if !android.InList(requiredApex, apexInfo.InApexes) {
} else if !apexInfo.InApexByBaseName(requiredApex) {
// An apex variant for a specific apex is required but this is the wrong apex.
return -1, nil
}
@ -496,7 +496,7 @@ func getBootImageJar(ctx android.SingletonContext, image *bootImageConfig, modul
switch image.name {
case artBootImageName:
if len(apexInfo.InApexes) > 0 && allHavePrefix(apexInfo.InApexes, "com.android.art") {
if apexInfo.InApexByBaseName("com.android.art") || apexInfo.InApexByBaseName("com.android.art.debug") || apexInfo.InApexByBaseName("com.android.art,testing") {
// ok: found the jar in the ART apex
} else if name == "jacocoagent" && ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
// exception (skip and continue): Jacoco platform variant for a coverage build
@ -523,21 +523,17 @@ func getBootImageJar(ctx android.SingletonContext, image *bootImageConfig, modul
return index, jar.DexJarBuildPath()
}
func allHavePrefix(list []string, prefix string) bool {
for _, s := range list {
if s != prefix && !strings.HasPrefix(s, prefix+".") {
return false
}
}
return true
}
// buildBootImage takes a bootImageConfig, creates rules to build it, and returns the image.
func buildBootImage(ctx android.SingletonContext, image *bootImageConfig) *bootImageConfig {
// Collect dex jar paths for the boot image modules.
// This logic is tested in the apex package to avoid import cycle apex <-> java.
bootDexJars := make(android.Paths, image.modules.Len())
ctx.VisitAllModules(func(module android.Module) {
if !isActiveModule(module) {
return
}
if i, j := getBootImageJar(ctx, image, module); i != -1 {
if existing := bootDexJars[i]; existing != nil {
ctx.Errorf("Multiple dex jars found for %s:%s - %s and %s",
@ -867,6 +863,9 @@ func updatableBcpPackagesRule(ctx android.SingletonContext, image *bootImageConf
// Collect `permitted_packages` for updatable boot jars.
var updatablePackages []string
ctx.VisitAllModules(func(module android.Module) {
if !isActiveModule(module) {
return
}
if j, ok := module.(PermittedPackagesForUpdatableBootJars); ok {
name := ctx.ModuleName(module)
if i := android.IndexList(name, updatableModules); i != -1 {