Merge "Make copyBootJarsToPredefinedLocations simpler and less fragile"
This commit is contained in:
commit
cc1bd8364a
7 changed files with 163 additions and 50 deletions
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue