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 "reflect" 21 "strings" 22 23 "github.com/google/blueprint" 24 25 "android/soong/android" 26) 27 28// GlobalConfig stores the configuration for dex preopting. The fields are set 29// from product variables via dex_preopt_config.mk. 30type GlobalConfig struct { 31 DisablePreopt bool // disable preopt for all modules (excluding boot images) 32 DisablePreoptBootImages bool // disable prepot for boot images 33 DisablePreoptModules []string // modules with preopt disabled by product-specific config 34 35 OnlyPreoptArtBootImage bool // only preopt jars in the ART boot image 36 37 PreoptWithUpdatableBcp bool // If updatable boot jars are included in dexpreopt or not. 38 39 HasSystemOther bool // store odex files that match PatternsOnSystemOther on the system_other partition 40 PatternsOnSystemOther []string // patterns (using '%' to denote a prefix match) to put odex on the system_other partition 41 42 DisableGenerateProfile bool // don't generate profiles 43 ProfileDir string // directory to find profiles in 44 45 BootJars android.ConfiguredJarList // modules for jars that form the boot class path 46 ApexBootJars android.ConfiguredJarList // jars within apex that form the boot class path 47 48 ArtApexJars android.ConfiguredJarList // modules for jars that are in the ART APEX 49 TestOnlyArtBootImageJars android.ConfiguredJarList // modules for jars to be included in the ART boot image for testing 50 51 SystemServerJars android.ConfiguredJarList // system_server classpath jars on the platform 52 SystemServerApps []string // apps that are loaded into system server 53 ApexSystemServerJars android.ConfiguredJarList // system_server classpath jars delivered via apex 54 StandaloneSystemServerJars android.ConfiguredJarList // jars on the platform that system_server loads dynamically using separate classloaders 55 ApexStandaloneSystemServerJars android.ConfiguredJarList // jars delivered via apex that system_server loads dynamically using separate classloaders 56 SpeedApps []string // apps that should be speed optimized 57 58 BrokenSuboptimalOrderOfSystemServerJars bool // if true, sub-optimal order does not cause a build error 59 60 PreoptFlags []string // global dex2oat flags that should be used if no module-specific dex2oat flags are specified 61 62 DefaultCompilerFilter string // default compiler filter to pass to dex2oat, overridden by --compiler-filter= in module-specific dex2oat flags 63 SystemServerCompilerFilter string // default compiler filter to pass to dex2oat for system server jars 64 65 GenerateDMFiles bool // generate Dex Metadata files 66 67 NoDebugInfo bool // don't generate debug info by default 68 DontResolveStartupStrings bool // don't resolve string literals loaded during application startup. 69 AlwaysSystemServerDebugInfo bool // always generate mini debug info for system server modules (overrides NoDebugInfo=true) 70 NeverSystemServerDebugInfo bool // never generate mini debug info for system server modules (overrides NoDebugInfo=false) 71 AlwaysOtherDebugInfo bool // always generate mini debug info for non-system server modules (overrides NoDebugInfo=true) 72 NeverOtherDebugInfo bool // never generate mini debug info for non-system server modules (overrides NoDebugInfo=true) 73 74 IsEng bool // build is a eng variant 75 SanitizeLite bool // build is the second phase of a SANITIZE_LITE build 76 77 DefaultAppImages bool // build app images (TODO: .art files?) by default 78 79 Dex2oatXmx string // max heap size for dex2oat 80 Dex2oatXms string // initial heap size for dex2oat 81 82 EmptyDirectory string // path to an empty directory 83 84 CpuVariant map[android.ArchType]string // cpu variant for each architecture 85 InstructionSetFeatures map[android.ArchType]string // instruction set for each architecture 86 87 BootImageProfiles android.Paths // path to a boot-image-profile.txt file 88 BootFlags string // extra flags to pass to dex2oat for the boot image 89 Dex2oatImageXmx string // max heap size for dex2oat for the boot image 90 Dex2oatImageXms string // initial heap size for dex2oat for the boot image 91 92 // If true, downgrade the compiler filter of dexpreopt to "verify" when verify_uses_libraries 93 // check fails, instead of failing the build. This will disable any AOT-compilation. 94 // 95 // The intended use case for this flag is to have a smoother migration path for the Java 96 // modules that need to add <uses-library> information in their build files. The flag allows to 97 // quickly silence build errors. This flag should be used with caution and only as a temporary 98 // measure, as it masks real errors and affects performance. 99 RelaxUsesLibraryCheck bool 100 101 // "true" to force preopt with CMC GC (a.k.a., UFFD GC); "false" to force preopt with CC GC; 102 // "default" to determine the GC type based on the kernel version file. 103 EnableUffdGc string 104} 105 106var allPlatformSystemServerJarsKey = android.NewOnceKey("allPlatformSystemServerJars") 107 108// Returns all jars on the platform that system_server loads, including those on classpath and those 109// loaded dynamically. 110func (g *GlobalConfig) AllPlatformSystemServerJars(ctx android.PathContext) *android.ConfiguredJarList { 111 return ctx.Config().Once(allPlatformSystemServerJarsKey, func() interface{} { 112 res := g.SystemServerJars.AppendList(&g.StandaloneSystemServerJars) 113 return &res 114 }).(*android.ConfiguredJarList) 115} 116 117var allApexSystemServerJarsKey = android.NewOnceKey("allApexSystemServerJars") 118 119// Returns all jars delivered via apex that system_server loads, including those on classpath and 120// those loaded dynamically. 121func (g *GlobalConfig) AllApexSystemServerJars(ctx android.PathContext) *android.ConfiguredJarList { 122 return ctx.Config().Once(allApexSystemServerJarsKey, func() interface{} { 123 res := g.ApexSystemServerJars.AppendList(&g.ApexStandaloneSystemServerJars) 124 return &res 125 }).(*android.ConfiguredJarList) 126} 127 128var allSystemServerClasspathJarsKey = android.NewOnceKey("allSystemServerClasspathJars") 129 130// Returns all system_server classpath jars. 131func (g *GlobalConfig) AllSystemServerClasspathJars(ctx android.PathContext) *android.ConfiguredJarList { 132 return ctx.Config().Once(allSystemServerClasspathJarsKey, func() interface{} { 133 res := g.SystemServerJars.AppendList(&g.ApexSystemServerJars) 134 return &res 135 }).(*android.ConfiguredJarList) 136} 137 138var allSystemServerJarsKey = android.NewOnceKey("allSystemServerJars") 139 140// Returns all jars that system_server loads. 141func (g *GlobalConfig) AllSystemServerJars(ctx android.PathContext) *android.ConfiguredJarList { 142 return ctx.Config().Once(allSystemServerJarsKey, func() interface{} { 143 res := g.AllPlatformSystemServerJars(ctx).AppendList(g.AllApexSystemServerJars(ctx)) 144 return &res 145 }).(*android.ConfiguredJarList) 146} 147 148// GlobalSoongConfig contains the global config that is generated from Soong, 149// stored in dexpreopt_soong.config. 150type GlobalSoongConfig struct { 151 // Paths to tools possibly used by the generated commands. 152 Profman android.Path 153 Dex2oat android.Path 154 Aapt android.Path 155 SoongZip android.Path 156 Zip2zip android.Path 157 ManifestCheck android.Path 158 ConstructContext android.Path 159 UffdGcFlag android.WritablePath 160} 161 162type ModuleConfig struct { 163 Name string 164 DexLocation string // dex location on device 165 BuildPath android.OutputPath 166 DexPath android.Path 167 ManifestPath android.OptionalPath 168 UncompressedDex bool 169 HasApkLibraries bool 170 PreoptFlags []string 171 172 ProfileClassListing android.OptionalPath 173 ProfileIsTextListing bool 174 ProfileBootListing android.OptionalPath 175 176 EnforceUsesLibraries bool // turn on build-time verify_uses_libraries check 177 EnforceUsesLibrariesStatusFile android.Path // a file with verify_uses_libraries errors (if any) 178 ProvidesUsesLibrary string // library name (usually the same as module name) 179 ClassLoaderContexts ClassLoaderContextMap 180 181 Archs []android.ArchType 182 DexPreoptImagesDeps []android.OutputPaths 183 184 DexPreoptImageLocationsOnHost []string // boot image location on host (file path without the arch subdirectory) 185 DexPreoptImageLocationsOnDevice []string // boot image location on device (file path without the arch subdirectory) 186 187 PreoptBootClassPathDexFiles android.Paths // file paths of boot class path files 188 PreoptBootClassPathDexLocations []string // virtual locations of boot class path files 189 190 NoCreateAppImage bool 191 ForceCreateAppImage bool 192 193 PresignedPrebuilt bool 194 195 // ApexPartition is the partition in which the dexpreopt files of apex system server jars (if any) are installed. 196 // This is a noop unless the module is apex system server jar. 197 ApexPartition string 198} 199 200type globalSoongConfigSingleton struct{} 201 202var pctx = android.NewPackageContext("android/soong/dexpreopt") 203 204func init() { 205 pctx.Import("android/soong/android") 206 android.RegisterParallelSingletonType("dexpreopt-soong-config", func() android.Singleton { 207 return &globalSoongConfigSingleton{} 208 }) 209} 210 211func constructPath(ctx android.PathContext, path string) android.Path { 212 buildDirPrefix := ctx.Config().SoongOutDir() + "/" 213 if path == "" { 214 return nil 215 } else if strings.HasPrefix(path, buildDirPrefix) { 216 return android.PathForOutput(ctx, strings.TrimPrefix(path, buildDirPrefix)) 217 } else { 218 return android.PathForSource(ctx, path) 219 } 220} 221 222func constructPaths(ctx android.PathContext, paths []string) android.Paths { 223 var ret android.Paths 224 for _, path := range paths { 225 ret = append(ret, constructPath(ctx, path)) 226 } 227 return ret 228} 229 230func constructWritablePath(ctx android.PathContext, path string) android.WritablePath { 231 if path == "" { 232 return nil 233 } 234 return constructPath(ctx, path).(android.WritablePath) 235} 236 237// ParseGlobalConfig parses the given data assumed to be read from the global 238// dexpreopt.config file into a GlobalConfig struct. 239func ParseGlobalConfig(ctx android.PathContext, data []byte) (*GlobalConfig, error) { 240 type GlobalJSONConfig struct { 241 *GlobalConfig 242 243 // Copies of entries in GlobalConfig that are not constructable without extra parameters. They will be 244 // used to construct the real value manually below. 245 BootImageProfiles []string 246 } 247 248 config := GlobalJSONConfig{} 249 err := json.Unmarshal(data, &config) 250 if err != nil { 251 return config.GlobalConfig, err 252 } 253 254 // Construct paths that require a PathContext. 255 config.GlobalConfig.BootImageProfiles = constructPaths(ctx, config.BootImageProfiles) 256 257 return config.GlobalConfig, nil 258} 259 260type globalConfigAndRaw struct { 261 global *GlobalConfig 262 data []byte 263 pathErrors []error 264} 265 266// GetGlobalConfig returns the global dexpreopt.config that's created in the 267// make config phase. It is loaded once the first time it is called for any 268// ctx.Config(), and returns the same data for all future calls with the same 269// ctx.Config(). A value can be inserted for tests using 270// setDexpreoptTestGlobalConfig. 271func GetGlobalConfig(ctx android.PathContext) *GlobalConfig { 272 return getGlobalConfigRaw(ctx).global 273} 274 275// GetGlobalConfigRawData is the same as GetGlobalConfig, except that it returns 276// the literal content of dexpreopt.config. 277func GetGlobalConfigRawData(ctx android.PathContext) []byte { 278 return getGlobalConfigRaw(ctx).data 279} 280 281var globalConfigOnceKey = android.NewOnceKey("DexpreoptGlobalConfig") 282var testGlobalConfigOnceKey = android.NewOnceKey("TestDexpreoptGlobalConfig") 283 284type pathContextErrorCollector struct { 285 android.PathContext 286 errors []error 287} 288 289func (p *pathContextErrorCollector) Errorf(format string, args ...interface{}) { 290 p.errors = append(p.errors, fmt.Errorf(format, args...)) 291} 292 293func getGlobalConfigRaw(ctx android.PathContext) globalConfigAndRaw { 294 config := ctx.Config().Once(globalConfigOnceKey, func() interface{} { 295 if data, err := ctx.Config().DexpreoptGlobalConfig(ctx); err != nil { 296 panic(err) 297 } else if data != nil { 298 pathErrorCollectorCtx := &pathContextErrorCollector{PathContext: ctx} 299 globalConfig, err := ParseGlobalConfig(pathErrorCollectorCtx, data) 300 if err != nil { 301 panic(err) 302 } 303 return globalConfigAndRaw{globalConfig, data, pathErrorCollectorCtx.errors} 304 } 305 306 // No global config filename set, see if there is a test config set 307 return ctx.Config().Once(testGlobalConfigOnceKey, func() interface{} { 308 // Nope, return a config with preopting disabled 309 return globalConfigAndRaw{&GlobalConfig{ 310 DisablePreopt: true, 311 DisablePreoptBootImages: true, 312 DisableGenerateProfile: true, 313 }, nil, nil} 314 }) 315 }).(globalConfigAndRaw) 316 317 // Avoid non-deterministic errors by reporting cached path errors on all callers. 318 for _, err := range config.pathErrors { 319 if ctx.Config().AllowMissingDependencies() { 320 // When AllowMissingDependencies it set, report errors through AddMissingDependencies. 321 // If AddMissingDependencies doesn't exist on the current context (for example when 322 // called with a SingletonContext), just swallow the errors since there is no way to 323 // report them. 324 if missingDepsCtx, ok := ctx.(interface { 325 AddMissingDependencies(missingDeps []string) 326 }); ok { 327 missingDepsCtx.AddMissingDependencies([]string{err.Error()}) 328 } 329 } else { 330 android.ReportPathErrorf(ctx, "%s", err) 331 } 332 } 333 334 return config 335} 336 337// SetTestGlobalConfig sets a GlobalConfig that future calls to GetGlobalConfig 338// will return. It must be called before the first call to GetGlobalConfig for 339// the config. 340func SetTestGlobalConfig(config android.Config, globalConfig *GlobalConfig) { 341 config.Once(testGlobalConfigOnceKey, func() interface{} { return globalConfigAndRaw{globalConfig, nil, nil} }) 342} 343 344// This struct is required to convert ModuleConfig from/to JSON. 345// The types of fields in ModuleConfig are not convertible, 346// so moduleJSONConfig has those fields as a convertible type. 347type moduleJSONConfig struct { 348 *ModuleConfig 349 350 BuildPath string 351 DexPath string 352 ManifestPath string 353 354 ProfileClassListing string 355 ProfileBootListing string 356 357 EnforceUsesLibrariesStatusFile string 358 ClassLoaderContexts jsonClassLoaderContextMap 359 360 DexPreoptImagesDeps [][]string 361 362 PreoptBootClassPathDexFiles []string 363} 364 365// ParseModuleConfig parses a per-module dexpreopt.config file into a 366// ModuleConfig struct. It is not used in Soong, which receives a ModuleConfig 367// struct directly from java/dexpreopt.go. It is used in dexpreopt_gen called 368// from Make to read the module dexpreopt.config written in the Make config 369// stage. 370func ParseModuleConfig(ctx android.PathContext, data []byte) (*ModuleConfig, error) { 371 config := moduleJSONConfig{} 372 373 err := json.Unmarshal(data, &config) 374 if err != nil { 375 return config.ModuleConfig, err 376 } 377 378 // Construct paths that require a PathContext. 379 config.ModuleConfig.BuildPath = constructPath(ctx, config.BuildPath).(android.OutputPath) 380 config.ModuleConfig.DexPath = constructPath(ctx, config.DexPath) 381 config.ModuleConfig.ManifestPath = android.OptionalPathForPath(constructPath(ctx, config.ManifestPath)) 382 config.ModuleConfig.ProfileClassListing = android.OptionalPathForPath(constructPath(ctx, config.ProfileClassListing)) 383 config.ModuleConfig.EnforceUsesLibrariesStatusFile = constructPath(ctx, config.EnforceUsesLibrariesStatusFile) 384 config.ModuleConfig.ClassLoaderContexts = fromJsonClassLoaderContext(ctx, config.ClassLoaderContexts) 385 config.ModuleConfig.PreoptBootClassPathDexFiles = constructPaths(ctx, config.PreoptBootClassPathDexFiles) 386 387 // This needs to exist, but dependencies are already handled in Make, so we don't need to pass them through JSON. 388 config.ModuleConfig.DexPreoptImagesDeps = make([]android.OutputPaths, len(config.ModuleConfig.Archs)) 389 390 return config.ModuleConfig, nil 391} 392 393func pathsListToStringLists(pathsList []android.OutputPaths) [][]string { 394 ret := make([][]string, 0, len(pathsList)) 395 for _, paths := range pathsList { 396 ret = append(ret, paths.Strings()) 397 } 398 return ret 399} 400 401func moduleConfigToJSON(config *ModuleConfig) ([]byte, error) { 402 return json.MarshalIndent(&moduleJSONConfig{ 403 BuildPath: config.BuildPath.String(), 404 DexPath: config.DexPath.String(), 405 ManifestPath: config.ManifestPath.String(), 406 ProfileClassListing: config.ProfileClassListing.String(), 407 ProfileBootListing: config.ProfileBootListing.String(), 408 EnforceUsesLibrariesStatusFile: config.EnforceUsesLibrariesStatusFile.String(), 409 ClassLoaderContexts: toJsonClassLoaderContext(config.ClassLoaderContexts), 410 DexPreoptImagesDeps: pathsListToStringLists(config.DexPreoptImagesDeps), 411 PreoptBootClassPathDexFiles: config.PreoptBootClassPathDexFiles.Strings(), 412 ModuleConfig: config, 413 }, "", " ") 414} 415 416// WriteModuleConfig serializes a ModuleConfig into a per-module dexpreopt.config JSON file. 417// These config files are used for post-processing. 418func WriteModuleConfig(ctx android.ModuleContext, config *ModuleConfig, path android.WritablePath) { 419 if path == nil { 420 return 421 } 422 423 data, err := moduleConfigToJSON(config) 424 if err != nil { 425 ctx.ModuleErrorf("failed to JSON marshal module dexpreopt.config: %v", err) 426 return 427 } 428 429 android.WriteFileRule(ctx, path, string(data)) 430} 431 432// dex2oatModuleName returns the name of the module to use for the dex2oat host 433// tool. It should be a binary module with public visibility that is compiled 434// and installed for host. 435func dex2oatModuleName(config android.Config) string { 436 // Default to the debug variant of dex2oat to help find bugs. 437 // Set USE_DEX2OAT_DEBUG to false for only building non-debug versions. 438 if config.Getenv("USE_DEX2OAT_DEBUG") == "false" { 439 return "dex2oat" 440 } else { 441 return "dex2oatd" 442 } 443} 444 445type dex2oatDependencyTag struct { 446 blueprint.BaseDependencyTag 447 android.LicenseAnnotationToolchainDependencyTag 448} 449 450func (d dex2oatDependencyTag) ExcludeFromVisibilityEnforcement() { 451} 452 453func (d dex2oatDependencyTag) ExcludeFromApexContents() { 454} 455 456func (d dex2oatDependencyTag) AllowDisabledModuleDependency(target android.Module) bool { 457 // RegisterToolDeps may run after the prebuilt mutators and hence register a 458 // dependency on the source module even when the prebuilt is to be used. 459 // dex2oatPathFromDep takes that into account when it retrieves the path to 460 // the binary, but we also need to disable the check for dependencies on 461 // disabled modules. 462 return target.IsReplacedByPrebuilt() 463} 464 465func (d dex2oatDependencyTag) AllowDisabledModuleDependencyProxy( 466 ctx android.OtherModuleProviderContext, target android.ModuleProxy) bool { 467 return android.OtherModuleProviderOrDefault( 468 ctx, target, android.CommonModuleInfoKey).ReplacedByPrebuilt 469} 470 471// Dex2oatDepTag represents the dependency onto the dex2oatd module. It is added to any module that 472// needs dexpreopting and so it makes no sense for it to be checked for visibility or included in 473// the apex. 474var Dex2oatDepTag = dex2oatDependencyTag{} 475 476var _ android.ExcludeFromVisibilityEnforcementTag = Dex2oatDepTag 477var _ android.ExcludeFromApexContentsTag = Dex2oatDepTag 478var _ android.AllowDisabledModuleDependency = Dex2oatDepTag 479 480// RegisterToolDeps adds the necessary dependencies to binary modules for tools 481// that are required later when Get(Cached)GlobalSoongConfig is called. It 482// should be called from a mutator that's registered with 483// android.RegistrationContext.FinalDepsMutators. 484func RegisterToolDeps(ctx android.BottomUpMutatorContext) { 485 dex2oatBin := dex2oatModuleName(ctx.Config()) 486 v := ctx.Config().BuildOSTarget.Variations() 487 ctx.AddFarVariationDependencies(v, Dex2oatDepTag, dex2oatBin) 488} 489 490func IsDex2oatNeeded(ctx android.PathContext) bool { 491 global := GetGlobalConfig(ctx) 492 return !global.DisablePreopt || !global.DisablePreoptBootImages 493} 494 495func dex2oatPathFromDep(ctx android.ModuleContext) android.Path { 496 if !IsDex2oatNeeded(ctx) { 497 return nil 498 } 499 500 dex2oatBin := dex2oatModuleName(ctx.Config()) 501 502 // Find the right dex2oat module, trying to follow PrebuiltDepTag from source 503 // to prebuilt if there is one. We wouldn't have to do this if the 504 // prebuilt_postdeps mutator that replaces source deps with prebuilt deps was 505 // run after RegisterToolDeps above, but changing that leads to ordering 506 // problems between mutators (RegisterToolDeps needs to run late to act on 507 // final variants, while prebuilt_postdeps needs to run before many of the 508 // PostDeps mutators, like the APEX mutators). Hence we need to dig out the 509 // prebuilt explicitly here instead. 510 var dex2oatModule android.Module 511 ctx.WalkDeps(func(child, parent android.Module) bool { 512 if parent == ctx.Module() && ctx.OtherModuleDependencyTag(child) == Dex2oatDepTag { 513 // Found the source module, or prebuilt module that has replaced the source. 514 dex2oatModule = child 515 if android.IsModulePrebuilt(child) { 516 return false // If it's the prebuilt we're done. 517 } else { 518 return true // Recurse to check if the source has a prebuilt dependency. 519 } 520 } 521 if parent == dex2oatModule && ctx.OtherModuleDependencyTag(child) == android.PrebuiltDepTag { 522 if p := android.GetEmbeddedPrebuilt(child); p != nil && p.UsePrebuilt() { 523 dex2oatModule = child // Found a prebuilt that should be used. 524 } 525 } 526 return false 527 }) 528 529 if dex2oatModule == nil { 530 // If this happens there's probably a missing call to AddToolDeps in DepsMutator. 531 panic(fmt.Sprintf("Failed to lookup %s dependency", dex2oatBin)) 532 } 533 534 dex2oatPath := dex2oatModule.(android.HostToolProvider).HostToolPath() 535 if !dex2oatPath.Valid() { 536 panic(fmt.Sprintf("Failed to find host tool path in %s", dex2oatModule)) 537 } 538 539 return dex2oatPath.Path() 540} 541 542// createGlobalSoongConfig creates a GlobalSoongConfig from the current context. 543// Should not be used in dexpreopt_gen. 544func createGlobalSoongConfig(ctx android.ModuleContext) *GlobalSoongConfig { 545 return &GlobalSoongConfig{ 546 Profman: ctx.Config().HostToolPath(ctx, "profman"), 547 Dex2oat: dex2oatPathFromDep(ctx), 548 Aapt: ctx.Config().HostToolPath(ctx, "aapt2"), 549 SoongZip: ctx.Config().HostToolPath(ctx, "soong_zip"), 550 Zip2zip: ctx.Config().HostToolPath(ctx, "zip2zip"), 551 ManifestCheck: ctx.Config().HostToolPath(ctx, "manifest_check"), 552 ConstructContext: ctx.Config().HostToolPath(ctx, "construct_context"), 553 UffdGcFlag: getUffdGcFlagPath(ctx), 554 } 555} 556 557// The main reason for this Once cache for GlobalSoongConfig is to make the 558// dex2oat path available to singletons. In ordinary modules we get it through a 559// Dex2oatDepTag dependency, but in singletons there's no simple way to do the 560// same thing and ensure the right variant is selected, hence this cache to make 561// the resolved path available to singletons. This means we depend on there 562// being at least one ordinary module with a Dex2oatDepTag dependency. 563// 564// TODO(b/147613152): Implement a way to deal with dependencies from singletons, 565// and then possibly remove this cache altogether. 566var globalSoongConfigOnceKey = android.NewOnceKey("DexpreoptGlobalSoongConfig") 567 568// GetGlobalSoongConfig creates a GlobalSoongConfig the first time it's called, 569// and later returns the same cached instance. 570func GetGlobalSoongConfig(ctx android.ModuleContext) *GlobalSoongConfig { 571 globalSoong := ctx.Config().Once(globalSoongConfigOnceKey, func() interface{} { 572 return createGlobalSoongConfig(ctx) 573 }).(*GlobalSoongConfig) 574 575 // Always resolve the tool path from the dependency, to ensure that every 576 // module has the dependency added properly. 577 myDex2oat := dex2oatPathFromDep(ctx) 578 if myDex2oat != globalSoong.Dex2oat { 579 panic(fmt.Sprintf("Inconsistent dex2oat path in cached config: expected %s, got %s", globalSoong.Dex2oat, myDex2oat)) 580 } 581 582 return globalSoong 583} 584 585// GetCachedGlobalSoongConfig returns a cached GlobalSoongConfig created by an 586// earlier GetGlobalSoongConfig call. This function works with any context 587// compatible with a basic PathContext, since it doesn't try to create a 588// GlobalSoongConfig with the proper paths (which requires a full 589// ModuleContext). If there has been no prior call to GetGlobalSoongConfig, nil 590// is returned. 591func GetCachedGlobalSoongConfig(ctx android.PathContext) *GlobalSoongConfig { 592 return ctx.Config().Once(globalSoongConfigOnceKey, func() interface{} { 593 return (*GlobalSoongConfig)(nil) 594 }).(*GlobalSoongConfig) 595} 596 597type globalJsonSoongConfig struct { 598 Profman string 599 Dex2oat string 600 Aapt string 601 SoongZip string 602 Zip2zip string 603 ManifestCheck string 604 ConstructContext string 605 UffdGcFlag string 606} 607 608// ParseGlobalSoongConfig parses the given data assumed to be read from the 609// global dexpreopt_soong.config file into a GlobalSoongConfig struct. It is 610// only used in dexpreopt_gen. 611func ParseGlobalSoongConfig(ctx android.PathContext, data []byte) (*GlobalSoongConfig, error) { 612 var jc globalJsonSoongConfig 613 614 err := json.Unmarshal(data, &jc) 615 if err != nil { 616 return &GlobalSoongConfig{}, err 617 } 618 619 config := &GlobalSoongConfig{ 620 Profman: constructPath(ctx, jc.Profman), 621 Dex2oat: constructPath(ctx, jc.Dex2oat), 622 Aapt: constructPath(ctx, jc.Aapt), 623 SoongZip: constructPath(ctx, jc.SoongZip), 624 Zip2zip: constructPath(ctx, jc.Zip2zip), 625 ManifestCheck: constructPath(ctx, jc.ManifestCheck), 626 ConstructContext: constructPath(ctx, jc.ConstructContext), 627 UffdGcFlag: constructWritablePath(ctx, jc.UffdGcFlag), 628 } 629 630 return config, nil 631} 632 633// checkBootJarsConfigConsistency checks the consistency of BootJars and ApexBootJars fields in 634// DexpreoptGlobalConfig and Config.productVariables. 635func checkBootJarsConfigConsistency(ctx android.SingletonContext, dexpreoptConfig *GlobalConfig, config android.Config) { 636 compareBootJars := func(property string, dexpreoptJars, variableJars android.ConfiguredJarList) { 637 dexpreoptPairs := dexpreoptJars.CopyOfApexJarPairs() 638 variablePairs := variableJars.CopyOfApexJarPairs() 639 if !reflect.DeepEqual(dexpreoptPairs, variablePairs) { 640 ctx.Errorf("Inconsistent configuration of %[1]s\n"+ 641 " dexpreopt.GlobalConfig.%[1]s = %[2]s\n"+ 642 " productVariables.%[1]s = %[3]s", 643 property, dexpreoptPairs, variablePairs) 644 } 645 } 646 647 compareBootJars("BootJars", dexpreoptConfig.BootJars, config.NonApexBootJars()) 648 compareBootJars("ApexBootJars", dexpreoptConfig.ApexBootJars, config.ApexBootJars()) 649} 650 651func (s *globalSoongConfigSingleton) GenerateBuildActions(ctx android.SingletonContext) { 652 global := GetGlobalConfig(ctx) 653 checkBootJarsConfigConsistency(ctx, global, ctx.Config()) 654 655 if global.DisablePreopt { 656 return 657 } 658 659 buildUffdGcFlag(ctx, global) 660 661 config := GetCachedGlobalSoongConfig(ctx) 662 if config == nil { 663 // No module has enabled dexpreopting, so we assume there will be no calls 664 // to dexpreopt_gen. 665 return 666 } 667 668 jc := globalJsonSoongConfig{ 669 Profman: config.Profman.String(), 670 Dex2oat: config.Dex2oat.String(), 671 Aapt: config.Aapt.String(), 672 SoongZip: config.SoongZip.String(), 673 Zip2zip: config.Zip2zip.String(), 674 ManifestCheck: config.ManifestCheck.String(), 675 ConstructContext: config.ConstructContext.String(), 676 UffdGcFlag: config.UffdGcFlag.String(), 677 } 678 679 data, err := json.Marshal(jc) 680 if err != nil { 681 ctx.Errorf("failed to JSON marshal GlobalSoongConfig: %v", err) 682 return 683 } 684 685 android.WriteFileRule(ctx, android.PathForOutput(ctx, "dexpreopt_soong.config"), string(data)) 686} 687 688func (s *globalSoongConfigSingleton) MakeVars(ctx android.MakeVarsContext) { 689 if GetGlobalConfig(ctx).DisablePreopt { 690 return 691 } 692 693 config := GetCachedGlobalSoongConfig(ctx) 694 if config == nil { 695 return 696 } 697 698 ctx.Strict("DEX2OAT", config.Dex2oat.String()) 699 ctx.Strict("DEXPREOPT_GEN_DEPS", strings.Join([]string{ 700 config.Profman.String(), 701 config.Dex2oat.String(), 702 config.Aapt.String(), 703 config.SoongZip.String(), 704 config.Zip2zip.String(), 705 config.ManifestCheck.String(), 706 config.ConstructContext.String(), 707 config.UffdGcFlag.String(), 708 }, " ")) 709} 710 711func buildUffdGcFlag(ctx android.BuilderContext, global *GlobalConfig) { 712 uffdGcFlag := getUffdGcFlagPath(ctx) 713 714 if global.EnableUffdGc == "true" { 715 android.WriteFileRuleVerbatim(ctx, uffdGcFlag, "--runtime-arg -Xgc:CMC") 716 } else if global.EnableUffdGc == "false" { 717 android.WriteFileRuleVerbatim(ctx, uffdGcFlag, "") 718 } else if global.EnableUffdGc == "default" { 719 // Generated by `build/make/core/Makefile`. 720 kernelVersionFile := android.PathForOutput(ctx, "dexpreopt/kernel_version_for_uffd_gc.txt") 721 // Determine the UFFD GC flag by the kernel version file. 722 rule := android.NewRuleBuilder(pctx, ctx) 723 rule.Command(). 724 Tool(ctx.Config().HostToolPath(ctx, "construct_uffd_gc_flag")). 725 Input(kernelVersionFile). 726 Output(uffdGcFlag) 727 rule.Restat().Build("dexpreopt_uffd_gc_flag", "dexpreopt_uffd_gc_flag") 728 } else { 729 panic(fmt.Sprintf("Unknown value of PRODUCT_ENABLE_UFFD_GC: %s", global.EnableUffdGc)) 730 } 731} 732 733func GlobalConfigForTests(ctx android.PathContext) *GlobalConfig { 734 return &GlobalConfig{ 735 DisablePreopt: false, 736 DisablePreoptModules: nil, 737 OnlyPreoptArtBootImage: false, 738 HasSystemOther: false, 739 PatternsOnSystemOther: nil, 740 DisableGenerateProfile: false, 741 ProfileDir: "", 742 BootJars: android.EmptyConfiguredJarList(), 743 ApexBootJars: android.EmptyConfiguredJarList(), 744 ArtApexJars: android.EmptyConfiguredJarList(), 745 TestOnlyArtBootImageJars: android.EmptyConfiguredJarList(), 746 SystemServerJars: android.EmptyConfiguredJarList(), 747 SystemServerApps: nil, 748 ApexSystemServerJars: android.EmptyConfiguredJarList(), 749 StandaloneSystemServerJars: android.EmptyConfiguredJarList(), 750 ApexStandaloneSystemServerJars: android.EmptyConfiguredJarList(), 751 SpeedApps: nil, 752 PreoptFlags: nil, 753 DefaultCompilerFilter: "", 754 SystemServerCompilerFilter: "", 755 GenerateDMFiles: false, 756 NoDebugInfo: false, 757 DontResolveStartupStrings: false, 758 AlwaysSystemServerDebugInfo: false, 759 NeverSystemServerDebugInfo: false, 760 AlwaysOtherDebugInfo: false, 761 NeverOtherDebugInfo: false, 762 IsEng: false, 763 SanitizeLite: false, 764 DefaultAppImages: false, 765 Dex2oatXmx: "", 766 Dex2oatXms: "", 767 EmptyDirectory: "empty_dir", 768 CpuVariant: nil, 769 InstructionSetFeatures: nil, 770 BootImageProfiles: nil, 771 BootFlags: "", 772 Dex2oatImageXmx: "", 773 Dex2oatImageXms: "", 774 } 775} 776 777func globalSoongConfigForTests(ctx android.BuilderContext) *GlobalSoongConfig { 778 return &GlobalSoongConfig{ 779 Profman: android.PathForTesting("profman"), 780 Dex2oat: android.PathForTesting("dex2oat"), 781 Aapt: android.PathForTesting("aapt2"), 782 SoongZip: android.PathForTesting("soong_zip"), 783 Zip2zip: android.PathForTesting("zip2zip"), 784 ManifestCheck: android.PathForTesting("manifest_check"), 785 ConstructContext: android.PathForTesting("construct_context"), 786 UffdGcFlag: android.PathForOutput(ctx, "dexpreopt_test", "uffd_gc_flag.txt"), 787 } 788} 789 790func GetDexpreoptDirName(ctx android.PathContext) string { 791 prefix := "dexpreopt_" 792 targets := ctx.Config().Targets[android.Android] 793 if len(targets) > 0 { 794 return prefix + targets[0].Arch.ArchType.String() 795 } 796 return prefix + "unknown_target" 797} 798 799func getUffdGcFlagPath(ctx android.PathContext) android.WritablePath { 800 return android.PathForOutput(ctx, "dexpreopt/uffd_gc_flag.txt") 801} 802