blob: 2808f05bf38b5fb2c7ef47260f5eb75aecf74f8f [file] [log] [blame]
// Copyright (c) 2019, 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 static com.android.tools.r8.ToolHelper.CLASSPATH_SEPARATOR;
import static org.junit.Assert.assertEquals;
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.DescriptorUtils;
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.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;
// The type arguments R8Command, Builder is not relevant for running external R8.
public class ExternalR8TestBuilder
extends TestShrinkerBuilder<
R8Command,
Builder,
ExternalR8TestCompileResult,
ExternalR8TestRunResult,
ExternalR8TestBuilder> {
// The r8.jar to run.
private Path r8jar = ToolHelper.R8_JAR;
// Ordered list of program jar entries.
private final List<Path> programJars = new ArrayList<>();
// Ordered list of library jar entries.
private final List<Path> libJars = new ArrayList<>();
// Proguard configuration file lines.
private final List<String> config = new ArrayList<>();
// Additional Proguard configuration files.
private List<Path> proguardConfigFiles = new ArrayList<>();
// External JDK to use to run R8
private final TestRuntime runtime;
// Enable/disable compiling with assertions
private boolean enableAssertions = true;
// Allow test proguard options
private boolean allowTestProguardOptions = false;
private String dumpInputToFile = null;
private String dumpInputToDirectory = null;
private boolean addR8ExternalDeps = false;
private List<String> jvmFlags = new ArrayList<>();
private ExternalR8TestBuilder(
TestState state, Builder builder, Backend backend, TestRuntime runtime) {
super(state, builder, backend);
assert runtime != null;
this.runtime = runtime;
}
public static ExternalR8TestBuilder create(
TestState state, Backend backend, TestRuntime runtime) {
return new ExternalR8TestBuilder(state, R8Command.builder(), backend, runtime);
}
@Override
ExternalR8TestBuilder self() {
return this;
}
public ExternalR8TestBuilder addJvmFlag(String flag) {
jvmFlags.add(flag);
return self();
}
public ExternalR8TestBuilder enableAssertions(boolean enable) {
enableAssertions = enable;
return self();
}
public ExternalR8TestBuilder allowTestProguardOptions(boolean allow) {
allowTestProguardOptions = allow;
return self();
}
public ExternalR8TestBuilder dumpInputToFile(String arg) {
dumpInputToFile = arg;
return self();
}
public ExternalR8TestBuilder dumpInputToDirectory(String arg) {
dumpInputToDirectory = arg;
return self();
}
@Override
ExternalR8TestCompileResult internalCompile(
Builder builder,
Consumer<InternalOptions> optionsConsumer,
Supplier<AndroidApp> app,
BenchmarkResults benchmarkResults)
throws CompilationFailedException {
assert benchmarkResults == null;
assert !libraryDesugaringTestConfiguration.isEnabled();
try {
Path outputFolder = getState().getNewTempFolder();
Path outputJar = outputFolder.resolve("output.jar");
Path proguardMapFile = outputFolder.resolve("output.jar.map");
String classPath =
addR8ExternalDeps
? r8jar.toAbsolutePath().toString() + CLASSPATH_SEPARATOR + ToolHelper.DEPS
: r8jar.toAbsolutePath().toString();
List<String> command = new ArrayList<>();
if (runtime.isDex()) {
throw new Unimplemented();
}
Collections.addAll(command, runtime.asCf().getJavaExecutable().toString());
command.addAll(jvmFlags);
if (enableAssertions) {
command.add("-ea");
}
if (allowTestProguardOptions) {
command.add("-Dcom.android.tools.r8.allowTestProguardOptions=true");
}
Collections.addAll(
command,
"-cp",
classPath,
R8.class.getTypeName(),
"--output",
outputJar.toAbsolutePath().toString(),
"--pg-map-output",
proguardMapFile.toString(),
backend == Backend.CF ? "--classfile" : "--dex",
builder.getMode() == CompilationMode.DEBUG ? "--debug" : "--release");
if (!config.isEmpty()) {
Path proguardConfigFile = outputFolder.resolve("proguard-config.txt");
FileUtils.writeTextFile(proguardConfigFile, config);
command.add("--pg-conf");
command.add(proguardConfigFile.toAbsolutePath().toString());
}
for (Path proguardConfigFile : proguardConfigFiles) {
command.add("--pg-conf");
command.add(proguardConfigFile.toAbsolutePath().toString());
}
if (libJars.isEmpty()) {
command.add("--lib");
command.add(TestBase.runtimeJar(backend).toAbsolutePath().toString());
} else {
for (Path libJar : libJars) {
command.add("--lib");
command.add(libJar.toAbsolutePath().toString());
}
}
if (dumpInputToFile != null) {
command.add("--dumpinputtofile");
command.add(dumpInputToFile);
}
if (dumpInputToDirectory != null) {
command.add("--dumpinputtodirectory");
command.add(dumpInputToDirectory);
}
command.addAll(programJars.stream().map(Path::toString).collect(Collectors.toList()));
ProcessBuilder processBuilder = new ProcessBuilder(command);
ProcessResult processResult = ToolHelper.runProcess(processBuilder, getStdoutForTesting());
assertEquals(
"STDOUT\n:" + processResult.stdout + "\nSTDERR:\n" + processResult.stderr,
0,
processResult.exitCode);
String proguardMap =
proguardMapFile.toFile().exists()
? FileUtils.readTextFile(proguardMapFile, Charsets.UTF_8)
: "";
return new ExternalR8TestCompileResult(
getState(), outputJar, processResult, proguardMap, getMinApiLevel(), getOutputMode());
} catch (IOException e) {
throw new CompilationFailedException(e);
}
}
@Override
public ExternalR8TestBuilder addApplyMapping(String proguardMap) {
throw new Unimplemented("No support for adding mapfile content yet");
}
@Override
public ExternalR8TestBuilder addDataEntryResources(DataEntryResource... resources) {
throw new Unimplemented("No support for adding data entry resources");
}
@Override
public ExternalR8TestBuilder 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 programJar = getState().getNewTempFolder().resolve("input.jar");
ClassFileConsumer inputConsumer = new ClassFileConsumer.ArchiveConsumer(programJar);
for (Class<?> clazz : classes) {
String descriptor = DescriptorUtils.javaTypeToDescriptor(clazz.getName());
inputConsumer.accept(ByteDataView.of(ToolHelper.getClassAsBytes(clazz)), descriptor, null);
}
inputConsumer.finished(null);
programJars.add(programJar);
return self();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public ExternalR8TestBuilder addProgramFiles(Collection<Path> files) {
for (Path file : files) {
if (FileUtils.isJarFile(file)) {
programJars.add(file);
} else {
throw new Unimplemented("No support for adding paths directly");
}
}
return self();
}
@Override
public ExternalR8TestBuilder addProgramClassFileData(Collection<byte[]> classes) {
throw new Unimplemented("No support for adding classfile data directly");
}
@Override
public ExternalR8TestBuilder addProgramDexFileData(Collection<byte[]> data) {
throw new Unimplemented("No support for adding dex file data directly");
}
@Override
public ExternalR8TestBuilder addLibraryFiles(Collection<Path> files) {
libJars.addAll(files);
return self();
}
@Override
public ExternalR8TestBuilder addClasspathClasses(Collection<Class<?>> classes) {
throw new Unimplemented("No support for adding classpath data directly");
}
@Override
public ExternalR8TestBuilder addClasspathFiles(Collection<Path> files) {
throw new Unimplemented("No support for adding classpath data directly");
}
@Override
public ExternalR8TestBuilder addKeepRuleFiles(List<Path> proguardConfigFiles) {
this.proguardConfigFiles.addAll(proguardConfigFiles);
return self();
}
@Override
public ExternalR8TestBuilder addKeepRules(Collection<String> rules) {
config.addAll(rules);
return self();
}
public ExternalR8TestBuilder useR8WithRelocatedDeps() {
return useProvidedR8(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR);
}
public ExternalR8TestBuilder useProvidedR8(Path r8jar) {
this.r8jar = r8jar;
return self();
}
public ExternalR8TestBuilder addR8ExternalDepsToClasspath() {
this.addR8ExternalDeps = true;
return self();
}
}