| // Copyright (c) 2017, 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.compatproguard; |
| |
| import com.android.tools.r8.CompatProguardCommandBuilder; |
| import com.android.tools.r8.CompilationFailedException; |
| import com.android.tools.r8.CompilationMode; |
| import com.android.tools.r8.DiagnosticsHandler; |
| import com.android.tools.r8.MapIdProvider; |
| import com.android.tools.r8.OutputMode; |
| import com.android.tools.r8.R8; |
| import com.android.tools.r8.SourceFileProvider; |
| import com.android.tools.r8.Version; |
| import com.android.tools.r8.errors.CompilationError; |
| import com.android.tools.r8.origin.CommandLineOrigin; |
| import com.android.tools.r8.utils.ExceptionUtils; |
| import com.android.tools.r8.utils.FileUtils; |
| import com.android.tools.r8.utils.MapIdTemplateProvider; |
| import com.android.tools.r8.utils.SourceFileTemplateProvider; |
| import com.google.common.collect.ImmutableList; |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| import java.util.List; |
| |
| /** |
| * Proguard + dx compatibility interface for r8. |
| * |
| * This should become a mostly drop-in replacement for uses of Proguard followed by dx for |
| * use with the Android Platform build. |
| * |
| * It accepts all Proguard flags supported by r8, except -outjars. |
| * |
| * It accepts a few dx flags which are known to be used in the Android Platform build. |
| * |
| * The flag -outjars does not make sense as r8 (like Proguard + dx) produces Dex output. |
| * For output use --output as for R8 proper. |
| */ |
| public class CompatProguard { |
| public static class CompatProguardOptions { |
| public final String output; |
| CompilationMode mode; |
| public final int minApi; |
| public final boolean forceProguardCompatibility; |
| public final boolean includeDataResources; |
| public final boolean multiDex; |
| public final String mainDexList; |
| public final MapIdProvider mapIdProvider; |
| public final SourceFileProvider sourceFileProvider; |
| public final String depsFileOutput; |
| public final List<String> proguardConfig; |
| public boolean printHelpAndExit; |
| |
| // Flags to disable experimental features. |
| public boolean disableVerticalClassMerging; |
| |
| CompatProguardOptions( |
| List<String> proguardConfig, |
| String output, |
| CompilationMode mode, |
| int minApi, |
| boolean multiDex, |
| boolean forceProguardCompatibility, |
| boolean includeDataResources, |
| String mainDexList, |
| MapIdProvider mapIdProvider, |
| SourceFileProvider sourceFileProvider, |
| String depsFileOutput, |
| boolean printHelpAndExit, |
| boolean disableVerticalClassMerging) { |
| this.output = output; |
| this.mode = mode; |
| this.minApi = minApi; |
| this.forceProguardCompatibility = forceProguardCompatibility; |
| this.includeDataResources = includeDataResources; |
| this.multiDex = multiDex; |
| this.mainDexList = mainDexList; |
| this.proguardConfig = proguardConfig; |
| this.mapIdProvider = mapIdProvider; |
| this.sourceFileProvider = sourceFileProvider; |
| this.depsFileOutput = depsFileOutput; |
| this.printHelpAndExit = printHelpAndExit; |
| this.disableVerticalClassMerging = disableVerticalClassMerging; |
| } |
| |
| public static CompatProguardOptions parse(String[] args) { |
| DiagnosticsHandler handler = new DiagnosticsHandler() {}; |
| String output = null; |
| CompilationMode mode = null; |
| int minApi = 1; |
| boolean forceProguardCompatibility = false; |
| boolean includeDataResources = true; |
| boolean multiDex = false; |
| String mainDexList = null; |
| boolean printHelpAndExit = false; |
| MapIdProvider mapIdProvider = null; |
| SourceFileProvider sourceFileProvider = null; |
| String depsFileOutput = null; |
| // Flags to disable experimental features. |
| boolean disableVerticalClassMerging = false; |
| // These flags are currently ignored. |
| boolean minimalMainDex = false; |
| boolean coreLibrary = false; |
| boolean noLocals = false; |
| |
| ImmutableList.Builder<String> builder = ImmutableList.builder(); |
| if (args.length > 0) { |
| StringBuilder currentLine = new StringBuilder(); |
| for (int i = 0; i < args.length; i++) { |
| String arg = args[i]; |
| if (arg.charAt(0) == '-') { |
| if (arg.equals("-h") || arg.equals("--help")) { |
| printHelpAndExit = true; |
| } else if (arg.equals("--debug")) { |
| if (mode == CompilationMode.RELEASE) { |
| throw new CompilationError("Cannot compile in both --debug and --release mode."); |
| } |
| mode = CompilationMode.DEBUG; |
| } else if (arg.equals("--release")) { |
| if (mode == CompilationMode.DEBUG) { |
| throw new CompilationError("Cannot compile in both --debug and --release mode."); |
| } |
| mode = CompilationMode.RELEASE; |
| } else if (arg.equals("--min-api")) { |
| minApi = Integer.valueOf(args[++i]); |
| } else if (arg.equals("--force-proguard-compatibility")) { |
| forceProguardCompatibility = true; |
| } else if (arg.equals("--no-data-resources")) { |
| includeDataResources = false; |
| } else if (arg.equals("--output")) { |
| output = args[++i]; |
| } else if (arg.equals("--multi-dex")) { |
| multiDex = true; |
| } else if (arg.equals("--main-dex-list")) { |
| mainDexList = args[++i]; |
| } else if (arg.startsWith("--main-dex-list=")) { |
| mainDexList = arg.substring("--main-dex-list=".length()); |
| } else if (arg.equals("--map-id-template")) { |
| mapIdProvider = MapIdTemplateProvider.create(args[++i], handler); |
| } else if (arg.equals("--source-file-template")) { |
| sourceFileProvider = SourceFileTemplateProvider.create(args[++i], handler); |
| } else if (arg.equals("--deps-file")) { |
| depsFileOutput = args[++i]; |
| } else if (arg.equals("--no-vertical-class-merging")) { |
| disableVerticalClassMerging = true; |
| } else if (arg.equals("--minimal-main-dex")) { |
| minimalMainDex = true; |
| } else if (arg.equals("--core-library")) { |
| coreLibrary = true; |
| } else if (arg.equals("--no-locals")) { |
| noLocals = true; |
| } else if (arg.equals("-outjars")) { |
| throw new CompilationError( |
| "Proguard argument -outjar is not supported. Use R8 compatible --output flag"); |
| } else { |
| if (currentLine.length() > 0) { |
| builder.add(currentLine.toString()); |
| } |
| currentLine = new StringBuilder(arg); |
| } |
| } else { |
| if (currentLine.length() > 0) { |
| currentLine.append(' '); |
| } |
| currentLine.append(arg); |
| } |
| } |
| if (currentLine.length() > 0) { |
| builder.add(currentLine.toString()); |
| } |
| } |
| return new CompatProguardOptions( |
| builder.build(), |
| output, |
| mode, |
| minApi, |
| multiDex, |
| forceProguardCompatibility, |
| includeDataResources, |
| mainDexList, |
| mapIdProvider, |
| sourceFileProvider, |
| depsFileOutput, |
| printHelpAndExit, |
| disableVerticalClassMerging); |
| } |
| |
| public static void print() { |
| System.out.println("-h/--help : print this help message"); |
| System.out.println("--release : compile without debugging information (default)."); |
| System.out.println("--debug : compile with debugging information."); |
| System.out.println("--min-api n : specify the targeted min android api level"); |
| System.out.println("--main-dex-list list : specify main dex list for multi-dexing"); |
| System.out.println("--minimal-main-dex : ignored (provided for compatibility)"); |
| System.out.println("--multi-dex : ignored (provided for compatibility)"); |
| System.out.println("--no-locals : ignored (provided for compatibility)"); |
| System.out.println("--core-library : ignored (provided for compatibility)"); |
| System.out.println("--force-proguard-compatibility : Proguard compatibility mode"); |
| System.out.println("--no-data-resources : ignore all data resources"); |
| } |
| } |
| |
| private static void printVersion() { |
| System.out.println("CompatProguard " + Version.getVersionString()); |
| } |
| |
| private static void printHelp() { |
| printVersion(); |
| System.out.println(); |
| System.out.println("compatproguard [options] --output <dir> <proguard-config>*"); |
| System.out.println(); |
| System.out.println("Where options are:"); |
| CompatProguardOptions.print(); |
| } |
| |
| private static void run(String[] args) throws CompilationFailedException { |
| // Run R8 passing all the options from the command line as a Proguard configuration. |
| CompatProguardOptions options = CompatProguardOptions.parse(args); |
| if (options.printHelpAndExit || options.output == null) { |
| System.out.println(); |
| printHelp(); |
| return; |
| } |
| CompatProguardCommandBuilder builder = |
| new CompatProguardCommandBuilder( |
| options.forceProguardCompatibility, options.disableVerticalClassMerging); |
| builder |
| .setOutput(Paths.get(options.output), OutputMode.DexIndexed, options.includeDataResources) |
| .addProguardConfiguration(options.proguardConfig, CommandLineOrigin.INSTANCE) |
| .setMinApiLevel(options.minApi) |
| .setMapIdProvider(options.mapIdProvider) |
| .setSourceFileProvider(options.sourceFileProvider); |
| if (options.mode != null) { |
| builder.setMode(options.mode); |
| } |
| if (options.mainDexList != null) { |
| builder.addMainDexListFiles(Paths.get(options.mainDexList)); |
| } |
| if (options.depsFileOutput != null) { |
| Path target = Paths.get(options.output); |
| if (!FileUtils.isArchive(target)) { |
| target = target.resolve("classes.dex"); |
| } |
| builder.setInputDependencyGraphConsumer(new DepsFileWriter(target, options.depsFileOutput)); |
| } |
| R8.run(builder.build()); |
| } |
| |
| public static void main(String[] args) { |
| ExceptionUtils.withMainProgramHandler(() -> run(args)); |
| } |
| } |