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 "strings" 20 21 "android/soong/android" 22 "android/soong/dexpreopt" 23) 24 25type DexpreopterInterface interface { 26 // True if the java module is to be dexed and installed on devices. 27 // Structs that embed dexpreopter must implement this. 28 IsInstallable() bool 29 30 // True if dexpreopt is disabled for the java module. 31 dexpreoptDisabled(ctx android.BaseModuleContext) bool 32 33 // If the java module is to be installed into an APEX, this list contains information about the 34 // dexpreopt outputs to be installed on devices. Note that these dexpreopt outputs are installed 35 // outside of the APEX. 36 DexpreoptBuiltInstalledForApex() []dexpreopterInstall 37 38 // The Make entries to install the dexpreopt outputs. Derived from 39 // `DexpreoptBuiltInstalledForApex`. 40 AndroidMkEntriesForApex() []android.AndroidMkEntries 41 42 // See `dexpreopter.outputProfilePathOnHost`. 43 OutputProfilePathOnHost() android.Path 44} 45 46type dexpreopterInstall struct { 47 // A unique name to distinguish an output from others for the same java library module. Usually in 48 // the form of `<arch>-<encoded-path>.odex/vdex/art`. 49 name string 50 51 // The name of the input java module. 52 moduleName string 53 54 // The path to the dexpreopt output on host. 55 outputPathOnHost android.Path 56 57 // The directory on the device for the output to install to. 58 installDirOnDevice android.InstallPath 59 60 // The basename (the last segment of the path) for the output to install as. 61 installFileOnDevice string 62} 63 64// The full module name of the output in the makefile. 65func (install *dexpreopterInstall) FullModuleName() string { 66 return install.moduleName + install.SubModuleName() 67} 68 69// The sub-module name of the output in the makefile (the name excluding the java module name). 70func (install *dexpreopterInstall) SubModuleName() string { 71 return "-dexpreopt-" + install.name 72} 73 74// Returns Make entries for installing the file. 75// 76// This function uses a value receiver rather than a pointer receiver to ensure that the object is 77// safe to use in `android.AndroidMkExtraEntriesFunc`. 78func (install dexpreopterInstall) ToMakeEntries() android.AndroidMkEntries { 79 return android.AndroidMkEntries{ 80 Class: "ETC", 81 SubName: install.SubModuleName(), 82 OutputFile: android.OptionalPathForPath(install.outputPathOnHost), 83 ExtraEntries: []android.AndroidMkExtraEntriesFunc{ 84 func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { 85 entries.SetString("LOCAL_MODULE_PATH", install.installDirOnDevice.String()) 86 entries.SetString("LOCAL_INSTALLED_MODULE_STEM", install.installFileOnDevice) 87 entries.SetString("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", "false") 88 }, 89 }, 90 } 91} 92 93type dexpreopter struct { 94 dexpreoptProperties DexpreoptProperties 95 importDexpreoptProperties ImportDexpreoptProperties 96 97 installPath android.InstallPath 98 uncompressedDex bool 99 isSDKLibrary bool 100 isApp bool 101 isTest bool 102 isPresignedPrebuilt bool 103 preventInstall bool 104 105 manifestFile android.Path 106 statusFile android.WritablePath 107 enforceUsesLibs bool 108 classLoaderContexts dexpreopt.ClassLoaderContextMap 109 110 // See the `dexpreopt` function for details. 111 builtInstalled string 112 builtInstalledForApex []dexpreopterInstall 113 114 // The config is used for two purposes: 115 // - Passing dexpreopt information about libraries from Soong to Make. This is needed when 116 // a <uses-library> is defined in Android.bp, but used in Android.mk (see dex_preopt_config_merger.py). 117 // Note that dexpreopt.config might be needed even if dexpreopt is disabled for the library itself. 118 // - Dexpreopt post-processing (using dexpreopt artifacts from a prebuilt system image to incrementally 119 // dexpreopt another partition). 120 configPath android.WritablePath 121 122 // The path to the profile on host that dexpreopter generates. This is used as the input for 123 // dex2oat. 124 outputProfilePathOnHost android.Path 125 126 // The path to the profile that dexpreopter accepts. It must be in the binary format. If this is 127 // set, it overrides the profile settings in `dexpreoptProperties`. 128 inputProfilePathOnHost android.Path 129} 130 131type DexpreoptProperties struct { 132 Dex_preopt struct { 133 // If false, prevent dexpreopting. Defaults to true. 134 Enabled *bool 135 136 // If true, generate an app image (.art file) for this module. 137 App_image *bool 138 139 // If true, use a checked-in profile to guide optimization. Defaults to false unless 140 // a matching profile is set or a profile is found in PRODUCT_DEX_PREOPT_PROFILE_DIR 141 // that matches the name of this module, in which case it is defaulted to true. 142 Profile_guided *bool 143 144 // If set, provides the path to profile relative to the Android.bp file. If not set, 145 // defaults to searching for a file that matches the name of this module in the default 146 // profile location set by PRODUCT_DEX_PREOPT_PROFILE_DIR, or empty if not found. 147 Profile *string `android:"path"` 148 } 149 150 Dex_preopt_result struct { 151 // True if profile-guided optimization is actually enabled. 152 Profile_guided bool 153 } `blueprint:"mutated"` 154} 155 156type ImportDexpreoptProperties struct { 157 Dex_preopt struct { 158 // If true, use the profile in the prebuilt APEX to guide optimization. Defaults to false. 159 Profile_guided *bool 160 } 161} 162 163func init() { 164 dexpreopt.DexpreoptRunningInSoong = true 165} 166 167func isApexVariant(ctx android.BaseModuleContext) bool { 168 apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) 169 return !apexInfo.IsForPlatform() 170} 171 172func forPrebuiltApex(ctx android.BaseModuleContext) bool { 173 apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) 174 return apexInfo.ForPrebuiltApex 175} 176 177func moduleName(ctx android.BaseModuleContext) string { 178 // Remove the "prebuilt_" prefix if the module is from a prebuilt because the prefix is not 179 // expected by dexpreopter. 180 return android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()) 181} 182 183// Returns whether dexpreopt is applicable to the module. 184// When it returns true, neither profile nor dexpreopt artifacts will be generated. 185func (d *dexpreopter) dexpreoptDisabled(ctx android.BaseModuleContext) bool { 186 if !ctx.Device() { 187 return true 188 } 189 190 if d.isTest { 191 return true 192 } 193 194 if !BoolDefault(d.dexpreoptProperties.Dex_preopt.Enabled, true) { 195 return true 196 } 197 198 // If the module is from a prebuilt APEX, it shouldn't be installable, but it can still be 199 // dexpreopted. 200 if !ctx.Module().(DexpreopterInterface).IsInstallable() && !forPrebuiltApex(ctx) { 201 return true 202 } 203 204 if !android.IsModulePreferred(ctx.Module()) { 205 return true 206 } 207 208 global := dexpreopt.GetGlobalConfig(ctx) 209 210 isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx)) 211 if isApexVariant(ctx) { 212 // Don't preopt APEX variant module unless the module is an APEX system server jar. 213 if !isApexSystemServerJar { 214 return true 215 } 216 } else { 217 // Don't preopt the platform variant of an APEX system server jar to avoid conflicts. 218 if isApexSystemServerJar { 219 return true 220 } 221 } 222 223 // TODO: contains no java code 224 225 return false 226} 227 228func dexpreoptToolDepsMutator(ctx android.BottomUpMutatorContext) { 229 if d, ok := ctx.Module().(DexpreopterInterface); !ok || d.dexpreoptDisabled(ctx) || !dexpreopt.IsDex2oatNeeded(ctx) { 230 return 231 } 232 dexpreopt.RegisterToolDeps(ctx) 233} 234 235func (d *dexpreopter) odexOnSystemOther(ctx android.ModuleContext, installPath android.InstallPath) bool { 236 return dexpreopt.OdexOnSystemOtherByName(moduleName(ctx), android.InstallPathToOnDevicePath(ctx, installPath), dexpreopt.GetGlobalConfig(ctx)) 237} 238 239// Returns the install path of the dex jar of a module. 240// 241// Do not rely on `ApexInfo.ApexVariationName` because it can be something like "apex1000", rather 242// than the `name` in the path `/apex/<name>` as suggested in its comment. 243// 244// This function is on a best-effort basis. It cannot handle the case where an APEX jar is not a 245// system server jar, which is fine because we currently only preopt system server jars for APEXes. 246func (d *dexpreopter) getInstallPath( 247 ctx android.ModuleContext, defaultInstallPath android.InstallPath) android.InstallPath { 248 global := dexpreopt.GetGlobalConfig(ctx) 249 if global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx)) { 250 dexLocation := dexpreopt.GetSystemServerDexLocation(ctx, global, moduleName(ctx)) 251 return android.PathForModuleInPartitionInstall(ctx, "", strings.TrimPrefix(dexLocation, "/")) 252 } 253 if !d.dexpreoptDisabled(ctx) && isApexVariant(ctx) && 254 filepath.Base(defaultInstallPath.PartitionDir()) != "apex" { 255 ctx.ModuleErrorf("unable to get the install path of the dex jar for dexpreopt") 256 } 257 return defaultInstallPath 258} 259 260func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.WritablePath) { 261 global := dexpreopt.GetGlobalConfig(ctx) 262 263 // TODO(b/148690468): The check on d.installPath is to bail out in cases where 264 // the dexpreopter struct hasn't been fully initialized before we're called, 265 // e.g. in aar.go. This keeps the behaviour that dexpreopting is effectively 266 // disabled, even if installable is true. 267 if d.installPath.Base() == "." { 268 return 269 } 270 271 dexLocation := android.InstallPathToOnDevicePath(ctx, d.installPath) 272 273 providesUsesLib := moduleName(ctx) 274 if ulib, ok := ctx.Module().(ProvidesUsesLib); ok { 275 name := ulib.ProvidesUsesLib() 276 if name != nil { 277 providesUsesLib = *name 278 } 279 } 280 281 // If it is test, make config files regardless of its dexpreopt setting. 282 // The config files are required for apps defined in make which depend on the lib. 283 if d.isTest && d.dexpreoptDisabled(ctx) { 284 return 285 } 286 287 isSystemServerJar := global.AllSystemServerJars(ctx).ContainsJar(moduleName(ctx)) 288 289 bootImage := defaultBootImageConfig(ctx) 290 // When `global.PreoptWithUpdatableBcp` is true, `bcpForDexpreopt` below includes the mainline 291 // boot jars into bootclasspath, so we should include the mainline boot image as well because it's 292 // generated from those jars. 293 if global.PreoptWithUpdatableBcp { 294 bootImage = mainlineBootImageConfig(ctx) 295 } 296 dexFiles, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp) 297 298 targets := ctx.MultiTargets() 299 if len(targets) == 0 { 300 // assume this is a java library, dexpreopt for all arches for now 301 for _, target := range ctx.Config().Targets[android.Android] { 302 if target.NativeBridge == android.NativeBridgeDisabled { 303 targets = append(targets, target) 304 } 305 } 306 if isSystemServerJar && moduleName(ctx) != "com.android.location.provider" { 307 // If the module is a system server jar, only preopt for the primary arch because the jar can 308 // only be loaded by system server. "com.android.location.provider" is a special case because 309 // it's also used by apps as a shared library. 310 targets = targets[:1] 311 } 312 } 313 314 var archs []android.ArchType 315 var images android.Paths 316 var imagesDeps []android.OutputPaths 317 for _, target := range targets { 318 archs = append(archs, target.Arch.ArchType) 319 variant := bootImage.getVariant(target) 320 images = append(images, variant.imagePathOnHost) 321 imagesDeps = append(imagesDeps, variant.imagesDeps) 322 } 323 // The image locations for all Android variants are identical. 324 hostImageLocations, deviceImageLocations := bootImage.getAnyAndroidVariant().imageLocations() 325 326 var profileClassListing android.OptionalPath 327 var profileBootListing android.OptionalPath 328 profileIsTextListing := false 329 if d.inputProfilePathOnHost != nil { 330 profileClassListing = android.OptionalPathForPath(d.inputProfilePathOnHost) 331 } else if BoolDefault(d.dexpreoptProperties.Dex_preopt.Profile_guided, true) && !forPrebuiltApex(ctx) { 332 // If dex_preopt.profile_guided is not set, default it based on the existence of the 333 // dexprepot.profile option or the profile class listing. 334 if String(d.dexpreoptProperties.Dex_preopt.Profile) != "" { 335 profileClassListing = android.OptionalPathForPath( 336 android.PathForModuleSrc(ctx, String(d.dexpreoptProperties.Dex_preopt.Profile))) 337 profileBootListing = android.ExistentPathForSource(ctx, 338 ctx.ModuleDir(), String(d.dexpreoptProperties.Dex_preopt.Profile)+"-boot") 339 profileIsTextListing = true 340 } else if global.ProfileDir != "" { 341 profileClassListing = android.ExistentPathForSource(ctx, 342 global.ProfileDir, moduleName(ctx)+".prof") 343 } 344 } 345 346 d.dexpreoptProperties.Dex_preopt_result.Profile_guided = profileClassListing.Valid() 347 348 // Full dexpreopt config, used to create dexpreopt build rules. 349 dexpreoptConfig := &dexpreopt.ModuleConfig{ 350 Name: moduleName(ctx), 351 DexLocation: dexLocation, 352 BuildPath: android.PathForModuleOut(ctx, "dexpreopt", moduleName(ctx)+".jar").OutputPath, 353 DexPath: dexJarFile, 354 ManifestPath: android.OptionalPathForPath(d.manifestFile), 355 UncompressedDex: d.uncompressedDex, 356 HasApkLibraries: false, 357 PreoptFlags: nil, 358 359 ProfileClassListing: profileClassListing, 360 ProfileIsTextListing: profileIsTextListing, 361 ProfileBootListing: profileBootListing, 362 363 EnforceUsesLibrariesStatusFile: dexpreopt.UsesLibrariesStatusFile(ctx), 364 EnforceUsesLibraries: d.enforceUsesLibs, 365 ProvidesUsesLibrary: providesUsesLib, 366 ClassLoaderContexts: d.classLoaderContexts, 367 368 Archs: archs, 369 DexPreoptImagesDeps: imagesDeps, 370 DexPreoptImageLocationsOnHost: hostImageLocations, 371 DexPreoptImageLocationsOnDevice: deviceImageLocations, 372 373 PreoptBootClassPathDexFiles: dexFiles.Paths(), 374 PreoptBootClassPathDexLocations: dexLocations, 375 376 PreoptExtractedApk: false, 377 378 NoCreateAppImage: !BoolDefault(d.dexpreoptProperties.Dex_preopt.App_image, true), 379 ForceCreateAppImage: BoolDefault(d.dexpreoptProperties.Dex_preopt.App_image, false), 380 381 PresignedPrebuilt: d.isPresignedPrebuilt, 382 } 383 384 d.configPath = android.PathForModuleOut(ctx, "dexpreopt", "dexpreopt.config") 385 dexpreopt.WriteModuleConfig(ctx, dexpreoptConfig, d.configPath) 386 387 if d.dexpreoptDisabled(ctx) { 388 return 389 } 390 391 globalSoong := dexpreopt.GetGlobalSoongConfig(ctx) 392 393 dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, globalSoong, global, dexpreoptConfig) 394 if err != nil { 395 ctx.ModuleErrorf("error generating dexpreopt rule: %s", err.Error()) 396 return 397 } 398 399 dexpreoptRule.Build("dexpreopt", "dexpreopt") 400 401 isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx)) 402 403 for _, install := range dexpreoptRule.Installs() { 404 // Remove the "/" prefix because the path should be relative to $ANDROID_PRODUCT_OUT. 405 installDir := strings.TrimPrefix(filepath.Dir(install.To), "/") 406 installBase := filepath.Base(install.To) 407 arch := filepath.Base(installDir) 408 installPath := android.PathForModuleInPartitionInstall(ctx, "", installDir) 409 isProfile := strings.HasSuffix(installBase, ".prof") 410 411 if isProfile { 412 d.outputProfilePathOnHost = install.From 413 } 414 415 if isApexSystemServerJar { 416 // Profiles are handled separately because they are installed into the APEX. 417 if !isProfile { 418 // APEX variants of java libraries are hidden from Make, so their dexpreopt 419 // outputs need special handling. Currently, for APEX variants of java 420 // libraries, only those in the system server classpath are handled here. 421 // Preopting of boot classpath jars in the ART APEX are handled in 422 // java/dexpreopt_bootjars.go, and other APEX jars are not preopted. 423 // The installs will be handled by Make as sub-modules of the java library. 424 d.builtInstalledForApex = append(d.builtInstalledForApex, dexpreopterInstall{ 425 name: arch + "-" + installBase, 426 moduleName: moduleName(ctx), 427 outputPathOnHost: install.From, 428 installDirOnDevice: installPath, 429 installFileOnDevice: installBase, 430 }) 431 } 432 } else if !d.preventInstall { 433 ctx.InstallFile(installPath, installBase, install.From) 434 } 435 } 436 437 if !isApexSystemServerJar { 438 d.builtInstalled = dexpreoptRule.Installs().String() 439 } 440} 441 442func (d *dexpreopter) DexpreoptBuiltInstalledForApex() []dexpreopterInstall { 443 return d.builtInstalledForApex 444} 445 446func (d *dexpreopter) AndroidMkEntriesForApex() []android.AndroidMkEntries { 447 var entries []android.AndroidMkEntries 448 for _, install := range d.builtInstalledForApex { 449 entries = append(entries, install.ToMakeEntries()) 450 } 451 return entries 452} 453 454func (d *dexpreopter) OutputProfilePathOnHost() android.Path { 455 return d.outputProfilePathOnHost 456} 457