blob: d91646a02414a50644744269d37f81f563be0f58 [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;
import com.android.tools.r8.R8Command.Builder;
import com.android.tools.r8.TestBase.Backend;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.benchmarks.BenchmarkResults;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.base.Charsets;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Supplier;
// The type arguments R8Command, Builder is not relevant for running Proguard.
public class ProguardTestBuilder
extends TestShrinkerBuilder<
R8Command, Builder, ProguardTestCompileResult, ProguardTestRunResult, ProguardTestBuilder> {
// Version of Proguard to use.
private final ProguardVersion version;
// Ordered list of injar entries.
private List<Path> injars = new ArrayList<>();
// Ordered list of libraryjar entries.
private List<Path> libraryjars = new ArrayList<>();
// Proguard configuration file lines.
private List<String> config = new ArrayList<>();
// Additional Proguard configuration files.
private List<Path> proguardConfigFiles = new ArrayList<>();
private ProguardTestBuilder(
TestState state, ProguardVersion version, Builder builder, Backend backend) {
super(state, builder, backend);
this.version = version;
}
public static ProguardTestBuilder create(TestState state, ProguardVersion version) {
return new ProguardTestBuilder(state, version, R8Command.builder(), Backend.CF);
}
@Override
public boolean isProguardTestBuilder() {
return true;
}
@Override
ProguardTestBuilder self() {
return this;
}
@Override
ProguardTestCompileResult internalCompile(
Builder builder,
Consumer<InternalOptions> optionsConsumer,
Supplier<AndroidApp> app,
BenchmarkResults benchmarkResults)
throws CompilationFailedException {
assert benchmarkResults == null;
assert !libraryDesugaringTestConfiguration.isEnabled();
try {
Path proguardOutputFolder = getState().getNewTempFolder();
Path outJar = proguardOutputFolder.resolve("output.jar");
Path configFile = proguardOutputFolder.resolve("configuration.txt");
Path mapFile = proguardOutputFolder.resolve("mapping.txt");
FileUtils.writeTextFile(configFile, config);
List<String> command = new ArrayList<>();
command.add(version.getProguardScript().toString());
// Without -forceprocessing Proguard just checks the creation time on the in/out jars.
command.add("-forceprocessing");
for (Path injar : injars) {
command.add("-injars");
command.add(injar.toString());
}
for (Path libraryjar : libraryjars) {
command.add("-libraryjars");
command.add(libraryjar.toString());
}
if (libraryjars.isEmpty()) {
command.add("-libraryjars");
// TODO(sgjesse): Add support for running with Android Jar.
// command.add(ToolHelper.getAndroidJar(AndroidApiLevel.P).toString());
command.add(ToolHelper.getJava8RuntimeJar().toString());
}
command.add("-include");
command.add(configFile.toString());
for (Path proguardConfigFile : proguardConfigFiles) {
command.add("-include");
command.add(proguardConfigFile.toAbsolutePath().toString());
}
command.add("-outjar");
command.add(outJar.toString());
command.add("-printmapping");
command.add(mapFile.toString());
if (enableTreeShaking.isFalse()) {
command.add("-dontshrink");
}
if (enableMinification.isFalse()) {
command.add("-dontobfuscate");
}
ProcessBuilder pbuilder = new ProcessBuilder(command);
ProcessResult result = ToolHelper.runProcess(pbuilder, getStdoutForTesting());
if (result.exitCode != 0) {
throw new CompilationFailedException(result.toString());
}
String proguardMap =
Files.exists(mapFile) ? FileUtils.readTextFile(mapFile, Charsets.UTF_8) : "";
return new ProguardTestCompileResult(
result, getState(), outJar, getMinApiLevel(), proguardMap);
} catch (IOException e) {
throw new CompilationFailedException(e);
}
}
@Override
public ProguardTestBuilder addApplyMapping(String proguardMap) {
throw new Unimplemented("No support for adding mapfile content yet");
}
@Override
public ProguardTestBuilder addDataEntryResources(DataEntryResource... resources) {
throw new Unimplemented("No support for adding data entry resources");
}
@Override
public ProguardTestBuilder addProgramClasses(Collection<Class<?>> classes) {
// Adding a collection of classes will build a jar of exactly those classes so that no other
// classes are made available via a too broad classpath directory.
try {
Path out = getState().getNewTempFolder().resolve("out.jar");
TestBase.writeClassesToJar(out, classes);
injars.add(out);
return self();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public ProguardTestBuilder addProgramFiles(Collection<Path> files) {
List<Path> classfiles = new ArrayList<>(files.size());
for (Path file : files) {
if (FileUtils.isJarFile(file)) {
injars.add(file);
} else if (FileUtils.isClassFile(file)) {
classfiles.add(file);
} else {
throw new Unimplemented("No support for adding non-jar or non classfiles.");
}
}
if (!classfiles.isEmpty()) {
try {
Path out = getState().getNewTempFolder().resolve("out.jar");
TestBase.writeClassFilesToJar(out, classfiles);
injars.add(out);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return self();
}
@Override
public ProguardTestBuilder addProgramClassFileData(Collection<byte[]> classes) {
try {
Path out = getState().getNewTempFolder().resolve("out.jar");
TestBase.writeClassFileDataToJar(out, classes);
injars.add(out);
} catch (IOException e) {
throw new RuntimeException(e);
}
return self();
}
@Override
public ProguardTestBuilder addProgramDexFileData(Collection<byte[]> data) {
throw new Unimplemented("No support for adding dex file data directly");
}
@Override
public ProguardTestBuilder addKeepRuleFiles(List<Path> proguardConfigFiles) {
this.proguardConfigFiles.addAll(proguardConfigFiles);
return self();
}
@Override
public ProguardTestBuilder addKeepRules(Collection<String> rules) {
config.addAll(rules);
return self();
}
@Override
public ProguardTestBuilder addLibraryFiles(Collection<Path> files) {
for (Path file : files) {
if (FileUtils.isJarFile(file)) {
libraryjars.add(file);
} else {
throw new Unimplemented(
"No support for adding class files directly (we need to compute the descriptor)");
}
}
return self();
}
@Override
public ProguardTestBuilder addLibraryClasses(Class<?>... classes) {
addLibraryClasses(Arrays.asList(classes));
return self();
}
@Override
public ProguardTestBuilder addLibraryClasses(Collection<Class<?>> classes) {
List<Path> pathsForClasses = new ArrayList<>(classes.size());
for (Class<?> clazz : classes) {
pathsForClasses.add(ToolHelper.getClassFileForTestClass(clazz));
}
try {
Path out = getState().getNewTempFolder().resolve("out.jar");
TestBase.writeClassFilesToJar(out, pathsForClasses);
libraryjars.add(out);
} catch (IOException e) {
throw new RuntimeException(e);
}
return self();
}
@Override
public ProguardTestBuilder addClasspathClasses(Collection<Class<?>> classes) {
throw new Unimplemented("No support for adding classpath data directly");
}
@Override
public ProguardTestBuilder addClasspathFiles(Collection<Path> files) {
throw new Unimplemented("No support for adding classpath data directly");
}
@Override
public ProguardTestBuilder setProgramConsumer(ProgramConsumer programConsumer) {
throw new Unimplemented("No support for program consumer");
}
@Override
public ProguardTestBuilder setMinApi(int minApiLevel) {
if (backend == Backend.DEX) {
throw new Unimplemented("No support for setting min api");
}
return self();
}
@Override
public ProguardTestBuilder addMainDexListFiles(Collection<Path> files) {
throw new Unimplemented("No support for adding main dex list files");
}
@Override
public ProguardTestBuilder setMainDexListConsumer(StringConsumer consumer) {
throw new Unimplemented("No support for main dex list consumer");
}
@Override
public ProguardTestBuilder setMode(CompilationMode mode) {
throw new Unimplemented("No support for setting compilation mode");
}
@Override
public ProguardTestBuilder disableDesugaring() {
throw new Unimplemented("No support for disabling desugaring");
}
@Override
public ProguardTestBuilder addOptionsModification(Consumer<InternalOptions> optionsConsumer) {
throw new Unimplemented("No support for changing internal options");
}
}