• 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	"sort"
20	"strings"
21
22	"github.com/google/blueprint/proptools"
23
24	"android/soong/android"
25	"android/soong/dexpreopt"
26)
27
28type DexpreopterInterface interface {
29	// True if the java module is to be dexed and installed on devices.
30	// Structs that embed dexpreopter must implement this.
31	IsInstallable() bool
32
33	// True if dexpreopt is disabled for the java module.
34	dexpreoptDisabled(ctx android.BaseModuleContext, libraryName string) bool
35
36	// If the java module is to be installed into an APEX, this list contains information about the
37	// dexpreopt outputs to be installed on devices. Note that these dexpreopt outputs are installed
38	// outside of the APEX.
39	ApexSystemServerDexpreoptInstalls() []DexpreopterInstall
40
41	// ApexSystemServerDexJars returns the list of dex jars if this is an apex system server jar.
42	ApexSystemServerDexJars() android.Paths
43
44	// See `dexpreopter.outputProfilePathOnHost`.
45	OutputProfilePathOnHost() android.Path
46}
47
48type DexpreopterInstall struct {
49	// The path to the dexpreopt output on host.
50	OutputPathOnHost android.Path
51
52	// The directory on the device for the output to install to.
53	InstallDirOnDevice android.InstallPath
54
55	// The basename (the last segment of the path) for the output to install as.
56	InstallFileOnDevice string
57}
58
59type Dexpreopter struct {
60	dexpreopter
61}
62
63type dexpreopter struct {
64	dexpreoptProperties       DexpreoptProperties
65	importDexpreoptProperties ImportDexpreoptProperties
66
67	// If true, the dexpreopt rules will not be generated
68	// Unlike Dex_preopt.Enabled which is user-facing,
69	// shouldDisableDexpreopt is a mutated propery.
70	shouldDisableDexpreopt bool
71
72	installPath         android.InstallPath
73	uncompressedDex     bool
74	isSDKLibrary        bool
75	isApp               bool
76	isTest              bool
77	isPresignedPrebuilt bool
78	preventInstall      bool
79
80	manifestFile        android.Path
81	statusFile          android.WritablePath
82	enforceUsesLibs     bool
83	classLoaderContexts dexpreopt.ClassLoaderContextMap
84
85	// See the `dexpreopt` function for details.
86	builtInstalled                    string
87	apexSystemServerDexpreoptInstalls []DexpreopterInstall
88	apexSystemServerDexJars           android.Paths
89
90	// The config is used for two purposes:
91	// - Passing dexpreopt information about libraries from Soong to Make. This is needed when
92	//   a <uses-library> is defined in Android.bp, but used in Android.mk (see dex_preopt_config_merger.py).
93	//   Note that dexpreopt.config might be needed even if dexpreopt is disabled for the library itself.
94	// - Dexpreopt post-processing (using dexpreopt artifacts from a prebuilt system image to incrementally
95	//   dexpreopt another partition).
96	configPath android.WritablePath
97
98	// The path to the profile on host that dexpreopter generates. This is used as the input for
99	// dex2oat.
100	outputProfilePathOnHost android.Path
101
102	// The path to the profile that dexpreopter accepts. It must be in the binary format. If this is
103	// set, it overrides the profile settings in `dexpreoptProperties`.
104	inputProfilePathOnHost android.Path
105
106	// The path to the profile that matches the dex optimized by r8/d8. It is in text format. If this is
107	// set, it will be converted to a binary profile which will be subsequently used for dexpreopt.
108	rewrittenProfile android.Path
109}
110
111type DexpreoptProperties struct {
112	Dex_preopt struct {
113		// If false, prevent dexpreopting.  Defaults to true.
114		Enabled proptools.Configurable[bool] `android:"replace_instead_of_append"`
115
116		// If true, generate an app image (.art file) for this module.
117		App_image proptools.Configurable[bool] `android:"replace_instead_of_append"`
118
119		// If true, use a checked-in profile to guide optimization.  Defaults to false unless
120		// a matching profile is set or a profile is found in PRODUCT_DEX_PREOPT_PROFILE_DIR
121		// that matches the name of this module, in which case it is defaulted to true.
122		Profile_guided proptools.Configurable[bool] `android:"replace_instead_of_append"`
123
124		// If set, provides the path to profile relative to the Android.bp file.  If not set,
125		// defaults to searching for a file that matches the name of this module in the default
126		// profile location set by PRODUCT_DEX_PREOPT_PROFILE_DIR, or empty if not found.
127		Profile proptools.Configurable[string] `android:"path,replace_instead_of_append"`
128
129		// If set to true, r8/d8 will use `profile` as input to generate a new profile that matches
130		// the optimized dex.
131		// The new profile will be subsequently used as the profile to dexpreopt the dex file.
132		Enable_profile_rewriting proptools.Configurable[bool] `android:"replace_instead_of_append"`
133	}
134
135	Dex_preopt_result struct {
136		// True if profile-guided optimization is actually enabled.
137		Profile_guided bool
138	} `blueprint:"mutated"`
139}
140
141type ImportDexpreoptProperties struct {
142	Dex_preopt struct {
143		// If true, use the profile in the prebuilt APEX to guide optimization. Defaults to false.
144		Profile_guided *bool
145	}
146}
147
148func init() {
149	dexpreopt.DexpreoptRunningInSoong = true
150}
151
152func isApexVariant(ctx android.BaseModuleContext) bool {
153	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
154	return !apexInfo.IsForPlatform()
155}
156
157func forPrebuiltApex(ctx android.BaseModuleContext) bool {
158	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
159	return apexInfo.ForPrebuiltApex
160}
161
162// For apex variant of modules, this returns true on the source variant if the prebuilt apex
163// has been selected using apex_contributions.
164// The prebuilt apex will be responsible for generating the dexpreopt rules of the deapexed java lib.
165func disableSourceApexVariant(ctx android.BaseModuleContext) bool {
166	if !isApexVariant(ctx) {
167		return false // platform variant
168	}
169	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
170	psi := android.PrebuiltSelectionInfoMap{}
171	ctx.VisitDirectDepsProxy(func(am android.ModuleProxy) {
172		if prebuiltSelectionInfo, ok := android.OtherModuleProvider(ctx, am, android.PrebuiltSelectionInfoProvider); ok {
173			psi = prebuiltSelectionInfo
174		}
175	})
176
177	// Find the apex variant for this module
178	apexVariants := []string{}
179	if apexInfo.BaseApexName != "" {
180		apexVariants = append(apexVariants, apexInfo.BaseApexName)
181	}
182	if apexInfo.ApexAvailableName != "" {
183		apexVariants = append(apexVariants, apexInfo.ApexAvailableName)
184	}
185	disableSource := false
186	// find the selected apexes
187	for _, apexVariant := range apexVariants {
188		if len(psi.GetSelectedModulesForApiDomain(apexVariant)) > 0 {
189			// If the apex_contribution for this api domain is non-empty, disable the source variant
190			disableSource = true
191		}
192	}
193	return disableSource
194}
195
196// Returns whether dexpreopt is applicable to the module.
197// When it returns true, neither profile nor dexpreopt artifacts will be generated.
198func (d *dexpreopter) dexpreoptDisabled(ctx android.BaseModuleContext, libName string) bool {
199	if !ctx.Device() {
200		return true
201	}
202
203	if d.isTest {
204		return true
205	}
206
207	if !d.dexpreoptProperties.Dex_preopt.Enabled.GetOrDefault(ctx, true) {
208		return true
209	}
210
211	if d.shouldDisableDexpreopt {
212		return true
213	}
214
215	// If the module is from a prebuilt APEX, it shouldn't be installable, but it can still be
216	// dexpreopted.
217	if !ctx.Module().(DexpreopterInterface).IsInstallable() && !forPrebuiltApex(ctx) {
218		return true
219	}
220
221	if !android.IsModulePreferred(ctx.Module()) {
222		return true
223	}
224
225	if _, isApex := android.ModuleProvider(ctx, android.ApexBundleInfoProvider); isApex {
226		// dexpreopt rules for system server jars can be generated in the ModuleCtx of prebuilt apexes
227		return false
228	}
229
230	global := dexpreopt.GetGlobalConfig(ctx)
231
232	// Use the libName argument to determine if the library being dexpreopt'd is a system server jar
233	// ctx.ModuleName() is not safe. In case of prebuilt apexes, the dexpreopt rules of system server jars
234	// are created in the ctx object of the top-level prebuilt apex.
235	isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(libName)
236
237	if _, isApex := android.ModuleProvider(ctx, android.ApexBundleInfoProvider); isApex || isApexVariant(ctx) {
238		// dexpreopt rules for system server jars can be generated in the ModuleCtx of prebuilt apexes
239		if !isApexSystemServerJar {
240			return true
241		}
242	} else {
243		// Don't preopt the platform variant of an APEX system server jar to avoid conflicts.
244		if isApexSystemServerJar {
245			return true
246		}
247	}
248
249	// TODO: contains no java code
250
251	return false
252}
253
254func dexpreoptToolDepsMutator(ctx android.BottomUpMutatorContext) {
255	if _, isApex := android.ModuleProvider(ctx, android.ApexBundleInfoProvider); isApex && dexpreopt.IsDex2oatNeeded(ctx) {
256		// prebuilt apexes can genererate rules to dexpreopt deapexed jars
257		// Add a dex2oat dep aggressively on _every_ apex module
258		dexpreopt.RegisterToolDeps(ctx)
259		return
260	}
261	if d, ok := ctx.Module().(DexpreopterInterface); !ok || d.dexpreoptDisabled(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName())) || !dexpreopt.IsDex2oatNeeded(ctx) {
262		return
263	}
264	dexpreopt.RegisterToolDeps(ctx)
265}
266
267// Returns the install path of the dex jar of a module.
268//
269// Do not rely on `ApexInfo.ApexVariationName` because it can be something like "apex1000", rather
270// than the `name` in the path `/apex/<name>` as suggested in its comment.
271//
272// This function is on a best-effort basis. It cannot handle the case where an APEX jar is not a
273// system server jar, which is fine because we currently only preopt system server jars for APEXes.
274func (d *dexpreopter) getInstallPath(
275	ctx android.ModuleContext, libName string, defaultInstallPath android.InstallPath) android.InstallPath {
276	global := dexpreopt.GetGlobalConfig(ctx)
277	if global.AllApexSystemServerJars(ctx).ContainsJar(libName) {
278		dexLocation := dexpreopt.GetSystemServerDexLocation(ctx, global, libName)
279		return android.PathForModuleInPartitionInstall(ctx, "", strings.TrimPrefix(dexLocation, "/"))
280	}
281	if !d.dexpreoptDisabled(ctx, libName) && isApexVariant(ctx) &&
282		filepath.Base(defaultInstallPath.PartitionDir()) != "apex" {
283		ctx.ModuleErrorf("unable to get the install path of the dex jar for dexpreopt")
284	}
285	return defaultInstallPath
286}
287
288// DexpreoptPrebuiltApexSystemServerJars generates the dexpreopt artifacts from a jar file that has been deapexed from a prebuilt apex
289func (d *Dexpreopter) DexpreoptPrebuiltApexSystemServerJars(ctx android.ModuleContext, libraryName string, di *android.DeapexerInfo) {
290	// A single prebuilt apex can have multiple apex system jars
291	// initialize the output path for this dex jar
292	dc := dexpreopt.GetGlobalConfig(ctx)
293	d.installPath = android.PathForModuleInPartitionInstall(ctx, "", strings.TrimPrefix(dexpreopt.GetSystemServerDexLocation(ctx, dc, libraryName), "/"))
294	// generate the rules for creating the .odex and .vdex files for this system server jar
295	dexJarFile := di.PrebuiltExportPath(ApexRootRelativePathToJavaLib(libraryName))
296	if dexJarFile == nil {
297		ctx.ModuleErrorf(
298			`Could not find library %s in prebuilt apex %s.
299Please make sure that the value of PRODUCT_APEX_(SYSTEM_SERVER|STANDALONE_SYSTEM_SERVER)_JARS is correct`, libraryName, ctx.ModuleName())
300	}
301	d.inputProfilePathOnHost = nil // reset: TODO(spandandas): Make dexpreopter stateless
302	if android.InList(libraryName, di.GetDexpreoptProfileGuidedExportedModuleNames()) {
303		// Set the profile path to guide optimization
304		prof := di.PrebuiltExportPath(ApexRootRelativePathToJavaLib(libraryName) + ".prof")
305		if prof == nil {
306			ctx.ModuleErrorf("Could not find a .prof file in this prebuilt apex")
307		}
308		d.inputProfilePathOnHost = prof
309	}
310
311	d.dexpreopt(ctx, libraryName, dexJarFile)
312}
313
314func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, libName string, dexJarFile android.Path) {
315	global := dexpreopt.GetGlobalConfig(ctx)
316
317	// TODO(b/148690468): The check on d.installPath is to bail out in cases where
318	// the dexpreopter struct hasn't been fully initialized before we're called,
319	// e.g. in aar.go. This keeps the behaviour that dexpreopting is effectively
320	// disabled, even if installable is true.
321	if d.installPath.Base() == "." {
322		return
323	}
324
325	dexLocation := android.InstallPathToOnDevicePath(ctx, d.installPath)
326
327	providesUsesLib := libName
328	if ulib, ok := ctx.Module().(ProvidesUsesLib); ok {
329		name := ulib.ProvidesUsesLib()
330		if name != nil {
331			providesUsesLib = *name
332		}
333	}
334
335	// If it is test, make config files regardless of its dexpreopt setting.
336	// The config files are required for apps defined in make which depend on the lib.
337	if d.isTest && d.dexpreoptDisabled(ctx, libName) {
338		return
339	}
340
341	isSystemServerJar := global.AllSystemServerJars(ctx).ContainsJar(libName)
342
343	bootImage := defaultBootImageConfig(ctx)
344	// When `global.PreoptWithUpdatableBcp` is true, `bcpForDexpreopt` below includes the mainline
345	// boot jars into bootclasspath, so we should include the mainline boot image as well because it's
346	// generated from those jars.
347	if global.PreoptWithUpdatableBcp {
348		bootImage = mainlineBootImageConfig(ctx)
349	}
350	dexFiles, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp)
351
352	targets := ctx.MultiTargets()
353	if len(targets) == 0 {
354		// assume this is a java library, dexpreopt for all arches for now
355		for _, target := range ctx.Config().Targets[android.Android] {
356			if target.NativeBridge == android.NativeBridgeDisabled {
357				targets = append(targets, target)
358			}
359		}
360		if isSystemServerJar && libName != "com.android.location.provider" {
361			// If the module is a system server jar, only preopt for the primary arch because the jar can
362			// only be loaded by system server. "com.android.location.provider" is a special case because
363			// it's also used by apps as a shared library.
364			targets = targets[:1]
365		}
366	}
367
368	var archs []android.ArchType
369	var images android.Paths
370	var imagesDeps []android.OutputPaths
371	for _, target := range targets {
372		archs = append(archs, target.Arch.ArchType)
373		variant := bootImage.getVariant(target)
374		images = append(images, variant.imagePathOnHost)
375		imagesDeps = append(imagesDeps, variant.imagesDeps)
376	}
377	// The image locations for all Android variants are identical.
378	hostImageLocations, deviceImageLocations := bootImage.getAnyAndroidVariant().imageLocations()
379
380	var profileClassListing android.OptionalPath
381	var profileBootListing android.OptionalPath
382	profileIsTextListing := false
383
384	if d.inputProfilePathOnHost != nil {
385		profileClassListing = android.OptionalPathForPath(d.inputProfilePathOnHost)
386	} else if d.dexpreoptProperties.Dex_preopt.Profile_guided.GetOrDefault(ctx, true) && !forPrebuiltApex(ctx) {
387		// If enable_profile_rewriting is set, use the rewritten profile instead of the checked-in profile
388		if d.EnableProfileRewriting(ctx) {
389			profileClassListing = android.OptionalPathForPath(d.GetRewrittenProfile())
390			profileIsTextListing = true
391		} else if profile := d.GetProfile(ctx); profile != "" {
392			// If dex_preopt.profile_guided is not set, default it based on the existence of the
393			// dexprepot.profile option or the profile class listing.
394			profileClassListing = android.OptionalPathForPath(
395				android.PathForModuleSrc(ctx, profile))
396			profileBootListing = android.ExistentPathForSource(ctx,
397				ctx.ModuleDir(), profile+"-boot")
398			profileIsTextListing = true
399		} else if global.ProfileDir != "" {
400			profileClassListing = android.ExistentPathForSource(ctx,
401				global.ProfileDir, libName+".prof")
402		}
403	}
404
405	d.dexpreoptProperties.Dex_preopt_result.Profile_guided = profileClassListing.Valid()
406
407	// A single apex can have multiple system server jars
408	// Use the dexJar to create a unique scope for each
409	dexJarStem := strings.TrimSuffix(dexJarFile.Base(), dexJarFile.Ext())
410
411	appImage := d.dexpreoptProperties.Dex_preopt.App_image.Get(ctx)
412
413	// Full dexpreopt config, used to create dexpreopt build rules.
414	dexpreoptConfig := &dexpreopt.ModuleConfig{
415		Name:            libName,
416		DexLocation:     dexLocation,
417		BuildPath:       android.PathForModuleOut(ctx, "dexpreopt", dexJarStem, libName+".jar").OutputPath,
418		DexPath:         dexJarFile,
419		ManifestPath:    android.OptionalPathForPath(d.manifestFile),
420		UncompressedDex: d.uncompressedDex,
421		HasApkLibraries: false,
422		PreoptFlags:     nil,
423
424		ProfileClassListing:  profileClassListing,
425		ProfileIsTextListing: profileIsTextListing,
426		ProfileBootListing:   profileBootListing,
427
428		EnforceUsesLibrariesStatusFile: dexpreopt.UsesLibrariesStatusFile(ctx),
429		EnforceUsesLibraries:           d.enforceUsesLibs,
430		ProvidesUsesLibrary:            providesUsesLib,
431		ClassLoaderContexts:            d.classLoaderContexts,
432
433		Archs:                           archs,
434		DexPreoptImagesDeps:             imagesDeps,
435		DexPreoptImageLocationsOnHost:   hostImageLocations,
436		DexPreoptImageLocationsOnDevice: deviceImageLocations,
437
438		PreoptBootClassPathDexFiles:     dexFiles.Paths(),
439		PreoptBootClassPathDexLocations: dexLocations,
440
441		NoCreateAppImage:    !appImage.GetOrDefault(true),
442		ForceCreateAppImage: appImage.GetOrDefault(false),
443
444		PresignedPrebuilt: d.isPresignedPrebuilt,
445	}
446
447	if ctx.Config().InstallApexSystemServerDexpreoptSamePartition() {
448		dexpreoptConfig.ApexPartition = android.PathForModuleInstall(ctx).Partition()
449	} else {
450		dexpreoptConfig.ApexPartition = "system"
451	}
452
453	d.configPath = android.PathForModuleOut(ctx, "dexpreopt", dexJarStem, "dexpreopt.config")
454	dexpreopt.WriteModuleConfig(ctx, dexpreoptConfig, d.configPath)
455	ctx.CheckbuildFile(d.configPath)
456
457	if d.dexpreoptDisabled(ctx, libName) {
458		return
459	}
460
461	globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
462
463	// The root "product_packages.txt" is generated by `build/make/core/Makefile`. It contains a list
464	// of all packages that are installed on the device. We use `grep` to filter the list by the app's
465	// dependencies to create a per-app list, and use `rsync --checksum` to prevent the file's mtime
466	// from being changed if the contents don't change. This avoids unnecessary dexpreopt reruns.
467	productPackages := android.PathForModuleInPartitionInstall(ctx, "", "product_packages.txt")
468	appProductPackages := android.PathForModuleOut(ctx, "dexpreopt", dexJarStem, "product_packages.txt")
469	appProductPackagesStaging := appProductPackages.ReplaceExtension(ctx, "txt.tmp")
470	clcNames, _ := dexpreopt.ComputeClassLoaderContextDependencies(dexpreoptConfig.ClassLoaderContexts)
471	sort.Strings(clcNames) // The order needs to be deterministic.
472	productPackagesRule := android.NewRuleBuilder(pctx, ctx)
473	if len(clcNames) > 0 {
474		productPackagesRule.Command().
475			Text("grep -F -x").
476			FlagForEachArg("-e ", clcNames).
477			Input(productPackages).
478			FlagWithOutput("> ", appProductPackagesStaging).
479			Text("|| true")
480	} else {
481		productPackagesRule.Command().
482			Text("rm -f").Output(appProductPackagesStaging).
483			Text("&&").
484			Text("touch").Output(appProductPackagesStaging)
485	}
486	productPackagesRule.Command().
487		Text("rsync --checksum").
488		Input(appProductPackagesStaging).
489		Output(appProductPackages)
490	productPackagesRule.Restat().Build("product_packages."+dexJarStem, "dexpreopt product_packages")
491
492	dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(
493		ctx, globalSoong, global, dexpreoptConfig, appProductPackages)
494	if err != nil {
495		ctx.ModuleErrorf("error generating dexpreopt rule: %s", err.Error())
496		return
497	}
498
499	dexpreoptRule.Build("dexpreopt"+"."+dexJarStem, "dexpreopt")
500
501	// The current ctx might be of a deapexer module created by a prebuilt apex
502	// Use the path of the dex file to determine the library name
503	isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(dexJarStem)
504
505	dexpreoptPartition := d.installPath.Partition()
506	// dexpreoptPartition is set to empty for dexpreopts of system APEX and system_other.
507	// In case of system APEX, however, we can set it to "system" manually.
508	// TODO(b/346662300): Let dexpreopter generate the installPath for dexpreopt files instead of
509	// using the dex location to generate the installPath.
510	if isApexSystemServerJar {
511		dexpreoptPartition = dexpreoptConfig.ApexPartition
512	}
513	for _, install := range dexpreoptRule.Installs() {
514		// Remove the "/" prefix because the path should be relative to $ANDROID_PRODUCT_OUT.
515		installDir := strings.TrimPrefix(filepath.Dir(install.To), "/")
516		partition := dexpreoptPartition
517		if strings.HasPrefix(installDir, partition+"/") {
518			installDir = strings.TrimPrefix(installDir, partition+"/")
519		} else {
520			// If the partition for the installDir is different from the install partition, set the
521			// partition empty to install the dexpreopt files to the desired partition.
522			// TODO(b/346439786): Define and use the dexpreopt module type to avoid this mismatch.
523			partition = ""
524		}
525		installBase := filepath.Base(install.To)
526		installPath := android.PathForModuleInPartitionInstall(ctx, partition, installDir)
527		isProfile := strings.HasSuffix(installBase, ".prof")
528
529		if isProfile {
530			d.outputProfilePathOnHost = install.From
531		}
532
533		if isApexSystemServerJar {
534			// Profiles are handled separately because they are installed into the APEX.
535			if !isProfile {
536				// APEX variants of java libraries are hidden from Make, so their dexpreopt
537				// outputs need special handling. Currently, for APEX variants of java
538				// libraries, only those in the system server classpath are handled here.
539				// Preopting of boot classpath jars in the ART APEX are handled in
540				// java/dexpreopt_bootjars.go, and other APEX jars are not preopted.
541				// The installs will be handled the apex module that includes this library.
542				di := DexpreopterInstall{
543					OutputPathOnHost:    install.From,
544					InstallDirOnDevice:  installPath,
545					InstallFileOnDevice: installBase,
546				}
547				d.apexSystemServerDexpreoptInstalls = append(d.apexSystemServerDexpreoptInstalls, di)
548
549			}
550		} else if !d.preventInstall {
551			// Install without adding to checkbuild to match behavior of previous Make-based checkbuild rules
552			ctx.InstallFileWithoutCheckbuild(installPath, installBase, install.From)
553		}
554	}
555
556	if isApexSystemServerJar {
557		// Store the dex jar location for system server jars in apexes, the apex will copy the file into
558		// a known location for dex2oat.
559		d.apexSystemServerDexJars = append(d.apexSystemServerDexJars, dexJarFile)
560	} else if isSystemServerJar && !d.preventInstall {
561		// Copy the dex jar into a known location for dex2oat for non-apex system server jars.
562		android.CopyFileRule(ctx, dexJarFile, android.PathForOutput(ctx, dexpreopt.SystemServerDexjarsDir, dexJarFile.Base()))
563	}
564
565	if !isApexSystemServerJar {
566		d.builtInstalled = dexpreoptRule.Installs().String()
567	}
568
569	if isSystemServerJar {
570		checkSystemServerOrder(ctx, libName)
571	}
572}
573
574func getModuleInstallPathInfo(ctx android.ModuleContext, fullInstallPath string) (android.InstallPath, string, string) {
575	installPath := android.PathForModuleInstall(ctx)
576	installDir, installBase := filepath.Split(strings.TrimPrefix(fullInstallPath, "/"))
577
578	if !strings.HasPrefix(installDir, installPath.Partition()+"/") {
579		// Return empty filename if the install partition is not for the target image.
580		return installPath, "", ""
581	}
582	relDir, err := filepath.Rel(installPath.Partition(), installDir)
583	if err != nil {
584		panic(err)
585	}
586	return installPath, relDir, installBase
587}
588
589// installFile will install the file if `install` path and the target install partition are the same.
590func installFile(ctx android.ModuleContext, install android.RuleBuilderInstall) {
591	installPath, relDir, name := getModuleInstallPathInfo(ctx, install.To)
592	// Empty name means the install partition is not for the target image.
593	// For the system image, files for "apex" and "system_other" are skipped here.
594	// The skipped "apex" files are for testing only, for example,
595	// "/apex/art_boot_images/javalib/x86/boot.vdex".
596	// TODO(b/320196894): Files for "system_other" are skipped because soong creates the system
597	// image only for now.
598	if name != "" {
599		ctx.InstallFile(installPath.Join(ctx, relDir), name, install.From)
600	}
601}
602
603func (d *dexpreopter) ApexSystemServerDexpreoptInstalls() []DexpreopterInstall {
604	return d.apexSystemServerDexpreoptInstalls
605}
606
607func (d *dexpreopter) ApexSystemServerDexJars() android.Paths {
608	return d.apexSystemServerDexJars
609}
610
611func (d *dexpreopter) OutputProfilePathOnHost() android.Path {
612	return d.outputProfilePathOnHost
613}
614
615func (d *dexpreopter) disableDexpreopt() {
616	d.shouldDisableDexpreopt = true
617}
618
619func (d *dexpreopter) EnableProfileRewriting(ctx android.BaseModuleContext) bool {
620	return d.dexpreoptProperties.Dex_preopt.Enable_profile_rewriting.GetOrDefault(ctx, false)
621}
622
623func (d *dexpreopter) GetProfile(ctx android.BaseModuleContext) string {
624	return d.dexpreoptProperties.Dex_preopt.Profile.GetOrDefault(ctx, "")
625}
626
627func (d *dexpreopter) GetProfileGuided(ctx android.BaseModuleContext) bool {
628	return d.dexpreoptProperties.Dex_preopt.Profile_guided.GetOrDefault(ctx, false)
629}
630
631func (d *dexpreopter) GetRewrittenProfile() android.Path {
632	return d.rewrittenProfile
633}
634
635func (d *dexpreopter) SetRewrittenProfile(p android.Path) {
636	d.rewrittenProfile = p
637}
638
639// Check the order of jars on the system server classpath and give a warning/error if a jar precedes
640// one of its dependencies. This is not an error, but a missed optimization, as dexpreopt won't
641// have the dependency jar in the class loader context, and it won't be able to resolve any
642// references to its classes and methods.
643func checkSystemServerOrder(ctx android.ModuleContext, libName string) {
644	config := dexpreopt.GetGlobalConfig(ctx)
645	jars := config.AllSystemServerClasspathJars(ctx)
646	jarIndex := config.AllSystemServerJars(ctx).IndexOfJar(libName)
647	ctx.WalkDeps(func(dep android.Module, parent android.Module) bool {
648		tag := ctx.OtherModuleDependencyTag(dep)
649		// Ideally this should only be walking relevant dependencies, but to maintain existing behavior
650		// for now just exclude any known irrelevant dependencies that would lead to incorrect errors.
651		if _, ok := tag.(bootclasspathDependencyTag); ok {
652			return false
653		} else if tag == traceReferencesTag {
654			// Allow ordering inversion if the dependency is purely for tracing references.
655			return false
656		}
657		depIndex := jars.IndexOfJar(dep.Name())
658		if jarIndex < depIndex && !config.BrokenSuboptimalOrderOfSystemServerJars {
659			jar := jars.Jar(jarIndex)
660			dep := jars.Jar(depIndex)
661			ctx.ModuleErrorf("non-optimal order of jars on the system server classpath:"+
662				" '%s' precedes its dependency '%s', so dexpreopt is unable to resolve any"+
663				" references from '%s' to '%s'.\n", jar, dep, jar, dep)
664		}
665		return true
666	})
667}
668