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