• 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
15// The dexpreopt package converts a global dexpreopt config and a module dexpreopt config into rules to perform
16// dexpreopting.
17//
18// It is used in two places; in the dexpeopt_gen binary for modules defined in Make, and directly linked into Soong.
19//
20// For Make modules it is built into the dexpreopt_gen binary, which is executed as a Make rule using global config and
21// module config specified in JSON files.  The binary writes out two shell scripts, only updating them if they have
22// changed.  One script takes an APK or JAR as an input and produces a zip file containing any outputs of preopting,
23// in the location they should be on the device.  The Make build rules will unzip the zip file into $(PRODUCT_OUT) when
24// installing the APK, which will install the preopt outputs into $(PRODUCT_OUT)/system or $(PRODUCT_OUT)/system_other
25// as necessary.  The zip file may be empty if preopting was disabled for any reason.
26//
27// The intermediate shell scripts allow changes to this package or to the global config to regenerate the shell scripts
28// but only require re-executing preopting if the script has changed.
29//
30// For Soong modules this package is linked directly into Soong and run from the java package.  It generates the same
31// commands as for make, using athe same global config JSON file used by make, but using a module config structure
32// provided by Soong.  The generated commands are then converted into Soong rule and written directly to the ninja file,
33// with no extra shell scripts involved.
34package dexpreopt
35
36import (
37	"fmt"
38	"path/filepath"
39	"runtime"
40	"strings"
41
42	"android/soong/android"
43
44	"github.com/google/blueprint/pathtools"
45)
46
47const SystemPartition = "/system/"
48const SystemOtherPartition = "/system_other/"
49
50var DexpreoptRunningInSoong = false
51
52// GenerateDexpreoptRule generates a set of commands that will preopt a module based on a GlobalConfig and a
53// ModuleConfig.  The produced files and their install locations will be available through rule.Installs().
54func GenerateDexpreoptRule(ctx android.PathContext, globalSoong *GlobalSoongConfig,
55	global *GlobalConfig, module *ModuleConfig) (rule *android.RuleBuilder, err error) {
56
57	defer func() {
58		if r := recover(); r != nil {
59			if _, ok := r.(runtime.Error); ok {
60				panic(r)
61			} else if e, ok := r.(error); ok {
62				err = e
63				rule = nil
64			} else {
65				panic(r)
66			}
67		}
68	}()
69
70	rule = android.NewRuleBuilder()
71
72	generateProfile := module.ProfileClassListing.Valid() && !global.DisableGenerateProfile
73	generateBootProfile := module.ProfileBootListing.Valid() && !global.DisableGenerateProfile
74
75	var profile android.WritablePath
76	if generateProfile {
77		profile = profileCommand(ctx, globalSoong, global, module, rule)
78	}
79	if generateBootProfile {
80		bootProfileCommand(ctx, globalSoong, global, module, rule)
81	}
82
83	if !dexpreoptDisabled(ctx, global, module) {
84		// Don't preopt individual boot jars, they will be preopted together.
85		if !contains(global.BootJars, module.Name) {
86			appImage := (generateProfile || module.ForceCreateAppImage || global.DefaultAppImages) &&
87				!module.NoCreateAppImage
88
89			generateDM := shouldGenerateDM(module, global)
90
91			for archIdx, _ := range module.Archs {
92				dexpreoptCommand(ctx, globalSoong, global, module, rule, archIdx, profile, appImage, generateDM)
93			}
94		}
95	}
96
97	return rule, nil
98}
99
100func dexpreoptDisabled(ctx android.PathContext, global *GlobalConfig, module *ModuleConfig) bool {
101	if contains(global.DisablePreoptModules, module.Name) {
102		return true
103	}
104
105	// Don't preopt system server jars that are updatable.
106	for _, p := range global.UpdatableSystemServerJars {
107		if _, jar := android.SplitApexJarPair(p); jar == module.Name {
108			return true
109		}
110	}
111
112	// If OnlyPreoptBootImageAndSystemServer=true and module is not in boot class path skip
113	// Also preopt system server jars since selinux prevents system server from loading anything from
114	// /data. If we don't do this they will need to be extracted which is not favorable for RAM usage
115	// or performance. If PreoptExtractedApk is true, we ignore the only preopt boot image options.
116	if global.OnlyPreoptBootImageAndSystemServer && !contains(global.BootJars, module.Name) &&
117		!contains(global.SystemServerJars, module.Name) && !module.PreoptExtractedApk {
118		return true
119	}
120
121	return false
122}
123
124func profileCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, global *GlobalConfig,
125	module *ModuleConfig, rule *android.RuleBuilder) android.WritablePath {
126
127	profilePath := module.BuildPath.InSameDir(ctx, "profile.prof")
128	profileInstalledPath := module.DexLocation + ".prof"
129
130	if !module.ProfileIsTextListing {
131		rule.Command().FlagWithOutput("touch ", profilePath)
132	}
133
134	cmd := rule.Command().
135		Text(`ANDROID_LOG_TAGS="*:e"`).
136		Tool(globalSoong.Profman)
137
138	if module.ProfileIsTextListing {
139		// The profile is a test listing of classes (used for framework jars).
140		// We need to generate the actual binary profile before being able to compile.
141		cmd.FlagWithInput("--create-profile-from=", module.ProfileClassListing.Path())
142	} else {
143		// The profile is binary profile (used for apps). Run it through profman to
144		// ensure the profile keys match the apk.
145		cmd.
146			Flag("--copy-and-update-profile-key").
147			FlagWithInput("--profile-file=", module.ProfileClassListing.Path())
148	}
149
150	cmd.
151		FlagWithInput("--apk=", module.DexPath).
152		Flag("--dex-location="+module.DexLocation).
153		FlagWithOutput("--reference-profile-file=", profilePath)
154
155	if !module.ProfileIsTextListing {
156		cmd.Text(fmt.Sprintf(`|| echo "Profile out of date for %s"`, module.DexPath))
157	}
158	rule.Install(profilePath, profileInstalledPath)
159
160	return profilePath
161}
162
163func bootProfileCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, global *GlobalConfig,
164	module *ModuleConfig, rule *android.RuleBuilder) android.WritablePath {
165
166	profilePath := module.BuildPath.InSameDir(ctx, "profile.bprof")
167	profileInstalledPath := module.DexLocation + ".bprof"
168
169	if !module.ProfileIsTextListing {
170		rule.Command().FlagWithOutput("touch ", profilePath)
171	}
172
173	cmd := rule.Command().
174		Text(`ANDROID_LOG_TAGS="*:e"`).
175		Tool(globalSoong.Profman)
176
177	// The profile is a test listing of methods.
178	// We need to generate the actual binary profile.
179	cmd.FlagWithInput("--create-profile-from=", module.ProfileBootListing.Path())
180
181	cmd.
182		Flag("--generate-boot-profile").
183		FlagWithInput("--apk=", module.DexPath).
184		Flag("--dex-location="+module.DexLocation).
185		FlagWithOutput("--reference-profile-file=", profilePath)
186
187	if !module.ProfileIsTextListing {
188		cmd.Text(fmt.Sprintf(`|| echo "Profile out of date for %s"`, module.DexPath))
189	}
190	rule.Install(profilePath, profileInstalledPath)
191
192	return profilePath
193}
194
195func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, global *GlobalConfig,
196	module *ModuleConfig, rule *android.RuleBuilder, archIdx int, profile android.WritablePath,
197	appImage bool, generateDM bool) {
198
199	arch := module.Archs[archIdx]
200
201	// HACK: make soname in Soong-generated .odex files match Make.
202	base := filepath.Base(module.DexLocation)
203	if filepath.Ext(base) == ".jar" {
204		base = "javalib.jar"
205	} else if filepath.Ext(base) == ".apk" {
206		base = "package.apk"
207	}
208
209	toOdexPath := func(path string) string {
210		return filepath.Join(
211			filepath.Dir(path),
212			"oat",
213			arch.String(),
214			pathtools.ReplaceExtension(filepath.Base(path), "odex"))
215	}
216
217	odexPath := module.BuildPath.InSameDir(ctx, "oat", arch.String(), pathtools.ReplaceExtension(base, "odex"))
218	odexInstallPath := toOdexPath(module.DexLocation)
219	if odexOnSystemOther(module, global) {
220		odexInstallPath = filepath.Join(SystemOtherPartition, odexInstallPath)
221	}
222
223	vdexPath := odexPath.ReplaceExtension(ctx, "vdex")
224	vdexInstallPath := pathtools.ReplaceExtension(odexInstallPath, "vdex")
225
226	invocationPath := odexPath.ReplaceExtension(ctx, "invocation")
227
228	systemServerJars := NonUpdatableSystemServerJars(ctx, global)
229
230	// The class loader context using paths in the build
231	var classLoaderContextHost android.Paths
232
233	// The class loader context using paths as they will be on the device
234	var classLoaderContextTarget []string
235
236	// Extra paths that will be appended to the class loader if the APK manifest has targetSdkVersion < 28
237	var conditionalClassLoaderContextHost28 android.Paths
238	var conditionalClassLoaderContextTarget28 []string
239
240	// Extra paths that will be appended to the class loader if the APK manifest has targetSdkVersion < 29
241	var conditionalClassLoaderContextHost29 android.Paths
242	var conditionalClassLoaderContextTarget29 []string
243
244	// A flag indicating if the '&' class loader context is used.
245	unknownClassLoaderContext := false
246
247	if module.EnforceUsesLibraries {
248		usesLibs := append(copyOf(module.UsesLibraries), module.PresentOptionalUsesLibraries...)
249
250		// Create class loader context for dex2oat from uses libraries and filtered optional libraries
251		for _, l := range usesLibs {
252
253			classLoaderContextHost = append(classLoaderContextHost,
254				pathForLibrary(module, l))
255			classLoaderContextTarget = append(classLoaderContextTarget,
256				filepath.Join("/system/framework", l+".jar"))
257		}
258
259		const httpLegacy = "org.apache.http.legacy"
260		const httpLegacyImpl = "org.apache.http.legacy.impl"
261
262		// org.apache.http.legacy contains classes that were in the default classpath until API 28.  If the
263		// targetSdkVersion in the manifest or APK is < 28, and the module does not explicitly depend on
264		// org.apache.http.legacy, then implicitly add the classes to the classpath for dexpreopt.  One the
265		// device the classes will be in a file called org.apache.http.legacy.impl.jar.
266		module.LibraryPaths[httpLegacyImpl] = module.LibraryPaths[httpLegacy]
267
268		if !contains(module.UsesLibraries, httpLegacy) && !contains(module.PresentOptionalUsesLibraries, httpLegacy) {
269			conditionalClassLoaderContextHost28 = append(conditionalClassLoaderContextHost28,
270				pathForLibrary(module, httpLegacyImpl))
271			conditionalClassLoaderContextTarget28 = append(conditionalClassLoaderContextTarget28,
272				filepath.Join("/system/framework", httpLegacyImpl+".jar"))
273		}
274
275		const hidlBase = "android.hidl.base-V1.0-java"
276		const hidlManager = "android.hidl.manager-V1.0-java"
277
278		// android.hidl.base-V1.0-java and android.hidl.manager-V1.0 contain classes that were in the default
279		// classpath until API 29.  If the targetSdkVersion in the manifest or APK is < 29 then implicitly add
280		// the classes to the classpath for dexpreopt.
281		conditionalClassLoaderContextHost29 = append(conditionalClassLoaderContextHost29,
282			pathForLibrary(module, hidlManager))
283		conditionalClassLoaderContextTarget29 = append(conditionalClassLoaderContextTarget29,
284			filepath.Join("/system/framework", hidlManager+".jar"))
285		conditionalClassLoaderContextHost29 = append(conditionalClassLoaderContextHost29,
286			pathForLibrary(module, hidlBase))
287		conditionalClassLoaderContextTarget29 = append(conditionalClassLoaderContextTarget29,
288			filepath.Join("/system/framework", hidlBase+".jar"))
289	} else if jarIndex := android.IndexList(module.Name, systemServerJars); jarIndex >= 0 {
290		// System server jars should be dexpreopted together: class loader context of each jar
291		// should include all preceding jars on the system server classpath.
292		for _, otherJar := range systemServerJars[:jarIndex] {
293			classLoaderContextHost = append(classLoaderContextHost, SystemServerDexJarHostPath(ctx, otherJar))
294			classLoaderContextTarget = append(classLoaderContextTarget, "/system/framework/"+otherJar+".jar")
295		}
296
297		// Copy the system server jar to a predefined location where dex2oat will find it.
298		dexPathHost := SystemServerDexJarHostPath(ctx, module.Name)
299		rule.Command().Text("mkdir -p").Flag(filepath.Dir(dexPathHost.String()))
300		rule.Command().Text("cp -f").Input(module.DexPath).Output(dexPathHost)
301	} else {
302		// Pass special class loader context to skip the classpath and collision check.
303		// This will get removed once LOCAL_USES_LIBRARIES is enforced.
304		// Right now LOCAL_USES_LIBRARIES is opt in, for the case where it's not specified we still default
305		// to the &.
306		unknownClassLoaderContext = true
307	}
308
309	rule.Command().FlagWithArg("mkdir -p ", filepath.Dir(odexPath.String()))
310	rule.Command().FlagWithOutput("rm -f ", odexPath)
311	// Set values in the environment of the rule.  These may be modified by construct_context.sh.
312	if unknownClassLoaderContext {
313		rule.Command().
314			Text(`class_loader_context_arg=--class-loader-context=\&`).
315			Text(`stored_class_loader_context_arg=""`)
316	} else {
317		rule.Command().
318			Text("class_loader_context_arg=--class-loader-context=PCL[" + strings.Join(classLoaderContextHost.Strings(), ":") + "]").
319			Implicits(classLoaderContextHost).
320			Text("stored_class_loader_context_arg=--stored-class-loader-context=PCL[" + strings.Join(classLoaderContextTarget, ":") + "]")
321	}
322
323	if module.EnforceUsesLibraries {
324		if module.ManifestPath != nil {
325			rule.Command().Text(`target_sdk_version="$(`).
326				Tool(globalSoong.ManifestCheck).
327				Flag("--extract-target-sdk-version").
328				Input(module.ManifestPath).
329				Text(`)"`)
330		} else {
331			// No manifest to extract targetSdkVersion from, hope that DexJar is an APK
332			rule.Command().Text(`target_sdk_version="$(`).
333				Tool(globalSoong.Aapt).
334				Flag("dump badging").
335				Input(module.DexPath).
336				Text(`| grep "targetSdkVersion" | sed -n "s/targetSdkVersion:'\(.*\)'/\1/p"`).
337				Text(`)"`)
338		}
339		rule.Command().Textf(`dex_preopt_host_libraries="%s"`,
340			strings.Join(classLoaderContextHost.Strings(), " ")).
341			Implicits(classLoaderContextHost)
342		rule.Command().Textf(`dex_preopt_target_libraries="%s"`,
343			strings.Join(classLoaderContextTarget, " "))
344		rule.Command().Textf(`conditional_host_libs_28="%s"`,
345			strings.Join(conditionalClassLoaderContextHost28.Strings(), " ")).
346			Implicits(conditionalClassLoaderContextHost28)
347		rule.Command().Textf(`conditional_target_libs_28="%s"`,
348			strings.Join(conditionalClassLoaderContextTarget28, " "))
349		rule.Command().Textf(`conditional_host_libs_29="%s"`,
350			strings.Join(conditionalClassLoaderContextHost29.Strings(), " ")).
351			Implicits(conditionalClassLoaderContextHost29)
352		rule.Command().Textf(`conditional_target_libs_29="%s"`,
353			strings.Join(conditionalClassLoaderContextTarget29, " "))
354		rule.Command().Text("source").Tool(globalSoong.ConstructContext).Input(module.DexPath)
355	}
356
357	// Devices that do not have a product partition use a symlink from /product to /system/product.
358	// Because on-device dexopt will see dex locations starting with /product, we change the paths
359	// to mimic this behavior.
360	dexLocationArg := module.DexLocation
361	if strings.HasPrefix(dexLocationArg, "/system/product/") {
362		dexLocationArg = strings.TrimPrefix(dexLocationArg, "/system")
363	}
364
365	cmd := rule.Command().
366		Text(`ANDROID_LOG_TAGS="*:e"`).
367		Tool(globalSoong.Dex2oat).
368		Flag("--avoid-storing-invocation").
369		FlagWithOutput("--write-invocation-to=", invocationPath).ImplicitOutput(invocationPath).
370		Flag("--runtime-arg").FlagWithArg("-Xms", global.Dex2oatXms).
371		Flag("--runtime-arg").FlagWithArg("-Xmx", global.Dex2oatXmx).
372		Flag("--runtime-arg").FlagWithInputList("-Xbootclasspath:", module.PreoptBootClassPathDexFiles, ":").
373		Flag("--runtime-arg").FlagWithList("-Xbootclasspath-locations:", module.PreoptBootClassPathDexLocations, ":").
374		Flag("${class_loader_context_arg}").
375		Flag("${stored_class_loader_context_arg}").
376		FlagWithArg("--boot-image=", strings.Join(module.DexPreoptImageLocations, ":")).Implicits(module.DexPreoptImagesDeps[archIdx].Paths()).
377		FlagWithInput("--dex-file=", module.DexPath).
378		FlagWithArg("--dex-location=", dexLocationArg).
379		FlagWithOutput("--oat-file=", odexPath).ImplicitOutput(vdexPath).
380		// Pass an empty directory, dex2oat shouldn't be reading arbitrary files
381		FlagWithArg("--android-root=", global.EmptyDirectory).
382		FlagWithArg("--instruction-set=", arch.String()).
383		FlagWithArg("--instruction-set-variant=", global.CpuVariant[arch]).
384		FlagWithArg("--instruction-set-features=", global.InstructionSetFeatures[arch]).
385		Flag("--no-generate-debug-info").
386		Flag("--generate-build-id").
387		Flag("--abort-on-hard-verifier-error").
388		Flag("--force-determinism").
389		FlagWithArg("--no-inline-from=", "core-oj.jar")
390
391	var preoptFlags []string
392	if len(module.PreoptFlags) > 0 {
393		preoptFlags = module.PreoptFlags
394	} else if len(global.PreoptFlags) > 0 {
395		preoptFlags = global.PreoptFlags
396	}
397
398	if len(preoptFlags) > 0 {
399		cmd.Text(strings.Join(preoptFlags, " "))
400	}
401
402	if module.UncompressedDex {
403		cmd.FlagWithArg("--copy-dex-files=", "false")
404	}
405
406	if !android.PrefixInList(preoptFlags, "--compiler-filter=") {
407		var compilerFilter string
408		if contains(global.SystemServerJars, module.Name) {
409			// Jars of system server, use the product option if it is set, speed otherwise.
410			if global.SystemServerCompilerFilter != "" {
411				compilerFilter = global.SystemServerCompilerFilter
412			} else {
413				compilerFilter = "speed"
414			}
415		} else if contains(global.SpeedApps, module.Name) || contains(global.SystemServerApps, module.Name) {
416			// Apps loaded into system server, and apps the product default to being compiled with the
417			// 'speed' compiler filter.
418			compilerFilter = "speed"
419		} else if profile != nil {
420			// For non system server jars, use speed-profile when we have a profile.
421			compilerFilter = "speed-profile"
422		} else if global.DefaultCompilerFilter != "" {
423			compilerFilter = global.DefaultCompilerFilter
424		} else {
425			compilerFilter = "quicken"
426		}
427		cmd.FlagWithArg("--compiler-filter=", compilerFilter)
428	}
429
430	if generateDM {
431		cmd.FlagWithArg("--copy-dex-files=", "false")
432		dmPath := module.BuildPath.InSameDir(ctx, "generated.dm")
433		dmInstalledPath := pathtools.ReplaceExtension(module.DexLocation, "dm")
434		tmpPath := module.BuildPath.InSameDir(ctx, "primary.vdex")
435		rule.Command().Text("cp -f").Input(vdexPath).Output(tmpPath)
436		rule.Command().Tool(globalSoong.SoongZip).
437			FlagWithArg("-L", "9").
438			FlagWithOutput("-o", dmPath).
439			Flag("-j").
440			Input(tmpPath)
441		rule.Install(dmPath, dmInstalledPath)
442	}
443
444	// By default, emit debug info.
445	debugInfo := true
446	if global.NoDebugInfo {
447		// If the global setting suppresses mini-debug-info, disable it.
448		debugInfo = false
449	}
450
451	// PRODUCT_SYSTEM_SERVER_DEBUG_INFO overrides WITH_DEXPREOPT_DEBUG_INFO.
452	// PRODUCT_OTHER_JAVA_DEBUG_INFO overrides WITH_DEXPREOPT_DEBUG_INFO.
453	if contains(global.SystemServerJars, module.Name) {
454		if global.AlwaysSystemServerDebugInfo {
455			debugInfo = true
456		} else if global.NeverSystemServerDebugInfo {
457			debugInfo = false
458		}
459	} else {
460		if global.AlwaysOtherDebugInfo {
461			debugInfo = true
462		} else if global.NeverOtherDebugInfo {
463			debugInfo = false
464		}
465	}
466
467	// Never enable on eng.
468	if global.IsEng {
469		debugInfo = false
470	}
471
472	if debugInfo {
473		cmd.Flag("--generate-mini-debug-info")
474	} else {
475		cmd.Flag("--no-generate-mini-debug-info")
476	}
477
478	// Set the compiler reason to 'prebuilt' to identify the oat files produced
479	// during the build, as opposed to compiled on the device.
480	cmd.FlagWithArg("--compilation-reason=", "prebuilt")
481
482	if appImage {
483		appImagePath := odexPath.ReplaceExtension(ctx, "art")
484		appImageInstallPath := pathtools.ReplaceExtension(odexInstallPath, "art")
485		cmd.FlagWithOutput("--app-image-file=", appImagePath).
486			FlagWithArg("--image-format=", "lz4")
487		if !global.DontResolveStartupStrings {
488			cmd.FlagWithArg("--resolve-startup-const-strings=", "true")
489		}
490		rule.Install(appImagePath, appImageInstallPath)
491	}
492
493	if profile != nil {
494		cmd.FlagWithInput("--profile-file=", profile)
495	}
496
497	rule.Install(odexPath, odexInstallPath)
498	rule.Install(vdexPath, vdexInstallPath)
499}
500
501func shouldGenerateDM(module *ModuleConfig, global *GlobalConfig) bool {
502	// Generating DM files only makes sense for verify, avoid doing for non verify compiler filter APKs.
503	// No reason to use a dm file if the dex is already uncompressed.
504	return global.GenerateDMFiles && !module.UncompressedDex &&
505		contains(module.PreoptFlags, "--compiler-filter=verify")
506}
507
508func OdexOnSystemOtherByName(name string, dexLocation string, global *GlobalConfig) bool {
509	if !global.HasSystemOther {
510		return false
511	}
512
513	if global.SanitizeLite {
514		return false
515	}
516
517	if contains(global.SpeedApps, name) || contains(global.SystemServerApps, name) {
518		return false
519	}
520
521	for _, f := range global.PatternsOnSystemOther {
522		if makefileMatch(filepath.Join(SystemPartition, f), dexLocation) {
523			return true
524		}
525	}
526
527	return false
528}
529
530func odexOnSystemOther(module *ModuleConfig, global *GlobalConfig) bool {
531	return OdexOnSystemOtherByName(module.Name, module.DexLocation, global)
532}
533
534// PathToLocation converts .../system/framework/arm64/boot.art to .../system/framework/boot.art
535func PathToLocation(path android.Path, arch android.ArchType) string {
536	pathArch := filepath.Base(filepath.Dir(path.String()))
537	if pathArch != arch.String() {
538		panic(fmt.Errorf("last directory in %q must be %q", path, arch.String()))
539	}
540	return filepath.Join(filepath.Dir(filepath.Dir(path.String())), filepath.Base(path.String()))
541}
542
543func pathForLibrary(module *ModuleConfig, lib string) android.Path {
544	path, ok := module.LibraryPaths[lib]
545	if !ok {
546		panic(fmt.Errorf("unknown library path for %q", lib))
547	}
548	return path
549}
550
551func makefileMatch(pattern, s string) bool {
552	percent := strings.IndexByte(pattern, '%')
553	switch percent {
554	case -1:
555		return pattern == s
556	case len(pattern) - 1:
557		return strings.HasPrefix(s, pattern[:len(pattern)-1])
558	default:
559		panic(fmt.Errorf("unsupported makefile pattern %q", pattern))
560	}
561}
562
563// Expected format for apexJarValue = <apex name>:<jar name>
564func GetJarLocationFromApexJarPair(apexJarValue string) string {
565	apex, jar := android.SplitApexJarPair(apexJarValue)
566	return filepath.Join("/apex", apex, "javalib", jar+".jar")
567}
568
569func GetJarsFromApexJarPairs(apexJarPairs []string) []string {
570	modules := make([]string, len(apexJarPairs))
571	for i, p := range apexJarPairs {
572		_, jar := android.SplitApexJarPair(p)
573		modules[i] = jar
574	}
575	return modules
576}
577
578var nonUpdatableSystemServerJarsKey = android.NewOnceKey("nonUpdatableSystemServerJars")
579
580// TODO: eliminate the superficial global config parameter by moving global config definition
581// from java subpackage to dexpreopt.
582func NonUpdatableSystemServerJars(ctx android.PathContext, global *GlobalConfig) []string {
583	return ctx.Config().Once(nonUpdatableSystemServerJarsKey, func() interface{} {
584		return android.RemoveListFromList(global.SystemServerJars,
585			GetJarsFromApexJarPairs(global.UpdatableSystemServerJars))
586	}).([]string)
587}
588
589// A predefined location for the system server dex jars. This is needed in order to generate
590// class loader context for dex2oat, as the path to the jar in the Soong module may be unknown
591// at that time (Soong processes the jars in dependency order, which may be different from the
592// the system server classpath order).
593func SystemServerDexJarHostPath(ctx android.PathContext, jar string) android.OutputPath {
594	if DexpreoptRunningInSoong {
595		// Soong module, just use the default output directory $OUT/soong.
596		return android.PathForOutput(ctx, "system_server_dexjars", jar+".jar")
597	} else {
598		// Make module, default output directory is $OUT (passed via the "null config" created
599		// by dexpreopt_gen). Append Soong subdirectory to match Soong module paths.
600		return android.PathForOutput(ctx, "soong", "system_server_dexjars", jar+".jar")
601	}
602}
603
604func contains(l []string, s string) bool {
605	for _, e := range l {
606		if e == s {
607			return true
608		}
609	}
610	return false
611}
612
613var copyOf = android.CopyOf
614