Merge "Fix boot jar handling when both source and prebuilt APEXes and modules are present."
diff --git a/android/apex.go b/android/apex.go
index d3f8d2a..b87ff09 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -111,6 +111,19 @@
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 {
diff --git a/apex/apex_test.go b/apex/apex_test.go
index cded770..7f5be7e 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -4398,6 +4398,215 @@
})
}
+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 @@
"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)
diff --git a/java/boot_jars.go b/java/boot_jars.go
index 823275b..ac8107b 100644
--- a/java/boot_jars.go
+++ b/java/boot_jars.go
@@ -49,14 +49,36 @@
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 @@
// 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 {
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 78d9d02..36d0d30 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -486,7 +486,7 @@
// 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 @@
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 @@
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 @@
// 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 {