Merge "Remove execute-only support."
diff --git a/android/apex.go b/android/apex.go
index a4a5231..17ec9b1 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -38,9 +38,12 @@
Module
apexModuleBase() *ApexModuleBase
- // Marks that this module should be built for the APEX of the specified name.
+ // Marks that this module should be built for the APEXes of the specified names.
// Call this before apex.apexMutator is run.
- BuildForApex(apexName string)
+ BuildForApexes(apexNames []string)
+
+ // Returns the name of the APEXes that this modoule will be built for
+ ApexVariations() []string
// Returns the name of APEX that this module will be built for. Empty string
// is returned when 'IsForPlatform() == true'. Note that a module can be
@@ -66,7 +69,7 @@
IsInstallableToApex() bool
// Mutate this module into one or more variants each of which is built
- // for an APEX marked via BuildForApex().
+ // for an APEX marked via BuildForApexes().
CreateApexVariations(mctx BottomUpMutatorContext) []Module
// Sets the name of the apex variant of this module. Called inside
@@ -110,14 +113,20 @@
return m
}
-func (m *ApexModuleBase) BuildForApex(apexName string) {
+func (m *ApexModuleBase) BuildForApexes(apexNames []string) {
m.apexVariationsLock.Lock()
defer m.apexVariationsLock.Unlock()
- if !InList(apexName, m.apexVariations) {
- m.apexVariations = append(m.apexVariations, apexName)
+ for _, apexName := range apexNames {
+ if !InList(apexName, m.apexVariations) {
+ m.apexVariations = append(m.apexVariations, apexName)
+ }
}
}
+func (m *ApexModuleBase) ApexVariations() []string {
+ return m.apexVariations
+}
+
func (m *ApexModuleBase) ApexName() string {
return m.ApexProperties.ApexName
}
@@ -218,18 +227,20 @@
}
// Update the map to mark that a module named moduleName is directly or indirectly
-// depended on by an APEX named apexName. Directly depending means that a module
+// depended on by the specified APEXes. Directly depending means that a module
// is explicitly listed in the build definition of the APEX via properties like
// native_shared_libs, java_libs, etc.
-func UpdateApexDependency(apexName string, moduleName string, directDep bool) {
+func UpdateApexDependency(apexNames []string, moduleName string, directDep bool) {
apexNamesMapMutex.Lock()
defer apexNamesMapMutex.Unlock()
- apexNames, ok := apexNamesMap()[moduleName]
- if !ok {
- apexNames = make(map[string]bool)
- apexNamesMap()[moduleName] = apexNames
+ for _, apexName := range apexNames {
+ apexesForModule, ok := apexNamesMap()[moduleName]
+ if !ok {
+ apexesForModule = make(map[string]bool)
+ apexNamesMap()[moduleName] = apexesForModule
+ }
+ apexesForModule[apexName] = apexesForModule[apexName] || directDep
}
- apexNames[apexName] = apexNames[apexName] || directDep
}
// TODO(b/146393795): remove this when b/146393795 is fixed
diff --git a/apex/apex.go b/apex/apex.go
index e5f6424..54a335a 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -94,7 +94,6 @@
"libcutils",
"libcutils_headers",
"libdiagnose_usb",
- "liblog",
"liblog_headers",
"libmdnssd",
"libminijail",
@@ -170,7 +169,6 @@
"libicuuc_headers",
"libicuuc_stubdata",
"libjdwp_headers",
- "liblog",
"liblog_headers",
"liblz4",
"liblzma",
@@ -787,7 +785,6 @@
"libjemalloc5",
"liblinker_main",
"liblinker_malloc",
- "liblog",
"liblog_headers",
"liblz4",
"liblzma",
@@ -820,7 +817,6 @@
"libcutils_headers",
"libgtest_prod",
"libjsoncpp",
- "liblog",
"liblog_headers",
"libnativehelper_header_only",
"libnetd_client_headers",
@@ -861,7 +857,6 @@
"libhidltransport-impl-internal",
"libhwbinder-impl-internal",
"libjsoncpp",
- "liblog",
"liblog_headers",
"libprocessgroup",
"libprocessgroup_headers",
@@ -1020,7 +1015,7 @@
}
func RegisterPostDepsMutators(ctx android.RegisterMutatorsContext) {
- ctx.BottomUp("apex_deps", apexDepsMutator)
+ ctx.TopDown("apex_deps", apexDepsMutator)
ctx.BottomUp("apex", apexMutator).Parallel()
ctx.BottomUp("apex_flattened", apexFlattenedMutator).Parallel()
ctx.BottomUp("apex_uses", apexUsesMutator).Parallel()
@@ -1028,24 +1023,29 @@
// Mark the direct and transitive dependencies of apex bundles so that they
// can be built for the apex bundles.
-func apexDepsMutator(mctx android.BottomUpMutatorContext) {
+func apexDepsMutator(mctx android.TopDownMutatorContext) {
+ var apexBundleNames []string
+ var directDep bool
if a, ok := mctx.Module().(*apexBundle); ok && !a.vndkApex {
- apexBundleName := mctx.ModuleName()
- mctx.WalkDeps(func(child, parent android.Module) bool {
- depName := mctx.OtherModuleName(child)
- // If the parent is apexBundle, this child is directly depended.
- _, directDep := parent.(*apexBundle)
- android.UpdateApexDependency(apexBundleName, depName, directDep)
-
- if am, ok := child.(android.ApexModule); ok && am.CanHaveApexVariants() &&
- (directDep || am.DepIsInSameApex(mctx, child)) {
- am.BuildForApex(apexBundleName)
- return true
- } else {
- return false
- }
- })
+ apexBundleNames = []string{mctx.ModuleName()}
+ directDep = true
+ } else if am, ok := mctx.Module().(android.ApexModule); ok {
+ apexBundleNames = am.ApexVariations()
+ directDep = false
}
+
+ if len(apexBundleNames) == 0 {
+ return
+ }
+
+ mctx.VisitDirectDeps(func(child android.Module) {
+ depName := mctx.OtherModuleName(child)
+ if am, ok := child.(android.ApexModule); ok && am.CanHaveApexVariants() &&
+ (directDep || am.DepIsInSameApex(mctx, child)) {
+ android.UpdateApexDependency(apexBundleNames, depName, directDep)
+ am.BuildForApexes(apexBundleNames)
+ }
+ })
}
// Create apex variations if a module is included in APEX(s).
diff --git a/cc/config/clang.go b/cc/config/clang.go
index 8618d09..26a104b 100644
--- a/cc/config/clang.go
+++ b/cc/config/clang.go
@@ -169,6 +169,13 @@
"-Wno-reorder-init-list",
// http://b/145211066
"-Wno-implicit-int-float-conversion",
+ // New warnings to be fixed after clang-r377782.
+ "-Wno-bitwise-conditional-parentheses", // http://b/148286937
+ "-Wno-bool-operation", // http://b/148287141
+ "-Wno-int-in-bool-context", // http://b/148287349
+ "-Wno-sizeof-array-div", // http://b/148815709
+ "-Wno-tautological-bitwise-compare", // http://b/148831363
+ "-Wno-tautological-overlap-compare", // http://b/148815696
}, " "))
// Extra cflags for external third-party projects to disable warnings that
diff --git a/cc/config/global.go b/cc/config/global.go
index 333885f..57a5852 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -127,8 +127,8 @@
// prebuilts/clang default settings.
ClangDefaultBase = "prebuilts/clang/host"
- ClangDefaultVersion = "clang-r370808b"
- ClangDefaultShortVersion = "10.0.2"
+ ClangDefaultVersion = "clang-r377782"
+ ClangDefaultShortVersion = "10.0.3"
// Directories with warnings from Android.bp files.
WarningAllowedProjects = []string{
diff --git a/cc/config/x86_darwin_host.go b/cc/config/x86_darwin_host.go
index 63b9d48..25225b5 100644
--- a/cc/config/x86_darwin_host.go
+++ b/cc/config/x86_darwin_host.go
@@ -100,7 +100,7 @@
pctx.VariableFunc("macSdkRoot", func(ctx android.PackageVarContext) string {
return xcrunSdk(ctx, "--show-sdk-path")
})
- pctx.StaticVariable("macMinVersion", "10.8")
+ pctx.StaticVariable("macMinVersion", "10.10")
pctx.VariableFunc("MacArPath", func(ctx android.PackageVarContext) string {
return xcrun(ctx, "--find", "ar")
})
diff --git a/dexpreopt/Android.bp b/dexpreopt/Android.bp
index c5f24e2..b8f7ea6 100644
--- a/dexpreopt/Android.bp
+++ b/dexpreopt/Android.bp
@@ -4,6 +4,7 @@
srcs: [
"config.go",
"dexpreopt.go",
+ "testing.go",
],
testSrcs: [
"dexpreopt_test.go",
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index e353878..98850e5 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -16,14 +16,16 @@
import (
"encoding/json"
+ "fmt"
"strings"
+ "github.com/google/blueprint"
+
"android/soong/android"
)
// GlobalConfig stores the configuration for dex preopting. The fields are set
-// from product variables via dex_preopt_config.mk, except for SoongConfig
-// which come from CreateGlobalSoongConfig.
+// from product variables via dex_preopt_config.mk.
type GlobalConfig struct {
DisablePreopt bool // disable preopt for all modules
DisablePreoptModules []string // modules with preopt disabled by product-specific config
@@ -81,8 +83,6 @@
BootFlags string // extra flags to pass to dex2oat for the boot image
Dex2oatImageXmx string // max heap size for dex2oat for the boot image
Dex2oatImageXms string // initial heap size for dex2oat for the boot image
-
- SoongConfig GlobalSoongConfig // settings read from dexpreopt_soong.config
}
// GlobalSoongConfig contains the global config that is generated from Soong,
@@ -178,14 +178,11 @@
return constructPath(ctx, path).(android.WritablePath)
}
-// LoadGlobalConfig reads the global dexpreopt.config file into a GlobalConfig
-// struct, except the SoongConfig field which is set from the provided
-// soongConfig argument. LoadGlobalConfig is used directly in Soong and in
-// dexpreopt_gen called from Make to read the $OUT/dexpreopt.config written by
-// Make.
-func LoadGlobalConfig(ctx android.PathContext, data []byte, soongConfig GlobalSoongConfig) (GlobalConfig, error) {
+// ParseGlobalConfig parses the given data assumed to be read from the global
+// dexpreopt.config file into a GlobalConfig struct.
+func ParseGlobalConfig(ctx android.PathContext, data []byte) (*GlobalConfig, error) {
type GlobalJSONConfig struct {
- GlobalConfig
+ *GlobalConfig
// Copies of entries in GlobalConfig that are not constructable without extra parameters. They will be
// used to construct the real value manually below.
@@ -203,19 +200,70 @@
config.GlobalConfig.DirtyImageObjects = android.OptionalPathForPath(constructPath(ctx, config.DirtyImageObjects))
config.GlobalConfig.BootImageProfiles = constructPaths(ctx, config.BootImageProfiles)
- // Set this here to force the caller to provide a value for this struct (from
- // either CreateGlobalSoongConfig or LoadGlobalSoongConfig).
- config.GlobalConfig.SoongConfig = soongConfig
-
return config.GlobalConfig, nil
}
-// LoadModuleConfig reads a per-module dexpreopt.config file into a ModuleConfig struct. It is not used in Soong, which
-// receives a ModuleConfig struct directly from java/dexpreopt.go. It is used in dexpreopt_gen called from oMake to
-// read the module dexpreopt.config written by Make.
-func LoadModuleConfig(ctx android.PathContext, data []byte) (ModuleConfig, error) {
+type globalConfigAndRaw struct {
+ global *GlobalConfig
+ data []byte
+}
+
+// GetGlobalConfig returns the global dexpreopt.config that's created in the
+// make config phase. It is loaded once the first time it is called for any
+// ctx.Config(), and returns the same data for all future calls with the same
+// ctx.Config(). A value can be inserted for tests using
+// setDexpreoptTestGlobalConfig.
+func GetGlobalConfig(ctx android.PathContext) *GlobalConfig {
+ return getGlobalConfigRaw(ctx).global
+}
+
+// GetGlobalConfigRawData is the same as GetGlobalConfig, except that it returns
+// the literal content of dexpreopt.config.
+func GetGlobalConfigRawData(ctx android.PathContext) []byte {
+ return getGlobalConfigRaw(ctx).data
+}
+
+var globalConfigOnceKey = android.NewOnceKey("DexpreoptGlobalConfig")
+var testGlobalConfigOnceKey = android.NewOnceKey("TestDexpreoptGlobalConfig")
+
+func getGlobalConfigRaw(ctx android.PathContext) globalConfigAndRaw {
+ return ctx.Config().Once(globalConfigOnceKey, func() interface{} {
+ if data, err := ctx.Config().DexpreoptGlobalConfig(ctx); err != nil {
+ panic(err)
+ } else if data != nil {
+ globalConfig, err := ParseGlobalConfig(ctx, data)
+ if err != nil {
+ panic(err)
+ }
+ return globalConfigAndRaw{globalConfig, data}
+ }
+
+ // No global config filename set, see if there is a test config set
+ return ctx.Config().Once(testGlobalConfigOnceKey, func() interface{} {
+ // Nope, return a config with preopting disabled
+ return globalConfigAndRaw{&GlobalConfig{
+ DisablePreopt: true,
+ DisableGenerateProfile: true,
+ }, nil}
+ })
+ }).(globalConfigAndRaw)
+}
+
+// SetTestGlobalConfig sets a GlobalConfig that future calls to GetGlobalConfig
+// will return. It must be called before the first call to GetGlobalConfig for
+// the config.
+func SetTestGlobalConfig(config android.Config, globalConfig *GlobalConfig) {
+ config.Once(testGlobalConfigOnceKey, func() interface{} { return globalConfigAndRaw{globalConfig, nil} })
+}
+
+// ParseModuleConfig parses a per-module dexpreopt.config file into a
+// ModuleConfig struct. It is not used in Soong, which receives a ModuleConfig
+// struct directly from java/dexpreopt.go. It is used in dexpreopt_gen called
+// from Make to read the module dexpreopt.config written in the Make config
+// stage.
+func ParseModuleConfig(ctx android.PathContext, data []byte) (*ModuleConfig, error) {
type ModuleJSONConfig struct {
- ModuleConfig
+ *ModuleConfig
// Copies of entries in ModuleConfig that are not constructable without extra parameters. They will be
// used to construct the real value manually below.
@@ -252,21 +300,63 @@
return config.ModuleConfig, nil
}
-// CreateGlobalSoongConfig creates a GlobalSoongConfig from the current context.
-// Should not be used in dexpreopt_gen.
-func CreateGlobalSoongConfig(ctx android.PathContext) GlobalSoongConfig {
- // Default to debug version to help find bugs.
+// dex2oatModuleName returns the name of the module to use for the dex2oat host
+// tool. It should be a binary module with public visibility that is compiled
+// and installed for host.
+func dex2oatModuleName(config android.Config) string {
+ // Default to the debug variant of dex2oat to help find bugs.
// Set USE_DEX2OAT_DEBUG to false for only building non-debug versions.
- var dex2oatBinary string
- if ctx.Config().Getenv("USE_DEX2OAT_DEBUG") == "false" {
- dex2oatBinary = "dex2oat"
+ if config.Getenv("USE_DEX2OAT_DEBUG") == "false" {
+ return "dex2oat"
} else {
- dex2oatBinary = "dex2oatd"
+ return "dex2oatd"
+ }
+}
+
+var dex2oatDepTag = struct {
+ blueprint.BaseDependencyTag
+}{}
+
+// RegisterToolDeps adds the necessary dependencies to binary modules for tools
+// that are required later when Get(Cached)GlobalSoongConfig is called. It
+// should be called from a mutator that's registered with
+// android.RegistrationContext.FinalDepsMutators.
+func RegisterToolDeps(ctx android.BottomUpMutatorContext) {
+ dex2oatBin := dex2oatModuleName(ctx.Config())
+ v := ctx.Config().BuildOSTarget.Variations()
+ ctx.AddFarVariationDependencies(v, dex2oatDepTag, dex2oatBin)
+}
+
+func dex2oatPathFromDep(ctx android.ModuleContext) android.Path {
+ dex2oatBin := dex2oatModuleName(ctx.Config())
+
+ dex2oatModule := ctx.GetDirectDepWithTag(dex2oatBin, dex2oatDepTag)
+ if dex2oatModule == nil {
+ // If this happens there's probably a missing call to AddToolDeps in DepsMutator.
+ panic(fmt.Sprintf("Failed to lookup %s dependency", dex2oatBin))
}
- return GlobalSoongConfig{
+ dex2oatPath := dex2oatModule.(android.HostToolProvider).HostToolPath()
+ if !dex2oatPath.Valid() {
+ panic(fmt.Sprintf("Failed to find host tool path in %s", dex2oatModule))
+ }
+
+ return dex2oatPath.Path()
+}
+
+// createGlobalSoongConfig creates a GlobalSoongConfig from the current context.
+// Should not be used in dexpreopt_gen.
+func createGlobalSoongConfig(ctx android.ModuleContext) *GlobalSoongConfig {
+ if ctx.Config().TestProductVariables != nil {
+ // If we're called in a test there'll be a confusing error from the path
+ // functions below that gets reported without a stack trace, so let's panic
+ // properly with a more helpful message.
+ panic("This should not be called from tests. Please call GlobalSoongConfigForTests somewhere in the test setup.")
+ }
+
+ return &GlobalSoongConfig{
Profman: ctx.Config().HostToolPath(ctx, "profman"),
- Dex2oat: ctx.Config().HostToolPath(ctx, dex2oatBinary),
+ Dex2oat: dex2oatPathFromDep(ctx),
Aapt: ctx.Config().HostToolPath(ctx, "aapt"),
SoongZip: ctx.Config().HostToolPath(ctx, "soong_zip"),
Zip2zip: ctx.Config().HostToolPath(ctx, "zip2zip"),
@@ -275,6 +365,47 @@
}
}
+// The main reason for this Once cache for GlobalSoongConfig is to make the
+// dex2oat path available to singletons. In ordinary modules we get it through a
+// dex2oatDepTag dependency, but in singletons there's no simple way to do the
+// same thing and ensure the right variant is selected, hence this cache to make
+// the resolved path available to singletons. This means we depend on there
+// being at least one ordinary module with a dex2oatDepTag dependency.
+//
+// TODO(b/147613152): Implement a way to deal with dependencies from singletons,
+// and then possibly remove this cache altogether (but the use in
+// GlobalSoongConfigForTests also needs to be rethought).
+var globalSoongConfigOnceKey = android.NewOnceKey("DexpreoptGlobalSoongConfig")
+
+// GetGlobalSoongConfig creates a GlobalSoongConfig the first time it's called,
+// and later returns the same cached instance.
+func GetGlobalSoongConfig(ctx android.ModuleContext) *GlobalSoongConfig {
+ globalSoong := ctx.Config().Once(globalSoongConfigOnceKey, func() interface{} {
+ return createGlobalSoongConfig(ctx)
+ }).(*GlobalSoongConfig)
+
+ // Always resolve the tool path from the dependency, to ensure that every
+ // module has the dependency added properly.
+ myDex2oat := dex2oatPathFromDep(ctx)
+ if myDex2oat != globalSoong.Dex2oat {
+ panic(fmt.Sprintf("Inconsistent dex2oat path in cached config: expected %s, got %s", globalSoong.Dex2oat, myDex2oat))
+ }
+
+ return globalSoong
+}
+
+// GetCachedGlobalSoongConfig returns a cached GlobalSoongConfig created by an
+// earlier GetGlobalSoongConfig call. This function works with any context
+// compatible with a basic PathContext, since it doesn't try to create a
+// GlobalSoongConfig with the proper paths (which requires a full
+// ModuleContext). If there has been no prior call to GetGlobalSoongConfig, nil
+// is returned.
+func GetCachedGlobalSoongConfig(ctx android.PathContext) *GlobalSoongConfig {
+ return ctx.Config().Once(globalSoongConfigOnceKey, func() interface{} {
+ return (*GlobalSoongConfig)(nil)
+ }).(*GlobalSoongConfig)
+}
+
type globalJsonSoongConfig struct {
Profman string
Dex2oat string
@@ -285,17 +416,18 @@
ConstructContext string
}
-// LoadGlobalSoongConfig reads the dexpreopt_soong.config file into a
-// GlobalSoongConfig struct. It is only used in dexpreopt_gen.
-func LoadGlobalSoongConfig(ctx android.PathContext, data []byte) (GlobalSoongConfig, error) {
+// ParseGlobalSoongConfig parses the given data assumed to be read from the
+// global dexpreopt_soong.config file into a GlobalSoongConfig struct. It is
+// only used in dexpreopt_gen.
+func ParseGlobalSoongConfig(ctx android.PathContext, data []byte) (*GlobalSoongConfig, error) {
var jc globalJsonSoongConfig
err := json.Unmarshal(data, &jc)
if err != nil {
- return GlobalSoongConfig{}, err
+ return &GlobalSoongConfig{}, err
}
- config := GlobalSoongConfig{
+ config := &GlobalSoongConfig{
Profman: constructPath(ctx, jc.Profman),
Dex2oat: constructPath(ctx, jc.Dex2oat),
Aapt: constructPath(ctx, jc.Aapt),
@@ -309,7 +441,17 @@
}
func (s *globalSoongConfigSingleton) GenerateBuildActions(ctx android.SingletonContext) {
- config := CreateGlobalSoongConfig(ctx)
+ if GetGlobalConfig(ctx).DisablePreopt {
+ return
+ }
+
+ config := GetCachedGlobalSoongConfig(ctx)
+ if config == nil {
+ // No module has enabled dexpreopting, so we assume there will be no calls
+ // to dexpreopt_gen.
+ return
+ }
+
jc := globalJsonSoongConfig{
Profman: config.Profman.String(),
Dex2oat: config.Dex2oat.String(),
@@ -336,7 +478,14 @@
}
func (s *globalSoongConfigSingleton) MakeVars(ctx android.MakeVarsContext) {
- config := CreateGlobalSoongConfig(ctx)
+ if GetGlobalConfig(ctx).DisablePreopt {
+ return
+ }
+
+ config := GetCachedGlobalSoongConfig(ctx)
+ if config == nil {
+ return
+ }
ctx.Strict("DEX2OAT", config.Dex2oat.String())
ctx.Strict("DEXPREOPT_GEN_DEPS", strings.Join([]string{
@@ -350,8 +499,8 @@
}, " "))
}
-func GlobalConfigForTests(ctx android.PathContext) GlobalConfig {
- return GlobalConfig{
+func GlobalConfigForTests(ctx android.PathContext) *GlobalConfig {
+ return &GlobalConfig{
DisablePreopt: false,
DisablePreoptModules: nil,
OnlyPreoptBootImageAndSystemServer: false,
@@ -389,7 +538,14 @@
BootFlags: "",
Dex2oatImageXmx: "",
Dex2oatImageXms: "",
- SoongConfig: GlobalSoongConfig{
+ }
+}
+
+func GlobalSoongConfigForTests(config android.Config) *GlobalSoongConfig {
+ // Install the test GlobalSoongConfig in the Once cache so that later calls to
+ // Get(Cached)GlobalSoongConfig returns it without trying to create a real one.
+ return config.Once(globalSoongConfigOnceKey, func() interface{} {
+ return &GlobalSoongConfig{
Profman: android.PathForTesting("profman"),
Dex2oat: android.PathForTesting("dex2oat"),
Aapt: android.PathForTesting("aapt"),
@@ -397,6 +553,6 @@
Zip2zip: android.PathForTesting("zip2zip"),
ManifestCheck: android.PathForTesting("manifest_check"),
ConstructContext: android.PathForTesting("construct_context.sh"),
- },
- }
+ }
+ }).(*GlobalSoongConfig)
}
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 2074339..9b0e7a5 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -41,16 +41,25 @@
"android/soong/android"
+ "github.com/google/blueprint"
"github.com/google/blueprint/pathtools"
)
const SystemPartition = "/system/"
const SystemOtherPartition = "/system_other/"
+type dependencyTag struct {
+ blueprint.BaseDependencyTag
+ name string
+}
+
+var SystemServerDepTag = dependencyTag{name: "system-server-dep"}
+var SystemServerForcedDepTag = dependencyTag{name: "system-server-forced-dep"}
+
// GenerateDexpreoptRule generates a set of commands that will preopt a module based on a GlobalConfig and a
// ModuleConfig. The produced files and their install locations will be available through rule.Installs().
-func GenerateDexpreoptRule(ctx android.PathContext,
- global GlobalConfig, module ModuleConfig) (rule *android.RuleBuilder, err error) {
+func GenerateDexpreoptRule(ctx android.PathContext, globalSoong *GlobalSoongConfig,
+ global *GlobalConfig, module *ModuleConfig) (rule *android.RuleBuilder, err error) {
defer func() {
if r := recover(); r != nil {
@@ -72,13 +81,13 @@
var profile android.WritablePath
if generateProfile {
- profile = profileCommand(ctx, global, module, rule)
+ profile = profileCommand(ctx, globalSoong, global, module, rule)
}
if generateBootProfile {
- bootProfileCommand(ctx, global, module, rule)
+ bootProfileCommand(ctx, globalSoong, global, module, rule)
}
- if !dexpreoptDisabled(global, module) {
+ if !dexpreoptDisabled(ctx, global, module) {
// Don't preopt individual boot jars, they will be preopted together.
if !contains(global.BootJars, module.Name) {
appImage := (generateProfile || module.ForceCreateAppImage || global.DefaultAppImages) &&
@@ -87,7 +96,7 @@
generateDM := shouldGenerateDM(module, global)
for archIdx, _ := range module.Archs {
- dexpreoptCommand(ctx, global, module, rule, archIdx, profile, appImage, generateDM)
+ dexpreoptCommand(ctx, globalSoong, global, module, rule, archIdx, profile, appImage, generateDM)
}
}
}
@@ -95,7 +104,7 @@
return rule, nil
}
-func dexpreoptDisabled(global GlobalConfig, module ModuleConfig) bool {
+func dexpreoptDisabled(ctx android.PathContext, global *GlobalConfig, module *ModuleConfig) bool {
if contains(global.DisablePreoptModules, module.Name) {
return true
}
@@ -107,6 +116,13 @@
}
}
+ // Don't preopt system server jars that are not Soong modules.
+ if android.InList(module.Name, NonUpdatableSystemServerJars(ctx, global)) {
+ if _, ok := ctx.(android.ModuleContext); !ok {
+ return true
+ }
+ }
+
// If OnlyPreoptBootImageAndSystemServer=true and module is not in boot class path skip
// Also preopt system server jars since selinux prevents system server from loading anything from
// /data. If we don't do this they will need to be extracted which is not favorable for RAM usage
@@ -119,8 +135,8 @@
return false
}
-func profileCommand(ctx android.PathContext, global GlobalConfig, module ModuleConfig,
- rule *android.RuleBuilder) android.WritablePath {
+func profileCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, global *GlobalConfig,
+ module *ModuleConfig, rule *android.RuleBuilder) android.WritablePath {
profilePath := module.BuildPath.InSameDir(ctx, "profile.prof")
profileInstalledPath := module.DexLocation + ".prof"
@@ -131,7 +147,7 @@
cmd := rule.Command().
Text(`ANDROID_LOG_TAGS="*:e"`).
- Tool(global.SoongConfig.Profman)
+ Tool(globalSoong.Profman)
if module.ProfileIsTextListing {
// The profile is a test listing of classes (used for framework jars).
@@ -158,8 +174,8 @@
return profilePath
}
-func bootProfileCommand(ctx android.PathContext, global GlobalConfig, module ModuleConfig,
- rule *android.RuleBuilder) android.WritablePath {
+func bootProfileCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, global *GlobalConfig,
+ module *ModuleConfig, rule *android.RuleBuilder) android.WritablePath {
profilePath := module.BuildPath.InSameDir(ctx, "profile.bprof")
profileInstalledPath := module.DexLocation + ".bprof"
@@ -170,7 +186,7 @@
cmd := rule.Command().
Text(`ANDROID_LOG_TAGS="*:e"`).
- Tool(global.SoongConfig.Profman)
+ Tool(globalSoong.Profman)
// The profile is a test listing of methods.
// We need to generate the actual binary profile.
@@ -190,8 +206,9 @@
return profilePath
}
-func dexpreoptCommand(ctx android.PathContext, global GlobalConfig, module ModuleConfig, rule *android.RuleBuilder,
- archIdx int, profile android.WritablePath, appImage bool, generateDM bool) {
+func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, global *GlobalConfig,
+ module *ModuleConfig, rule *android.RuleBuilder, archIdx int, profile android.WritablePath,
+ appImage bool, generateDM bool) {
arch := module.Archs[archIdx]
@@ -236,7 +253,8 @@
var conditionalClassLoaderContextHost29 android.Paths
var conditionalClassLoaderContextTarget29 []string
- var classLoaderContextHostString string
+ var classLoaderContextHostString, classLoaderContextDeviceString string
+ var classLoaderDeps android.Paths
if module.EnforceUsesLibraries {
usesLibs := append(copyOf(module.UsesLibraries), module.PresentOptionalUsesLibraries...)
@@ -282,6 +300,30 @@
filepath.Join("/system/framework", hidlBase+".jar"))
classLoaderContextHostString = strings.Join(classLoaderContextHost.Strings(), ":")
+ } else if android.InList(module.Name, NonUpdatableSystemServerJars(ctx, global)) {
+ // We expect that all dexpreopted system server jars are Soong modules.
+ mctx, isModule := ctx.(android.ModuleContext)
+ if !isModule {
+ panic("Cannot dexpreopt system server jar that is not a soong module.")
+ }
+
+ // System server jars should be dexpreopted together: class loader context of each jar
+ // should include preceding jars (which can be found as dependencies of the current jar
+ // with a special tag).
+ var jarsOnHost android.Paths
+ var jarsOnDevice []string
+ mctx.VisitDirectDepsWithTag(SystemServerDepTag, func(dep android.Module) {
+ depName := mctx.OtherModuleName(dep)
+ if jar, ok := dep.(interface{ DexJar() android.Path }); ok {
+ jarsOnHost = append(jarsOnHost, jar.DexJar())
+ jarsOnDevice = append(jarsOnDevice, "/system/framework/"+depName+".jar")
+ } else {
+ mctx.ModuleErrorf("module \"%s\" is not a jar", depName)
+ }
+ })
+ classLoaderContextHostString = strings.Join(jarsOnHost.Strings(), ":")
+ classLoaderContextDeviceString = strings.Join(jarsOnDevice, ":")
+ classLoaderDeps = jarsOnHost
} else {
// Pass special class loader context to skip the classpath and collision check.
// This will get removed once LOCAL_USES_LIBRARIES is enforced.
@@ -293,20 +335,25 @@
rule.Command().FlagWithArg("mkdir -p ", filepath.Dir(odexPath.String()))
rule.Command().FlagWithOutput("rm -f ", odexPath)
// Set values in the environment of the rule. These may be modified by construct_context.sh.
- rule.Command().FlagWithArg("class_loader_context_arg=--class-loader-context=", classLoaderContextHostString)
- rule.Command().Text(`stored_class_loader_context_arg=""`)
+ if classLoaderContextHostString == `\&` {
+ rule.Command().Text(`class_loader_context_arg=--class-loader-context=\&`)
+ rule.Command().Text(`stored_class_loader_context_arg=""`)
+ } else {
+ rule.Command().Text("class_loader_context_arg=--class-loader-context=PCL[" + classLoaderContextHostString + "]")
+ rule.Command().Text("stored_class_loader_context_arg=--stored-class-loader-context=PCL[" + classLoaderContextDeviceString + "]")
+ }
if module.EnforceUsesLibraries {
if module.ManifestPath != nil {
rule.Command().Text(`target_sdk_version="$(`).
- Tool(global.SoongConfig.ManifestCheck).
+ Tool(globalSoong.ManifestCheck).
Flag("--extract-target-sdk-version").
Input(module.ManifestPath).
Text(`)"`)
} else {
// No manifest to extract targetSdkVersion from, hope that DexJar is an APK
rule.Command().Text(`target_sdk_version="$(`).
- Tool(global.SoongConfig.Aapt).
+ Tool(globalSoong.Aapt).
Flag("dump badging").
Input(module.DexPath).
Text(`| grep "targetSdkVersion" | sed -n "s/targetSdkVersion:'\(.*\)'/\1/p"`).
@@ -327,7 +374,7 @@
Implicits(conditionalClassLoaderContextHost29)
rule.Command().Textf(`conditional_target_libs_29="%s"`,
strings.Join(conditionalClassLoaderContextTarget29, " "))
- rule.Command().Text("source").Tool(global.SoongConfig.ConstructContext).Input(module.DexPath)
+ rule.Command().Text("source").Tool(globalSoong.ConstructContext).Input(module.DexPath)
}
// Devices that do not have a product partition use a symlink from /product to /system/product.
@@ -340,7 +387,7 @@
cmd := rule.Command().
Text(`ANDROID_LOG_TAGS="*:e"`).
- Tool(global.SoongConfig.Dex2oat).
+ Tool(globalSoong.Dex2oat).
Flag("--avoid-storing-invocation").
FlagWithOutput("--write-invocation-to=", invocationPath).ImplicitOutput(invocationPath).
Flag("--runtime-arg").FlagWithArg("-Xms", global.Dex2oatXms).
@@ -348,7 +395,7 @@
Flag("--runtime-arg").FlagWithInputList("-Xbootclasspath:", module.PreoptBootClassPathDexFiles, ":").
Flag("--runtime-arg").FlagWithList("-Xbootclasspath-locations:", module.PreoptBootClassPathDexLocations, ":").
Flag("${class_loader_context_arg}").
- Flag("${stored_class_loader_context_arg}").
+ Flag("${stored_class_loader_context_arg}").Implicits(classLoaderDeps).
FlagWithArg("--boot-image=", strings.Join(module.DexPreoptImageLocations, ":")).Implicits(module.DexPreoptImagesDeps[archIdx].Paths()).
FlagWithInput("--dex-file=", module.DexPath).
FlagWithArg("--dex-location=", dexLocationArg).
@@ -409,7 +456,7 @@
dmInstalledPath := pathtools.ReplaceExtension(module.DexLocation, "dm")
tmpPath := module.BuildPath.InSameDir(ctx, "primary.vdex")
rule.Command().Text("cp -f").Input(vdexPath).Output(tmpPath)
- rule.Command().Tool(global.SoongConfig.SoongZip).
+ rule.Command().Tool(globalSoong.SoongZip).
FlagWithArg("-L", "9").
FlagWithOutput("-o", dmPath).
Flag("-j").
@@ -474,14 +521,14 @@
rule.Install(vdexPath, vdexInstallPath)
}
-func shouldGenerateDM(module ModuleConfig, global GlobalConfig) bool {
+func shouldGenerateDM(module *ModuleConfig, global *GlobalConfig) bool {
// Generating DM files only makes sense for verify, avoid doing for non verify compiler filter APKs.
// No reason to use a dm file if the dex is already uncompressed.
return global.GenerateDMFiles && !module.UncompressedDex &&
contains(module.PreoptFlags, "--compiler-filter=verify")
}
-func OdexOnSystemOtherByName(name string, dexLocation string, global GlobalConfig) bool {
+func OdexOnSystemOtherByName(name string, dexLocation string, global *GlobalConfig) bool {
if !global.HasSystemOther {
return false
}
@@ -503,7 +550,7 @@
return false
}
-func odexOnSystemOther(module ModuleConfig, global GlobalConfig) bool {
+func odexOnSystemOther(module *ModuleConfig, global *GlobalConfig) bool {
return OdexOnSystemOtherByName(module.Name, module.DexLocation, global)
}
@@ -516,7 +563,7 @@
return filepath.Join(filepath.Dir(filepath.Dir(path.String())), filepath.Base(path.String()))
}
-func pathForLibrary(module ModuleConfig, lib string) android.Path {
+func pathForLibrary(module *ModuleConfig, lib string) android.Path {
path, ok := module.LibraryPaths[lib]
if !ok {
panic(fmt.Errorf("unknown library path for %q", lib))
@@ -542,6 +589,26 @@
return filepath.Join("/apex", apex, "javalib", jar+".jar")
}
+func GetJarsFromApexJarPairs(apexJarPairs []string) []string {
+ modules := make([]string, len(apexJarPairs))
+ for i, p := range apexJarPairs {
+ _, jar := android.SplitApexJarPair(p)
+ modules[i] = jar
+ }
+ return modules
+}
+
+var nonUpdatableSystemServerJarsKey = android.NewOnceKey("nonUpdatableSystemServerJars")
+
+// TODO: eliminate the superficial global config parameter by moving global config definition
+// from java subpackage to dexpreopt.
+func NonUpdatableSystemServerJars(ctx android.PathContext, global *GlobalConfig) []string {
+ return ctx.Config().Once(nonUpdatableSystemServerJarsKey, func() interface{} {
+ return android.RemoveListFromList(global.SystemServerJars,
+ GetJarsFromApexJarPairs(global.UpdatableSystemServerJars))
+ }).([]string)
+}
+
func contains(l []string, s string) bool {
for _, e := range l {
if e == s {
diff --git a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
index e2818bb..e89f045 100644
--- a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
+++ b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
@@ -80,13 +80,13 @@
globalSoongConfigData, err := ioutil.ReadFile(*globalSoongConfigPath)
if err != nil {
- fmt.Fprintf(os.Stderr, "error reading global config %q: %s\n", *globalSoongConfigPath, err)
+ fmt.Fprintf(os.Stderr, "error reading global Soong config %q: %s\n", *globalSoongConfigPath, err)
os.Exit(2)
}
- globalSoongConfig, err := dexpreopt.LoadGlobalSoongConfig(ctx, globalSoongConfigData)
+ globalSoongConfig, err := dexpreopt.ParseGlobalSoongConfig(ctx, globalSoongConfigData)
if err != nil {
- fmt.Fprintf(os.Stderr, "error loading global config %q: %s\n", *globalSoongConfigPath, err)
+ fmt.Fprintf(os.Stderr, "error parsing global Soong config %q: %s\n", *globalSoongConfigPath, err)
os.Exit(2)
}
@@ -96,9 +96,9 @@
os.Exit(2)
}
- globalConfig, err := dexpreopt.LoadGlobalConfig(ctx, globalConfigData, globalSoongConfig)
+ globalConfig, err := dexpreopt.ParseGlobalConfig(ctx, globalConfigData)
if err != nil {
- fmt.Fprintf(os.Stderr, "error parse global config %q: %s\n", *globalConfigPath, err)
+ fmt.Fprintf(os.Stderr, "error parsing global config %q: %s\n", *globalConfigPath, err)
os.Exit(2)
}
@@ -108,9 +108,9 @@
os.Exit(2)
}
- moduleConfig, err := dexpreopt.LoadModuleConfig(ctx, moduleConfigData)
+ moduleConfig, err := dexpreopt.ParseModuleConfig(ctx, moduleConfigData)
if err != nil {
- fmt.Fprintf(os.Stderr, "error loading module config %q: %s\n", *moduleConfigPath, err)
+ fmt.Fprintf(os.Stderr, "error parsing module config %q: %s\n", *moduleConfigPath, err)
os.Exit(2)
}
@@ -130,12 +130,12 @@
}
}()
- writeScripts(ctx, globalConfig, moduleConfig, *dexpreoptScriptPath)
+ writeScripts(ctx, globalSoongConfig, globalConfig, moduleConfig, *dexpreoptScriptPath)
}
-func writeScripts(ctx android.PathContext, global dexpreopt.GlobalConfig, module dexpreopt.ModuleConfig,
- dexpreoptScriptPath string) {
- dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, global, module)
+func writeScripts(ctx android.PathContext, globalSoong *dexpreopt.GlobalSoongConfig,
+ global *dexpreopt.GlobalConfig, module *dexpreopt.ModuleConfig, dexpreoptScriptPath string) {
+ dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, globalSoong, global, module)
if err != nil {
panic(err)
}
@@ -150,7 +150,7 @@
dexpreoptRule.Command().Text("mkdir -p").Flag(filepath.Dir(installPath.String()))
dexpreoptRule.Command().Text("cp -f").Input(install.From).Output(installPath)
}
- dexpreoptRule.Command().Tool(global.SoongConfig.SoongZip).
+ dexpreoptRule.Command().Tool(globalSoong.SoongZip).
FlagWithArg("-o ", "$2").
FlagWithArg("-C ", installDir.String()).
FlagWithArg("-D ", installDir.String())
diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go
index a128dc0..d239993 100644
--- a/dexpreopt/dexpreopt_test.go
+++ b/dexpreopt/dexpreopt_test.go
@@ -20,20 +20,20 @@
"testing"
)
-func testSystemModuleConfig(ctx android.PathContext, name string) ModuleConfig {
+func testSystemModuleConfig(ctx android.PathContext, name string) *ModuleConfig {
return testModuleConfig(ctx, name, "system")
}
-func testSystemProductModuleConfig(ctx android.PathContext, name string) ModuleConfig {
+func testSystemProductModuleConfig(ctx android.PathContext, name string) *ModuleConfig {
return testModuleConfig(ctx, name, "system/product")
}
-func testProductModuleConfig(ctx android.PathContext, name string) ModuleConfig {
+func testProductModuleConfig(ctx android.PathContext, name string) *ModuleConfig {
return testModuleConfig(ctx, name, "product")
}
-func testModuleConfig(ctx android.PathContext, name, partition string) ModuleConfig {
- return ModuleConfig{
+func testModuleConfig(ctx android.PathContext, name, partition string) *ModuleConfig {
+ return &ModuleConfig{
Name: name,
DexLocation: fmt.Sprintf("/%s/app/test/%s.apk", partition, name),
BuildPath: android.PathForOutput(ctx, fmt.Sprintf("%s/%s.apk", name, name)),
@@ -61,10 +61,13 @@
}
func TestDexPreopt(t *testing.T) {
- ctx := android.PathContextForTesting(android.TestConfig("out", nil, "", nil))
- global, module := GlobalConfigForTests(ctx), testSystemModuleConfig(ctx, "test")
+ config := android.TestConfig("out", nil, "", nil)
+ ctx := android.PathContextForTesting(config)
+ globalSoong := GlobalSoongConfigForTests(config)
+ global := GlobalConfigForTests(ctx)
+ module := testSystemModuleConfig(ctx, "test")
- rule, err := GenerateDexpreoptRule(ctx, global, module)
+ rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module)
if err != nil {
t.Fatal(err)
}
@@ -80,7 +83,9 @@
}
func TestDexPreoptSystemOther(t *testing.T) {
- ctx := android.PathContextForTesting(android.TestConfig("out", nil, "", nil))
+ config := android.TestConfig("out", nil, "", nil)
+ ctx := android.PathContextForTesting(config)
+ globalSoong := GlobalSoongConfigForTests(config)
global := GlobalConfigForTests(ctx)
systemModule := testSystemModuleConfig(ctx, "Stest")
systemProductModule := testSystemProductModuleConfig(ctx, "SPtest")
@@ -89,7 +94,7 @@
global.HasSystemOther = true
type moduleTest struct {
- module ModuleConfig
+ module *ModuleConfig
expectedPartition string
}
tests := []struct {
@@ -118,7 +123,7 @@
for _, test := range tests {
global.PatternsOnSystemOther = test.patterns
for _, mt := range test.moduleTests {
- rule, err := GenerateDexpreoptRule(ctx, global, mt.module)
+ rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, mt.module)
if err != nil {
t.Fatal(err)
}
@@ -138,12 +143,15 @@
}
func TestDexPreoptProfile(t *testing.T) {
- ctx := android.PathContextForTesting(android.TestConfig("out", nil, "", nil))
- global, module := GlobalConfigForTests(ctx), testSystemModuleConfig(ctx, "test")
+ config := android.TestConfig("out", nil, "", nil)
+ ctx := android.PathContextForTesting(config)
+ globalSoong := GlobalSoongConfigForTests(config)
+ global := GlobalConfigForTests(ctx)
+ module := testSystemModuleConfig(ctx, "test")
module.ProfileClassListing = android.OptionalPathForPath(android.PathForTesting("profile"))
- rule, err := GenerateDexpreoptRule(ctx, global, module)
+ rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module)
if err != nil {
t.Fatal(err)
}
diff --git a/dexpreopt/testing.go b/dexpreopt/testing.go
new file mode 100644
index 0000000..b572eb3
--- /dev/null
+++ b/dexpreopt/testing.go
@@ -0,0 +1,47 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package dexpreopt
+
+import (
+ "android/soong/android"
+)
+
+type dummyToolBinary struct {
+ android.ModuleBase
+}
+
+func (m *dummyToolBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
+
+func (m *dummyToolBinary) HostToolPath() android.OptionalPath {
+ return android.OptionalPathForPath(android.PathForTesting("dex2oat"))
+}
+
+func dummyToolBinaryFactory() android.Module {
+ module := &dummyToolBinary{}
+ android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
+ return module
+}
+
+func RegisterToolModulesForTest(ctx *android.TestContext) {
+ ctx.RegisterModuleType("dummy_tool_binary", dummyToolBinaryFactory)
+}
+
+func BpToolModulesForTest() string {
+ return `
+ dummy_tool_binary {
+ name: "dex2oatd",
+ }
+ `
+}
diff --git a/java/app.go b/java/app.go
index 9503ec4..02f3e7f 100755
--- a/java/app.go
+++ b/java/app.go
@@ -142,6 +142,10 @@
noticeOutputs android.NoticeOutputs
}
+func (a *AndroidApp) IsInstallable() bool {
+ return Bool(a.properties.Installable)
+}
+
func (a *AndroidApp) ExportedProguardFlagFiles() android.Paths {
return nil
}
@@ -338,7 +342,6 @@
installDir = filepath.Join("app", a.installApkName)
}
a.dexpreopter.installPath = android.PathForModuleInstall(ctx, installDir, a.installApkName+".apk")
- a.dexpreopter.isInstallable = Bool(a.properties.Installable)
a.dexpreopter.uncompressedDex = a.shouldUncompressDex(ctx)
a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries()
@@ -922,6 +925,10 @@
Filename *string
}
+func (a *AndroidAppImport) IsInstallable() bool {
+ return true
+}
+
// Updates properties with variant-specific values.
func (a *AndroidAppImport) processVariants(ctx android.LoadHookContext) {
config := ctx.Config()
@@ -1064,7 +1071,6 @@
}
a.dexpreopter.installPath = installDir.Join(ctx, a.BaseModuleName()+".apk")
- a.dexpreopter.isInstallable = true
a.dexpreopter.isPresignedPrebuilt = Bool(a.properties.Presigned)
a.dexpreopter.uncompressedDex = a.shouldUncompressDex(ctx)
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index c81e199..4313964 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -19,6 +19,11 @@
"android/soong/dexpreopt"
)
+type dexpreopterInterface interface {
+ IsInstallable() bool // Structs that embed dexpreopter must implement this.
+ dexpreoptDisabled(ctx android.BaseModuleContext) bool
+}
+
type dexpreopter struct {
dexpreoptProperties DexpreoptProperties
@@ -26,7 +31,6 @@
uncompressedDex bool
isSDKLibrary bool
isTest bool
- isInstallable bool
isPresignedPrebuilt bool
manifestFile android.Path
@@ -58,8 +62,8 @@
}
}
-func (d *dexpreopter) dexpreoptDisabled(ctx android.ModuleContext) bool {
- global := dexpreoptGlobalConfig(ctx)
+func (d *dexpreopter) dexpreoptDisabled(ctx android.BaseModuleContext) bool {
+ global := dexpreopt.GetGlobalConfig(ctx)
if global.DisablePreopt {
return true
@@ -81,7 +85,11 @@
return true
}
- if !d.isInstallable {
+ if !ctx.Module().(dexpreopterInterface).IsInstallable() {
+ return true
+ }
+
+ if ctx.Host() {
return true
}
@@ -95,16 +103,28 @@
return false
}
+func dexpreoptToolDepsMutator(ctx android.BottomUpMutatorContext) {
+ if d, ok := ctx.Module().(dexpreopterInterface); !ok || d.dexpreoptDisabled(ctx) {
+ return
+ }
+ dexpreopt.RegisterToolDeps(ctx)
+}
+
func odexOnSystemOther(ctx android.ModuleContext, installPath android.InstallPath) bool {
- return dexpreopt.OdexOnSystemOtherByName(ctx.ModuleName(), android.InstallPathToOnDevicePath(ctx, installPath), dexpreoptGlobalConfig(ctx))
+ return dexpreopt.OdexOnSystemOtherByName(ctx.ModuleName(), android.InstallPathToOnDevicePath(ctx, installPath), dexpreopt.GetGlobalConfig(ctx))
}
func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.ModuleOutPath) android.ModuleOutPath {
- if d.dexpreoptDisabled(ctx) {
+ // TODO(b/148690468): The check on d.installPath is to bail out in cases where
+ // the dexpreopter struct hasn't been fully initialized before we're called,
+ // e.g. in aar.go. This keeps the behaviour that dexpreopting is effectively
+ // disabled, even if installable is true.
+ if d.dexpreoptDisabled(ctx) || d.installPath.Base() == "." {
return dexJarFile
}
- global := dexpreoptGlobalConfig(ctx)
+ globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
+ global := dexpreopt.GetGlobalConfig(ctx)
bootImage := defaultBootImageConfig(ctx)
dexFiles := bootImage.dexPathsDeps.Paths()
dexLocations := bootImage.dexLocationsDeps
@@ -156,7 +176,7 @@
}
}
- dexpreoptConfig := dexpreopt.ModuleConfig{
+ dexpreoptConfig := &dexpreopt.ModuleConfig{
Name: ctx.ModuleName(),
DexLocation: dexLocation,
BuildPath: android.PathForModuleOut(ctx, "dexpreopt", ctx.ModuleName()+".jar").OutputPath,
@@ -191,7 +211,7 @@
PresignedPrebuilt: d.isPresignedPrebuilt,
}
- dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, global, dexpreoptConfig)
+ dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, globalSoong, global, dexpreoptConfig)
if err != nil {
ctx.ModuleErrorf("error generating dexpreopt rule: %s", err.Error())
return dexJarFile
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 607a437..655a476 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -165,7 +165,7 @@
}
func skipDexpreoptBootJars(ctx android.PathContext) bool {
- if dexpreoptGlobalConfig(ctx).DisablePreopt {
+ if dexpreopt.GetGlobalConfig(ctx).DisablePreopt {
return true
}
@@ -205,11 +205,15 @@
if skipDexpreoptBootJars(ctx) {
return
}
+ if dexpreopt.GetCachedGlobalSoongConfig(ctx) == nil {
+ // No module has enabled dexpreopting, so we assume there will be no boot image to make.
+ return
+ }
d.dexpreoptConfigForMake = android.PathForOutput(ctx, ctx.Config().DeviceName(), "dexpreopt.config")
writeGlobalConfigForMake(ctx, d.dexpreoptConfigForMake)
- global := dexpreoptGlobalConfig(ctx)
+ global := dexpreopt.GetGlobalConfig(ctx)
// Skip recompiling the boot image for the second sanitization phase. We'll get separate paths
// and invalidate first-stage artifacts which are crucial to SANITIZE_LITE builds.
@@ -295,7 +299,8 @@
func buildBootImageRuleForArch(ctx android.SingletonContext, image *bootImage,
arch android.ArchType, profile android.Path, missingDeps []string) android.WritablePaths {
- global := dexpreoptGlobalConfig(ctx)
+ globalSoong := dexpreopt.GetCachedGlobalSoongConfig(ctx)
+ global := dexpreopt.GetGlobalConfig(ctx)
symbolsDir := image.symbolsDir.Join(ctx, image.installSubdir, arch.String())
symbolsFile := symbolsDir.Join(ctx, image.stem+".oat")
@@ -330,7 +335,7 @@
invocationPath := outputPath.ReplaceExtension(ctx, "invocation")
- cmd.Tool(global.SoongConfig.Dex2oat).
+ cmd.Tool(globalSoong.Dex2oat).
Flag("--avoid-storing-invocation").
FlagWithOutput("--write-invocation-to=", invocationPath).ImplicitOutput(invocationPath).
Flag("--runtime-arg").FlagWithArg("-Xms", global.Dex2oatImageXms).
@@ -433,7 +438,8 @@
Rebuild with ART_BOOT_IMAGE_EXTRA_ARGS="--runtime-arg -verbose:verifier" to see verification errors.`
func bootImageProfileRule(ctx android.SingletonContext, image *bootImage, missingDeps []string) android.WritablePath {
- global := dexpreoptGlobalConfig(ctx)
+ globalSoong := dexpreopt.GetCachedGlobalSoongConfig(ctx)
+ global := dexpreopt.GetGlobalConfig(ctx)
if global.DisableGenerateProfile || ctx.Config().IsPdkBuild() || ctx.Config().UnbundledBuild() {
return nil
@@ -464,7 +470,7 @@
rule.Command().
Text(`ANDROID_LOG_TAGS="*:e"`).
- Tool(global.SoongConfig.Profman).
+ Tool(globalSoong.Profman).
FlagWithInput("--create-profile-from=", bootImageProfile).
FlagForEachInput("--apk=", image.dexPathsDeps.Paths()).
FlagForEachArg("--dex-location=", image.dexLocationsDeps).
@@ -487,7 +493,8 @@
var bootImageProfileRuleKey = android.NewOnceKey("bootImageProfileRule")
func bootFrameworkProfileRule(ctx android.SingletonContext, image *bootImage, missingDeps []string) android.WritablePath {
- global := dexpreoptGlobalConfig(ctx)
+ globalSoong := dexpreopt.GetCachedGlobalSoongConfig(ctx)
+ global := dexpreopt.GetGlobalConfig(ctx)
if global.DisableGenerateProfile || ctx.Config().IsPdkBuild() || ctx.Config().UnbundledBuild() {
return nil
@@ -513,7 +520,7 @@
rule.Command().
Text(`ANDROID_LOG_TAGS="*:e"`).
- Tool(global.SoongConfig.Profman).
+ Tool(globalSoong.Profman).
Flag("--generate-boot-profile").
FlagWithInput("--create-profile-from=", bootFrameworkProfile).
FlagForEachInput("--apk=", image.dexPathsDeps.Paths()).
@@ -575,7 +582,7 @@
}
func writeGlobalConfigForMake(ctx android.SingletonContext, path android.WritablePath) {
- data := dexpreoptGlobalConfigRaw(ctx).data
+ data := dexpreopt.GetGlobalConfigRawData(ctx)
ctx.Build(pctx, android.BuildParams{
Rule: android.WriteFile,
diff --git a/java/dexpreopt_bootjars_test.go b/java/dexpreopt_bootjars_test.go
index 4ce30f6..c3b2133 100644
--- a/java/dexpreopt_bootjars_test.go
+++ b/java/dexpreopt_bootjars_test.go
@@ -49,7 +49,7 @@
pathCtx := android.PathContextForTesting(config)
dexpreoptConfig := dexpreopt.GlobalConfigForTests(pathCtx)
dexpreoptConfig.BootJars = []string{"foo", "bar", "baz"}
- setDexpreoptTestGlobalConfig(config, dexpreoptConfig)
+ dexpreopt.SetTestGlobalConfig(config, dexpreoptConfig)
ctx := testContext()
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index 57b6125..96f8042 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -22,60 +22,14 @@
"android/soong/dexpreopt"
)
-// dexpreoptGlobalConfig returns the global dexpreopt.config. It is loaded once the first time it is called for any
-// ctx.Config(), and returns the same data for all future calls with the same ctx.Config(). A value can be inserted
-// for tests using setDexpreoptTestGlobalConfig.
-func dexpreoptGlobalConfig(ctx android.PathContext) dexpreopt.GlobalConfig {
- return dexpreoptGlobalConfigRaw(ctx).global
-}
-
-type globalConfigAndRaw struct {
- global dexpreopt.GlobalConfig
- data []byte
-}
-
-func dexpreoptGlobalConfigRaw(ctx android.PathContext) globalConfigAndRaw {
- return ctx.Config().Once(dexpreoptGlobalConfigKey, func() interface{} {
- if data, err := ctx.Config().DexpreoptGlobalConfig(ctx); err != nil {
- panic(err)
- } else if data != nil {
- soongConfig := dexpreopt.CreateGlobalSoongConfig(ctx)
- globalConfig, err := dexpreopt.LoadGlobalConfig(ctx, data, soongConfig)
- if err != nil {
- panic(err)
- }
- return globalConfigAndRaw{globalConfig, data}
- }
-
- // No global config filename set, see if there is a test config set
- return ctx.Config().Once(dexpreoptTestGlobalConfigKey, func() interface{} {
- // Nope, return a config with preopting disabled
- return globalConfigAndRaw{dexpreopt.GlobalConfig{
- DisablePreopt: true,
- DisableGenerateProfile: true,
- }, nil}
- })
- }).(globalConfigAndRaw)
-}
-
-// setDexpreoptTestGlobalConfig sets a GlobalConfig that future calls to dexpreoptGlobalConfig will return. It must
-// be called before the first call to dexpreoptGlobalConfig for the config.
-func setDexpreoptTestGlobalConfig(config android.Config, globalConfig dexpreopt.GlobalConfig) {
- config.Once(dexpreoptTestGlobalConfigKey, func() interface{} { return globalConfigAndRaw{globalConfig, nil} })
-}
-
-var dexpreoptGlobalConfigKey = android.NewOnceKey("DexpreoptGlobalConfig")
-var dexpreoptTestGlobalConfigKey = android.NewOnceKey("TestDexpreoptGlobalConfig")
-
// systemServerClasspath returns the on-device locations of the modules in the system server classpath. It is computed
// once the first time it is called for any ctx.Config(), and returns the same slice for all future calls with the same
// ctx.Config().
-func systemServerClasspath(ctx android.PathContext) []string {
+func systemServerClasspath(ctx android.MakeVarsContext) []string {
return ctx.Config().OnceStringSlice(systemServerClasspathKey, func() []string {
- global := dexpreoptGlobalConfig(ctx)
-
+ global := dexpreopt.GetGlobalConfig(ctx)
var systemServerClasspathLocations []string
- for _, m := range global.SystemServerJars {
+ for _, m := range *DexpreoptedSystemServerJars(ctx.Config()) {
systemServerClasspathLocations = append(systemServerClasspathLocations,
filepath.Join("/system/framework", m+".jar"))
}
@@ -112,15 +66,6 @@
return moduleName
}
-func getJarsFromApexJarPairs(apexJarPairs []string) []string {
- modules := make([]string, len(apexJarPairs))
- for i, p := range apexJarPairs {
- _, jar := android.SplitApexJarPair(p)
- modules[i] = jar
- }
- return modules
-}
-
var (
bootImageConfigKey = android.NewOnceKey("bootImageConfig")
artBootImageName = "art"
@@ -131,7 +76,7 @@
func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig {
return ctx.Config().Once(bootImageConfigKey, func() interface{} {
- global := dexpreoptGlobalConfig(ctx)
+ global := dexpreopt.GetGlobalConfig(ctx)
targets := dexpreoptTargets(ctx)
deviceDir := android.PathForOutput(ctx, ctx.Config().DeviceName())
@@ -141,7 +86,7 @@
artModules = append(artModules, "jacocoagent")
}
frameworkModules := android.RemoveListFromList(global.BootJars,
- concat(artModules, getJarsFromApexJarPairs(global.UpdatableBootJars)))
+ concat(artModules, dexpreopt.GetJarsFromApexJarPairs(global.UpdatableBootJars)))
artSubdir := "apex/com.android.art/javalib"
frameworkSubdir := "system/framework"
@@ -237,7 +182,7 @@
func defaultBootclasspath(ctx android.PathContext) []string {
return ctx.Config().OnceStringSlice(defaultBootclasspathKey, func() []string {
- global := dexpreoptGlobalConfig(ctx)
+ global := dexpreopt.GetGlobalConfig(ctx)
image := defaultBootImageConfig(ctx)
updatableBootclasspath := make([]string, len(global.UpdatableBootJars))
diff --git a/java/java.go b/java/java.go
index 279d674..ceedd89 100644
--- a/java/java.go
+++ b/java/java.go
@@ -23,12 +23,14 @@
"path/filepath"
"strconv"
"strings"
+ "sync"
"github.com/google/blueprint"
"github.com/google/blueprint/pathtools"
"github.com/google/blueprint/proptools"
"android/soong/android"
+ "android/soong/dexpreopt"
"android/soong/java/config"
"android/soong/tradefed"
)
@@ -52,6 +54,8 @@
PropertyName: "java_tests",
},
})
+
+ android.PostDepsMutators(RegisterPostDepsMutators)
}
func RegisterJavaBuildComponents(ctx android.RegistrationContext) {
@@ -72,10 +76,52 @@
ctx.RegisterModuleType("java_host_for_device", HostForDeviceFactory)
ctx.RegisterModuleType("dex_import", DexImportFactory)
+ ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) {
+ ctx.BottomUp("dexpreopt_tool_deps", dexpreoptToolDepsMutator).Parallel()
+ })
+
ctx.RegisterSingletonType("logtags", LogtagsSingleton)
ctx.RegisterSingletonType("kythe_java_extract", kytheExtractJavaFactory)
}
+func RegisterPostDepsMutators(ctx android.RegisterMutatorsContext) {
+ ctx.BottomUp("ordered_system_server_jars", systemServerJarsDepsMutator)
+}
+
+var (
+ dexpreoptedSystemServerJarsKey = android.NewOnceKey("dexpreoptedSystemServerJars")
+ dexpreoptedSystemServerJarsLock sync.Mutex
+)
+
+func DexpreoptedSystemServerJars(config android.Config) *[]string {
+ return config.Once(dexpreoptedSystemServerJarsKey, func() interface{} {
+ return &[]string{}
+ }).(*[]string)
+}
+
+// A PostDepsMutator pass that enforces total order on non-updatable system server jars. A total
+// order is neededed because such jars must be dexpreopted together (each jar on the list must have
+// all preceding jars in its class loader context). The total order must be compatible with the
+// partial order imposed by genuine dependencies between system server jars (which is not always
+// respected by the PRODUCT_SYSTEM_SERVER_JARS variable).
+//
+// An earlier mutator pass creates genuine dependencies, and this pass traverses the jars in that
+// order (which is partial and non-deterministic). This pass adds additional dependencies between
+// jars, making the order total and deterministic. It also constructs a global ordered list.
+func systemServerJarsDepsMutator(ctx android.BottomUpMutatorContext) {
+ jars := dexpreopt.NonUpdatableSystemServerJars(ctx, dexpreopt.GetGlobalConfig(ctx))
+ name := ctx.ModuleName()
+ if android.InList(name, jars) {
+ dexpreoptedSystemServerJarsLock.Lock()
+ defer dexpreoptedSystemServerJarsLock.Unlock()
+ jars := DexpreoptedSystemServerJars(ctx.Config())
+ for _, dep := range *jars {
+ ctx.AddDependency(ctx.Module(), dexpreopt.SystemServerDepTag, dep)
+ }
+ *jars = append(*jars, name)
+ }
+}
+
func (j *Module) checkSdkVersion(ctx android.ModuleContext) {
if j.SocSpecific() || j.DeviceSpecific() ||
(j.ProductSpecific() && ctx.Config().EnforceProductPartitionInterface()) {
@@ -659,6 +705,11 @@
} else if j.shouldInstrumentStatic(ctx) {
ctx.AddVariationDependencies(nil, staticLibTag, "jacocoagent")
}
+
+ // services depend on com.android.location.provider, but dependency in not registered in a Blueprint file
+ if ctx.ModuleName() == "services" {
+ ctx.AddDependency(ctx.Module(), dexpreopt.SystemServerForcedDepTag, "com.android.location.provider")
+ }
}
func hasSrcExt(srcs []string, ext string) bool {
@@ -1734,6 +1785,10 @@
return j.jacocoReportClassesFile
}
+func (j *Module) IsInstallable() bool {
+ return Bool(j.properties.Installable)
+}
+
//
// Java libraries (.jar file)
//
@@ -1771,7 +1826,6 @@
j.checkSdkVersion(ctx)
j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar")
j.dexpreopter.isSDKLibrary = j.deviceProperties.IsSDKLibrary
- j.dexpreopter.isInstallable = Bool(j.properties.Installable)
j.dexpreopter.uncompressedDex = shouldUncompressDex(ctx, &j.dexpreopter)
j.deviceProperties.UncompressDex = j.dexpreopter.uncompressedDex
j.compile(ctx, nil)
@@ -2528,13 +2582,16 @@
return proptools.StringDefault(j.properties.Stem, j.ModuleBase.Name())
}
+func (j *DexImport) IsInstallable() bool {
+ return true
+}
+
func (j *DexImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
if len(j.properties.Jars) != 1 {
ctx.PropertyErrorf("jars", "exactly one jar must be provided")
}
j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar")
- j.dexpreopter.isInstallable = true
j.dexpreopter.uncompressedDex = shouldUncompressDex(ctx, &j.dexpreopter)
inputJar := ctx.ExpandSource(j.properties.Jars[0], "jars")
diff --git a/java/java_test.go b/java/java_test.go
index 17921ca..a2226b5 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -57,7 +57,15 @@
}
func testConfig(env map[string]string, bp string, fs map[string][]byte) android.Config {
- return TestConfig(buildDir, env, bp, fs)
+ bp += dexpreopt.BpToolModulesForTest()
+
+ config := TestConfig(buildDir, env, bp, fs)
+
+ // Set up the global Once cache used for dexpreopt.GlobalSoongConfig, so that
+ // it doesn't create a real one, which would fail.
+ _ = dexpreopt.GlobalSoongConfigForTests(config)
+
+ return config
}
func testContext() *android.TestContext {
@@ -86,6 +94,8 @@
cc.RegisterRequiredBuildComponentsForTest(ctx)
ctx.RegisterModuleType("ndk_prebuilt_shared_stl", cc.NdkPrebuiltSharedStlFactory)
+ dexpreopt.RegisterToolModulesForTest(ctx)
+
return ctx
}
@@ -93,7 +103,7 @@
t.Helper()
pathCtx := android.PathContextForTesting(config)
- setDexpreoptTestGlobalConfig(config, dexpreopt.GlobalConfigForTests(pathCtx))
+ dexpreopt.SetTestGlobalConfig(config, dexpreopt.GlobalConfigForTests(pathCtx))
ctx.Register(config)
_, errs := ctx.ParseBlueprintsFiles("Android.bp")
@@ -112,7 +122,7 @@
ctx := testContext()
pathCtx := android.PathContextForTesting(config)
- setDexpreoptTestGlobalConfig(config, dexpreopt.GlobalConfigForTests(pathCtx))
+ dexpreopt.SetTestGlobalConfig(config, dexpreopt.GlobalConfigForTests(pathCtx))
ctx.Register(config)
_, errs := ctx.ParseBlueprintsFiles("Android.bp")
diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go
index 1c4f574..36d4f04 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -237,6 +237,7 @@
if fi.IsDir() {
if err := os.Remove(old); err == nil {
ctx.Println("Removed directory that is no longer installed: ", old)
+ cleanEmptyDirs(ctx, filepath.Dir(old))
} else {
ctx.Println("Failed to remove directory that is no longer installed (%q): %v", old, err)
ctx.Println("It's recommended to run `m installclean`")
@@ -244,6 +245,7 @@
} else {
if err := os.Remove(old); err == nil {
ctx.Println("Removed file that is no longer installed: ", old)
+ cleanEmptyDirs(ctx, filepath.Dir(old))
} else if !os.IsNotExist(err) {
ctx.Fatalf("Failed to remove file that is no longer installed (%q): %v", old, err)
}
@@ -254,3 +256,16 @@
// Use the new list as the base for the next build
os.Rename(file, oldFile)
}
+
+func cleanEmptyDirs(ctx Context, dir string) {
+ files, err := ioutil.ReadDir(dir)
+ if err != nil || len(files) > 0 {
+ return
+ }
+ if err := os.Remove(dir); err == nil {
+ ctx.Println("Removed directory that is no longer installed: ", dir)
+ } else {
+ ctx.Fatalf("Failed to remove directory that is no longer installed (%q): %v", dir, err)
+ }
+ cleanEmptyDirs(ctx, filepath.Dir(dir))
+}