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	"strings"
19
20	"github.com/google/blueprint"
21	"github.com/google/blueprint/proptools"
22
23	"android/soong/android"
24	"android/soong/remoteexec"
25)
26
27var d8, d8RE = remoteexec.MultiCommandStaticRules(pctx, "d8",
28	blueprint.RuleParams{
29		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
30			`$d8Template${config.D8Cmd} ${config.DexFlags} --output $outDir $d8Flags $in && ` +
31			`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
32			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $out $outDir/classes.dex.jar $in`,
33		CommandDeps: []string{
34			"${config.D8Cmd}",
35			"${config.SoongZipCmd}",
36			"${config.MergeZipsCmd}",
37		},
38	}, map[string]*remoteexec.REParams{
39		"$d8Template": &remoteexec.REParams{
40			Labels:          map[string]string{"type": "compile", "compiler": "d8"},
41			Inputs:          []string{"${config.D8Jar}"},
42			ExecStrategy:    "${config.RED8ExecStrategy}",
43			ToolchainInputs: []string{"${config.JavaCmd}"},
44			Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
45		},
46		"$zipTemplate": &remoteexec.REParams{
47			Labels:       map[string]string{"type": "tool", "name": "soong_zip"},
48			Inputs:       []string{"${config.SoongZipCmd}", "$outDir"},
49			OutputFiles:  []string{"$outDir/classes.dex.jar"},
50			ExecStrategy: "${config.RED8ExecStrategy}",
51			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
52		},
53	}, []string{"outDir", "d8Flags", "zipFlags"}, nil)
54
55var r8, r8RE = remoteexec.MultiCommandStaticRules(pctx, "r8",
56	blueprint.RuleParams{
57		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
58			`rm -f "$outDict" && ` +
59			`$r8Template${config.R8Cmd} ${config.DexFlags} -injars $in --output $outDir ` +
60			`--force-proguard-compatibility ` +
61			`--no-data-resources ` +
62			`-printmapping $outDict ` +
63			`$r8Flags && ` +
64			`touch "$outDict" && ` +
65			`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
66			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $out $outDir/classes.dex.jar $in`,
67		CommandDeps: []string{
68			"${config.R8Cmd}",
69			"${config.SoongZipCmd}",
70			"${config.MergeZipsCmd}",
71		},
72	}, map[string]*remoteexec.REParams{
73		"$r8Template": &remoteexec.REParams{
74			Labels:          map[string]string{"type": "compile", "compiler": "r8"},
75			Inputs:          []string{"$implicits", "${config.R8Jar}"},
76			ExecStrategy:    "${config.RER8ExecStrategy}",
77			ToolchainInputs: []string{"${config.JavaCmd}"},
78			Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
79		},
80		"$zipTemplate": &remoteexec.REParams{
81			Labels:       map[string]string{"type": "tool", "name": "soong_zip"},
82			Inputs:       []string{"${config.SoongZipCmd}", "$outDir"},
83			OutputFiles:  []string{"$outDir/classes.dex.jar"},
84			ExecStrategy: "${config.RER8ExecStrategy}",
85			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
86		},
87	}, []string{"outDir", "outDict", "r8Flags", "zipFlags"}, []string{"implicits"})
88
89func (j *Module) dexCommonFlags(ctx android.ModuleContext) []string {
90	flags := j.deviceProperties.Dxflags
91	// Translate all the DX flags to D8 ones until all the build files have been migrated
92	// to D8 flags. See: b/69377755
93	flags = android.RemoveListFromList(flags,
94		[]string{"--core-library", "--dex", "--multi-dex"})
95
96	if ctx.Config().Getenv("NO_OPTIMIZE_DX") != "" {
97		flags = append(flags, "--debug")
98	}
99
100	if ctx.Config().Getenv("GENERATE_DEX_DEBUG") != "" {
101		flags = append(flags,
102			"--debug",
103			"--verbose")
104	}
105
106	minSdkVersion, err := j.minSdkVersion().effectiveVersion(ctx)
107	if err != nil {
108		ctx.PropertyErrorf("min_sdk_version", "%s", err)
109	}
110
111	flags = append(flags, "--min-api "+minSdkVersion.asNumberString())
112	return flags
113}
114
115func (j *Module) d8Flags(ctx android.ModuleContext, flags javaBuilderFlags) ([]string, android.Paths) {
116	d8Flags := j.dexCommonFlags(ctx)
117
118	d8Flags = append(d8Flags, flags.bootClasspath.FormRepeatedClassPath("--lib ")...)
119	d8Flags = append(d8Flags, flags.classpath.FormRepeatedClassPath("--lib ")...)
120
121	var d8Deps android.Paths
122	d8Deps = append(d8Deps, flags.bootClasspath...)
123	d8Deps = append(d8Deps, flags.classpath...)
124
125	return d8Flags, d8Deps
126}
127
128func (j *Module) r8Flags(ctx android.ModuleContext, flags javaBuilderFlags) (r8Flags []string, r8Deps android.Paths) {
129	opt := j.deviceProperties.Optimize
130
131	// When an app contains references to APIs that are not in the SDK specified by
132	// its LOCAL_SDK_VERSION for example added by support library or by runtime
133	// classes added by desugaring, we artifically raise the "SDK version" "linked" by
134	// ProGuard, to
135	// - suppress ProGuard warnings of referencing symbols unknown to the lower SDK version.
136	// - prevent ProGuard stripping subclass in the support library that extends class added in the higher SDK version.
137	// See b/20667396
138	var proguardRaiseDeps classpath
139	ctx.VisitDirectDepsWithTag(proguardRaiseTag, func(dep android.Module) {
140		proguardRaiseDeps = append(proguardRaiseDeps, dep.(Dependency).HeaderJars()...)
141	})
142
143	r8Flags = append(r8Flags, j.dexCommonFlags(ctx)...)
144
145	r8Flags = append(r8Flags, proguardRaiseDeps.FormJavaClassPath("-libraryjars"))
146	r8Flags = append(r8Flags, flags.bootClasspath.FormJavaClassPath("-libraryjars"))
147	r8Flags = append(r8Flags, flags.classpath.FormJavaClassPath("-libraryjars"))
148
149	r8Deps = append(r8Deps, proguardRaiseDeps...)
150	r8Deps = append(r8Deps, flags.bootClasspath...)
151	r8Deps = append(r8Deps, flags.classpath...)
152
153	flagFiles := android.Paths{
154		android.PathForSource(ctx, "build/make/core/proguard.flags"),
155	}
156
157	if j.shouldInstrumentStatic(ctx) {
158		flagFiles = append(flagFiles,
159			android.PathForSource(ctx, "build/make/core/proguard.jacoco.flags"))
160	}
161
162	flagFiles = append(flagFiles, j.extraProguardFlagFiles...)
163	// TODO(ccross): static android library proguard files
164
165	flagFiles = append(flagFiles, android.PathsForModuleSrc(ctx, j.deviceProperties.Optimize.Proguard_flags_files)...)
166
167	r8Flags = append(r8Flags, android.JoinWithPrefix(flagFiles.Strings(), "-include "))
168	r8Deps = append(r8Deps, flagFiles...)
169
170	// TODO(b/70942988): This is included from build/make/core/proguard.flags
171	r8Deps = append(r8Deps, android.PathForSource(ctx,
172		"build/make/core/proguard_basic_keeps.flags"))
173
174	r8Flags = append(r8Flags, j.deviceProperties.Optimize.Proguard_flags...)
175
176	// TODO(ccross): Don't shrink app instrumentation tests by default.
177	if !Bool(opt.Shrink) {
178		r8Flags = append(r8Flags, "-dontshrink")
179	}
180
181	if !Bool(opt.Optimize) {
182		r8Flags = append(r8Flags, "-dontoptimize")
183	}
184
185	// TODO(ccross): error if obufscation + app instrumentation test.
186	if !Bool(opt.Obfuscate) {
187		r8Flags = append(r8Flags, "-dontobfuscate")
188	}
189	// TODO(ccross): if this is an instrumentation test of an obfuscated app, use the
190	// dictionary of the app and move the app from libraryjars to injars.
191
192	// Don't strip out debug information for eng builds.
193	if ctx.Config().Eng() {
194		r8Flags = append(r8Flags, "--debug")
195	}
196
197	return r8Flags, r8Deps
198}
199
200func (j *Module) compileDex(ctx android.ModuleContext, flags javaBuilderFlags,
201	classesJar android.Path, jarName string) android.ModuleOutPath {
202
203	useR8 := j.deviceProperties.EffectiveOptimizeEnabled()
204
205	// Compile classes.jar into classes.dex and then javalib.jar
206	javalibJar := android.PathForModuleOut(ctx, "dex", jarName)
207	outDir := android.PathForModuleOut(ctx, "dex")
208
209	zipFlags := "--ignore_missing_files"
210	if proptools.Bool(j.deviceProperties.Uncompress_dex) {
211		zipFlags += " -L 0"
212	}
213
214	if useR8 {
215		proguardDictionary := android.PathForModuleOut(ctx, "proguard_dictionary")
216		j.proguardDictionary = proguardDictionary
217		r8Flags, r8Deps := j.r8Flags(ctx, flags)
218		rule := r8
219		args := map[string]string{
220			"r8Flags":  strings.Join(r8Flags, " "),
221			"zipFlags": zipFlags,
222			"outDict":  j.proguardDictionary.String(),
223			"outDir":   outDir.String(),
224		}
225		if ctx.Config().IsEnvTrue("RBE_R8") {
226			rule = r8RE
227			args["implicits"] = strings.Join(r8Deps.Strings(), ",")
228		}
229		ctx.Build(pctx, android.BuildParams{
230			Rule:           rule,
231			Description:    "r8",
232			Output:         javalibJar,
233			ImplicitOutput: proguardDictionary,
234			Input:          classesJar,
235			Implicits:      r8Deps,
236			Args:           args,
237		})
238	} else {
239		d8Flags, d8Deps := j.d8Flags(ctx, flags)
240		rule := d8
241		if ctx.Config().IsEnvTrue("RBE_D8") {
242			rule = d8RE
243		}
244		ctx.Build(pctx, android.BuildParams{
245			Rule:        rule,
246			Description: "d8",
247			Output:      javalibJar,
248			Input:       classesJar,
249			Implicits:   d8Deps,
250			Args: map[string]string{
251				"d8Flags":  strings.Join(d8Flags, " "),
252				"zipFlags": zipFlags,
253				"outDir":   outDir.String(),
254			},
255		})
256	}
257	if proptools.Bool(j.deviceProperties.Uncompress_dex) {
258		alignedJavalibJar := android.PathForModuleOut(ctx, "aligned", jarName)
259		TransformZipAlign(ctx, alignedJavalibJar, javalibJar)
260		javalibJar = alignedJavalibJar
261	}
262
263	return javalibJar
264}
265