• 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	// 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