diff options
author | 2016-01-15 23:40:14 +0000 | |
---|---|---|
committer | 2016-01-18 14:32:16 +0000 | |
commit | e3ce21fab041c6c8e6da2811f5dce75615ccb1f4 (patch) | |
tree | 810872c48d6b59f8b7cc3de6dbe5cfb4c0bfbb87 | |
parent | 4a24477ce0fa3249160cc61b455db3ad6e247a43 (diff) |
support hidden --java_optimization_mode flag in java_test rule.
--
MOS_MIGRATED_REVID=112290581
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaTestRule.java | 6 | ||||
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java | 27 | ||||
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/rules/android/AndroidConfiguration.java | 19 | ||||
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java | 22 | ||||
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/rules/android/AndroidSdk.java | 3 | ||||
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/rules/java/JavaBinary.java | 114 | ||||
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/rules/java/JavaConfiguration.java | 17 | ||||
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/rules/java/JavaOptions.java | 8 | ||||
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/rules/java/JavaSemantics.java | 16 | ||||
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/rules/java/ProguardHelper.java (renamed from src/main/java/com/google/devtools/build/lib/rules/android/ProguardHelper.java) | 143 |
10 files changed, 282 insertions, 93 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaTestRule.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaTestRule.java index c3c5b7558c..82c0012a60 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaTestRule.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaTestRule.java @@ -14,8 +14,10 @@ package com.google.devtools.build.lib.bazel.rules.java; +import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition.HOST; import static com.google.devtools.build.lib.packages.Attribute.attr; import static com.google.devtools.build.lib.packages.BuildType.LABEL; +import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST; import static com.google.devtools.build.lib.packages.BuildType.TRISTATE; import static com.google.devtools.build.lib.syntax.Type.BOOLEAN; import static com.google.devtools.build.lib.syntax.Type.STRING; @@ -51,6 +53,10 @@ public final class BazelJavaTestRule implements RuleDefinition { return builder .requiresConfigurationFragments(JavaConfiguration.class, Jvm.class) .setImplicitOutputsFunction(BazelJavaRuleClasses.JAVA_BINARY_IMPLICIT_OUTPUTS) + // Proguard can be run over java_test targets using the --java_optimization_mode flag. + // Primarily this is intended to help test changes to Proguard. + .add(attr(":proguard", LABEL).cfg(HOST).value(JavaSemantics.PROGUARD).exec()) + .add(attr(":extra_proguard_specs", LABEL_LIST).value(JavaSemantics.EXTRA_PROGUARD_SPECS)) .override(attr("stamp", TRISTATE).value(TriState.NO)) .override(attr("use_testrunner", BOOLEAN).value(true)) .override(attr(":java_launcher", LABEL).value(JavaSemantics.JAVA_LAUNCHER)) diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java index de84cec192..2979e4e0db 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java @@ -14,7 +14,6 @@ package com.google.devtools.build.lib.rules.android; import static com.google.common.base.Strings.isNullOrEmpty; -import static com.google.devtools.build.lib.rules.android.ProguardHelper.PROGUARD_SPECS; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; @@ -48,7 +47,6 @@ import com.google.devtools.build.lib.packages.BuildType; import com.google.devtools.build.lib.packages.TriState; import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory; import com.google.devtools.build.lib.rules.android.AndroidRuleClasses.MultidexMode; -import com.google.devtools.build.lib.rules.android.ProguardHelper.ProguardOutput; import com.google.devtools.build.lib.rules.cpp.CcToolchainProvider; import com.google.devtools.build.lib.rules.cpp.CppHelper; import com.google.devtools.build.lib.rules.java.DeployArchiveBuilder; @@ -59,6 +57,8 @@ import com.google.devtools.build.lib.rules.java.JavaConfiguration.JavaOptimizati import com.google.devtools.build.lib.rules.java.JavaSemantics; import com.google.devtools.build.lib.rules.java.JavaSourceInfoProvider; import com.google.devtools.build.lib.rules.java.JavaTargetAttributes; +import com.google.devtools.build.lib.rules.java.ProguardHelper; +import com.google.devtools.build.lib.rules.java.ProguardHelper.ProguardOutput; import com.google.devtools.build.lib.syntax.Type; import com.google.devtools.build.lib.util.Preconditions; import com.google.devtools.build.lib.vfs.PathFragment; @@ -131,7 +131,9 @@ public abstract class AndroidBinary implements RuleConfiguredTargetFactory { } if (ruleContext.attributes().isAttributeValueExplicitlySpecified("proguard_apply_mapping") - && ruleContext.attributes().get(PROGUARD_SPECS, BuildType.LABEL_LIST).isEmpty()) { + && ruleContext.attributes() + .get(ProguardHelper.PROGUARD_SPECS, BuildType.LABEL_LIST) + .isEmpty()) { ruleContext.attributeError("proguard_apply_mapping", "'proguard_apply_mapping' can only be used when 'proguard_specs' is also set"); return null; @@ -342,7 +344,7 @@ public abstract class AndroidBinary implements RuleConfiguredTargetFactory { ImmutableList<Artifact> apksUnderTest, Artifact proguardMapping) throws InterruptedException { ImmutableList<Artifact> proguardSpecs = ProguardHelper.collectTransitiveProguardSpecs( - ruleContext, resourceApk.getResourceProguardConfig()); + ruleContext, ImmutableList.of(resourceApk.getResourceProguardConfig())); ProguardOutput proguardOutput = applyProguard( @@ -731,7 +733,10 @@ public abstract class AndroidBinary implements RuleConfiguredTargetFactory { .getJavaOptimizationMode(); } - /** Applies the proguard specifications, and creates a ProguardedJar. */ + /** + * Applies the proguard specifications, and creates a ProguardedJar. Proguard's output artifacts + * are added to the given {@code filesBuilder}. + */ private static ProguardOutput applyProguard( RuleContext ruleContext, AndroidCommon common, @@ -757,9 +762,12 @@ public abstract class AndroidBinary implements RuleConfiguredTargetFactory { .add(sdk.getAndroidJar()) .addTransitive(common.getTransitiveNeverLinkLibraries()) .build(); - return ProguardHelper.createProguardAction(ruleContext, sdk.getProguard(), deployJarArtifact, - proguardSpecs, proguardMapping, libraryJars, proguardOutputJar, - ruleContext.attributes().get("proguard_generate_mapping", Type.BOOLEAN), filesBuilder); + ProguardOutput result = ProguardHelper.createProguardAction(ruleContext, sdk.getProguard(), + deployJarArtifact, proguardSpecs, proguardMapping, libraryJars, proguardOutputJar, + ruleContext.attributes().get("proguard_generate_mapping", Type.BOOLEAN)); + // Since Proguard is being run, add its output artifacts to the given filesBuilder + result.addAllToSet(filesBuilder); + return result; } private static ProguardOutput createEmptyProguardAction(RuleContext ruleContext, @@ -767,8 +775,7 @@ public abstract class AndroidBinary implements RuleConfiguredTargetFactory { ImmutableList.Builder<Artifact> failures = ImmutableList.<Artifact>builder().add(proguardOutputJar); if (ruleContext.attributes().get("proguard_generate_mapping", Type.BOOLEAN)) { - failures.add( - ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_BINARY_PROGUARD_MAP)); + failures.add(ruleContext.getImplicitOutputArtifact(JavaSemantics.JAVA_BINARY_PROGUARD_MAP)); } JavaOptimizationMode optMode = getJavaOptimizationMode(ruleContext); ruleContext.registerAction( diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidConfiguration.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidConfiguration.java index 05f5e2ceb5..564a344325 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidConfiguration.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidConfiguration.java @@ -21,7 +21,6 @@ import com.google.devtools.build.lib.Constants; import com.google.devtools.build.lib.analysis.config.BuildConfiguration; import com.google.devtools.build.lib.analysis.config.BuildConfiguration.DefaultLabelConverter; import com.google.devtools.build.lib.analysis.config.BuildConfiguration.Fragment; -import com.google.devtools.build.lib.analysis.config.BuildConfiguration.LabelConverter; import com.google.devtools.build.lib.analysis.config.BuildConfiguration.StrictDepsConverter; import com.google.devtools.build.lib.analysis.config.BuildConfiguration.StrictDepsMode; import com.google.devtools.build.lib.analysis.config.BuildOptions; @@ -146,14 +145,6 @@ public class AndroidConfiguration extends BuildConfiguration.Fragment { help = "Specifies Android SDK/platform that is used to build Android applications.") public Label sdk; - @Option(name = "proguard_top", - defaultValue = "null", - category = "version", - converter = LabelConverter.class, - help = "Specifies which version of ProGuard to use for code removal when building an " - + "Android binary.") - public Label proguard; - @Option(name = "legacy_android_native_support", defaultValue = "true", category = "semantics", @@ -188,10 +179,6 @@ public class AndroidConfiguration extends BuildConfiguration.Fragment { @Override public void addAllLabels(Multimap<String, Label> labelMap) { - if (proguard != null) { - labelMap.put("android_proguard", proguard); - } - if (androidCrosstoolTop != null) { labelMap.put("android_crosstool_top", androidCrosstoolTop); } @@ -249,7 +236,6 @@ public class AndroidConfiguration extends BuildConfiguration.Fragment { private final boolean incrementalNativeLibs; private final boolean fatApk; private final ConfigurationDistinguisher configurationDistinguisher; - private final Label proguard; private final boolean useJackForDexing; private final boolean jackSanityChecks; @@ -261,7 +247,6 @@ public class AndroidConfiguration extends BuildConfiguration.Fragment { this.cpu = options.cpu; this.fatApk = !options.realFatApkCpus().isEmpty(); this.configurationDistinguisher = options.configurationDistinguisher; - this.proguard = options.proguard; this.useJackForDexing = options.useJackForDexing; this.jackSanityChecks = options.jackSanityChecks; } @@ -314,8 +299,4 @@ public class AndroidConfiguration extends BuildConfiguration.Fragment { public String getOutputDirectoryName() { return configurationDistinguisher.suffix; } - - public Label getProguardLabel() { - return proguard; - } } diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java index 09702fcf93..f4704d329b 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java @@ -47,6 +47,7 @@ import com.google.devtools.build.lib.packages.TriState; import com.google.devtools.build.lib.rules.android.AndroidConfiguration.ConfigurationDistinguisher; import com.google.devtools.build.lib.rules.cpp.CppOptions; import com.google.devtools.build.lib.rules.java.JavaCompilationArgsProvider; +import com.google.devtools.build.lib.rules.java.JavaConfiguration; import com.google.devtools.build.lib.rules.java.JavaSemantics; import com.google.devtools.build.lib.syntax.Type; import com.google.devtools.build.lib.util.FileType; @@ -92,8 +93,6 @@ public final class AndroidRuleClasses { fromTemplates("%{name}_deploy.jar"); public static final SafeImplicitOutputsFunction ANDROID_BINARY_PROGUARD_JAR = fromTemplates("%{name}_proguard.jar"); - public static final SafeImplicitOutputsFunction ANDROID_BINARY_PROGUARD_MAP = - fromTemplates("%{name}_proguard.map"); public static final SafeImplicitOutputsFunction ANDROID_BINARY_INSTRUMENTED_JAR = fromTemplates("%{name}_instrumented.jar"); public static final SafeImplicitOutputsFunction ANDROID_TEST_FILTERED_JAR = @@ -150,19 +149,6 @@ public final class AndroidRuleClasses { public static final Label DEFAULT_AAR_GENERATOR = Label.parseAbsoluteUnchecked(Constants.TOOLS_REPOSITORY + "//tools/android:aar_generator"); - /** - * Implementation for the :proguard attribute. - */ - static final LateBoundLabel<BuildConfiguration> PROGUARD = - new LateBoundLabel<BuildConfiguration>(AndroidConfiguration.class) { - @Override - public Label getDefault(Rule rule, BuildConfiguration configuration) { - // If --proguard_top is not specified, null is returned. AndroidSdk will take care of using - // android_sdk.proguard then. - return configuration.getFragment(AndroidConfiguration.class).getProguardLabel(); - } - }; - public static final LateBoundLabel<BuildConfiguration> ANDROID_SDK = new LateBoundLabel<BuildConfiguration>(DEFAULT_ANDROID_SDK, AndroidConfiguration.class) { @Override @@ -276,7 +262,7 @@ public final class AndroidRuleClasses { if (hasProguardSpecs) { functions.add(AndroidRuleClasses.ANDROID_BINARY_PROGUARD_JAR); if (mapping) { - functions.add(AndroidRuleClasses.ANDROID_BINARY_PROGUARD_MAP); + functions.add(JavaSemantics.JAVA_BINARY_PROGUARD_MAP); } } return fromFunctions(functions).getImplicitOutputs(rule); @@ -315,10 +301,10 @@ public final class AndroidRuleClasses { @Override public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) { return builder - .requiresConfigurationFragments(AndroidConfiguration.class) + .requiresConfigurationFragments(JavaConfiguration.class, AndroidConfiguration.class) .setUndocumented() // This is the Proguard that comes from the --proguard_top attribute. - .add(attr(":proguard", LABEL).cfg(HOST).value(PROGUARD).exec()) + .add(attr(":proguard", LABEL).cfg(HOST).value(JavaSemantics.PROGUARD).exec()) // This is the Proguard in the BUILD file that contains the android_sdk rule. Used when // --proguard_top is not specified. .add(attr("proguard", LABEL).mandatory().cfg(HOST).allowedFileTypes(ANY_FILE).exec()) diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSdk.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSdk.java index f00b415aae..8904b22e68 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSdk.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSdk.java @@ -23,6 +23,7 @@ import com.google.devtools.build.lib.analysis.RunfilesProvider; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.collect.nestedset.Order; import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory; +import com.google.devtools.build.lib.rules.java.JavaConfiguration; /** * Implementation of the {@code android_sdk} rule. @@ -33,7 +34,7 @@ public class AndroidSdk implements RuleConfiguredTargetFactory { // If the user didn't specify --proguard_top, go with the proguard attribute in the android_sdk // rule. Otherwise, use what she told us to. FilesToRunProvider proguard = - ruleContext.getFragment(AndroidConfiguration.class).getProguardLabel() == null + ruleContext.getFragment(JavaConfiguration.class).getProguardBinary() == null ? ruleContext.getExecutablePrerequisite("proguard", Mode.HOST) : ruleContext.getExecutablePrerequisite(":proguard", Mode.HOST); diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaBinary.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaBinary.java index 87d45b9c92..67ba2c3dff 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaBinary.java +++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaBinary.java @@ -14,6 +14,7 @@ package com.google.devtools.build.lib.rules.java; import static com.google.devtools.build.lib.rules.java.DeployArchiveBuilder.Compression.COMPRESSED; +import static com.google.devtools.build.lib.rules.java.DeployArchiveBuilder.Compression.UNCOMPRESSED; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -22,6 +23,7 @@ import com.google.common.collect.Lists; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.analysis.ConfiguredTarget; import com.google.devtools.build.lib.analysis.FileProvider; +import com.google.devtools.build.lib.analysis.FilesToRunProvider; import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode; import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder; import com.google.devtools.build.lib.analysis.RuleContext; @@ -46,6 +48,8 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import javax.annotation.Nullable; + /** * An implementation of java_binary. */ @@ -213,21 +217,45 @@ public class JavaBinary implements RuleConfiguredTargetFactory { RuleConfiguredTargetBuilder builder = new RuleConfiguredTargetBuilder(ruleContext); - semantics.addProviders(ruleContext, common, jvmFlags, classJar, srcJar, - genClassJar, genSourceJar, ImmutableMap.<Artifact, Artifact>of(), + semantics.addProviders(ruleContext, common, jvmFlags, classJar, srcJar, + genClassJar, genSourceJar, ImmutableMap.<Artifact, Artifact>of(), helper, filesBuilder, builder); + Artifact deployJar = + ruleContext.getImplicitOutputArtifact(JavaSemantics.JAVA_BINARY_DEPLOY_JAR); + boolean runProguard = applyProguardIfRequested( + ruleContext, deployJar, common.getBootClasspath(), mainClass, filesBuilder); + NestedSet<Artifact> filesToBuild = filesBuilder.build(); + // Need not include normal runtime classpath in runfiles if Proguard is used because _deploy.jar + // is used as classpath instead. Keeping runfiles unchanged has however the advantage that + // manually running executable without --singlejar works (although it won't depend on Proguard). collectDefaultRunfiles(runfilesBuilder, ruleContext, common, filesToBuild, launcher, dynamicRuntimeActionInputs); Runfiles defaultRunfiles = runfilesBuilder.build(); - RunfilesSupport runfilesSupport = createExecutable - ? runfilesSupport = RunfilesSupport.withExecutable( - ruleContext, defaultRunfiles, executable, - semantics.getExtraArguments(ruleContext, common)) - : null; + RunfilesSupport runfilesSupport = null; + if (createExecutable) { + List<String> extraArgs = new ArrayList<>(semantics.getExtraArguments(ruleContext, common)); + if (runProguard) { + // Instead of changing the classpath written into the wrapper script, pass --singlejar when + // running the script (which causes the deploy.jar written by Proguard to be used instead of + // the normal classpath). It's a bit odd to do this b/c manually running the script wouldn't + // use Proguard's output unless --singlejar is explicitly supplied. On the other hand the + // behavior of the script is more consistent: the (proguarded) deploy.jar is only used with + // --singlejar. Moreover, people will almost always run tests using blaze test, which does + // use Proguard's output thanks to this extra arg when enabled. Also, it's actually hard to + // get the classpath changed in the wrapper script (would require calling + // JavaCommon.setClasspathFragment with a new fragment at the *end* of this method because + // the classpath is evaluated lazily when generating the wrapper script) and the wrapper + // script would essentially have an if (--singlejar was set), set classpath to deploy jar, + // otherwise, set classpath to deploy jar. + extraArgs.add("--wrapper_script_flag=--singlejar"); + } + runfilesSupport = + RunfilesSupport.withExecutable(ruleContext, defaultRunfiles, executable, extraArgs); + } RunfilesProvider runfilesProvider = RunfilesProvider.withData( defaultRunfiles, @@ -236,26 +264,30 @@ public class JavaBinary implements RuleConfiguredTargetFactory { ImmutableList<String> deployManifestLines = getDeployManifestLines(ruleContext, originalMainClass); - Artifact deployJar = - ruleContext.getImplicitOutputArtifact(JavaSemantics.JAVA_BINARY_DEPLOY_JAR); - - Artifact unstrippedDeployJar = - ruleContext.getImplicitOutputArtifact(JavaSemantics.JAVA_UNSTRIPPED_BINARY_DEPLOY_JAR); - + // When running Proguard: + // (1) write single jar to intermediate destination; Proguard will write _deploy.jar file + // (2) Don't depend on runfiles to avoid circular dependency, since _deploy.jar is itself part + // of runfiles when Proguard runs (because executable then needs it) and _deploy.jar depends + // on this single jar. + // (3) Don't bother with compression since Proguard will write the final jar anyways deployArchiveBuilder - .setOutputJar(deployJar) + .setOutputJar( + runProguard + ? ruleContext.getImplicitOutputArtifact(JavaSemantics.JAVA_BINARY_MERGED_JAR) + : deployJar) .setJavaStartClass(mainClass) .setDeployManifestLines(deployManifestLines) .setAttributes(attributes) .addRuntimeJars(common.getJavaCompilationArtifacts().getRuntimeJars()) .setIncludeBuildData(true) .setRunfilesMiddleman( - runfilesSupport == null ? null : runfilesSupport.getRunfilesMiddleman()) - .setCompression(COMPRESSED) - .setLauncher(launcher); - - deployArchiveBuilder.build(); + runProguard || runfilesSupport == null ? null : runfilesSupport.getRunfilesMiddleman()) + .setCompression(runProguard ? UNCOMPRESSED : COMPRESSED) + .setLauncher(launcher) + .build(); + Artifact unstrippedDeployJar = + ruleContext.getImplicitOutputArtifact(JavaSemantics.JAVA_UNSTRIPPED_BINARY_DEPLOY_JAR); if (stripAsDefault) { unstrippedDeployArchiveBuilder .setOutputJar(unstrippedDeployJar) @@ -413,4 +445,48 @@ public class JavaBinary implements RuleConfiguredTargetFactory { return result.build(); } + + /** + * This method uses {@link ProguardHelper#applyProguardIfRequested} to create a proguard action + * if necessary and adds any artifacts created by proguard to the given {@code filesBuilder}. + * This is convenience to make sure the proguarded Jar is included in the files to build, which is + * necessary because the Jar written by proguard is used at runtime. + * If this method returns {@code true} the Proguard is being used and we need to use a + * {@link DeployArchiveBuilder} to write the input artifact assumed by + * {@link ProguardHelper#applyProguardIfRequested}. + */ + private static boolean applyProguardIfRequested(RuleContext ruleContext, Artifact deployJar, + ImmutableList<Artifact> bootclasspath, String mainClassName, + NestedSetBuilder<Artifact> filesBuilder) throws InterruptedException { + // We only support proguarding tests so Proguard doesn't try to proguard itself. + if (!ruleContext.getRule().getRuleClass().endsWith("_test")) { + return false; + } + ProguardHelper.ProguardOutput output = + JavaBinaryProguardHelper.INSTANCE.applyProguardIfRequested( + ruleContext, deployJar, bootclasspath, mainClassName); + if (output == null) { + return false; + } + output.addAllToSet(filesBuilder); + return true; + } + + private static class JavaBinaryProguardHelper extends ProguardHelper { + + static final JavaBinaryProguardHelper INSTANCE = new JavaBinaryProguardHelper(); + + @Override + @Nullable + protected FilesToRunProvider findProguard(RuleContext ruleContext) { + // TODO(bazel-team): Find a way to use Proguard specified in android_sdk rules + return ruleContext.getExecutablePrerequisite(":proguard", Mode.HOST); + } + + @Override + protected ImmutableList<Artifact> collectProguardSpecsForRule(RuleContext ruleContext, + String mainClassName) { + return ImmutableList.of(generateSpecForJavaBinary(ruleContext, mainClassName)); + } + } } diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaConfiguration.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaConfiguration.java index 315cf67797..2983140fd0 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaConfiguration.java +++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaConfiguration.java @@ -32,6 +32,8 @@ import com.google.devtools.common.options.TriState; import java.util.List; +import javax.annotation.Nullable; + /** * A java compiler configuration containing the flags required for compilation. */ @@ -137,17 +139,17 @@ public final class JavaConfiguration extends Fragment { private final Label javacBootclasspath; private final Label javacExtdir; private final ImmutableList<String> javacOpts; + private final Label proguardBinary; private final ImmutableList<Label> extraProguardSpecs; private final TriState bundleTranslations; private final ImmutableList<Label> translationTargets; private final String javaCpu; private final JavaOptimizationMode javaOptimizationMode; - private final Label javaToolchain; // TODO(dmarting): remove when we have rolled out the new behavior private final boolean legacyBazelJavaTest; - + JavaConfiguration(boolean generateJavaDeps, List<String> defaultJvmFlags, JavaOptions javaOptions, Label javaToolchain, String javaCpu, ImmutableList<String> defaultJavaBuilderJvmOpts) @@ -168,6 +170,7 @@ public final class JavaConfiguration extends Fragment { this.javacBootclasspath = javaOptions.javacBootclasspath; this.javacExtdir = javaOptions.javacExtdir; this.javacOpts = ImmutableList.copyOf(javaOptions.javacOpts); + this.proguardBinary = javaOptions.proguard; this.extraProguardSpecs = ImmutableList.copyOf(javaOptions.extraProguardSpecs); this.bundleTranslations = javaOptions.bundleTranslations; this.javaCpu = javaCpu; @@ -299,6 +302,14 @@ public final class JavaConfiguration extends Fragment { } /** + * Returns the label provided with --proguard_top, if any. + */ + @Nullable + public Label getProguardBinary() { + return proguardBinary; + } + + /** * Returns all labels provided with --extra_proguard_specs. */ public List<Label> getExtraProguardSpecs() { @@ -341,7 +352,7 @@ public final class JavaConfiguration extends Fragment { public JavaOptimizationMode getJavaOptimizationMode() { return javaOptimizationMode; } - + /** * Returns true if java_test in Bazel should behave in legacy mode that existed before we * open-sourced our test runner. diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaOptions.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaOptions.java index 2a091cacc9..0a5fcc0455 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaOptions.java +++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaOptions.java @@ -346,6 +346,14 @@ public class JavaOptions extends FragmentOptions { + "The \"launcher\" attribute overrides this flag. ") public Label javaLauncher; + @Option(name = "proguard_top", + defaultValue = "null", + category = "version", + converter = LabelConverter.class, + help = "Specifies which version of ProGuard to use for code removal when building a Java " + + "binary.") + public Label proguard; + @Option(name = "extra_proguard_specs", allowMultiple = true, defaultValue = "", // Ignored diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaSemantics.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaSemantics.java index 741228b4c4..b502cdcc1f 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaSemantics.java +++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaSemantics.java @@ -62,9 +62,12 @@ public interface JavaSemantics { SafeImplicitOutputsFunction JAVA_BINARY_DEPLOY_JAR = fromTemplates("%{name}_deploy.jar"); - + SafeImplicitOutputsFunction JAVA_BINARY_MERGED_JAR = + fromTemplates("%{name}_merged.jar"); SafeImplicitOutputsFunction JAVA_UNSTRIPPED_BINARY_DEPLOY_JAR = fromTemplates("%{name}_deploy.jar.unstripped"); + SafeImplicitOutputsFunction JAVA_BINARY_PROGUARD_MAP = + fromTemplates("%{name}_proguard.map"); SafeImplicitOutputsFunction JAVA_BINARY_DEPLOY_SOURCE_JAR = fromTemplates("%{name}_deploy-src.jar"); @@ -182,6 +185,17 @@ public interface JavaSemantics { } }; + /** + * Implementation for the :proguard attribute. + */ + LateBoundLabel<BuildConfiguration> PROGUARD = + new LateBoundLabel<BuildConfiguration>(JavaConfiguration.class) { + @Override + public Label getDefault(Rule rule, BuildConfiguration configuration) { + return configuration.getFragment(JavaConfiguration.class).getProguardBinary(); + } + }; + LateBoundLabelList<BuildConfiguration> EXTRA_PROGUARD_SPECS = new LateBoundLabelList<BuildConfiguration>() { @Override diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/ProguardHelper.java b/src/main/java/com/google/devtools/build/lib/rules/java/ProguardHelper.java index f752e182a5..98e01d1f09 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/ProguardHelper.java +++ b/src/main/java/com/google/devtools/build/lib/rules/java/ProguardHelper.java @@ -11,7 +11,10 @@ // 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.google.devtools.build.lib.rules.android; +package com.google.devtools.build.lib.rules.java; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.devtools.build.lib.collect.nestedset.Order.NAIVE_LINK_ORDER; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; @@ -28,26 +31,30 @@ import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; import com.google.devtools.build.lib.packages.BuildType; -import com.google.devtools.build.lib.rules.java.JavaConfiguration; import com.google.devtools.build.lib.rules.java.JavaConfiguration.JavaOptimizationMode; -import com.google.devtools.build.lib.rules.java.ProguardSpecProvider; import javax.annotation.Nullable; /** - * Common code for proguarding Android binaries. + * Common code for proguarding Java binaries. */ -public class ProguardHelper { +public abstract class ProguardHelper { - static final String PROGUARD_SPECS = "proguard_specs"; + /** + * Attribute for attaching proguard specs explicitly to a rule, if such an attribute is desired. + */ + public static final String PROGUARD_SPECS = "proguard_specs"; + /** + * Pair summarizing Proguard's output: a Jar file and an optional obfuscation mapping file. + */ @Immutable - static final class ProguardOutput { + public static final class ProguardOutput { private final Artifact outputJar; @Nullable private final Artifact mapping; - ProguardOutput(Artifact outputJar, @Nullable Artifact mapping) { - this.outputJar = outputJar; + public ProguardOutput(Artifact outputJar, @Nullable Artifact mapping) { + this.outputJar = checkNotNull(outputJar); this.mapping = mapping; } @@ -60,9 +67,76 @@ public class ProguardHelper { return mapping; } + /** Adds the output artifacts to the given set builder. */ + public void addAllToSet(NestedSetBuilder<Artifact> filesBuilder) { + filesBuilder.add(outputJar); + if (mapping != null) { + filesBuilder.add(mapping); + } + } + } + + protected ProguardHelper() {} + + /** + * Creates an action to run Proguard to <i>output</i> the given {@code deployJar} artifact + * if --java_optimization_mode calls for it from an assumed input artifact + * {@link JavaSemantics#JAVA_BINARY_MERGED_JAR}. Returns the artifacts that Proguard will + * generate or {@code null} if Proguard isn't used. + * + * <p>If this method returns artifacts then {@link DeployArchiveBuilder} needs to write the + * assumed input artifact (instead of the conventional deploy.jar, which now Proguard writes). + * Do not use this method for binary rules that themselves declare {@link #PROGUARD_SPECS} + * attributes, which includes of 1/2016 {@code android_binary} and {@code android_test}. + */ + @Nullable + public ProguardOutput applyProguardIfRequested(RuleContext ruleContext, Artifact deployJar, + ImmutableList<Artifact> bootclasspath, String mainClassName) throws InterruptedException { + JavaOptimizationMode optMode = getJavaOptimizationMode(ruleContext); + if (optMode == JavaOptimizationMode.NOOP || optMode == JavaOptimizationMode.LEGACY) { + // For simplicity do nothing in LEGACY mode + return null; + } + + Preconditions.checkArgument(bootclasspath.isEmpty(), + "Bootclasspath should be empty b/c not compiling for Android device: %s", bootclasspath); + FilesToRunProvider proguard = findProguard(ruleContext); + if (proguard == null) { + ruleContext.ruleError("--proguard_top required for --java_optimization_mode=" + optMode); + return null; + } + + ImmutableList<Artifact> proguardSpecs = collectProguardSpecs(ruleContext, mainClassName); + Artifact singleJar = + ruleContext.getImplicitOutputArtifact(JavaSemantics.JAVA_BINARY_MERGED_JAR); + return createProguardAction(ruleContext, proguard, singleJar, proguardSpecs, (Artifact) null, + NestedSetBuilder.<Artifact>emptySet(NAIVE_LINK_ORDER), deployJar, + /* mappingRequested */ false); + } + + private ImmutableList<Artifact> collectProguardSpecs( + RuleContext ruleContext, String mainClassName) { + return ProguardHelper.collectTransitiveProguardSpecs(ruleContext, + collectProguardSpecsForRule(ruleContext, mainClassName)); } - private ProguardHelper() {} + /** + * Returns the Proguard binary to invoke when using {@link #applyProguardIfRequested}. Returning + * {@code null} from this method will generate an error in that method. + * + * @return Proguard binary or {@code null} if none is available + */ + @Nullable + protected abstract FilesToRunProvider findProguard(RuleContext ruleContext); + + /** + * Returns rule-specific proguard specs not captured by {@link #PROGUARD_SPECS} attributes when + * using {@link #applyProguardIfRequested}. Typically these are generated artifacts such as specs + * generated for android resources. This method is only called if Proguard will definitely used, + * so it's ok to generate files here. + */ + protected abstract ImmutableList<Artifact> collectProguardSpecsForRule( + RuleContext ruleContext, String mainClassName); /** * Retrieves the full set of proguard specs that should be applied to this binary, including the @@ -72,10 +146,12 @@ public class ProguardHelper { * * <p>If Proguard shouldn't be applied, or the legacy link mode is used and there are no * proguard_specs on this rule, an empty list will be returned, regardless of any given specs or - * specs from dependencies. {@link AndroidBinary#createAndroidBinary} relies on that behavior. + * specs from dependencies. + * {@link com.google.devtools.build.lib.rules.android.AndroidBinary#createAndroidBinary} relies on + * that behavior. */ public static ImmutableList<Artifact> collectTransitiveProguardSpecs(RuleContext ruleContext, - Artifact... specsToInclude) { + Iterable<Artifact> specsToInclude) { JavaOptimizationMode optMode = getJavaOptimizationMode(ruleContext); if (optMode == JavaOptimizationMode.NOOP) { return ImmutableList.of(); @@ -94,7 +170,7 @@ public class ProguardHelper { ImmutableSortedSet.Builder<Artifact> builder = ImmutableSortedSet.orderedBy(Artifact.EXEC_PATH_COMPARATOR) .addAll(proguardSpecs) - .add(specsToInclude) + .addAll(specsToInclude) .addAll(ruleContext .getPrerequisiteArtifacts(":extra_proguard_specs", Mode.TARGET) .list()); @@ -120,6 +196,31 @@ public class ProguardHelper { } /** + * Creates a proguard spec that tells proguard to use the JDK's rt.jar as a library jar, similar + * to how android_binary would give Android SDK's android.jar to Proguard as library jar, and + * to keep the binary's entry point, ie., the main() method to be invoked. + */ + protected static Artifact generateSpecForJavaBinary(RuleContext ruleContext, + String mainClassName) { + // Add -libraryjars <java.home>/lib/rt.jar so Proguard uses JDK bootclasspath, which JavaCommon + // doesn't expose when building for JDK (see checkArgument in applyProguardIfRequested). + // Note <java.home>/lib/rt.jar refers to rt.jar that comes with JVM running Proguard, which + // should be identical to the JVM that will run the binary. + Artifact result = ProguardHelper.getProguardConfigArtifact(ruleContext, "jvm"); + ruleContext.registerAction( + new FileWriteAction( + ruleContext.getActionOwner(), + result, + String.format("-libraryjars <java.home>/lib/rt.jar%n" + + "-keep class %s {%n" + + " public static void main(java.lang.String[]);%n" + + "}", + mainClassName), + /*executable*/ false)); + return result; + } + + /** * Creates an action to run Proguard over the given {@code programJar} with various other given * inputs to produce {@code proguardOutputJar}. If requested explicitly, or implicitly with * --java_optimization_mode, the action also produces a mapping file (which shows what methods and @@ -134,7 +235,6 @@ public class ProguardHelper { * @param libraryJars any other Jar files that the {@code programJar} will run against * @param mappingRequested whether to ask Proguard to output a mapping file (a mapping will be * produced anyway if --java_optimization_mode includes obfuscation) - * @param filesBuilder all artifacts produced by this rule will be added to this builder */ public static ProguardOutput createProguardAction(RuleContext ruleContext, FilesToRunProvider proguard, @@ -143,8 +243,7 @@ public class ProguardHelper { @Nullable Artifact proguardMapping, NestedSet<Artifact> libraryJars, Artifact proguardOutputJar, - boolean mappingRequested, - NestedSetBuilder<Artifact> filesBuilder) throws InterruptedException { + boolean mappingRequested) throws InterruptedException { JavaOptimizationMode optMode = getJavaOptimizationMode(ruleContext); Preconditions.checkArgument(optMode != JavaOptimizationMode.NOOP); Preconditions.checkArgument(optMode != JavaOptimizationMode.LEGACY || !proguardSpecs.isEmpty()); @@ -165,8 +264,6 @@ public class ProguardHelper { .addArgument(libraryJar.getExecPathString()); } - filesBuilder.add(proguardOutputJar); - if (proguardMapping != null) { builder.addInput(proguardMapping) .addArgument("-applymapping") @@ -184,13 +281,12 @@ public class ProguardHelper { if (mappingRequested || optMode.alwaysGenerateOutputMapping()) { // TODO(bazel-team): Verify that proguard spec files don't contain -printmapping directions // which this -printmapping command line flag will override. - proguardOutputMap = ruleContext.getImplicitOutputArtifact( - AndroidRuleClasses.ANDROID_BINARY_PROGUARD_MAP); + proguardOutputMap = + ruleContext.getImplicitOutputArtifact(JavaSemantics.JAVA_BINARY_PROGUARD_MAP); builder.addOutput(proguardOutputMap) .addArgument("-printmapping") .addArgument(proguardOutputMap.getExecPathString()); - filesBuilder.add(proguardOutputMap); } ruleContext.registerAction(builder.build(ruleContext)); @@ -209,7 +305,10 @@ public class ProguardHelper { ruleContext.getBinOrGenfilesDirectory())); } - private static JavaOptimizationMode getJavaOptimizationMode(RuleContext ruleContext) { + /** + * Returns {@link JavaConfiguration#getJavaOptimizationMode()}. + */ + public static JavaOptimizationMode getJavaOptimizationMode(RuleContext ruleContext) { return ruleContext.getConfiguration().getFragment(JavaConfiguration.class) .getJavaOptimizationMode(); } |