| // Copyright 2015 The Bazel Authors. All rights reserved. |
| // |
| // 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.google.devtools.build.lib.rules.java; |
| |
| import static com.google.common.base.Preconditions.checkNotNull; |
| import static com.google.common.base.Preconditions.checkState; |
| |
| import com.google.common.base.Ascii; |
| import com.google.common.base.Joiner; |
| import com.google.common.base.Optional; |
| import com.google.common.base.Preconditions; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableSortedSet; |
| import com.google.common.collect.Iterables; |
| import com.google.devtools.build.lib.actions.Artifact; |
| import com.google.devtools.build.lib.analysis.FilesToRunProvider; |
| import com.google.devtools.build.lib.analysis.RuleContext; |
| import com.google.devtools.build.lib.analysis.TransitiveInfoCollection; |
| import com.google.devtools.build.lib.analysis.actions.ActionConstructionContext; |
| import com.google.devtools.build.lib.analysis.actions.CustomCommandLine; |
| import com.google.devtools.build.lib.analysis.actions.FileWriteAction; |
| import com.google.devtools.build.lib.analysis.actions.SpawnAction; |
| import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode; |
| import com.google.devtools.build.lib.cmdline.Label; |
| 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.AttributeMap; |
| import com.google.devtools.build.lib.packages.BuildType; |
| import com.google.devtools.build.lib.rules.java.JavaConfiguration.JavaOptimizationMode; |
| import com.google.devtools.build.lib.syntax.Type; |
| import java.util.Map; |
| import javax.annotation.Nullable; |
| |
| /** Common code for proguarding Java binaries. */ |
| public abstract class ProguardHelper { |
| |
| /** |
| * Attribute for attaching proguard specs explicitly to a rule, if such an attribute is desired. |
| */ |
| public static final String PROGUARD_SPECS = "proguard_specs"; |
| |
| /** A class collecting Proguard output artifacts. */ |
| @Immutable |
| public static final class ProguardOutput { |
| private final Artifact outputJar; |
| @Nullable private final Artifact mapping; |
| @Nullable private final Artifact protoMapping; |
| @Nullable private final Artifact seeds; |
| @Nullable private final Artifact usage; |
| @Nullable private final Artifact constantStringObfuscatedMapping; |
| private final Artifact config; |
| |
| public ProguardOutput( |
| Artifact outputJar, |
| @Nullable Artifact mapping, |
| @Nullable Artifact protoMapping, |
| @Nullable Artifact seeds, |
| @Nullable Artifact usage, |
| @Nullable Artifact constantStringObfuscatedMapping, |
| Artifact config) { |
| this.outputJar = checkNotNull(outputJar); |
| this.mapping = mapping; |
| this.protoMapping = protoMapping; |
| this.seeds = seeds; |
| this.usage = usage; |
| this.constantStringObfuscatedMapping = constantStringObfuscatedMapping; |
| this.config = config; |
| } |
| |
| public Artifact getOutputJar() { |
| return outputJar; |
| } |
| |
| @Nullable |
| public Artifact getMapping() { |
| return mapping; |
| } |
| |
| @Nullable |
| public Artifact getProtoMapping() { |
| return protoMapping; |
| } |
| |
| @Nullable |
| public Artifact getConstantStringObfuscatedMapping() { |
| return constantStringObfuscatedMapping; |
| } |
| |
| @Nullable |
| public Artifact getSeeds() { |
| return seeds; |
| } |
| |
| @Nullable |
| public Artifact getUsage() { |
| return usage; |
| } |
| |
| public Artifact getConfig() { |
| return config; |
| } |
| |
| /** Adds the output artifacts to the given set builder. */ |
| public void addAllToSet(NestedSetBuilder<Artifact> filesBuilder) { |
| addAllToSet(filesBuilder, null); |
| } |
| |
| /** |
| * Adds the output artifacts to the given set builder. If the progaurd map was updated then add |
| * the updated map instead of the original proguard output map |
| */ |
| public void addAllToSet(NestedSetBuilder<Artifact> filesBuilder, Artifact finalProguardMap) { |
| filesBuilder.add(outputJar); |
| if (protoMapping != null) { |
| filesBuilder.add(protoMapping); |
| } |
| if (constantStringObfuscatedMapping != null) { |
| filesBuilder.add(constantStringObfuscatedMapping); |
| } |
| if (seeds != null) { |
| filesBuilder.add(seeds); |
| } |
| if (usage != null) { |
| filesBuilder.add(usage); |
| } |
| if (config != null) { |
| filesBuilder.add(config); |
| } |
| if (finalProguardMap != null) { |
| filesBuilder.add(finalProguardMap); |
| } else 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 |
| * com.google.devtools.build.lib.rules.java.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 as of |
| * includes 1/2016 {@code android_binary} and {@code android_test}. |
| */ |
| @Nullable |
| public ProguardOutput applyProguardIfRequested( |
| RuleContext ruleContext, |
| Artifact deployJar, |
| ImmutableList<Artifact> bootclasspath, |
| String mainClassName, |
| JavaSemantics semantics) |
| 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 not be empty"); |
| FilesToRunProvider proguard = findProguard(ruleContext); |
| if (proguard == null) { |
| ruleContext.ruleError("--proguard_top required for --java_optimization_mode=" + optMode); |
| return null; |
| } |
| |
| ImmutableList<Artifact> proguardSpecs = |
| collectProguardSpecs(ruleContext, bootclasspath, mainClassName); |
| Artifact singleJar = |
| ruleContext.getImplicitOutputArtifact(JavaSemantics.JAVA_BINARY_MERGED_JAR); |
| |
| // TODO(bazel-team): Verify that proguard spec files don't contain -printmapping directions |
| // which this -printmapping command line flag will override. |
| Artifact proguardOutputMap = null; |
| if (genProguardMapping(ruleContext.attributes()) || optMode.alwaysGenerateOutputMapping()) { |
| proguardOutputMap = |
| ruleContext.getImplicitOutputArtifact(JavaSemantics.JAVA_BINARY_PROGUARD_MAP); |
| } |
| return createOptimizationActions( |
| ruleContext, |
| proguard, |
| singleJar, |
| proguardSpecs, |
| /* proguardSeeds */ (Artifact) null, |
| /* proguardUsage */ (Artifact) null, |
| /* proguardMapping */ (Artifact) null, |
| /* proguardDictionary */ (Artifact) null, |
| bootclasspath, |
| deployJar, |
| semantics, |
| /* optimizationPases */ 3, |
| proguardOutputMap); |
| } |
| |
| private ImmutableList<Artifact> collectProguardSpecs( |
| RuleContext ruleContext, ImmutableList<Artifact> bootclasspath, String mainClassName) { |
| return ProguardHelper.collectTransitiveProguardSpecs( |
| ruleContext, collectProguardSpecsForRule(ruleContext, bootclasspath, mainClassName)); |
| } |
| |
| /** |
| * 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, ImmutableList<Artifact> bootclasspath, String mainClassName); |
| |
| /** |
| * Retrieves the full set of proguard specs that should be applied to this binary, including the |
| * specs passed in, if Proguard should run on the given rule. {@link #createProguardAction} relies |
| * on this method returning an empty list if the given rule doesn't declare specs in |
| * --java_optimization_mode=legacy. |
| * |
| * <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 |
| * com.google.devtools.build.lib.rules.android.AndroidBinary#createAndroidBinary} relies on that |
| * behavior. |
| */ |
| public static ImmutableList<Artifact> collectTransitiveProguardSpecs( |
| RuleContext ruleContext, Iterable<Artifact> specsToInclude) { |
| return collectTransitiveProguardSpecs( |
| ruleContext, |
| Iterables.concat( |
| specsToInclude, |
| ruleContext.getPrerequisiteArtifacts(":extra_proguard_specs", Mode.TARGET).list()), |
| ruleContext.attributes().has(PROGUARD_SPECS, BuildType.LABEL_LIST) |
| ? ruleContext.getPrerequisiteArtifacts(PROGUARD_SPECS, Mode.TARGET).list() |
| : ImmutableList.<Artifact>of(), |
| ruleContext.getPrerequisites("deps", Mode.TARGET, ProguardSpecProvider.PROVIDER)); |
| } |
| |
| /** |
| * Retrieves the full set of proguard specs that should be applied to this binary, including the |
| * specs passed in, if Proguard should run on the given rule. |
| * |
| * <p>Unlike {@link #collectTransitiveProguardSpecs(RuleContext, Iterable)}, this method requires |
| * values to be passed in explicitly, and does not extract them from rule attributes. |
| * |
| * <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 |
| * com.google.devtools.build.lib.rules.android.AndroidBinary#createAndroidBinary} relies on that |
| * behavior. |
| */ |
| public static ImmutableList<Artifact> collectTransitiveProguardSpecs( |
| RuleContext context, |
| Iterable<Artifact> specsToInclude, |
| ImmutableList<Artifact> localProguardSpecs, |
| Iterable<ProguardSpecProvider> proguardDeps) { |
| return collectTransitiveProguardSpecs( |
| context.getLabel(), context, specsToInclude, localProguardSpecs, proguardDeps); |
| } |
| /** |
| * Retrieves the full set of proguard specs that should be applied to this binary, including the |
| * specs passed in, if Proguard should run on the given rule. |
| * |
| * <p>Unlike {@link #collectTransitiveProguardSpecs(RuleContext, Iterable)}, this method requires |
| * values to be passed in explicitly, and does not extract them from rule attributes. |
| * |
| * <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 |
| * com.google.devtools.build.lib.rules.android.AndroidBinary#createAndroidBinary} relies on that |
| * behavior. |
| */ |
| public static ImmutableList<Artifact> collectTransitiveProguardSpecs( |
| Label label, |
| ActionConstructionContext context, |
| Iterable<Artifact> specsToInclude, |
| ImmutableList<Artifact> localProguardSpecs, |
| Iterable<ProguardSpecProvider> proguardDeps) { |
| JavaOptimizationMode optMode = getJavaOptimizationMode(context); |
| if (optMode == JavaOptimizationMode.NOOP) { |
| return ImmutableList.of(); |
| } |
| |
| if (optMode == JavaOptimizationMode.LEGACY && localProguardSpecs.isEmpty()) { |
| return ImmutableList.of(); |
| } |
| |
| // TODO(bazel-team): In modes except LEGACY verify that proguard specs don't include -dont... |
| // flags since those flags would override the desired optMode |
| ImmutableSortedSet.Builder<Artifact> builder = |
| ImmutableSortedSet.orderedBy(Artifact.EXEC_PATH_COMPARATOR) |
| .addAll(localProguardSpecs) |
| .addAll(specsToInclude); |
| for (ProguardSpecProvider dep : proguardDeps) { |
| builder.addAll(dep.getTransitiveProguardSpecs()); |
| } |
| |
| // Generate and include implicit Proguard spec for requested mode. |
| if (!optMode.getImplicitProguardDirectives().isEmpty()) { |
| Artifact implicitDirectives = |
| getProguardConfigArtifact(label, context, Ascii.toLowerCase(optMode.name())); |
| context.registerAction( |
| FileWriteAction.create( |
| context, |
| implicitDirectives, |
| optMode.getImplicitProguardDirectives(), |
| /*makeExecutable=*/ false)); |
| builder.add(implicitDirectives); |
| } |
| |
| return builder.build().asList(); |
| } |
| |
| /** |
| * Creates a proguard spec that tells proguard to keep the binary's entry point, ie., the {@code |
| * main()} method to be invoked. |
| */ |
| protected static Artifact generateSpecForJavaBinary( |
| RuleContext ruleContext, String mainClassName) { |
| Artifact result = ProguardHelper.getProguardConfigArtifact(ruleContext, "jvm"); |
| ruleContext.registerAction( |
| FileWriteAction.create( |
| ruleContext, |
| result, |
| String.format( |
| "-keep class %s {%n public static void main(java.lang.String[]);%n}", |
| mainClassName), |
| /*makeExecutable=*/ false)); |
| return result; |
| } |
| |
| /** @return true if proguard_generate_mapping is specified. */ |
| public static final boolean genProguardMapping(AttributeMap rule) { |
| return rule.has("proguard_generate_mapping", Type.BOOLEAN) |
| && rule.get("proguard_generate_mapping", Type.BOOLEAN); |
| } |
| |
| public static final boolean genObfuscatedConstantStringMap(AttributeMap rule) { |
| return rule.has("proguard_generate_obfuscated_constant_string_mapping", Type.BOOLEAN) |
| && rule.get("proguard_generate_obfuscated_constant_string_mapping", Type.BOOLEAN); |
| } |
| |
| public static ProguardOutput getProguardOutputs( |
| Artifact outputJar, |
| @Nullable Artifact proguardSeeds, |
| @Nullable Artifact proguardUsage, |
| RuleContext ruleContext, |
| JavaSemantics semantics, |
| @Nullable Artifact proguardOutputMap) |
| throws InterruptedException { |
| JavaOptimizationMode optMode = getJavaOptimizationMode(ruleContext); |
| boolean mappingRequested = genProguardMapping(ruleContext.attributes()); |
| |
| Artifact proguardOutputProtoMap = null; |
| Artifact proguardConstantStringMap = null; |
| |
| if (mappingRequested || optMode.alwaysGenerateOutputMapping()) { |
| // TODO(bazel-team): if rex is enabled, the proguard map will change and then will no |
| // longer correspond to the proto map |
| proguardOutputProtoMap = semantics.getProtoMapping(ruleContext); |
| } |
| |
| if (genObfuscatedConstantStringMap(ruleContext.attributes())) { |
| proguardConstantStringMap = semantics.getObfuscatedConstantStringMap(ruleContext); |
| } |
| |
| Artifact proguardConfigOutput = |
| ruleContext.getImplicitOutputArtifact(JavaSemantics.JAVA_BINARY_PROGUARD_CONFIG); |
| |
| return new ProguardOutput( |
| outputJar, |
| proguardOutputMap, |
| proguardOutputProtoMap, |
| proguardSeeds, |
| proguardUsage, |
| proguardConstantStringMap, |
| proguardConfigOutput); |
| } |
| |
| /** |
| * 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 |
| * classes in the output Jar correspond to which methods and classes in the input). The "pair" |
| * returned by this method indicates whether a mapping is being produced. |
| * |
| * <p>See the Proguard manual for the meaning of the various artifacts in play. |
| * |
| * @param proguard Proguard executable to use |
| * @param proguardSpecs Proguard specification files to pass to Proguard |
| * @param proguardMapping optional mapping file for Proguard to apply |
| * @param proguardDictionary Optional dictionary file for Proguard to apply |
| * @param libraryJars any other Jar files that the {@code programJar} will run against |
| * @param optimizationPasses if not null specifies to break proguard up into multiple passes with |
| * the given number of optimization passes. |
| * @param proguardOutputMap mapping generated by Proguard if requested. could be null. |
| */ |
| public static ProguardOutput createOptimizationActions( |
| RuleContext ruleContext, |
| FilesToRunProvider proguard, |
| Artifact programJar, |
| ImmutableList<Artifact> proguardSpecs, |
| @Nullable Artifact proguardSeeds, |
| @Nullable Artifact proguardUsage, |
| @Nullable Artifact proguardMapping, |
| @Nullable Artifact proguardDictionary, |
| Iterable<Artifact> libraryJars, |
| Artifact proguardOutputJar, |
| JavaSemantics semantics, |
| @Nullable Integer optimizationPasses, |
| @Nullable Artifact proguardOutputMap) |
| throws InterruptedException { |
| JavaOptimizationMode optMode = getJavaOptimizationMode(ruleContext); |
| Preconditions.checkArgument(optMode != JavaOptimizationMode.NOOP); |
| Preconditions.checkArgument(optMode != JavaOptimizationMode.LEGACY || !proguardSpecs.isEmpty()); |
| |
| ProguardOutput output = |
| getProguardOutputs( |
| proguardOutputJar, |
| proguardSeeds, |
| proguardUsage, |
| ruleContext, |
| semantics, |
| proguardOutputMap); |
| |
| if (Iterables.size(libraryJars) > 1) { |
| JavaTargetAttributes attributes = new JavaTargetAttributes.Builder(semantics).build(); |
| Artifact combinedLibraryJar = |
| getProguardTempArtifact( |
| ruleContext, Ascii.toLowerCase(optMode.name()), "combined_library_jars.jar"); |
| new DeployArchiveBuilder(semantics, ruleContext) |
| .setOutputJar(combinedLibraryJar) |
| .setAttributes(attributes) |
| .addRuntimeJars(libraryJars) |
| .build(); |
| libraryJars = ImmutableList.of(combinedLibraryJar); |
| } |
| |
| if (optimizationPasses == null) { |
| // Run proguard as a single step. |
| SpawnAction.Builder proguardAction = new SpawnAction.Builder(); |
| CustomCommandLine.Builder commandLine = CustomCommandLine.builder(); |
| defaultAction( |
| proguardAction, |
| commandLine, |
| proguard, |
| programJar, |
| proguardSpecs, |
| proguardMapping, |
| proguardDictionary, |
| libraryJars, |
| output.getOutputJar(), |
| output.getMapping(), |
| output.getProtoMapping(), |
| output.getSeeds(), |
| output.getUsage(), |
| output.getConstantStringObfuscatedMapping(), |
| output.getConfig()); |
| proguardAction |
| .setProgressMessage("Trimming binary with Proguard") |
| .addOutput(proguardOutputJar); |
| proguardAction.addCommandLine(commandLine.build()); |
| ruleContext.registerAction(proguardAction.build(ruleContext)); |
| } else { |
| // Optimization passes have been specified, so run proguard in multiple phases. |
| Artifact lastStageOutput = |
| getProguardTempArtifact( |
| ruleContext, Ascii.toLowerCase(optMode.name()), "proguard_preoptimization.jar"); |
| SpawnAction.Builder initialAction = new SpawnAction.Builder(); |
| CustomCommandLine.Builder initialCommandLine = CustomCommandLine.builder(); |
| defaultAction( |
| initialAction, |
| initialCommandLine, |
| proguard, |
| programJar, |
| proguardSpecs, |
| proguardMapping, |
| proguardDictionary, |
| libraryJars, |
| output.getOutputJar(), |
| /* proguardOutputMap */ null, |
| /* proguardOutputProtoMap */ null, |
| output.getSeeds(), // ProGuard only prints seeds during INITIAL and NORMAL runtypes. |
| /* proguardUsage */ null, |
| /* constantStringObfuscatedMapping */ null, |
| /* proguardConfigOutput */ null); |
| initialAction |
| .setProgressMessage("Trimming binary with Proguard: Verification/Shrinking Pass") |
| .addOutput(lastStageOutput); |
| initialCommandLine.add("-runtype INITIAL").addExecPath("-nextstageoutput", lastStageOutput); |
| initialAction.addCommandLine(initialCommandLine.build()); |
| ruleContext.registerAction(initialAction.build(ruleContext)); |
| |
| for (int i = 1; i <= optimizationPasses; i++) { |
| // Run configured optimizers in order in each pass |
| for (Map.Entry<String, Optional<Label>> optimizer : |
| getBytecodeOptimizers(ruleContext).entrySet()) { |
| String mnemonic = optimizer.getKey(); |
| Optional<Label> target = optimizer.getValue(); |
| FilesToRunProvider executable = null; |
| if (target.isPresent()) { |
| for (TransitiveInfoCollection optimizerDep : |
| ruleContext.getPrerequisites(":bytecode_optimizers", Mode.HOST)) { |
| if (optimizerDep.getLabel().equals(target.get())) { |
| executable = optimizerDep.getProvider(FilesToRunProvider.class); |
| break; |
| } |
| } |
| } else { |
| checkState("Proguard".equals(mnemonic), "Need label to run %s", mnemonic); |
| executable = proguard; |
| } |
| Artifact optimizationOutput = |
| getProguardTempArtifact( |
| ruleContext, |
| Ascii.toLowerCase(optMode.name()), |
| mnemonic + "_optimization_" + i + ".jar"); |
| SpawnAction.Builder optimizationAction = new SpawnAction.Builder(); |
| CustomCommandLine.Builder optimizationCommandLine = CustomCommandLine.builder(); |
| defaultAction( |
| optimizationAction, |
| optimizationCommandLine, |
| checkNotNull(executable, "couldn't find optimizer %s", optimizer), |
| programJar, |
| proguardSpecs, |
| proguardMapping, |
| proguardDictionary, |
| libraryJars, |
| output.getOutputJar(), |
| /* proguardOutputMap */ null, |
| /* proguardOutputProtoMap */ null, |
| /* proguardSeeds */ null, |
| /* proguardUsage */ null, |
| /* constantStringObfuscatedMapping */ null, |
| /* proguardConfigOutput */ null); |
| optimizationAction |
| .setProgressMessage("Trimming binary with %s: Optimization Pass %d", mnemonic, +i) |
| .setMnemonic(mnemonic) |
| .addInput(lastStageOutput) |
| .addOutput(optimizationOutput); |
| optimizationCommandLine |
| .add("-runtype OPTIMIZATION") |
| .addExecPath("-laststageoutput", lastStageOutput) |
| .addExecPath("-nextstageoutput", optimizationOutput); |
| optimizationAction.addCommandLine(optimizationCommandLine.build()); |
| ruleContext.registerAction(optimizationAction.build(ruleContext)); |
| lastStageOutput = optimizationOutput; |
| } |
| } |
| |
| SpawnAction.Builder finalAction = new SpawnAction.Builder(); |
| CustomCommandLine.Builder finalCommandLine = CustomCommandLine.builder(); |
| defaultAction( |
| finalAction, |
| finalCommandLine, |
| proguard, |
| programJar, |
| proguardSpecs, |
| proguardMapping, |
| proguardDictionary, |
| libraryJars, |
| output.getOutputJar(), |
| output.getMapping(), |
| output.getProtoMapping(), |
| /* proguardSeeds */ null, // runtype FINAL does not produce seeds. |
| output.getUsage(), |
| output.getConstantStringObfuscatedMapping(), |
| output.getConfig()); |
| finalAction |
| .setProgressMessage("Trimming binary with Proguard: Obfuscation and Final Output Pass") |
| .addInput(lastStageOutput) |
| .addOutput(proguardOutputJar); |
| finalCommandLine.add("-runtype FINAL").addExecPath("-laststageoutput", lastStageOutput); |
| finalAction.addCommandLine(finalCommandLine.build()); |
| ruleContext.registerAction(finalAction.build(ruleContext)); |
| } |
| |
| return output; |
| } |
| |
| private static void defaultAction( |
| SpawnAction.Builder builder, |
| CustomCommandLine.Builder commandLine, |
| FilesToRunProvider executable, |
| Artifact programJar, |
| ImmutableList<Artifact> proguardSpecs, |
| @Nullable Artifact proguardMapping, |
| @Nullable Artifact proguardDictionary, |
| Iterable<Artifact> libraryJars, |
| Artifact proguardOutputJar, |
| @Nullable Artifact proguardOutputMap, |
| @Nullable Artifact proguardOutputProtoMap, |
| @Nullable Artifact proguardSeeds, |
| @Nullable Artifact proguardUsage, |
| @Nullable Artifact constantStringObfuscatedMapping, |
| @Nullable Artifact proguardConfigOutput) { |
| |
| builder |
| .addInputs(libraryJars) |
| .addInputs(proguardSpecs) |
| .setExecutable(executable) |
| .setMnemonic("Proguard") |
| .addInput(programJar); |
| |
| commandLine |
| .add("-forceprocessing") |
| .addExecPath("-injars", programJar) |
| // This is handled by the build system there is no need for proguard to check if things are |
| // up to date. |
| .add("-outjars") |
| // Don't register the output jar as an output of the action, because multiple proguard |
| // actions will be created for optimization runs which will overwrite the jar, and only |
| // the final proguard action will declare the output jar as an output. |
| .addExecPath(proguardOutputJar); |
| |
| for (Artifact libraryJar : libraryJars) { |
| commandLine.addExecPath("-libraryjars", libraryJar); |
| } |
| |
| if (proguardMapping != null) { |
| builder.addInput(proguardMapping); |
| commandLine.addExecPath("-applymapping", proguardMapping); |
| } |
| |
| if (proguardDictionary != null) { |
| builder.addInput(proguardDictionary); |
| commandLine |
| .addExecPath("-obfuscationdictionary", proguardDictionary) |
| .addExecPath("-classobfuscationdictionary", proguardDictionary) |
| .addExecPath("-packageobfuscationdictionary", proguardDictionary); |
| } |
| |
| for (Artifact proguardSpec : proguardSpecs) { |
| commandLine.addPrefixedExecPath("@", proguardSpec); |
| } |
| |
| if (proguardOutputMap != null) { |
| builder.addOutput(proguardOutputMap); |
| commandLine.addExecPath("-printmapping", proguardOutputMap); |
| } |
| |
| if (proguardOutputProtoMap != null) { |
| builder.addOutput(proguardOutputProtoMap); |
| commandLine.addExecPath("-protomapping", proguardOutputProtoMap); |
| } |
| |
| if (constantStringObfuscatedMapping != null) { |
| builder.addOutput(constantStringObfuscatedMapping); |
| commandLine.addExecPath( |
| "-obfuscatedconstantstringoutputfile", constantStringObfuscatedMapping); |
| } |
| |
| if (proguardSeeds != null) { |
| builder.addOutput(proguardSeeds); |
| commandLine.addExecPath("-printseeds", proguardSeeds); |
| } |
| |
| if (proguardUsage != null) { |
| builder.addOutput(proguardUsage); |
| commandLine.addExecPath("-printusage", proguardUsage); |
| } |
| |
| if (proguardConfigOutput != null) { |
| builder.addOutput(proguardConfigOutput); |
| commandLine.addExecPath("-printconfiguration", proguardConfigOutput); |
| } |
| } |
| |
| /** Returns an intermediate artifact used to run Proguard. */ |
| public static Artifact getProguardTempArtifact( |
| RuleContext ruleContext, String prefix, String name) { |
| return getProguardTempArtifact(ruleContext.getLabel(), ruleContext, prefix, name); |
| } |
| |
| /** Returns an intermediate artifact used to run Proguard. */ |
| public static Artifact getProguardTempArtifact( |
| Label label, ActionConstructionContext context, String prefix, String name) { |
| // TODO(bazel-team): Remove the redundant inclusion of the rule name, as getUniqueDirectory |
| // includes the rulename as well. |
| return context.getUniqueDirectoryArtifact( |
| "proguard", Joiner.on("_").join(prefix, label.getName(), name)); |
| } |
| |
| public static Artifact getProguardConfigArtifact(RuleContext ruleContext, String prefix) { |
| return getProguardConfigArtifact(ruleContext.getLabel(), ruleContext, prefix); |
| } |
| |
| public static Artifact getProguardConfigArtifact( |
| Label label, ActionConstructionContext context, String prefix) { |
| return getProguardTempArtifact(label, context, prefix, "proguard.cfg"); |
| } |
| |
| /** Returns {@link JavaConfiguration#getJavaOptimizationMode()}. */ |
| public static JavaOptimizationMode getJavaOptimizationMode(ActionConstructionContext context) { |
| return context |
| .getConfiguration() |
| .getFragment(JavaConfiguration.class) |
| .getJavaOptimizationMode(); |
| } |
| |
| private static Map<String, Optional<Label>> getBytecodeOptimizers(RuleContext ruleContext) { |
| return ruleContext |
| .getConfiguration() |
| .getFragment(JavaConfiguration.class) |
| .getBytecodeOptimizers(); |
| } |
| } |