| // 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.utils.AndroidApp; | |
| import com.android.tools.r8.utils.InternalOptions; | |
| import com.android.tools.r8.utils.UncheckedApiLevel; | |
| import com.android.tools.r8.utils.internal.FileUtils; | |
| import com.android.tools.r8.utils.internal.exceptions.Unimplemented; | |
| 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.Collections; | |
| 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, SingleTestRunResult, 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); | |
| } | |
| public List<String> getConfig() { | |
| return Collections.unmodifiableList(config); | |
| } | |
| @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(TestDeps.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()); | |
| ProcessBuilder pbuilder = new ProcessBuilder(command); | |
| ProcessResult result = ToolHelper.runProcess(pbuilder, getStdoutForTesting()); | |
| if (result.exitCode != 0) { | |
| throw InternalCompilationFailedExceptionUtils.createForTesting(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 InternalCompilationFailedExceptionUtils.createForTesting(e); | |
| } | |
| } | |
| @Override | |
| public ProguardTestBuilder addApplyMapping(String proguardMap) { | |
| throw new Unimplemented("No support for adding mapfile content yet"); | |
| } | |
| @Override | |
| public ProguardTestBuilder addDataResources(List<? extends DataResource> 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(UncheckedApiLevel 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"); | |
| } | |
| } |