• 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/"
49const SystemServerDexjarsDir = "system_server_dexjars"
50
51var DexpreoptRunningInSoong = false
52
53// GenerateDexpreoptRule generates a set of commands that will preopt a module based on a GlobalConfig and a
54// ModuleConfig.  The produced files and their install locations will be available through rule.Installs().
55func GenerateDexpreoptRule(ctx android.BuilderContext, globalSoong *GlobalSoongConfig,
56	global *GlobalConfig, module *ModuleConfig, productPackages android.Path) (
57	rule *android.RuleBuilder, err error) {
58
59	defer func() {
60		if r := recover(); r != nil {
61			if _, ok := r.(runtime.Error); ok {
62				panic(r)
63			} else if e, ok := r.(error); ok {
64				err = e
65				rule = nil
66			} else {
67				panic(r)
68			}
69		}
70	}()
71
72	rule = android.NewRuleBuilder(pctx, ctx)
73
74	generateProfile := module.ProfileClassListing.Valid() && !global.DisableGenerateProfile
75	generateBootProfile := module.ProfileBootListing.Valid() && !global.DisableGenerateProfile
76
77	var profile android.WritablePath
78	if generateProfile {
79		profile = profileCommand(ctx, globalSoong, global, module, rule)
80	}
81	if generateBootProfile {
82		bootProfileCommand(ctx, globalSoong, global, module, rule)
83	}
84
85	if !dexpreoptDisabled(ctx, global, module) {
86		if valid, err := validateClassLoaderContext(module.ClassLoaderContexts); err != nil {
87			android.ReportPathErrorf(ctx, "%s", err.Error())
88		} else if valid {
89			fixClassLoaderContext(module.ClassLoaderContexts)
90
91			appImage := (generateProfile || module.ForceCreateAppImage || global.DefaultAppImages) &&
92				!module.NoCreateAppImage
93
94			generateDM := shouldGenerateDM(module, global)
95
96			for archIdx, _ := range module.Archs {
97				dexpreoptCommand(ctx, globalSoong, global, module, rule, archIdx, profile, appImage,
98					generateDM, productPackages)
99			}
100		}
101	}
102
103	return rule, nil
104}
105
106// If dexpreopt is applicable to the module, returns whether dexpreopt is disabled. Otherwise, the
107// behavior is undefined.
108// When it returns true, dexpreopt artifacts will not be generated, but profile will still be
109// generated if profile-guided compilation is requested.
110func dexpreoptDisabled(ctx android.PathContext, global *GlobalConfig, module *ModuleConfig) bool {
111	if ctx.Config().UnbundledBuild() {
112		return true
113	}
114
115	if global.DisablePreopt {
116		return true
117	}
118
119	if contains(global.DisablePreoptModules, module.Name) {
120		return true
121	}
122
123	// Don't preopt individual boot jars, they will be preopted together.
124	if global.BootJars.ContainsJar(module.Name) {
125		return true
126	}
127
128	if global.OnlyPreoptArtBootImage {
129		return true
130	}
131
132	return false
133}
134
135func profileCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, global *GlobalConfig,
136	module *ModuleConfig, rule *android.RuleBuilder) android.WritablePath {
137
138	profilePath := module.BuildPath.InSameDir(ctx, "profile.prof")
139	profileInstalledPath := module.DexLocation + ".prof"
140
141	if !module.ProfileIsTextListing {
142		rule.Command().Text("rm -f").Output(profilePath)
143		rule.Command().Text("touch").Output(profilePath)
144	}
145
146	cmd := rule.Command().
147		Text(`ANDROID_LOG_TAGS="*:e"`).
148		Tool(globalSoong.Profman)
149
150	if module.ProfileIsTextListing {
151		// The profile is a test listing of classes (used for framework jars).
152		// We need to generate the actual binary profile before being able to compile.
153		cmd.FlagWithInput("--create-profile-from=", module.ProfileClassListing.Path())
154	} else {
155		// The profile is binary profile (used for apps). Run it through profman to
156		// ensure the profile keys match the apk.
157		cmd.
158			Flag("--copy-and-update-profile-key").
159			FlagWithInput("--profile-file=", module.ProfileClassListing.Path())
160	}
161
162	cmd.
163		Flag("--output-profile-type=app").
164		FlagWithInput("--apk=", module.DexPath).
165		Flag("--dex-location="+module.DexLocation).
166		FlagWithOutput("--reference-profile-file=", profilePath)
167
168	if !module.ProfileIsTextListing {
169		cmd.Text(fmt.Sprintf(`|| echo "Profile out of date for %s"`, module.DexPath))
170	}
171	rule.Install(profilePath, profileInstalledPath)
172
173	return profilePath
174}
175
176func bootProfileCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, global *GlobalConfig,
177	module *ModuleConfig, rule *android.RuleBuilder) android.WritablePath {
178
179	profilePath := module.BuildPath.InSameDir(ctx, "profile.bprof")
180	profileInstalledPath := module.DexLocation + ".bprof"
181
182	if !module.ProfileIsTextListing {
183		rule.Command().Text("rm -f").Output(profilePath)
184		rule.Command().Text("touch").Output(profilePath)
185	}
186
187	cmd := rule.Command().
188		Text(`ANDROID_LOG_TAGS="*:e"`).
189		Tool(globalSoong.Profman)
190
191	// The profile is a test listing of methods.
192	// We need to generate the actual binary profile.
193	cmd.FlagWithInput("--create-profile-from=", module.ProfileBootListing.Path())
194
195	cmd.
196		Flag("--output-profile-type=bprof").
197		FlagWithInput("--apk=", module.DexPath).
198		Flag("--dex-location="+module.DexLocation).
199		FlagWithOutput("--reference-profile-file=", profilePath)
200
201	if !module.ProfileIsTextListing {
202		cmd.Text(fmt.Sprintf(`|| echo "Profile out of date for %s"`, module.DexPath))
203	}
204	rule.Install(profilePath, profileInstalledPath)
205
206	return profilePath
207}
208
209// Returns the dex location of a system server java library.
210func GetSystemServerDexLocation(ctx android.PathContext, global *GlobalConfig, lib string) string {
211	if apex := global.AllApexSystemServerJars(ctx).ApexOfJar(lib); apex != "" {
212		return fmt.Sprintf("/apex/%s/javalib/%s.jar", apex, lib)
213	}
214
215	if apex := global.AllPlatformSystemServerJars(ctx).ApexOfJar(lib); apex == "system_ext" {
216		return fmt.Sprintf("/system_ext/framework/%s.jar", lib)
217	}
218
219	return fmt.Sprintf("/system/framework/%s.jar", lib)
220}
221
222// Returns the location to the odex file for the dex file at `path`.
223func ToOdexPath(path string, arch android.ArchType, partition string) string {
224	if strings.HasPrefix(path, "/apex/") {
225		return filepath.Join(partition, "framework/oat", arch.String(),
226			strings.ReplaceAll(path[1:], "/", "@")+"@classes.odex")
227	}
228
229	return filepath.Join(filepath.Dir(path), "oat", arch.String(),
230		pathtools.ReplaceExtension(filepath.Base(path), "odex"))
231}
232
233func dexpreoptCommand(ctx android.BuilderContext, globalSoong *GlobalSoongConfig,
234	global *GlobalConfig, module *ModuleConfig, rule *android.RuleBuilder, archIdx int,
235	profile android.WritablePath, appImage bool, generateDM bool, productPackages android.Path) {
236
237	arch := module.Archs[archIdx]
238
239	// HACK: make soname in Soong-generated .odex files match Make.
240	base := filepath.Base(module.DexLocation)
241	if filepath.Ext(base) == ".jar" {
242		base = "javalib.jar"
243	} else if filepath.Ext(base) == ".apk" {
244		base = "package.apk"
245	}
246
247	odexPath := module.BuildPath.InSameDir(ctx, "oat", arch.String(), pathtools.ReplaceExtension(base, "odex"))
248	odexSymbolsPath := odexPath.ReplaceExtension(ctx, "symbols.odex")
249	odexInstallPath := ToOdexPath(module.DexLocation, arch, module.ApexPartition)
250	if odexOnSystemOther(module, global) {
251		odexInstallPath = filepath.Join(SystemOtherPartition, odexInstallPath)
252	}
253
254	vdexPath := odexPath.ReplaceExtension(ctx, "vdex")
255	vdexInstallPath := pathtools.ReplaceExtension(odexInstallPath, "vdex")
256
257	invocationPath := odexPath.ReplaceExtension(ctx, "invocation")
258
259	systemServerJars := global.AllSystemServerJars(ctx)
260	systemServerClasspathJars := global.AllSystemServerClasspathJars(ctx)
261
262	rule.Command().FlagWithArg("mkdir -p ", filepath.Dir(odexPath.String()))
263	rule.Command().FlagWithOutput("rm -f ", odexPath).
264		FlagWithArg("rm -f ", odexSymbolsPath.String())
265
266	if jarIndex := systemServerJars.IndexOfJar(module.Name); jarIndex >= 0 {
267		// System server jars should be dexpreopted together: class loader context of each jar
268		// should include all preceding jars on the system server classpath.
269
270		var clcHost android.Paths
271		var clcTarget []string
272		endIndex := systemServerClasspathJars.IndexOfJar(module.Name)
273		if endIndex < 0 {
274			// The jar is a standalone one. Use the full classpath as the class loader context.
275			endIndex = systemServerClasspathJars.Len()
276		}
277		for i := 0; i < endIndex; i++ {
278			lib := systemServerClasspathJars.Jar(i)
279			clcHost = append(clcHost, SystemServerDexJarHostPath(ctx, lib))
280			clcTarget = append(clcTarget, GetSystemServerDexLocation(ctx, global, lib))
281		}
282
283		clcHostString := "PCL[" + strings.Join(clcHost.Strings(), ":") + "]"
284		clcTargetString := "PCL[" + strings.Join(clcTarget, ":") + "]"
285
286		if !systemServerClasspathJars.ContainsJar(module.Name) {
287			// Standalone jars are loaded by separate class loaders with SYSTEMSERVERCLASSPATH as the
288			// parent.
289			clcHostString = "PCL[];" + clcHostString
290			clcTargetString = "PCL[];" + clcTargetString
291		}
292
293		rule.Command().
294			Text(`class_loader_context_arg=--class-loader-context="` + clcHostString + `"`).
295			Implicits(clcHost).
296			Text(`stored_class_loader_context_arg=--stored-class-loader-context="` + clcTargetString + `"`)
297
298	} else {
299		// There are three categories of Java modules handled here:
300		//
301		// - Modules that have passed verify_uses_libraries check. They are AOT-compiled and
302		//   expected to be loaded on device without CLC mismatch errors.
303		//
304		// - Modules that have failed the check in relaxed mode, so it didn't cause a build error.
305		//   They are dexpreopted with "verify" filter and not AOT-compiled.
306		//   TODO(b/132357300): ensure that CLC mismatch errors are ignored with "verify" filter.
307		//
308		// - Modules that didn't run the check. They are AOT-compiled, but it's unknown if they
309		//   will have CLC mismatch errors on device (the check is disabled by default).
310		//
311		// TODO(b/132357300): enable the check by default and eliminate the last category, so that
312		// no time/space is wasted on AOT-compiling modules that will fail CLC check on device.
313
314		var manifestOrApk android.Path
315		if module.ManifestPath.Valid() {
316			// Ok, there is an XML manifest.
317			manifestOrApk = module.ManifestPath.Path()
318		} else if filepath.Ext(base) == ".apk" {
319			// Ok, there is is an APK with the manifest inside.
320			manifestOrApk = module.DexPath
321		}
322
323		// Generate command that saves target SDK version in a shell variable.
324		if manifestOrApk == nil {
325			// There is neither an XML manifest nor APK => nowhere to extract targetSdkVersion from.
326			// Set the latest ("any") version: then construct_context will not add any compatibility
327			// libraries (if this is incorrect, there will be a CLC mismatch and dexopt on device).
328			rule.Command().Textf(`target_sdk_version=%d`, AnySdkVersion)
329		} else {
330			rule.Command().Text(`target_sdk_version="$(`).
331				Tool(globalSoong.ManifestCheck).
332				Flag("--extract-target-sdk-version").
333				Input(manifestOrApk).
334				FlagWithInput("--aapt ", globalSoong.Aapt).
335				Text(`)"`)
336		}
337
338		// Generate command that saves host and target class loader context in shell variables.
339		_, paths := ComputeClassLoaderContextDependencies(module.ClassLoaderContexts)
340		rule.Command().
341			Text(`eval "$(`).Tool(globalSoong.ConstructContext).
342			Text(` --target-sdk-version ${target_sdk_version}`).
343			FlagWithArg("--context-json=", module.ClassLoaderContexts.DumpForFlag()).
344			FlagWithInput("--product-packages=", productPackages).
345			Implicits(paths).
346			Text(`)"`)
347	}
348
349	// Devices that do not have a product partition use a symlink from /product to /system/product.
350	// Because on-device dexopt will see dex locations starting with /product, we change the paths
351	// to mimic this behavior.
352	dexLocationArg := module.DexLocation
353	if strings.HasPrefix(dexLocationArg, "/system/product/") {
354		dexLocationArg = strings.TrimPrefix(dexLocationArg, "/system")
355	}
356
357	cmd := rule.Command().
358		Text(`ANDROID_LOG_TAGS="*:e"`).
359		Tool(globalSoong.Dex2oat).
360		Flag("--avoid-storing-invocation").
361		FlagWithOutput("--write-invocation-to=", invocationPath).ImplicitOutput(invocationPath).
362		Flag("--runtime-arg").FlagWithArg("-Xms", global.Dex2oatXms).
363		Flag("--runtime-arg").FlagWithArg("-Xmx", global.Dex2oatXmx).
364		Flag("--runtime-arg").FlagWithInputList("-Xbootclasspath:", module.PreoptBootClassPathDexFiles, ":").
365		Flag("--runtime-arg").FlagWithList("-Xbootclasspath-locations:", module.PreoptBootClassPathDexLocations, ":").
366		Flag("${class_loader_context_arg}").
367		Flag("${stored_class_loader_context_arg}").
368		FlagWithArg("--boot-image=", strings.Join(module.DexPreoptImageLocationsOnHost, ":")).Implicits(module.DexPreoptImagesDeps[archIdx].Paths()).
369		FlagWithInput("--dex-file=", module.DexPath).
370		FlagWithArg("--dex-location=", dexLocationArg).
371		FlagWithOutput("--oat-file=", odexPath).ImplicitOutput(vdexPath).
372		// Pass an empty directory, dex2oat shouldn't be reading arbitrary files
373		FlagWithArg("--android-root=", global.EmptyDirectory).
374		FlagWithArg("--instruction-set=", arch.String()).
375		FlagWithArg("--instruction-set-variant=", global.CpuVariant[arch]).
376		FlagWithArg("--instruction-set-features=", global.InstructionSetFeatures[arch]).
377		FlagWithOutput("--oat-symbols=", odexSymbolsPath).
378		Flag("--generate-debug-info").
379		Flag("--strip").
380		Flag("--generate-build-id").
381		Flag("--abort-on-hard-verifier-error").
382		Flag("--force-determinism").
383		FlagWithArg("--no-inline-from=", "core-oj.jar").
384		Text("$(cat").Input(globalSoong.UffdGcFlag).Text(")")
385
386	var preoptFlags []string
387	if len(module.PreoptFlags) > 0 {
388		preoptFlags = module.PreoptFlags
389	} else if len(global.PreoptFlags) > 0 {
390		preoptFlags = global.PreoptFlags
391	}
392
393	if len(preoptFlags) > 0 {
394		cmd.Text(strings.Join(preoptFlags, " "))
395	}
396
397	if module.UncompressedDex {
398		cmd.FlagWithArg("--copy-dex-files=", "false")
399	}
400
401	if !android.PrefixInList(preoptFlags, "--compiler-filter=") {
402		var compilerFilter string
403		if systemServerJars.ContainsJar(module.Name) {
404			if global.SystemServerCompilerFilter != "" {
405				// Use the product option if it is set.
406				compilerFilter = global.SystemServerCompilerFilter
407			} else if profile != nil {
408				// Use "speed-profile" for system server jars that have a profile.
409				compilerFilter = "speed-profile"
410			} else {
411				// Use "speed" for system server jars that do not have a profile.
412				compilerFilter = "speed"
413			}
414		} else if contains(global.SpeedApps, module.Name) || contains(global.SystemServerApps, module.Name) {
415			// Apps loaded into system server, and apps the product default to being compiled with the
416			// 'speed' compiler filter.
417			compilerFilter = "speed"
418		} else if profile != nil {
419			// For non system server jars, use speed-profile when we have a profile.
420			compilerFilter = "speed-profile"
421		} else if global.DefaultCompilerFilter != "" {
422			compilerFilter = global.DefaultCompilerFilter
423		} else {
424			compilerFilter = "quicken"
425		}
426		if module.EnforceUsesLibraries {
427			// If the verify_uses_libraries check failed (in this case status file contains a
428			// non-empty error message), then use "verify" compiler filter to avoid compiling any
429			// code (it would be rejected on device because of a class loader context mismatch).
430			cmd.Text("--compiler-filter=$(if test -s ").
431				Input(module.EnforceUsesLibrariesStatusFile).
432				Text(" ; then echo verify ; else echo " + compilerFilter + " ; fi)")
433		} else {
434			cmd.FlagWithArg("--compiler-filter=", compilerFilter)
435		}
436	}
437
438	if generateDM {
439		cmd.FlagWithArg("--copy-dex-files=", "false")
440		dmPath := module.BuildPath.InSameDir(ctx, "generated.dm")
441		dmInstalledPath := pathtools.ReplaceExtension(module.DexLocation, "dm")
442		tmpPath := module.BuildPath.InSameDir(ctx, "primary.vdex")
443		rule.Command().Text("cp -f").Input(vdexPath).Output(tmpPath)
444		rule.Command().Tool(globalSoong.SoongZip).
445			FlagWithArg("-L", "9").
446			FlagWithOutput("-o", dmPath).
447			Flag("-j").
448			Input(tmpPath)
449		rule.Install(dmPath, dmInstalledPath)
450	}
451
452	// By default, emit debug info.
453	debugInfo := true
454	if global.NoDebugInfo {
455		// If the global setting suppresses mini-debug-info, disable it.
456		debugInfo = false
457	}
458
459	// PRODUCT_SYSTEM_SERVER_DEBUG_INFO overrides WITH_DEXPREOPT_DEBUG_INFO.
460	// PRODUCT_OTHER_JAVA_DEBUG_INFO overrides WITH_DEXPREOPT_DEBUG_INFO.
461	if systemServerJars.ContainsJar(module.Name) {
462		if global.AlwaysSystemServerDebugInfo {
463			debugInfo = true
464		} else if global.NeverSystemServerDebugInfo {
465			debugInfo = false
466		}
467	} else {
468		if global.AlwaysOtherDebugInfo {
469			debugInfo = true
470		} else if global.NeverOtherDebugInfo {
471			debugInfo = false
472		}
473	}
474
475	if debugInfo {
476		cmd.Flag("--generate-mini-debug-info")
477	} else {
478		cmd.Flag("--no-generate-mini-debug-info")
479	}
480
481	// Set the compiler reason to 'prebuilt' to identify the oat files produced
482	// during the build, as opposed to compiled on the device.
483	cmd.FlagWithArg("--compilation-reason=", "prebuilt")
484
485	if appImage {
486		appImagePath := odexPath.ReplaceExtension(ctx, "art")
487		appImageInstallPath := pathtools.ReplaceExtension(odexInstallPath, "art")
488		cmd.FlagWithOutput("--app-image-file=", appImagePath).
489			FlagWithArg("--image-format=", "lz4")
490		if !global.DontResolveStartupStrings {
491			cmd.FlagWithArg("--resolve-startup-const-strings=", "true")
492		}
493		rule.Install(appImagePath, appImageInstallPath)
494	}
495
496	if profile != nil {
497		cmd.FlagWithInput("--profile-file=", profile)
498	}
499
500	rule.Install(odexPath, odexInstallPath)
501	rule.Install(vdexPath, vdexInstallPath)
502}
503
504func shouldGenerateDM(module *ModuleConfig, global *GlobalConfig) bool {
505	// Generating DM files only makes sense for verify, avoid doing for non verify compiler filter APKs.
506	// No reason to use a dm file if the dex is already uncompressed.
507	return global.GenerateDMFiles && !module.UncompressedDex &&
508		contains(module.PreoptFlags, "--compiler-filter=verify")
509}
510
511func OdexOnSystemOtherByName(name string, dexLocation string, global *GlobalConfig) bool {
512	if !global.HasSystemOther {
513		return false
514	}
515
516	if global.SanitizeLite {
517		return false
518	}
519
520	if contains(global.SystemServerApps, name) {
521		return false
522	}
523
524	for _, f := range global.PatternsOnSystemOther {
525		if makefileMatch("/"+f, dexLocation) || makefileMatch(filepath.Join(SystemPartition, f), dexLocation) {
526			return true
527		}
528	}
529
530	return false
531}
532
533func odexOnSystemOther(module *ModuleConfig, global *GlobalConfig) bool {
534	return OdexOnSystemOtherByName(module.Name, module.DexLocation, global)
535}
536
537// PathToLocation converts .../system/framework/arm64/boot.art to .../system/framework/boot.art
538func PathToLocation(path android.Path, arch android.ArchType) string {
539	return PathStringToLocation(path.String(), arch)
540}
541
542// PathStringToLocation converts .../system/framework/arm64/boot.art to .../system/framework/boot.art
543func PathStringToLocation(path string, arch android.ArchType) string {
544	pathArch := filepath.Base(filepath.Dir(path))
545	if pathArch != arch.String() {
546		panic(fmt.Errorf("last directory in %q must be %q", path, arch.String()))
547	}
548	return filepath.Join(filepath.Dir(filepath.Dir(path)), filepath.Base(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// A predefined location for the system server dex jars. This is needed in order to generate
564// class loader context for dex2oat, as the path to the jar in the Soong module may be unknown
565// at that time (Soong processes the jars in dependency order, which may be different from the
566// the system server classpath order).
567func SystemServerDexJarHostPath(ctx android.PathContext, jar string) android.OutputPath {
568	if DexpreoptRunningInSoong {
569		// Soong module, just use the default output directory $OUT/soong.
570		return android.PathForOutput(ctx, SystemServerDexjarsDir, jar+".jar")
571	} else {
572		// Make module, default output directory is $OUT (passed via the "null config" created
573		// by dexpreopt_gen). Append Soong subdirectory to match Soong module paths.
574		return android.PathForOutput(ctx, "soong", SystemServerDexjarsDir, jar+".jar")
575	}
576}
577
578// Returns path to a file containing the reult of verify_uses_libraries check (empty if the check
579// has succeeded, or an error message if it failed).
580func UsesLibrariesStatusFile(ctx android.ModuleContext) android.WritablePath {
581	return android.PathForModuleOut(ctx, "enforce_uses_libraries.status")
582}
583
584func contains(l []string, s string) bool {
585	for _, e := range l {
586		if e == s {
587			return true
588		}
589	}
590	return false
591}
592