| // 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"); |
| } |
| } |