Put proguard command parameters in a file.
Summary:
This moves most of the ProGuard command-line into a file and uses ProGuard's
support for '@file' syntax to read the parameters.
Test Plan: Sandcastle builds.
diff --git a/src/com/facebook/buck/android/AndroidBinaryRule.java b/src/com/facebook/buck/android/AndroidBinaryRule.java
index ac08603..67596be 100644
--- a/src/com/facebook/buck/android/AndroidBinaryRule.java
+++ b/src/com/facebook/buck/android/AndroidBinaryRule.java
@@ -909,7 +909,7 @@
// Run ProGuard on the classpath entries.
// TODO: ProGuardObfuscateStep's final argument should be a Path
- ProGuardObfuscateStep obfuscateCommand = new ProGuardObfuscateStep(
+ Step obfuscateCommand = ProGuardObfuscateStep.create(
generatedProGuardConfig,
proguardConfigsBuilder.build(),
useAndroidProguardConfigWithOptimizations,
diff --git a/src/com/facebook/buck/android/ProGuardObfuscateStep.java b/src/com/facebook/buck/android/ProGuardObfuscateStep.java
index 8b44d29..7644019 100644
--- a/src/com/facebook/buck/android/ProGuardObfuscateStep.java
+++ b/src/com/facebook/buck/android/ProGuardObfuscateStep.java
@@ -16,11 +16,14 @@
package com.facebook.buck.android;
+import com.facebook.buck.event.ThrowableLogEvent;
import com.facebook.buck.shell.ShellStep;
+import com.facebook.buck.step.AbstractExecutionStep;
+import com.facebook.buck.step.CompositeStep;
import com.facebook.buck.step.ExecutionContext;
+import com.facebook.buck.step.Step;
import com.facebook.buck.util.AndroidPlatformTarget;
import com.facebook.buck.util.Functions;
-import com.facebook.buck.util.HumanReadableException;
import com.facebook.buck.zip.CustomZipOutputStream;
import com.facebook.buck.zip.ZipOutputStreams;
import com.google.common.annotations.VisibleForTesting;
@@ -35,47 +38,57 @@
import java.io.File;
import java.io.IOException;
+import java.nio.file.Paths;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
public final class ProGuardObfuscateStep extends ShellStep {
- private final String generatedProGuardConfig;
-
- private final Set<String> customProguardConfigs;
-
private final Map<String, String> inputAndOutputEntries;
-
- private final Set<String> additionalLibraryJarsForProguard;
-
- private final boolean useAndroidProguardConfigWithOptimizations;
-
- private final String proguardDirectory;
+ private final String pathToProGuardCommandLineArgsFile;
/**
- * @param generatedProGuardConfig Proguard configuration as produced by aapt.
- * @param customProguardConfigs Main rule and its dependencies proguard configurations.
- * @param useProguardOptimizations Whether to include the Android SDK proguard defaults.
- * @param inputAndOutputEntries Map of input/output pairs to proguard. The key represents an
- * input jar (-injars); the value an output jar (-outjars).
- * @param additionalLibraryJarsForProguard Libraries that are not operated upon by proguard but
- * needed to resolve symbols.
- * @param proguardDirectory Output directory for various proguard-generated meta artifacts.
+ * @return step that writes out ProGuard's command line arguments to a text file and then runs
+ * ProGuard using those arguments. We write the arguments to a file to avoid blowing out
+ * exec()'s ARG_MAX limit.
*/
- public ProGuardObfuscateStep(
+ public static Step create(
String generatedProGuardConfig,
Set<String> customProguardConfigs,
boolean useProguardOptimizations,
Map<String, String> inputAndOutputEntries,
Set<String> additionalLibraryJarsForProguard,
String proguardDirectory) {
- this.generatedProGuardConfig = Preconditions.checkNotNull(generatedProGuardConfig);
- this.customProguardConfigs = ImmutableSet.copyOf(customProguardConfigs);
- this.useAndroidProguardConfigWithOptimizations = useProguardOptimizations;
+
+ String pathToProGuardCommandLineArgsFile = proguardDirectory + "/command-line.txt";
+
+ CommandLineHelperStep commandLineHelperStep = new CommandLineHelperStep(
+ generatedProGuardConfig,
+ customProguardConfigs,
+ useProguardOptimizations,
+ inputAndOutputEntries,
+ additionalLibraryJarsForProguard,
+ proguardDirectory,
+ pathToProGuardCommandLineArgsFile);
+
+ ProGuardObfuscateStep proGuardStep = new ProGuardObfuscateStep(
+ inputAndOutputEntries, pathToProGuardCommandLineArgsFile);
+
+ return new CompositeStep(ImmutableList.of(commandLineHelperStep, proGuardStep));
+ }
+
+ /**
+ * @param inputAndOutputEntries Map of input/output pairs to proguard. The key represents an
+ * input jar (-injars); the value an output jar (-outjars).
+ * @param pathToProGuardCommandLineArgsFile Path to file containing arguments to ProGuard.
+ */
+ private ProGuardObfuscateStep(
+ Map<String, String> inputAndOutputEntries,
+ String pathToProGuardCommandLineArgsFile) {
this.inputAndOutputEntries = ImmutableMap.copyOf(inputAndOutputEntries);
- this.additionalLibraryJarsForProguard = ImmutableSet.copyOf(additionalLibraryJarsForProguard);
- this.proguardDirectory = Preconditions.checkNotNull(proguardDirectory);
+ this.pathToProGuardCommandLineArgsFile = Preconditions.checkNotNull(
+ pathToProGuardCommandLineArgsFile);
}
@Override
@@ -85,46 +98,16 @@
@Override
protected ImmutableList<String> getShellCommandInternal(ExecutionContext context) {
- ImmutableList.Builder<String> args = ImmutableList.builder();
AndroidPlatformTarget androidPlatformTarget = context.getAndroidPlatformTarget();
- Joiner pathJoiner = Joiner.on(':');
// Run ProGuard as a standalone executable JAR file.
String proguardJar = androidPlatformTarget.getProguardJar().getAbsolutePath();
- args.add("java").add("-Xmx1024M").add("-jar").add(proguardJar);
- // -include
- if (useAndroidProguardConfigWithOptimizations) {
- args.add("-include")
- .add(androidPlatformTarget.getOptimizedProguardConfig().getAbsolutePath());
- } else {
- args.add("-include").add(androidPlatformTarget.getProguardConfig().getAbsolutePath());
- }
- for (String proguardConfig : customProguardConfigs) {
- args.add("-include").add(proguardConfig);
- }
- args.add("-include").add(generatedProGuardConfig);
-
- // -injars and -outjars paired together for each input.
- for (Map.Entry<String, String> inputOutputEntry : inputAndOutputEntries.entrySet()) {
- args.add("-injars").add(inputOutputEntry.getKey());
- args.add("-outjars").add(inputOutputEntry.getValue());
- }
-
- // -libraryjars
- Iterable<String> bootclasspathPaths = Iterables.transform(
- androidPlatformTarget.getBootclasspathEntries(), Functions.FILE_TO_ABSOLUTE_PATH);
- Iterable<String> libraryJars = Iterables.concat(bootclasspathPaths,
- additionalLibraryJarsForProguard);
- args.add("-libraryjars").add(pathJoiner.join(libraryJars));
-
- // -dump
- args.add("-dump").add(proguardDirectory + "/dump.txt");
- args.add("-printseeds").add(proguardDirectory + "/seeds.txt");
- args.add("-printusage").add(proguardDirectory + "/usage.txt");
- args.add("-printmapping").add(proguardDirectory + "/mapping.txt");
- args.add("-printconfiguration").add(proguardDirectory + "/configuration.txt");
-
+ ImmutableList.Builder<String> args = ImmutableList.builder();
+ args.add("java")
+ .add("-Xmx1024M")
+ .add("-jar").add(proguardJar)
+ .add("@" + pathToProGuardCommandLineArgsFile);
return args.build();
}
@@ -138,23 +121,27 @@
// account for this and remove those entries from the classes to dex so we hack things here to
// ensure that the files exist but are empty.
if (exitCode == 0) {
- ensureAllOutputsExist();
+ exitCode = ensureAllOutputsExist(context);
}
return exitCode;
}
- private void ensureAllOutputsExist() {
+ private int ensureAllOutputsExist(ExecutionContext context) {
for (String outputJar : inputAndOutputEntries.values()) {
File outputJarFile = new File(outputJar);
if (!outputJarFile.exists()) {
try {
createEmptyZip(outputJarFile);
} catch (IOException e) {
- throw new HumanReadableException("Failed to create empty jar file: %s", outputJar);
+ context.getBuckEventBus().post(ThrowableLogEvent.create(e,
+ "Error creating empty zip file at: %s.",
+ outputJarFile));
+ return 1;
}
}
}
+ return 0;
}
@VisibleForTesting
@@ -170,29 +157,159 @@
@Override
public boolean equals(Object obj) {
- if (obj == null || !(obj instanceof ProGuardObfuscateStep)) {
+ if (this == obj) {
+ return true;
+ } else if (!(obj instanceof ProGuardObfuscateStep)) {
return false;
}
- ProGuardObfuscateStep that = (ProGuardObfuscateStep) obj;
- return
- Objects.equal(useAndroidProguardConfigWithOptimizations,
- that.useAndroidProguardConfigWithOptimizations) &&
- Objects.equal(additionalLibraryJarsForProguard,
- that.additionalLibraryJarsForProguard) &&
- Objects.equal(customProguardConfigs, that.customProguardConfigs) &&
- Objects.equal(generatedProGuardConfig, that.generatedProGuardConfig) &&
- Objects.equal(inputAndOutputEntries, that.inputAndOutputEntries) &&
- Objects.equal(proguardDirectory, that.proguardDirectory);
+ ProGuardObfuscateStep that = (ProGuardObfuscateStep) obj;
+ return Objects.equal(this.inputAndOutputEntries, that.inputAndOutputEntries) &&
+ Objects.equal(this.pathToProGuardCommandLineArgsFile,
+ that.pathToProGuardCommandLineArgsFile);
}
@Override
public int hashCode() {
- return Objects.hashCode(useAndroidProguardConfigWithOptimizations,
- additionalLibraryJarsForProguard,
- customProguardConfigs,
- generatedProGuardConfig,
- inputAndOutputEntries,
- proguardDirectory);
+ return Objects.hashCode(inputAndOutputEntries, pathToProGuardCommandLineArgsFile);
+ }
+
+ /**
+ * Helper class to run as a step before ProGuardObfuscateStep to write out the
+ * command-line parameters to a file. The ProGuardObfuscateStep references
+ * this file when it runs using ProGuard's '@' syntax. This allows for longer
+ * command-lines than would otherwise be supported.
+ */
+ private static class CommandLineHelperStep extends AbstractExecutionStep {
+
+ private final String generatedProGuardConfig;
+ private final Set<String> customProguardConfigs;
+ private final Map<String, String> inputAndOutputEntries;
+ private final Set<String> additionalLibraryJarsForProguard;
+ private final boolean useAndroidProguardConfigWithOptimizations;
+ private final String proguardDirectory;
+ private final String pathToProGuardCommandLineArgsFile;
+
+ /**
+ * @param generatedProGuardConfig Proguard configuration as produced by aapt.
+ * @param customProguardConfigs Main rule and its dependencies proguard configurations.
+ * @param useProguardOptimizations Whether to include the Android SDK proguard defaults.
+ * @param inputAndOutputEntries Map of input/output pairs to proguard. The key represents an
+ * input jar (-injars); the value an output jar (-outjars).
+ * @param additionalLibraryJarsForProguard Libraries that are not operated upon by proguard but
+ * needed to resolve symbols.
+ * @param proguardDirectory Output directory for various proguard-generated meta artifacts.
+ * @param pathToProGuardCommandLineArgsFile Path to file containing arguments to ProGuard.
+ */
+ private CommandLineHelperStep(
+ String generatedProGuardConfig,
+ Set<String> customProguardConfigs,
+ boolean useProguardOptimizations,
+ Map<String, String> inputAndOutputEntries,
+ Set<String> additionalLibraryJarsForProguard,
+ String proguardDirectory,
+ String pathToProGuardCommandLineArgsFile) {
+ super("write_proguard_command_line_parameters");
+ this.generatedProGuardConfig = Preconditions.checkNotNull(generatedProGuardConfig);
+ this.customProguardConfigs = ImmutableSet.copyOf(customProguardConfigs);
+ this.useAndroidProguardConfigWithOptimizations = useProguardOptimizations;
+ this.inputAndOutputEntries = ImmutableMap.copyOf(inputAndOutputEntries);
+ this.additionalLibraryJarsForProguard = ImmutableSet.copyOf(additionalLibraryJarsForProguard);
+ this.proguardDirectory = Preconditions.checkNotNull(proguardDirectory);
+ this.pathToProGuardCommandLineArgsFile = pathToProGuardCommandLineArgsFile;
+ }
+
+ @Override
+ public int execute(ExecutionContext context) {
+ String proGuardArguments = Joiner.on('\n').join(getParameters(context));
+ try {
+ context.getProjectFilesystem().writeContentsToPath(
+ proGuardArguments,
+ Paths.get(pathToProGuardCommandLineArgsFile));
+ } catch (IOException e) {
+ context.getBuckEventBus().post(ThrowableLogEvent.create(e,
+ "Error writing ProGuard arguments to file: %s.",
+ pathToProGuardCommandLineArgsFile));
+ return 1;
+ }
+
+ return 0;
+ }
+
+ /** @return the list of arguments to pass to ProGuard. */
+ private ImmutableList<String> getParameters(ExecutionContext context) {
+ ImmutableList.Builder<String> args = ImmutableList.builder();
+ AndroidPlatformTarget androidPlatformTarget = context.getAndroidPlatformTarget();
+ Joiner pathJoiner = Joiner.on(':');
+
+ // Relative paths should be interpreted relative to project directory root, not the
+ // written parameters file.
+ args.add("-basedirectory")
+ .add(context.getProjectDirectoryRoot().getAbsolutePath());
+
+ // -include
+ if (useAndroidProguardConfigWithOptimizations) {
+ args.add("-include")
+ .add(androidPlatformTarget.getOptimizedProguardConfig().getAbsolutePath());
+ } else {
+ args.add("-include").add(androidPlatformTarget.getProguardConfig().getAbsolutePath());
+ }
+ for (String proguardConfig : customProguardConfigs) {
+ args.add("-include").add(proguardConfig);
+ }
+ args.add("-include").add(generatedProGuardConfig);
+
+ // -injars and -outjars paired together for each input.
+ for (Map.Entry<String, String> inputOutputEntry : inputAndOutputEntries.entrySet()) {
+ args.add("-injars").add(inputOutputEntry.getKey());
+ args.add("-outjars").add(inputOutputEntry.getValue());
+ }
+
+ // -libraryjars
+ Iterable<String> bootclasspathPaths = Iterables.transform(
+ androidPlatformTarget.getBootclasspathEntries(), Functions.FILE_TO_ABSOLUTE_PATH);
+ Iterable<String> libraryJars = Iterables.concat(bootclasspathPaths,
+ additionalLibraryJarsForProguard);
+ args.add("-libraryjars").add(pathJoiner.join(libraryJars));
+
+ // -dump
+ args.add("-dump").add(proguardDirectory + "/dump.txt");
+ args.add("-printseeds").add(proguardDirectory + "/seeds.txt");
+ args.add("-printusage").add(proguardDirectory + "/usage.txt");
+ args.add("-printmapping").add(proguardDirectory + "/mapping.txt");
+ args.add("-printconfiguration").add(proguardDirectory + "/configuration.txt");
+
+ return args.build();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof CommandLineHelperStep)) {
+ return false;
+ }
+ CommandLineHelperStep that = (CommandLineHelperStep) obj;
+
+ return
+ Objects.equal(useAndroidProguardConfigWithOptimizations,
+ that.useAndroidProguardConfigWithOptimizations) &&
+ Objects.equal(additionalLibraryJarsForProguard,
+ that.additionalLibraryJarsForProguard) &&
+ Objects.equal(customProguardConfigs, that.customProguardConfigs) &&
+ Objects.equal(generatedProGuardConfig, that.generatedProGuardConfig) &&
+ Objects.equal(inputAndOutputEntries, that.inputAndOutputEntries) &&
+ Objects.equal(proguardDirectory, that.proguardDirectory) &&
+ Objects.equal(pathToProGuardCommandLineArgsFile, that.pathToProGuardCommandLineArgsFile);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(useAndroidProguardConfigWithOptimizations,
+ additionalLibraryJarsForProguard,
+ customProguardConfigs,
+ generatedProGuardConfig,
+ inputAndOutputEntries,
+ proguardDirectory,
+ pathToProGuardCommandLineArgsFile);
+ }
}
}
diff --git a/src/com/facebook/buck/step/CompositeStep.java b/src/com/facebook/buck/step/CompositeStep.java
index 2e9e7dd..5ef5745 100644
--- a/src/com/facebook/buck/step/CompositeStep.java
+++ b/src/com/facebook/buck/step/CompositeStep.java
@@ -18,6 +18,7 @@
import com.google.common.base.Function;
import com.google.common.base.Joiner;
+import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
@@ -73,4 +74,20 @@
return steps.iterator();
}
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ } else if (!(obj instanceof CompositeStep)) {
+ return false;
+ }
+
+ CompositeStep that = (CompositeStep) obj;
+ return Objects.equal(this.steps, that.steps);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(steps);
+ }
}
diff --git a/test/com/facebook/buck/android/AndroidBinaryRuleTest.java b/test/com/facebook/buck/android/AndroidBinaryRuleTest.java
index 70ccdb7..b8d8ecc 100644
--- a/test/com/facebook/buck/android/AndroidBinaryRuleTest.java
+++ b/test/com/facebook/buck/android/AndroidBinaryRuleTest.java
@@ -133,8 +133,8 @@
ImmutableSet.<String>of(),
"buck-out/gen/java/src/com/facebook/base/.proguard/apk/proguard.txt");
- ProGuardObfuscateStep expectedObfuscation =
- new ProGuardObfuscateStep(
+ Step expectedObfuscation =
+ ProGuardObfuscateStep.create(
"buck-out/gen/java/src/com/facebook/base/.proguard/apk/proguard.txt",
ImmutableSet.<String>of(),
false,