|  | // 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.debug.DebugTestConfig; | 
|  | 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) | 
|  | throws CompilationFailedException { | 
|  | 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) { | 
|  | command.add("-dontshrink"); | 
|  | } | 
|  | if (!enableOptimization) { | 
|  | command.add("-dontoptimize"); | 
|  | } | 
|  | if (!enableMinification) { | 
|  | 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(getState(), outJar, minApiLevel, 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 DebugTestConfig debugConfig() { | 
|  | throw new Unimplemented("No support for debug config"); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public ProguardTestBuilder addOptionsModification(Consumer<InternalOptions> optionsConsumer) { | 
|  | throw new Unimplemented("No support for changing internal options"); | 
|  | } | 
|  | } |