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 java 16 17import ( 18 "path/filepath" 19 "sort" 20 "strings" 21 22 "github.com/google/blueprint/proptools" 23 24 "android/soong/android" 25 "android/soong/dexpreopt" 26) 27 28type DexpreopterInterface interface { 29 // True if the java module is to be dexed and installed on devices. 30 // Structs that embed dexpreopter must implement this. 31 IsInstallable() bool 32 33 // True if dexpreopt is disabled for the java module. 34 dexpreoptDisabled(ctx android.BaseModuleContext, libraryName string) bool 35 36 // If the java module is to be installed into an APEX, this list contains information about the 37 // dexpreopt outputs to be installed on devices. Note that these dexpreopt outputs are installed 38 // outside of the APEX. 39 DexpreoptBuiltInstalledForApex() []dexpreopterInstall 40 41 // The Make entries to install the dexpreopt outputs. Derived from 42 // `DexpreoptBuiltInstalledForApex`. 43 AndroidMkEntriesForApex() []android.AndroidMkEntries 44 45 // See `dexpreopter.outputProfilePathOnHost`. 46 OutputProfilePathOnHost() android.Path 47} 48 49type dexpreopterInstall struct { 50 // A unique name to distinguish an output from others for the same java library module. Usually in 51 // the form of `<arch>-<encoded-path>.odex/vdex/art`. 52 name string 53 54 // The name of the input java module. 55 moduleName string 56 57 // The path to the dexpreopt output on host. 58 outputPathOnHost android.Path 59 60 // The directory on the device for the output to install to. 61 installDirOnDevice android.InstallPath 62 63 // The basename (the last segment of the path) for the output to install as. 64 installFileOnDevice string 65} 66 67// The full module name of the output in the makefile. 68func (install *dexpreopterInstall) FullModuleName() string { 69 return install.moduleName + install.SubModuleName() 70} 71 72// The sub-module name of the output in the makefile (the name excluding the java module name). 73func (install *dexpreopterInstall) SubModuleName() string { 74 return "-dexpreopt-" + install.name 75} 76 77// Returns Make entries for installing the file. 78// 79// This function uses a value receiver rather than a pointer receiver to ensure that the object is 80// safe to use in `android.AndroidMkExtraEntriesFunc`. 81func (install dexpreopterInstall) ToMakeEntries() android.AndroidMkEntries { 82 return android.AndroidMkEntries{ 83 Class: "ETC", 84 OutputFile: android.OptionalPathForPath(install.outputPathOnHost), 85 ExtraEntries: []android.AndroidMkExtraEntriesFunc{ 86 func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { 87 entries.SetString("LOCAL_MODULE", install.FullModuleName()) 88 entries.SetString("LOCAL_MODULE_PATH", install.installDirOnDevice.String()) 89 entries.SetString("LOCAL_INSTALLED_MODULE_STEM", install.installFileOnDevice) 90 entries.SetString("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", "false") 91 }, 92 }, 93 } 94} 95 96type Dexpreopter struct { 97 dexpreopter 98} 99 100type dexpreopter struct { 101 dexpreoptProperties DexpreoptProperties 102 importDexpreoptProperties ImportDexpreoptProperties 103 104 // If true, the dexpreopt rules will not be generated 105 // Unlike Dex_preopt.Enabled which is user-facing, 106 // shouldDisableDexpreopt is a mutated propery. 107 shouldDisableDexpreopt bool 108 109 installPath android.InstallPath 110 uncompressedDex bool 111 isSDKLibrary bool 112 isApp bool 113 isTest bool 114 isPresignedPrebuilt bool 115 preventInstall bool 116 117 manifestFile android.Path 118 statusFile android.WritablePath 119 enforceUsesLibs bool 120 classLoaderContexts dexpreopt.ClassLoaderContextMap 121 122 // See the `dexpreopt` function for details. 123 builtInstalled string 124 builtInstalledForApex []dexpreopterInstall 125 126 // The config is used for two purposes: 127 // - Passing dexpreopt information about libraries from Soong to Make. This is needed when 128 // a <uses-library> is defined in Android.bp, but used in Android.mk (see dex_preopt_config_merger.py). 129 // Note that dexpreopt.config might be needed even if dexpreopt is disabled for the library itself. 130 // - Dexpreopt post-processing (using dexpreopt artifacts from a prebuilt system image to incrementally 131 // dexpreopt another partition). 132 configPath android.WritablePath 133 134 // The path to the profile on host that dexpreopter generates. This is used as the input for 135 // dex2oat. 136 outputProfilePathOnHost android.Path 137 138 // The path to the profile that dexpreopter accepts. It must be in the binary format. If this is 139 // set, it overrides the profile settings in `dexpreoptProperties`. 140 inputProfilePathOnHost android.Path 141 142 // The path to the profile that matches the dex optimized by r8/d8. It is in text format. If this is 143 // set, it will be converted to a binary profile which will be subsequently used for dexpreopt. 144 rewrittenProfile android.Path 145} 146 147type DexpreoptProperties struct { 148 Dex_preopt struct { 149 // If false, prevent dexpreopting. Defaults to true. 150 Enabled proptools.Configurable[bool] `android:"replace_instead_of_append"` 151 152 // If true, generate an app image (.art file) for this module. 153 App_image proptools.Configurable[bool] `android:"replace_instead_of_append"` 154 155 // If true, use a checked-in profile to guide optimization. Defaults to false unless 156 // a matching profile is set or a profile is found in PRODUCT_DEX_PREOPT_PROFILE_DIR 157 // that matches the name of this module, in which case it is defaulted to true. 158 Profile_guided proptools.Configurable[bool] `android:"replace_instead_of_append"` 159 160 // If set, provides the path to profile relative to the Android.bp file. If not set, 161 // defaults to searching for a file that matches the name of this module in the default 162 // profile location set by PRODUCT_DEX_PREOPT_PROFILE_DIR, or empty if not found. 163 Profile proptools.Configurable[string] `android:"path,replace_instead_of_append"` 164 165 // If set to true, r8/d8 will use `profile` as input to generate a new profile that matches 166 // the optimized dex. 167 // The new profile will be subsequently used as the profile to dexpreopt the dex file. 168 Enable_profile_rewriting proptools.Configurable[bool] `android:"replace_instead_of_append"` 169 } 170 171 Dex_preopt_result struct { 172 // True if profile-guided optimization is actually enabled. 173 Profile_guided bool 174 } `blueprint:"mutated"` 175} 176 177type ImportDexpreoptProperties struct { 178 Dex_preopt struct { 179 // If true, use the profile in the prebuilt APEX to guide optimization. Defaults to false. 180 Profile_guided *bool 181 } 182} 183 184func init() { 185 dexpreopt.DexpreoptRunningInSoong = true 186} 187 188func isApexVariant(ctx android.BaseModuleContext) bool { 189 apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) 190 return !apexInfo.IsForPlatform() 191} 192 193func forPrebuiltApex(ctx android.BaseModuleContext) bool { 194 apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) 195 return apexInfo.ForPrebuiltApex 196} 197 198// For apex variant of modules, this returns true on the source variant if the prebuilt apex 199// has been selected using apex_contributions. 200// The prebuilt apex will be responsible for generating the dexpreopt rules of the deapexed java lib. 201func disableSourceApexVariant(ctx android.BaseModuleContext) bool { 202 if !isApexVariant(ctx) { 203 return false // platform variant 204 } 205 apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) 206 psi := android.PrebuiltSelectionInfoMap{} 207 ctx.VisitDirectDeps(func(am android.Module) { 208 if prebuiltSelectionInfo, ok := android.OtherModuleProvider(ctx, am, android.PrebuiltSelectionInfoProvider); ok { 209 psi = prebuiltSelectionInfo 210 } 211 }) 212 213 // Find the apex variant for this module 214 apexVariantsWithoutTestApexes := []string{} 215 if apexInfo.BaseApexName != "" { 216 // This is a transitive dependency of an override_apex 217 apexVariantsWithoutTestApexes = append(apexVariantsWithoutTestApexes, apexInfo.BaseApexName) 218 } else { 219 _, variants, _ := android.ListSetDifference(apexInfo.InApexVariants, apexInfo.TestApexes) 220 apexVariantsWithoutTestApexes = append(apexVariantsWithoutTestApexes, variants...) 221 } 222 if apexInfo.ApexAvailableName != "" { 223 apexVariantsWithoutTestApexes = append(apexVariantsWithoutTestApexes, apexInfo.ApexAvailableName) 224 } 225 disableSource := false 226 // find the selected apexes 227 for _, apexVariant := range apexVariantsWithoutTestApexes { 228 if len(psi.GetSelectedModulesForApiDomain(apexVariant)) > 0 { 229 // If the apex_contribution for this api domain is non-empty, disable the source variant 230 disableSource = true 231 } 232 } 233 return disableSource 234} 235 236// Returns whether dexpreopt is applicable to the module. 237// When it returns true, neither profile nor dexpreopt artifacts will be generated. 238func (d *dexpreopter) dexpreoptDisabled(ctx android.BaseModuleContext, libName string) bool { 239 if !ctx.Device() { 240 return true 241 } 242 243 if d.isTest { 244 return true 245 } 246 247 if !d.dexpreoptProperties.Dex_preopt.Enabled.GetOrDefault(ctx, true) { 248 return true 249 } 250 251 if d.shouldDisableDexpreopt { 252 return true 253 } 254 255 // If the module is from a prebuilt APEX, it shouldn't be installable, but it can still be 256 // dexpreopted. 257 if !ctx.Module().(DexpreopterInterface).IsInstallable() && !forPrebuiltApex(ctx) { 258 return true 259 } 260 261 if !android.IsModulePreferred(ctx.Module()) { 262 return true 263 } 264 265 if _, isApex := android.ModuleProvider(ctx, android.ApexBundleInfoProvider); isApex { 266 // dexpreopt rules for system server jars can be generated in the ModuleCtx of prebuilt apexes 267 return false 268 } 269 270 global := dexpreopt.GetGlobalConfig(ctx) 271 272 // Use the libName argument to determine if the library being dexpreopt'd is a system server jar 273 // ctx.ModuleName() is not safe. In case of prebuilt apexes, the dexpreopt rules of system server jars 274 // are created in the ctx object of the top-level prebuilt apex. 275 isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(libName) 276 277 if _, isApex := android.ModuleProvider(ctx, android.ApexBundleInfoProvider); isApex || isApexVariant(ctx) { 278 // dexpreopt rules for system server jars can be generated in the ModuleCtx of prebuilt apexes 279 if !isApexSystemServerJar { 280 return true 281 } 282 ai, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) 283 allApexInfos := []android.ApexInfo{} 284 if allApexInfosProvider, ok := android.ModuleProvider(ctx, android.AllApexInfoProvider); ok { 285 allApexInfos = allApexInfosProvider.ApexInfos 286 } 287 if len(allApexInfos) > 0 && !ai.MinSdkVersion.EqualTo(allApexInfos[0].MinSdkVersion) { 288 // Apex system server jars are dexpreopted and installed on to the system image. 289 // Since we can have BigAndroid and Go variants of system server jar providing apexes, 290 // and these two variants can have different min_sdk_versions, hide one of the apex variants 291 // from make to prevent collisions. 292 // 293 // Unlike cc, min_sdk_version does not have an effect on the build actions of java libraries. 294 ctx.Module().MakeUninstallable() 295 } 296 } else { 297 // Don't preopt the platform variant of an APEX system server jar to avoid conflicts. 298 if isApexSystemServerJar { 299 return true 300 } 301 } 302 303 // TODO: contains no java code 304 305 return false 306} 307 308func dexpreoptToolDepsMutator(ctx android.BottomUpMutatorContext) { 309 if _, isApex := android.ModuleProvider(ctx, android.ApexBundleInfoProvider); isApex && dexpreopt.IsDex2oatNeeded(ctx) { 310 // prebuilt apexes can genererate rules to dexpreopt deapexed jars 311 // Add a dex2oat dep aggressively on _every_ apex module 312 dexpreopt.RegisterToolDeps(ctx) 313 return 314 } 315 if d, ok := ctx.Module().(DexpreopterInterface); !ok || d.dexpreoptDisabled(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName())) || !dexpreopt.IsDex2oatNeeded(ctx) { 316 return 317 } 318 dexpreopt.RegisterToolDeps(ctx) 319} 320 321// Returns the install path of the dex jar of a module. 322// 323// Do not rely on `ApexInfo.ApexVariationName` because it can be something like "apex1000", rather 324// than the `name` in the path `/apex/<name>` as suggested in its comment. 325// 326// This function is on a best-effort basis. It cannot handle the case where an APEX jar is not a 327// system server jar, which is fine because we currently only preopt system server jars for APEXes. 328func (d *dexpreopter) getInstallPath( 329 ctx android.ModuleContext, libName string, defaultInstallPath android.InstallPath) android.InstallPath { 330 global := dexpreopt.GetGlobalConfig(ctx) 331 if global.AllApexSystemServerJars(ctx).ContainsJar(libName) { 332 dexLocation := dexpreopt.GetSystemServerDexLocation(ctx, global, libName) 333 return android.PathForModuleInPartitionInstall(ctx, "", strings.TrimPrefix(dexLocation, "/")) 334 } 335 if !d.dexpreoptDisabled(ctx, libName) && isApexVariant(ctx) && 336 filepath.Base(defaultInstallPath.PartitionDir()) != "apex" { 337 ctx.ModuleErrorf("unable to get the install path of the dex jar for dexpreopt") 338 } 339 return defaultInstallPath 340} 341 342// DexpreoptPrebuiltApexSystemServerJars generates the dexpreopt artifacts from a jar file that has been deapexed from a prebuilt apex 343func (d *Dexpreopter) DexpreoptPrebuiltApexSystemServerJars(ctx android.ModuleContext, libraryName string, di *android.DeapexerInfo) { 344 // A single prebuilt apex can have multiple apex system jars 345 // initialize the output path for this dex jar 346 dc := dexpreopt.GetGlobalConfig(ctx) 347 d.installPath = android.PathForModuleInPartitionInstall(ctx, "", strings.TrimPrefix(dexpreopt.GetSystemServerDexLocation(ctx, dc, libraryName), "/")) 348 // generate the rules for creating the .odex and .vdex files for this system server jar 349 dexJarFile := di.PrebuiltExportPath(ApexRootRelativePathToJavaLib(libraryName)) 350 351 d.inputProfilePathOnHost = nil // reset: TODO(spandandas): Make dexpreopter stateless 352 if android.InList(libraryName, di.GetDexpreoptProfileGuidedExportedModuleNames()) { 353 // Set the profile path to guide optimization 354 prof := di.PrebuiltExportPath(ApexRootRelativePathToJavaLib(libraryName) + ".prof") 355 if prof == nil { 356 ctx.ModuleErrorf("Could not find a .prof file in this prebuilt apex") 357 } 358 d.inputProfilePathOnHost = prof 359 } 360 361 d.dexpreopt(ctx, libraryName, dexJarFile) 362} 363 364func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, libName string, dexJarFile android.Path) { 365 global := dexpreopt.GetGlobalConfig(ctx) 366 367 // TODO(b/148690468): The check on d.installPath is to bail out in cases where 368 // the dexpreopter struct hasn't been fully initialized before we're called, 369 // e.g. in aar.go. This keeps the behaviour that dexpreopting is effectively 370 // disabled, even if installable is true. 371 if d.installPath.Base() == "." { 372 return 373 } 374 375 dexLocation := android.InstallPathToOnDevicePath(ctx, d.installPath) 376 377 providesUsesLib := libName 378 if ulib, ok := ctx.Module().(ProvidesUsesLib); ok { 379 name := ulib.ProvidesUsesLib() 380 if name != nil { 381 providesUsesLib = *name 382 } 383 } 384 385 // If it is test, make config files regardless of its dexpreopt setting. 386 // The config files are required for apps defined in make which depend on the lib. 387 if d.isTest && d.dexpreoptDisabled(ctx, libName) { 388 return 389 } 390 391 isSystemServerJar := global.AllSystemServerJars(ctx).ContainsJar(libName) 392 393 bootImage := defaultBootImageConfig(ctx) 394 // When `global.PreoptWithUpdatableBcp` is true, `bcpForDexpreopt` below includes the mainline 395 // boot jars into bootclasspath, so we should include the mainline boot image as well because it's 396 // generated from those jars. 397 if global.PreoptWithUpdatableBcp { 398 bootImage = mainlineBootImageConfig(ctx) 399 } 400 dexFiles, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp) 401 402 targets := ctx.MultiTargets() 403 if len(targets) == 0 { 404 // assume this is a java library, dexpreopt for all arches for now 405 for _, target := range ctx.Config().Targets[android.Android] { 406 if target.NativeBridge == android.NativeBridgeDisabled { 407 targets = append(targets, target) 408 } 409 } 410 if isSystemServerJar && libName != "com.android.location.provider" { 411 // If the module is a system server jar, only preopt for the primary arch because the jar can 412 // only be loaded by system server. "com.android.location.provider" is a special case because 413 // it's also used by apps as a shared library. 414 targets = targets[:1] 415 } 416 } 417 418 var archs []android.ArchType 419 var images android.Paths 420 var imagesDeps []android.OutputPaths 421 for _, target := range targets { 422 archs = append(archs, target.Arch.ArchType) 423 variant := bootImage.getVariant(target) 424 images = append(images, variant.imagePathOnHost) 425 imagesDeps = append(imagesDeps, variant.imagesDeps) 426 } 427 // The image locations for all Android variants are identical. 428 hostImageLocations, deviceImageLocations := bootImage.getAnyAndroidVariant().imageLocations() 429 430 var profileClassListing android.OptionalPath 431 var profileBootListing android.OptionalPath 432 profileIsTextListing := false 433 434 if d.inputProfilePathOnHost != nil { 435 profileClassListing = android.OptionalPathForPath(d.inputProfilePathOnHost) 436 } else if d.dexpreoptProperties.Dex_preopt.Profile_guided.GetOrDefault(ctx, true) && !forPrebuiltApex(ctx) { 437 // If enable_profile_rewriting is set, use the rewritten profile instead of the checked-in profile 438 if d.EnableProfileRewriting(ctx) { 439 profileClassListing = android.OptionalPathForPath(d.GetRewrittenProfile()) 440 profileIsTextListing = true 441 } else if profile := d.GetProfile(ctx); profile != "" { 442 // If dex_preopt.profile_guided is not set, default it based on the existence of the 443 // dexprepot.profile option or the profile class listing. 444 profileClassListing = android.OptionalPathForPath( 445 android.PathForModuleSrc(ctx, profile)) 446 profileBootListing = android.ExistentPathForSource(ctx, 447 ctx.ModuleDir(), profile+"-boot") 448 profileIsTextListing = true 449 } else if global.ProfileDir != "" { 450 profileClassListing = android.ExistentPathForSource(ctx, 451 global.ProfileDir, libName+".prof") 452 } 453 } 454 455 d.dexpreoptProperties.Dex_preopt_result.Profile_guided = profileClassListing.Valid() 456 457 // A single apex can have multiple system server jars 458 // Use the dexJar to create a unique scope for each 459 dexJarStem := strings.TrimSuffix(dexJarFile.Base(), dexJarFile.Ext()) 460 461 appImage := d.dexpreoptProperties.Dex_preopt.App_image.Get(ctx) 462 463 // Full dexpreopt config, used to create dexpreopt build rules. 464 dexpreoptConfig := &dexpreopt.ModuleConfig{ 465 Name: libName, 466 DexLocation: dexLocation, 467 BuildPath: android.PathForModuleOut(ctx, "dexpreopt", dexJarStem, libName+".jar").OutputPath, 468 DexPath: dexJarFile, 469 ManifestPath: android.OptionalPathForPath(d.manifestFile), 470 UncompressedDex: d.uncompressedDex, 471 HasApkLibraries: false, 472 PreoptFlags: nil, 473 474 ProfileClassListing: profileClassListing, 475 ProfileIsTextListing: profileIsTextListing, 476 ProfileBootListing: profileBootListing, 477 478 EnforceUsesLibrariesStatusFile: dexpreopt.UsesLibrariesStatusFile(ctx), 479 EnforceUsesLibraries: d.enforceUsesLibs, 480 ProvidesUsesLibrary: providesUsesLib, 481 ClassLoaderContexts: d.classLoaderContexts, 482 483 Archs: archs, 484 DexPreoptImagesDeps: imagesDeps, 485 DexPreoptImageLocationsOnHost: hostImageLocations, 486 DexPreoptImageLocationsOnDevice: deviceImageLocations, 487 488 PreoptBootClassPathDexFiles: dexFiles.Paths(), 489 PreoptBootClassPathDexLocations: dexLocations, 490 491 NoCreateAppImage: !appImage.GetOrDefault(true), 492 ForceCreateAppImage: appImage.GetOrDefault(false), 493 494 PresignedPrebuilt: d.isPresignedPrebuilt, 495 } 496 497 if ctx.Config().InstallApexSystemServerDexpreoptSamePartition() { 498 dexpreoptConfig.ApexPartition = android.PathForModuleInstall(ctx).Partition() 499 } else { 500 dexpreoptConfig.ApexPartition = "system" 501 } 502 503 d.configPath = android.PathForModuleOut(ctx, "dexpreopt", dexJarStem, "dexpreopt.config") 504 dexpreopt.WriteModuleConfig(ctx, dexpreoptConfig, d.configPath) 505 ctx.CheckbuildFile(d.configPath) 506 507 if d.dexpreoptDisabled(ctx, libName) { 508 return 509 } 510 511 globalSoong := dexpreopt.GetGlobalSoongConfig(ctx) 512 513 // The root "product_packages.txt" is generated by `build/make/core/Makefile`. It contains a list 514 // of all packages that are installed on the device. We use `grep` to filter the list by the app's 515 // dependencies to create a per-app list, and use `rsync --checksum` to prevent the file's mtime 516 // from being changed if the contents don't change. This avoids unnecessary dexpreopt reruns. 517 productPackages := android.PathForModuleInPartitionInstall(ctx, "", "product_packages.txt") 518 appProductPackages := android.PathForModuleOut(ctx, "dexpreopt", dexJarStem, "product_packages.txt") 519 appProductPackagesStaging := appProductPackages.ReplaceExtension(ctx, "txt.tmp") 520 clcNames, _ := dexpreopt.ComputeClassLoaderContextDependencies(dexpreoptConfig.ClassLoaderContexts) 521 sort.Strings(clcNames) // The order needs to be deterministic. 522 productPackagesRule := android.NewRuleBuilder(pctx, ctx) 523 if len(clcNames) > 0 { 524 productPackagesRule.Command(). 525 Text("grep -F -x"). 526 FlagForEachArg("-e ", clcNames). 527 Input(productPackages). 528 FlagWithOutput("> ", appProductPackagesStaging). 529 Text("|| true") 530 } else { 531 productPackagesRule.Command(). 532 Text("rm -f").Output(appProductPackagesStaging). 533 Text("&&"). 534 Text("touch").Output(appProductPackagesStaging) 535 } 536 productPackagesRule.Command(). 537 Text("rsync --checksum"). 538 Input(appProductPackagesStaging). 539 Output(appProductPackages) 540 productPackagesRule.Restat().Build("product_packages."+dexJarStem, "dexpreopt product_packages") 541 542 // Prebuilts are active, do not copy the dexpreopt'd source javalib to out/soong/system_server_dexjars 543 // The javalib from the deapexed prebuilt will be copied to this location. 544 // TODO (b/331665856): Implement a principled solution for this. 545 copyApexSystemServerJarDex := !disableSourceApexVariant(ctx) && !ctx.Module().IsHideFromMake() 546 dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule( 547 ctx, globalSoong, global, dexpreoptConfig, appProductPackages, copyApexSystemServerJarDex) 548 if err != nil { 549 ctx.ModuleErrorf("error generating dexpreopt rule: %s", err.Error()) 550 return 551 } 552 553 dexpreoptRule.Build("dexpreopt"+"."+dexJarStem, "dexpreopt") 554 555 // The current ctx might be of a deapexer module created by a prebuilt apex 556 // Use the path of the dex file to determine the library name 557 isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(dexJarStem) 558 559 dexpreoptPartition := d.installPath.Partition() 560 // dexpreoptPartition is set to empty for dexpreopts of system APEX and system_other. 561 // In case of system APEX, however, we can set it to "system" manually. 562 // TODO(b/346662300): Let dexpreopter generate the installPath for dexpreopt files instead of 563 // using the dex location to generate the installPath. 564 if isApexSystemServerJar { 565 dexpreoptPartition = dexpreoptConfig.ApexPartition 566 } 567 for _, install := range dexpreoptRule.Installs() { 568 // Remove the "/" prefix because the path should be relative to $ANDROID_PRODUCT_OUT. 569 installDir := strings.TrimPrefix(filepath.Dir(install.To), "/") 570 partition := dexpreoptPartition 571 if strings.HasPrefix(installDir, partition+"/") { 572 installDir = strings.TrimPrefix(installDir, partition+"/") 573 } else { 574 // If the partition for the installDir is different from the install partition, set the 575 // partition empty to install the dexpreopt files to the desired partition. 576 // TODO(b/346439786): Define and use the dexpreopt module type to avoid this mismatch. 577 partition = "" 578 } 579 installBase := filepath.Base(install.To) 580 arch := filepath.Base(installDir) 581 installPath := android.PathForModuleInPartitionInstall(ctx, partition, installDir) 582 isProfile := strings.HasSuffix(installBase, ".prof") 583 584 if isProfile { 585 d.outputProfilePathOnHost = install.From 586 } 587 588 if isApexSystemServerJar { 589 // Profiles are handled separately because they are installed into the APEX. 590 if !isProfile { 591 // APEX variants of java libraries are hidden from Make, so their dexpreopt 592 // outputs need special handling. Currently, for APEX variants of java 593 // libraries, only those in the system server classpath are handled here. 594 // Preopting of boot classpath jars in the ART APEX are handled in 595 // java/dexpreopt_bootjars.go, and other APEX jars are not preopted. 596 // The installs will be handled by Make as sub-modules of the java library. 597 di := dexpreopterInstall{ 598 name: arch + "-" + installBase, 599 moduleName: libName, 600 outputPathOnHost: install.From, 601 installDirOnDevice: installPath, 602 installFileOnDevice: installBase, 603 } 604 ctx.InstallFile(di.installDirOnDevice, di.installFileOnDevice, di.outputPathOnHost) 605 d.builtInstalledForApex = append(d.builtInstalledForApex, di) 606 607 } 608 } else if !d.preventInstall { 609 // Install without adding to checkbuild to match behavior of previous Make-based checkbuild rules 610 ctx.InstallFileWithoutCheckbuild(installPath, installBase, install.From) 611 } 612 } 613 614 if !isApexSystemServerJar { 615 d.builtInstalled = dexpreoptRule.Installs().String() 616 } 617} 618 619func getModuleInstallPathInfo(ctx android.ModuleContext, fullInstallPath string) (android.InstallPath, string, string) { 620 installPath := android.PathForModuleInstall(ctx) 621 installDir, installBase := filepath.Split(strings.TrimPrefix(fullInstallPath, "/")) 622 623 if !strings.HasPrefix(installDir, installPath.Partition()+"/") { 624 // Return empty filename if the install partition is not for the target image. 625 return installPath, "", "" 626 } 627 relDir, err := filepath.Rel(installPath.Partition(), installDir) 628 if err != nil { 629 panic(err) 630 } 631 return installPath, relDir, installBase 632} 633 634// installFile will install the file if `install` path and the target install partition are the same. 635func installFile(ctx android.ModuleContext, install android.RuleBuilderInstall) { 636 installPath, relDir, name := getModuleInstallPathInfo(ctx, install.To) 637 // Empty name means the install partition is not for the target image. 638 // For the system image, files for "apex" and "system_other" are skipped here. 639 // The skipped "apex" files are for testing only, for example, 640 // "/apex/art_boot_images/javalib/x86/boot.vdex". 641 // TODO(b/320196894): Files for "system_other" are skipped because soong creates the system 642 // image only for now. 643 if name != "" { 644 ctx.InstallFile(installPath.Join(ctx, relDir), name, install.From) 645 } 646} 647 648func (d *dexpreopter) DexpreoptBuiltInstalledForApex() []dexpreopterInstall { 649 return d.builtInstalledForApex 650} 651 652func (d *dexpreopter) AndroidMkEntriesForApex() []android.AndroidMkEntries { 653 var entries []android.AndroidMkEntries 654 for _, install := range d.builtInstalledForApex { 655 entries = append(entries, install.ToMakeEntries()) 656 } 657 return entries 658} 659 660func (d *dexpreopter) OutputProfilePathOnHost() android.Path { 661 return d.outputProfilePathOnHost 662} 663 664func (d *dexpreopter) disableDexpreopt() { 665 d.shouldDisableDexpreopt = true 666} 667 668func (d *dexpreopter) EnableProfileRewriting(ctx android.BaseModuleContext) bool { 669 return d.dexpreoptProperties.Dex_preopt.Enable_profile_rewriting.GetOrDefault(ctx, false) 670} 671 672func (d *dexpreopter) GetProfile(ctx android.BaseModuleContext) string { 673 return d.dexpreoptProperties.Dex_preopt.Profile.GetOrDefault(ctx, "") 674} 675 676func (d *dexpreopter) GetProfileGuided(ctx android.BaseModuleContext) bool { 677 return d.dexpreoptProperties.Dex_preopt.Profile_guided.GetOrDefault(ctx, false) 678} 679 680func (d *dexpreopter) GetRewrittenProfile() android.Path { 681 return d.rewrittenProfile 682} 683 684func (d *dexpreopter) SetRewrittenProfile(p android.Path) { 685 d.rewrittenProfile = p 686} 687