Merge changes from topic "init_first_stage_soong"
* changes:
Fix ndk and aml arch order
Add ramdisk_available to sysprop_library
Add install_in_root to cc_binary
diff --git a/android/deapexer.go b/android/deapexer.go
index 63508d7..f3c541c 100644
--- a/android/deapexer.go
+++ b/android/deapexer.go
@@ -15,31 +15,67 @@
package android
import (
- "fmt"
- "strings"
-
"github.com/google/blueprint"
)
// Provides support for interacting with the `deapexer` module to which a `prebuilt_apex` module
// will delegate the work to export files from a prebuilt '.apex` file.
+//
+// The actual processing that is done is quite convoluted but it is all about combining information
+// from multiple different sources in order to allow a prebuilt module to use a file extracted from
+// an apex file. As follows:
+//
+// 1. A prebuilt module, e.g. prebuilt_bootclasspath_fragment or java_import needs to use a file
+// from a prebuilt_apex/apex_set. It knows the path of the file within the apex but does not know
+// where the apex file is or what apex to use.
+//
+// 2. The connection between the prebuilt module and the prebuilt_apex/apex_set is created through
+// use of an exported_... property on the latter. That causes four things to occur:
+// a. A `deapexer` mopdule is created by the prebuilt_apex/apex_set to extract files from the
+// apex file.
+// b. A dependency is added from the prebuilt_apex/apex_set modules onto the prebuilt modules
+// listed in those properties.
+// c. An APEX variant is created for each of those prebuilt modules.
+// d. A dependency is added from the prebuilt modules to the `deapexer` module.
+//
+// 3. The prebuilt_apex/apex_set modules do not know which files are available in the apex file.
+// That information could be specified on the prebuilt_apex/apex_set modules but without
+// automated generation of those modules it would be expensive to maintain. So, instead they
+// obtain that information from the prebuilt modules. They do not know what files are actually in
+// the apex file either but they know what files they need from it. So, the
+// prebuilt_apex/apex_set modules obtain the files that should be in the apex file from those
+// modules and then pass those onto the `deapexer` module.
+//
+// 4. The `deapexer` module's ninja rule extracts all the files from the apex file into an output
+// directory and checks that all the expected files are there. The expected files are declared as
+// the outputs of the ninja rule so they are available to other modules.
+//
+// 5. The prebuilt modules then retrieve the paths to the files that they needed from the `deapexer`
+// module.
+//
+// The files that are passed to `deapexer` and those that are passed back have a unique identifier
+// that links them together. e.g. If the `deapexer` is passed something like this:
+// core-libart{.dexjar} -> javalib/core-libart.jar
+// it will return something like this:
+// core-libart{.dexjar} -> out/soong/.....deapexer.../javalib/core-libart.jar
+//
+// The reason why the `deapexer` module is separate from the prebuilt_apex/apex_set is to avoid
+// cycles. e.g.
+// prebuilt_apex "com.android.art" depends upon java_import "core-libart":
+// This is so it can create an APEX variant of the latter and obtain information about the
+// files that it needs from the apex file.
+// java_import "core-libart" depends upon `deapexer` module:
+// This is so it can retrieve the paths to the files it needs.
// The information exported by the `deapexer` module, access it using `DeapxerInfoProvider`.
type DeapexerInfo struct {
// map from the name of an exported file from a prebuilt_apex to the path to that file. The
- // exported file name is of the form <module>{<tag>} where <tag> is currently only allowed to be
- // ".dexjar".
+ // exported file name is of the form <module>{<tag>}.
//
// See Prebuilt.ApexInfoMutator for more information.
exports map[string]Path
}
-// The set of supported prebuilt export tags. Used to verify the tag parameter for
-// `PrebuiltExportPath`.
-var supportedPrebuiltExportTags = map[string]struct{}{
- ".dexjar": {},
-}
-
// PrebuiltExportPath provides the path, or nil if not available, of a file exported from the
// prebuilt_apex that created this ApexInfo.
//
@@ -51,12 +87,6 @@
//
// See apex/deapexer.go for more information.
func (i DeapexerInfo) PrebuiltExportPath(name, tag string) Path {
-
- if _, ok := supportedPrebuiltExportTags[tag]; !ok {
- panic(fmt.Errorf("unsupported prebuilt export tag %q, expected one of %s",
- tag, strings.Join(SortedStringKeys(supportedPrebuiltExportTags), ", ")))
- }
-
path := i.exports[name+"{"+tag+"}"]
return path
}
@@ -79,5 +109,22 @@
blueprint.BaseDependencyTag
}
+// Mark this tag so dependencies that use it are excluded from APEX contents.
+func (t deapexerTagStruct) ExcludeFromApexContents() {}
+
+var _ ExcludeFromApexContentsTag = DeapexerTag
+
// A tag that is used for dependencies on the `deapexer` module.
var DeapexerTag = deapexerTagStruct{}
+
+// RequiredFilesFromPrebuiltApex must be implemented by modules that require files to be exported
+// from a prebuilt_apex/apex_set.
+type RequiredFilesFromPrebuiltApex interface {
+ // RequiredFilesFromPrebuiltApex returns a map from the key (module name plus tag) to the required
+ // path of the file within the prebuilt .apex file.
+ //
+ // For each key/file pair this will cause the file to be extracted out of the prebuilt .apex file,
+ // and the path to the extracted file will be stored in the DeapexerInfo using that key, The path
+ // can then be retrieved using the PrebuiltExportPath(name, tag) method.
+ RequiredFilesFromPrebuiltApex(ctx BaseModuleContext) map[string]string
+}
diff --git a/apex/apex.go b/apex/apex.go
index 9e714ab..494e167 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -171,8 +171,7 @@
Ignore_system_library_special_case *bool
// Whenever apex_payload.img of the APEX should include dm-verity hashtree.
- // Default value is false.
- // TODO(b/190621617): change default value to true.
+ // Default value is true.
Generate_hashtree *bool
// Whenever apex_payload.img of the APEX should not be dm-verity signed. Should be only
@@ -1343,7 +1342,7 @@
// See the generate_hashtree property
func (a *apexBundle) shouldGenerateHashtree() bool {
- return proptools.BoolDefault(a.properties.Generate_hashtree, false)
+ return proptools.BoolDefault(a.properties.Generate_hashtree, true)
}
// See the test_only_unsigned_payload property
@@ -1756,7 +1755,9 @@
ctx.PropertyErrorf("systemserverclasspath_fragments", "%q is not a systemserverclasspath_fragment module", depName)
return false
}
- filesInfo = append(filesInfo, apexClasspathFragmentProtoFile(ctx, child))
+ if af := apexClasspathFragmentProtoFile(ctx, child); af != nil {
+ filesInfo = append(filesInfo, *af)
+ }
return true
}
case javaLibTag:
@@ -2159,17 +2160,23 @@
}
// Add classpaths.proto config.
- filesToAdd = append(filesToAdd, apexClasspathFragmentProtoFile(ctx, module))
+ if af := apexClasspathFragmentProtoFile(ctx, module); af != nil {
+ filesToAdd = append(filesToAdd, *af)
+ }
return filesToAdd
}
-// apexClasspathFragmentProtoFile returns apexFile structure defining the classpath.proto config that
-// the module contributes to the apex.
-func apexClasspathFragmentProtoFile(ctx android.ModuleContext, module blueprint.Module) apexFile {
- fragmentInfo := ctx.OtherModuleProvider(module, java.ClasspathFragmentProtoContentInfoProvider).(java.ClasspathFragmentProtoContentInfo)
- classpathProtoOutput := fragmentInfo.ClasspathFragmentProtoOutput
- return newApexFile(ctx, classpathProtoOutput, classpathProtoOutput.Base(), fragmentInfo.ClasspathFragmentProtoInstallDir.Rel(), etc, nil)
+// apexClasspathFragmentProtoFile returns *apexFile structure defining the classpath.proto config that
+// the module contributes to the apex; or nil if the proto config was not generated.
+func apexClasspathFragmentProtoFile(ctx android.ModuleContext, module blueprint.Module) *apexFile {
+ info := ctx.OtherModuleProvider(module, java.ClasspathFragmentProtoContentInfoProvider).(java.ClasspathFragmentProtoContentInfo)
+ if !info.ClasspathFragmentProtoGenerated {
+ return nil
+ }
+ classpathProtoOutput := info.ClasspathFragmentProtoOutput
+ af := newApexFile(ctx, classpathProtoOutput, classpathProtoOutput.Base(), info.ClasspathFragmentProtoInstallDir.Rel(), etc, nil)
+ return &af
}
// apexFileForBootclasspathFragmentContentModule creates an apexFile for a bootclasspath_fragment
@@ -2355,16 +2362,30 @@
})
}
-// Enforce that Java deps of the apex are using stable SDKs to compile
+// checkUpdatable enforces APEX and its transitive dep properties to have desired values for updatable APEXes.
func (a *apexBundle) checkUpdatable(ctx android.ModuleContext) {
if a.Updatable() {
if String(a.properties.Min_sdk_version) == "" {
ctx.PropertyErrorf("updatable", "updatable APEXes should set min_sdk_version as well")
}
a.checkJavaStableSdkVersion(ctx)
+ a.checkClasspathFragments(ctx)
}
}
+// checkClasspathFragments enforces that all classpath fragments in deps generate classpaths.proto config.
+func (a *apexBundle) checkClasspathFragments(ctx android.ModuleContext) {
+ ctx.VisitDirectDeps(func(module android.Module) {
+ if tag := ctx.OtherModuleDependencyTag(module); tag == bcpfTag || tag == sscpfTag {
+ info := ctx.OtherModuleProvider(module, java.ClasspathFragmentProtoContentInfoProvider).(java.ClasspathFragmentProtoContentInfo)
+ if !info.ClasspathFragmentProtoGenerated {
+ ctx.OtherModuleErrorf(module, "is included in updatable apex %v, it must not set generate_classpaths_proto to false", ctx.ModuleName())
+ }
+ }
+ })
+}
+
+// checkJavaStableSdkVersion enforces that all Java deps are using stable SDKs to compile.
func (a *apexBundle) checkJavaStableSdkVersion(ctx android.ModuleContext) {
// Visit direct deps only. As long as we guarantee top-level deps are using stable SDKs,
// java's checkLinkType guarantees correct usage for transitive deps
@@ -2383,7 +2404,7 @@
})
}
-// Ensures that the all the dependencies are marked as available for this APEX
+// checkApexAvailability ensures that the all the dependencies are marked as available for this APEX.
func (a *apexBundle) checkApexAvailability(ctx android.ModuleContext) {
// Let's be practical. Availability for test, host, and the VNDK apex isn't important
if ctx.Host() || a.testApex || a.vndkApex {
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 8b97467..484b807 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -6700,6 +6700,47 @@
`)
}
+func TestUpdatable_should_not_set_generate_classpaths_proto(t *testing.T) {
+ testApexError(t, `"mysystemserverclasspathfragment" .* it must not set generate_classpaths_proto to false`, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ systemserverclasspath_fragments: [
+ "mysystemserverclasspathfragment",
+ ],
+ min_sdk_version: "29",
+ updatable: true,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ java_library {
+ name: "foo",
+ srcs: ["b.java"],
+ min_sdk_version: "29",
+ installable: true,
+ apex_available: [
+ "myapex",
+ ],
+ }
+
+ systemserverclasspath_fragment {
+ name: "mysystemserverclasspathfragment",
+ generate_classpaths_proto: false,
+ contents: [
+ "foo",
+ ],
+ apex_available: [
+ "myapex",
+ ],
+ }
+ `)
+}
+
func TestNoUpdatableJarsInBootImage(t *testing.T) {
// Set the BootJars in dexpreopt.GlobalConfig and productVariables to the same value. This can
// result in an invalid configuration as it does not set the ArtApexJars and allows art apex
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index 1971dcd..66bc9e0 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -16,6 +16,7 @@
import (
"fmt"
+ "sort"
"strings"
"testing"
@@ -359,6 +360,19 @@
addPrebuilt := func(prefer bool, contents ...string) android.FixturePreparer {
text := fmt.Sprintf(`
+ prebuilt_apex {
+ name: "com.android.art",
+ arch: {
+ arm64: {
+ src: "com.android.art-arm64.apex",
+ },
+ arm: {
+ src: "com.android.art-arm.apex",
+ },
+ },
+ exported_bootclasspath_fragments: ["mybootclasspathfragment"],
+ }
+
prebuilt_bootclasspath_fragment {
name: "mybootclasspathfragment",
image_name: "art",
@@ -372,7 +386,47 @@
return android.FixtureAddTextFile("prebuilts/module_sdk/art/Android.bp", text)
}
- t.Run("boot image files", func(t *testing.T) {
+ t.Run("boot image files from source", func(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ commonPreparer,
+
+ // Configure some libraries in the art bootclasspath_fragment that match the source
+ // bootclasspath_fragment's contents property.
+ java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
+ addSource("foo", "bar"),
+ ).RunTest(t)
+
+ ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+ "etc/classpaths/bootclasspath.pb",
+ "javalib/arm/boot.art",
+ "javalib/arm/boot.oat",
+ "javalib/arm/boot.vdex",
+ "javalib/arm/boot-bar.art",
+ "javalib/arm/boot-bar.oat",
+ "javalib/arm/boot-bar.vdex",
+ "javalib/arm64/boot.art",
+ "javalib/arm64/boot.oat",
+ "javalib/arm64/boot.vdex",
+ "javalib/arm64/boot-bar.art",
+ "javalib/arm64/boot-bar.oat",
+ "javalib/arm64/boot-bar.vdex",
+ "javalib/bar.jar",
+ "javalib/foo.jar",
+ })
+
+ java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+ `bar`,
+ `com.android.art.key`,
+ `mybootclasspathfragment`,
+ })
+
+ // Make sure that the source bootclasspath_fragment copies its dex files to the predefined
+ // locations for the art image.
+ module := result.ModuleForTests("mybootclasspathfragment", "android_common_apex10000")
+ checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
+ })
+
+ t.Run("boot image files with preferred prebuilt", func(t *testing.T) {
result := android.GroupFixturePreparers(
commonPreparer,
@@ -407,7 +461,13 @@
`bar`,
`com.android.art.key`,
`mybootclasspathfragment`,
+ `prebuilt_com.android.art`,
})
+
+ // Make sure that the prebuilt bootclasspath_fragment copies its dex files to the predefined
+ // locations for the art image.
+ module := result.ModuleForTests("prebuilt_mybootclasspathfragment", "android_common_com.android.art")
+ checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
})
t.Run("source with inconsistency between config and contents", func(t *testing.T) {
@@ -528,11 +588,37 @@
`prebuilt_mybootclasspathfragment`,
})
- java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common", []string{
+ java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_com.android.art", []string{
+ `com.android.art.deapexer`,
`dex2oatd`,
`prebuilt_bar`,
`prebuilt_foo`,
})
+
+ module := result.ModuleForTests("mybootclasspathfragment", "android_common_com.android.art")
+ checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
+}
+
+// checkCopiesToPredefinedLocationForArt checks that the supplied modules are copied to the
+// predefined locations of boot dex jars used as inputs for the ART boot image.
+func checkCopiesToPredefinedLocationForArt(t *testing.T, config android.Config, module android.TestingModule, modules ...string) {
+ t.Helper()
+ bootJarLocations := []string{}
+ for _, output := range module.AllOutputs() {
+ output = android.StringRelativeToTop(config, output)
+ if strings.HasPrefix(output, "out/soong/test_device/dex_artjars_input/") {
+ bootJarLocations = append(bootJarLocations, output)
+ }
+ }
+
+ sort.Strings(bootJarLocations)
+ expected := []string{}
+ for _, m := range modules {
+ expected = append(expected, fmt.Sprintf("out/soong/test_device/dex_artjars_input/%s.jar", m))
+ }
+ sort.Strings(expected)
+
+ android.AssertArrayString(t, "copies to predefined locations for art", expected, bootJarLocations)
}
func TestBootclasspathFragmentContentsNoName(t *testing.T) {
diff --git a/apex/deapexer.go b/apex/deapexer.go
index c7cdbfa..9088c49 100644
--- a/apex/deapexer.go
+++ b/apex/deapexer.go
@@ -42,8 +42,8 @@
// DeapexerExportedFile defines the properties needed to expose a file from the deapexer module.
type DeapexerExportedFile struct {
- // The tag parameter which must be passed to android.OutputFileProducer OutputFiles(tag) method
- // to retrieve the path to the unpacked file.
+ // The tag parameter which must be passed to android.DeapexerInfo's PrebuiltExportPath(name, tag)
+ // method to retrieve the path to the unpacked file.
Tag string
// The path within the APEX that needs to be exported.
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 1283d23..aaa3a22 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -217,52 +217,57 @@
// apex specific variants of the exported java modules available for use from within make.
apexName := p.BaseModuleName()
for _, fi := range p.apexFilesForAndroidMk {
- moduleName := fi.androidMkModuleName + "." + apexName
- entries := android.AndroidMkEntries{
- Class: fi.class.nameInMake(),
- OverrideName: moduleName,
- OutputFile: android.OptionalPathForPath(fi.builtFile),
- Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
- ExtraEntries: []android.AndroidMkExtraEntriesFunc{
- func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
- entries.SetString("LOCAL_MODULE_PATH", p.installDir.ToMakePath().String())
-
- // soong_java_prebuilt.mk sets LOCAL_MODULE_SUFFIX := .jar Therefore
- // we need to remove the suffix from LOCAL_MODULE_STEM, otherwise
- // we will have foo.jar.jar
- entries.SetString("LOCAL_MODULE_STEM", strings.TrimSuffix(fi.stem(), ".jar"))
- var classesJar android.Path
- var headerJar android.Path
- if javaModule, ok := fi.module.(java.ApexDependency); ok {
- classesJar = javaModule.ImplementationAndResourcesJars()[0]
- headerJar = javaModule.HeaderJars()[0]
- } else {
- classesJar = fi.builtFile
- headerJar = fi.builtFile
- }
- entries.SetString("LOCAL_SOONG_CLASSES_JAR", classesJar.String())
- entries.SetString("LOCAL_SOONG_HEADER_JAR", headerJar.String())
- entries.SetString("LOCAL_SOONG_DEX_JAR", fi.builtFile.String())
- entries.SetString("LOCAL_DEX_PREOPT", "false")
- },
- },
- ExtraFooters: []android.AndroidMkExtraFootersFunc{
- func(w io.Writer, name, prefix, moduleDir string) {
- // m <module_name> will build <module_name>.<apex_name> as well.
- if fi.androidMkModuleName != moduleName {
- fmt.Fprintf(w, ".PHONY: %s\n", fi.androidMkModuleName)
- fmt.Fprintf(w, "%s: %s\n", fi.androidMkModuleName, moduleName)
- }
- },
- },
- }
-
+ entries := p.createEntriesForApexFile(fi, apexName)
entriesList = append(entriesList, entries)
}
return entriesList
}
+// createEntriesForApexFile creates an AndroidMkEntries for the supplied apexFile
+func (p *prebuiltCommon) createEntriesForApexFile(fi apexFile, apexName string) android.AndroidMkEntries {
+ moduleName := fi.androidMkModuleName + "." + apexName
+ entries := android.AndroidMkEntries{
+ Class: fi.class.nameInMake(),
+ OverrideName: moduleName,
+ OutputFile: android.OptionalPathForPath(fi.builtFile),
+ Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
+ ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ entries.SetString("LOCAL_MODULE_PATH", p.installDir.ToMakePath().String())
+
+ // soong_java_prebuilt.mk sets LOCAL_MODULE_SUFFIX := .jar Therefore
+ // we need to remove the suffix from LOCAL_MODULE_STEM, otherwise
+ // we will have foo.jar.jar
+ entries.SetString("LOCAL_MODULE_STEM", strings.TrimSuffix(fi.stem(), ".jar"))
+ var classesJar android.Path
+ var headerJar android.Path
+ if javaModule, ok := fi.module.(java.ApexDependency); ok {
+ classesJar = javaModule.ImplementationAndResourcesJars()[0]
+ headerJar = javaModule.HeaderJars()[0]
+ } else {
+ classesJar = fi.builtFile
+ headerJar = fi.builtFile
+ }
+ entries.SetString("LOCAL_SOONG_CLASSES_JAR", classesJar.String())
+ entries.SetString("LOCAL_SOONG_HEADER_JAR", headerJar.String())
+ entries.SetString("LOCAL_SOONG_DEX_JAR", fi.builtFile.String())
+ entries.SetString("LOCAL_DEX_PREOPT", "false")
+ },
+ },
+ ExtraFooters: []android.AndroidMkExtraFootersFunc{
+ func(w io.Writer, name, prefix, moduleDir string) {
+ // m <module_name> will build <module_name>.<apex_name> as well.
+ if fi.androidMkModuleName != moduleName {
+ fmt.Fprintf(w, ".PHONY: %s\n", fi.androidMkModuleName)
+ fmt.Fprintf(w, "%s: %s\n", fi.androidMkModuleName, moduleName)
+ }
+ },
+ },
+ }
+ return entries
+}
+
// prebuiltApexModuleCreator defines the methods that need to be implemented by prebuilt_apex and
// apex_set in order to create the modules needed to provide access to the prebuilt .apex file.
type prebuiltApexModuleCreator interface {
@@ -549,21 +554,36 @@
}
// Compute the deapexer properties from the transitive dependencies of this module.
- javaModules := []string{}
- exportedFiles := map[string]string{}
+ commonModules := []string{}
+ exportedFilesByKey := map[string]string{}
+ requiringModulesByKey := map[string]android.Module{}
ctx.WalkDeps(func(child, parent android.Module) bool {
tag := ctx.OtherModuleDependencyTag(child)
name := android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(child))
if java.IsBootclasspathFragmentContentDepTag(tag) || tag == exportedJavaLibTag {
- javaModules = append(javaModules, name)
+ commonModules = append(commonModules, name)
// Add the dex implementation jar to the set of exported files. The path here must match the
// path of the file in the APEX created by apexFileForJavaModule(...).
- exportedFiles[name+"{.dexjar}"] = filepath.Join("javalib", name+".jar")
+ exportedFilesByKey[name+"{.dexjar}"] = filepath.Join("javalib", name+".jar")
} else if tag == exportedBootclasspathFragmentTag {
- // Only visit the children of the bootclasspath_fragment for now.
+ commonModules = append(commonModules, name)
+
+ requiredFiles := child.(android.RequiredFilesFromPrebuiltApex).RequiredFilesFromPrebuiltApex(ctx)
+ for k, v := range requiredFiles {
+ if f, ok := exportedFilesByKey[k]; ok && f != v {
+ otherModule := requiringModulesByKey[k]
+ ctx.ModuleErrorf("inconsistent paths have been requested for key %q, %s requires path %s while %s requires path %s",
+ k, child, v, otherModule, f)
+ continue
+ }
+ exportedFilesByKey[k] = v
+ requiringModulesByKey[k] = child
+ }
+
+ // Make sure to visit the children of the bootclasspath_fragment.
return true
}
@@ -572,16 +592,16 @@
// Create properties for deapexer module.
deapexerProperties := &DeapexerProperties{
- // Remove any duplicates from the java modules lists as a module may be included via a direct
+ // Remove any duplicates from the common modules lists as a module may be included via a direct
// dependency as well as transitive ones.
- CommonModules: android.SortedUniqueStrings(javaModules),
+ CommonModules: android.SortedUniqueStrings(commonModules),
}
// Populate the exported files property in a fixed order.
- for _, tag := range android.SortedStringKeys(exportedFiles) {
+ for _, tag := range android.SortedStringKeys(exportedFilesByKey) {
deapexerProperties.ExportedFiles = append(deapexerProperties.ExportedFiles, DeapexerExportedFile{
Tag: tag,
- Path: exportedFiles[tag],
+ Path: exportedFilesByKey[tag],
})
}
diff --git a/apex/systemserver_classpath_fragment_test.go b/apex/systemserver_classpath_fragment_test.go
index 95b6e23..537f51d 100644
--- a/apex/systemserver_classpath_fragment_test.go
+++ b/apex/systemserver_classpath_fragment_test.go
@@ -76,3 +76,54 @@
`mysystemserverclasspathfragment`,
})
}
+
+func TestSystemserverclasspathFragmentNoGeneratedProto(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForTestWithSystemserverclasspathFragment,
+ prepareForTestWithMyapex,
+ ).RunTestWithBp(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ systemserverclasspath_fragments: [
+ "mysystemserverclasspathfragment",
+ ],
+ updatable: false,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ java_library {
+ name: "foo",
+ srcs: ["b.java"],
+ installable: true,
+ apex_available: [
+ "myapex",
+ ],
+ }
+
+ systemserverclasspath_fragment {
+ name: "mysystemserverclasspathfragment",
+ generate_classpaths_proto: false,
+ contents: [
+ "foo",
+ ],
+ apex_available: [
+ "myapex",
+ ],
+ }
+ `)
+
+ ensureExactContents(t, result.TestContext, "myapex", "android_common_myapex_image", []string{
+ "javalib/foo.jar",
+ })
+
+ java.CheckModuleDependencies(t, result.TestContext, "myapex", "android_common_myapex_image", []string{
+ `myapex.key`,
+ `mysystemserverclasspathfragment`,
+ })
+}
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index fb6b47e..daaec39 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -144,10 +144,22 @@
// module cannot contribute to hidden API processing, e.g. because it is a prebuilt module in a
// versioned sdk.
produceHiddenAPIOutput(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput
+
+ // produceBootImageFiles produces the boot image (i.e. .art, .oat and .vdex) files for each of the
+ // required android.ArchType values in the returned map.
+ //
+ // It must return nil if the boot image files cannot be produced for whatever reason.
+ produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig, contents []android.Module) bootImageFilesByArch
}
var _ commonBootclasspathFragment = (*BootclasspathFragmentModule)(nil)
+// bootImageFilesByArch is a map from android.ArchType to the paths to the boot image files.
+//
+// The paths include the .art, .oat and .vdex files, one for each of the modules from which the boot
+// image is created.
+type bootImageFilesByArch map[android.ArchType]android.Paths
+
func bootclasspathFragmentFactory() android.Module {
m := &BootclasspathFragmentModule{}
m.AddProperties(&m.properties)
@@ -285,7 +297,7 @@
modules android.ConfiguredJarList
// Map from arch type to the boot image files.
- bootImageFilesByArch map[android.ArchType]android.OutputPaths
+ bootImageFilesByArch bootImageFilesByArch
// Map from the base module name (without prebuilt_ prefix) of a fragment's contents module to the
// hidden API encoded dex jar path.
@@ -299,7 +311,7 @@
// Get a map from ArchType to the associated boot image's contents for Android.
//
// Extension boot images only return their own files, not the files of the boot images they extend.
-func (i BootclasspathFragmentApexContentInfo) AndroidBootImageFilesByArchType() map[android.ArchType]android.OutputPaths {
+func (i BootclasspathFragmentApexContentInfo) AndroidBootImageFilesByArchType() bootImageFilesByArch {
return i.bootImageFilesByArch
}
@@ -409,17 +421,52 @@
// Perform hidden API processing.
hiddenAPIOutput := b.generateHiddenAPIBuildActions(ctx, contents, fragments)
+ var bootImageFilesByArch bootImageFilesByArch
+ if imageConfig != nil {
+ // Delegate the production of the boot image files to a module type specific method.
+ common := ctx.Module().(commonBootclasspathFragment)
+ bootImageFilesByArch = common.produceBootImageFiles(ctx, imageConfig, contents)
+
+ if shouldCopyBootFilesToPredefinedLocations(ctx, imageConfig) {
+ // Copy the dex jars of this fragment's content modules to their predefined locations.
+ copyBootJarsToPredefinedLocations(ctx, hiddenAPIOutput.EncodedBootDexFilesByModule, imageConfig.dexPathsByModule)
+ }
+ }
+
// A prebuilt fragment cannot contribute to an apex.
if !android.IsModulePrebuilt(ctx.Module()) {
// Provide the apex content info.
- b.provideApexContentInfo(ctx, imageConfig, contents, hiddenAPIOutput)
+ b.provideApexContentInfo(ctx, imageConfig, hiddenAPIOutput, bootImageFilesByArch)
}
}
}
+// shouldCopyBootFilesToPredefinedLocations determines whether the current module should copy boot
+// files, e.g. boot dex jars or boot image files, to the predefined location expected by the rest
+// of the build.
+//
+// This ensures that only a single module will copy its files to the image configuration.
+func shouldCopyBootFilesToPredefinedLocations(ctx android.ModuleContext, imageConfig *bootImageConfig) bool {
+ // Bootclasspath fragment modules that are for the platform do not produce boot related files.
+ apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+ if apexInfo.IsForPlatform() {
+ return false
+ }
+
+ // If the image configuration has no modules specified then it means that the build has been
+ // configured to build something other than a boot image, e.g. an sdk, so do not try and copy the
+ // files.
+ if imageConfig.modules.Len() == 0 {
+ return false
+ }
+
+ // Only copy files from the module that is preferred.
+ return isActiveModule(ctx.Module())
+}
+
// provideApexContentInfo creates, initializes and stores the apex content info for use by other
// modules.
-func (b *BootclasspathFragmentModule) provideApexContentInfo(ctx android.ModuleContext, imageConfig *bootImageConfig, contents []android.Module, hiddenAPIOutput *HiddenAPIOutput) {
+func (b *BootclasspathFragmentModule) provideApexContentInfo(ctx android.ModuleContext, imageConfig *bootImageConfig, hiddenAPIOutput *HiddenAPIOutput, bootImageFilesByArch bootImageFilesByArch) {
// Construct the apex content info from the config.
info := BootclasspathFragmentApexContentInfo{
// Populate the apex content info with paths to the dex jars.
@@ -428,28 +475,10 @@
if imageConfig != nil {
info.modules = imageConfig.modules
-
- if !SkipDexpreoptBootJars(ctx) {
- // Force the GlobalSoongConfig to be created and cached for use by the dex_bootjars
- // GenerateSingletonBuildActions method as it cannot create it for itself.
- dexpreopt.GetGlobalSoongConfig(ctx)
-
- // Only generate the boot image if the configuration does not skip it.
- if b.generateBootImageBuildActions(ctx, contents, imageConfig) {
- // Allow the apex to access the boot image files.
- files := map[android.ArchType]android.OutputPaths{}
- for _, variant := range imageConfig.variants {
- // We also generate boot images for host (for testing), but we don't need those in the apex.
- // TODO(b/177892522) - consider changing this to check Os.OsClass = android.Device
- if variant.target.Os == android.Android {
- files[variant.target.Arch.ArchType] = variant.imagesDeps
- }
- }
- info.bootImageFilesByArch = files
- }
- }
}
+ info.bootImageFilesByArch = bootImageFilesByArch
+
// Make the apex content info available for other modules.
ctx.SetProvider(BootclasspathFragmentApexContentInfoProvider, info)
}
@@ -586,7 +615,6 @@
// createHiddenAPIFlagInput creates a HiddenAPIFlagInput struct and initializes it with information derived
// from the properties on this module and its dependencies.
func (b *BootclasspathFragmentModule) createHiddenAPIFlagInput(ctx android.ModuleContext, contents []android.Module, fragments []android.Module) HiddenAPIFlagInput {
-
// Merge the HiddenAPIInfo from all the fragment dependencies.
dependencyHiddenApiInfo := newHiddenAPIInfo()
dependencyHiddenApiInfo.mergeFromFragmentDeps(ctx, fragments)
@@ -614,6 +642,30 @@
return hiddenAPIRulesForBootclasspathFragment(ctx, contents, input)
}
+// produceBootImageFiles builds the boot image files from the source if it is required.
+func (b *BootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig, contents []android.Module) bootImageFilesByArch {
+ if SkipDexpreoptBootJars(ctx) {
+ return nil
+ }
+
+ // Force the GlobalSoongConfig to be created and cached for use by the dex_bootjars
+ // GenerateSingletonBuildActions method as it cannot create it for itself.
+ dexpreopt.GetGlobalSoongConfig(ctx)
+
+ // Only generate the boot image if the configuration does not skip it.
+ if !b.generateBootImageBuildActions(ctx, contents, imageConfig) {
+ return nil
+ }
+
+ // Only make the files available to an apex if they were actually generated.
+ files := bootImageFilesByArch{}
+ for _, variant := range imageConfig.apexVariants() {
+ files[variant.target.Arch.ArchType] = variant.imagesDeps.Paths()
+ }
+
+ return files
+}
+
// generateBootImageBuildActions generates ninja rules to create the boot image if required for this
// module.
//
@@ -635,10 +687,6 @@
return false
}
- // Copy the dex jars of this fragment's content modules to their predefined locations.
- bootDexJarByModule := extractEncodedDexJarsFromModules(ctx, contents)
- copyBootJarsToPredefinedLocations(ctx, bootDexJarByModule, imageConfig.dexPathsByModule)
-
// Build a profile for the image config and then use that to build the boot image.
profile := bootImageProfileRule(ctx, imageConfig)
buildBootImage(ctx, imageConfig, profile)
@@ -855,8 +903,88 @@
return &output
}
+// produceBootImageFiles extracts the boot image files from the APEX if available.
+func (module *prebuiltBootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig, contents []android.Module) bootImageFilesByArch {
+ if !shouldCopyBootFilesToPredefinedLocations(ctx, imageConfig) {
+ return nil
+ }
+
+ var deapexerModule android.Module
+ ctx.VisitDirectDeps(func(module android.Module) {
+ tag := ctx.OtherModuleDependencyTag(module)
+ // Save away the `deapexer` module on which this depends, if any.
+ if tag == android.DeapexerTag {
+ deapexerModule = module
+ }
+ })
+
+ if deapexerModule == nil {
+ // This should never happen as a variant for a prebuilt_apex is only created if the
+ // deapexer module has been configured to export the dex implementation jar for this module.
+ ctx.ModuleErrorf("internal error: module does not depend on a `deapexer` module")
+ return nil
+ }
+
+ di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo)
+ name := module.BaseModuleName()
+ for _, variant := range imageConfig.apexVariants() {
+ arch := variant.target.Arch.ArchType
+ for _, toPath := range variant.imagesDeps {
+ // Get the path to the file that the deapexer extracted from the prebuilt apex file.
+ tag := createBootImageTag(arch, toPath.Base())
+ fromPath := di.PrebuiltExportPath(name, tag)
+
+ // Copy the file to the predefined location.
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Cp,
+ Input: fromPath,
+ Output: toPath,
+ })
+ }
+ }
+
+ // The returned files will be made available to APEXes that include a bootclasspath_fragment.
+ // However, as a prebuilt_bootclasspath_fragment can never contribute to an APEX there is no point
+ // in returning any files.
+ return nil
+}
+
var _ commonBootclasspathFragment = (*prebuiltBootclasspathFragmentModule)(nil)
+// createBootImageTag creates the tag to uniquely identify the boot image file among all of the
+// files that a module requires from the prebuilt .apex file.
+func createBootImageTag(arch android.ArchType, baseName string) string {
+ tag := fmt.Sprintf(".bootimage-%s-%s", arch, baseName)
+ return tag
+}
+
+// RequiredFilesFromPrebuiltApex returns the list of all files the prebuilt_bootclasspath_fragment
+// requires from a prebuilt .apex file.
+//
+// If there is no image config associated with this fragment then it returns nil. Otherwise, it
+// returns the files that are listed in the image config.
+func (module *prebuiltBootclasspathFragmentModule) RequiredFilesFromPrebuiltApex(ctx android.BaseModuleContext) map[string]string {
+ imageConfig := module.getImageConfig(ctx)
+ if imageConfig != nil {
+ // Add the boot image files, e.g. .art, .oat and .vdex files.
+ files := map[string]string{}
+ name := module.BaseModuleName()
+ for _, variant := range imageConfig.apexVariants() {
+ arch := variant.target.Arch.ArchType
+ for _, path := range variant.imagesDeps.Paths() {
+ base := path.Base()
+ tag := createBootImageTag(arch, base)
+ key := fmt.Sprintf("%s{%s}", name, tag)
+ files[key] = filepath.Join("javalib", arch.String(), base)
+ }
+ }
+ return files
+ }
+ return nil
+}
+
+var _ android.RequiredFilesFromPrebuiltApex = (*prebuiltBootclasspathFragmentModule)(nil)
+
func prebuiltBootclasspathFragmentFactory() android.Module {
m := &prebuiltBootclasspathFragmentModule{}
m.AddProperties(&m.properties, &m.prebuiltProperties)
diff --git a/java/classpath_fragment.go b/java/classpath_fragment.go
index 4c1e749..3d72580 100644
--- a/java/classpath_fragment.go
+++ b/java/classpath_fragment.go
@@ -19,6 +19,7 @@
import (
"fmt"
"github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
"strings"
"android/soong/android"
@@ -44,6 +45,11 @@
}
type classpathFragmentProperties struct {
+ // Whether to generated classpaths.proto config instance for the fragment. If the config is not
+ // generated, then relevant boot jars are added to platform classpath, i.e. platform_bootclasspath
+ // or platform_systemserverclasspath. This is useful for non-updatable APEX boot jars, to keep
+ // them as part of dexopt on device. Defaults to true.
+ Generate_classpaths_proto *bool
}
// classpathFragment interface is implemented by a module that contributes jars to a *CLASSPATH
@@ -101,24 +107,28 @@
}
func (c *ClasspathFragmentBase) generateClasspathProtoBuildActions(ctx android.ModuleContext, jars []classpathJar) {
- outputFilename := strings.ToLower(c.classpathType.String()) + ".pb"
- c.outputFilepath = android.PathForModuleOut(ctx, outputFilename).OutputPath
- c.installDirPath = android.PathForModuleInstall(ctx, "etc", "classpaths")
+ generateProto := proptools.BoolDefault(c.properties.Generate_classpaths_proto, true)
+ if generateProto {
+ outputFilename := strings.ToLower(c.classpathType.String()) + ".pb"
+ c.outputFilepath = android.PathForModuleOut(ctx, outputFilename).OutputPath
+ c.installDirPath = android.PathForModuleInstall(ctx, "etc", "classpaths")
- generatedJson := android.PathForModuleOut(ctx, outputFilename+".json")
- writeClasspathsJson(ctx, generatedJson, jars)
+ generatedJson := android.PathForModuleOut(ctx, outputFilename+".json")
+ writeClasspathsJson(ctx, generatedJson, jars)
- rule := android.NewRuleBuilder(pctx, ctx)
- rule.Command().
- BuiltTool("conv_classpaths_proto").
- Flag("encode").
- Flag("--format=json").
- FlagWithInput("--input=", generatedJson).
- FlagWithOutput("--output=", c.outputFilepath)
+ rule := android.NewRuleBuilder(pctx, ctx)
+ rule.Command().
+ BuiltTool("conv_classpaths_proto").
+ Flag("encode").
+ Flag("--format=json").
+ FlagWithInput("--input=", generatedJson).
+ FlagWithOutput("--output=", c.outputFilepath)
- rule.Build("classpath_fragment", "Compiling "+c.outputFilepath.String())
+ rule.Build("classpath_fragment", "Compiling "+c.outputFilepath.String())
+ }
classpathProtoInfo := ClasspathFragmentProtoContentInfo{
+ ClasspathFragmentProtoGenerated: generateProto,
ClasspathFragmentProtoInstallDir: c.installDirPath,
ClasspathFragmentProtoOutput: c.outputFilepath,
}
@@ -164,6 +174,9 @@
var ClasspathFragmentProtoContentInfoProvider = blueprint.NewProvider(ClasspathFragmentProtoContentInfo{})
type ClasspathFragmentProtoContentInfo struct {
+ // Whether the classpaths.proto config is generated for the fragment.
+ ClasspathFragmentProtoGenerated bool
+
// ClasspathFragmentProtoOutput is an output path for the generated classpaths.proto config of this module.
//
// The file should be copied to a relevant place on device, see ClasspathFragmentProtoInstallDir
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index dc8df5e..bb85784 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -358,6 +358,19 @@
return ret
}
+// apexVariants returns a list of all *bootImageVariant that could be included in an apex.
+func (image *bootImageConfig) apexVariants() []*bootImageVariant {
+ variants := []*bootImageVariant{}
+ for _, variant := range image.variants {
+ // We also generate boot images for host (for testing), but we don't need those in the apex.
+ // TODO(b/177892522) - consider changing this to check Os.OsClass = android.Device
+ if variant.target.Os == android.Android {
+ variants = append(variants, variant)
+ }
+ }
+ return variants
+}
+
// Return boot image locations (as a list of symbolic paths).
//
// The image "location" is a symbolic path that, with multiarchitecture support, doesn't really
@@ -489,7 +502,7 @@
}
}
-// buildBootImage takes a bootImageConfig, creates rules to build it, and returns the image.
+// buildBootImage takes a bootImageConfig, and creates rules to build it.
func buildBootImage(ctx android.ModuleContext, image *bootImageConfig, profile android.WritablePath) {
var zipFiles android.Paths
for _, variant := range image.variants {