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	"strings"
21
22	"github.com/google/blueprint"
23
24	"android/soong/android"
25)
26
27// GlobalConfig stores the configuration for dex preopting. The fields are set
28// from product variables via dex_preopt_config.mk.
29type GlobalConfig struct {
30	DisablePreopt        bool     // disable preopt for all modules
31	DisablePreoptModules []string // modules with preopt disabled by product-specific config
32
33	OnlyPreoptBootImageAndSystemServer bool // only preopt jars in the boot image or system server
34
35	UseArtImage bool // use the art image (use other boot class path dex files without image)
36
37	HasSystemOther        bool     // store odex files that match PatternsOnSystemOther on the system_other partition
38	PatternsOnSystemOther []string // patterns (using '%' to denote a prefix match) to put odex on the system_other partition
39
40	DisableGenerateProfile bool   // don't generate profiles
41	ProfileDir             string // directory to find profiles in
42
43	BootJars          []string // modules for jars that form the boot class path
44	UpdatableBootJars []string // jars within apex that form the boot class path
45
46	ArtApexJars []string // modules for jars that are in the ART APEX
47
48	SystemServerJars          []string // jars that form the system server
49	SystemServerApps          []string // apps that are loaded into system server
50	UpdatableSystemServerJars []string // jars within apex that are loaded into system server
51	SpeedApps                 []string // apps that should be speed optimized
52
53	BrokenSuboptimalOrderOfSystemServerJars bool // if true, sub-optimal order does not cause a build error
54
55	PreoptFlags []string // global dex2oat flags that should be used if no module-specific dex2oat flags are specified
56
57	DefaultCompilerFilter      string // default compiler filter to pass to dex2oat, overridden by --compiler-filter= in module-specific dex2oat flags
58	SystemServerCompilerFilter string // default compiler filter to pass to dex2oat for system server jars
59
60	GenerateDMFiles bool // generate Dex Metadata files
61
62	NoDebugInfo                 bool // don't generate debug info by default
63	DontResolveStartupStrings   bool // don't resolve string literals loaded during application startup.
64	AlwaysSystemServerDebugInfo bool // always generate mini debug info for system server modules (overrides NoDebugInfo=true)
65	NeverSystemServerDebugInfo  bool // never generate mini debug info for system server modules (overrides NoDebugInfo=false)
66	AlwaysOtherDebugInfo        bool // always generate mini debug info for non-system server modules (overrides NoDebugInfo=true)
67	NeverOtherDebugInfo         bool // never generate mini debug info for non-system server modules (overrides NoDebugInfo=true)
68
69	IsEng        bool // build is a eng variant
70	SanitizeLite bool // build is the second phase of a SANITIZE_LITE build
71
72	DefaultAppImages bool // build app images (TODO: .art files?) by default
73
74	Dex2oatXmx string // max heap size for dex2oat
75	Dex2oatXms string // initial heap size for dex2oat
76
77	EmptyDirectory string // path to an empty directory
78
79	CpuVariant             map[android.ArchType]string // cpu variant for each architecture
80	InstructionSetFeatures map[android.ArchType]string // instruction set for each architecture
81
82	// Only used for boot image
83	DirtyImageObjects android.OptionalPath // path to a dirty-image-objects file
84	BootImageProfiles android.Paths        // path to a boot-image-profile.txt file
85	BootFlags         string               // extra flags to pass to dex2oat for the boot image
86	Dex2oatImageXmx   string               // max heap size for dex2oat for the boot image
87	Dex2oatImageXms   string               // initial heap size for dex2oat for the boot image
88}
89
90// GlobalSoongConfig contains the global config that is generated from Soong,
91// stored in dexpreopt_soong.config.
92type GlobalSoongConfig struct {
93	// Paths to tools possibly used by the generated commands.
94	Profman          android.Path
95	Dex2oat          android.Path
96	Aapt             android.Path
97	SoongZip         android.Path
98	Zip2zip          android.Path
99	ManifestCheck    android.Path
100	ConstructContext android.Path
101}
102
103// LibraryPath contains paths to the library DEX jar on host and on device.
104type LibraryPath struct {
105	Host   android.Path
106	Device string
107}
108
109// LibraryPaths is a map from library name to on-host and on-device paths to its DEX jar.
110type LibraryPaths map[string]*LibraryPath
111
112type ModuleConfig struct {
113	Name            string
114	DexLocation     string // dex location on device
115	BuildPath       android.OutputPath
116	DexPath         android.Path
117	ManifestPath    android.Path
118	UncompressedDex bool
119	HasApkLibraries bool
120	PreoptFlags     []string
121
122	ProfileClassListing  android.OptionalPath
123	ProfileIsTextListing bool
124	ProfileBootListing   android.OptionalPath
125
126	EnforceUsesLibraries  bool
127	OptionalUsesLibraries []string
128	UsesLibraries         []string
129	LibraryPaths          LibraryPaths
130
131	Archs                   []android.ArchType
132	DexPreoptImages         []android.Path
133	DexPreoptImagesDeps     []android.OutputPaths
134	DexPreoptImageLocations []string
135
136	PreoptBootClassPathDexFiles     android.Paths // file paths of boot class path files
137	PreoptBootClassPathDexLocations []string      // virtual locations of boot class path files
138
139	PreoptExtractedApk bool // Overrides OnlyPreoptModules
140
141	NoCreateAppImage    bool
142	ForceCreateAppImage bool
143
144	PresignedPrebuilt bool
145}
146
147type globalSoongConfigSingleton struct{}
148
149var pctx = android.NewPackageContext("android/soong/dexpreopt")
150
151func init() {
152	pctx.Import("android/soong/android")
153	android.RegisterSingletonType("dexpreopt-soong-config", func() android.Singleton {
154		return &globalSoongConfigSingleton{}
155	})
156}
157
158func constructPath(ctx android.PathContext, path string) android.Path {
159	buildDirPrefix := ctx.Config().BuildDir() + "/"
160	if path == "" {
161		return nil
162	} else if strings.HasPrefix(path, buildDirPrefix) {
163		return android.PathForOutput(ctx, strings.TrimPrefix(path, buildDirPrefix))
164	} else {
165		return android.PathForSource(ctx, path)
166	}
167}
168
169func constructPaths(ctx android.PathContext, paths []string) android.Paths {
170	var ret android.Paths
171	for _, path := range paths {
172		ret = append(ret, constructPath(ctx, path))
173	}
174	return ret
175}
176
177func constructWritablePath(ctx android.PathContext, path string) android.WritablePath {
178	if path == "" {
179		return nil
180	}
181	return constructPath(ctx, path).(android.WritablePath)
182}
183
184// ParseGlobalConfig parses the given data assumed to be read from the global
185// dexpreopt.config file into a GlobalConfig struct.
186func ParseGlobalConfig(ctx android.PathContext, data []byte) (*GlobalConfig, error) {
187	type GlobalJSONConfig struct {
188		*GlobalConfig
189
190		// Copies of entries in GlobalConfig that are not constructable without extra parameters.  They will be
191		// used to construct the real value manually below.
192		DirtyImageObjects string
193		BootImageProfiles []string
194	}
195
196	config := GlobalJSONConfig{}
197	err := json.Unmarshal(data, &config)
198	if err != nil {
199		return config.GlobalConfig, err
200	}
201
202	// Construct paths that require a PathContext.
203	config.GlobalConfig.DirtyImageObjects = android.OptionalPathForPath(constructPath(ctx, config.DirtyImageObjects))
204	config.GlobalConfig.BootImageProfiles = constructPaths(ctx, config.BootImageProfiles)
205
206	return config.GlobalConfig, nil
207}
208
209type globalConfigAndRaw struct {
210	global *GlobalConfig
211	data   []byte
212}
213
214// GetGlobalConfig returns the global dexpreopt.config that's created in the
215// make config phase. It is loaded once the first time it is called for any
216// ctx.Config(), and returns the same data for all future calls with the same
217// ctx.Config(). A value can be inserted for tests using
218// setDexpreoptTestGlobalConfig.
219func GetGlobalConfig(ctx android.PathContext) *GlobalConfig {
220	return getGlobalConfigRaw(ctx).global
221}
222
223// GetGlobalConfigRawData is the same as GetGlobalConfig, except that it returns
224// the literal content of dexpreopt.config.
225func GetGlobalConfigRawData(ctx android.PathContext) []byte {
226	return getGlobalConfigRaw(ctx).data
227}
228
229var globalConfigOnceKey = android.NewOnceKey("DexpreoptGlobalConfig")
230var testGlobalConfigOnceKey = android.NewOnceKey("TestDexpreoptGlobalConfig")
231
232func getGlobalConfigRaw(ctx android.PathContext) globalConfigAndRaw {
233	return ctx.Config().Once(globalConfigOnceKey, func() interface{} {
234		if data, err := ctx.Config().DexpreoptGlobalConfig(ctx); err != nil {
235			panic(err)
236		} else if data != nil {
237			globalConfig, err := ParseGlobalConfig(ctx, data)
238			if err != nil {
239				panic(err)
240			}
241			return globalConfigAndRaw{globalConfig, data}
242		}
243
244		// No global config filename set, see if there is a test config set
245		return ctx.Config().Once(testGlobalConfigOnceKey, func() interface{} {
246			// Nope, return a config with preopting disabled
247			return globalConfigAndRaw{&GlobalConfig{
248				DisablePreopt:          true,
249				DisableGenerateProfile: true,
250			}, nil}
251		})
252	}).(globalConfigAndRaw)
253}
254
255// SetTestGlobalConfig sets a GlobalConfig that future calls to GetGlobalConfig
256// will return. It must be called before the first call to GetGlobalConfig for
257// the config.
258func SetTestGlobalConfig(config android.Config, globalConfig *GlobalConfig) {
259	config.Once(testGlobalConfigOnceKey, func() interface{} { return globalConfigAndRaw{globalConfig, nil} })
260}
261
262// ParseModuleConfig parses a per-module dexpreopt.config file into a
263// ModuleConfig struct. It is not used in Soong, which receives a ModuleConfig
264// struct directly from java/dexpreopt.go. It is used in dexpreopt_gen called
265// from Make to read the module dexpreopt.config written in the Make config
266// stage.
267func ParseModuleConfig(ctx android.PathContext, data []byte) (*ModuleConfig, error) {
268	type jsonLibraryPath struct {
269		Host   string
270		Device string
271	}
272
273	type jsonLibraryPaths map[string]jsonLibraryPath
274
275	type ModuleJSONConfig struct {
276		*ModuleConfig
277
278		// Copies of entries in ModuleConfig that are not constructable without extra parameters.  They will be
279		// used to construct the real value manually below.
280		BuildPath                   string
281		DexPath                     string
282		ManifestPath                string
283		ProfileClassListing         string
284		LibraryPaths                jsonLibraryPaths
285		DexPreoptImages             []string
286		DexPreoptImageLocations     []string
287		PreoptBootClassPathDexFiles []string
288	}
289
290	// convert JSON map of library paths to LibraryPaths
291	constructLibraryPaths := func(ctx android.PathContext, paths jsonLibraryPaths) LibraryPaths {
292		m := LibraryPaths{}
293		for lib, path := range paths {
294			m[lib] = &LibraryPath{
295				constructPath(ctx, path.Host),
296				path.Device,
297			}
298		}
299		return m
300	}
301
302	config := ModuleJSONConfig{}
303
304	err := json.Unmarshal(data, &config)
305	if err != nil {
306		return config.ModuleConfig, err
307	}
308
309	// Construct paths that require a PathContext.
310	config.ModuleConfig.BuildPath = constructPath(ctx, config.BuildPath).(android.OutputPath)
311	config.ModuleConfig.DexPath = constructPath(ctx, config.DexPath)
312	config.ModuleConfig.ManifestPath = constructPath(ctx, config.ManifestPath)
313	config.ModuleConfig.ProfileClassListing = android.OptionalPathForPath(constructPath(ctx, config.ProfileClassListing))
314	config.ModuleConfig.LibraryPaths = constructLibraryPaths(ctx, config.LibraryPaths)
315	config.ModuleConfig.DexPreoptImages = constructPaths(ctx, config.DexPreoptImages)
316	config.ModuleConfig.DexPreoptImageLocations = config.DexPreoptImageLocations
317	config.ModuleConfig.PreoptBootClassPathDexFiles = constructPaths(ctx, config.PreoptBootClassPathDexFiles)
318
319	// This needs to exist, but dependencies are already handled in Make, so we don't need to pass them through JSON.
320	config.ModuleConfig.DexPreoptImagesDeps = make([]android.OutputPaths, len(config.ModuleConfig.DexPreoptImages))
321
322	return config.ModuleConfig, nil
323}
324
325// dex2oatModuleName returns the name of the module to use for the dex2oat host
326// tool. It should be a binary module with public visibility that is compiled
327// and installed for host.
328func dex2oatModuleName(config android.Config) string {
329	// Default to the debug variant of dex2oat to help find bugs.
330	// Set USE_DEX2OAT_DEBUG to false for only building non-debug versions.
331	if config.Getenv("USE_DEX2OAT_DEBUG") == "false" {
332		return "dex2oat"
333	} else {
334		return "dex2oatd"
335	}
336}
337
338var dex2oatDepTag = struct {
339	blueprint.BaseDependencyTag
340}{}
341
342// RegisterToolDeps adds the necessary dependencies to binary modules for tools
343// that are required later when Get(Cached)GlobalSoongConfig is called. It
344// should be called from a mutator that's registered with
345// android.RegistrationContext.FinalDepsMutators.
346func RegisterToolDeps(ctx android.BottomUpMutatorContext) {
347	dex2oatBin := dex2oatModuleName(ctx.Config())
348	v := ctx.Config().BuildOSTarget.Variations()
349	ctx.AddFarVariationDependencies(v, dex2oatDepTag, dex2oatBin)
350}
351
352func dex2oatPathFromDep(ctx android.ModuleContext) android.Path {
353	dex2oatBin := dex2oatModuleName(ctx.Config())
354
355	dex2oatModule := ctx.GetDirectDepWithTag(dex2oatBin, dex2oatDepTag)
356	if dex2oatModule == nil {
357		// If this happens there's probably a missing call to AddToolDeps in DepsMutator.
358		panic(fmt.Sprintf("Failed to lookup %s dependency", dex2oatBin))
359	}
360
361	dex2oatPath := dex2oatModule.(android.HostToolProvider).HostToolPath()
362	if !dex2oatPath.Valid() {
363		panic(fmt.Sprintf("Failed to find host tool path in %s", dex2oatModule))
364	}
365
366	return dex2oatPath.Path()
367}
368
369// createGlobalSoongConfig creates a GlobalSoongConfig from the current context.
370// Should not be used in dexpreopt_gen.
371func createGlobalSoongConfig(ctx android.ModuleContext) *GlobalSoongConfig {
372	if ctx.Config().TestProductVariables != nil {
373		// If we're called in a test there'll be a confusing error from the path
374		// functions below that gets reported without a stack trace, so let's panic
375		// properly with a more helpful message.
376		panic("This should not be called from tests. Please call GlobalSoongConfigForTests somewhere in the test setup.")
377	}
378
379	return &GlobalSoongConfig{
380		Profman:          ctx.Config().HostToolPath(ctx, "profman"),
381		Dex2oat:          dex2oatPathFromDep(ctx),
382		Aapt:             ctx.Config().HostToolPath(ctx, "aapt"),
383		SoongZip:         ctx.Config().HostToolPath(ctx, "soong_zip"),
384		Zip2zip:          ctx.Config().HostToolPath(ctx, "zip2zip"),
385		ManifestCheck:    ctx.Config().HostToolPath(ctx, "manifest_check"),
386		ConstructContext: ctx.Config().HostToolPath(ctx, "construct_context"),
387	}
388}
389
390// The main reason for this Once cache for GlobalSoongConfig is to make the
391// dex2oat path available to singletons. In ordinary modules we get it through a
392// dex2oatDepTag dependency, but in singletons there's no simple way to do the
393// same thing and ensure the right variant is selected, hence this cache to make
394// the resolved path available to singletons. This means we depend on there
395// being at least one ordinary module with a dex2oatDepTag dependency.
396//
397// TODO(b/147613152): Implement a way to deal with dependencies from singletons,
398// and then possibly remove this cache altogether (but the use in
399// GlobalSoongConfigForTests also needs to be rethought).
400var globalSoongConfigOnceKey = android.NewOnceKey("DexpreoptGlobalSoongConfig")
401
402// GetGlobalSoongConfig creates a GlobalSoongConfig the first time it's called,
403// and later returns the same cached instance.
404func GetGlobalSoongConfig(ctx android.ModuleContext) *GlobalSoongConfig {
405	globalSoong := ctx.Config().Once(globalSoongConfigOnceKey, func() interface{} {
406		return createGlobalSoongConfig(ctx)
407	}).(*GlobalSoongConfig)
408
409	// Always resolve the tool path from the dependency, to ensure that every
410	// module has the dependency added properly.
411	myDex2oat := dex2oatPathFromDep(ctx)
412	if myDex2oat != globalSoong.Dex2oat {
413		panic(fmt.Sprintf("Inconsistent dex2oat path in cached config: expected %s, got %s", globalSoong.Dex2oat, myDex2oat))
414	}
415
416	return globalSoong
417}
418
419// GetCachedGlobalSoongConfig returns a cached GlobalSoongConfig created by an
420// earlier GetGlobalSoongConfig call. This function works with any context
421// compatible with a basic PathContext, since it doesn't try to create a
422// GlobalSoongConfig with the proper paths (which requires a full
423// ModuleContext). If there has been no prior call to GetGlobalSoongConfig, nil
424// is returned.
425func GetCachedGlobalSoongConfig(ctx android.PathContext) *GlobalSoongConfig {
426	return ctx.Config().Once(globalSoongConfigOnceKey, func() interface{} {
427		return (*GlobalSoongConfig)(nil)
428	}).(*GlobalSoongConfig)
429}
430
431type globalJsonSoongConfig struct {
432	Profman          string
433	Dex2oat          string
434	Aapt             string
435	SoongZip         string
436	Zip2zip          string
437	ManifestCheck    string
438	ConstructContext string
439}
440
441// ParseGlobalSoongConfig parses the given data assumed to be read from the
442// global dexpreopt_soong.config file into a GlobalSoongConfig struct. It is
443// only used in dexpreopt_gen.
444func ParseGlobalSoongConfig(ctx android.PathContext, data []byte) (*GlobalSoongConfig, error) {
445	var jc globalJsonSoongConfig
446
447	err := json.Unmarshal(data, &jc)
448	if err != nil {
449		return &GlobalSoongConfig{}, err
450	}
451
452	config := &GlobalSoongConfig{
453		Profman:          constructPath(ctx, jc.Profman),
454		Dex2oat:          constructPath(ctx, jc.Dex2oat),
455		Aapt:             constructPath(ctx, jc.Aapt),
456		SoongZip:         constructPath(ctx, jc.SoongZip),
457		Zip2zip:          constructPath(ctx, jc.Zip2zip),
458		ManifestCheck:    constructPath(ctx, jc.ManifestCheck),
459		ConstructContext: constructPath(ctx, jc.ConstructContext),
460	}
461
462	return config, nil
463}
464
465func (s *globalSoongConfigSingleton) GenerateBuildActions(ctx android.SingletonContext) {
466	if GetGlobalConfig(ctx).DisablePreopt {
467		return
468	}
469
470	config := GetCachedGlobalSoongConfig(ctx)
471	if config == nil {
472		// No module has enabled dexpreopting, so we assume there will be no calls
473		// to dexpreopt_gen.
474		return
475	}
476
477	jc := globalJsonSoongConfig{
478		Profman:          config.Profman.String(),
479		Dex2oat:          config.Dex2oat.String(),
480		Aapt:             config.Aapt.String(),
481		SoongZip:         config.SoongZip.String(),
482		Zip2zip:          config.Zip2zip.String(),
483		ManifestCheck:    config.ManifestCheck.String(),
484		ConstructContext: config.ConstructContext.String(),
485	}
486
487	data, err := json.Marshal(jc)
488	if err != nil {
489		ctx.Errorf("failed to JSON marshal GlobalSoongConfig: %v", err)
490		return
491	}
492
493	ctx.Build(pctx, android.BuildParams{
494		Rule:   android.WriteFile,
495		Output: android.PathForOutput(ctx, "dexpreopt_soong.config"),
496		Args: map[string]string{
497			"content": string(data),
498		},
499	})
500}
501
502func (s *globalSoongConfigSingleton) MakeVars(ctx android.MakeVarsContext) {
503	if GetGlobalConfig(ctx).DisablePreopt {
504		return
505	}
506
507	config := GetCachedGlobalSoongConfig(ctx)
508	if config == nil {
509		return
510	}
511
512	ctx.Strict("DEX2OAT", config.Dex2oat.String())
513	ctx.Strict("DEXPREOPT_GEN_DEPS", strings.Join([]string{
514		config.Profman.String(),
515		config.Dex2oat.String(),
516		config.Aapt.String(),
517		config.SoongZip.String(),
518		config.Zip2zip.String(),
519		config.ManifestCheck.String(),
520		config.ConstructContext.String(),
521	}, " "))
522}
523
524func GlobalConfigForTests(ctx android.PathContext) *GlobalConfig {
525	return &GlobalConfig{
526		DisablePreopt:                      false,
527		DisablePreoptModules:               nil,
528		OnlyPreoptBootImageAndSystemServer: false,
529		HasSystemOther:                     false,
530		PatternsOnSystemOther:              nil,
531		DisableGenerateProfile:             false,
532		ProfileDir:                         "",
533		BootJars:                           nil,
534		UpdatableBootJars:                  nil,
535		ArtApexJars:                        nil,
536		SystemServerJars:                   nil,
537		SystemServerApps:                   nil,
538		UpdatableSystemServerJars:          nil,
539		SpeedApps:                          nil,
540		PreoptFlags:                        nil,
541		DefaultCompilerFilter:              "",
542		SystemServerCompilerFilter:         "",
543		GenerateDMFiles:                    false,
544		NoDebugInfo:                        false,
545		DontResolveStartupStrings:          false,
546		AlwaysSystemServerDebugInfo:        false,
547		NeverSystemServerDebugInfo:         false,
548		AlwaysOtherDebugInfo:               false,
549		NeverOtherDebugInfo:                false,
550		IsEng:                              false,
551		SanitizeLite:                       false,
552		DefaultAppImages:                   false,
553		Dex2oatXmx:                         "",
554		Dex2oatXms:                         "",
555		EmptyDirectory:                     "empty_dir",
556		CpuVariant:                         nil,
557		InstructionSetFeatures:             nil,
558		DirtyImageObjects:                  android.OptionalPath{},
559		BootImageProfiles:                  nil,
560		BootFlags:                          "",
561		Dex2oatImageXmx:                    "",
562		Dex2oatImageXms:                    "",
563	}
564}
565
566func GlobalSoongConfigForTests(config android.Config) *GlobalSoongConfig {
567	// Install the test GlobalSoongConfig in the Once cache so that later calls to
568	// Get(Cached)GlobalSoongConfig returns it without trying to create a real one.
569	return config.Once(globalSoongConfigOnceKey, func() interface{} {
570		return &GlobalSoongConfig{
571			Profman:          android.PathForTesting("profman"),
572			Dex2oat:          android.PathForTesting("dex2oat"),
573			Aapt:             android.PathForTesting("aapt"),
574			SoongZip:         android.PathForTesting("soong_zip"),
575			Zip2zip:          android.PathForTesting("zip2zip"),
576			ManifestCheck:    android.PathForTesting("manifest_check"),
577			ConstructContext: android.PathForTesting("construct_context"),
578		}
579	}).(*GlobalSoongConfig)
580}
581