1// Copyright 2018 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package dexpreopt 16 17import ( 18 "encoding/json" 19 "fmt" 20 "strings" 21 22 "github.com/google/blueprint" 23 24 "android/soong/android" 25) 26 27// GlobalConfig stores the configuration for dex preopting. The fields are set 28// from product variables via dex_preopt_config.mk. 29type GlobalConfig struct { 30 DisablePreopt bool // disable preopt for all modules 31 DisablePreoptModules []string // modules with preopt disabled by product-specific config 32 33 OnlyPreoptBootImageAndSystemServer bool // only preopt jars in the boot image or system server 34 35 UseArtImage bool // use the art image (use other boot class path dex files without image) 36 37 HasSystemOther bool // store odex files that match PatternsOnSystemOther on the system_other partition 38 PatternsOnSystemOther []string // patterns (using '%' to denote a prefix match) to put odex on the system_other partition 39 40 DisableGenerateProfile bool // don't generate profiles 41 ProfileDir string // directory to find profiles in 42 43 BootJars []string // modules for jars that form the boot class path 44 UpdatableBootJars []string // jars within apex that form the boot class path 45 46 ArtApexJars []string // modules for jars that are in the ART APEX 47 48 SystemServerJars []string // jars that form the system server 49 SystemServerApps []string // apps that are loaded into system server 50 UpdatableSystemServerJars []string // jars within apex that are loaded into system server 51 SpeedApps []string // apps that should be speed optimized 52 53 PreoptFlags []string // global dex2oat flags that should be used if no module-specific dex2oat flags are specified 54 55 DefaultCompilerFilter string // default compiler filter to pass to dex2oat, overridden by --compiler-filter= in module-specific dex2oat flags 56 SystemServerCompilerFilter string // default compiler filter to pass to dex2oat for system server jars 57 58 GenerateDMFiles bool // generate Dex Metadata files 59 60 NoDebugInfo bool // don't generate debug info by default 61 DontResolveStartupStrings bool // don't resolve string literals loaded during application startup. 62 AlwaysSystemServerDebugInfo bool // always generate mini debug info for system server modules (overrides NoDebugInfo=true) 63 NeverSystemServerDebugInfo bool // never generate mini debug info for system server modules (overrides NoDebugInfo=false) 64 AlwaysOtherDebugInfo bool // always generate mini debug info for non-system server modules (overrides NoDebugInfo=true) 65 NeverOtherDebugInfo bool // never generate mini debug info for non-system server modules (overrides NoDebugInfo=true) 66 67 IsEng bool // build is a eng variant 68 SanitizeLite bool // build is the second phase of a SANITIZE_LITE build 69 70 DefaultAppImages bool // build app images (TODO: .art files?) by default 71 72 Dex2oatXmx string // max heap size for dex2oat 73 Dex2oatXms string // initial heap size for dex2oat 74 75 EmptyDirectory string // path to an empty directory 76 77 CpuVariant map[android.ArchType]string // cpu variant for each architecture 78 InstructionSetFeatures map[android.ArchType]string // instruction set for each architecture 79 80 // Only used for boot image 81 DirtyImageObjects android.OptionalPath // path to a dirty-image-objects file 82 BootImageProfiles android.Paths // path to a boot-image-profile.txt file 83 BootFlags string // extra flags to pass to dex2oat for the boot image 84 Dex2oatImageXmx string // max heap size for dex2oat for the boot image 85 Dex2oatImageXms string // initial heap size for dex2oat for the boot image 86} 87 88// GlobalSoongConfig contains the global config that is generated from Soong, 89// stored in dexpreopt_soong.config. 90type GlobalSoongConfig struct { 91 // Paths to tools possibly used by the generated commands. 92 Profman android.Path 93 Dex2oat android.Path 94 Aapt android.Path 95 SoongZip android.Path 96 Zip2zip android.Path 97 ManifestCheck android.Path 98 ConstructContext android.Path 99} 100 101type ModuleConfig struct { 102 Name string 103 DexLocation string // dex location on device 104 BuildPath android.OutputPath 105 DexPath android.Path 106 ManifestPath android.Path 107 UncompressedDex bool 108 HasApkLibraries bool 109 PreoptFlags []string 110 111 ProfileClassListing android.OptionalPath 112 ProfileIsTextListing bool 113 ProfileBootListing android.OptionalPath 114 115 EnforceUsesLibraries bool 116 PresentOptionalUsesLibraries []string 117 UsesLibraries []string 118 LibraryPaths map[string]android.Path 119 120 Archs []android.ArchType 121 DexPreoptImages []android.Path 122 DexPreoptImagesDeps []android.OutputPaths 123 DexPreoptImageLocations []string 124 125 PreoptBootClassPathDexFiles android.Paths // file paths of boot class path files 126 PreoptBootClassPathDexLocations []string // virtual locations of boot class path files 127 128 PreoptExtractedApk bool // Overrides OnlyPreoptModules 129 130 NoCreateAppImage bool 131 ForceCreateAppImage bool 132 133 PresignedPrebuilt bool 134} 135 136type globalSoongConfigSingleton struct{} 137 138var pctx = android.NewPackageContext("android/soong/dexpreopt") 139 140func init() { 141 pctx.Import("android/soong/android") 142 android.RegisterSingletonType("dexpreopt-soong-config", func() android.Singleton { 143 return &globalSoongConfigSingleton{} 144 }) 145} 146 147func constructPath(ctx android.PathContext, path string) android.Path { 148 buildDirPrefix := ctx.Config().BuildDir() + "/" 149 if path == "" { 150 return nil 151 } else if strings.HasPrefix(path, buildDirPrefix) { 152 return android.PathForOutput(ctx, strings.TrimPrefix(path, buildDirPrefix)) 153 } else { 154 return android.PathForSource(ctx, path) 155 } 156} 157 158func constructPaths(ctx android.PathContext, paths []string) android.Paths { 159 var ret android.Paths 160 for _, path := range paths { 161 ret = append(ret, constructPath(ctx, path)) 162 } 163 return ret 164} 165 166func constructPathMap(ctx android.PathContext, paths map[string]string) map[string]android.Path { 167 ret := map[string]android.Path{} 168 for key, path := range paths { 169 ret[key] = constructPath(ctx, path) 170 } 171 return ret 172} 173 174func constructWritablePath(ctx android.PathContext, path string) android.WritablePath { 175 if path == "" { 176 return nil 177 } 178 return constructPath(ctx, path).(android.WritablePath) 179} 180 181// ParseGlobalConfig parses the given data assumed to be read from the global 182// dexpreopt.config file into a GlobalConfig struct. 183func ParseGlobalConfig(ctx android.PathContext, data []byte) (*GlobalConfig, error) { 184 type GlobalJSONConfig struct { 185 *GlobalConfig 186 187 // Copies of entries in GlobalConfig that are not constructable without extra parameters. They will be 188 // used to construct the real value manually below. 189 DirtyImageObjects string 190 BootImageProfiles []string 191 } 192 193 config := GlobalJSONConfig{} 194 err := json.Unmarshal(data, &config) 195 if err != nil { 196 return config.GlobalConfig, err 197 } 198 199 // Construct paths that require a PathContext. 200 config.GlobalConfig.DirtyImageObjects = android.OptionalPathForPath(constructPath(ctx, config.DirtyImageObjects)) 201 config.GlobalConfig.BootImageProfiles = constructPaths(ctx, config.BootImageProfiles) 202 203 return config.GlobalConfig, nil 204} 205 206type globalConfigAndRaw struct { 207 global *GlobalConfig 208 data []byte 209} 210 211// GetGlobalConfig returns the global dexpreopt.config that's created in the 212// make config phase. It is loaded once the first time it is called for any 213// ctx.Config(), and returns the same data for all future calls with the same 214// ctx.Config(). A value can be inserted for tests using 215// setDexpreoptTestGlobalConfig. 216func GetGlobalConfig(ctx android.PathContext) *GlobalConfig { 217 return getGlobalConfigRaw(ctx).global 218} 219 220// GetGlobalConfigRawData is the same as GetGlobalConfig, except that it returns 221// the literal content of dexpreopt.config. 222func GetGlobalConfigRawData(ctx android.PathContext) []byte { 223 return getGlobalConfigRaw(ctx).data 224} 225 226var globalConfigOnceKey = android.NewOnceKey("DexpreoptGlobalConfig") 227var testGlobalConfigOnceKey = android.NewOnceKey("TestDexpreoptGlobalConfig") 228 229func getGlobalConfigRaw(ctx android.PathContext) globalConfigAndRaw { 230 return ctx.Config().Once(globalConfigOnceKey, func() interface{} { 231 if data, err := ctx.Config().DexpreoptGlobalConfig(ctx); err != nil { 232 panic(err) 233 } else if data != nil { 234 globalConfig, err := ParseGlobalConfig(ctx, data) 235 if err != nil { 236 panic(err) 237 } 238 return globalConfigAndRaw{globalConfig, data} 239 } 240 241 // No global config filename set, see if there is a test config set 242 return ctx.Config().Once(testGlobalConfigOnceKey, func() interface{} { 243 // Nope, return a config with preopting disabled 244 return globalConfigAndRaw{&GlobalConfig{ 245 DisablePreopt: true, 246 DisableGenerateProfile: true, 247 }, nil} 248 }) 249 }).(globalConfigAndRaw) 250} 251 252// SetTestGlobalConfig sets a GlobalConfig that future calls to GetGlobalConfig 253// will return. It must be called before the first call to GetGlobalConfig for 254// the config. 255func SetTestGlobalConfig(config android.Config, globalConfig *GlobalConfig) { 256 config.Once(testGlobalConfigOnceKey, func() interface{} { return globalConfigAndRaw{globalConfig, nil} }) 257} 258 259// ParseModuleConfig parses a per-module dexpreopt.config file into a 260// ModuleConfig struct. It is not used in Soong, which receives a ModuleConfig 261// struct directly from java/dexpreopt.go. It is used in dexpreopt_gen called 262// from Make to read the module dexpreopt.config written in the Make config 263// stage. 264func ParseModuleConfig(ctx android.PathContext, data []byte) (*ModuleConfig, error) { 265 type ModuleJSONConfig struct { 266 *ModuleConfig 267 268 // Copies of entries in ModuleConfig that are not constructable without extra parameters. They will be 269 // used to construct the real value manually below. 270 BuildPath string 271 DexPath string 272 ManifestPath string 273 ProfileClassListing string 274 LibraryPaths map[string]string 275 DexPreoptImages []string 276 DexPreoptImageLocations []string 277 PreoptBootClassPathDexFiles []string 278 } 279 280 config := ModuleJSONConfig{} 281 282 err := json.Unmarshal(data, &config) 283 if err != nil { 284 return config.ModuleConfig, err 285 } 286 287 // Construct paths that require a PathContext. 288 config.ModuleConfig.BuildPath = constructPath(ctx, config.BuildPath).(android.OutputPath) 289 config.ModuleConfig.DexPath = constructPath(ctx, config.DexPath) 290 config.ModuleConfig.ManifestPath = constructPath(ctx, config.ManifestPath) 291 config.ModuleConfig.ProfileClassListing = android.OptionalPathForPath(constructPath(ctx, config.ProfileClassListing)) 292 config.ModuleConfig.LibraryPaths = constructPathMap(ctx, config.LibraryPaths) 293 config.ModuleConfig.DexPreoptImages = constructPaths(ctx, config.DexPreoptImages) 294 config.ModuleConfig.DexPreoptImageLocations = config.DexPreoptImageLocations 295 config.ModuleConfig.PreoptBootClassPathDexFiles = constructPaths(ctx, config.PreoptBootClassPathDexFiles) 296 297 // This needs to exist, but dependencies are already handled in Make, so we don't need to pass them through JSON. 298 config.ModuleConfig.DexPreoptImagesDeps = make([]android.OutputPaths, len(config.ModuleConfig.DexPreoptImages)) 299 300 return config.ModuleConfig, nil 301} 302 303// dex2oatModuleName returns the name of the module to use for the dex2oat host 304// tool. It should be a binary module with public visibility that is compiled 305// and installed for host. 306func dex2oatModuleName(config android.Config) string { 307 // Default to the debug variant of dex2oat to help find bugs. 308 // Set USE_DEX2OAT_DEBUG to false for only building non-debug versions. 309 if config.Getenv("USE_DEX2OAT_DEBUG") == "false" { 310 return "dex2oat" 311 } else { 312 return "dex2oatd" 313 } 314} 315 316var dex2oatDepTag = struct { 317 blueprint.BaseDependencyTag 318}{} 319 320// RegisterToolDeps adds the necessary dependencies to binary modules for tools 321// that are required later when Get(Cached)GlobalSoongConfig is called. It 322// should be called from a mutator that's registered with 323// android.RegistrationContext.FinalDepsMutators. 324func RegisterToolDeps(ctx android.BottomUpMutatorContext) { 325 dex2oatBin := dex2oatModuleName(ctx.Config()) 326 v := ctx.Config().BuildOSTarget.Variations() 327 ctx.AddFarVariationDependencies(v, dex2oatDepTag, dex2oatBin) 328} 329 330func dex2oatPathFromDep(ctx android.ModuleContext) android.Path { 331 dex2oatBin := dex2oatModuleName(ctx.Config()) 332 333 dex2oatModule := ctx.GetDirectDepWithTag(dex2oatBin, dex2oatDepTag) 334 if dex2oatModule == nil { 335 // If this happens there's probably a missing call to AddToolDeps in DepsMutator. 336 panic(fmt.Sprintf("Failed to lookup %s dependency", dex2oatBin)) 337 } 338 339 dex2oatPath := dex2oatModule.(android.HostToolProvider).HostToolPath() 340 if !dex2oatPath.Valid() { 341 panic(fmt.Sprintf("Failed to find host tool path in %s", dex2oatModule)) 342 } 343 344 return dex2oatPath.Path() 345} 346 347// createGlobalSoongConfig creates a GlobalSoongConfig from the current context. 348// Should not be used in dexpreopt_gen. 349func createGlobalSoongConfig(ctx android.ModuleContext) *GlobalSoongConfig { 350 if ctx.Config().TestProductVariables != nil { 351 // If we're called in a test there'll be a confusing error from the path 352 // functions below that gets reported without a stack trace, so let's panic 353 // properly with a more helpful message. 354 panic("This should not be called from tests. Please call GlobalSoongConfigForTests somewhere in the test setup.") 355 } 356 357 return &GlobalSoongConfig{ 358 Profman: ctx.Config().HostToolPath(ctx, "profman"), 359 Dex2oat: dex2oatPathFromDep(ctx), 360 Aapt: ctx.Config().HostToolPath(ctx, "aapt"), 361 SoongZip: ctx.Config().HostToolPath(ctx, "soong_zip"), 362 Zip2zip: ctx.Config().HostToolPath(ctx, "zip2zip"), 363 ManifestCheck: ctx.Config().HostToolPath(ctx, "manifest_check"), 364 ConstructContext: android.PathForSource(ctx, "build/make/core/construct_context.sh"), 365 } 366} 367 368// The main reason for this Once cache for GlobalSoongConfig is to make the 369// dex2oat path available to singletons. In ordinary modules we get it through a 370// dex2oatDepTag dependency, but in singletons there's no simple way to do the 371// same thing and ensure the right variant is selected, hence this cache to make 372// the resolved path available to singletons. This means we depend on there 373// being at least one ordinary module with a dex2oatDepTag dependency. 374// 375// TODO(b/147613152): Implement a way to deal with dependencies from singletons, 376// and then possibly remove this cache altogether (but the use in 377// GlobalSoongConfigForTests also needs to be rethought). 378var globalSoongConfigOnceKey = android.NewOnceKey("DexpreoptGlobalSoongConfig") 379 380// GetGlobalSoongConfig creates a GlobalSoongConfig the first time it's called, 381// and later returns the same cached instance. 382func GetGlobalSoongConfig(ctx android.ModuleContext) *GlobalSoongConfig { 383 globalSoong := ctx.Config().Once(globalSoongConfigOnceKey, func() interface{} { 384 return createGlobalSoongConfig(ctx) 385 }).(*GlobalSoongConfig) 386 387 // Always resolve the tool path from the dependency, to ensure that every 388 // module has the dependency added properly. 389 myDex2oat := dex2oatPathFromDep(ctx) 390 if myDex2oat != globalSoong.Dex2oat { 391 panic(fmt.Sprintf("Inconsistent dex2oat path in cached config: expected %s, got %s", globalSoong.Dex2oat, myDex2oat)) 392 } 393 394 return globalSoong 395} 396 397// GetCachedGlobalSoongConfig returns a cached GlobalSoongConfig created by an 398// earlier GetGlobalSoongConfig call. This function works with any context 399// compatible with a basic PathContext, since it doesn't try to create a 400// GlobalSoongConfig with the proper paths (which requires a full 401// ModuleContext). If there has been no prior call to GetGlobalSoongConfig, nil 402// is returned. 403func GetCachedGlobalSoongConfig(ctx android.PathContext) *GlobalSoongConfig { 404 return ctx.Config().Once(globalSoongConfigOnceKey, func() interface{} { 405 return (*GlobalSoongConfig)(nil) 406 }).(*GlobalSoongConfig) 407} 408 409type globalJsonSoongConfig struct { 410 Profman string 411 Dex2oat string 412 Aapt string 413 SoongZip string 414 Zip2zip string 415 ManifestCheck string 416 ConstructContext string 417} 418 419// ParseGlobalSoongConfig parses the given data assumed to be read from the 420// global dexpreopt_soong.config file into a GlobalSoongConfig struct. It is 421// only used in dexpreopt_gen. 422func ParseGlobalSoongConfig(ctx android.PathContext, data []byte) (*GlobalSoongConfig, error) { 423 var jc globalJsonSoongConfig 424 425 err := json.Unmarshal(data, &jc) 426 if err != nil { 427 return &GlobalSoongConfig{}, err 428 } 429 430 config := &GlobalSoongConfig{ 431 Profman: constructPath(ctx, jc.Profman), 432 Dex2oat: constructPath(ctx, jc.Dex2oat), 433 Aapt: constructPath(ctx, jc.Aapt), 434 SoongZip: constructPath(ctx, jc.SoongZip), 435 Zip2zip: constructPath(ctx, jc.Zip2zip), 436 ManifestCheck: constructPath(ctx, jc.ManifestCheck), 437 ConstructContext: constructPath(ctx, jc.ConstructContext), 438 } 439 440 return config, nil 441} 442 443func (s *globalSoongConfigSingleton) GenerateBuildActions(ctx android.SingletonContext) { 444 if GetGlobalConfig(ctx).DisablePreopt { 445 return 446 } 447 448 config := GetCachedGlobalSoongConfig(ctx) 449 if config == nil { 450 // No module has enabled dexpreopting, so we assume there will be no calls 451 // to dexpreopt_gen. 452 return 453 } 454 455 jc := globalJsonSoongConfig{ 456 Profman: config.Profman.String(), 457 Dex2oat: config.Dex2oat.String(), 458 Aapt: config.Aapt.String(), 459 SoongZip: config.SoongZip.String(), 460 Zip2zip: config.Zip2zip.String(), 461 ManifestCheck: config.ManifestCheck.String(), 462 ConstructContext: config.ConstructContext.String(), 463 } 464 465 data, err := json.Marshal(jc) 466 if err != nil { 467 ctx.Errorf("failed to JSON marshal GlobalSoongConfig: %v", err) 468 return 469 } 470 471 ctx.Build(pctx, android.BuildParams{ 472 Rule: android.WriteFile, 473 Output: android.PathForOutput(ctx, "dexpreopt_soong.config"), 474 Args: map[string]string{ 475 "content": string(data), 476 }, 477 }) 478} 479 480func (s *globalSoongConfigSingleton) MakeVars(ctx android.MakeVarsContext) { 481 if GetGlobalConfig(ctx).DisablePreopt { 482 return 483 } 484 485 config := GetCachedGlobalSoongConfig(ctx) 486 if config == nil { 487 return 488 } 489 490 ctx.Strict("DEX2OAT", config.Dex2oat.String()) 491 ctx.Strict("DEXPREOPT_GEN_DEPS", strings.Join([]string{ 492 config.Profman.String(), 493 config.Dex2oat.String(), 494 config.Aapt.String(), 495 config.SoongZip.String(), 496 config.Zip2zip.String(), 497 config.ManifestCheck.String(), 498 config.ConstructContext.String(), 499 }, " ")) 500} 501 502func GlobalConfigForTests(ctx android.PathContext) *GlobalConfig { 503 return &GlobalConfig{ 504 DisablePreopt: false, 505 DisablePreoptModules: nil, 506 OnlyPreoptBootImageAndSystemServer: false, 507 HasSystemOther: false, 508 PatternsOnSystemOther: nil, 509 DisableGenerateProfile: false, 510 ProfileDir: "", 511 BootJars: nil, 512 UpdatableBootJars: nil, 513 ArtApexJars: nil, 514 SystemServerJars: nil, 515 SystemServerApps: nil, 516 UpdatableSystemServerJars: nil, 517 SpeedApps: nil, 518 PreoptFlags: nil, 519 DefaultCompilerFilter: "", 520 SystemServerCompilerFilter: "", 521 GenerateDMFiles: false, 522 NoDebugInfo: false, 523 DontResolveStartupStrings: false, 524 AlwaysSystemServerDebugInfo: false, 525 NeverSystemServerDebugInfo: false, 526 AlwaysOtherDebugInfo: false, 527 NeverOtherDebugInfo: false, 528 IsEng: false, 529 SanitizeLite: false, 530 DefaultAppImages: false, 531 Dex2oatXmx: "", 532 Dex2oatXms: "", 533 EmptyDirectory: "empty_dir", 534 CpuVariant: nil, 535 InstructionSetFeatures: nil, 536 DirtyImageObjects: android.OptionalPath{}, 537 BootImageProfiles: nil, 538 BootFlags: "", 539 Dex2oatImageXmx: "", 540 Dex2oatImageXms: "", 541 } 542} 543 544func GlobalSoongConfigForTests(config android.Config) *GlobalSoongConfig { 545 // Install the test GlobalSoongConfig in the Once cache so that later calls to 546 // Get(Cached)GlobalSoongConfig returns it without trying to create a real one. 547 return config.Once(globalSoongConfigOnceKey, func() interface{} { 548 return &GlobalSoongConfig{ 549 Profman: android.PathForTesting("profman"), 550 Dex2oat: android.PathForTesting("dex2oat"), 551 Aapt: android.PathForTesting("aapt"), 552 SoongZip: android.PathForTesting("soong_zip"), 553 Zip2zip: android.PathForTesting("zip2zip"), 554 ManifestCheck: android.PathForTesting("manifest_check"), 555 ConstructContext: android.PathForTesting("construct_context.sh"), 556 } 557 }).(*GlobalSoongConfig) 558} 559