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