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