• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2017 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 java
16
17import (
18	"strconv"
19	"strings"
20
21	"github.com/google/blueprint"
22	"github.com/google/blueprint/proptools"
23
24	"android/soong/android"
25	"android/soong/remoteexec"
26)
27
28type DexProperties struct {
29	// If set to true, compile dex regardless of installable.  Defaults to false.
30	Compile_dex *bool
31
32	// list of module-specific flags that will be used for dex compiles
33	Dxflags []string `android:"arch_variant"`
34
35	// A list of files containing rules that specify the classes to keep in the main dex file.
36	Main_dex_rules []string `android:"path"`
37
38	Optimize struct {
39		// If false, disable all optimization.  Defaults to true for android_app and
40		// android_test_helper_app modules, false for android_test, java_library, and java_test modules.
41		Enabled *bool
42		// True if the module containing this has it set by default.
43		EnabledByDefault bool `blueprint:"mutated"`
44
45		// If true, then `d8` will be used on eng builds instead of `r8`, even though
46		// optimize.enabled is true.
47		D8_on_eng *bool
48
49		// Whether to allow that library classes inherit from program classes.
50		// Defaults to false.
51		Ignore_library_extends_program *bool
52
53		// Whether to continue building even if warnings are emitted.  Defaults to true.
54		Ignore_warnings *bool
55
56		// Whether runtime invisible annotations should be kept by R8. Defaults to false.
57		// This is equivalent to:
58		//   -keepattributes RuntimeInvisibleAnnotations,
59		//                   RuntimeInvisibleParameterAnnotations,
60		//                   RuntimeInvisibleTypeAnnotations
61		// This is only applicable when RELEASE_R8_ONLY_RUNTIME_VISIBLE_ANNOTATIONS is
62		// enabled and will be used to migrate away from keeping runtime invisible
63		// annotations (b/387958004).
64		Keep_runtime_invisible_annotations *bool
65
66		// If true, runs R8 in Proguard compatibility mode, otherwise runs R8 in full mode.
67		// Defaults to false for apps and tests, true for libraries.
68		Proguard_compatibility *bool
69
70		// If true, R8 will not add public or protected members (fields or methods) to
71		// the API surface of the compilation unit, i.e., classes that are kept or
72		// have kept subclasses will not expose any members added by R8 for internal
73		// use. That includes renamed members if obfuscation is enabled.
74		// This should only be used for building targets that go on the bootclasspath.
75		// Defaults to false.
76		Protect_api_surface *bool
77
78		// If true, optimize for size by removing unused code.  Defaults to true for apps,
79		// false for libraries and tests.
80		Shrink *bool
81
82		// If true, optimize bytecode.  Defaults to false.
83		Optimize *bool
84
85		// If true, obfuscate bytecode.  Defaults to false.
86		Obfuscate *bool
87
88		// If true, do not use the flag files generated by aapt that automatically keep
89		// classes referenced by the app manifest.  Defaults to false.
90		No_aapt_flags *bool
91
92		// If true, optimize for size by removing unused resources. Defaults to false.
93		Shrink_resources *bool
94
95		// If true, use optimized resource shrinking in R8, overriding the
96		// Shrink_resources setting. Defaults to false.
97		// Optimized shrinking means that R8 will trace and treeshake resources together with code
98		// and apply additional optimizations. This implies non final fields in the R classes.
99		Optimized_shrink_resources *bool
100
101		// Flags to pass to proguard.
102		Proguard_flags []string
103
104		// Specifies the locations of files containing proguard flags.
105		Proguard_flags_files []string `android:"path"`
106
107		// If true, transitive reverse dependencies of this module will have this
108		// module's proguard spec appended to their optimization action
109		Export_proguard_flags_files *bool
110
111		// Path to a file containing a list of class names that should not be compiled using R8.
112		// These classes will be compiled by D8 similar to when Optimize.Enabled is false.
113		//
114		// Example:
115		//
116		//   r8.exclude:
117		//   com.example.Foo
118		//   com.example.Bar
119		//   com.example.Bar$Baz
120		//
121		// By default all classes are compiled using R8 when Optimize.Enabled is set.
122		Exclude *string `android:"path"`
123
124		// Optional list of downstream (Java) libraries from which to trace and preserve references
125		// when optimizing. Note that this requires that the source reference does *not* have
126		// a strict lib dependency on this target; dependencies should be on intermediate targets
127		// statically linked into this target, e.g., if A references B, and we want to trace and
128		// keep references from A when optimizing B, you would create an intermediate B.impl (
129		// containing all static code), have A depend on `B.impl` via libs, and set
130		// `trace_references_from: ["A"]` on B.
131		//
132		// Also note that these are *not* inherited across targets, they must be specified at the
133		// top-level target that is optimized.
134		//
135		// TODO(b/212737576): Handle this implicitly using bottom-up deps mutation and implicit
136		// creation of a proxy `.impl` library.
137		Trace_references_from proptools.Configurable[[]string] `android:"arch_variant"`
138	}
139
140	// Keep the data uncompressed. We always need uncompressed dex for execution,
141	// so this might actually save space by avoiding storing the same data twice.
142	// This defaults to reasonable value based on module and should not be set.
143	// It exists only to support ART tests.
144	Uncompress_dex *bool
145
146	// Exclude kotlinc generate files: *.kotlin_module, *.kotlin_builtins. Defaults to false.
147	Exclude_kotlinc_generated_files *bool
148
149	// Disable dex container (also known as "multi-dex").
150	// This may be necessary as a temporary workaround to mask toolchain bugs (see b/341652226).
151	No_dex_container *bool
152}
153
154type dexer struct {
155	dexProperties DexProperties
156
157	// list of extra proguard flag files
158	extraProguardFlagsFiles android.Paths
159	proguardDictionary      android.OptionalPath
160	proguardConfiguration   android.OptionalPath
161	proguardUsageZip        android.OptionalPath
162	resourcesInput          android.OptionalPath
163	resourcesOutput         android.OptionalPath
164
165	providesTransitiveHeaderJarsForR8
166}
167
168func (d *dexer) effectiveOptimizeEnabled(ctx android.EarlyModuleContext) bool {
169	// For eng builds, if Optimize.D8_on_eng is true, then disable optimization.
170	if ctx.Config().Eng() && proptools.Bool(d.dexProperties.Optimize.D8_on_eng) {
171		return false
172	}
173	// Otherwise, use the legacy logic of a default value which can be explicitly overridden by the module.
174	return BoolDefault(d.dexProperties.Optimize.Enabled, d.dexProperties.Optimize.EnabledByDefault)
175}
176
177func (d *DexProperties) resourceShrinkingEnabled(ctx android.ModuleContext) bool {
178	return !ctx.Config().Eng() && BoolDefault(d.Optimize.Optimized_shrink_resources, Bool(d.Optimize.Shrink_resources))
179}
180
181func (d *DexProperties) optimizedResourceShrinkingEnabled(ctx android.ModuleContext) bool {
182	return d.resourceShrinkingEnabled(ctx) && BoolDefault(d.Optimize.Optimized_shrink_resources, ctx.Config().UseOptimizedResourceShrinkingByDefault())
183}
184
185func (d *dexer) optimizeOrObfuscateEnabled(ctx android.EarlyModuleContext) bool {
186	return d.effectiveOptimizeEnabled(ctx) && (proptools.Bool(d.dexProperties.Optimize.Optimize) || proptools.Bool(d.dexProperties.Optimize.Obfuscate))
187}
188
189var d8, d8RE = pctx.MultiCommandRemoteStaticRules("d8",
190	blueprint.RuleParams{
191		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
192			`$d8Template${config.D8Cmd} ${config.D8Flags} $d8Flags --output $outDir --no-dex-input-jar $in && ` +
193			`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
194			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $mergeZipsFlags $out $outDir/classes.dex.jar $in && ` +
195			`rm -f "$outDir"/classes*.dex "$outDir/classes.dex.jar"`,
196		CommandDeps: []string{
197			"${config.D8Cmd}",
198			"${config.SoongZipCmd}",
199			"${config.MergeZipsCmd}",
200		},
201	}, map[string]*remoteexec.REParams{
202		"$d8Template": &remoteexec.REParams{
203			Labels:          map[string]string{"type": "compile", "compiler": "d8"},
204			Inputs:          []string{"${config.D8Jar}"},
205			ExecStrategy:    "${config.RED8ExecStrategy}",
206			ToolchainInputs: []string{"${config.JavaCmd}"},
207			Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
208		},
209		"$zipTemplate": &remoteexec.REParams{
210			Labels:       map[string]string{"type": "tool", "name": "soong_zip"},
211			Inputs:       []string{"${config.SoongZipCmd}", "$outDir"},
212			OutputFiles:  []string{"$outDir/classes.dex.jar"},
213			ExecStrategy: "${config.RED8ExecStrategy}",
214			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
215		},
216	}, []string{"outDir", "d8Flags", "zipFlags", "mergeZipsFlags"}, nil)
217
218// Include all of the args for d8r8, so that we can generate the partialcompileclean target's build using the same list.
219var d8r8Clean = pctx.AndroidStaticRule("d8r8-partialcompileclean",
220	blueprint.RuleParams{
221		Command: `rm -rf "${outDir}" "${outDict}" "${outConfig}" "${outUsage}" "${outUsageZip}" "${outUsageDir}" ` +
222			`"${resourcesOutput}" "${outR8ArtProfile}" ${builtOut}`,
223	}, "outDir", "outDict", "outConfig", "outUsage", "outUsageZip", "outUsageDir", "builtOut",
224	"d8Flags", "r8Flags", "zipFlags", "mergeZipsFlags", "resourcesOutput", "outR8ArtProfile", "implicits",
225)
226
227var d8r8, d8r8RE = pctx.MultiCommandRemoteStaticRules("d8r8",
228	blueprint.RuleParams{
229		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
230			`rm -f "$outDict" && rm -f "$outConfig" && rm -rf "${outUsageDir}" && ` +
231			`mkdir -p $$(dirname ${outUsage}) && ` +
232			`if [ -n "$${SOONG_USE_PARTIAL_COMPILE}" ]; then ` +
233			` for f in "${outConfig}" "${outDict}" "${outUsage}" "${resourcesOutput}"; do ` +
234			`   test -n "$${f}" && test ! -f "$${f}" && mkdir -p "$$(dirname "$${f}")" && touch "$${f}" || true; ` +
235			` done && ` +
236			` $d8Template${config.D8Cmd} ${config.D8Flags} $d8Flags --output $outDir --no-dex-input-jar $in; ` +
237			`else ` +
238			` $r8Template${config.R8Cmd} ${config.R8Flags} $r8Flags -injars $in --output $outDir ` +
239			` --no-data-resources ` +
240			` -printmapping ${outDict} ` +
241			` -printconfiguration ${outConfig} ` +
242			` -printusage ${outUsage} ` +
243			` --deps-file ${out}.d && ` +
244			` touch "${outDict}" "${outConfig}" "${outUsage}"; ` +
245			`fi && ` +
246			`${config.SoongZipCmd} -o ${outUsageZip} -C ${outUsageDir} -f ${outUsage} && ` +
247			`rm -rf ${outUsageDir} && ` +
248			`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
249			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $mergeZipsFlags $out $outDir/classes.dex.jar $in && ` +
250			`rm -f "$outDir"/classes*.dex "$outDir/classes.dex.jar" `,
251		CommandDeps: []string{
252			"${config.D8Cmd}",
253			"${config.R8Cmd}",
254			"${config.SoongZipCmd}",
255			"${config.MergeZipsCmd}",
256		},
257	}, map[string]*remoteexec.REParams{
258		"$d8Template": &remoteexec.REParams{
259			Labels:          map[string]string{"type": "compile", "compiler": "d8"},
260			Inputs:          []string{"${config.D8Jar}"},
261			ExecStrategy:    "${config.RED8ExecStrategy}",
262			ToolchainInputs: []string{"${config.JavaCmd}"},
263			Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
264		},
265		"$r8Template": &remoteexec.REParams{
266			Labels:          map[string]string{"type": "compile", "compiler": "r8"},
267			Inputs:          []string{"$implicits", "${config.R8Jar}"},
268			OutputFiles:     []string{"${outUsage}", "${outConfig}", "${outDict}", "${resourcesOutput}", "${outR8ArtProfile}"},
269			ExecStrategy:    "${config.RER8ExecStrategy}",
270			ToolchainInputs: []string{"${config.JavaCmd}"},
271			Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
272		},
273		"$zipTemplate": &remoteexec.REParams{
274			Labels:       map[string]string{"type": "tool", "name": "soong_zip"},
275			Inputs:       []string{"${config.SoongZipCmd}", "$outDir"},
276			OutputFiles:  []string{"$outDir/classes.dex.jar"},
277			ExecStrategy: "${config.RED8ExecStrategy}",
278			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
279		},
280	}, []string{"outDir", "outDict", "outConfig", "outUsage", "outUsageZip", "outUsageDir",
281		"d8Flags", "r8Flags", "zipFlags", "mergeZipsFlags", "resourcesOutput", "outR8ArtProfile"}, []string{"implicits"})
282
283var r8, r8RE = pctx.MultiCommandRemoteStaticRules("r8",
284	blueprint.RuleParams{
285		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
286			`rm -f "$outDict" && rm -f "$outConfig" && rm -rf "${outUsageDir}" && ` +
287			`mkdir -p $$(dirname ${outUsage}) && ` +
288			`$r8Template${config.R8Cmd} ${config.R8Flags} $r8Flags -injars $in --output $outDir ` +
289			`--no-data-resources ` +
290			`-printmapping ${outDict} ` +
291			`-printconfiguration ${outConfig} ` +
292			`-printusage ${outUsage} ` +
293			`--deps-file ${out}.d && ` +
294			`touch "${outDict}" "${outConfig}" "${outUsage}" && ` +
295			`${config.SoongZipCmd} -o ${outUsageZip} -C ${outUsageDir} -f ${outUsage} && ` +
296			`rm -rf ${outUsageDir} && ` +
297			`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
298			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $mergeZipsFlags $out $outDir/classes.dex.jar $in && ` +
299			`rm -f "$outDir"/classes*.dex "$outDir/classes.dex.jar"`,
300		Depfile: "${out}.d",
301		Deps:    blueprint.DepsGCC,
302		CommandDeps: []string{
303			"${config.R8Cmd}",
304			"${config.SoongZipCmd}",
305			"${config.MergeZipsCmd}",
306		},
307	}, map[string]*remoteexec.REParams{
308		"$r8Template": &remoteexec.REParams{
309			Labels:          map[string]string{"type": "compile", "compiler": "r8"},
310			Inputs:          []string{"$implicits", "${config.R8Jar}"},
311			OutputFiles:     []string{"${outUsage}", "${outConfig}", "${outDict}", "${resourcesOutput}", "${outR8ArtProfile}"},
312			ExecStrategy:    "${config.RER8ExecStrategy}",
313			ToolchainInputs: []string{"${config.JavaCmd}"},
314			Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
315		},
316		"$zipTemplate": &remoteexec.REParams{
317			Labels:       map[string]string{"type": "tool", "name": "soong_zip"},
318			Inputs:       []string{"${config.SoongZipCmd}", "$outDir"},
319			OutputFiles:  []string{"$outDir/classes.dex.jar"},
320			ExecStrategy: "${config.RER8ExecStrategy}",
321			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
322		},
323		"$zipUsageTemplate": &remoteexec.REParams{
324			Labels:       map[string]string{"type": "tool", "name": "soong_zip"},
325			Inputs:       []string{"${config.SoongZipCmd}", "${outUsage}"},
326			OutputFiles:  []string{"${outUsageZip}"},
327			ExecStrategy: "${config.RER8ExecStrategy}",
328			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
329		},
330	}, []string{"outDir", "outDict", "outConfig", "outUsage", "outUsageZip", "outUsageDir",
331		"r8Flags", "zipFlags", "mergeZipsFlags", "resourcesOutput", "outR8ArtProfile"}, []string{"implicits"})
332
333func (d *dexer) dexCommonFlags(ctx android.ModuleContext,
334	dexParams *compileDexParams) (flags []string, deps android.Paths) {
335
336	flags = d.dexProperties.Dxflags
337	// Translate all the DX flags to D8 ones until all the build files have been migrated
338	// to D8 flags. See: b/69377755
339	flags = android.RemoveListFromList(flags,
340		[]string{"--core-library", "--dex", "--multi-dex"})
341
342	for _, f := range android.PathsForModuleSrc(ctx, d.dexProperties.Main_dex_rules) {
343		flags = append(flags, "--main-dex-rules", f.String())
344		deps = append(deps, f)
345	}
346
347	var requestReleaseMode, requestDebugMode bool
348	requestReleaseMode, flags = android.RemoveFromList("--release", flags)
349	requestDebugMode, flags = android.RemoveFromList("--debug", flags)
350
351	if ctx.Config().Getenv("NO_OPTIMIZE_DX") != "" || ctx.Config().Getenv("GENERATE_DEX_DEBUG") != "" {
352		requestDebugMode = true
353		requestReleaseMode = false
354	}
355
356	// Don't strip out debug information for eng builds, unless the target
357	// explicitly provided the `--release` build flag. This allows certain
358	// test targets to remain optimized as part of eng test_suites builds.
359	if requestDebugMode {
360		flags = append(flags, "--debug")
361	} else if requestReleaseMode {
362		flags = append(flags, "--release")
363	} else if ctx.Config().Eng() {
364		flags = append(flags, "--debug")
365	} else if !d.effectiveOptimizeEnabled(ctx) && d.dexProperties.Optimize.EnabledByDefault {
366		// D8 uses --debug by default, whereas R8 uses --release by default.
367		// For targets that default to R8 usage (e.g., apps), but override this default, we still
368		// want D8 to run in release mode, preserving semantics as much as possible between the two.
369		flags = append(flags, "--release")
370	}
371
372	// Supplying the platform build flag disables various features like API modeling and desugaring.
373	// For targets with a stable min SDK version (i.e., when the min SDK is both explicitly specified
374	// and managed+versioned), we suppress this flag to ensure portability.
375	// Note: Targets with a min SDK kind of core_platform (e.g., framework.jar) or unspecified (e.g.,
376	// services.jar), are not classified as stable, which is WAI.
377	// TODO(b/232073181): Expand to additional min SDK cases after validation.
378	var addAndroidPlatformBuildFlag = false
379	if !dexParams.sdkVersion.Stable() {
380		addAndroidPlatformBuildFlag = true
381	}
382
383	effectiveVersion, err := dexParams.minSdkVersion.EffectiveVersion(ctx)
384	if err != nil {
385		ctx.PropertyErrorf("min_sdk_version", "%s", err)
386	}
387	if !Bool(d.dexProperties.No_dex_container) && effectiveVersion.FinalOrFutureInt() >= 36 && ctx.Config().UseDexV41() {
388		// W is 36, but we have not bumped the SDK version yet, so check for both.
389		if ctx.Config().PlatformSdkVersion().FinalInt() >= 36 ||
390			ctx.Config().PlatformSdkCodename() == "Baklava" {
391			flags = append([]string{"-JDcom.android.tools.r8.dexContainerExperiment"}, flags...)
392		}
393	}
394
395	// If the specified SDK level is 10000, then configure the compiler to use the
396	// current platform SDK level and to compile the build as a platform build.
397	var minApiFlagValue = effectiveVersion.FinalOrFutureInt()
398	if minApiFlagValue == 10000 {
399		minApiFlagValue = ctx.Config().PlatformSdkVersion().FinalInt()
400		addAndroidPlatformBuildFlag = true
401	}
402	flags = append(flags, "--min-api "+strconv.Itoa(minApiFlagValue))
403
404	if addAndroidPlatformBuildFlag {
405		flags = append(flags, "--android-platform-build")
406	}
407	return flags, deps
408}
409
410func (d *dexer) d8Flags(ctx android.ModuleContext, dexParams *compileDexParams) (d8Flags []string, d8Deps android.Paths, artProfileOutput *android.OutputPath) {
411	flags := dexParams.flags
412	d8Flags = append(d8Flags, flags.bootClasspath.FormRepeatedClassPath("--lib ")...)
413	d8Flags = append(d8Flags, flags.dexClasspath.FormRepeatedClassPath("--lib ")...)
414
415	d8Deps = append(d8Deps, flags.bootClasspath...)
416	d8Deps = append(d8Deps, flags.dexClasspath...)
417
418	if flags, deps, profileOutput := d.addArtProfile(ctx, dexParams); profileOutput != nil {
419		d8Flags = append(d8Flags, flags...)
420		d8Deps = append(d8Deps, deps...)
421		artProfileOutput = profileOutput
422	}
423
424	return d8Flags, d8Deps, artProfileOutput
425}
426
427func (d *dexer) r8Flags(ctx android.ModuleContext, dexParams *compileDexParams, debugMode bool) (r8Flags []string, r8Deps android.Paths, artProfileOutput *android.OutputPath) {
428	flags := dexParams.flags
429	opt := d.dexProperties.Optimize
430
431	// When an app contains references to APIs that are not in the SDK specified by
432	// its LOCAL_SDK_VERSION for example added by support library or by runtime
433	// classes added by desugaring, we artifically raise the "SDK version" "linked" by
434	// ProGuard, to
435	// - suppress ProGuard warnings of referencing symbols unknown to the lower SDK version.
436	// - prevent ProGuard stripping subclass in the support library that extends class added in the higher SDK version.
437	// See b/20667396
438	// TODO(b/360905238): Remove SdkSystemServer exception after resolving missing class references.
439	if !dexParams.sdkVersion.Stable() || dexParams.sdkVersion.Kind == android.SdkSystemServer {
440		var proguardRaiseDeps classpath
441		ctx.VisitDirectDepsProxyWithTag(proguardRaiseTag, func(m android.ModuleProxy) {
442			if dep, ok := android.OtherModuleProvider(ctx, m, JavaInfoProvider); ok {
443				proguardRaiseDeps = append(proguardRaiseDeps, dep.RepackagedHeaderJars...)
444			}
445		})
446		r8Flags = append(r8Flags, proguardRaiseDeps.FormJavaClassPath("-libraryjars"))
447		r8Deps = append(r8Deps, proguardRaiseDeps...)
448	}
449
450	r8Flags = append(r8Flags, flags.bootClasspath.FormJavaClassPath("-libraryjars"))
451	r8Deps = append(r8Deps, flags.bootClasspath...)
452	r8Flags = append(r8Flags, flags.dexClasspath.FormJavaClassPath("-libraryjars"))
453	r8Deps = append(r8Deps, flags.dexClasspath...)
454
455	transitiveStaticLibsLookupMap := map[android.Path]bool{}
456	for _, jar := range d.transitiveStaticLibsHeaderJarsForR8.ToList() {
457		transitiveStaticLibsLookupMap[jar] = true
458	}
459	transitiveHeaderJars := android.Paths{}
460	for _, jar := range d.transitiveLibsHeaderJarsForR8.ToList() {
461		if _, ok := transitiveStaticLibsLookupMap[jar]; ok {
462			// don't include a lib if it is already packaged in the current JAR as a static lib
463			continue
464		}
465		transitiveHeaderJars = append(transitiveHeaderJars, jar)
466	}
467	transitiveClasspath := classpath(transitiveHeaderJars)
468	r8Flags = append(r8Flags, transitiveClasspath.FormJavaClassPath("-libraryjars"))
469	r8Deps = append(r8Deps, transitiveClasspath...)
470
471	flagFiles := android.Paths{
472		android.PathForSource(ctx, "build/make/core/proguard.flags"),
473	}
474
475	if ctx.Config().UseR8GlobalCheckNotNullFlags() {
476		flagFiles = append(flagFiles, android.PathForSource(ctx,
477			"build/make/core/proguard/checknotnull.flags"))
478	}
479
480	flagFiles = append(flagFiles, d.extraProguardFlagsFiles...)
481	// TODO(ccross): static android library proguard files
482
483	flagFiles = append(flagFiles, android.PathsForModuleSrc(ctx, opt.Proguard_flags_files)...)
484
485	traceReferencesSources := android.Paths{}
486	ctx.VisitDirectDepsProxyWithTag(traceReferencesTag, func(m android.ModuleProxy) {
487		if dep, ok := android.OtherModuleProvider(ctx, m, JavaInfoProvider); ok {
488			traceReferencesSources = append(traceReferencesSources, dep.ImplementationJars...)
489		}
490	})
491	if len(traceReferencesSources) > 0 {
492		traceTarget := dexParams.classesJar
493		traceLibs := android.FirstUniquePaths(append(flags.bootClasspath.Paths(), flags.dexClasspath.Paths()...))
494		traceReferencesFlags := android.PathForModuleOut(ctx, "proguard", "trace_references.flags")
495		TraceReferences(ctx, traceReferencesSources, traceTarget, traceLibs, traceReferencesFlags)
496		flagFiles = append(flagFiles, traceReferencesFlags)
497	}
498
499	flagFiles = android.FirstUniquePaths(flagFiles)
500
501	r8Flags = append(r8Flags, android.JoinWithPrefix(flagFiles.Strings(), "-include "))
502	r8Deps = append(r8Deps, flagFiles...)
503
504	// TODO(b/70942988): This is included from build/make/core/proguard.flags
505	r8Deps = append(r8Deps, android.PathForSource(ctx,
506		"build/make/core/proguard_basic_keeps.flags"))
507
508	r8Flags = append(r8Flags, opt.Proguard_flags...)
509
510	if BoolDefault(opt.Ignore_library_extends_program, false) {
511		r8Flags = append(r8Flags, "--ignore-library-extends-program")
512	}
513
514	if BoolDefault(opt.Keep_runtime_invisible_annotations, false) {
515		r8Flags = append(r8Flags, "--keep-runtime-invisible-annotations")
516	}
517
518	if BoolDefault(opt.Proguard_compatibility, !ctx.Config().UseR8FullModeByDefault()) {
519		r8Flags = append(r8Flags, "--force-proguard-compatibility")
520	}
521
522	if BoolDefault(opt.Protect_api_surface, false) {
523		r8Flags = append(r8Flags, "--protect-api-surface")
524	}
525
526	// Avoid unnecessary stack frame noise by only injecting source map ids for non-debug
527	// optimized or obfuscated targets.
528	if (Bool(opt.Optimize) || Bool(opt.Obfuscate)) && !debugMode {
529		// TODO(b/213833843): Allow configuration of the prefix via a build variable.
530		var sourceFilePrefix = "go/retraceme "
531		var sourceFileTemplate = "\"" + sourceFilePrefix + "%MAP_ID\""
532		r8Flags = append(r8Flags, "--map-id-template", "%MAP_HASH")
533		r8Flags = append(r8Flags, "--source-file-template", sourceFileTemplate)
534	}
535
536	// TODO(ccross): Don't shrink app instrumentation tests by default.
537	if !Bool(opt.Shrink) {
538		r8Flags = append(r8Flags, "-dontshrink")
539	}
540
541	if !Bool(opt.Optimize) {
542		r8Flags = append(r8Flags, "-dontoptimize")
543	}
544
545	// TODO(ccross): error if obufscation + app instrumentation test.
546	if !Bool(opt.Obfuscate) {
547		r8Flags = append(r8Flags, "-dontobfuscate")
548	}
549	// TODO(ccross): if this is an instrumentation test of an obfuscated app, use the
550	// dictionary of the app and move the app from libraryjars to injars.
551
552	// TODO(b/180878971): missing classes should be added to the relevant builds.
553	// TODO(b/229727645): do not use true as default for Android platform builds.
554	if proptools.BoolDefault(opt.Ignore_warnings, true) {
555		r8Flags = append(r8Flags, "-ignorewarnings")
556	}
557
558	// resourcesInput is empty when we don't use resource shrinking, if on, pass these to R8
559	if d.resourcesInput.Valid() {
560		r8Flags = append(r8Flags, "--resource-input", d.resourcesInput.Path().String())
561		r8Deps = append(r8Deps, d.resourcesInput.Path())
562		r8Flags = append(r8Flags, "--resource-output", d.resourcesOutput.Path().String())
563		if d.dexProperties.optimizedResourceShrinkingEnabled(ctx) {
564			r8Flags = append(r8Flags, "--optimized-resource-shrinking")
565			if Bool(d.dexProperties.Optimize.Optimized_shrink_resources) {
566				// Explicitly opted into optimized shrinking, no need for keeping R$id entries
567				r8Flags = append(r8Flags, "--force-optimized-resource-shrinking")
568			}
569		}
570	}
571
572	if flags, deps, profileOutput := d.addArtProfile(ctx, dexParams); profileOutput != nil {
573		r8Flags = append(r8Flags, flags...)
574		r8Deps = append(r8Deps, deps...)
575		artProfileOutput = profileOutput
576	}
577
578	if ctx.Config().UseR8StoreStoreFenceConstructorInlining() {
579		r8Flags = append(r8Flags, "--store-store-fence-constructor-inlining")
580	}
581
582	if opt.Exclude != nil {
583		r8Flags = append(r8Flags, "--exclude", *opt.Exclude)
584		r8Deps = append(r8Deps, android.PathForModuleSrc(ctx, *opt.Exclude))
585	}
586
587	return r8Flags, r8Deps, artProfileOutput
588}
589
590type compileDexParams struct {
591	flags           javaBuilderFlags
592	sdkVersion      android.SdkSpec
593	minSdkVersion   android.ApiLevel
594	classesJar      android.Path
595	jarName         string
596	artProfileInput *string
597}
598
599// Adds --art-profile to r8/d8 command.
600// r8/d8 will output a generated profile file to match the optimized dex code.
601func (d *dexer) addArtProfile(ctx android.ModuleContext, dexParams *compileDexParams) (flags []string, deps android.Paths, artProfileOutputPath *android.OutputPath) {
602	if dexParams.artProfileInput == nil {
603		return nil, nil, nil
604	}
605	artProfileInputPath := android.PathForModuleSrc(ctx, *dexParams.artProfileInput)
606	artProfileOutputPathValue := android.PathForModuleOut(ctx, "profile.prof.txt").OutputPath
607	artProfileOutputPath = &artProfileOutputPathValue
608	flags = []string{
609		"--art-profile",
610		artProfileInputPath.String(),
611		artProfileOutputPath.String(),
612	}
613	deps = append(deps, artProfileInputPath)
614	return flags, deps, artProfileOutputPath
615
616}
617
618// Return the compiled dex jar and (optional) profile _after_ r8 optimization
619func (d *dexer) compileDex(ctx android.ModuleContext, dexParams *compileDexParams) (android.Path, android.Path) {
620
621	// Compile classes.jar into classes.dex and then javalib.jar
622	javalibJar := android.PathForModuleOut(ctx, "dex", dexParams.jarName).OutputPath
623	cleanPhonyPath := android.PathForModuleOut(ctx, "dex", dexParams.jarName+"-partialcompileclean").OutputPath
624	outDir := android.PathForModuleOut(ctx, "dex")
625
626	zipFlags := "--ignore_missing_files"
627	if proptools.Bool(d.dexProperties.Uncompress_dex) {
628		zipFlags += " -L 0"
629	}
630
631	commonFlags, commonDeps := d.dexCommonFlags(ctx, dexParams)
632
633	// Exclude kotlinc generated files when "exclude_kotlinc_generated_files" is set to true.
634	mergeZipsFlags := ""
635	if proptools.BoolDefault(d.dexProperties.Exclude_kotlinc_generated_files, false) {
636		mergeZipsFlags = "-stripFile META-INF/*.kotlin_module -stripFile **/*.kotlin_builtins"
637	}
638
639	useR8 := d.effectiveOptimizeEnabled(ctx)
640	useD8 := !useR8 || ctx.Config().PartialCompileFlags().Use_d8
641	rbeR8 := ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_R8")
642	rbeD8 := ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_D8")
643	var rule blueprint.Rule
644	var description string
645	var artProfileOutputPath *android.OutputPath
646	var implicitOutputs android.WritablePaths
647	var deps android.Paths
648	args := map[string]string{
649		"zipFlags":       zipFlags,
650		"outDir":         outDir.String(),
651		"mergeZipsFlags": mergeZipsFlags,
652	}
653	if useR8 {
654		proguardDictionary := android.PathForModuleOut(ctx, "proguard_dictionary")
655		d.proguardDictionary = android.OptionalPathForPath(proguardDictionary)
656		proguardConfiguration := android.PathForModuleOut(ctx, "proguard_configuration")
657		d.proguardConfiguration = android.OptionalPathForPath(proguardConfiguration)
658		proguardUsageDir := android.PathForModuleOut(ctx, "proguard_usage")
659		proguardUsage := proguardUsageDir.Join(ctx, ctx.Namespace().Path,
660			android.ModuleNameWithPossibleOverride(ctx), "unused.txt")
661		proguardUsageZip := android.PathForModuleOut(ctx, "proguard_usage.zip")
662		d.proguardUsageZip = android.OptionalPathForPath(proguardUsageZip)
663		resourcesOutput := android.PathForModuleOut(ctx, "package-res-shrunken.apk")
664		d.resourcesOutput = android.OptionalPathForPath(resourcesOutput)
665		implicitOutputs = append(implicitOutputs, android.WritablePaths{
666			proguardDictionary,
667			proguardUsageZip,
668			proguardConfiguration,
669		}...)
670		description = "r8"
671		debugMode := android.InList("--debug", commonFlags)
672		r8Flags, r8Deps, r8ArtProfileOutputPath := d.r8Flags(ctx, dexParams, debugMode)
673		deps = append(deps, r8Deps...)
674		args["r8Flags"] = strings.Join(append(commonFlags, r8Flags...), " ")
675		if r8ArtProfileOutputPath != nil {
676			artProfileOutputPath = r8ArtProfileOutputPath
677			// Add the implicit r8 Art profile output to args so that r8RE knows
678			// about this implicit output
679			args["outR8ArtProfile"] = r8ArtProfileOutputPath.String()
680		}
681		args["outDict"] = proguardDictionary.String()
682		args["outConfig"] = proguardConfiguration.String()
683		args["outUsageDir"] = proguardUsageDir.String()
684		args["outUsage"] = proguardUsage.String()
685		args["outUsageZip"] = proguardUsageZip.String()
686		if d.resourcesInput.Valid() {
687			implicitOutputs = append(implicitOutputs, resourcesOutput)
688			args["resourcesOutput"] = resourcesOutput.String()
689		}
690
691		rule = r8
692		if rbeR8 {
693			rule = r8RE
694			args["implicits"] = strings.Join(deps.Strings(), ",")
695		}
696	}
697	if useD8 {
698		description = "d8"
699		d8Flags, d8Deps, d8ArtProfileOutputPath := d.d8Flags(ctx, dexParams)
700		deps = append(deps, d8Deps...)
701		deps = append(deps, commonDeps...)
702		args["d8Flags"] = strings.Join(append(commonFlags, d8Flags...), " ")
703		if d8ArtProfileOutputPath != nil {
704			artProfileOutputPath = d8ArtProfileOutputPath
705		}
706		// If we are generating both d8 and r8, only use RBE when both are enabled.
707		switch {
708		case useR8 && rule == r8:
709			rule = d8r8
710			description = "d8r8"
711		case useR8 && rule == r8RE && rbeD8:
712			rule = d8r8RE
713			description = "d8r8"
714		case rbeD8:
715			rule = d8RE
716		default:
717			rule = d8
718		}
719	}
720	if artProfileOutputPath != nil {
721		implicitOutputs = append(
722			implicitOutputs,
723			artProfileOutputPath,
724		)
725	}
726	ctx.Build(pctx, android.BuildParams{
727		Rule:            rule,
728		Description:     description,
729		Output:          javalibJar,
730		ImplicitOutputs: implicitOutputs,
731		Input:           dexParams.classesJar,
732		Implicits:       deps,
733		Args:            args,
734	})
735	if useR8 && useD8 {
736		// Generate the rule for partial compile clean.
737		args["builtOut"] = javalibJar.String()
738		ctx.Build(pctx, android.BuildParams{
739			Rule:        d8r8Clean,
740			Description: "d8r8Clean",
741			Output:      cleanPhonyPath,
742			Args:        args,
743			PhonyOutput: true,
744		})
745		ctx.Phony("partialcompileclean", cleanPhonyPath)
746	}
747
748	if proptools.Bool(d.dexProperties.Uncompress_dex) {
749		alignedJavalibJar := android.PathForModuleOut(ctx, "aligned", dexParams.jarName).OutputPath
750		TransformZipAlign(ctx, alignedJavalibJar, javalibJar, nil)
751		javalibJar = alignedJavalibJar
752	}
753
754	return javalibJar, artProfileOutputPath
755}
756
757type ProguardInfo struct {
758	ModuleName         string
759	Class              string
760	ProguardDictionary android.Path
761	ProguardUsageZip   android.Path
762	ClassesJar         android.Path
763}
764
765var ProguardProvider = blueprint.NewProvider[ProguardInfo]()
766