• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2019 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	"fmt"
19	"path/filepath"
20	"strings"
21
22	"android/soong/android"
23	"android/soong/dexpreopt"
24
25	"github.com/google/blueprint"
26	"github.com/google/blueprint/proptools"
27)
28
29// =================================================================================================
30// WIP - see http://b/177892522 for details
31//
32// The build support for boot images is currently being migrated away from singleton to modules so
33// the documentation may not be strictly accurate. Rather than update the documentation at every
34// step which will create a lot of churn the changes that have been made will be listed here and the
35// documentation will be updated once it is closer to the final result.
36//
37// Changes:
38// 1) dex_bootjars is now a singleton module and not a plain singleton.
39// 2) Boot images are now represented by the boot_image module type.
40// 3) The art boot image is called "art-boot-image", the framework boot image is called
41//    "framework-boot-image".
42// 4) They are defined in art/build/boot/Android.bp and frameworks/base/boot/Android.bp
43//    respectively.
44// 5) Each boot_image retrieves the appropriate boot image configuration from the map returned by
45//    genBootImageConfigs() using the image_name specified in the boot_image module.
46// =================================================================================================
47
48// This comment describes:
49//   1. ART boot images in general (their types, structure, file layout, etc.)
50//   2. build system support for boot images
51//
52// 1. ART boot images
53// ------------------
54//
55// A boot image in ART is a set of files that contain AOT-compiled native code and a heap snapshot
56// of AOT-initialized classes for the bootclasspath Java libraries. A boot image is compiled from a
57// set of DEX jars by the dex2oat compiler. A boot image is used for two purposes: 1) it is
58// installed on device and loaded at runtime, and 2) other Java libraries and apps are compiled
59// against it (compilation may take place either on host, known as "dexpreopt", or on device, known
60// as "dexopt").
61//
62// A boot image is not a single file, but a collection of interrelated files. Each boot image has a
63// number of components that correspond to the Java libraries that constitute it. For each component
64// there are multiple files:
65//   - *.oat or *.odex file with native code (architecture-specific, one per instruction set)
66//   - *.art file with pre-initialized Java classes (architecture-specific, one per instruction set)
67//   - *.vdex file with verification metadata for the DEX bytecode (architecture independent)
68//
69// *.vdex files for the boot images do not contain the DEX bytecode itself, because the
70// bootclasspath DEX files are stored on disk in uncompressed and aligned form. Consequently a boot
71// image is not self-contained and cannot be used without its DEX files. To simplify the management
72// of boot image files, ART uses a certain naming scheme and associates the following metadata with
73// each boot image:
74//   - A stem, which is a symbolic name that is prepended to boot image file names.
75//   - A location (on-device path to the boot image files).
76//   - A list of boot image locations (on-device paths to dependency boot images).
77//   - A set of DEX locations (on-device paths to the DEX files, one location for one DEX file used
78//     to compile the boot image).
79//
80// There are two kinds of boot images:
81//   - primary boot images
82//   - boot image extensions
83//
84// 1.1. Primary boot images
85// ------------------------
86//
87// A primary boot image is compiled for a core subset of bootclasspath Java libraries. It does not
88// depend on any other images, and other boot images may depend on it.
89//
90// For example, assuming that the stem is "boot", the location is /apex/com.android.art/javalib/,
91// the set of core bootclasspath libraries is A B C, and the boot image is compiled for ARM targets
92// (32 and 64 bits), it will have three components with the following files:
93//   - /apex/com.android.art/javalib/{arm,arm64}/boot.{art,oat,vdex}
94//   - /apex/com.android.art/javalib/{arm,arm64}/boot-B.{art,oat,vdex}
95//   - /apex/com.android.art/javalib/{arm,arm64}/boot-C.{art,oat,vdex}
96//
97// The files of the first component are special: they do not have the component name appended after
98// the stem. This naming convention dates back to the times when the boot image was not split into
99// components, and there were just boot.oat and boot.art. The decision to split was motivated by
100// licensing reasons for one of the bootclasspath libraries.
101//
102// As of November 2020 the only primary boot image in Android is the image in the ART APEX
103// com.android.art. The primary ART boot image contains the Core libraries that are part of the ART
104// module. When the ART module gets updated, the primary boot image will be updated with it, and all
105// dependent images will get invalidated (the checksum of the primary image stored in dependent
106// images will not match), unless they are updated in sync with the ART module.
107//
108// 1.2. Boot image extensions
109// --------------------------
110//
111// A boot image extension is compiled for a subset of bootclasspath Java libraries (in particular,
112// this subset does not include the Core bootclasspath libraries that go into the primary boot
113// image). A boot image extension depends on the primary boot image and optionally some other boot
114// image extensions. Other images may depend on it. In other words, boot image extensions can form
115// acyclic dependency graphs.
116//
117// The motivation for boot image extensions comes from the Mainline project. Consider a situation
118// when the list of bootclasspath libraries is A B C, and both A and B are parts of the Android
119// platform, but C is part of an updatable APEX com.android.C. When the APEX is updated, the Java
120// code for C might have changed compared to the code that was used to compile the boot image.
121// Consequently, the whole boot image is obsolete and invalidated (even though the code for A and B
122// that does not depend on C is up to date). To avoid this, the original monolithic boot image is
123// split in two parts: the primary boot image that contains A B, and the boot image extension that
124// contains C and depends on the primary boot image (extends it).
125//
126// For example, assuming that the stem is "boot", the location is /system/framework, the set of
127// bootclasspath libraries is D E (where D is part of the platform and is located in
128// /system/framework, and E is part of a non-updatable APEX com.android.E and is located in
129// /apex/com.android.E/javalib), and the boot image is compiled for ARM targets (32 and 64 bits),
130// it will have two components with the following files:
131//   - /system/framework/{arm,arm64}/boot-D.{art,oat,vdex}
132//   - /system/framework/{arm,arm64}/boot-E.{art,oat,vdex}
133//
134// As of November 2020 the only boot image extension in Android is the Framework boot image
135// extension. It extends the primary ART boot image and contains Framework libraries and other
136// bootclasspath libraries from the platform and non-updatable APEXes that are not included in the
137// ART image. The Framework boot image extension is updated together with the platform. In the
138// future other boot image extensions may be added for some updatable modules.
139//
140//
141// 2. Build system support for boot images
142// ---------------------------------------
143//
144// The primary ART boot image needs to be compiled with one dex2oat invocation that depends on DEX
145// jars for the core libraries. Framework boot image extension needs to be compiled with one dex2oat
146// invocation that depends on the primary ART boot image and all bootclasspath DEX jars except the
147// core libraries as they are already part of the primary ART boot image.
148//
149// 2.1. Libraries that go in the boot images
150// -----------------------------------------
151//
152// The contents of each boot image are determined by the PRODUCT variables. The primary ART APEX
153// boot image contains libraries listed in the ART_APEX_JARS variable in the AOSP makefiles. The
154// Framework boot image extension contains libraries specified in the PRODUCT_BOOT_JARS and
155// PRODUCT_BOOT_JARS_EXTRA variables. The AOSP makefiles specify some common Framework libraries,
156// but more product-specific libraries can be added in the product makefiles.
157//
158// Each component of the PRODUCT_BOOT_JARS and PRODUCT_BOOT_JARS_EXTRA variables is a
159// colon-separated pair <apex>:<library>, where <apex> is the variant name of a non-updatable APEX,
160// "platform" if the library is a part of the platform in the system partition, or "system_ext" if
161// it's in the system_ext partition.
162//
163// In these variables APEXes are identified by their "variant names", i.e. the names they get
164// mounted as in /apex on device. In Soong modules that is the name set in the "apex_name"
165// properties, which default to the "name" values. For example, many APEXes have both
166// com.android.xxx and com.google.android.xxx modules in Soong, but take the same place
167// /apex/com.android.xxx at runtime. In these cases the variant name is always com.android.xxx,
168// regardless which APEX goes into the product. See also android.ApexInfo.ApexVariationName and
169// apex.apexBundleProperties.Apex_name.
170//
171// A related variable PRODUCT_APEX_BOOT_JARS contains bootclasspath libraries that are in APEXes.
172// They are not included in the boot image. The only exception here are ART jars and core-icu4j.jar
173// that have been historically part of the boot image and are now in apexes; they are in boot images
174// and core-icu4j.jar is generally treated as being part of PRODUCT_BOOT_JARS.
175//
176// One exception to the above rules are "coverage" builds (a special build flavor which requires
177// setting environment variable EMMA_INSTRUMENT_FRAMEWORK=true). In coverage builds the Java code in
178// boot image libraries is instrumented, which means that the instrumentation library (jacocoagent)
179// needs to be added to the list of bootclasspath DEX jars.
180//
181// In general, there is a requirement that the source code for a boot image library must be
182// available at build time (e.g. it cannot be a stub that has a separate implementation library).
183//
184// 2.2. Static configs
185// -------------------
186//
187// Because boot images are used to dexpreopt other Java modules, the paths to boot image files must
188// be known by the time dexpreopt build rules for the dependent modules are generated. Boot image
189// configs are constructed very early during the build, before build rule generation. The configs
190// provide predefined paths to boot image files (these paths depend only on static build
191// configuration, such as PRODUCT variables, and use hard-coded directory names).
192//
193// 2.3. Singleton
194// --------------
195//
196// Build rules for the boot images are generated with a Soong singleton. Because a singleton has no
197// dependencies on other modules, it has to find the modules for the DEX jars using VisitAllModules.
198// Soong loops through all modules and compares each module against a list of bootclasspath library
199// names. Then it generates build rules that copy DEX jars from their intermediate module-specific
200// locations to the hard-coded locations predefined in the boot image configs.
201//
202// It would be possible to use a module with proper dependencies instead, but that would require
203// changes in the way Soong generates variables for Make: a singleton can use one MakeVars() method
204// that writes variables to out/soong/make_vars-*.mk, which is included early by the main makefile,
205// but module(s) would have to use out/soong/Android-*.mk which has a group of LOCAL_* variables
206// for each module, and is included later.
207//
208// 2.4. Install rules
209// ------------------
210//
211// The primary boot image and the Framework extension are installed in different ways. The primary
212// boot image is part of the ART APEX: it is copied into the APEX intermediate files, packaged
213// together with other APEX contents, extracted and mounted on device. The Framework boot image
214// extension is installed by the rules defined in makefiles (make/core/dex_preopt_libart.mk). Soong
215// writes out a few DEXPREOPT_IMAGE_* variables for Make; these variables contain boot image names,
216// paths and so on.
217//
218
219var artApexNames = []string{
220	"com.android.art",
221	"com.android.art.debug",
222	"com.android.art.testing",
223	"com.google.android.art",
224	"com.google.android.art.debug",
225	"com.google.android.art.testing",
226}
227
228var (
229	dexBootJarsFragmentsKey         = android.NewOnceKey("dexBootJarsFragments")
230	apexContributionsMetadataDepTag = dependencyTag{name: "all_apex_contributions"}
231)
232
233func init() {
234	RegisterDexpreoptBootJarsComponents(android.InitRegistrationContext)
235}
236
237// Target-independent description of a boot image.
238//
239// WARNING: All fields in this struct should be initialized in the genBootImageConfigs function.
240// Failure to do so can lead to data races if there is no synchronization enforced ordering between
241// the writer and the reader.
242type bootImageConfig struct {
243	// If this image is an extension, the image that it extends.
244	extends *bootImageConfig
245
246	// Image name (used in directory names and ninja rule names).
247	name string
248
249	// If the module with the given name exists, this config is enabled.
250	enabledIfExists string
251
252	// Basename of the image: the resulting filenames are <stem>[-<jar>].{art,oat,vdex}.
253	stem string
254
255	// Output directory for the image files.
256	dir android.OutputPath
257
258	// Output directory for the image files with debug symbols.
259	symbolsDir android.OutputPath
260
261	// The relative location where the image files are installed. On host, the location is relative to
262	// $ANDROID_PRODUCT_OUT.
263	//
264	// Only the configs that are built by platform_bootclasspath are installable on device. On device,
265	// the location is relative to "/".
266	installDir string
267
268	// A list of (location, jar) pairs for the Java modules in this image.
269	modules android.ConfiguredJarList
270
271	// File paths to jars.
272	dexPaths     android.WritablePaths // for this image
273	dexPathsDeps android.WritablePaths // for the dependency images and in this image
274
275	// Map from module name (without prebuilt_ prefix) to the predefined build path.
276	dexPathsByModule map[string]android.WritablePath
277
278	// File path to a zip archive with all image files (or nil, if not needed).
279	zip android.WritablePath
280
281	// Target-dependent fields.
282	variants []*bootImageVariant
283
284	// Path of the preloaded classes file.
285	preloadedClassesFile string
286
287	// The "--compiler-filter" argument.
288	compilerFilter string
289
290	// The "--single-image" argument.
291	singleImage bool
292
293	// Profiles imported from APEXes, in addition to the profile at the default path. Each entry must
294	// be the name of an APEX module.
295	profileImports []string
296
297	// The name of the module that provides boot image profiles, if any.
298	profileProviderModule string
299}
300
301// Target-dependent description of a boot image.
302//
303// WARNING: The warning comment on bootImageConfig applies here too.
304type bootImageVariant struct {
305	*bootImageConfig
306
307	// Target for which the image is generated.
308	target android.Target
309
310	// The "locations" of jars.
311	dexLocations     []string // for this image
312	dexLocationsDeps []string // for the dependency images and in this image
313
314	// Paths to image files.
315	imagePathOnHost   android.OutputPath // first image file path on host
316	imagePathOnDevice string             // first image file path on device
317
318	// All the files that constitute this image variant, i.e. .art, .oat and .vdex files.
319	imagesDeps android.OutputPaths
320
321	// The path to the base image variant's imagePathOnHost field, where base image variant
322	// means the image variant that this extends.
323	//
324	// This is only set for a variant of an image that extends another image.
325	baseImages android.OutputPaths
326
327	// The paths to the base image variant's imagesDeps field, where base image variant
328	// means the image variant that this extends.
329	//
330	// This is only set for a variant of an image that extends another image.
331	baseImagesDeps android.Paths
332
333	// Rules which should be used in make to install the outputs on host.
334	//
335	// Deprecated: Not initialized correctly, see struct comment.
336	installs android.RuleBuilderInstalls
337
338	// Rules which should be used in make to install the vdex outputs on host.
339	//
340	// Deprecated: Not initialized correctly, see struct comment.
341	vdexInstalls android.RuleBuilderInstalls
342
343	// Rules which should be used in make to install the unstripped outputs on host.
344	//
345	// Deprecated: Not initialized correctly, see struct comment.
346	unstrippedInstalls android.RuleBuilderInstalls
347
348	// Path to the license metadata file for the module that built the image.
349	//
350	// Deprecated: Not initialized correctly, see struct comment.
351	licenseMetadataFile android.OptionalPath
352}
353
354// Get target-specific boot image variant for the given boot image config and target.
355func (image bootImageConfig) getVariant(target android.Target) *bootImageVariant {
356	for _, variant := range image.variants {
357		if variant.target.Os == target.Os && variant.target.Arch.ArchType == target.Arch.ArchType {
358			return variant
359		}
360	}
361	return nil
362}
363
364// Return any (the first) variant which is for the device (as opposed to for the host).
365func (image bootImageConfig) getAnyAndroidVariant() *bootImageVariant {
366	for _, variant := range image.variants {
367		if variant.target.Os == android.Android {
368			return variant
369		}
370	}
371	return nil
372}
373
374// Return the name of a boot image module given a boot image config and a component (module) index.
375// A module name is a combination of the Java library name, and the boot image stem (that is stored
376// in the config).
377func (image bootImageConfig) moduleName(ctx android.PathContext, idx int) string {
378	// The first module of the primary boot image is special: its module name has only the stem, but
379	// not the library name. All other module names are of the form <stem>-<library name>
380	m := image.modules.Jar(idx)
381	name := image.stem
382	if idx != 0 || image.extends != nil {
383		name += "-" + android.ModuleStem(ctx.Config(), image.modules.Apex(idx), m)
384	}
385	return name
386}
387
388// Return the name of the first boot image module, or stem if the list of modules is empty.
389func (image bootImageConfig) firstModuleNameOrStem(ctx android.PathContext) string {
390	if image.modules.Len() > 0 {
391		return image.moduleName(ctx, 0)
392	} else {
393		return image.stem
394	}
395}
396
397// Return filenames for the given boot image component, given the output directory and a list of
398// extensions.
399func (image bootImageConfig) moduleFiles(ctx android.PathContext, dir android.OutputPath, exts ...string) android.OutputPaths {
400	ret := make(android.OutputPaths, 0, image.modules.Len()*len(exts))
401	for i := 0; i < image.modules.Len(); i++ {
402		name := image.moduleName(ctx, i)
403		for _, ext := range exts {
404			ret = append(ret, dir.Join(ctx, name+ext))
405		}
406		if image.singleImage {
407			break
408		}
409	}
410	return ret
411}
412
413// apexVariants returns a list of all *bootImageVariant that could be included in an apex.
414func (image *bootImageConfig) apexVariants() []*bootImageVariant {
415	variants := []*bootImageVariant{}
416	for _, variant := range image.variants {
417		// We also generate boot images for host (for testing), but we don't need those in the apex.
418		// TODO(b/177892522) - consider changing this to check Os.OsClass = android.Device
419		if variant.target.Os == android.Android {
420			variants = append(variants, variant)
421		}
422	}
423	return variants
424}
425
426// Return boot image locations (as a list of symbolic paths).
427//
428// The image "location" is a symbolic path that, with multiarchitecture support, doesn't really
429// exist on the device. Typically it is /apex/com.android.art/javalib/boot.art and should be the
430// same for all supported architectures on the device. The concrete architecture specific files
431// actually end up in architecture-specific sub-directory such as arm, arm64, x86, or x86_64.
432//
433// For example a physical file /apex/com.android.art/javalib/x86/boot.art has "image location"
434// /apex/com.android.art/javalib/boot.art (which is not an actual file).
435//
436// For a primary boot image the list of locations has a single element.
437//
438// For a boot image extension the list of locations contains a location for all dependency images
439// (including the primary image) and the location of the extension itself. For example, for the
440// Framework boot image extension that depends on the primary ART boot image the list contains two
441// elements.
442//
443// The location is passed as an argument to the ART tools like dex2oat instead of the real path.
444// ART tools will then reconstruct the architecture-specific real path.
445func (image *bootImageVariant) imageLocations() (imageLocationsOnHost []string, imageLocationsOnDevice []string) {
446	if image.extends != nil {
447		imageLocationsOnHost, imageLocationsOnDevice = image.extends.getVariant(image.target).imageLocations()
448	}
449	return append(imageLocationsOnHost, dexpreopt.PathToLocation(image.imagePathOnHost, image.target.Arch.ArchType)),
450		append(imageLocationsOnDevice, dexpreopt.PathStringToLocation(image.imagePathOnDevice, image.target.Arch.ArchType))
451}
452
453func (image *bootImageConfig) isProfileGuided() bool {
454	return image.compilerFilter == "speed-profile"
455}
456
457func (image *bootImageConfig) isEnabled(ctx android.BaseModuleContext) bool {
458	return ctx.OtherModuleExists(image.enabledIfExists)
459}
460
461func dexpreoptBootJarsFactory() android.SingletonModule {
462	m := &dexpreoptBootJars{}
463	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
464	return m
465}
466
467func RegisterDexpreoptBootJarsComponents(ctx android.RegistrationContext) {
468	ctx.RegisterParallelSingletonModuleType("dex_bootjars", dexpreoptBootJarsFactory)
469	ctx.RegisterModuleType("art_boot_images", artBootImagesFactory)
470}
471
472func SkipDexpreoptBootJars(ctx android.PathContext) bool {
473	global := dexpreopt.GetGlobalConfig(ctx)
474	return global.DisablePreoptBootImages || !shouldBuildBootImages(ctx.Config(), global)
475}
476
477// Singleton module for generating boot image build rules.
478type dexpreoptBootJars struct {
479	android.SingletonModuleBase
480
481	// Default boot image config (currently always the Framework boot image extension). It should be
482	// noted that JIT-Zygote builds use ART APEX image instead of the Framework boot image extension,
483	// but the switch is handled not here, but in the makefiles (triggered with
484	// DEXPREOPT_USE_ART_IMAGE=true).
485	defaultBootImage *bootImageConfig
486
487	// Other boot image configs (currently the list contains only the primary ART APEX image. It
488	// used to contain an experimental JIT-Zygote image (now replaced with the ART APEX image). In
489	// the future other boot image extensions may be added.
490	otherImages []*bootImageConfig
491
492	// Build path to a config file that Soong writes for Make (to be used in makefiles that install
493	// the default boot image).
494	dexpreoptConfigForMake android.WritablePath
495
496	// Build path to the boot framework profile.
497	// This is used as the `OutputFile` in `AndroidMkEntries`.
498	// A non-nil value ensures that this singleton module does not get skipped in AndroidMkEntries processing.
499	bootFrameworkProfile android.WritablePath
500}
501
502func (dbj *dexpreoptBootJars) DepsMutator(ctx android.BottomUpMutatorContext) {
503	// Create a dependency on all_apex_contributions to determine the selected mainline module
504	ctx.AddDependency(ctx.Module(), apexContributionsMetadataDepTag, "all_apex_contributions")
505
506	if dexpreopt.IsDex2oatNeeded(ctx) {
507		// Add a dependency onto the dex2oat tool which is needed for creating the boot image. The
508		// path is retrieved from the dependency by GetGlobalSoongConfig(ctx).
509		dexpreopt.RegisterToolDeps(ctx)
510	}
511
512	imageConfigs := genBootImageConfigs(ctx)
513	for _, config := range imageConfigs {
514		if !config.isEnabled(ctx) {
515			continue
516		}
517		// For accessing the boot jars.
518		addDependenciesOntoBootImageModules(ctx, config.modules, dexpreoptBootJar)
519		// Create a dependency on the apex selected using RELEASE_APEX_CONTRIBUTIONS_*
520		// TODO: b/308174306 - Remove the direct depedendency edge to the java_library (source/prebuilt) once all mainline modules
521		// have been flagged using RELEASE_APEX_CONTRIBUTIONS_*
522		apexes := []string{}
523		for i := 0; i < config.modules.Len(); i++ {
524			apexes = append(apexes, config.modules.Apex(i))
525		}
526		addDependenciesOntoSelectedBootImageApexes(ctx, android.FirstUniqueStrings(apexes)...)
527	}
528
529	if ctx.OtherModuleExists("platform-bootclasspath") {
530		// For accessing all bootclasspath fragments.
531		addDependencyOntoApexModulePair(ctx, "platform", "platform-bootclasspath", platform)
532	} else if ctx.OtherModuleExists("art-bootclasspath-fragment") {
533		// For accessing the ART bootclasspath fragment on a thin manifest (e.g., master-art) where
534		// platform-bootclasspath doesn't exist.
535		addDependencyOntoApexModulePair(ctx, "com.android.art", "art-bootclasspath-fragment", fragment)
536	}
537}
538
539// Create a dependency from dex_bootjars to the specific apexes selected using all_apex_contributions
540// This dependency will be used to get the path to the deapexed dex boot jars and profile (via a provider)
541func addDependenciesOntoSelectedBootImageApexes(ctx android.BottomUpMutatorContext, apexes ...string) {
542	psi := android.PrebuiltSelectionInfoMap{}
543	ctx.VisitDirectDepsWithTag(apexContributionsMetadataDepTag, func(am android.Module) {
544		if info, exists := android.OtherModuleProvider(ctx, am, android.PrebuiltSelectionInfoProvider); exists {
545			psi = info
546		}
547	})
548	for _, apex := range apexes {
549		for _, selected := range psi.GetSelectedModulesForApiDomain(apex) {
550			// We need to add a dep on only the apex listed in `contents` of the selected apex_contributions module
551			// This is not available in a structured format in `apex_contributions`, so this hack adds a dep on all `contents`
552			// (some modules like art.module.public.api do not have an apex variation since it is a pure stub module that does not get installed)
553			tag := bootclasspathDependencyTag{
554				typ: dexpreoptBootJar,
555			}
556
557			dep := android.RemoveOptionalPrebuiltPrefix(selected)
558			if ctx.OtherModuleDependencyVariantExists(ctx.Target().Variations(), dep) {
559				ctx.AddFarVariationDependencies(ctx.Target().Variations(), tag, dep)
560			}
561		}
562	}
563}
564
565func gatherBootclasspathFragments(ctx android.ModuleContext) map[string]android.Module {
566	return ctx.Config().Once(dexBootJarsFragmentsKey, func() interface{} {
567		fragments := make(map[string]android.Module)
568
569		type moduleInApexPair struct {
570			module string
571			apex   string
572		}
573
574		var modulesInApexes []moduleInApexPair
575
576		// Find the list of modules in apexes.
577		ctx.WalkDeps(func(child, parent android.Module) bool {
578			if !isActiveModule(ctx, child) {
579				return false
580			}
581			tag := ctx.OtherModuleDependencyTag(child)
582			if bcpTag, ok := tag.(bootclasspathDependencyTag); ok {
583				if bcpTag.typ == platform {
584					return true
585				}
586				if bcpTag.typ == fragment {
587					if bcpTag.moduleInApex == "" {
588						panic(fmt.Errorf("expected fragment to be in apex"))
589					}
590					modulesInApexes = append(modulesInApexes, moduleInApexPair{bcpTag.moduleInApex, ctx.OtherModuleName(child)})
591					return true
592				}
593			}
594			return false
595		})
596
597		for _, moduleInApex := range modulesInApexes {
598			// Find a desired module in an apex.
599			ctx.WalkDeps(func(child, parent android.Module) bool {
600				t := ctx.OtherModuleDependencyTag(child)
601				if bcpTag, ok := t.(bootclasspathDependencyTag); ok {
602					if bcpTag.typ == platform {
603						return true
604					}
605					if bcpTag.typ == fragment && ctx.OtherModuleName(child) == moduleInApex.apex {
606						// This is the dependency from this module to the apex, recurse into it.
607						return true
608					}
609				} else if android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(child)) == moduleInApex.module {
610					// This is the desired module inside the apex.
611					fragments[android.RemoveOptionalPrebuiltPrefix(moduleInApex.apex)] = child
612				}
613				return false
614			})
615		}
616
617		return fragments
618	}).(map[string]android.Module)
619}
620
621func getBootclasspathFragmentByApex(ctx android.ModuleContext, apexName string) android.Module {
622	return gatherBootclasspathFragments(ctx)[apexName]
623}
624
625// GenerateAndroidBuildActions generates the build rules for boot images.
626func (d *dexpreoptBootJars) GenerateAndroidBuildActions(ctx android.ModuleContext) {
627	imageConfigs := genBootImageConfigs(ctx)
628	d.defaultBootImage = defaultBootImageConfig(ctx)
629	d.otherImages = make([]*bootImageConfig, 0, len(imageConfigs)-1)
630	var profileInstalls android.RuleBuilderInstalls
631	var artBootImageHostInstalls android.RuleBuilderInstalls
632	for _, name := range getImageNames() {
633		config := imageConfigs[name]
634		if config != d.defaultBootImage {
635			d.otherImages = append(d.otherImages, config)
636		}
637		if !config.isEnabled(ctx) {
638			continue
639		}
640		installs := generateBootImage(ctx, config)
641		profileInstalls = append(profileInstalls, installs...)
642		if config == d.defaultBootImage {
643			bootProfile, installs := bootFrameworkProfileRule(ctx, config)
644			d.bootFrameworkProfile = bootProfile
645			profileInstalls = append(profileInstalls, installs...)
646		}
647		// Gather the install files of the host variant of the ART boot image.
648		// These installed files will be used in ART tests.
649		if config.name == "art" {
650			for _, variant := range config.variants {
651				if variant.target.Os != ctx.Config().BuildOS {
652					// not a host variant
653					continue
654				}
655				artBootImageHostInstalls = append(artBootImageHostInstalls, variant.installs...)
656				artBootImageHostInstalls = append(artBootImageHostInstalls, variant.vdexInstalls...)
657			}
658		}
659	}
660	if len(profileInstalls) > 0 {
661		android.SetProvider(ctx, profileInstallInfoProvider, profileInstallInfo{
662			profileInstalls:            profileInstalls,
663			profileLicenseMetadataFile: android.OptionalPathForPath(ctx.LicenseMetadataFile()),
664		})
665		for _, install := range profileInstalls {
666			installFile(ctx, install)
667		}
668	}
669	// Set a provider containing the install files of the host variant of the ART boot image.
670	// The actual install rules will be created by `art_boot_images`
671	android.SetProvider(
672		ctx,
673		artBootImageHostInfoProvider,
674		artBootImageHostInfo{
675			installs: artBootImageHostInstalls,
676		},
677	)
678
679	d.buildBootZip(ctx)
680}
681
682// Build the boot.zip which contains the boot jars and their compilation output
683// We can do this only if preopt is enabled and if the product uses libart config (which sets the
684// default properties for preopting).
685// Origionally, this was only for ART Cloud.
686func (d *dexpreoptBootJars) buildBootZip(ctx android.ModuleContext) {
687	image := d.defaultBootImage
688	if image == nil || SkipDexpreoptBootJars(ctx) {
689		return
690	}
691	global := dexpreopt.GetGlobalConfig(ctx)
692	globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
693	if global.DisablePreopt || global.OnlyPreoptArtBootImage {
694		return
695	}
696
697	bootclasspathDexFiles, bootclassPathLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp)
698	if len(bootclasspathDexFiles) == 0 {
699		return
700	}
701
702	systemServerDexjarsDir := android.PathForOutput(ctx, dexpreopt.SystemServerDexjarsDir)
703
704	bootZipMetadataTmp := android.PathForModuleOut(ctx, "boot_zip", "METADATA.txt.tmp")
705	bootZipMetadata := android.PathForModuleOut(ctx, "boot_zip", "METADATA.txt")
706	newlineFile := android.PathForModuleOut(ctx, "boot_zip", "newline.txt")
707	android.WriteFileRule(ctx, newlineFile, "")
708
709	dexPreoptRootDir := filepath.Dir(filepath.Dir(bootclasspathDexFiles[0].String()))
710
711	var sb strings.Builder
712	sb.WriteString("bootclasspath = ")
713	for i, bootclasspathJar := range bootclasspathDexFiles {
714		if i > 0 {
715			sb.WriteString(":")
716		}
717		rel, err := filepath.Rel(dexPreoptRootDir, bootclasspathJar.String())
718		if err != nil {
719			ctx.ModuleErrorf("All dexpreopt jars should be under the same rootdir %q, but %q wasn't.", dexPreoptRootDir, bootclasspathJar)
720		} else {
721			sb.WriteString(rel)
722		}
723	}
724	sb.WriteString("\nbootclasspath-locations = ")
725	for i, bootclasspathLocation := range bootclassPathLocations {
726		if i > 0 {
727			sb.WriteString(":")
728		}
729		sb.WriteString(bootclasspathLocation)
730	}
731	sb.WriteString("\nboot-image = ")
732
733	// Infix can be 'art' (ART image for testing), 'boot' (primary), or 'mainline' (mainline
734	// extension). Soong creates a set of variables for Make, one or each boot image. The only
735	// reason why the ART image is exposed to Make is testing (art gtests) and benchmarking (art
736	// golem benchmarks). Install rules that use those variables are in dex_preopt_libart.mk. Here
737	// for dexpreopt purposes the infix is always 'boot' or 'mainline'.
738	dexpreoptInfix := "boot"
739	if global.PreoptWithUpdatableBcp {
740		dexpreoptInfix = "mainline"
741	}
742
743	var dexPreoptImageZipBoot android.Path
744	var dexPreoptImageZipArt android.Path
745	var dexPreoptImageZipMainline android.Path
746	for _, current := range append(d.otherImages, image) {
747		if current.name == dexpreoptInfix {
748			_, imageLocationsOnDevice := current.getAnyAndroidVariant().imageLocations()
749			for i, location := range imageLocationsOnDevice {
750				imageLocationsOnDevice[i] = strings.TrimPrefix(location, "/")
751			}
752			sb.WriteString(strings.Join(imageLocationsOnDevice, ":"))
753		}
754		switch current.name {
755		case "boot":
756			dexPreoptImageZipBoot = current.zip
757		case "art":
758			dexPreoptImageZipArt = current.zip
759		case "mainline":
760			dexPreoptImageZipMainline = current.zip
761		}
762	}
763	sb.WriteString("\nextra-args = ")
764	android.WriteFileRuleVerbatim(ctx, bootZipMetadataTmp, sb.String())
765	ctx.Build(pctx, android.BuildParams{
766		Rule: android.Cat,
767		Inputs: []android.Path{
768			bootZipMetadataTmp,
769			globalSoong.UffdGcFlag,
770			newlineFile,
771		},
772		Output: bootZipMetadata,
773	})
774
775	bootZipFirstPart := android.PathForModuleOut(ctx, "boot_zip", "boot_first_part.zip")
776	bootZip := android.PathForModuleOut(ctx, "boot_zip", "boot.zip")
777	builder := android.NewRuleBuilder(pctx, ctx)
778	cmd := builder.Command().BuiltTool("soong_zip").
779		FlagWithOutput("-o ", bootZipFirstPart).
780		FlagWithArg("-C ", filepath.Dir(filepath.Dir(bootclasspathDexFiles[0].String())))
781	for _, bootclasspathJar := range bootclasspathDexFiles {
782		cmd.FlagWithInput("-f ", bootclasspathJar)
783	}
784	for i := range global.SystemServerJars.Len() {
785		// Use "/system" path for JARs with "platform:" prefix. These JARs counterintuitively use
786		// "platform" prefix but they will be actually installed to /system partition.
787		// For the remaining system server JARs use the partition signified by the prefix.
788		// For example, prefix "system_ext:" will use "/system_ext" path.
789		dir := global.SystemServerJars.Apex(i)
790		if dir == "platform" {
791			dir = "system"
792		}
793		jar := global.SystemServerJars.Jar(i) + ".jar"
794		cmd.FlagWithArg("-e ", dir+"/framework/"+jar)
795		cmd.FlagWithInput("-f ", systemServerDexjarsDir.Join(ctx, jar))
796	}
797	cmd.Flag("-j")
798	cmd.FlagWithInput("-f ", bootZipMetadata)
799
800	builder.Command().BuiltTool("merge_zips").
801		Output(bootZip).
802		Input(bootZipFirstPart).
803		Input(dexPreoptImageZipBoot).
804		Input(dexPreoptImageZipArt).
805		Input(dexPreoptImageZipMainline)
806
807	builder.Build("boot_zip", "build boot.zip")
808
809	ctx.DistForGoal("droidcore", bootZipMetadata)
810	ctx.DistForGoal("droidcore", bootZip)
811}
812
813// GenerateSingletonBuildActions generates build rules for the dexpreopt config for Make.
814func (d *dexpreoptBootJars) GenerateSingletonBuildActions(ctx android.SingletonContext) {
815	d.dexpreoptConfigForMake =
816		android.PathForOutput(ctx, dexpreopt.GetDexpreoptDirName(ctx), "dexpreopt.config")
817	writeGlobalConfigForMake(ctx, d.dexpreoptConfigForMake)
818}
819
820// shouldBuildBootImages determines whether boot images should be built.
821func shouldBuildBootImages(config android.Config, global *dexpreopt.GlobalConfig) bool {
822	// Skip recompiling the boot image for the second sanitization phase. We'll get separate paths
823	// and invalidate first-stage artifacts which are crucial to SANITIZE_LITE builds.
824	// Note: this is technically incorrect. Compiled code contains stack checks which may depend
825	//       on ASAN settings.
826	if len(config.SanitizeDevice()) == 1 && config.SanitizeDevice()[0] == "address" && global.SanitizeLite {
827		return false
828	}
829	return true
830}
831
832func generateBootImage(ctx android.ModuleContext, imageConfig *bootImageConfig) android.RuleBuilderInstalls {
833	apexJarModulePairs := getModulesForImage(ctx, imageConfig)
834
835	// Copy module dex jars to their predefined locations.
836	bootDexJarsByModule := extractEncodedDexJarsFromModulesOrBootclasspathFragments(ctx, apexJarModulePairs)
837	copyBootJarsToPredefinedLocations(ctx, bootDexJarsByModule, imageConfig.dexPathsByModule)
838
839	// Build a profile for the image config from the profile at the default path. The profile will
840	// then be used along with profiles imported from APEXes to build the boot image.
841	profile, profileInstalls := bootImageProfileRule(ctx, imageConfig)
842
843	// If dexpreopt of boot image jars should be skipped, stop after generating a profile.
844	global := dexpreopt.GetGlobalConfig(ctx)
845	if SkipDexpreoptBootJars(ctx) || (global.OnlyPreoptArtBootImage && imageConfig.name != "art") {
846		return profileInstalls
847	}
848
849	// Build boot image files for the android variants.
850	androidBootImageFiles := buildBootImageVariantsForAndroidOs(ctx, imageConfig, profile)
851
852	// Zip the android variant boot image files up.
853	buildBootImageZipInPredefinedLocation(ctx, imageConfig, androidBootImageFiles.byArch)
854
855	// Build boot image files for the host variants. There are use directly by ART host side tests.
856	buildBootImageVariantsForBuildOs(ctx, imageConfig, profile)
857
858	// Create a `dump-oat-<image-name>` rule that runs `oatdump` for debugging purposes.
859	dumpOatRules(ctx, imageConfig)
860
861	return profileInstalls
862}
863
864type apexJarModulePair struct {
865	apex      string
866	jarModule android.Module
867}
868
869func getModulesForImage(ctx android.ModuleContext, imageConfig *bootImageConfig) []apexJarModulePair {
870	modules := make([]apexJarModulePair, 0, imageConfig.modules.Len())
871	for i := 0; i < imageConfig.modules.Len(); i++ {
872		found := false
873		dexpreoptBootJarModules, _ := gatherApexModulePairDepsWithTag(ctx, dexpreoptBootJar)
874		for _, module := range dexpreoptBootJarModules {
875			name := android.RemoveOptionalPrebuiltPrefix(module.Name())
876			if name == imageConfig.modules.Jar(i) {
877				modules = append(modules, apexJarModulePair{
878					apex:      imageConfig.modules.Apex(i),
879					jarModule: module,
880				})
881				found = true
882				break
883			}
884		}
885		if !found && !ctx.Config().AllowMissingDependencies() {
886			ctx.ModuleErrorf(
887				"Boot image '%s' module '%s' not added as a dependency of dex_bootjars",
888				imageConfig.name,
889				imageConfig.modules.Jar(i))
890			return []apexJarModulePair{}
891		}
892	}
893	return modules
894}
895
896// extractEncodedDexJarsFromModulesOrBootclasspathFragments gets the hidden API encoded dex jars for
897// the given modules.
898func extractEncodedDexJarsFromModulesOrBootclasspathFragments(ctx android.ModuleContext, apexJarModulePairs []apexJarModulePair) bootDexJarByModule {
899	apexNameToApexExportInfoMap := getApexNameToApexExportsInfoMap(ctx)
900	encodedDexJarsByModuleName := bootDexJarByModule{}
901	for _, pair := range apexJarModulePairs {
902		dexJarPath := getDexJarForApex(ctx, pair, apexNameToApexExportInfoMap)
903		encodedDexJarsByModuleName.addPath(pair.jarModule, dexJarPath)
904	}
905	return encodedDexJarsByModuleName
906}
907
908type apexNameToApexExportsInfoMap map[string]android.ApexExportsInfo
909
910// javaLibraryPathOnHost returns the path to the java library which is exported by the apex for hiddenapi and dexpreopt and a boolean indicating whether the java library exists
911// For prebuilt apexes, this is created by deapexing the prebuilt apex
912func (m *apexNameToApexExportsInfoMap) javaLibraryDexPathOnHost(ctx android.ModuleContext, apex string, javalib string) (android.Path, bool) {
913	if info, exists := (*m)[apex]; exists {
914		if dex, exists := info.LibraryNameToDexJarPathOnHost[javalib]; exists {
915			return dex, true
916		} else {
917			ctx.ModuleErrorf("Apex %s does not provide a dex boot jar for library %s\n", apex, javalib)
918		}
919	}
920	// An apex entry could not be found. Return false.
921	// TODO: b/308174306 - When all the mainline modules have been flagged, make this a hard error
922	return nil, false
923}
924
925// Returns the stem of an artifact inside a prebuilt apex
926func ModuleStemForDeapexing(m android.Module) string {
927	bmn, _ := m.(interface{ BaseModuleName() string })
928	return bmn.BaseModuleName()
929}
930
931// Returns the java libraries exported by the apex for hiddenapi and dexpreopt
932// This information can come from two mechanisms
933// 1. New: Direct deps to _selected_ apexes. The apexes return a ApexExportsInfo
934// 2. Legacy: An edge to java_library or java_import (java_sdk_library) module. For prebuilt apexes, this serves as a hook and is populated by deapexers of prebuilt apxes
935// TODO: b/308174306 - Once all mainline modules have been flagged, drop (2)
936func getDexJarForApex(ctx android.ModuleContext, pair apexJarModulePair, apexNameToApexExportsInfoMap apexNameToApexExportsInfoMap) android.Path {
937	if dex, found := apexNameToApexExportsInfoMap.javaLibraryDexPathOnHost(ctx, pair.apex, ModuleStemForDeapexing(pair.jarModule)); found {
938		return dex
939	}
940	// TODO: b/308174306 - Remove the legacy mechanism
941	if android.IsConfiguredJarForPlatform(pair.apex) || android.IsModulePrebuilt(pair.jarModule) {
942		// This gives us the dex jar with the hidden API flags encoded from the monolithic hidden API
943		// files or the dex jar extracted from a prebuilt APEX. We can't use this for a boot jar for
944		// a source APEX because there is no guarantee that it is the same as the jar packed into the
945		// APEX. In practice, they are the same when we are building from a full source tree, but they
946		// are different when we are building from a thin manifest (e.g., master-art), where there is
947		// no monolithic hidden API files at all.
948		return retrieveEncodedBootDexJarFromModule(ctx, pair.jarModule)
949	} else {
950		// Use exactly the same jar that is packed into the APEX.
951		fragment := getBootclasspathFragmentByApex(ctx, pair.apex)
952		if fragment == nil {
953			ctx.ModuleErrorf("Boot jar '%[1]s' is from APEX '%[2]s', but a bootclasspath_fragment for "+
954				"APEX '%[2]s' doesn't exist or is not added as a dependency of dex_bootjars",
955				pair.jarModule.Name(),
956				pair.apex)
957			return nil
958		}
959		bootclasspathFragmentInfo, _ := android.OtherModuleProvider(ctx, fragment, BootclasspathFragmentApexContentInfoProvider)
960		jar, err := bootclasspathFragmentInfo.DexBootJarPathForContentModule(pair.jarModule)
961		if err != nil {
962			if ctx.Config().AllowMissingDependencies() {
963				ctx.AddMissingDependencies([]string{pair.jarModule.String()})
964			} else {
965				ctx.ModuleErrorf("%s", err)
966			}
967		}
968		return jar
969	}
970	return nil
971}
972
973// copyBootJarsToPredefinedLocations generates commands that will copy boot jars to predefined
974// paths in the global config.
975func copyBootJarsToPredefinedLocations(ctx android.ModuleContext, srcBootDexJarsByModule bootDexJarByModule, dstBootJarsByModule map[string]android.WritablePath) {
976	// Create the super set of module names.
977	names := []string{}
978	names = append(names, android.SortedKeys(srcBootDexJarsByModule)...)
979	names = append(names, android.SortedKeys(dstBootJarsByModule)...)
980	names = android.SortedUniqueStrings(names)
981	for _, name := range names {
982		src := srcBootDexJarsByModule[name]
983		dst := dstBootJarsByModule[name]
984
985		if src == nil {
986			// A dex boot jar should be provided by the source java module. It needs to be installable or
987			// have compile_dex=true - cf. assignments to java.Module.dexJarFile.
988			//
989			// However, the source java module may be either replaced or overridden (using prefer:true) by
990			// a prebuilt java module with the same name. In that case the dex boot jar needs to be
991			// provided by the corresponding prebuilt APEX module. That APEX is the one that refers
992			// through a exported_(boot|systemserver)classpath_fragments property to a
993			// prebuilt_(boot|systemserver)classpath_fragment module, which in turn lists the prebuilt
994			// java module in the contents property. If that chain is broken then this dependency will
995			// fail.
996			if !ctx.Config().AllowMissingDependencies() {
997				ctx.ModuleErrorf("module %s does not provide a dex boot jar (see comment next to this message in Soong for details)", name)
998			} else {
999				ctx.AddMissingDependencies([]string{name})
1000			}
1001		} else if dst == nil {
1002			ctx.ModuleErrorf("module %s is not part of the boot configuration", name)
1003		} else {
1004			ctx.Build(pctx, android.BuildParams{
1005				Rule:   android.Cp,
1006				Input:  src,
1007				Output: dst,
1008			})
1009		}
1010	}
1011}
1012
1013// buildBootImageVariantsForAndroidOs generates rules to build the boot image variants for the
1014// android.Android OsType and returns a map from the architectures to the paths of the generated
1015// boot image files.
1016//
1017// The paths are returned because they are needed elsewhere in Soong, e.g. for populating an APEX.
1018func buildBootImageVariantsForAndroidOs(ctx android.ModuleContext, image *bootImageConfig, profile android.WritablePath) bootImageOutputs {
1019	return buildBootImageForOsType(ctx, image, profile, android.Android)
1020}
1021
1022// buildBootImageVariantsForBuildOs generates rules to build the boot image variants for the
1023// config.BuildOS OsType, i.e. the type of OS on which the build is being running.
1024//
1025// The files need to be generated into their predefined location because they are used from there
1026// both within Soong and outside, e.g. for ART based host side testing and also for use by some
1027// cloud based tools. However, they are not needed by callers of this function and so the paths do
1028// not need to be returned from this func, unlike the buildBootImageVariantsForAndroidOs func.
1029func buildBootImageVariantsForBuildOs(ctx android.ModuleContext, image *bootImageConfig, profile android.WritablePath) {
1030	buildBootImageForOsType(ctx, image, profile, ctx.Config().BuildOS)
1031}
1032
1033// bootImageFilesByArch is a map from android.ArchType to the paths to the boot image files.
1034//
1035// The paths include the .art, .oat and .vdex files, one for each of the modules from which the boot
1036// image is created.
1037type bootImageFilesByArch map[android.ArchType]android.Paths
1038
1039// bootImageOutputs encapsulates information about boot images that were created/obtained by
1040// commonBootclasspathFragment.produceBootImageFiles.
1041type bootImageOutputs struct {
1042	// Map from arch to the paths to the boot image files created/obtained for that arch.
1043	byArch bootImageFilesByArch
1044
1045	variants []bootImageVariantOutputs
1046
1047	// The path to the profile file created/obtained for the boot image.
1048	profile android.WritablePath
1049}
1050
1051// buildBootImageForOsType takes a bootImageConfig, a profile file and an android.OsType
1052// boot image files are required for and it creates rules to build the boot image
1053// files for all the required architectures for them.
1054//
1055// It returns a map from android.ArchType to the predefined paths of the boot image files.
1056func buildBootImageForOsType(ctx android.ModuleContext, image *bootImageConfig, profile android.WritablePath, requiredOsType android.OsType) bootImageOutputs {
1057	filesByArch := bootImageFilesByArch{}
1058	imageOutputs := bootImageOutputs{
1059		byArch:  filesByArch,
1060		profile: profile,
1061	}
1062	for _, variant := range image.variants {
1063		if variant.target.Os == requiredOsType {
1064			variantOutputs := buildBootImageVariant(ctx, variant, profile)
1065			imageOutputs.variants = append(imageOutputs.variants, variantOutputs)
1066			filesByArch[variant.target.Arch.ArchType] = variant.imagesDeps.Paths()
1067		}
1068	}
1069
1070	return imageOutputs
1071}
1072
1073// buildBootImageZipInPredefinedLocation generates a zip file containing all the boot image files.
1074//
1075// The supplied filesByArch is nil when the boot image files have not been generated. Otherwise, it
1076// is a map from android.ArchType to the predefined locations.
1077func buildBootImageZipInPredefinedLocation(ctx android.ModuleContext, image *bootImageConfig, filesByArch bootImageFilesByArch) {
1078	if filesByArch == nil {
1079		return
1080	}
1081
1082	// Compute the list of files from all the architectures.
1083	zipFiles := android.Paths{}
1084	for _, archType := range android.ArchTypeList() {
1085		zipFiles = append(zipFiles, filesByArch[archType]...)
1086	}
1087
1088	rule := android.NewRuleBuilder(pctx, ctx)
1089	rule.Command().
1090		BuiltTool("soong_zip").
1091		FlagWithOutput("-o ", image.zip).
1092		FlagWithArg("-C ", image.dir.Join(ctx, android.Android.String()).String()).
1093		FlagWithInputList("-f ", zipFiles, " -f ")
1094
1095	rule.Build("zip_"+image.name, "zip "+image.name+" image")
1096}
1097
1098type bootImageVariantOutputs struct {
1099	config *bootImageVariant
1100}
1101
1102// Returns the profile file for an apex
1103// This information can come from two mechanisms
1104// 1. New: Direct deps to _selected_ apexes. The apexes return a BootclasspathFragmentApexContentInfo
1105// 2. Legacy: An edge to bootclasspath_fragment module. For prebuilt apexes, this serves as a hook and is populated by deapexers of prebuilt apxes
1106// TODO: b/308174306 - Once all mainline modules have been flagged, drop (2)
1107func getProfilePathForApex(ctx android.ModuleContext, apexName string, apexNameToBcpInfoMap map[string]android.ApexExportsInfo) android.Path {
1108	if info, exists := apexNameToBcpInfoMap[apexName]; exists {
1109		return info.ProfilePathOnHost
1110	}
1111	// TODO: b/308174306 - Remove the legacy mechanism
1112	fragment := getBootclasspathFragmentByApex(ctx, apexName)
1113	if fragment == nil {
1114		ctx.ModuleErrorf("Boot image config imports profile from '%[2]s', but a "+
1115			"bootclasspath_fragment for APEX '%[2]s' doesn't exist or is not added as a "+
1116			"dependency of dex_bootjars",
1117			apexName)
1118		return nil
1119	}
1120	return fragment.(commonBootclasspathFragment).getProfilePath()
1121}
1122
1123func getApexNameToApexExportsInfoMap(ctx android.ModuleContext) apexNameToApexExportsInfoMap {
1124	apexNameToApexExportsInfoMap := apexNameToApexExportsInfoMap{}
1125
1126	ctx.VisitDirectDeps(func(am android.Module) {
1127		tag := ctx.OtherModuleDependencyTag(am)
1128		if bcpTag, ok := tag.(bootclasspathDependencyTag); ok && bcpTag.typ == dexpreoptBootJar {
1129			if bcpTag.moduleInApex == "" {
1130				info, exists := android.OtherModuleProvider(ctx, am, android.ApexExportsInfoProvider)
1131				if exists {
1132					apexNameToApexExportsInfoMap[info.ApexName] = info
1133				}
1134			}
1135		}
1136	})
1137	return apexNameToApexExportsInfoMap
1138}
1139
1140func packageFileForTargetImage(ctx android.ModuleContext, image *bootImageVariant) {
1141	if image.target.Os != ctx.Os() {
1142		// This is not for the target device.
1143		return
1144	}
1145
1146	for _, install := range image.installs {
1147		installFile(ctx, install)
1148	}
1149
1150	for _, install := range image.vdexInstalls {
1151		installPath, relDir, name := getModuleInstallPathInfo(ctx, install.To)
1152		if name == "" {
1153			continue
1154		}
1155		// Note that the vdex files are identical between architectures. Copy the vdex to a no arch directory
1156		// and create symlinks for both the primary and secondary arches.
1157		ctx.InstallSymlink(installPath.Join(ctx, relDir), name, installPath.Join(ctx, "framework", name))
1158		if image.target.Arch.ArchType.Name == ctx.DeviceConfig().DeviceArch() {
1159			// Copy the vdex from the primary arch to the no-arch directory
1160			// e.g. /system/framework/$bootjar.vdex
1161			ctx.InstallFile(installPath.Join(ctx, "framework"), name, install.From)
1162		}
1163	}
1164}
1165
1166var artBootImageHostInfoProvider = blueprint.NewProvider[artBootImageHostInfo]()
1167
1168// artBootImageHostInfo contains the install locations of the host variant of ART boot image
1169// this contains both the primary and secondary arch locations
1170type artBootImageHostInfo struct {
1171	installs android.RuleBuilderInstalls
1172}
1173
1174// Generate boot image build rules for a specific target.
1175func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, profile android.Path) bootImageVariantOutputs {
1176
1177	globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
1178	global := dexpreopt.GetGlobalConfig(ctx)
1179
1180	arch := image.target.Arch.ArchType
1181	os := image.target.Os.String() // We need to distinguish host-x86 and device-x86.
1182	symbolsDir := image.symbolsDir.Join(ctx, os, image.installDir, arch.String())
1183	symbolsFile := symbolsDir.Join(ctx, image.stem+".oat")
1184	outputDir := image.dir.Join(ctx, os, image.installDir, arch.String())
1185	outputPath := outputDir.Join(ctx, image.stem+".oat")
1186	oatLocation := dexpreopt.PathToLocation(outputPath, arch)
1187	imagePath := outputPath.ReplaceExtension(ctx, "art")
1188
1189	rule := android.NewRuleBuilder(pctx, ctx)
1190
1191	rule.Command().Text("mkdir").Flag("-p").Flag(symbolsDir.String())
1192	rule.Command().Text("rm").Flag("-f").
1193		Flag(symbolsDir.Join(ctx, "*.art").String()).
1194		Flag(symbolsDir.Join(ctx, "*.oat").String()).
1195		Flag(symbolsDir.Join(ctx, "*.vdex").String()).
1196		Flag(symbolsDir.Join(ctx, "*.invocation").String())
1197	rule.Command().Text("rm").Flag("-f").
1198		Flag(outputDir.Join(ctx, "*.art").String()).
1199		Flag(outputDir.Join(ctx, "*.oat").String()).
1200		Flag(outputDir.Join(ctx, "*.vdex").String()).
1201		Flag(outputDir.Join(ctx, "*.invocation").String())
1202
1203	cmd := rule.Command()
1204
1205	extraFlags := ctx.Config().Getenv("ART_BOOT_IMAGE_EXTRA_ARGS")
1206	if extraFlags == "" {
1207		// Use ANDROID_LOG_TAGS to suppress most logging by default...
1208		cmd.Text(`ANDROID_LOG_TAGS="*:e"`)
1209	} else {
1210		// ...unless the boot image is generated specifically for testing, then allow all logging.
1211		cmd.Text(`ANDROID_LOG_TAGS="*:v"`)
1212	}
1213
1214	invocationPath := outputPath.ReplaceExtension(ctx, "invocation")
1215
1216	apexNameToApexExportsInfoMap := getApexNameToApexExportsInfoMap(ctx)
1217
1218	cmd.Tool(globalSoong.Dex2oat).
1219		Flag("--avoid-storing-invocation").
1220		FlagWithOutput("--write-invocation-to=", invocationPath).ImplicitOutput(invocationPath).
1221		Flag("--runtime-arg").FlagWithArg("-Xms", global.Dex2oatImageXms).
1222		Flag("--runtime-arg").FlagWithArg("-Xmx", global.Dex2oatImageXmx)
1223
1224	if image.isProfileGuided() && !global.DisableGenerateProfile {
1225		if profile != nil {
1226			cmd.FlagWithInput("--profile-file=", profile)
1227		}
1228
1229		for _, apex := range image.profileImports {
1230			importedProfile := getProfilePathForApex(ctx, apex, apexNameToApexExportsInfoMap)
1231			if importedProfile == nil {
1232				ctx.ModuleErrorf("Boot image config '%[1]s' imports profile from '%[2]s', but '%[2]s' "+
1233					"doesn't provide a profile",
1234					image.name,
1235					apex)
1236				return bootImageVariantOutputs{}
1237			}
1238			cmd.FlagWithInput("--profile-file=", importedProfile)
1239		}
1240	}
1241
1242	dirtyImageFile := "frameworks/base/config/dirty-image-objects"
1243	dirtyImagePath := android.ExistentPathForSource(ctx, dirtyImageFile)
1244	if dirtyImagePath.Valid() {
1245		cmd.FlagWithInput("--dirty-image-objects=", dirtyImagePath.Path())
1246	}
1247
1248	if image.extends != nil {
1249		// It is a boot image extension, so it needs the boot images that it depends on.
1250		baseImageLocations := make([]string, 0, len(image.baseImages))
1251		for _, image := range image.baseImages {
1252			baseImageLocations = append(baseImageLocations, dexpreopt.PathToLocation(image, arch))
1253		}
1254		cmd.
1255			Flag("--runtime-arg").FlagWithInputList("-Xbootclasspath:", image.dexPathsDeps.Paths(), ":").
1256			Flag("--runtime-arg").FlagWithList("-Xbootclasspath-locations:", image.dexLocationsDeps, ":").
1257			// Add the path to the first file in the boot image with the arch specific directory removed,
1258			// dex2oat will reconstruct the path to the actual file when it needs it. As the actual path
1259			// to the file cannot be passed to the command make sure to add the actual path as an Implicit
1260			// dependency to ensure that it is built before the command runs.
1261			FlagWithList("--boot-image=", baseImageLocations, ":").Implicits(image.baseImages.Paths()).
1262			// Similarly, the dex2oat tool will automatically find the paths to other files in the base
1263			// boot image so make sure to add them as implicit dependencies to ensure that they are built
1264			// before this command is run.
1265			Implicits(image.baseImagesDeps)
1266	} else {
1267		// It is a primary image, so it needs a base address.
1268		cmd.FlagWithArg("--base=", ctx.Config().LibartImgDeviceBaseAddress())
1269	}
1270
1271	if len(image.preloadedClassesFile) > 0 {
1272		// We always expect a preloaded classes file to be available. However, if we cannot find it, it's
1273		// OK to not pass the flag to dex2oat.
1274		preloadedClassesPath := android.ExistentPathForSource(ctx, image.preloadedClassesFile)
1275		if preloadedClassesPath.Valid() {
1276			cmd.FlagWithInput("--preloaded-classes=", preloadedClassesPath.Path())
1277		}
1278	}
1279
1280	cmd.
1281		FlagForEachInput("--dex-file=", image.dexPaths.Paths()).
1282		FlagForEachArg("--dex-location=", image.dexLocations).
1283		Flag("--generate-debug-info").
1284		Flag("--generate-build-id").
1285		Flag("--image-format=lz4hc").
1286		FlagWithArg("--oat-symbols=", symbolsFile.String()).
1287		FlagWithArg("--oat-file=", outputPath.String()).
1288		FlagWithArg("--oat-location=", oatLocation).
1289		FlagWithArg("--image=", imagePath.String()).
1290		FlagWithArg("--instruction-set=", arch.String()).
1291		FlagWithArg("--android-root=", global.EmptyDirectory).
1292		FlagWithArg("--no-inline-from=", "core-oj.jar").
1293		Flag("--force-determinism").
1294		Flag("--abort-on-hard-verifier-error")
1295
1296	// We don't strip on host to make perf tools work.
1297	if image.target.Os == android.Android {
1298		cmd.Flag("--strip")
1299	}
1300
1301	// If the image is profile-guided but the profile is disabled, we omit "--compiler-filter" to
1302	// leave the decision to dex2oat to pick the compiler filter.
1303	if !(image.isProfileGuided() && global.DisableGenerateProfile) {
1304		cmd.FlagWithArg("--compiler-filter=", image.compilerFilter)
1305	}
1306
1307	if image.singleImage {
1308		cmd.Flag("--single-image")
1309	}
1310
1311	// Use the default variant/features for host builds.
1312	// The map below contains only device CPU info (which might be x86 on some devices).
1313	if image.target.Os == android.Android {
1314		cmd.FlagWithArg("--instruction-set-variant=", global.CpuVariant[arch])
1315		cmd.FlagWithArg("--instruction-set-features=", global.InstructionSetFeatures[arch])
1316	}
1317
1318	if image.target.Os == android.Android {
1319		cmd.Text("$(cat").Input(globalSoong.UffdGcFlag).Text(")")
1320	}
1321
1322	if global.BootFlags != "" {
1323		cmd.Flag(global.BootFlags)
1324	}
1325
1326	if extraFlags != "" {
1327		cmd.Flag(extraFlags)
1328	}
1329
1330	cmd.Textf(`|| ( echo %s ; false )`, proptools.ShellEscape(failureMessage))
1331
1332	installDir := filepath.Dir(image.imagePathOnDevice)
1333
1334	var vdexInstalls android.RuleBuilderInstalls
1335	var unstrippedInstalls android.RuleBuilderInstalls
1336
1337	for _, artOrOat := range image.moduleFiles(ctx, outputDir, ".art", ".oat") {
1338		cmd.ImplicitOutput(artOrOat)
1339
1340		// Install the .oat and .art files
1341		rule.Install(artOrOat, filepath.Join(installDir, artOrOat.Base()))
1342	}
1343
1344	for _, vdex := range image.moduleFiles(ctx, outputDir, ".vdex") {
1345		cmd.ImplicitOutput(vdex)
1346
1347		// Note that the vdex files are identical between architectures.
1348		// Make rules will create symlinks to share them between architectures.
1349		vdexInstalls = append(vdexInstalls,
1350			android.RuleBuilderInstall{vdex, filepath.Join(installDir, vdex.Base())})
1351	}
1352
1353	for _, unstrippedOat := range image.moduleFiles(ctx, symbolsDir, ".oat") {
1354		cmd.ImplicitOutput(unstrippedOat)
1355
1356		// Install the unstripped oat files.  The Make rules will put these in $(TARGET_OUT_UNSTRIPPED)
1357		unstrippedInstalls = append(unstrippedInstalls,
1358			android.RuleBuilderInstall{unstrippedOat, filepath.Join(installDir, unstrippedOat.Base())})
1359	}
1360
1361	rule.Build(image.name+"JarsDexpreopt_"+image.target.String(), "dexpreopt "+image.name+" jars "+arch.String())
1362
1363	// save output and installed files for makevars
1364	// TODO - these are always the same and so should be initialized in genBootImageConfigs
1365	image.installs = rule.Installs()
1366	image.vdexInstalls = vdexInstalls
1367	image.unstrippedInstalls = unstrippedInstalls
1368	packageFileForTargetImage(ctx, image)
1369
1370	// Only set the licenseMetadataFile from the active module.
1371	if isActiveModule(ctx, ctx.Module()) {
1372		image.licenseMetadataFile = android.OptionalPathForPath(ctx.LicenseMetadataFile())
1373	}
1374
1375	return bootImageVariantOutputs{
1376		image,
1377	}
1378}
1379
1380const failureMessage = `ERROR: Dex2oat failed to compile a boot image.
1381It is likely that the boot classpath is inconsistent.
1382Rebuild with ART_BOOT_IMAGE_EXTRA_ARGS="--runtime-arg -verbose:verifier" to see verification errors.`
1383
1384func bootImageProfileRuleCommon(ctx android.ModuleContext, name string, dexFiles android.Paths, dexLocations []string) android.WritablePath {
1385	globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
1386	global := dexpreopt.GetGlobalConfig(ctx)
1387
1388	if global.DisableGenerateProfile {
1389		return nil
1390	}
1391
1392	defaultProfile := "frameworks/base/boot/boot-image-profile.txt"
1393	// If ART is prebuilt, primarily in next release configs, this will still use
1394	// the profile from source which represent the latest code, so it may not
1395	// correspond to the BCP jars in the prebuilt APEX, but this is the profile we
1396	// have access to.
1397	artProfile := "art/build/boot/boot-image-profile.txt"
1398	extraProfile := "frameworks/base/boot/boot-image-profile-extra.txt"
1399
1400	rule := android.NewRuleBuilder(pctx, ctx)
1401
1402	var profiles android.Paths
1403	if len(global.BootImageProfiles) > 0 {
1404		profiles = append(profiles, global.BootImageProfiles...)
1405	} else if path := android.ExistentPathForSource(ctx, defaultProfile); path.Valid() {
1406		profiles = append(profiles, path.Path())
1407	} else {
1408		// No profile (not even a default one, which is the case on some branches
1409		// like master-art-host that don't have frameworks/base).
1410		// Return nil and continue without profile.
1411		return nil
1412	}
1413	if path := android.ExistentPathForSource(ctx, artProfile); path.Valid() {
1414		profiles = append(profiles, path.Path())
1415	}
1416	if path := android.ExistentPathForSource(ctx, extraProfile); path.Valid() {
1417		profiles = append(profiles, path.Path())
1418	}
1419	bootImageProfile := android.PathForModuleOut(ctx, name, "boot-image-profile.txt")
1420	rule.Command().Text("cat").Inputs(profiles).Text(">").Output(bootImageProfile)
1421
1422	profile := android.PathForModuleOut(ctx, name, "boot.prof")
1423
1424	rule.Command().
1425		Text(`ANDROID_LOG_TAGS="*:e"`).
1426		Tool(globalSoong.Profman).
1427		Flag("--output-profile-type=boot").
1428		FlagWithInput("--create-profile-from=", bootImageProfile).
1429		FlagForEachInput("--apk=", dexFiles).
1430		FlagForEachArg("--dex-location=", dexLocations).
1431		FlagWithOutput("--reference-profile-file=", profile)
1432
1433	rule.Build("bootJarsProfile_"+name, "profile boot jars "+name)
1434
1435	return profile
1436}
1437
1438type profileInstallInfo struct {
1439	// Rules which should be used in make to install the outputs.
1440	profileInstalls android.RuleBuilderInstalls
1441
1442	// Path to the license metadata file for the module that built the profile.
1443	profileLicenseMetadataFile android.OptionalPath
1444}
1445
1446var profileInstallInfoProvider = blueprint.NewProvider[profileInstallInfo]()
1447
1448func bootImageProfileRule(ctx android.ModuleContext, image *bootImageConfig) (android.WritablePath, android.RuleBuilderInstalls) {
1449	if !image.isProfileGuided() {
1450		return nil, nil
1451	}
1452
1453	profile := bootImageProfileRuleCommon(ctx, image.name, image.dexPathsDeps.Paths(), image.getAnyAndroidVariant().dexLocationsDeps)
1454
1455	if image == defaultBootImageConfig(ctx) && profile != nil {
1456		rule := android.NewRuleBuilder(pctx, ctx)
1457		rule.Install(profile, "/system/etc/boot-image.prof")
1458		return profile, rule.Installs()
1459	}
1460	return profile, nil
1461}
1462
1463// bootFrameworkProfileRule generates the rule to create the boot framework profile and
1464// returns a path to the generated file.
1465func bootFrameworkProfileRule(ctx android.ModuleContext, image *bootImageConfig) (android.WritablePath, android.RuleBuilderInstalls) {
1466	globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
1467	global := dexpreopt.GetGlobalConfig(ctx)
1468
1469	if global.DisableGenerateProfile || ctx.Config().UnbundledBuild() {
1470		return nil, nil
1471	}
1472
1473	defaultProfile := "frameworks/base/boot/boot-profile.txt"
1474	bootFrameworkProfile := android.PathForSource(ctx, defaultProfile)
1475
1476	profile := image.dir.Join(ctx, "boot.bprof")
1477
1478	rule := android.NewRuleBuilder(pctx, ctx)
1479	rule.Command().
1480		Text(`ANDROID_LOG_TAGS="*:e"`).
1481		Tool(globalSoong.Profman).
1482		Flag("--output-profile-type=bprof").
1483		FlagWithInput("--create-profile-from=", bootFrameworkProfile).
1484		FlagForEachInput("--apk=", image.dexPathsDeps.Paths()).
1485		FlagForEachArg("--dex-location=", image.getAnyAndroidVariant().dexLocationsDeps).
1486		FlagWithOutput("--reference-profile-file=", profile)
1487
1488	rule.Install(profile, "/system/etc/boot-image.bprof")
1489	rule.Build("bootFrameworkProfile", "profile boot framework jars")
1490	return profile, rule.Installs()
1491}
1492
1493func dumpOatRules(ctx android.ModuleContext, image *bootImageConfig) {
1494	var allPhonies android.Paths
1495	name := image.name
1496	globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
1497	for _, image := range image.variants {
1498		arch := image.target.Arch.ArchType
1499		suffix := arch.String()
1500		// Host and target might both use x86 arch. We need to ensure the names are unique.
1501		if image.target.Os.Class == android.Host {
1502			suffix = "host-" + suffix
1503		}
1504		// Create a rule to call oatdump.
1505		output := android.PathForOutput(ctx, name+"."+suffix+".oatdump.txt")
1506		rule := android.NewRuleBuilder(pctx, ctx)
1507		imageLocationsOnHost, _ := image.imageLocations()
1508
1509		cmd := rule.Command().
1510			BuiltTool("oatdump").
1511			FlagWithInputList("--runtime-arg -Xbootclasspath:", image.dexPathsDeps.Paths(), ":").
1512			FlagWithList("--runtime-arg -Xbootclasspath-locations:", image.dexLocationsDeps, ":").
1513			FlagWithArg("--image=", strings.Join(imageLocationsOnHost, ":")).Implicits(image.imagesDeps.Paths()).
1514			FlagWithOutput("--output=", output).
1515			FlagWithArg("--instruction-set=", arch.String())
1516		if image.target.Os == android.Android {
1517			cmd.Text("$(cat").Input(globalSoong.UffdGcFlag).Text(")")
1518		}
1519		rule.Build("dump-oat-"+name+"-"+suffix, "dump oat "+name+" "+arch.String())
1520
1521		// Create a phony rule that depends on the output file and prints the path.
1522		phony := android.PathForPhony(ctx, "dump-oat-"+name+"-"+suffix)
1523		rule = android.NewRuleBuilder(pctx, ctx)
1524		rule.Command().
1525			Implicit(output).
1526			ImplicitOutput(phony).
1527			Text("echo").FlagWithArg("Output in ", output.String())
1528		rule.Build("phony-dump-oat-"+name+"-"+suffix, "dump oat "+name+" "+arch.String())
1529
1530		allPhonies = append(allPhonies, phony)
1531	}
1532
1533	phony := android.PathForPhony(ctx, "dump-oat-"+name)
1534	ctx.Build(pctx, android.BuildParams{
1535		Rule:        android.Phony,
1536		Output:      phony,
1537		Inputs:      allPhonies,
1538		Description: "dump-oat-" + name,
1539	})
1540}
1541
1542func writeGlobalConfigForMake(ctx android.SingletonContext, path android.WritablePath) {
1543	data := dexpreopt.GetGlobalConfigRawData(ctx)
1544
1545	android.WriteFileRule(ctx, path, string(data))
1546}
1547
1548// Define Make variables for boot image names, paths, etc. These variables are used in makefiles
1549// (make/core/dex_preopt_libart.mk) to generate install rules that copy boot image files to the
1550// correct output directories.
1551func (d *dexpreoptBootJars) MakeVars(ctx android.MakeVarsContext) {
1552	if d.dexpreoptConfigForMake != nil && !SkipDexpreoptBootJars(ctx) {
1553		ctx.Strict("DEX_PREOPT_CONFIG_FOR_MAKE", d.dexpreoptConfigForMake.String())
1554		ctx.Strict("DEX_PREOPT_SOONG_CONFIG_FOR_MAKE", android.PathForOutput(ctx, "dexpreopt_soong.config").String())
1555	}
1556
1557	image := d.defaultBootImage
1558	if image != nil && !SkipDexpreoptBootJars(ctx) {
1559		global := dexpreopt.GetGlobalConfig(ctx)
1560		dexPaths, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp)
1561		ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_FILES", strings.Join(dexPaths.Strings(), " "))
1562		ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS", strings.Join(dexLocations, " "))
1563
1564		// The primary ART boot image is exposed to Make for testing (gtests) and benchmarking
1565		// (golem) purposes.
1566		for _, current := range append(d.otherImages, image) {
1567			for _, variant := range current.variants {
1568				suffix := ""
1569				if variant.target.Os.Class == android.Host {
1570					suffix = "_host"
1571				}
1572				sfx := variant.name + suffix + "_" + variant.target.Arch.ArchType.String()
1573				ctx.Strict("DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_"+sfx, variant.vdexInstalls.String())
1574				ctx.Strict("DEXPREOPT_IMAGE_"+sfx, variant.imagePathOnHost.String())
1575				ctx.Strict("DEXPREOPT_IMAGE_DEPS_"+sfx, strings.Join(variant.imagesDeps.Strings(), " "))
1576				ctx.Strict("DEXPREOPT_IMAGE_BUILT_INSTALLED_"+sfx, variant.installs.String())
1577				ctx.Strict("DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_"+sfx, variant.unstrippedInstalls.String())
1578				if variant.licenseMetadataFile.Valid() {
1579					ctx.Strict("DEXPREOPT_IMAGE_LICENSE_METADATA_"+sfx, variant.licenseMetadataFile.String())
1580				}
1581			}
1582			imageLocationsOnHost, imageLocationsOnDevice := current.getAnyAndroidVariant().imageLocations()
1583			ctx.Strict("DEXPREOPT_IMAGE_LOCATIONS_ON_HOST"+current.name, strings.Join(imageLocationsOnHost, ":"))
1584			ctx.Strict("DEXPREOPT_IMAGE_LOCATIONS_ON_DEVICE"+current.name, strings.Join(imageLocationsOnDevice, ":"))
1585			ctx.Strict("DEXPREOPT_IMAGE_ZIP_"+current.name, current.zip.String())
1586		}
1587		ctx.Strict("DEXPREOPT_IMAGE_NAMES", strings.Join(getImageNames(), " "))
1588	}
1589}
1590
1591// Add one of the outputs in `OutputFile`
1592// This ensures that this singleton module does not get skipped when writing out/soong/Android-*.mk
1593func (d *dexpreoptBootJars) AndroidMkEntries() []android.AndroidMkEntries {
1594	return []android.AndroidMkEntries{{
1595		Class:      "ETC",
1596		OutputFile: android.OptionalPathForPath(d.bootFrameworkProfile),
1597	}}
1598}
1599
1600// artBootImages is a thin wrapper around `dex_bootjars`.
1601// it creates the installation rules for the host variant of the ART boot image.
1602type artBootImages struct {
1603	android.ModuleBase
1604
1605	// A non-empty file that will be written as `LOCAL_SOONG_INSTALLED_MODULE` in out/soong/Android-*.mk
1606	outputFile android.OptionalPath
1607}
1608
1609func artBootImagesFactory() android.Module {
1610	m := &artBootImages{}
1611	android.InitAndroidMultiTargetsArchModule(m, android.HostSupported, android.MultilibCommon)
1612	return m
1613}
1614
1615func (dbj *artBootImages) DepsMutator(ctx android.BottomUpMutatorContext) {
1616	// Create a dependency on `dex_bootjars` to access the intermediate locations of host art boot image.
1617	tag := bootclasspathDependencyTag{
1618		typ: dexpreoptBootJar,
1619	}
1620	ctx.AddVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(), tag, "dex_bootjars")
1621}
1622
1623func (d *artBootImages) GenerateAndroidBuildActions(ctx android.ModuleContext) {
1624	ctx.VisitDirectDeps(func(m android.Module) {
1625		tag := ctx.OtherModuleDependencyTag(m)
1626		if bcpTag, ok := tag.(bootclasspathDependencyTag); ok && bcpTag.typ == dexpreoptBootJar {
1627			if bcpTag.moduleInApex != "" {
1628				panic("unhandled moduleInApex")
1629			}
1630			hostInstallsInfo, ok := android.OtherModuleProvider(ctx, m, artBootImageHostInfoProvider)
1631			if !ok {
1632				ctx.ModuleErrorf("Could not find information about the host variant of ART boot image")
1633			}
1634			installs := d.installFile(ctx, hostInstallsInfo.installs)
1635			if len(installs) > 0 {
1636				d.outputFile = android.OptionalPathForPath(installs[0])
1637				// Create a phony target that can ART run-tests can depend on.
1638				ctx.Phony(d.Name(), installs...)
1639			} else {
1640				// this might be true e.g. when building with `WITH_DEXPREOPT=false`
1641				// create an empty file so that the `art_boot_images` is known to the packaging system.
1642				d.outputFile = android.OptionalPathForPath(android.PathForModuleOut(ctx, "undefined_art_boot_images"))
1643			}
1644		}
1645	})
1646}
1647
1648// Creates an installation rule for host variant of ART boot image files.
1649// Returns the list of install locations (out/host/linux-x86/...)
1650func (d *artBootImages) installFile(ctx android.ModuleContext, ruleBuilderInstalls android.RuleBuilderInstalls) android.Paths {
1651	var ret android.Paths
1652	for _, ruleBuilderInstall := range ruleBuilderInstalls {
1653		installDir := android.PathForModuleInstall(
1654			ctx,
1655			strings.TrimPrefix(filepath.Dir(ruleBuilderInstall.To), "/"),
1656		)
1657		filename := filepath.Base(ruleBuilderInstall.To)
1658		ctx.InstallFile(
1659			installDir,
1660			filename,
1661			ruleBuilderInstall.From,
1662		)
1663		ret = append(ret, installDir.Join(ctx, filename))
1664	}
1665	return ret
1666}
1667
1668// Set `OutputFile` expclitly so that this module does not get elided when generating out/soong/Android-*.mk
1669func (d *artBootImages) AndroidMkEntries() []android.AndroidMkEntries {
1670	return []android.AndroidMkEntries{{
1671		Class:      "ETC",
1672		OutputFile: d.outputFile,
1673	}}
1674}
1675