• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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