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	"android/soong/android"
19	"android/soong/dexpreopt"
20)
21
22type dexpreopterInterface interface {
23	IsInstallable() bool // Structs that embed dexpreopter must implement this.
24	dexpreoptDisabled(ctx android.BaseModuleContext) bool
25}
26
27type dexpreopter struct {
28	dexpreoptProperties DexpreoptProperties
29
30	installPath         android.InstallPath
31	uncompressedDex     bool
32	isSDKLibrary        bool
33	isApp               bool
34	isTest              bool
35	isPresignedPrebuilt bool
36
37	manifestFile        android.Path
38	statusFile          android.WritablePath
39	enforceUsesLibs     bool
40	classLoaderContexts dexpreopt.ClassLoaderContextMap
41
42	builtInstalled string
43
44	// The config is used for two purposes:
45	// - Passing dexpreopt information about libraries from Soong to Make. This is needed when
46	//   a <uses-library> is defined in Android.bp, but used in Android.mk (see dex_preopt_config_merger.py).
47	//   Note that dexpreopt.config might be needed even if dexpreopt is disabled for the library itself.
48	// - Dexpreopt post-processing (using dexpreopt artifacts from a prebuilt system image to incrementally
49	//   dexpreopt another partition).
50	configPath android.WritablePath
51}
52
53type DexpreoptProperties struct {
54	Dex_preopt struct {
55		// If false, prevent dexpreopting.  Defaults to true.
56		Enabled *bool
57
58		// If true, generate an app image (.art file) for this module.
59		App_image *bool
60
61		// If true, use a checked-in profile to guide optimization.  Defaults to false unless
62		// a matching profile is set or a profile is found in PRODUCT_DEX_PREOPT_PROFILE_DIR
63		// that matches the name of this module, in which case it is defaulted to true.
64		Profile_guided *bool
65
66		// If set, provides the path to profile relative to the Android.bp file.  If not set,
67		// defaults to searching for a file that matches the name of this module in the default
68		// profile location set by PRODUCT_DEX_PREOPT_PROFILE_DIR, or empty if not found.
69		Profile *string `android:"path"`
70	}
71}
72
73func init() {
74	dexpreopt.DexpreoptRunningInSoong = true
75}
76
77func (d *dexpreopter) dexpreoptDisabled(ctx android.BaseModuleContext) bool {
78	global := dexpreopt.GetGlobalConfig(ctx)
79
80	if global.DisablePreopt {
81		return true
82	}
83
84	if inList(ctx.ModuleName(), global.DisablePreoptModules) {
85		return true
86	}
87
88	if d.isTest {
89		return true
90	}
91
92	if !BoolDefault(d.dexpreoptProperties.Dex_preopt.Enabled, true) {
93		return true
94	}
95
96	if !ctx.Module().(dexpreopterInterface).IsInstallable() {
97		return true
98	}
99
100	if ctx.Host() {
101		return true
102	}
103
104	// Don't preopt APEX variant module
105	if apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo); !apexInfo.IsForPlatform() {
106		return true
107	}
108
109	// TODO: contains no java code
110
111	return false
112}
113
114func dexpreoptToolDepsMutator(ctx android.BottomUpMutatorContext) {
115	if d, ok := ctx.Module().(dexpreopterInterface); !ok || d.dexpreoptDisabled(ctx) {
116		return
117	}
118	dexpreopt.RegisterToolDeps(ctx)
119}
120
121func odexOnSystemOther(ctx android.ModuleContext, installPath android.InstallPath) bool {
122	return dexpreopt.OdexOnSystemOtherByName(ctx.ModuleName(), android.InstallPathToOnDevicePath(ctx, installPath), dexpreopt.GetGlobalConfig(ctx))
123}
124
125func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.WritablePath) {
126	// TODO(b/148690468): The check on d.installPath is to bail out in cases where
127	// the dexpreopter struct hasn't been fully initialized before we're called,
128	// e.g. in aar.go. This keeps the behaviour that dexpreopting is effectively
129	// disabled, even if installable is true.
130	if d.installPath.Base() == "." {
131		return
132	}
133
134	dexLocation := android.InstallPathToOnDevicePath(ctx, d.installPath)
135
136	providesUsesLib := ctx.ModuleName()
137	if ulib, ok := ctx.Module().(ProvidesUsesLib); ok {
138		name := ulib.ProvidesUsesLib()
139		if name != nil {
140			providesUsesLib = *name
141		}
142	}
143
144	// If it is neither app nor test, make config files regardless of its dexpreopt setting.
145	// The config files are required for apps defined in make which depend on the lib.
146	// TODO(b/158843648): The config for apps should be generated as well regardless of setting.
147	if (d.isApp || d.isTest) && d.dexpreoptDisabled(ctx) {
148		return
149	}
150
151	global := dexpreopt.GetGlobalConfig(ctx)
152
153	isSystemServerJar := global.SystemServerJars.ContainsJar(ctx.ModuleName())
154
155	bootImage := defaultBootImageConfig(ctx)
156	if global.UseArtImage {
157		bootImage = artBootImageConfig(ctx)
158	}
159
160	// System server jars are an exception: they are dexpreopted without updatable bootclasspath.
161	dexFiles, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp && !isSystemServerJar)
162
163	targets := ctx.MultiTargets()
164	if len(targets) == 0 {
165		// assume this is a java library, dexpreopt for all arches for now
166		for _, target := range ctx.Config().Targets[android.Android] {
167			if target.NativeBridge == android.NativeBridgeDisabled {
168				targets = append(targets, target)
169			}
170		}
171		if isSystemServerJar && !d.isSDKLibrary {
172			// If the module is not an SDK library and it's a system server jar, only preopt the primary arch.
173			targets = targets[:1]
174		}
175	}
176
177	var archs []android.ArchType
178	var images android.Paths
179	var imagesDeps []android.OutputPaths
180	for _, target := range targets {
181		archs = append(archs, target.Arch.ArchType)
182		variant := bootImage.getVariant(target)
183		images = append(images, variant.imagePathOnHost)
184		imagesDeps = append(imagesDeps, variant.imagesDeps)
185	}
186	// The image locations for all Android variants are identical.
187	hostImageLocations, deviceImageLocations := bootImage.getAnyAndroidVariant().imageLocations()
188
189	var profileClassListing android.OptionalPath
190	var profileBootListing android.OptionalPath
191	profileIsTextListing := false
192	if BoolDefault(d.dexpreoptProperties.Dex_preopt.Profile_guided, true) {
193		// If dex_preopt.profile_guided is not set, default it based on the existence of the
194		// dexprepot.profile option or the profile class listing.
195		if String(d.dexpreoptProperties.Dex_preopt.Profile) != "" {
196			profileClassListing = android.OptionalPathForPath(
197				android.PathForModuleSrc(ctx, String(d.dexpreoptProperties.Dex_preopt.Profile)))
198			profileBootListing = android.ExistentPathForSource(ctx,
199				ctx.ModuleDir(), String(d.dexpreoptProperties.Dex_preopt.Profile)+"-boot")
200			profileIsTextListing = true
201		} else if global.ProfileDir != "" {
202			profileClassListing = android.ExistentPathForSource(ctx,
203				global.ProfileDir, ctx.ModuleName()+".prof")
204		}
205	}
206
207	// Full dexpreopt config, used to create dexpreopt build rules.
208	dexpreoptConfig := &dexpreopt.ModuleConfig{
209		Name:            ctx.ModuleName(),
210		DexLocation:     dexLocation,
211		BuildPath:       android.PathForModuleOut(ctx, "dexpreopt", ctx.ModuleName()+".jar").OutputPath,
212		DexPath:         dexJarFile,
213		ManifestPath:    android.OptionalPathForPath(d.manifestFile),
214		UncompressedDex: d.uncompressedDex,
215		HasApkLibraries: false,
216		PreoptFlags:     nil,
217
218		ProfileClassListing:  profileClassListing,
219		ProfileIsTextListing: profileIsTextListing,
220		ProfileBootListing:   profileBootListing,
221
222		EnforceUsesLibrariesStatusFile: dexpreopt.UsesLibrariesStatusFile(ctx),
223		EnforceUsesLibraries:           d.enforceUsesLibs,
224		ProvidesUsesLibrary:            providesUsesLib,
225		ClassLoaderContexts:            d.classLoaderContexts,
226
227		Archs:                           archs,
228		DexPreoptImagesDeps:             imagesDeps,
229		DexPreoptImageLocationsOnHost:   hostImageLocations,
230		DexPreoptImageLocationsOnDevice: deviceImageLocations,
231
232		PreoptBootClassPathDexFiles:     dexFiles.Paths(),
233		PreoptBootClassPathDexLocations: dexLocations,
234
235		PreoptExtractedApk: false,
236
237		NoCreateAppImage:    !BoolDefault(d.dexpreoptProperties.Dex_preopt.App_image, true),
238		ForceCreateAppImage: BoolDefault(d.dexpreoptProperties.Dex_preopt.App_image, false),
239
240		PresignedPrebuilt: d.isPresignedPrebuilt,
241	}
242
243	d.configPath = android.PathForModuleOut(ctx, "dexpreopt", "dexpreopt.config")
244	dexpreopt.WriteModuleConfig(ctx, dexpreoptConfig, d.configPath)
245
246	if d.dexpreoptDisabled(ctx) {
247		return
248	}
249
250	globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
251
252	dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, globalSoong, global, dexpreoptConfig)
253	if err != nil {
254		ctx.ModuleErrorf("error generating dexpreopt rule: %s", err.Error())
255		return
256	}
257
258	dexpreoptRule.Build("dexpreopt", "dexpreopt")
259
260	d.builtInstalled = dexpreoptRule.Installs().String()
261}
262