xref: /aosp_15_r20/build/soong/dexpreopt/config.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 dexpreopt
16
17import (
18	"encoding/json"
19	"fmt"
20	"reflect"
21	"strings"
22
23	"github.com/google/blueprint"
24
25	"android/soong/android"
26)
27
28// GlobalConfig stores the configuration for dex preopting. The fields are set
29// from product variables via dex_preopt_config.mk.
30type GlobalConfig struct {
31	DisablePreopt           bool     // disable preopt for all modules (excluding boot images)
32	DisablePreoptBootImages bool     // disable prepot for boot images
33	DisablePreoptModules    []string // modules with preopt disabled by product-specific config
34
35	OnlyPreoptArtBootImage bool // only preopt jars in the ART boot image
36
37	PreoptWithUpdatableBcp bool // If updatable boot jars are included in dexpreopt or not.
38
39	HasSystemOther        bool     // store odex files that match PatternsOnSystemOther on the system_other partition
40	PatternsOnSystemOther []string // patterns (using '%' to denote a prefix match) to put odex on the system_other partition
41
42	DisableGenerateProfile bool   // don't generate profiles
43	ProfileDir             string // directory to find profiles in
44
45	BootJars     android.ConfiguredJarList // modules for jars that form the boot class path
46	ApexBootJars android.ConfiguredJarList // jars within apex that form the boot class path
47
48	ArtApexJars              android.ConfiguredJarList // modules for jars that are in the ART APEX
49	TestOnlyArtBootImageJars android.ConfiguredJarList // modules for jars to be included in the ART boot image for testing
50
51	SystemServerJars               android.ConfiguredJarList // system_server classpath jars on the platform
52	SystemServerApps               []string                  // apps that are loaded into system server
53	ApexSystemServerJars           android.ConfiguredJarList // system_server classpath jars delivered via apex
54	StandaloneSystemServerJars     android.ConfiguredJarList // jars on the platform that system_server loads dynamically using separate classloaders
55	ApexStandaloneSystemServerJars android.ConfiguredJarList // jars delivered via apex that system_server loads dynamically using separate classloaders
56	SpeedApps                      []string                  // apps that should be speed optimized
57
58	BrokenSuboptimalOrderOfSystemServerJars bool // if true, sub-optimal order does not cause a build error
59
60	PreoptFlags []string // global dex2oat flags that should be used if no module-specific dex2oat flags are specified
61
62	DefaultCompilerFilter      string // default compiler filter to pass to dex2oat, overridden by --compiler-filter= in module-specific dex2oat flags
63	SystemServerCompilerFilter string // default compiler filter to pass to dex2oat for system server jars
64
65	GenerateDMFiles bool // generate Dex Metadata files
66
67	NoDebugInfo                 bool // don't generate debug info by default
68	DontResolveStartupStrings   bool // don't resolve string literals loaded during application startup.
69	AlwaysSystemServerDebugInfo bool // always generate mini debug info for system server modules (overrides NoDebugInfo=true)
70	NeverSystemServerDebugInfo  bool // never generate mini debug info for system server modules (overrides NoDebugInfo=false)
71	AlwaysOtherDebugInfo        bool // always generate mini debug info for non-system server modules (overrides NoDebugInfo=true)
72	NeverOtherDebugInfo         bool // never generate mini debug info for non-system server modules (overrides NoDebugInfo=true)
73
74	IsEng        bool // build is a eng variant
75	SanitizeLite bool // build is the second phase of a SANITIZE_LITE build
76
77	DefaultAppImages bool // build app images (TODO: .art files?) by default
78
79	Dex2oatXmx string // max heap size for dex2oat
80	Dex2oatXms string // initial heap size for dex2oat
81
82	EmptyDirectory string // path to an empty directory
83
84	CpuVariant             map[android.ArchType]string // cpu variant for each architecture
85	InstructionSetFeatures map[android.ArchType]string // instruction set for each architecture
86
87	BootImageProfiles android.Paths // path to a boot-image-profile.txt file
88	BootFlags         string        // extra flags to pass to dex2oat for the boot image
89	Dex2oatImageXmx   string        // max heap size for dex2oat for the boot image
90	Dex2oatImageXms   string        // initial heap size for dex2oat for the boot image
91
92	// If true, downgrade the compiler filter of dexpreopt to "verify" when verify_uses_libraries
93	// check fails, instead of failing the build. This will disable any AOT-compilation.
94	//
95	// The intended use case for this flag is to have a smoother migration path for the Java
96	// modules that need to add <uses-library> information in their build files. The flag allows to
97	// quickly silence build errors. This flag should be used with caution and only as a temporary
98	// measure, as it masks real errors and affects performance.
99	RelaxUsesLibraryCheck bool
100
101	// "true" to force preopt with CMC GC (a.k.a., UFFD GC); "false" to force preopt with CC GC;
102	// "default" to determine the GC type based on the kernel version file.
103	EnableUffdGc string
104}
105
106var allPlatformSystemServerJarsKey = android.NewOnceKey("allPlatformSystemServerJars")
107
108// Returns all jars on the platform that system_server loads, including those on classpath and those
109// loaded dynamically.
110func (g *GlobalConfig) AllPlatformSystemServerJars(ctx android.PathContext) *android.ConfiguredJarList {
111	return ctx.Config().Once(allPlatformSystemServerJarsKey, func() interface{} {
112		res := g.SystemServerJars.AppendList(&g.StandaloneSystemServerJars)
113		return &res
114	}).(*android.ConfiguredJarList)
115}
116
117var allApexSystemServerJarsKey = android.NewOnceKey("allApexSystemServerJars")
118
119// Returns all jars delivered via apex that system_server loads, including those on classpath and
120// those loaded dynamically.
121func (g *GlobalConfig) AllApexSystemServerJars(ctx android.PathContext) *android.ConfiguredJarList {
122	return ctx.Config().Once(allApexSystemServerJarsKey, func() interface{} {
123		res := g.ApexSystemServerJars.AppendList(&g.ApexStandaloneSystemServerJars)
124		return &res
125	}).(*android.ConfiguredJarList)
126}
127
128var allSystemServerClasspathJarsKey = android.NewOnceKey("allSystemServerClasspathJars")
129
130// Returns all system_server classpath jars.
131func (g *GlobalConfig) AllSystemServerClasspathJars(ctx android.PathContext) *android.ConfiguredJarList {
132	return ctx.Config().Once(allSystemServerClasspathJarsKey, func() interface{} {
133		res := g.SystemServerJars.AppendList(&g.ApexSystemServerJars)
134		return &res
135	}).(*android.ConfiguredJarList)
136}
137
138var allSystemServerJarsKey = android.NewOnceKey("allSystemServerJars")
139
140// Returns all jars that system_server loads.
141func (g *GlobalConfig) AllSystemServerJars(ctx android.PathContext) *android.ConfiguredJarList {
142	return ctx.Config().Once(allSystemServerJarsKey, func() interface{} {
143		res := g.AllPlatformSystemServerJars(ctx).AppendList(g.AllApexSystemServerJars(ctx))
144		return &res
145	}).(*android.ConfiguredJarList)
146}
147
148// GlobalSoongConfig contains the global config that is generated from Soong,
149// stored in dexpreopt_soong.config.
150type GlobalSoongConfig struct {
151	// Paths to tools possibly used by the generated commands.
152	Profman          android.Path
153	Dex2oat          android.Path
154	Aapt             android.Path
155	SoongZip         android.Path
156	Zip2zip          android.Path
157	ManifestCheck    android.Path
158	ConstructContext android.Path
159	UffdGcFlag       android.WritablePath
160}
161
162type ModuleConfig struct {
163	Name            string
164	DexLocation     string // dex location on device
165	BuildPath       android.OutputPath
166	DexPath         android.Path
167	ManifestPath    android.OptionalPath
168	UncompressedDex bool
169	HasApkLibraries bool
170	PreoptFlags     []string
171
172	ProfileClassListing  android.OptionalPath
173	ProfileIsTextListing bool
174	ProfileBootListing   android.OptionalPath
175
176	EnforceUsesLibraries           bool         // turn on build-time verify_uses_libraries check
177	EnforceUsesLibrariesStatusFile android.Path // a file with verify_uses_libraries errors (if any)
178	ProvidesUsesLibrary            string       // library name (usually the same as module name)
179	ClassLoaderContexts            ClassLoaderContextMap
180
181	Archs               []android.ArchType
182	DexPreoptImagesDeps []android.OutputPaths
183
184	DexPreoptImageLocationsOnHost   []string // boot image location on host (file path without the arch subdirectory)
185	DexPreoptImageLocationsOnDevice []string // boot image location on device (file path without the arch subdirectory)
186
187	PreoptBootClassPathDexFiles     android.Paths // file paths of boot class path files
188	PreoptBootClassPathDexLocations []string      // virtual locations of boot class path files
189
190	NoCreateAppImage    bool
191	ForceCreateAppImage bool
192
193	PresignedPrebuilt bool
194
195	// ApexPartition is the partition in which the dexpreopt files of apex system server jars (if any) are installed.
196	// This is a noop unless the module is apex system server jar.
197	ApexPartition string
198}
199
200type globalSoongConfigSingleton struct{}
201
202var pctx = android.NewPackageContext("android/soong/dexpreopt")
203
204func init() {
205	pctx.Import("android/soong/android")
206	android.RegisterParallelSingletonType("dexpreopt-soong-config", func() android.Singleton {
207		return &globalSoongConfigSingleton{}
208	})
209}
210
211func constructPath(ctx android.PathContext, path string) android.Path {
212	buildDirPrefix := ctx.Config().SoongOutDir() + "/"
213	if path == "" {
214		return nil
215	} else if strings.HasPrefix(path, buildDirPrefix) {
216		return android.PathForOutput(ctx, strings.TrimPrefix(path, buildDirPrefix))
217	} else {
218		return android.PathForSource(ctx, path)
219	}
220}
221
222func constructPaths(ctx android.PathContext, paths []string) android.Paths {
223	var ret android.Paths
224	for _, path := range paths {
225		ret = append(ret, constructPath(ctx, path))
226	}
227	return ret
228}
229
230func constructWritablePath(ctx android.PathContext, path string) android.WritablePath {
231	if path == "" {
232		return nil
233	}
234	return constructPath(ctx, path).(android.WritablePath)
235}
236
237// ParseGlobalConfig parses the given data assumed to be read from the global
238// dexpreopt.config file into a GlobalConfig struct.
239func ParseGlobalConfig(ctx android.PathContext, data []byte) (*GlobalConfig, error) {
240	type GlobalJSONConfig struct {
241		*GlobalConfig
242
243		// Copies of entries in GlobalConfig that are not constructable without extra parameters.  They will be
244		// used to construct the real value manually below.
245		BootImageProfiles []string
246	}
247
248	config := GlobalJSONConfig{}
249	err := json.Unmarshal(data, &config)
250	if err != nil {
251		return config.GlobalConfig, err
252	}
253
254	// Construct paths that require a PathContext.
255	config.GlobalConfig.BootImageProfiles = constructPaths(ctx, config.BootImageProfiles)
256
257	return config.GlobalConfig, nil
258}
259
260type globalConfigAndRaw struct {
261	global     *GlobalConfig
262	data       []byte
263	pathErrors []error
264}
265
266// GetGlobalConfig returns the global dexpreopt.config that's created in the
267// make config phase. It is loaded once the first time it is called for any
268// ctx.Config(), and returns the same data for all future calls with the same
269// ctx.Config(). A value can be inserted for tests using
270// setDexpreoptTestGlobalConfig.
271func GetGlobalConfig(ctx android.PathContext) *GlobalConfig {
272	return getGlobalConfigRaw(ctx).global
273}
274
275// GetGlobalConfigRawData is the same as GetGlobalConfig, except that it returns
276// the literal content of dexpreopt.config.
277func GetGlobalConfigRawData(ctx android.PathContext) []byte {
278	return getGlobalConfigRaw(ctx).data
279}
280
281var globalConfigOnceKey = android.NewOnceKey("DexpreoptGlobalConfig")
282var testGlobalConfigOnceKey = android.NewOnceKey("TestDexpreoptGlobalConfig")
283
284type pathContextErrorCollector struct {
285	android.PathContext
286	errors []error
287}
288
289func (p *pathContextErrorCollector) Errorf(format string, args ...interface{}) {
290	p.errors = append(p.errors, fmt.Errorf(format, args...))
291}
292
293func getGlobalConfigRaw(ctx android.PathContext) globalConfigAndRaw {
294	config := ctx.Config().Once(globalConfigOnceKey, func() interface{} {
295		if data, err := ctx.Config().DexpreoptGlobalConfig(ctx); err != nil {
296			panic(err)
297		} else if data != nil {
298			pathErrorCollectorCtx := &pathContextErrorCollector{PathContext: ctx}
299			globalConfig, err := ParseGlobalConfig(pathErrorCollectorCtx, data)
300			if err != nil {
301				panic(err)
302			}
303			return globalConfigAndRaw{globalConfig, data, pathErrorCollectorCtx.errors}
304		}
305
306		// No global config filename set, see if there is a test config set
307		return ctx.Config().Once(testGlobalConfigOnceKey, func() interface{} {
308			// Nope, return a config with preopting disabled
309			return globalConfigAndRaw{&GlobalConfig{
310				DisablePreopt:           true,
311				DisablePreoptBootImages: true,
312				DisableGenerateProfile:  true,
313			}, nil, nil}
314		})
315	}).(globalConfigAndRaw)
316
317	// Avoid non-deterministic errors by reporting cached path errors on all callers.
318	for _, err := range config.pathErrors {
319		if ctx.Config().AllowMissingDependencies() {
320			// When AllowMissingDependencies it set, report errors through AddMissingDependencies.
321			// If AddMissingDependencies doesn't exist on the current context (for example when
322			// called with a SingletonContext), just swallow the errors since there is no way to
323			// report them.
324			if missingDepsCtx, ok := ctx.(interface {
325				AddMissingDependencies(missingDeps []string)
326			}); ok {
327				missingDepsCtx.AddMissingDependencies([]string{err.Error()})
328			}
329		} else {
330			android.ReportPathErrorf(ctx, "%s", err)
331		}
332	}
333
334	return config
335}
336
337// SetTestGlobalConfig sets a GlobalConfig that future calls to GetGlobalConfig
338// will return. It must be called before the first call to GetGlobalConfig for
339// the config.
340func SetTestGlobalConfig(config android.Config, globalConfig *GlobalConfig) {
341	config.Once(testGlobalConfigOnceKey, func() interface{} { return globalConfigAndRaw{globalConfig, nil, nil} })
342}
343
344// This struct is required to convert ModuleConfig from/to JSON.
345// The types of fields in ModuleConfig are not convertible,
346// so moduleJSONConfig has those fields as a convertible type.
347type moduleJSONConfig struct {
348	*ModuleConfig
349
350	BuildPath    string
351	DexPath      string
352	ManifestPath string
353
354	ProfileClassListing string
355	ProfileBootListing  string
356
357	EnforceUsesLibrariesStatusFile string
358	ClassLoaderContexts            jsonClassLoaderContextMap
359
360	DexPreoptImagesDeps [][]string
361
362	PreoptBootClassPathDexFiles []string
363}
364
365// ParseModuleConfig parses a per-module dexpreopt.config file into a
366// ModuleConfig struct. It is not used in Soong, which receives a ModuleConfig
367// struct directly from java/dexpreopt.go. It is used in dexpreopt_gen called
368// from Make to read the module dexpreopt.config written in the Make config
369// stage.
370func ParseModuleConfig(ctx android.PathContext, data []byte) (*ModuleConfig, error) {
371	config := moduleJSONConfig{}
372
373	err := json.Unmarshal(data, &config)
374	if err != nil {
375		return config.ModuleConfig, err
376	}
377
378	// Construct paths that require a PathContext.
379	config.ModuleConfig.BuildPath = constructPath(ctx, config.BuildPath).(android.OutputPath)
380	config.ModuleConfig.DexPath = constructPath(ctx, config.DexPath)
381	config.ModuleConfig.ManifestPath = android.OptionalPathForPath(constructPath(ctx, config.ManifestPath))
382	config.ModuleConfig.ProfileClassListing = android.OptionalPathForPath(constructPath(ctx, config.ProfileClassListing))
383	config.ModuleConfig.EnforceUsesLibrariesStatusFile = constructPath(ctx, config.EnforceUsesLibrariesStatusFile)
384	config.ModuleConfig.ClassLoaderContexts = fromJsonClassLoaderContext(ctx, config.ClassLoaderContexts)
385	config.ModuleConfig.PreoptBootClassPathDexFiles = constructPaths(ctx, config.PreoptBootClassPathDexFiles)
386
387	// This needs to exist, but dependencies are already handled in Make, so we don't need to pass them through JSON.
388	config.ModuleConfig.DexPreoptImagesDeps = make([]android.OutputPaths, len(config.ModuleConfig.Archs))
389
390	return config.ModuleConfig, nil
391}
392
393func pathsListToStringLists(pathsList []android.OutputPaths) [][]string {
394	ret := make([][]string, 0, len(pathsList))
395	for _, paths := range pathsList {
396		ret = append(ret, paths.Strings())
397	}
398	return ret
399}
400
401func moduleConfigToJSON(config *ModuleConfig) ([]byte, error) {
402	return json.MarshalIndent(&moduleJSONConfig{
403		BuildPath:                      config.BuildPath.String(),
404		DexPath:                        config.DexPath.String(),
405		ManifestPath:                   config.ManifestPath.String(),
406		ProfileClassListing:            config.ProfileClassListing.String(),
407		ProfileBootListing:             config.ProfileBootListing.String(),
408		EnforceUsesLibrariesStatusFile: config.EnforceUsesLibrariesStatusFile.String(),
409		ClassLoaderContexts:            toJsonClassLoaderContext(config.ClassLoaderContexts),
410		DexPreoptImagesDeps:            pathsListToStringLists(config.DexPreoptImagesDeps),
411		PreoptBootClassPathDexFiles:    config.PreoptBootClassPathDexFiles.Strings(),
412		ModuleConfig:                   config,
413	}, "", "    ")
414}
415
416// WriteModuleConfig serializes a ModuleConfig into a per-module dexpreopt.config JSON file.
417// These config files are used for post-processing.
418func WriteModuleConfig(ctx android.ModuleContext, config *ModuleConfig, path android.WritablePath) {
419	if path == nil {
420		return
421	}
422
423	data, err := moduleConfigToJSON(config)
424	if err != nil {
425		ctx.ModuleErrorf("failed to JSON marshal module dexpreopt.config: %v", err)
426		return
427	}
428
429	android.WriteFileRule(ctx, path, string(data))
430}
431
432// dex2oatModuleName returns the name of the module to use for the dex2oat host
433// tool. It should be a binary module with public visibility that is compiled
434// and installed for host.
435func dex2oatModuleName(config android.Config) string {
436	// Default to the debug variant of dex2oat to help find bugs.
437	// Set USE_DEX2OAT_DEBUG to false for only building non-debug versions.
438	if config.Getenv("USE_DEX2OAT_DEBUG") == "false" {
439		return "dex2oat"
440	} else {
441		return "dex2oatd"
442	}
443}
444
445type dex2oatDependencyTag struct {
446	blueprint.BaseDependencyTag
447	android.LicenseAnnotationToolchainDependencyTag
448}
449
450func (d dex2oatDependencyTag) ExcludeFromVisibilityEnforcement() {
451}
452
453func (d dex2oatDependencyTag) ExcludeFromApexContents() {
454}
455
456func (d dex2oatDependencyTag) AllowDisabledModuleDependency(target android.Module) bool {
457	// RegisterToolDeps may run after the prebuilt mutators and hence register a
458	// dependency on the source module even when the prebuilt is to be used.
459	// dex2oatPathFromDep takes that into account when it retrieves the path to
460	// the binary, but we also need to disable the check for dependencies on
461	// disabled modules.
462	return target.IsReplacedByPrebuilt()
463}
464
465func (d dex2oatDependencyTag) AllowDisabledModuleDependencyProxy(
466	ctx android.OtherModuleProviderContext, target android.ModuleProxy) bool {
467	return android.OtherModuleProviderOrDefault(
468		ctx, target, android.CommonModuleInfoKey).ReplacedByPrebuilt
469}
470
471// Dex2oatDepTag represents the dependency onto the dex2oatd module. It is added to any module that
472// needs dexpreopting and so it makes no sense for it to be checked for visibility or included in
473// the apex.
474var Dex2oatDepTag = dex2oatDependencyTag{}
475
476var _ android.ExcludeFromVisibilityEnforcementTag = Dex2oatDepTag
477var _ android.ExcludeFromApexContentsTag = Dex2oatDepTag
478var _ android.AllowDisabledModuleDependency = Dex2oatDepTag
479
480// RegisterToolDeps adds the necessary dependencies to binary modules for tools
481// that are required later when Get(Cached)GlobalSoongConfig is called. It
482// should be called from a mutator that's registered with
483// android.RegistrationContext.FinalDepsMutators.
484func RegisterToolDeps(ctx android.BottomUpMutatorContext) {
485	dex2oatBin := dex2oatModuleName(ctx.Config())
486	v := ctx.Config().BuildOSTarget.Variations()
487	ctx.AddFarVariationDependencies(v, Dex2oatDepTag, dex2oatBin)
488}
489
490func IsDex2oatNeeded(ctx android.PathContext) bool {
491	global := GetGlobalConfig(ctx)
492	return !global.DisablePreopt || !global.DisablePreoptBootImages
493}
494
495func dex2oatPathFromDep(ctx android.ModuleContext) android.Path {
496	if !IsDex2oatNeeded(ctx) {
497		return nil
498	}
499
500	dex2oatBin := dex2oatModuleName(ctx.Config())
501
502	// Find the right dex2oat module, trying to follow PrebuiltDepTag from source
503	// to prebuilt if there is one. We wouldn't have to do this if the
504	// prebuilt_postdeps mutator that replaces source deps with prebuilt deps was
505	// run after RegisterToolDeps above, but changing that leads to ordering
506	// problems between mutators (RegisterToolDeps needs to run late to act on
507	// final variants, while prebuilt_postdeps needs to run before many of the
508	// PostDeps mutators, like the APEX mutators). Hence we need to dig out the
509	// prebuilt explicitly here instead.
510	var dex2oatModule android.Module
511	ctx.WalkDeps(func(child, parent android.Module) bool {
512		if parent == ctx.Module() && ctx.OtherModuleDependencyTag(child) == Dex2oatDepTag {
513			// Found the source module, or prebuilt module that has replaced the source.
514			dex2oatModule = child
515			if android.IsModulePrebuilt(child) {
516				return false // If it's the prebuilt we're done.
517			} else {
518				return true // Recurse to check if the source has a prebuilt dependency.
519			}
520		}
521		if parent == dex2oatModule && ctx.OtherModuleDependencyTag(child) == android.PrebuiltDepTag {
522			if p := android.GetEmbeddedPrebuilt(child); p != nil && p.UsePrebuilt() {
523				dex2oatModule = child // Found a prebuilt that should be used.
524			}
525		}
526		return false
527	})
528
529	if dex2oatModule == nil {
530		// If this happens there's probably a missing call to AddToolDeps in DepsMutator.
531		panic(fmt.Sprintf("Failed to lookup %s dependency", dex2oatBin))
532	}
533
534	dex2oatPath := dex2oatModule.(android.HostToolProvider).HostToolPath()
535	if !dex2oatPath.Valid() {
536		panic(fmt.Sprintf("Failed to find host tool path in %s", dex2oatModule))
537	}
538
539	return dex2oatPath.Path()
540}
541
542// createGlobalSoongConfig creates a GlobalSoongConfig from the current context.
543// Should not be used in dexpreopt_gen.
544func createGlobalSoongConfig(ctx android.ModuleContext) *GlobalSoongConfig {
545	return &GlobalSoongConfig{
546		Profman:          ctx.Config().HostToolPath(ctx, "profman"),
547		Dex2oat:          dex2oatPathFromDep(ctx),
548		Aapt:             ctx.Config().HostToolPath(ctx, "aapt2"),
549		SoongZip:         ctx.Config().HostToolPath(ctx, "soong_zip"),
550		Zip2zip:          ctx.Config().HostToolPath(ctx, "zip2zip"),
551		ManifestCheck:    ctx.Config().HostToolPath(ctx, "manifest_check"),
552		ConstructContext: ctx.Config().HostToolPath(ctx, "construct_context"),
553		UffdGcFlag:       getUffdGcFlagPath(ctx),
554	}
555}
556
557// The main reason for this Once cache for GlobalSoongConfig is to make the
558// dex2oat path available to singletons. In ordinary modules we get it through a
559// Dex2oatDepTag dependency, but in singletons there's no simple way to do the
560// same thing and ensure the right variant is selected, hence this cache to make
561// the resolved path available to singletons. This means we depend on there
562// being at least one ordinary module with a Dex2oatDepTag dependency.
563//
564// TODO(b/147613152): Implement a way to deal with dependencies from singletons,
565// and then possibly remove this cache altogether.
566var globalSoongConfigOnceKey = android.NewOnceKey("DexpreoptGlobalSoongConfig")
567
568// GetGlobalSoongConfig creates a GlobalSoongConfig the first time it's called,
569// and later returns the same cached instance.
570func GetGlobalSoongConfig(ctx android.ModuleContext) *GlobalSoongConfig {
571	globalSoong := ctx.Config().Once(globalSoongConfigOnceKey, func() interface{} {
572		return createGlobalSoongConfig(ctx)
573	}).(*GlobalSoongConfig)
574
575	// Always resolve the tool path from the dependency, to ensure that every
576	// module has the dependency added properly.
577	myDex2oat := dex2oatPathFromDep(ctx)
578	if myDex2oat != globalSoong.Dex2oat {
579		panic(fmt.Sprintf("Inconsistent dex2oat path in cached config: expected %s, got %s", globalSoong.Dex2oat, myDex2oat))
580	}
581
582	return globalSoong
583}
584
585// GetCachedGlobalSoongConfig returns a cached GlobalSoongConfig created by an
586// earlier GetGlobalSoongConfig call. This function works with any context
587// compatible with a basic PathContext, since it doesn't try to create a
588// GlobalSoongConfig with the proper paths (which requires a full
589// ModuleContext). If there has been no prior call to GetGlobalSoongConfig, nil
590// is returned.
591func GetCachedGlobalSoongConfig(ctx android.PathContext) *GlobalSoongConfig {
592	return ctx.Config().Once(globalSoongConfigOnceKey, func() interface{} {
593		return (*GlobalSoongConfig)(nil)
594	}).(*GlobalSoongConfig)
595}
596
597type globalJsonSoongConfig struct {
598	Profman          string
599	Dex2oat          string
600	Aapt             string
601	SoongZip         string
602	Zip2zip          string
603	ManifestCheck    string
604	ConstructContext string
605	UffdGcFlag       string
606}
607
608// ParseGlobalSoongConfig parses the given data assumed to be read from the
609// global dexpreopt_soong.config file into a GlobalSoongConfig struct. It is
610// only used in dexpreopt_gen.
611func ParseGlobalSoongConfig(ctx android.PathContext, data []byte) (*GlobalSoongConfig, error) {
612	var jc globalJsonSoongConfig
613
614	err := json.Unmarshal(data, &jc)
615	if err != nil {
616		return &GlobalSoongConfig{}, err
617	}
618
619	config := &GlobalSoongConfig{
620		Profman:          constructPath(ctx, jc.Profman),
621		Dex2oat:          constructPath(ctx, jc.Dex2oat),
622		Aapt:             constructPath(ctx, jc.Aapt),
623		SoongZip:         constructPath(ctx, jc.SoongZip),
624		Zip2zip:          constructPath(ctx, jc.Zip2zip),
625		ManifestCheck:    constructPath(ctx, jc.ManifestCheck),
626		ConstructContext: constructPath(ctx, jc.ConstructContext),
627		UffdGcFlag:       constructWritablePath(ctx, jc.UffdGcFlag),
628	}
629
630	return config, nil
631}
632
633// checkBootJarsConfigConsistency checks the consistency of BootJars and ApexBootJars fields in
634// DexpreoptGlobalConfig and Config.productVariables.
635func checkBootJarsConfigConsistency(ctx android.SingletonContext, dexpreoptConfig *GlobalConfig, config android.Config) {
636	compareBootJars := func(property string, dexpreoptJars, variableJars android.ConfiguredJarList) {
637		dexpreoptPairs := dexpreoptJars.CopyOfApexJarPairs()
638		variablePairs := variableJars.CopyOfApexJarPairs()
639		if !reflect.DeepEqual(dexpreoptPairs, variablePairs) {
640			ctx.Errorf("Inconsistent configuration of %[1]s\n"+
641				"    dexpreopt.GlobalConfig.%[1]s = %[2]s\n"+
642				"    productVariables.%[1]s       = %[3]s",
643				property, dexpreoptPairs, variablePairs)
644		}
645	}
646
647	compareBootJars("BootJars", dexpreoptConfig.BootJars, config.NonApexBootJars())
648	compareBootJars("ApexBootJars", dexpreoptConfig.ApexBootJars, config.ApexBootJars())
649}
650
651func (s *globalSoongConfigSingleton) GenerateBuildActions(ctx android.SingletonContext) {
652	global := GetGlobalConfig(ctx)
653	checkBootJarsConfigConsistency(ctx, global, ctx.Config())
654
655	if global.DisablePreopt {
656		return
657	}
658
659	buildUffdGcFlag(ctx, global)
660
661	config := GetCachedGlobalSoongConfig(ctx)
662	if config == nil {
663		// No module has enabled dexpreopting, so we assume there will be no calls
664		// to dexpreopt_gen.
665		return
666	}
667
668	jc := globalJsonSoongConfig{
669		Profman:          config.Profman.String(),
670		Dex2oat:          config.Dex2oat.String(),
671		Aapt:             config.Aapt.String(),
672		SoongZip:         config.SoongZip.String(),
673		Zip2zip:          config.Zip2zip.String(),
674		ManifestCheck:    config.ManifestCheck.String(),
675		ConstructContext: config.ConstructContext.String(),
676		UffdGcFlag:       config.UffdGcFlag.String(),
677	}
678
679	data, err := json.Marshal(jc)
680	if err != nil {
681		ctx.Errorf("failed to JSON marshal GlobalSoongConfig: %v", err)
682		return
683	}
684
685	android.WriteFileRule(ctx, android.PathForOutput(ctx, "dexpreopt_soong.config"), string(data))
686}
687
688func (s *globalSoongConfigSingleton) MakeVars(ctx android.MakeVarsContext) {
689	if GetGlobalConfig(ctx).DisablePreopt {
690		return
691	}
692
693	config := GetCachedGlobalSoongConfig(ctx)
694	if config == nil {
695		return
696	}
697
698	ctx.Strict("DEX2OAT", config.Dex2oat.String())
699	ctx.Strict("DEXPREOPT_GEN_DEPS", strings.Join([]string{
700		config.Profman.String(),
701		config.Dex2oat.String(),
702		config.Aapt.String(),
703		config.SoongZip.String(),
704		config.Zip2zip.String(),
705		config.ManifestCheck.String(),
706		config.ConstructContext.String(),
707		config.UffdGcFlag.String(),
708	}, " "))
709}
710
711func buildUffdGcFlag(ctx android.BuilderContext, global *GlobalConfig) {
712	uffdGcFlag := getUffdGcFlagPath(ctx)
713
714	if global.EnableUffdGc == "true" {
715		android.WriteFileRuleVerbatim(ctx, uffdGcFlag, "--runtime-arg -Xgc:CMC")
716	} else if global.EnableUffdGc == "false" {
717		android.WriteFileRuleVerbatim(ctx, uffdGcFlag, "")
718	} else if global.EnableUffdGc == "default" {
719		// Generated by `build/make/core/Makefile`.
720		kernelVersionFile := android.PathForOutput(ctx, "dexpreopt/kernel_version_for_uffd_gc.txt")
721		// Determine the UFFD GC flag by the kernel version file.
722		rule := android.NewRuleBuilder(pctx, ctx)
723		rule.Command().
724			Tool(ctx.Config().HostToolPath(ctx, "construct_uffd_gc_flag")).
725			Input(kernelVersionFile).
726			Output(uffdGcFlag)
727		rule.Restat().Build("dexpreopt_uffd_gc_flag", "dexpreopt_uffd_gc_flag")
728	} else {
729		panic(fmt.Sprintf("Unknown value of PRODUCT_ENABLE_UFFD_GC: %s", global.EnableUffdGc))
730	}
731}
732
733func GlobalConfigForTests(ctx android.PathContext) *GlobalConfig {
734	return &GlobalConfig{
735		DisablePreopt:                  false,
736		DisablePreoptModules:           nil,
737		OnlyPreoptArtBootImage:         false,
738		HasSystemOther:                 false,
739		PatternsOnSystemOther:          nil,
740		DisableGenerateProfile:         false,
741		ProfileDir:                     "",
742		BootJars:                       android.EmptyConfiguredJarList(),
743		ApexBootJars:                   android.EmptyConfiguredJarList(),
744		ArtApexJars:                    android.EmptyConfiguredJarList(),
745		TestOnlyArtBootImageJars:       android.EmptyConfiguredJarList(),
746		SystemServerJars:               android.EmptyConfiguredJarList(),
747		SystemServerApps:               nil,
748		ApexSystemServerJars:           android.EmptyConfiguredJarList(),
749		StandaloneSystemServerJars:     android.EmptyConfiguredJarList(),
750		ApexStandaloneSystemServerJars: android.EmptyConfiguredJarList(),
751		SpeedApps:                      nil,
752		PreoptFlags:                    nil,
753		DefaultCompilerFilter:          "",
754		SystemServerCompilerFilter:     "",
755		GenerateDMFiles:                false,
756		NoDebugInfo:                    false,
757		DontResolveStartupStrings:      false,
758		AlwaysSystemServerDebugInfo:    false,
759		NeverSystemServerDebugInfo:     false,
760		AlwaysOtherDebugInfo:           false,
761		NeverOtherDebugInfo:            false,
762		IsEng:                          false,
763		SanitizeLite:                   false,
764		DefaultAppImages:               false,
765		Dex2oatXmx:                     "",
766		Dex2oatXms:                     "",
767		EmptyDirectory:                 "empty_dir",
768		CpuVariant:                     nil,
769		InstructionSetFeatures:         nil,
770		BootImageProfiles:              nil,
771		BootFlags:                      "",
772		Dex2oatImageXmx:                "",
773		Dex2oatImageXms:                "",
774	}
775}
776
777func globalSoongConfigForTests(ctx android.BuilderContext) *GlobalSoongConfig {
778	return &GlobalSoongConfig{
779		Profman:          android.PathForTesting("profman"),
780		Dex2oat:          android.PathForTesting("dex2oat"),
781		Aapt:             android.PathForTesting("aapt2"),
782		SoongZip:         android.PathForTesting("soong_zip"),
783		Zip2zip:          android.PathForTesting("zip2zip"),
784		ManifestCheck:    android.PathForTesting("manifest_check"),
785		ConstructContext: android.PathForTesting("construct_context"),
786		UffdGcFlag:       android.PathForOutput(ctx, "dexpreopt_test", "uffd_gc_flag.txt"),
787	}
788}
789
790func GetDexpreoptDirName(ctx android.PathContext) string {
791	prefix := "dexpreopt_"
792	targets := ctx.Config().Targets[android.Android]
793	if len(targets) > 0 {
794		return prefix + targets[0].Arch.ArchType.String()
795	}
796	return prefix + "unknown_target"
797}
798
799func getUffdGcFlagPath(ctx android.PathContext) android.WritablePath {
800	return android.PathForOutput(ctx, "dexpreopt/uffd_gc_flag.txt")
801}
802