Merge "Make copyBootJarsToPredefinedLocations simpler and less fragile"

This commit is contained in:
Paul Duffin 2021-06-14 08:20:50 +00:00 committed by Gerrit Code Review
commit cc1bd8364a
7 changed files with 163 additions and 50 deletions

View file

@ -1690,6 +1690,16 @@ func (l *ConfiguredJarList) BuildPaths(ctx PathContext, dir OutputPath) Writable
return paths
}
// BuildPathsByModule returns a map from module name to build paths based on the given directory
// prefix.
func (l *ConfiguredJarList) BuildPathsByModule(ctx PathContext, dir OutputPath) map[string]WritablePath {
paths := map[string]WritablePath{}
for _, jar := range l.jars {
paths[jar] = dir.Join(ctx, ModuleStem(jar)+".jar")
}
return paths
}
// UnmarshalJSON converts JSON configuration from raw bytes into a
// ConfiguredJarList structure.
func (l *ConfiguredJarList) UnmarshalJSON(b []byte) error {

View file

@ -4769,7 +4769,7 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) {
// 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'", preparer)
testDexpreoptWithApexes(t, bp, "module libfoo does not provide a dex boot jar", preparer)
})
t.Run("prebuilt library preferred with source", func(t *testing.T) {

View file

@ -648,7 +648,8 @@ func (b *BootclasspathFragmentModule) generateBootImageBuildActions(ctx android.
}
// Copy the dex jars of this fragment's content modules to their predefined locations.
copyBootJarsToPredefinedLocations(ctx, contents, imageConfig.modules, imageConfig.dexPaths)
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)

View file

@ -15,7 +15,6 @@
package java
import (
"fmt"
"path/filepath"
"sort"
"strings"
@ -254,6 +253,9 @@ type bootImageConfig struct {
dexPaths android.WritablePaths // for this image
dexPathsDeps android.WritablePaths // for the dependency images and in this image
// Map from module name (without prebuilt_ prefix) to the predefined build path.
dexPathsByModule map[string]android.WritablePath
// File path to a zip archive with all image files (or nil, if not needed).
zip android.WritablePath
@ -461,53 +463,27 @@ func shouldBuildBootImages(config android.Config, global *dexpreopt.GlobalConfig
return true
}
// copyBootJarsToPredefinedLocations generates commands that will copy boot jars to
// predefined paths in the global config.
func copyBootJarsToPredefinedLocations(ctx android.ModuleContext, bootModules []android.Module, bootjars android.ConfiguredJarList, jarPathsPredefined android.WritablePaths) {
jarPaths := make(android.Paths, bootjars.Len())
for i, module := range bootModules {
if module != nil {
bootDexJar := module.(interface{ DexJarBuildPath() android.Path }).DexJarBuildPath()
jarPaths[i] = bootDexJar
name := android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(module))
if bootjars.Jar(i) != name {
ctx.ModuleErrorf("expected module %s at position %d but found %s", bootjars.Jar(i), i, name)
}
}
}
// The paths to bootclasspath DEX files need to be known at module GenerateAndroidBuildAction
// time, before the boot images are built (these paths are used in dexpreopt rule generation for
// Java libraries and apps). Generate rules that copy bootclasspath DEX jars to the predefined
// paths.
for i := range jarPaths {
input := jarPaths[i]
output := jarPathsPredefined[i]
module := bootjars.Jar(i)
if input == nil {
if ctx.Config().AllowMissingDependencies() {
apex := bootjars.Apex(i)
// Create an error rule that pretends to create the output file but will actually fail if it
// is run.
ctx.Build(pctx, android.BuildParams{
Rule: android.ErrorRule,
Output: output,
Args: map[string]string{
"error": fmt.Sprintf("missing dependencies: dex jar for %s:%s", module, apex),
},
})
} else {
ctx.ModuleErrorf("failed to find a dex jar path for module '%s'"+
", note that some jars may be filtered out by module constraints", module)
}
// copyBootJarsToPredefinedLocations generates commands that will copy boot jars to predefined
// paths in the global config.
func copyBootJarsToPredefinedLocations(ctx android.ModuleContext, srcBootDexJarsByModule bootDexJarByModule, dstBootJarsByModule map[string]android.WritablePath) {
// Create the super set of module names.
names := []string{}
names = append(names, android.SortedStringKeys(srcBootDexJarsByModule)...)
names = append(names, android.SortedStringKeys(dstBootJarsByModule)...)
names = android.SortedUniqueStrings(names)
for _, name := range names {
src := srcBootDexJarsByModule[name]
dst := dstBootJarsByModule[name]
if src == nil {
ctx.ModuleErrorf("module %s does not provide a dex boot jar", name)
} else if dst == nil {
ctx.ModuleErrorf("module %s is not part of the boot configuration", name)
} else {
ctx.Build(pctx, android.BuildParams{
Rule: android.Cp,
Input: input,
Output: output,
Input: src,
Output: dst,
})
}
}

View file

@ -100,6 +100,7 @@ func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig {
// TODO(b/143682396): use module dependencies instead
inputDir := deviceDir.Join(ctx, "dex_"+c.name+"jars_input")
c.dexPaths = c.modules.BuildPaths(ctx, inputDir)
c.dexPathsByModule = c.modules.BuildPathsByModule(ctx, inputDir)
c.dexPathsDeps = c.dexPaths
// Create target-specific variants.
@ -153,6 +154,9 @@ type updatableBootConfig struct {
// later on a singleton adds commands to copy actual jars to the predefined paths.
dexPaths android.WritablePaths
// Map from module name (without prebuilt_ prefix) to the predefined build path.
dexPathsByModule map[string]android.WritablePath
// A list of dex locations (a.k.a. on-device paths) to the boot jars.
dexLocations []string
}
@ -166,10 +170,11 @@ func GetUpdatableBootConfig(ctx android.PathContext) updatableBootConfig {
dir := android.PathForOutput(ctx, ctx.Config().DeviceName(), "updatable_bootjars")
dexPaths := updatableBootJars.BuildPaths(ctx, dir)
dexPathsByModuleName := updatableBootJars.BuildPathsByModule(ctx, dir)
dexLocations := updatableBootJars.DevicePaths(ctx.Config(), android.Android)
return updatableBootConfig{updatableBootJars, dexPaths, dexLocations}
return updatableBootConfig{updatableBootJars, dexPaths, dexPathsByModuleName, dexLocations}
}).(updatableBootConfig)
}

View file

@ -571,6 +571,15 @@ type HiddenAPIFlagOutput struct {
AllFlagsPath android.Path
}
// bootDexJarByModule is a map from base module name (without prebuilt_ prefix) to the boot dex
// path.
type bootDexJarByModule map[string]android.Path
// addPath adds the path for a module to the map.
func (b bootDexJarByModule) addPath(module android.Module, path android.Path) {
b[android.RemoveOptionalPrebuiltPrefix(module.Name())] = path
}
// pathForValidation creates a path of the same type as the supplied type but with a name of
// <path>.valid.
//
@ -759,7 +768,7 @@ func extractBootDexJarsFromHiddenAPIModules(ctx android.ModuleContext, contents
bootDexJar := module.bootDexJar()
if bootDexJar == nil {
if ctx.Config().AlwaysUsePrebuiltSdks() {
// TODO(b/179354495): Remove this work around when it is unnecessary.
// TODO(b/179354495): Remove this workaround when it is unnecessary.
// Prebuilt modules like framework-wifi do not yet provide dex implementation jars. So,
// create a fake one that will cause a build error only if it is used.
fake := android.PathForModuleOut(ctx, "fake/boot-dex/%s.jar", module.Name())
@ -792,3 +801,113 @@ func extractClassJarsFromHiddenAPIModules(ctx android.ModuleContext, contents []
}
return classesJars
}
// deferReportingMissingBootDexJar returns true if a missing boot dex jar should not be reported by
// Soong but should instead only be reported in ninja if the file is actually built.
func deferReportingMissingBootDexJar(ctx android.ModuleContext, module android.Module) bool {
// TODO(b/179354495): Remove this workaround when it is unnecessary.
// Prebuilt modules like framework-wifi do not yet provide dex implementation jars. So,
// create a fake one that will cause a build error only if it is used.
if ctx.Config().AlwaysUsePrebuiltSdks() {
return true
}
// This is called for both platform_bootclasspath and bootclasspath_fragment modules.
//
// A bootclasspath_fragment module should only use the APEX variant of source or prebuilt modules.
// Ideally, a bootclasspath_fragment module should never have a platform variant created for it
// but unfortunately, due to b/187910671 it does.
//
// That causes issues when obtaining a boot dex jar for a prebuilt module as a prebuilt module
// used by a bootclasspath_fragment can only provide a boot dex jar when it is part of APEX, i.e.
// has an APEX variant not a platform variant.
//
// There are some other situations when a prebuilt module used by a bootclasspath_fragment cannot
// provide a boot dex jar:
// 1. If the bootclasspath_fragment is not exported by the prebuilt_apex/apex_set module then it
// does not have an APEX variant and only has a platform variant and neither do its content
// modules.
// 2. Some build configurations, e.g. setting TARGET_BUILD_USE_PREBUILT_SDKS causes all
// java_sdk_library_import modules to be treated as preferred and as many of them are not part
// of an apex they cannot provide a boot dex jar.
//
// The first case causes problems when the affected prebuilt modules are preferred but that is an
// invalid configuration and it is ok for it to fail as the work to enable that is not yet
// complete. The second case is used for building targets that do not use boot dex jars and so
// deferring error reporting to ninja is fine as the affected ninja targets should never be built.
// That is handled above.
//
// A platform_bootclasspath module can use libraries from both platform and APEX variants. Unlike
// the bootclasspath_fragment it supports dex_import modules which provides the dex file. So, it
// can obtain a boot dex jar from a prebuilt that is not part of an APEX. However, it is assumed
// that if the library can be part of an APEX then it is the APEX variant that is used.
//
// This check handles the slightly different requirements of the bootclasspath_fragment and
// platform_bootclasspath modules by only deferring error reporting for the platform variant of
// a prebuilt modules that has other variants which are part of an APEX.
//
// TODO(b/187910671): Remove this once platform variants are no longer created unnecessarily.
if android.IsModulePrebuilt(module) {
if am, ok := module.(android.ApexModule); ok && am.InAnyApex() {
apexInfo := ctx.OtherModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
if apexInfo.IsForPlatform() {
return true
}
}
}
// A bootclasspath module that is part of a versioned sdk never provides a boot dex jar as there
// is no equivalently versioned prebuilt APEX file from which it can be obtained. However,
// versioned bootclasspath modules are processed by Soong so in order to avoid them causing build
// failures missing boot dex jars need to be deferred.
if android.IsModuleInVersionedSdk(ctx.Module()) {
return true
}
return false
}
// handleMissingDexBootFile will either log a warning or create an error rule to create the fake
// file depending on the value returned from deferReportingMissingBootDexJar.
func handleMissingDexBootFile(ctx android.ModuleContext, module android.Module, fake android.WritablePath) {
if deferReportingMissingBootDexJar(ctx, module) {
// Create an error rule that pretends to create the output file but will actually fail if it
// is run.
ctx.Build(pctx, android.BuildParams{
Rule: android.ErrorRule,
Output: fake,
Args: map[string]string{
"error": fmt.Sprintf("missing dependencies: boot dex jar for %s", module),
},
})
} else {
ctx.ModuleErrorf("module %s does not provide a dex jar", module)
}
}
// retrieveEncodedBootDexJarFromModule returns a path to the boot dex jar from the supplied module's
// DexJarBuildPath() method.
//
// The returned path will usually be to a dex jar file that has been encoded with hidden API flags.
// However, under certain conditions, e.g. errors, or special build configurations it will return
// a path to a fake file.
func retrieveEncodedBootDexJarFromModule(ctx android.ModuleContext, module android.Module) android.Path {
bootDexJar := module.(interface{ DexJarBuildPath() android.Path }).DexJarBuildPath()
if bootDexJar == nil {
fake := android.PathForModuleOut(ctx, fmt.Sprintf("fake/encoded-dex/%s.jar", module.Name()))
bootDexJar = fake
handleMissingDexBootFile(ctx, module, fake)
}
return bootDexJar
}
// extractEncodedDexJarsFromModules extracts the encoded dex jars from the supplied modules.
func extractEncodedDexJarsFromModules(ctx android.ModuleContext, contents []android.Module) bootDexJarByModule {
encodedDexJarsByModuleName := bootDexJarByModule{}
for _, module := range contents {
path := retrieveEncodedBootDexJarFromModule(ctx, module)
encodedDexJarsByModuleName.addPath(module, path)
}
return encodedDexJarsByModuleName
}

View file

@ -389,11 +389,13 @@ func (b *platformBootclasspathModule) generateBootImageBuildActions(ctx android.
generateUpdatableBcpPackagesRule(ctx, imageConfig, updatableModules)
// Copy non-updatable module dex jars to their predefined locations.
copyBootJarsToPredefinedLocations(ctx, nonUpdatableModules, imageConfig.modules, imageConfig.dexPaths)
nonUpdatableBootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, nonUpdatableModules)
copyBootJarsToPredefinedLocations(ctx, nonUpdatableBootDexJarsByModule, imageConfig.dexPathsByModule)
// Copy updatable module dex jars to their predefined locations.
config := GetUpdatableBootConfig(ctx)
copyBootJarsToPredefinedLocations(ctx, updatableModules, config.modules, config.dexPaths)
updatableBootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, updatableModules)
copyBootJarsToPredefinedLocations(ctx, updatableBootDexJarsByModule, config.dexPathsByModule)
// Build a profile for the image config and then use that to build the boot image.
profile := bootImageProfileRule(ctx, imageConfig)