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