• 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	Optimize struct {
36		// If false, disable all optimization.  Defaults to true for android_app and android_test
37		// modules, false for java_library and java_test modules.
38		Enabled *bool
39		// True if the module containing this has it set by default.
40		EnabledByDefault bool `blueprint:"mutated"`
41
42		// If true, runs R8 in Proguard compatibility mode (default).
43		// Otherwise, runs R8 in full mode.
44		Proguard_compatibility *bool
45
46		// If true, optimize for size by removing unused code.  Defaults to true for apps,
47		// false for libraries and tests.
48		Shrink *bool
49
50		// If true, optimize bytecode.  Defaults to false.
51		Optimize *bool
52
53		// If true, obfuscate bytecode.  Defaults to false.
54		Obfuscate *bool
55
56		// If true, do not use the flag files generated by aapt that automatically keep
57		// classes referenced by the app manifest.  Defaults to false.
58		No_aapt_flags *bool
59
60		// Flags to pass to proguard.
61		Proguard_flags []string
62
63		// Specifies the locations of files containing proguard flags.
64		Proguard_flags_files []string `android:"path"`
65	}
66
67	// Keep the data uncompressed. We always need uncompressed dex for execution,
68	// so this might actually save space by avoiding storing the same data twice.
69	// This defaults to reasonable value based on module and should not be set.
70	// It exists only to support ART tests.
71	Uncompress_dex *bool
72}
73
74type dexer struct {
75	dexProperties DexProperties
76
77	// list of extra proguard flag files
78	extraProguardFlagFiles android.Paths
79	proguardDictionary     android.OptionalPath
80	proguardUsageZip       android.OptionalPath
81}
82
83func (d *dexer) effectiveOptimizeEnabled() bool {
84	return BoolDefault(d.dexProperties.Optimize.Enabled, d.dexProperties.Optimize.EnabledByDefault)
85}
86
87func init() {
88	pctx.HostBinToolVariable("runWithTimeoutCmd", "run_with_timeout")
89	pctx.SourcePathVariable("jstackCmd", "${config.JavaToolchain}/jstack")
90}
91
92var d8, d8RE = pctx.MultiCommandRemoteStaticRules("d8",
93	blueprint.RuleParams{
94		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
95			`$d8Template${config.D8Cmd} ${config.DexFlags} --output $outDir $d8Flags $in && ` +
96			`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
97			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $out $outDir/classes.dex.jar $in`,
98		CommandDeps: []string{
99			"${config.D8Cmd}",
100			"${config.SoongZipCmd}",
101			"${config.MergeZipsCmd}",
102		},
103	}, map[string]*remoteexec.REParams{
104		"$d8Template": &remoteexec.REParams{
105			Labels:          map[string]string{"type": "compile", "compiler": "d8"},
106			Inputs:          []string{"${config.D8Jar}"},
107			ExecStrategy:    "${config.RED8ExecStrategy}",
108			ToolchainInputs: []string{"${config.JavaCmd}"},
109			Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
110		},
111		"$zipTemplate": &remoteexec.REParams{
112			Labels:       map[string]string{"type": "tool", "name": "soong_zip"},
113			Inputs:       []string{"${config.SoongZipCmd}", "$outDir"},
114			OutputFiles:  []string{"$outDir/classes.dex.jar"},
115			ExecStrategy: "${config.RED8ExecStrategy}",
116			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
117		},
118	}, []string{"outDir", "d8Flags", "zipFlags"}, nil)
119
120var r8, r8RE = pctx.MultiCommandRemoteStaticRules("r8",
121	blueprint.RuleParams{
122		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
123			`rm -f "$outDict" && rm -rf "${outUsageDir}" && ` +
124			`mkdir -p $$(dirname ${outUsage}) && ` +
125			// TODO(b/181095653): remove R8 timeout and go back to config.R8Cmd.
126			`${runWithTimeoutCmd} -timeout 30m -on_timeout '${jstackCmd} $$PID' -- ` +
127			`$r8Template${config.JavaCmd} ${config.DexJavaFlags} -cp ${config.R8Jar} ` +
128			`com.android.tools.r8.compatproguard.CompatProguard -injars $in --output $outDir ` +
129			`--no-data-resources ` +
130			`-printmapping ${outDict} ` +
131			`-printusage ${outUsage} ` +
132			`$r8Flags && ` +
133			`touch "${outDict}" "${outUsage}" && ` +
134			`${config.SoongZipCmd} -o ${outUsageZip} -C ${outUsageDir} -f ${outUsage} && ` +
135			`rm -rf ${outUsageDir} && ` +
136			`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
137			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $out $outDir/classes.dex.jar $in`,
138		CommandDeps: []string{
139			"${config.R8Jar}",
140			"${config.SoongZipCmd}",
141			"${config.MergeZipsCmd}",
142			"${runWithTimeoutCmd}",
143		},
144	}, map[string]*remoteexec.REParams{
145		"$r8Template": &remoteexec.REParams{
146			Labels:          map[string]string{"type": "compile", "compiler": "r8"},
147			Inputs:          []string{"$implicits", "${config.R8Jar}"},
148			OutputFiles:     []string{"${outUsage}"},
149			ExecStrategy:    "${config.RER8ExecStrategy}",
150			ToolchainInputs: []string{"${config.JavaCmd}"},
151			Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
152		},
153		"$zipTemplate": &remoteexec.REParams{
154			Labels:       map[string]string{"type": "tool", "name": "soong_zip"},
155			Inputs:       []string{"${config.SoongZipCmd}", "$outDir"},
156			OutputFiles:  []string{"$outDir/classes.dex.jar"},
157			ExecStrategy: "${config.RER8ExecStrategy}",
158			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
159		},
160		"$zipUsageTemplate": &remoteexec.REParams{
161			Labels:       map[string]string{"type": "tool", "name": "soong_zip"},
162			Inputs:       []string{"${config.SoongZipCmd}", "${outUsage}"},
163			OutputFiles:  []string{"${outUsageZip}"},
164			ExecStrategy: "${config.RER8ExecStrategy}",
165			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
166		},
167	}, []string{"outDir", "outDict", "outUsage", "outUsageZip", "outUsageDir",
168		"r8Flags", "zipFlags"}, []string{"implicits"})
169
170func (d *dexer) dexCommonFlags(ctx android.ModuleContext, minSdkVersion android.SdkSpec) []string {
171	flags := d.dexProperties.Dxflags
172	// Translate all the DX flags to D8 ones until all the build files have been migrated
173	// to D8 flags. See: b/69377755
174	flags = android.RemoveListFromList(flags,
175		[]string{"--core-library", "--dex", "--multi-dex"})
176
177	if ctx.Config().Getenv("NO_OPTIMIZE_DX") != "" {
178		flags = append(flags, "--debug")
179	}
180
181	if ctx.Config().Getenv("GENERATE_DEX_DEBUG") != "" {
182		flags = append(flags,
183			"--debug",
184			"--verbose")
185	}
186
187	effectiveVersion, err := minSdkVersion.EffectiveVersion(ctx)
188	if err != nil {
189		ctx.PropertyErrorf("min_sdk_version", "%s", err)
190	}
191
192	flags = append(flags, "--min-api "+strconv.Itoa(effectiveVersion.FinalOrFutureInt()))
193	return flags
194}
195
196func d8Flags(flags javaBuilderFlags) (d8Flags []string, d8Deps android.Paths) {
197	d8Flags = append(d8Flags, flags.bootClasspath.FormRepeatedClassPath("--lib ")...)
198	d8Flags = append(d8Flags, flags.classpath.FormRepeatedClassPath("--lib ")...)
199
200	d8Deps = append(d8Deps, flags.bootClasspath...)
201	d8Deps = append(d8Deps, flags.classpath...)
202
203	return d8Flags, d8Deps
204}
205
206func (d *dexer) r8Flags(ctx android.ModuleContext, flags javaBuilderFlags) (r8Flags []string, r8Deps android.Paths) {
207	opt := d.dexProperties.Optimize
208
209	// When an app contains references to APIs that are not in the SDK specified by
210	// its LOCAL_SDK_VERSION for example added by support library or by runtime
211	// classes added by desugaring, we artifically raise the "SDK version" "linked" by
212	// ProGuard, to
213	// - suppress ProGuard warnings of referencing symbols unknown to the lower SDK version.
214	// - prevent ProGuard stripping subclass in the support library that extends class added in the higher SDK version.
215	// See b/20667396
216	var proguardRaiseDeps classpath
217	ctx.VisitDirectDepsWithTag(proguardRaiseTag, func(m android.Module) {
218		dep := ctx.OtherModuleProvider(m, JavaInfoProvider).(JavaInfo)
219		proguardRaiseDeps = append(proguardRaiseDeps, dep.HeaderJars...)
220	})
221
222	r8Flags = append(r8Flags, proguardRaiseDeps.FormJavaClassPath("-libraryjars"))
223	r8Flags = append(r8Flags, flags.bootClasspath.FormJavaClassPath("-libraryjars"))
224	r8Flags = append(r8Flags, flags.classpath.FormJavaClassPath("-libraryjars"))
225
226	r8Deps = append(r8Deps, proguardRaiseDeps...)
227	r8Deps = append(r8Deps, flags.bootClasspath...)
228	r8Deps = append(r8Deps, flags.classpath...)
229
230	flagFiles := android.Paths{
231		android.PathForSource(ctx, "build/make/core/proguard.flags"),
232	}
233
234	flagFiles = append(flagFiles, d.extraProguardFlagFiles...)
235	// TODO(ccross): static android library proguard files
236
237	flagFiles = append(flagFiles, android.PathsForModuleSrc(ctx, opt.Proguard_flags_files)...)
238
239	r8Flags = append(r8Flags, android.JoinWithPrefix(flagFiles.Strings(), "-include "))
240	r8Deps = append(r8Deps, flagFiles...)
241
242	// TODO(b/70942988): This is included from build/make/core/proguard.flags
243	r8Deps = append(r8Deps, android.PathForSource(ctx,
244		"build/make/core/proguard_basic_keeps.flags"))
245
246	r8Flags = append(r8Flags, opt.Proguard_flags...)
247
248	if BoolDefault(opt.Proguard_compatibility, true) {
249		r8Flags = append(r8Flags, "--force-proguard-compatibility")
250	}
251
252	// TODO(ccross): Don't shrink app instrumentation tests by default.
253	if !Bool(opt.Shrink) {
254		r8Flags = append(r8Flags, "-dontshrink")
255	}
256
257	if !Bool(opt.Optimize) {
258		r8Flags = append(r8Flags, "-dontoptimize")
259	}
260
261	// TODO(ccross): error if obufscation + app instrumentation test.
262	if !Bool(opt.Obfuscate) {
263		r8Flags = append(r8Flags, "-dontobfuscate")
264	}
265	// TODO(ccross): if this is an instrumentation test of an obfuscated app, use the
266	// dictionary of the app and move the app from libraryjars to injars.
267
268	// Don't strip out debug information for eng builds.
269	if ctx.Config().Eng() {
270		r8Flags = append(r8Flags, "--debug")
271	}
272
273	// TODO(b/180878971): missing classes should be added to the relevant builds.
274	r8Flags = append(r8Flags, "-ignorewarnings")
275
276	return r8Flags, r8Deps
277}
278
279func (d *dexer) compileDex(ctx android.ModuleContext, flags javaBuilderFlags, minSdkVersion android.SdkSpec,
280	classesJar android.Path, jarName string) android.OutputPath {
281
282	// Compile classes.jar into classes.dex and then javalib.jar
283	javalibJar := android.PathForModuleOut(ctx, "dex", jarName).OutputPath
284	outDir := android.PathForModuleOut(ctx, "dex")
285
286	zipFlags := "--ignore_missing_files"
287	if proptools.Bool(d.dexProperties.Uncompress_dex) {
288		zipFlags += " -L 0"
289	}
290
291	commonFlags := d.dexCommonFlags(ctx, minSdkVersion)
292
293	useR8 := d.effectiveOptimizeEnabled()
294	if useR8 {
295		proguardDictionary := android.PathForModuleOut(ctx, "proguard_dictionary")
296		d.proguardDictionary = android.OptionalPathForPath(proguardDictionary)
297		proguardUsageDir := android.PathForModuleOut(ctx, "proguard_usage")
298		proguardUsage := proguardUsageDir.Join(ctx, ctx.Namespace().Path,
299			android.ModuleNameWithPossibleOverride(ctx), "unused.txt")
300		proguardUsageZip := android.PathForModuleOut(ctx, "proguard_usage.zip")
301		d.proguardUsageZip = android.OptionalPathForPath(proguardUsageZip)
302		r8Flags, r8Deps := d.r8Flags(ctx, flags)
303		rule := r8
304		args := map[string]string{
305			"r8Flags":     strings.Join(append(commonFlags, r8Flags...), " "),
306			"zipFlags":    zipFlags,
307			"outDict":     proguardDictionary.String(),
308			"outUsageDir": proguardUsageDir.String(),
309			"outUsage":    proguardUsage.String(),
310			"outUsageZip": proguardUsageZip.String(),
311			"outDir":      outDir.String(),
312		}
313		if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_R8") {
314			rule = r8RE
315			args["implicits"] = strings.Join(r8Deps.Strings(), ",")
316		}
317		ctx.Build(pctx, android.BuildParams{
318			Rule:            rule,
319			Description:     "r8",
320			Output:          javalibJar,
321			ImplicitOutputs: android.WritablePaths{proguardDictionary, proguardUsageZip},
322			Input:           classesJar,
323			Implicits:       r8Deps,
324			Args:            args,
325		})
326	} else {
327		d8Flags, d8Deps := d8Flags(flags)
328		rule := d8
329		if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_D8") {
330			rule = d8RE
331		}
332		ctx.Build(pctx, android.BuildParams{
333			Rule:        rule,
334			Description: "d8",
335			Output:      javalibJar,
336			Input:       classesJar,
337			Implicits:   d8Deps,
338			Args: map[string]string{
339				"d8Flags":  strings.Join(append(commonFlags, d8Flags...), " "),
340				"zipFlags": zipFlags,
341				"outDir":   outDir.String(),
342			},
343		})
344	}
345	if proptools.Bool(d.dexProperties.Uncompress_dex) {
346		alignedJavalibJar := android.PathForModuleOut(ctx, "aligned", jarName).OutputPath
347		TransformZipAlign(ctx, alignedJavalibJar, javalibJar)
348		javalibJar = alignedJavalibJar
349	}
350
351	return javalibJar
352}
353