• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2018 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package 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	// dexpreopt does not initialize the soong config.
143	// Initialize the OncePer here.
144	if ctx.Config().OncePer == nil {
145		ctx.Config().OncePer = &android.OncePer{}
146	}
147	return ctx.Config().Once(allSystemServerJarsKey, func() interface{} {
148		res := g.AllPlatformSystemServerJars(ctx).AppendList(g.AllApexSystemServerJars(ctx))
149		return &res
150	}).(*android.ConfiguredJarList)
151}
152
153// GlobalSoongConfig contains the global config that is generated from Soong,
154// stored in dexpreopt_soong.config.
155type GlobalSoongConfig struct {
156	// Paths to tools possibly used by the generated commands.
157	Profman          android.Path
158	Dex2oat          android.Path
159	Aapt             android.Path
160	SoongZip         android.Path
161	Zip2zip          android.Path
162	ManifestCheck    android.Path
163	ConstructContext android.Path
164	UffdGcFlag       android.WritablePath
165}
166
167type ModuleConfig struct {
168	Name            string
169	DexLocation     string // dex location on device
170	BuildPath       android.OutputPath
171	DexPath         android.Path
172	ManifestPath    android.OptionalPath
173	UncompressedDex bool
174	HasApkLibraries bool
175	PreoptFlags     []string
176
177	ProfileClassListing  android.OptionalPath
178	ProfileIsTextListing bool
179	ProfileBootListing   android.OptionalPath
180
181	EnforceUsesLibraries           bool         // turn on build-time verify_uses_libraries check
182	EnforceUsesLibrariesStatusFile android.Path // a file with verify_uses_libraries errors (if any)
183	ProvidesUsesLibrary            string       // library name (usually the same as module name)
184	ClassLoaderContexts            ClassLoaderContextMap
185
186	Archs               []android.ArchType
187	DexPreoptImagesDeps []android.OutputPaths
188
189	DexPreoptImageLocationsOnHost   []string // boot image location on host (file path without the arch subdirectory)
190	DexPreoptImageLocationsOnDevice []string // boot image location on device (file path without the arch subdirectory)
191
192	PreoptBootClassPathDexFiles     android.Paths // file paths of boot class path files
193	PreoptBootClassPathDexLocations []string      // virtual locations of boot class path files
194
195	NoCreateAppImage    bool
196	ForceCreateAppImage bool
197
198	PresignedPrebuilt bool
199
200	// ApexPartition is the partition in which the dexpreopt files of apex system server jars (if any) are installed.
201	// This is a noop unless the module is apex system server jar.
202	ApexPartition string
203}
204
205type globalSoongConfigSingleton struct{}
206
207var pctx = android.NewPackageContext("android/soong/dexpreopt")
208
209func init() {
210	pctx.Import("android/soong/android")
211	android.RegisterParallelSingletonType("dexpreopt-soong-config", func() android.Singleton {
212		return &globalSoongConfigSingleton{}
213	})
214}
215
216func constructPath(ctx android.PathContext, path string) android.Path {
217	buildDirPrefix := ctx.Config().SoongOutDir() + "/"
218	if path == "" {
219		return nil
220	} else if strings.HasPrefix(path, buildDirPrefix) {
221		return android.PathForOutput(ctx, strings.TrimPrefix(path, buildDirPrefix))
222	} else {
223		return android.PathForSource(ctx, path)
224	}
225}
226
227func constructPaths(ctx android.PathContext, paths []string) android.Paths {
228	var ret android.Paths
229	for _, path := range paths {
230		ret = append(ret, constructPath(ctx, path))
231	}
232	return ret
233}
234
235func constructWritablePath(ctx android.PathContext, path string) android.WritablePath {
236	if path == "" {
237		return nil
238	}
239	return constructPath(ctx, path).(android.WritablePath)
240}
241
242// ParseGlobalConfig parses the given data assumed to be read from the global
243// dexpreopt.config file into a GlobalConfig struct.
244func ParseGlobalConfig(ctx android.PathContext, data []byte) (*GlobalConfig, error) {
245	type GlobalJSONConfig struct {
246		*GlobalConfig
247
248		// Copies of entries in GlobalConfig that are not constructable without extra parameters.  They will be
249		// used to construct the real value manually below.
250		BootImageProfiles []string
251	}
252
253	config := GlobalJSONConfig{}
254	err := json.Unmarshal(data, &config)
255	if err != nil {
256		return config.GlobalConfig, err
257	}
258
259	// Construct paths that require a PathContext.
260	config.GlobalConfig.BootImageProfiles = constructPaths(ctx, config.BootImageProfiles)
261
262	return config.GlobalConfig, nil
263}
264
265type globalConfigAndRaw struct {
266	global     *GlobalConfig
267	data       []byte
268	pathErrors []error
269}
270
271// GetGlobalConfig returns the global dexpreopt.config that's created in the
272// make config phase. It is loaded once the first time it is called for any
273// ctx.Config(), and returns the same data for all future calls with the same
274// ctx.Config(). A value can be inserted for tests using
275// setDexpreoptTestGlobalConfig.
276func GetGlobalConfig(ctx android.PathContext) *GlobalConfig {
277	return getGlobalConfigRaw(ctx).global
278}
279
280// GetGlobalConfigRawData is the same as GetGlobalConfig, except that it returns
281// the literal content of dexpreopt.config.
282func GetGlobalConfigRawData(ctx android.PathContext) []byte {
283	return getGlobalConfigRaw(ctx).data
284}
285
286var globalConfigOnceKey = android.NewOnceKey("DexpreoptGlobalConfig")
287var testGlobalConfigOnceKey = android.NewOnceKey("TestDexpreoptGlobalConfig")
288
289type pathContextErrorCollector struct {
290	android.PathContext
291	errors []error
292}
293
294func (p *pathContextErrorCollector) Errorf(format string, args ...interface{}) {
295	p.errors = append(p.errors, fmt.Errorf(format, args...))
296}
297
298func getGlobalConfigRaw(ctx android.PathContext) globalConfigAndRaw {
299	config := ctx.Config().Once(globalConfigOnceKey, func() interface{} {
300		if data, err := ctx.Config().DexpreoptGlobalConfig(ctx); err != nil {
301			panic(err)
302		} else if data != nil {
303			pathErrorCollectorCtx := &pathContextErrorCollector{PathContext: ctx}
304			globalConfig, err := ParseGlobalConfig(pathErrorCollectorCtx, data)
305			if err != nil {
306				panic(err)
307			}
308			return globalConfigAndRaw{globalConfig, data, pathErrorCollectorCtx.errors}
309		}
310
311		// No global config filename set, see if there is a test config set
312		return ctx.Config().Once(testGlobalConfigOnceKey, func() interface{} {
313			// Nope, return a config with preopting disabled
314			return globalConfigAndRaw{&GlobalConfig{
315				DisablePreopt:           true,
316				DisablePreoptBootImages: true,
317				DisableGenerateProfile:  true,
318			}, nil, nil}
319		})
320	}).(globalConfigAndRaw)
321
322	// Avoid non-deterministic errors by reporting cached path errors on all callers.
323	for _, err := range config.pathErrors {
324		if ctx.Config().AllowMissingDependencies() {
325			// When AllowMissingDependencies it set, report errors through AddMissingDependencies.
326			// If AddMissingDependencies doesn't exist on the current context (for example when
327			// called with a SingletonContext), just swallow the errors since there is no way to
328			// report them.
329			if missingDepsCtx, ok := ctx.(interface {
330				AddMissingDependencies(missingDeps []string)
331			}); ok {
332				missingDepsCtx.AddMissingDependencies([]string{err.Error()})
333			}
334		} else {
335			android.ReportPathErrorf(ctx, "%s", err)
336		}
337	}
338
339	return config
340}
341
342// SetTestGlobalConfig sets a GlobalConfig that future calls to GetGlobalConfig
343// will return. It must be called before the first call to GetGlobalConfig for
344// the config.
345func SetTestGlobalConfig(config android.Config, globalConfig *GlobalConfig) {
346	config.Once(testGlobalConfigOnceKey, func() interface{} { return globalConfigAndRaw{globalConfig, nil, nil} })
347}
348
349// This struct is required to convert ModuleConfig from/to JSON.
350// The types of fields in ModuleConfig are not convertible,
351// so moduleJSONConfig has those fields as a convertible type.
352type moduleJSONConfig struct {
353	*ModuleConfig
354
355	BuildPath    string
356	DexPath      string
357	ManifestPath string
358
359	ProfileClassListing string
360	ProfileBootListing  string
361
362	EnforceUsesLibrariesStatusFile string
363	ClassLoaderContexts            jsonClassLoaderContextMap
364
365	DexPreoptImagesDeps [][]string
366
367	PreoptBootClassPathDexFiles []string
368}
369
370// ParseModuleConfig parses a per-module dexpreopt.config file into a
371// ModuleConfig struct. It is not used in Soong, which receives a ModuleConfig
372// struct directly from java/dexpreopt.go. It is used in dexpreopt_gen called
373// from Make to read the module dexpreopt.config written in the Make config
374// stage.
375func ParseModuleConfig(ctx android.PathContext, data []byte) (*ModuleConfig, error) {
376	config := moduleJSONConfig{}
377
378	err := json.Unmarshal(data, &config)
379	if err != nil {
380		return config.ModuleConfig, err
381	}
382
383	// Construct paths that require a PathContext.
384	config.ModuleConfig.BuildPath = constructPath(ctx, config.BuildPath).(android.OutputPath)
385	config.ModuleConfig.DexPath = constructPath(ctx, config.DexPath)
386	config.ModuleConfig.ManifestPath = android.OptionalPathForPath(constructPath(ctx, config.ManifestPath))
387	config.ModuleConfig.ProfileClassListing = android.OptionalPathForPath(constructPath(ctx, config.ProfileClassListing))
388	config.ModuleConfig.EnforceUsesLibrariesStatusFile = constructPath(ctx, config.EnforceUsesLibrariesStatusFile)
389	config.ModuleConfig.ClassLoaderContexts = fromJsonClassLoaderContext(ctx, config.ClassLoaderContexts)
390	config.ModuleConfig.PreoptBootClassPathDexFiles = constructPaths(ctx, config.PreoptBootClassPathDexFiles)
391
392	// This needs to exist, but dependencies are already handled in Make, so we don't need to pass them through JSON.
393	config.ModuleConfig.DexPreoptImagesDeps = make([]android.OutputPaths, len(config.ModuleConfig.Archs))
394
395	return config.ModuleConfig, nil
396}
397
398func pathsListToStringLists(pathsList []android.OutputPaths) [][]string {
399	ret := make([][]string, 0, len(pathsList))
400	for _, paths := range pathsList {
401		ret = append(ret, paths.Strings())
402	}
403	return ret
404}
405
406func moduleConfigToJSON(config *ModuleConfig) ([]byte, error) {
407	return json.MarshalIndent(&moduleJSONConfig{
408		BuildPath:                      config.BuildPath.String(),
409		DexPath:                        config.DexPath.String(),
410		ManifestPath:                   config.ManifestPath.String(),
411		ProfileClassListing:            config.ProfileClassListing.String(),
412		ProfileBootListing:             config.ProfileBootListing.String(),
413		EnforceUsesLibrariesStatusFile: config.EnforceUsesLibrariesStatusFile.String(),
414		ClassLoaderContexts:            toJsonClassLoaderContext(config.ClassLoaderContexts),
415		DexPreoptImagesDeps:            pathsListToStringLists(config.DexPreoptImagesDeps),
416		PreoptBootClassPathDexFiles:    config.PreoptBootClassPathDexFiles.Strings(),
417		ModuleConfig:                   config,
418	}, "", "    ")
419}
420
421// WriteModuleConfig serializes a ModuleConfig into a per-module dexpreopt.config JSON file.
422// These config files are used for post-processing.
423func WriteModuleConfig(ctx android.ModuleContext, config *ModuleConfig, path android.WritablePath) {
424	if path == nil {
425		return
426	}
427
428	data, err := moduleConfigToJSON(config)
429	if err != nil {
430		ctx.ModuleErrorf("failed to JSON marshal module dexpreopt.config: %v", err)
431		return
432	}
433
434	android.WriteFileRule(ctx, path, string(data))
435}
436
437// dex2oatModuleName returns the name of the module to use for the dex2oat host
438// tool. It should be a binary module with public visibility that is compiled
439// and installed for host.
440func dex2oatModuleName(config android.Config) string {
441	// Default to the debug variant of dex2oat to help find bugs.
442	// Set USE_DEX2OAT_DEBUG to false for only building non-debug versions.
443	if config.Getenv("USE_DEX2OAT_DEBUG") == "false" {
444		return "dex2oat"
445	} else {
446		return "dex2oatd"
447	}
448}
449
450type dex2oatDependencyTag struct {
451	blueprint.BaseDependencyTag
452	android.LicenseAnnotationToolchainDependencyTag
453}
454
455func (d dex2oatDependencyTag) ExcludeFromVisibilityEnforcement() {
456}
457
458func (d dex2oatDependencyTag) ExcludeFromApexContents() {
459}
460
461func (d dex2oatDependencyTag) AllowDisabledModuleDependency(target android.Module) bool {
462	// RegisterToolDeps may run after the prebuilt mutators and hence register a
463	// dependency on the source module even when the prebuilt is to be used.
464	// dex2oatPathFromDep takes that into account when it retrieves the path to
465	// the binary, but we also need to disable the check for dependencies on
466	// disabled modules.
467	return target.IsReplacedByPrebuilt()
468}
469
470func (d dex2oatDependencyTag) AllowDisabledModuleDependencyProxy(
471	ctx android.OtherModuleProviderContext, target android.ModuleProxy) bool {
472	return android.OtherModulePointerProviderOrDefault(
473		ctx, target, android.CommonModuleInfoProvider).ReplacedByPrebuilt
474}
475
476// Dex2oatDepTag represents the dependency onto the dex2oatd module. It is added to any module that
477// needs dexpreopting and so it makes no sense for it to be checked for visibility or included in
478// the apex.
479var Dex2oatDepTag = dex2oatDependencyTag{}
480
481var _ android.ExcludeFromVisibilityEnforcementTag = Dex2oatDepTag
482var _ android.ExcludeFromApexContentsTag = Dex2oatDepTag
483var _ android.AllowDisabledModuleDependency = Dex2oatDepTag
484
485// RegisterToolDeps adds the necessary dependencies to binary modules for tools
486// that are required later when Get(Cached)GlobalSoongConfig is called. It
487// should be called from a mutator that's registered with
488// android.RegistrationContext.FinalDepsMutators.
489func RegisterToolDeps(ctx android.BottomUpMutatorContext) {
490	dex2oatBin := dex2oatModuleName(ctx.Config())
491	v := ctx.Config().BuildOSTarget.Variations()
492	ctx.AddFarVariationDependencies(v, Dex2oatDepTag, dex2oatBin)
493}
494
495func IsDex2oatNeeded(ctx android.PathContext) bool {
496	global := GetGlobalConfig(ctx)
497	return !global.DisablePreopt || !global.DisablePreoptBootImages
498}
499
500func dex2oatPathFromDep(ctx android.ModuleContext) android.Path {
501	if !IsDex2oatNeeded(ctx) {
502		return nil
503	}
504
505	dex2oatBin := dex2oatModuleName(ctx.Config())
506
507	// Find the right dex2oat module, trying to follow PrebuiltDepTag from source
508	// to prebuilt if there is one. We wouldn't have to do this if the
509	// prebuilt_postdeps mutator that replaces source deps with prebuilt deps was
510	// run after RegisterToolDeps above, but changing that leads to ordering
511	// problems between mutators (RegisterToolDeps needs to run late to act on
512	// final variants, while prebuilt_postdeps needs to run before many of the
513	// PostDeps mutators, like the APEX mutators). Hence we need to dig out the
514	// prebuilt explicitly here instead.
515	var dex2oatModule android.ModuleProxy
516	ctx.WalkDepsProxy(func(child, parent android.ModuleProxy) bool {
517		prebuiltInfo, isPrebuilt := android.OtherModuleProvider(ctx, child, android.PrebuiltModuleInfoProvider)
518		if android.EqualModules(parent, ctx.Module()) && ctx.OtherModuleDependencyTag(child) == Dex2oatDepTag {
519			// Found the source module, or prebuilt module that has replaced the source.
520			dex2oatModule = child
521			if isPrebuilt {
522				return false // If it's the prebuilt we're done.
523			} else {
524				return true // Recurse to check if the source has a prebuilt dependency.
525			}
526		}
527		if android.EqualModules(parent, dex2oatModule) && ctx.OtherModuleDependencyTag(child) == android.PrebuiltDepTag {
528			if isPrebuilt && prebuiltInfo.UsePrebuilt {
529				dex2oatModule = child // Found a prebuilt that should be used.
530			}
531		}
532		return false
533	})
534
535	if dex2oatModule.IsNil() {
536		// If this happens there's probably a missing call to AddToolDeps in DepsMutator.
537		panic(fmt.Sprintf("Failed to lookup %s dependency", dex2oatBin))
538	}
539
540	dex2oatPath, ok := android.OtherModuleProvider(ctx, dex2oatModule, android.HostToolProviderInfoProvider)
541	if !ok || !dex2oatPath.HostToolPath.Valid() {
542		panic(fmt.Sprintf("Failed to find host tool path in %s", dex2oatModule))
543	}
544
545	return dex2oatPath.HostToolPath.Path()
546}
547
548// createGlobalSoongConfig creates a GlobalSoongConfig from the current context.
549// Should not be used in dexpreopt_gen.
550func createGlobalSoongConfig(ctx android.ModuleContext) *GlobalSoongConfig {
551	return &GlobalSoongConfig{
552		Profman:          ctx.Config().HostToolPath(ctx, "profman"),
553		Dex2oat:          dex2oatPathFromDep(ctx),
554		Aapt:             ctx.Config().HostToolPath(ctx, "aapt2"),
555		SoongZip:         ctx.Config().HostToolPath(ctx, "soong_zip"),
556		Zip2zip:          ctx.Config().HostToolPath(ctx, "zip2zip"),
557		ManifestCheck:    ctx.Config().HostToolPath(ctx, "manifest_check"),
558		ConstructContext: ctx.Config().HostToolPath(ctx, "construct_context"),
559		UffdGcFlag:       getUffdGcFlagPath(ctx),
560	}
561}
562
563// The main reason for this Once cache for GlobalSoongConfig is to make the
564// dex2oat path available to singletons. In ordinary modules we get it through a
565// Dex2oatDepTag dependency, but in singletons there's no simple way to do the
566// same thing and ensure the right variant is selected, hence this cache to make
567// the resolved path available to singletons. This means we depend on there
568// being at least one ordinary module with a Dex2oatDepTag dependency.
569//
570// TODO(b/147613152): Implement a way to deal with dependencies from singletons,
571// and then possibly remove this cache altogether.
572var globalSoongConfigOnceKey = android.NewOnceKey("DexpreoptGlobalSoongConfig")
573
574// GetGlobalSoongConfig creates a GlobalSoongConfig the first time it's called,
575// and later returns the same cached instance.
576func GetGlobalSoongConfig(ctx android.ModuleContext) *GlobalSoongConfig {
577	globalSoong := ctx.Config().Once(globalSoongConfigOnceKey, func() interface{} {
578		return createGlobalSoongConfig(ctx)
579	}).(*GlobalSoongConfig)
580
581	// Always resolve the tool path from the dependency, to ensure that every
582	// module has the dependency added properly.
583	myDex2oat := dex2oatPathFromDep(ctx)
584	if myDex2oat != globalSoong.Dex2oat {
585		panic(fmt.Sprintf("Inconsistent dex2oat path in cached config: expected %s, got %s", globalSoong.Dex2oat, myDex2oat))
586	}
587
588	return globalSoong
589}
590
591// GetCachedGlobalSoongConfig returns a cached GlobalSoongConfig created by an
592// earlier GetGlobalSoongConfig call. This function works with any context
593// compatible with a basic PathContext, since it doesn't try to create a
594// GlobalSoongConfig with the proper paths (which requires a full
595// ModuleContext). If there has been no prior call to GetGlobalSoongConfig, nil
596// is returned.
597func GetCachedGlobalSoongConfig(ctx android.PathContext) *GlobalSoongConfig {
598	return ctx.Config().Once(globalSoongConfigOnceKey, func() interface{} {
599		return (*GlobalSoongConfig)(nil)
600	}).(*GlobalSoongConfig)
601}
602
603type globalJsonSoongConfig struct {
604	Profman          string
605	Dex2oat          string
606	Aapt             string
607	SoongZip         string
608	Zip2zip          string
609	ManifestCheck    string
610	ConstructContext string
611	UffdGcFlag       string
612}
613
614// ParseGlobalSoongConfig parses the given data assumed to be read from the
615// global dexpreopt_soong.config file into a GlobalSoongConfig struct. It is
616// only used in dexpreopt_gen.
617func ParseGlobalSoongConfig(ctx android.PathContext, data []byte) (*GlobalSoongConfig, error) {
618	var jc globalJsonSoongConfig
619
620	err := json.Unmarshal(data, &jc)
621	if err != nil {
622		return &GlobalSoongConfig{}, err
623	}
624
625	config := &GlobalSoongConfig{
626		Profman:          constructPath(ctx, jc.Profman),
627		Dex2oat:          constructPath(ctx, jc.Dex2oat),
628		Aapt:             constructPath(ctx, jc.Aapt),
629		SoongZip:         constructPath(ctx, jc.SoongZip),
630		Zip2zip:          constructPath(ctx, jc.Zip2zip),
631		ManifestCheck:    constructPath(ctx, jc.ManifestCheck),
632		ConstructContext: constructPath(ctx, jc.ConstructContext),
633		UffdGcFlag:       constructWritablePath(ctx, jc.UffdGcFlag),
634	}
635
636	return config, nil
637}
638
639// checkBootJarsConfigConsistency checks the consistency of BootJars and ApexBootJars fields in
640// DexpreoptGlobalConfig and Config.productVariables.
641func checkBootJarsConfigConsistency(ctx android.SingletonContext, dexpreoptConfig *GlobalConfig, config android.Config) {
642	compareBootJars := func(property string, dexpreoptJars, variableJars android.ConfiguredJarList) {
643		dexpreoptPairs := dexpreoptJars.CopyOfApexJarPairs()
644		variablePairs := variableJars.CopyOfApexJarPairs()
645		if !reflect.DeepEqual(dexpreoptPairs, variablePairs) {
646			ctx.Errorf("Inconsistent configuration of %[1]s\n"+
647				"    dexpreopt.GlobalConfig.%[1]s = %[2]s\n"+
648				"    productVariables.%[1]s       = %[3]s",
649				property, dexpreoptPairs, variablePairs)
650		}
651	}
652
653	compareBootJars("BootJars", dexpreoptConfig.BootJars, config.NonApexBootJars())
654	compareBootJars("ApexBootJars", dexpreoptConfig.ApexBootJars, config.ApexBootJars())
655}
656
657func (s *globalSoongConfigSingleton) GenerateBuildActions(ctx android.SingletonContext) {
658	global := GetGlobalConfig(ctx)
659	checkBootJarsConfigConsistency(ctx, global, ctx.Config())
660
661	if global.DisablePreopt {
662		return
663	}
664
665	buildUffdGcFlag(ctx, global)
666
667	config := GetCachedGlobalSoongConfig(ctx)
668	if config == nil {
669		// No module has enabled dexpreopting, so we assume there will be no calls
670		// to dexpreopt_gen.
671		return
672	}
673
674	jc := globalJsonSoongConfig{
675		Profman:          config.Profman.String(),
676		Dex2oat:          config.Dex2oat.String(),
677		Aapt:             config.Aapt.String(),
678		SoongZip:         config.SoongZip.String(),
679		Zip2zip:          config.Zip2zip.String(),
680		ManifestCheck:    config.ManifestCheck.String(),
681		ConstructContext: config.ConstructContext.String(),
682		UffdGcFlag:       config.UffdGcFlag.String(),
683	}
684
685	data, err := json.Marshal(jc)
686	if err != nil {
687		ctx.Errorf("failed to JSON marshal GlobalSoongConfig: %v", err)
688		return
689	}
690
691	android.WriteFileRule(ctx, android.PathForOutput(ctx, "dexpreopt_soong.config"), string(data))
692}
693
694func (s *globalSoongConfigSingleton) MakeVars(ctx android.MakeVarsContext) {
695	if GetGlobalConfig(ctx).DisablePreopt {
696		return
697	}
698
699	config := GetCachedGlobalSoongConfig(ctx)
700	if config == nil {
701		return
702	}
703
704	ctx.Strict("DEX2OAT", config.Dex2oat.String())
705	ctx.Strict("DEXPREOPT_GEN_DEPS", strings.Join([]string{
706		config.Profman.String(),
707		config.Dex2oat.String(),
708		config.Aapt.String(),
709		config.SoongZip.String(),
710		config.Zip2zip.String(),
711		config.ManifestCheck.String(),
712		config.ConstructContext.String(),
713		config.UffdGcFlag.String(),
714	}, " "))
715}
716
717func buildUffdGcFlag(ctx android.BuilderContext, global *GlobalConfig) {
718	uffdGcFlag := getUffdGcFlagPath(ctx)
719
720	if global.EnableUffdGc == "true" {
721		android.WriteFileRuleVerbatim(ctx, uffdGcFlag, "--runtime-arg -Xgc:CMC")
722	} else if global.EnableUffdGc == "false" {
723		android.WriteFileRuleVerbatim(ctx, uffdGcFlag, "")
724	} else if global.EnableUffdGc == "default" {
725		// Generated by build/make/core/Makefile, or the android_device module in soong-only builds.
726		kernelVersionFile := android.PathForOutput(ctx, "dexpreopt/kernel_version_for_uffd_gc.txt")
727
728		// Determine the UFFD GC flag by the kernel version file.
729		rule := android.NewRuleBuilder(pctx, ctx)
730		rule.Command().
731			Tool(ctx.Config().HostToolPath(ctx, "construct_uffd_gc_flag")).
732			Input(kernelVersionFile).
733			Output(uffdGcFlag)
734		rule.Restat().Build("dexpreopt_uffd_gc_flag", "dexpreopt_uffd_gc_flag")
735	} else {
736		panic(fmt.Sprintf("Unknown value of PRODUCT_ENABLE_UFFD_GC: %s", global.EnableUffdGc))
737	}
738}
739
740func GlobalConfigForTests(ctx android.PathContext) *GlobalConfig {
741	return &GlobalConfig{
742		DisablePreopt:                  false,
743		DisablePreoptModules:           nil,
744		OnlyPreoptArtBootImage:         false,
745		HasSystemOther:                 false,
746		PatternsOnSystemOther:          nil,
747		DisableGenerateProfile:         false,
748		ProfileDir:                     "",
749		BootJars:                       android.EmptyConfiguredJarList(),
750		ApexBootJars:                   android.EmptyConfiguredJarList(),
751		ArtApexJars:                    android.EmptyConfiguredJarList(),
752		TestOnlyArtBootImageJars:       android.EmptyConfiguredJarList(),
753		SystemServerJars:               android.EmptyConfiguredJarList(),
754		SystemServerApps:               nil,
755		ApexSystemServerJars:           android.EmptyConfiguredJarList(),
756		StandaloneSystemServerJars:     android.EmptyConfiguredJarList(),
757		ApexStandaloneSystemServerJars: android.EmptyConfiguredJarList(),
758		SpeedApps:                      nil,
759		PreoptFlags:                    nil,
760		DefaultCompilerFilter:          "",
761		SystemServerCompilerFilter:     "",
762		GenerateDMFiles:                false,
763		NoDebugInfo:                    false,
764		DontResolveStartupStrings:      false,
765		AlwaysSystemServerDebugInfo:    false,
766		NeverSystemServerDebugInfo:     false,
767		AlwaysOtherDebugInfo:           false,
768		NeverOtherDebugInfo:            false,
769		IsEng:                          false,
770		SanitizeLite:                   false,
771		DefaultAppImages:               false,
772		Dex2oatXmx:                     "",
773		Dex2oatXms:                     "",
774		EmptyDirectory:                 "empty_dir",
775		CpuVariant:                     nil,
776		InstructionSetFeatures:         nil,
777		BootImageProfiles:              nil,
778		BootFlags:                      "",
779		Dex2oatImageXmx:                "",
780		Dex2oatImageXms:                "",
781	}
782}
783
784func globalSoongConfigForTests(ctx android.BuilderContext) *GlobalSoongConfig {
785	return &GlobalSoongConfig{
786		Profman:          android.PathForTesting("profman"),
787		Dex2oat:          android.PathForTesting("dex2oat"),
788		Aapt:             android.PathForTesting("aapt2"),
789		SoongZip:         android.PathForTesting("soong_zip"),
790		Zip2zip:          android.PathForTesting("zip2zip"),
791		ManifestCheck:    android.PathForTesting("manifest_check"),
792		ConstructContext: android.PathForTesting("construct_context"),
793		UffdGcFlag:       android.PathForOutput(ctx, "dexpreopt_test", "uffd_gc_flag.txt"),
794	}
795}
796
797func GetDexpreoptDirName(ctx android.PathContext) string {
798	prefix := "dexpreopt_"
799	targets := ctx.Config().Targets[android.Android]
800	if len(targets) > 0 {
801		return prefix + targets[0].Arch.ArchType.String()
802	}
803	return prefix + "unknown_target"
804}
805
806func getUffdGcFlagPath(ctx android.PathContext) android.WritablePath {
807	return android.PathForOutput(ctx, "dexpreopt/uffd_gc_flag.txt")
808}
809