blob: d8afbb332100dce3d183d8be0315fc4d12914845 [file] [log] [blame]
// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking.forceproguardcompatibility;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.fail;
import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.CompatProguardCommandBuilder;
import com.android.tools.r8.DataResource;
import com.android.tools.r8.DexIndexedConsumer;
import com.android.tools.r8.R8Command;
import com.android.tools.r8.StringConsumer;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
import java.io.File;
import java.nio.file.Path;
import java.util.List;
import java.util.function.Consumer;
public class ProguardCompatibilityTestBase extends TestBase {
protected Path proguardMap;
public enum Shrinker {
PROGUARD5,
PROGUARD6,
PROGUARD6_THEN_D8,
R8_COMPAT,
R8_COMPAT_CF,
R8,
R8_CF;
public boolean isR8() {
return this == R8_COMPAT
|| this == R8_COMPAT_CF
|| this == R8
|| this == R8_CF;
}
public boolean isProguard() {
return this == PROGUARD5 || this == PROGUARD6 || this == PROGUARD6_THEN_D8;
}
public boolean isFullModeR8() {
return this == R8
|| this == R8_CF;
}
public boolean generatesDex() {
return this == PROGUARD6_THEN_D8
|| this == R8_COMPAT
|| this == R8;
}
public boolean generatesCf() {
return this == PROGUARD5
|| this == PROGUARD6
|| this == R8_COMPAT_CF
|| this == R8_CF;
}
public Backend toBackend() {
if (generatesDex()) {
return Backend.DEX;
}
assert generatesCf();
return Backend.CF;
}
}
protected AndroidApp runShrinker(
Shrinker mode, List<Class<?>> programClasses, Iterable<String> proguardConfigs)
throws Exception {
return runShrinker(
mode, programClasses, String.join(System.lineSeparator(), proguardConfigs), null);
}
protected AndroidApp runShrinker(
Shrinker mode,
List<Class<?>> programClasses,
Iterable<String> proguardConfigs,
Consumer<InternalOptions> configure)
throws Exception {
return runShrinker(
mode, programClasses, String.join(System.lineSeparator(), proguardConfigs), configure);
}
protected AndroidApp runShrinker(
Shrinker mode,
List<Class<?>> programClasses,
String proguardConfig,
Consumer<InternalOptions> configure)
throws Exception {
proguardMap = File.createTempFile("proguard", ".map", temp.getRoot()).toPath();
switch (mode) {
case PROGUARD5:
return runProguard5(programClasses, proguardConfig, proguardMap);
case PROGUARD6:
return runProguard6(programClasses, proguardConfig, proguardMap);
case PROGUARD6_THEN_D8:
return runProguard6AndD8(programClasses, proguardConfig, proguardMap, configure);
case R8_COMPAT:
return runR8Compat(programClasses, proguardConfig, proguardMap, configure, Backend.DEX);
case R8_COMPAT_CF:
return runR8Compat(programClasses, proguardConfig, proguardMap, configure, Backend.CF);
case R8:
return runR8(programClasses, proguardConfig, proguardMap, configure, Backend.DEX);
case R8_CF:
return runR8(programClasses, proguardConfig, proguardMap, configure, Backend.CF);
}
throw new IllegalArgumentException("Unknown shrinker: " + mode);
}
protected CodeInspector inspectAfterShrinking(
Shrinker mode, List<Class<?>> programClasses, List<String> proguardConfigs) throws Exception {
return inspectAfterShrinking(mode, programClasses, proguardConfigs, null);
}
protected CodeInspector inspectAfterShrinking(
Shrinker mode,
List<Class<?>> programClasses,
List<String> proguardConfigs,
Consumer<InternalOptions> configure)
throws Exception {
return inspectAfterShrinking(
mode, programClasses, String.join(System.lineSeparator(), proguardConfigs), configure);
}
protected CodeInspector inspectAfterShrinking(
Shrinker mode,
List<Class<?>> programClasses,
String proguardConfig,
Consumer<InternalOptions> configure)
throws Exception {
switch (mode) {
case PROGUARD5:
return inspectProguard5Result(programClasses, proguardConfig);
case PROGUARD6:
return inspectProguard6Result(programClasses, proguardConfig);
case PROGUARD6_THEN_D8:
return inspectProguard6AndD8Result(programClasses, proguardConfig, configure);
case R8_COMPAT:
return inspectR8CompatResult(programClasses, proguardConfig, configure, Backend.DEX);
case R8_COMPAT_CF:
return inspectR8CompatResult(programClasses, proguardConfig, configure, Backend.CF);
case R8:
return inspectR8Result(programClasses, proguardConfig, configure, Backend.DEX);
case R8_CF:
return inspectR8Result(programClasses, proguardConfig, configure, Backend.CF);
}
throw new IllegalArgumentException("Unknown shrinker: " + mode);
}
protected AndroidApp runR8(
List<Class<?>> programClasses, String proguardConfig, Path proguardMap, Backend backend)
throws Exception {
return runR8(programClasses, proguardConfig, proguardMap, null, backend);
}
protected AndroidApp runR8(
List<Class<?>> programClasses,
String proguardConfig,
Path proguardMap,
Consumer<InternalOptions> configure,
Backend backend)
throws Exception {
AndroidApp app = readClassesAndRuntimeJar(programClasses, backend);
R8Command.Builder builder = ToolHelper.prepareR8CommandBuilder(app, emptyConsumer(backend));
ToolHelper.allowTestProguardOptions(builder);
builder.addProguardConfiguration(
ImmutableList.of(proguardConfig, toPrintMappingRule(proguardMap)), Origin.unknown());
return ToolHelper.runR8(builder.build(), configure);
}
protected CodeInspector inspectR8Result(
List<Class<?>> programClasses,
String proguardConfig,
Consumer<InternalOptions> configure,
Backend backend)
throws Exception {
return new CodeInspector(runR8(programClasses, proguardConfig, null, configure, backend));
}
protected AndroidApp runR8Compat(
List<Class<?>> programClasses,
String proguardConfig,
Path proguardMap,
Consumer<InternalOptions> configure,
Backend backend)
throws Exception {
CompatProguardCommandBuilder builder = new CompatProguardCommandBuilder(true);
builder.setProguardMapConsumer(StringConsumer.emptyConsumer());
ToolHelper.allowTestProguardOptions(builder);
builder.addProguardConfiguration(
ImmutableList.of(proguardConfig, toPrintMappingRule(proguardMap)), Origin.unknown());
programClasses.forEach(
clazz -> builder.addProgramFiles(ToolHelper.getClassFileForTestClass(clazz)));
if (backend == Backend.DEX) {
builder.addLibraryFiles(ToolHelper.getAndroidJar(ToolHelper.getMinApiLevelForDexVm()));
builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
} else {
assert backend == Backend.CF;
builder.addLibraryFiles(ToolHelper.getJava8RuntimeJar());
builder.setProgramConsumer(ClassFileConsumer.emptyConsumer());
}
return ToolHelper.runR8(builder.build(), configure);
}
protected CodeInspector inspectR8CompatResult(
List<Class<?>> programClasses,
String proguardConfig,
Consumer<InternalOptions> configure,
Backend backend)
throws Exception {
return new CodeInspector(runR8Compat(programClasses, proguardConfig, null, configure, backend));
}
protected AndroidApp runProguard5(
List<Class<?>> programClasses, String proguardConfig, Path proguardMap) throws Exception {
Path proguardedJar =
File.createTempFile("proguarded", FileUtils.JAR_EXTENSION, temp.getRoot()).toPath();
Path proguardConfigFile = File.createTempFile("proguard", ".config", temp.getRoot()).toPath();
FileUtils.writeTextFile(proguardConfigFile, proguardConfig);
ProcessResult result = ToolHelper.runProguardRaw(
jarTestClasses(programClasses),
proguardedJar,
ToolHelper.getAndroidJar(AndroidApiLevel.N),
proguardConfigFile,
proguardMap);
if (result.exitCode != 0) {
fail("Proguard failed, exit code " + result.exitCode + ", stderr:\n" + result.stderr);
}
return readJar(proguardedJar);
}
protected CodeInspector inspectProguard5Result(
List<Class<?>> programClasses, String proguardConfig) throws Exception {
proguardMap = File.createTempFile("proguard", ".map", temp.getRoot()).toPath();
return new CodeInspector(
runProguard5(programClasses, proguardConfig, proguardMap), proguardMap);
}
protected ProcessResult runProguard6Raw(
Path destination,
List<Class<?>> programClasses,
String proguardConfig,
Path proguardMap,
List<DataResource> dataResources)
throws Exception {
return runProguard6Raw(
destination,
jarTestClasses(programClasses, dataResources),
proguardConfig,
proguardMap,
null);
}
protected ProcessResult runProguard6Raw(
Path destination, Path jar, String proguardConfig, Path proguardMap) throws Exception {
return runProguard6Raw(destination, jar, proguardConfig, proguardMap, null);
}
protected ProcessResult runProguard6Raw(
Path destination,
Path jar,
String proguardConfig,
Path proguardMap,
Consumer<ProcessResult> processResultConsumer)
throws Exception {
Path proguardConfigFile = File.createTempFile("proguard", ".config", temp.getRoot()).toPath();
FileUtils.writeTextFile(proguardConfigFile, proguardConfig);
ProcessResult result =
ToolHelper.runProguard6Raw(
jar,
destination,
ToolHelper.getAndroidJar(AndroidApiLevel.N),
proguardConfigFile,
proguardMap);
if (result.exitCode != 0) {
fail("Proguard failed, exit code " + result.exitCode + ", stderr:\n" + result.stderr);
}
if (processResultConsumer != null) {
processResultConsumer.accept(result);
}
return result;
}
protected AndroidApp runProguard6(
List<Class<?>> programClasses, String proguardConfig, Path proguardMap) throws Exception {
return runProguard6(programClasses, proguardConfig, proguardMap, null);
}
protected AndroidApp runProguard6(
List<Class<?>> programClasses,
String proguardConfig,
Path proguardMap,
List<DataResource> dataResources)
throws Exception {
Path proguardedJar =
File.createTempFile("proguarded", FileUtils.JAR_EXTENSION, temp.getRoot()).toPath();
runProguard6Raw(proguardedJar, programClasses, proguardConfig, proguardMap, dataResources);
return readJar(proguardedJar);
}
protected CodeInspector inspectProguard6Result(
List<Class<?>> programClasses, String proguardConfig) throws Exception {
proguardMap = File.createTempFile("proguard", ".map", temp.getRoot()).toPath();
return new CodeInspector(
runProguard6(programClasses, proguardConfig, proguardMap), proguardMap);
}
protected AndroidApp runProguard6AndD8(
List<Class<?>> programClasses,
String proguardConfig,
Path proguardMap,
Consumer<InternalOptions> configure)
throws Exception {
Path proguardedJar =
File.createTempFile("proguarded", FileUtils.JAR_EXTENSION, temp.getRoot()).toPath();
Path proguardConfigFile = File.createTempFile("proguard", ".config", temp.getRoot()).toPath();
FileUtils.writeTextFile(proguardConfigFile, proguardConfig);
ProcessResult result = ToolHelper.runProguard6Raw(
jarTestClasses(programClasses),
proguardedJar,
ToolHelper.getAndroidJar(AndroidApiLevel.N),
proguardConfigFile,
proguardMap);
if (result.exitCode != 0) {
fail("Proguard failed, exit code " + result.exitCode + ", stderr:\n" + result.stderr);
}
return ToolHelper.runD8(readJar(proguardedJar), configure);
}
protected CodeInspector inspectProguard6AndD8Result(
List<Class<?>> programClasses, String proguardConfig, Consumer<InternalOptions> configure)
throws Exception {
proguardMap = File.createTempFile("proguard", ".map", temp.getRoot()).toPath();
return new CodeInspector(
runProguard6AndD8(programClasses, proguardConfig, proguardMap, configure), proguardMap);
}
protected void verifyClassesPresent(
CodeInspector codeInspector, Class<?>... classesOfInterest) {
for (Class klass : classesOfInterest) {
ClassSubject c = codeInspector.clazz(klass);
assertThat(c, isPresent());
}
}
protected void verifyClassesAbsent(
CodeInspector codeInspector, Class<?>... classesOfInterest) {
for (Class klass : classesOfInterest) {
ClassSubject c = codeInspector.clazz(klass);
assertThat(c, not(isPresent()));
}
}
private String toPrintMappingRule(Path proguardMap) {
return proguardMap == null ? "" : "-printmapping " + proguardMap.toAbsolutePath();
}
}