blob: 0adaf991702dd25008999a84010b16599ea5ff8c [file] [log] [blame]
Ulya Trafimovichf5d91bb2022-05-04 12:00:02 +01001// 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 dexFiles, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp)
263
264 targets := ctx.MultiTargets()
265 if len(targets) == 0 {
266 // assume this is a java library, dexpreopt for all arches for now
267 for _, target := range ctx.Config().Targets[android.Android] {
268 if target.NativeBridge == android.NativeBridgeDisabled {
269 targets = append(targets, target)
270 }
271 }
272 if isSystemServerJar && !d.isSDKLibrary {
273 // If the module is not an SDK library and it's a system server jar, only preopt the primary arch.
274 targets = targets[:1]
275 }
276 }
277
278 var archs []android.ArchType
279 var images android.Paths
280 var imagesDeps []android.OutputPaths
281 for _, target := range targets {
282 archs = append(archs, target.Arch.ArchType)
283 variant := bootImage.getVariant(target)
284 images = append(images, variant.imagePathOnHost)
285 imagesDeps = append(imagesDeps, variant.imagesDeps)
286 }
287 // The image locations for all Android variants are identical.
288 hostImageLocations, deviceImageLocations := bootImage.getAnyAndroidVariant().imageLocations()
289
290 var profileClassListing android.OptionalPath
291 var profileBootListing android.OptionalPath
292 profileIsTextListing := false
293 if BoolDefault(d.dexpreoptProperties.Dex_preopt.Profile_guided, true) {
294 // If dex_preopt.profile_guided is not set, default it based on the existence of the
295 // dexprepot.profile option or the profile class listing.
296 if String(d.dexpreoptProperties.Dex_preopt.Profile) != "" {
297 profileClassListing = android.OptionalPathForPath(
298 android.PathForModuleSrc(ctx, String(d.dexpreoptProperties.Dex_preopt.Profile)))
299 profileBootListing = android.ExistentPathForSource(ctx,
300 ctx.ModuleDir(), String(d.dexpreoptProperties.Dex_preopt.Profile)+"-boot")
301 profileIsTextListing = true
302 } else if global.ProfileDir != "" {
303 profileClassListing = android.ExistentPathForSource(ctx,
304 global.ProfileDir, moduleName(ctx)+".prof")
305 }
306 }
307
308 // Full dexpreopt config, used to create dexpreopt build rules.
309 dexpreoptConfig := &dexpreopt.ModuleConfig{
310 Name: moduleName(ctx),
311 DexLocation: dexLocation,
312 BuildPath: android.PathForModuleOut(ctx, "dexpreopt", moduleName(ctx)+".jar").OutputPath,
313 DexPath: dexJarFile,
314 ManifestPath: android.OptionalPathForPath(d.manifestFile),
315 UncompressedDex: d.uncompressedDex,
316 HasApkLibraries: false,
317 PreoptFlags: nil,
318
319 ProfileClassListing: profileClassListing,
320 ProfileIsTextListing: profileIsTextListing,
321 ProfileBootListing: profileBootListing,
322
323 EnforceUsesLibrariesStatusFile: dexpreopt.UsesLibrariesStatusFile(ctx),
324 EnforceUsesLibraries: d.enforceUsesLibs,
325 ProvidesUsesLibrary: providesUsesLib,
326 ClassLoaderContexts: d.classLoaderContexts,
327
328 Archs: archs,
329 DexPreoptImagesDeps: imagesDeps,
330 DexPreoptImageLocationsOnHost: hostImageLocations,
331 DexPreoptImageLocationsOnDevice: deviceImageLocations,
332
333 PreoptBootClassPathDexFiles: dexFiles.Paths(),
334 PreoptBootClassPathDexLocations: dexLocations,
335
336 PreoptExtractedApk: false,
337
338 NoCreateAppImage: !BoolDefault(d.dexpreoptProperties.Dex_preopt.App_image, true),
339 ForceCreateAppImage: BoolDefault(d.dexpreoptProperties.Dex_preopt.App_image, false),
340
341 PresignedPrebuilt: d.isPresignedPrebuilt,
342 }
343
344 d.configPath = android.PathForModuleOut(ctx, "dexpreopt", "dexpreopt.config")
345 dexpreopt.WriteModuleConfig(ctx, dexpreoptConfig, d.configPath)
346
347 if d.dexpreoptDisabled(ctx) {
348 return
349 }
350
351 globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
352
353 dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, globalSoong, global, dexpreoptConfig)
354 if err != nil {
355 ctx.ModuleErrorf("error generating dexpreopt rule: %s", err.Error())
356 return
357 }
358
359 dexpreoptRule.Build("dexpreopt", "dexpreopt")
360
361 isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx))
362
363 for _, install := range dexpreoptRule.Installs() {
364 // Remove the "/" prefix because the path should be relative to $ANDROID_PRODUCT_OUT.
365 installDir := strings.TrimPrefix(filepath.Dir(install.To), "/")
366 installBase := filepath.Base(install.To)
367 arch := filepath.Base(installDir)
368 installPath := android.PathForModuleInPartitionInstall(ctx, "", installDir)
369
370 if isApexSystemServerJar {
371 // APEX variants of java libraries are hidden from Make, so their dexpreopt
372 // outputs need special handling. Currently, for APEX variants of java
373 // libraries, only those in the system server classpath are handled here.
374 // Preopting of boot classpath jars in the ART APEX are handled in
375 // java/dexpreopt_bootjars.go, and other APEX jars are not preopted.
376 // The installs will be handled by Make as sub-modules of the java library.
377 d.builtInstalledForApex = append(d.builtInstalledForApex, dexpreopterInstall{
378 name: arch + "-" + installBase,
379 moduleName: moduleName(ctx),
380 outputPathOnHost: install.From,
381 installDirOnDevice: installPath,
382 installFileOnDevice: installBase,
383 })
384 } else if !d.preventInstall {
385 ctx.InstallFile(installPath, installBase, install.From)
386 }
387 }
388
389 if !isApexSystemServerJar {
390 d.builtInstalled = dexpreoptRule.Installs().String()
391 }
392}
393
394func (d *dexpreopter) DexpreoptBuiltInstalledForApex() []dexpreopterInstall {
395 return d.builtInstalledForApex
396}
397
398func (d *dexpreopter) AndroidMkEntriesForApex() []android.AndroidMkEntries {
399 var entries []android.AndroidMkEntries
400 for _, install := range d.builtInstalledForApex {
401 entries = append(entries, install.ToMakeEntries())
402 }
403 return entries
404}