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