diff options
author | Xavier Ducrohet <xav@google.com> | 2013-11-08 11:42:32 -0800 |
---|---|---|
committer | Xavier Ducrohet <xav@google.com> | 2013-11-08 18:48:17 -0800 |
commit | 042fb560b33ecfd6d183c7a61d346c2972873099 (patch) | |
tree | 446d8480484b606be0d5daacda11c3d3ae25e29a | |
parent | 2fd034b23391883949f190be1ee2bd0ad4d5afcf (diff) | |
download | build-042fb560b33ecfd6d183c7a61d346c2972873099.tar.gz |
Move to Proguard 4.10
Change-Id: I9410c371627faca9cb76df2b5306c37dc41dd2fb
7 files changed, 269 insertions, 66 deletions
diff --git a/builder/src/main/java/com/android/builder/AndroidBuilder.java b/builder/src/main/java/com/android/builder/AndroidBuilder.java index e6b78a3..9a6fa79 100644 --- a/builder/src/main/java/com/android/builder/AndroidBuilder.java +++ b/builder/src/main/java/com/android/builder/AndroidBuilder.java @@ -83,7 +83,7 @@ import static com.google.common.base.Preconditions.checkNotNull; * {@link #processTestManifest(String, int, int, String, String, Boolean, Boolean, java.util.List, String)} * {@link #processResources(java.io.File, java.io.File, java.io.File, java.util.List, String, String, String, String, String, com.android.builder.VariantConfiguration.Type, boolean, com.android.builder.model.AaptOptions)} * {@link #compileAllAidlFiles(java.util.List, java.io.File, java.util.List, com.android.builder.compiling.DependencyFileProcessor)} - * {@link #convertByteCode(Iterable, Iterable, File, String, DexOptions, boolean)} + * {@link #convertByteCode(Iterable, Iterable, File, DexOptions, boolean)} * {@link #packageApk(String, String, java.util.List, String, String, boolean, SigningConfig, String)} * * Java compilation is not handled but the builder provides the bootclasspath with @@ -926,8 +926,8 @@ public class AndroidBuilder { /** * Converts the bytecode to Dalvik format - * @param classesLocation the location of the compiler output - * @param libraries the list of libraries + * @param inputs the input files + * @param preDexedLibraries the list of pre-dexed libraries * @param outDexFile the location of the output classes.dex file * @param dexOptions dex options * @param incremental true if it should attempt incremental dex if applicable @@ -937,14 +937,13 @@ public class AndroidBuilder { * @throws LoggedErrorException */ public void convertByteCode( - @NonNull Iterable<File> classesLocation, - @NonNull Iterable<File> libraries, - @Nullable File proguardFile, - @NonNull String outDexFile, + @NonNull Iterable<File> inputs, + @NonNull Iterable<File> preDexedLibraries, + @NonNull File outDexFile, @NonNull DexOptions dexOptions, boolean incremental) throws IOException, InterruptedException, LoggedErrorException { - checkNotNull(classesLocation, "classesLocation cannot be null."); - checkNotNull(libraries, "libraries cannot be null."); + checkNotNull(inputs, "inputs cannot be null."); + checkNotNull(preDexedLibraries, "preDexedLibraries cannot be null."); checkNotNull(outDexFile, "outDexFile cannot be null."); checkNotNull(dexOptions, "dexOptions cannot be null."); @@ -972,45 +971,99 @@ public class AndroidBuilder { command.add("--core-library"); } + if (dexOptions.getJumboMode()) { + command.add("--force-jumbo"); + } + if (incremental) { command.add("--incremental"); command.add("--no-strict"); } command.add("--output"); - command.add(outDexFile); + command.add(outDexFile.getAbsolutePath()); - // clean up and add class inputs - List<String> classesList = Lists.newArrayList(); - for (File f : classesLocation) { + // clean up input list + List<String> inputList = Lists.newArrayList(); + for (File f : inputs) { if (f != null && f.exists()) { - classesList.add(f.getAbsolutePath()); + inputList.add(f.getAbsolutePath()); } } - if (!classesList.isEmpty()) { - mLogger.verbose("Dex class inputs: " + classesList); - command.addAll(classesList); + if (!inputList.isEmpty()) { + mLogger.verbose("Dex inputs: " + inputList); + command.addAll(inputList); } // clean up and add library inputs. List<String> libraryList = Lists.newArrayList(); - for (File f : libraries) { + for (File f : preDexedLibraries) { if (f != null && f.exists()) { libraryList.add(f.getAbsolutePath()); } } if (!libraryList.isEmpty()) { - mLogger.verbose("Dex library inputs: " + libraryList); + mLogger.verbose("Dex pre-dexed inputs: " + libraryList); command.addAll(libraryList); } - if (proguardFile != null && proguardFile.exists()) { - mLogger.verbose("ProGuarded inputs " + proguardFile); - command.add(proguardFile.getAbsolutePath()); + mCmdLineRunner.runCmdLine(command, null); + } + + /** + * Converts the bytecode to Dalvik format + * @param inputFile the input file + * @param outFile the location of the output classes.dex file + * @param dexOptions dex options + * + * @throws IOException + * @throws InterruptedException + * @throws LoggedErrorException + */ + public void preDexLibrary( + @NonNull File inputFile, + @NonNull File outFile, + @NonNull DexOptions dexOptions) + throws IOException, InterruptedException, LoggedErrorException { + checkNotNull(inputFile, "inputFile cannot be null."); + checkNotNull(outFile, "outFile cannot be null."); + checkNotNull(dexOptions, "dexOptions cannot be null."); + + // launch dx: create the command line + ArrayList<String> command = Lists.newArrayList(); + + String dx = mBuildTools.getPath(BuildToolInfo.PathId.DX); + if (dx == null || !new File(dx).isFile()) { + throw new IllegalStateException("dx is missing"); + } + + command.add(dx); + + if (dexOptions.getJavaMaxHeapSize() != null) { + command.add("-JXmx" + dexOptions.getJavaMaxHeapSize()); + } + + command.add("--dex"); + + if (mVerboseExec) { + command.add("--verbose"); + } + + if (dexOptions.isCoreLibrary()) { + command.add("--core-library"); } + if (dexOptions.getJumboMode()) { + command.add("--force-jumbo"); + } + + command.add("--output"); + command.add(outFile.getAbsolutePath()); + + command.add(inputFile.getAbsolutePath()); + mCmdLineRunner.runCmdLine(command, null); } diff --git a/builder/src/main/java/com/android/builder/DexOptions.java b/builder/src/main/java/com/android/builder/DexOptions.java index 966d081..1db13be 100644 --- a/builder/src/main/java/com/android/builder/DexOptions.java +++ b/builder/src/main/java/com/android/builder/DexOptions.java @@ -20,5 +20,7 @@ public interface DexOptions { boolean isCoreLibrary(); boolean getIncremental(); + boolean getPreDexLibraries(); + boolean getJumboMode(); String getJavaMaxHeapSize(); } diff --git a/gradle/build.gradle b/gradle/build.gradle index c1c22bc..f83bc7a 100644 --- a/gradle/build.gradle +++ b/gradle/build.gradle @@ -34,7 +34,7 @@ dependencies { compile "com.android.tools:common:$project.ext.baseAndroidVersion" compile "com.android.tools.lint:lint:$project.ext.baseAndroidVersion" - compile 'net.sf.proguard:proguard-gradle:4.9' + compile 'net.sf.proguard:proguard-gradle:4.10' testCompile 'junit:junit:3.8.1' @@ -57,7 +57,7 @@ configurations{ } dependencies{ - provided 'net.sf.proguard:proguard-gradle:4.9' + provided 'net.sf.proguard:proguard-gradle:4.10' } //Include provided for compilation diff --git a/gradle/src/main/groovy/com/android/build/gradle/BasePlugin.groovy b/gradle/src/main/groovy/com/android/build/gradle/BasePlugin.groovy index 6f8e9c6..36482e5 100644 --- a/gradle/src/main/groovy/com/android/build/gradle/BasePlugin.groovy +++ b/gradle/src/main/groovy/com/android/build/gradle/BasePlugin.groovy @@ -57,6 +57,7 @@ import com.android.build.gradle.tasks.Lint import com.android.build.gradle.tasks.MergeAssets import com.android.build.gradle.tasks.MergeResources import com.android.build.gradle.tasks.PackageApplication +import com.android.build.gradle.tasks.PreDex import com.android.build.gradle.tasks.ProcessAndroidResources import com.android.build.gradle.tasks.ProcessAppManifest import com.android.build.gradle.tasks.ProcessTestManifest @@ -1096,40 +1097,72 @@ public abstract class BasePlugin { VariantConfiguration variantConfig = variantData.variantConfiguration - Closure libraryClosure = { project.files(variantConfig.packagedJars) } - Closure sourceClosure = { variantData.javaCompileTask.outputs.files } - Closure proguardFileClosure = { } + boolean runProguard = !(variantData instanceof TestVariantData) && variantConfig.buildType.runProguard - if (!(variantData instanceof TestVariantData) && variantConfig.buildType.runProguard) { - libraryClosure = { Collections.emptyList() } - sourceClosure = { Collections.emptyList() } - - File outFile = createProguardTasks(variantData, variantConfig) - proguardFileClosure = { outFile } - } - - // Add a dex task - def dexTaskName = "dex${variantData.name}" - def dexTask = project.tasks.create(dexTaskName, Dex) + // common dex task configuration + String dexTaskName = "dex${variantData.name}" + Dex dexTask = project.tasks.create(dexTaskName, Dex) variantData.dexTask = dexTask - if (variantData.proguardTask != null) { - dexTask.dependsOn variantData.proguardTask - } else { - dexTask.dependsOn variantData.javaCompileTask - } dexTask.plugin = this dexTask.variant = variantData - dexTask.conventionMapping.libraries = libraryClosure - dexTask.conventionMapping.sourceFiles = sourceClosure - dexTask.conventionMapping.proguardedJar = proguardFileClosure dexTask.conventionMapping.outputFile = { project.file( "${project.buildDir}/libs/${project.archivesBaseName}-${variantData.baseName}.dex") } dexTask.dexOptions = extension.dexOptions + if (runProguard) { + + // first proguard task. + File outFile = createProguardTasks(variantData, variantConfig) + + // then dexing task + dexTask.dependsOn variantData.proguardTask + dexTask.conventionMapping.inputFiles = { project.files(outFile) } + dexTask.conventionMapping.preDexedLibraries = { Collections.emptyList() } + + } else { + + // if required, pre-dexing task. + PreDex preDexTask = null; + boolean runPreDex = extension.dexOptions.preDexLibraries + if (runPreDex) { + def preDexTaskName = "preDex${variantData.name}" + preDexTask = project.tasks.create(preDexTaskName, PreDex) + + preDexTask.dependsOn variantData.javaCompileTask + preDexTask.plugin = this + preDexTask.dexOptions = extension.dexOptions + + preDexTask.conventionMapping.inputFiles = { + project.files(variantConfig.packagedJars) + } + preDexTask.conventionMapping.outputFolder = { + project.file( + "${project.buildDir}/pre-dexed/${variantData.dirName}") + } + } + + // then dexing task + dexTask.dependsOn variantData.javaCompileTask + if (runPreDex) { + dexTask.dependsOn preDexTask + } + + dexTask.conventionMapping.inputFiles = { variantData.javaCompileTask.outputs.files } + if (runPreDex) { + dexTask.conventionMapping.preDexedLibraries = { + project.fileTree(preDexTask.outputFolder).files + } + } else { + dexTask.conventionMapping.preDexedLibraries = { + project.files(variantConfig.packagedJars) + } + } + } + // Add a task to generate application package def packageApp = project.tasks.create("package${variantData.name}", PackageApplication) variantData.packageApplicationTask = packageApp @@ -1144,13 +1177,10 @@ public abstract class BasePlugin { variantData.processResourcesTask.packageOutputFile } packageApp.conventionMapping.dexFile = { dexTask.outputFile } - packageApp.conventionMapping.packagedJars = { config.packagedJars } - packageApp.conventionMapping.javaResourceDir = { getOptionalDir(variantData.processJavaResources.destinationDir) } - packageApp.conventionMapping.jniDebugBuild = { config.buildType.jniDebugBuild } SigningConfigDsl sc = (SigningConfigDsl) config.signingConfig diff --git a/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/DexOptionsImpl.groovy b/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/DexOptionsImpl.groovy index 899337a..767b9d3 100644 --- a/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/DexOptionsImpl.groovy +++ b/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/DexOptionsImpl.groovy @@ -26,7 +26,13 @@ public class DexOptionsImpl implements DexOptions { private boolean coreLibraryFlag @Input - private boolean isIncrementalFlag = true + private boolean isIncrementalFlag = false + + @Input + private boolean isPreDexLibrariesFlag = true + + @Input + private boolean isJumboModeFlag = false @Input @Optional @@ -50,6 +56,24 @@ public class DexOptionsImpl implements DexOptions { return isIncrementalFlag } + @Override + boolean getPreDexLibraries() { + return isPreDexLibrariesFlag + } + + void setPreDexLibraries(boolean flag) { + isPreDexLibrariesFlag = flag + } + + public void setJumboMode(boolean flag) { + isJumboModeFlag = flag + } + + @Override + boolean getJumboMode() { + return isJumboModeFlag + } + public void setJavaMaxHeapSize(String theJavaMaxHeapSize) { if (theJavaMaxHeapSize.matches("\\d+[kKmMgGtT]?")) { javaMaxHeapSize = theJavaMaxHeapSize diff --git a/gradle/src/main/groovy/com/android/build/gradle/tasks/Dex.groovy b/gradle/src/main/groovy/com/android/build/gradle/tasks/Dex.groovy index 3db6c45..c74cdb3 100644 --- a/gradle/src/main/groovy/com/android/build/gradle/tasks/Dex.groovy +++ b/gradle/src/main/groovy/com/android/build/gradle/tasks/Dex.groovy @@ -14,14 +14,11 @@ * limitations under the License. */ package com.android.build.gradle.tasks - import com.android.build.gradle.internal.dsl.DexOptionsImpl import com.android.build.gradle.internal.tasks.IncrementalTask import com.android.ide.common.res2.FileStatus -import org.gradle.api.tasks.InputFile import org.gradle.api.tasks.InputFiles import org.gradle.api.tasks.Nested -import org.gradle.api.tasks.Optional import org.gradle.api.tasks.OutputFile public class Dex extends IncrementalTask { @@ -34,13 +31,10 @@ public class Dex extends IncrementalTask { // ----- PRIVATE TASK API ----- @InputFiles - Iterable<File> sourceFiles + Iterable<File> inputFiles @InputFiles - Iterable<File> libraries - - @InputFile @Optional - File proguardedJar + Iterable<File> preDexedLibraries @Nested DexOptionsImpl dexOptions @@ -48,10 +42,9 @@ public class Dex extends IncrementalTask { @Override protected void doFullTaskAction() { getBuilder().convertByteCode( - getSourceFiles(), - getLibraries(), - getProguardedJar(), - getOutputFile().absolutePath, + getInputFiles(), + getPreDexedLibraries(), + getOutputFile(), getDexOptions(), false) } @@ -59,10 +52,9 @@ public class Dex extends IncrementalTask { @Override protected void doIncrementalTaskAction(Map<File, FileStatus> changedInputs) { getBuilder().convertByteCode( - getSourceFiles(), - getLibraries(), - getProguardedJar(), - getOutputFile().absolutePath, + getInputFiles(), + getPreDexedLibraries(), + getOutputFile(), getDexOptions(), true) } diff --git a/gradle/src/main/groovy/com/android/build/gradle/tasks/PreDex.groovy b/gradle/src/main/groovy/com/android/build/gradle/tasks/PreDex.groovy new file mode 100644 index 0000000..b0aa26d --- /dev/null +++ b/gradle/src/main/groovy/com/android/build/gradle/tasks/PreDex.groovy @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.build.gradle.tasks +import com.android.SdkConstants +import com.android.annotations.NonNull +import com.android.build.gradle.internal.dsl.DexOptionsImpl +import com.android.build.gradle.internal.tasks.BaseTask +import com.android.builder.AndroidBuilder +import com.android.builder.DexOptions +import com.google.common.hash.HashCode +import com.google.common.hash.HashFunction +import com.google.common.hash.Hashing +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.Nested +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.TaskAction +import org.gradle.api.tasks.incremental.IncrementalTaskInputs + +public class PreDex extends BaseTask { + + // ----- PUBLIC TASK API ----- + + // ----- PRIVATE TASK API ----- + + // this is used automatically by Gradle, even though nothing + // in the class uses it. + @SuppressWarnings("GroovyUnusedDeclaration") + @InputFiles + Iterable<File> inputFiles + + @OutputDirectory + File outputFolder + + @Nested + DexOptionsImpl dexOptions + + @TaskAction + void taskAction(IncrementalTaskInputs taskInputs) { + final File outFolder = getOutputFolder() + final DexOptions options = getDexOptions() + + // if we are not in incremental mode, then outOfDate will contain + // all th files, but first we need to delete the previous output + if (!taskInputs.isIncremental()) { + emptyFolder(outFolder) + } + + AndroidBuilder builder = getBuilder() + + taskInputs.outOfDate { change -> + //noinspection GroovyAssignabilityCheck + File preDexedFile = getDexFileName(outFolder, change.file) + //noinspection GroovyAssignabilityCheck + builder.preDexLibrary(change.file, preDexedFile, options) + } + + taskInputs.removed { change -> + //noinspection GroovyAssignabilityCheck + File preDexedFile = getDexFileName(outFolder, change.file) + preDexedFile.delete() + } + } + + /** + * Returns a unique File for the pre-dexed library, even + * if there are 2 libraries with the same file names (but different + * paths) + * + * @param outFolder + * @param inputFile the library + * @return + */ + @NonNull + private static File getDexFileName(@NonNull File outFolder, @NonNull File inputFile) { + // get the filename + String name = inputFile.getName(); + // remove the extension + int pos = name.lastIndexOf('.'); + if (pos != -1) { + name = name.substring(0, pos); + } + + // add a hash of the original file path + HashFunction hashFunction = Hashing.md5(); + HashCode hashCode = hashFunction.hashString(inputFile.getAbsolutePath()); + + return new File(outFolder, name + "-" + hashCode.toString() + SdkConstants.DOT_JAR); + } +} |