dexpreopt.go 14 KB

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