Allow installing boot images outside of APEX.

After this change, `bootImageConfig.installDirOnDevice` can be set to a
path outside of the APEX, in which case, the boot image will not be
installed in the APEX. Instead, it will be installed to the given path
by Make.

This is a no-op change. Current behavior is not affected.

Bug: 211973309
Test: m nothing
Test: -
  1. m com.android.art
  2. See the boot image still being installed in the ART APEX.
Test: -
  1. Change `installDirOnDevice` of the ART boot image config to
     `system/framework`.
  2. See the boot image being installed in `/system/framework/<arch>`.
Change-Id: Ib13b17cc9e94dc5754c9b51b04df3307323b8783
This commit is contained in:
Jiakai Zhang 2022-01-12 17:56:19 +00:00
parent 9ab9437b40
commit 6decef916c
8 changed files with 204 additions and 29 deletions

View file

@ -1765,13 +1765,17 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
}
case bcpfTag:
{
if _, ok := child.(*java.BootclasspathFragmentModule); !ok {
bcpfModule, ok := child.(*java.BootclasspathFragmentModule)
if !ok {
ctx.PropertyErrorf("bootclasspath_fragments", "%q is not a bootclasspath_fragment module", depName)
return false
}
filesToAdd := apexBootclasspathFragmentFiles(ctx, child)
filesInfo = append(filesInfo, filesToAdd...)
for _, makeModuleName := range bcpfModule.BootImageDeviceInstallMakeModules() {
a.requiredDeps = append(a.requiredDeps, makeModuleName)
}
return true
}
case sscpfTag:
@ -2175,13 +2179,15 @@ func apexBootclasspathFragmentFiles(ctx android.ModuleContext, module blueprint.
var filesToAdd []apexFile
// Add the boot image files, e.g. .art, .oat and .vdex files.
for arch, files := range bootclasspathFragmentInfo.AndroidBootImageFilesByArchType() {
dirInApex := filepath.Join("javalib", arch.String())
for _, f := range files {
androidMkModuleName := "javalib_" + arch.String() + "_" + filepath.Base(f.String())
// TODO(b/177892522) - consider passing in the bootclasspath fragment module here instead of nil
af := newApexFile(ctx, f, androidMkModuleName, dirInApex, etc, nil)
filesToAdd = append(filesToAdd, af)
if bootclasspathFragmentInfo.ShouldInstallBootImageInApex() {
for arch, files := range bootclasspathFragmentInfo.AndroidBootImageFilesByArchType() {
dirInApex := filepath.Join("javalib", arch.String())
for _, f := range files {
androidMkModuleName := "javalib_" + arch.String() + "_" + filepath.Base(f.String())
// TODO(b/177892522) - consider passing in the bootclasspath fragment module here instead of nil
af := newApexFile(ctx, f, androidMkModuleName, dirInApex, etc, nil)
filesToAdd = append(filesToAdd, af)
}
}
}

View file

@ -8739,6 +8739,22 @@ func TestSdkLibraryCanHaveHigherMinSdkVersion(t *testing.T) {
})
}
// Verifies that the APEX depends on all the Make modules in the list.
func ensureContainsRequiredDeps(t *testing.T, ctx *android.TestContext, moduleName, variant string, deps []string) {
a := ctx.ModuleForTests(moduleName, variant).Module().(*apexBundle)
for _, dep := range deps {
android.AssertStringListContains(t, "", a.requiredDeps, dep)
}
}
// Verifies that the APEX does not depend on any of the Make modules in the list.
func ensureDoesNotContainRequiredDeps(t *testing.T, ctx *android.TestContext, moduleName, variant string, deps []string) {
a := ctx.ModuleForTests(moduleName, variant).Module().(*apexBundle)
for _, dep := range deps {
android.AssertStringListDoesNotContain(t, "", a.requiredDeps, dep)
}
}
func TestMain(m *testing.M) {
os.Exit(m.Run())
}

View file

@ -410,6 +410,7 @@ func TestBootclasspathFragmentInArtApex(t *testing.T) {
// bootclasspath_fragment's contents property.
java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
addSource("foo", "bar"),
java.FixtureSetBootImageInstallDirOnDevice("art", "apex/com.android.art/javalib"),
).RunTest(t)
ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
@ -437,12 +438,62 @@ func TestBootclasspathFragmentInArtApex(t *testing.T) {
`mybootclasspathfragment`,
})
// The boot images are installed in the APEX by Soong, so there shouldn't be any dexpreopt-related Make modules.
ensureDoesNotContainRequiredDeps(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
"mybootclasspathfragment-dexpreopt-arm64-boot.art",
"mybootclasspathfragment-dexpreopt-arm64-boot.oat",
"mybootclasspathfragment-dexpreopt-arm64-boot.vdex",
"mybootclasspathfragment-dexpreopt-arm64-boot-bar.art",
"mybootclasspathfragment-dexpreopt-arm64-boot-bar.oat",
"mybootclasspathfragment-dexpreopt-arm64-boot-bar.vdex",
"mybootclasspathfragment-dexpreopt-arm-boot.art",
"mybootclasspathfragment-dexpreopt-arm-boot.oat",
"mybootclasspathfragment-dexpreopt-arm-boot.vdex",
"mybootclasspathfragment-dexpreopt-arm-boot-bar.art",
"mybootclasspathfragment-dexpreopt-arm-boot-bar.oat",
"mybootclasspathfragment-dexpreopt-arm-boot-bar.vdex",
})
// 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 from source no boot image in apex", 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"),
java.FixtureSetBootImageInstallDirOnDevice("art", "system/framework"),
).RunTest(t)
ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
"etc/boot-image.prof",
"etc/classpaths/bootclasspath.pb",
"javalib/bar.jar",
"javalib/foo.jar",
})
ensureContainsRequiredDeps(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
"mybootclasspathfragment-dexpreopt-arm64-boot.art",
"mybootclasspathfragment-dexpreopt-arm64-boot.oat",
"mybootclasspathfragment-dexpreopt-arm64-boot.vdex",
"mybootclasspathfragment-dexpreopt-arm64-boot-bar.art",
"mybootclasspathfragment-dexpreopt-arm64-boot-bar.oat",
"mybootclasspathfragment-dexpreopt-arm64-boot-bar.vdex",
"mybootclasspathfragment-dexpreopt-arm-boot.art",
"mybootclasspathfragment-dexpreopt-arm-boot.oat",
"mybootclasspathfragment-dexpreopt-arm-boot.vdex",
"mybootclasspathfragment-dexpreopt-arm-boot-bar.art",
"mybootclasspathfragment-dexpreopt-arm-boot-bar.oat",
"mybootclasspathfragment-dexpreopt-arm-boot-bar.vdex",
})
})
t.Run("boot image disable generate profile", func(t *testing.T) {
result := android.GroupFixturePreparers(
commonPreparer,
@ -472,6 +523,8 @@ func TestBootclasspathFragmentInArtApex(t *testing.T) {
// Make sure that a preferred prebuilt with consistent contents doesn't affect the apex.
addPrebuilt(true, "foo", "bar"),
java.FixtureSetBootImageInstallDirOnDevice("art", "apex/com.android.art/javalib"),
).RunTest(t)
ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{

View file

@ -219,6 +219,11 @@ type BootclasspathFragmentModule struct {
// Collect the module directory for IDE info in java/jdeps.go.
modulePaths []string
// Installs for on-device boot image files. This list has entries only if the installs should be
// handled by Make (e.g., the boot image should be installed on the system partition, rather than
// in the APEX).
bootImageDeviceInstalls []dexpreopterInstall
}
// commonBootclasspathFragment defines the methods that are implemented by both source and prebuilt
@ -387,6 +392,9 @@ type BootclasspathFragmentApexContentInfo struct {
// Map from arch type to the boot image files.
bootImageFilesByArch bootImageFilesByArch
// True if the boot image should be installed in the APEX.
shouldInstallBootImageInApex bool
// Map from the base module name (without prebuilt_ prefix) of a fragment's contents module to the
// hidden API encoded dex jar path.
contentModuleDexJarPaths bootDexJarByModule
@ -410,6 +418,11 @@ func (i BootclasspathFragmentApexContentInfo) AndroidBootImageFilesByArchType()
return i.bootImageFilesByArch
}
// Return true if the boot image should be installed in the APEX.
func (i *BootclasspathFragmentApexContentInfo) ShouldInstallBootImageInApex() bool {
return i.shouldInstallBootImageInApex
}
// DexBootJarPathForContentModule returns the path to the dex boot jar for specified module.
//
// The dex boot jar is one which has had hidden API encoding performed on it.
@ -550,6 +563,24 @@ func (b *BootclasspathFragmentModule) GenerateAndroidBuildActions(ctx android.Mo
// Copy the dex jars of this fragment's content modules to their predefined locations.
copyBootJarsToPredefinedLocations(ctx, hiddenAPIOutput.EncodedBootDexFilesByModule, imageConfig.dexPathsByModule)
}
for _, variant := range imageConfig.apexVariants() {
arch := variant.target.Arch.ArchType.String()
for _, install := range variant.deviceInstalls {
// Remove the "/" prefix because the path should be relative to $ANDROID_PRODUCT_OUT.
installDir := strings.TrimPrefix(filepath.Dir(install.To), "/")
installBase := filepath.Base(install.To)
installPath := android.PathForModuleInPartitionInstall(ctx, "", installDir)
b.bootImageDeviceInstalls = append(b.bootImageDeviceInstalls, dexpreopterInstall{
name: arch + "-" + installBase,
moduleName: b.Name(),
outputPathOnHost: install.From,
installDirOnDevice: installPath,
installFileOnDevice: installBase,
})
}
}
}
// A prebuilt fragment cannot contribute to an apex.
@ -599,6 +630,8 @@ func (b *BootclasspathFragmentModule) provideApexContentInfo(ctx android.ModuleC
info.profilePathOnHost = imageConfig.profilePathOnHost
info.profileInstallPathInApex = imageConfig.profileInstallPathInApex
}
info.shouldInstallBootImageInApex = imageConfig.shouldInstallInApex()
}
info.bootImageFilesByArch = bootImageFilesByArch
@ -813,6 +846,23 @@ func (b *BootclasspathFragmentModule) generateBootImageBuildActions(ctx android.
return androidBootImageFilesByArch
}
func (b *BootclasspathFragmentModule) AndroidMkEntries() []android.AndroidMkEntries {
var entriesList []android.AndroidMkEntries
for _, install := range b.bootImageDeviceInstalls {
entriesList = append(entriesList, install.ToMakeEntries())
}
return entriesList
}
// Returns the names of all Make modules that handle the installation of the boot image.
func (b *BootclasspathFragmentModule) BootImageDeviceInstallMakeModules() []string {
var makeModules []string
for _, install := range b.bootImageDeviceInstalls {
makeModules = append(makeModules, install.FullModuleName())
}
return makeModules
}
// Collect information for opening IDE project files in java/jdeps.go.
func (b *BootclasspathFragmentModule) IDEInfo(dpInfo *android.IdeInfo) {
dpInfo.Deps = append(dpInfo.Deps, b.properties.Contents...)

View file

@ -57,6 +57,25 @@ func (install *dexpreopterInstall) SubModuleName() string {
return "-dexpreopt-" + install.name
}
// Returns Make entries for installing the file.
//
// This function uses a value receiver rather than a pointer receiver to ensure that the object is
// safe to use in `android.AndroidMkExtraEntriesFunc`.
func (install dexpreopterInstall) ToMakeEntries() android.AndroidMkEntries {
return android.AndroidMkEntries{
Class: "ETC",
SubName: install.SubModuleName(),
OutputFile: android.OptionalPathForPath(install.outputPathOnHost),
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.SetString("LOCAL_MODULE_PATH", install.installDirOnDevice.String())
entries.SetString("LOCAL_INSTALLED_MODULE_STEM", install.installFileOnDevice)
entries.SetString("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", "false")
},
},
}
}
type dexpreopter struct {
dexpreoptProperties DexpreoptProperties
@ -383,19 +402,7 @@ func (d *dexpreopter) DexpreoptBuiltInstalledForApex() []dexpreopterInstall {
func (d *dexpreopter) AndroidMkEntriesForApex() []android.AndroidMkEntries {
var entries []android.AndroidMkEntries
for _, install := range d.builtInstalledForApex {
install := install
entries = append(entries, android.AndroidMkEntries{
Class: "ETC",
SubName: install.SubModuleName(),
OutputFile: android.OptionalPathForPath(install.outputPathOnHost),
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.SetString("LOCAL_MODULE_PATH", install.installDirOnDevice.String())
entries.SetString("LOCAL_INSTALLED_MODULE_STEM", install.installFileOnDevice)
entries.SetString("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", "false")
},
},
})
entries = append(entries, install.ToMakeEntries())
}
return entries
}

View file

@ -313,10 +313,13 @@ type bootImageVariant struct {
// This is only set for a variant of an image that extends another image.
primaryImagesDeps android.Paths
// Rules which should be used in make to install the outputs.
// Rules which should be used in make to install the outputs on host.
installs android.RuleBuilderInstalls
vdexInstalls android.RuleBuilderInstalls
unstrippedInstalls android.RuleBuilderInstalls
// Rules which should be used in make to install the outputs on device.
deviceInstalls android.RuleBuilderInstalls
}
// Get target-specific boot image variant for the given boot image config and target.
@ -388,6 +391,11 @@ func (image *bootImageConfig) apexVariants() []*bootImageVariant {
return variants
}
// Returns true if the boot image should be installed in the APEX.
func (image *bootImageConfig) shouldInstallInApex() bool {
return strings.HasPrefix(image.installDirOnDevice, "apex/")
}
// Return boot image locations (as a list of symbolic paths).
//
// The image "location" is a symbolic path that, with multiarchitecture support, doesn't really
@ -710,6 +718,7 @@ func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, p
var vdexInstalls android.RuleBuilderInstalls
var unstrippedInstalls android.RuleBuilderInstalls
var deviceInstalls android.RuleBuilderInstalls
for _, artOrOat := range image.moduleFiles(ctx, outputDir, ".art", ".oat") {
cmd.ImplicitOutput(artOrOat)
@ -735,12 +744,21 @@ func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, p
android.RuleBuilderInstall{unstrippedOat, filepath.Join(installDir, unstrippedOat.Base())})
}
if image.installDirOnHost != image.installDirOnDevice && !image.shouldInstallInApex() && !ctx.Config().UnbundledBuild() {
installDirOnDevice := filepath.Join("/", image.installDirOnDevice, arch.String())
for _, file := range image.moduleFiles(ctx, outputDir, ".art", ".oat", ".vdex") {
deviceInstalls = append(deviceInstalls,
android.RuleBuilderInstall{file, filepath.Join(installDirOnDevice, file.Base())})
}
}
rule.Build(image.name+"JarsDexpreopt_"+image.target.String(), "dexpreopt "+image.name+" jars "+arch.String())
// save output and installed files for makevars
image.installs = rule.Installs()
image.vdexInstalls = vdexInstalls
image.unstrippedInstalls = unstrippedInstalls
image.deviceInstalls = deviceInstalls
}
const failureMessage = `ERROR: Dex2oat failed to compile a boot image.

View file

@ -41,17 +41,14 @@ func dexpreoptTargets(ctx android.PathContext) []android.Target {
var (
bootImageConfigKey = android.NewOnceKey("bootImageConfig")
bootImageConfigRawKey = android.NewOnceKey("bootImageConfigRaw")
artBootImageName = "art"
frameworkBootImageName = "boot"
)
// Construct the global boot image configs.
func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig {
return ctx.Config().Once(bootImageConfigKey, func() interface{} {
func genBootImageConfigRaw(ctx android.PathContext) map[string]*bootImageConfig {
return ctx.Config().Once(bootImageConfigRawKey, func() interface{} {
global := dexpreopt.GetGlobalConfig(ctx)
targets := dexpreoptTargets(ctx)
deviceDir := android.PathForOutput(ctx, ctx.Config().DeviceName())
artModules := global.ArtApexJars
frameworkModules := global.BootJars.RemoveList(artModules)
@ -79,10 +76,22 @@ func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig {
modules: frameworkModules,
}
configs := map[string]*bootImageConfig{
return map[string]*bootImageConfig{
artBootImageName: &artCfg,
frameworkBootImageName: &frameworkCfg,
}
}).(map[string]*bootImageConfig)
}
// Construct the global boot image configs.
func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig {
return ctx.Config().Once(bootImageConfigKey, func() interface{} {
targets := dexpreoptTargets(ctx)
deviceDir := android.PathForOutput(ctx, ctx.Config().DeviceName())
configs := genBootImageConfigRaw(ctx)
artCfg := configs[artBootImageName]
frameworkCfg := configs[frameworkBootImageName]
// common to all configs
for _, c := range configs {

View file

@ -506,3 +506,19 @@ func fakeApexMutator(mctx android.BottomUpMutatorContext) {
}
}
}
// Applies the given modifier on the boot image config with the given name.
func FixtureModifyBootImageConfig(name string, configModifier func(*bootImageConfig)) android.FixturePreparer {
return android.FixtureModifyConfig(func(androidConfig android.Config) {
pathCtx := android.PathContextForTesting(androidConfig)
config := genBootImageConfigRaw(pathCtx)
configModifier(config[name])
})
}
// Sets the value of `installDirOnDevice` of the boot image config with the given name.
func FixtureSetBootImageInstallDirOnDevice(name string, installDir string) android.FixturePreparer {
return FixtureModifyBootImageConfig(name, func(config *bootImageConfig) {
config.installDirOnDevice = installDir
})
}