diff options
author | Andrew Pellegrini <apell@google.com> | 2016-03-07 20:02:36 +0000 |
---|---|---|
committer | Damien Martin-Guillerez <dmarting@google.com> | 2016-03-08 03:45:14 +0000 |
commit | 52048e36215a70028ca355808f9ee36e2a9ca986 (patch) | |
tree | fb301a69cd414b06d56a03b4cb641832f1514ffb /src | |
parent | 4b1999792537270b46d0750ff8b4155b10b0320c (diff) |
Adds ResourceShrinkerAction to android_binary targets if they use Proguard and specify --experimental_android_resource_shrinking on the command line.
RELNOTES: Specifying --experimental_android_resource_shrinking on the command line will enable a resource shrinking pass on android_binary targets that already use Proguard.
--
MOS_MIGRATED_REVID=116572863
Diffstat (limited to 'src')
13 files changed, 393 insertions, 37 deletions
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 7fa4575bc9..42ddaf11be 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 @@ -193,8 +193,9 @@ public abstract class AndroidBinary implements RuleConfiguredTargetFactory { resourceApk = applicationManifest.packWithDataAndResources( ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_RESOURCES_APK), ruleContext, + false, /* isLibrary */ resourceDeps, - null, /* Artifact rTxt */ + ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_R_TXT), null, /* Artifact symbolsTxt */ ruleContext.getTokenizedStringListAttr("resource_configuration_filters"), ruleContext.getTokenizedStringListAttr("nocompress_extensions"), @@ -204,7 +205,8 @@ public abstract class AndroidBinary implements RuleConfiguredTargetFactory { getExpandedMakeVarsForAttr(ruleContext, "version_name"), false, /* incremental */ ProguardHelper.getProguardConfigArtifact(ruleContext, ""), - null /* manifestOut */); + null, /* manifestOut */ + ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_RESOURCES_ZIP)); if (ruleContext.hasErrors()) { return null; } @@ -212,6 +214,7 @@ public abstract class AndroidBinary implements RuleConfiguredTargetFactory { .packWithDataAndResources(ruleContext .getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_INCREMENTAL_RESOURCES_APK), ruleContext, + false, /* isLibrary */ resourceDeps, null, /* Artifact rTxt */ null, /* Artifact symbolsTxt */ @@ -223,7 +226,8 @@ public abstract class AndroidBinary implements RuleConfiguredTargetFactory { getExpandedMakeVarsForAttr(ruleContext, "version_name"), true, /* incremental */ ProguardHelper.getProguardConfigArtifact(ruleContext, "incremental"), - null /* manifestOut */); + null, /* manifestOut */ + null /* mergedResourcesOut */); if (ruleContext.hasErrors()) { return null; } @@ -231,6 +235,7 @@ public abstract class AndroidBinary implements RuleConfiguredTargetFactory { .createSplitManifest(ruleContext, "android_resources", false) .packWithDataAndResources(getDxArtifact(ruleContext, "android_resources.ap_"), ruleContext, + false, /* isLibrary */ resourceDeps, null, /* Artifact rTxt */ null, /* Artifact symbolsTxt */ @@ -242,7 +247,8 @@ public abstract class AndroidBinary implements RuleConfiguredTargetFactory { getExpandedMakeVarsForAttr(ruleContext, "version_name"), true, ProguardHelper.getProguardConfigArtifact(ruleContext, "incremental_split"), - null /* manifestOut */); + null, /* manifestOut */ + null /* mergedResourcesOut */); if (ruleContext.hasErrors()) { return null; } @@ -347,6 +353,12 @@ public abstract class AndroidBinary implements RuleConfiguredTargetFactory { ImmutableList<Artifact> proguardSpecs = ProguardHelper.collectTransitiveProguardSpecs( ruleContext, ImmutableList.of(resourceApk.getResourceProguardConfig())); + Artifact resourceApkArtifact = shrinkResources( + ruleContext, + androidCommon, + resourceApk, + deployJar, + proguardSpecs); ProguardOutput proguardOutput = applyProguard( ruleContext, @@ -378,7 +390,7 @@ public abstract class AndroidBinary implements RuleConfiguredTargetFactory { ApkActionBuilder apkBuilder = new ApkActionBuilder(ruleContext, androidSemantics) .classesDex(dexingOutput.classesDexZip) - .resourceApk(resourceApk.getArtifact()) + .resourceApk(resourceApkArtifact) .javaResourceZip(dexingOutput.javaResourceJar) .nativeLibs(nativeLibs); @@ -840,6 +852,76 @@ public abstract class AndroidBinary implements RuleConfiguredTargetFactory { return new ProguardOutput(deployJarArtifact, null); } + private static Artifact shrinkResources( + RuleContext ruleContext, + AndroidCommon androidCommon, + ResourceApk resourceApk, + Artifact deployJar, + ImmutableList<Artifact> proguardSpecs) throws InterruptedException { + + if (ruleContext.getFragment(AndroidConfiguration.class).useAndroidResourceShrinking() + && LocalResourceContainer.definesAndroidResources(ruleContext.attributes()) + && !proguardSpecs.isEmpty()) { + + // TODO(apell): Once ProGuard is split into multiple runs, use the Artifact from the shrinking + // pass here instead. + Artifact shrunkJar = ruleContext.getImplicitOutputArtifact( + AndroidRuleClasses.ANDROID_BINARY_SHRUNK_JAR); + AndroidSdkProvider sdk = AndroidSdkProvider.fromRuleContext(ruleContext); + + Iterable<Artifact> libraryJars = NestedSetBuilder.<Artifact>naiveLinkOrder() + .add(sdk.getAndroidJar()) + .addTransitive(androidCommon.getTransitiveNeverLinkLibraries()) + .build(); + Builder builder = new SpawnAction.Builder() + .addInput(deployJar) + .addInputs(libraryJars) + .addInputs(proguardSpecs) + .setExecutable(sdk.getProguard()) + .setProgressMessage("Finding Resource References With Proguard") + .setMnemonic("ProguardResourceMapping") + .addArgument("-injars") + .addArgument(deployJar.getExecPathString()); + + for (Artifact libraryJar : libraryJars) { + builder.addArgument("-libraryjars") + .addArgument(libraryJar.getExecPathString()); + } + + for (Artifact proguardSpec : proguardSpecs) { + builder.addArgument("@" + proguardSpec.getExecPathString()); + } + + builder.addArgument("-ignorewarnings") + .addArgument("-dontnote") + .addArgument("-forceprocessing") + .addArgument("-dontoptimize") + .addArgument("-dontobfuscate") + .addArgument("-dontpreverify") + .addArgument("-outjars") + .addOutputArgument(shrunkJar); + + ruleContext.registerAction(builder.build(ruleContext)); + + return new ResourceShrinkerActionBuilder(ruleContext) + .setResourceApkOut(ruleContext.getImplicitOutputArtifact( + AndroidRuleClasses.ANDROID_RESOURCES_SHRUNK_APK)) + .setShrunkResourcesOut(ruleContext.getImplicitOutputArtifact( + AndroidRuleClasses.ANDROID_RESOURCES_SHRUNK_ZIP)) + .withResourceFiles(ruleContext.getImplicitOutputArtifact( + AndroidRuleClasses.ANDROID_RESOURCES_ZIP)) + .withShrunkJar(shrunkJar) + .withPrimary(resourceApk.getPrimaryResource()) + .withDependencies(resourceApk.getResourceDependencies()) + .setConfigurationFilters( + ruleContext.getTokenizedStringListAttr("resource_configuration_filters")) + .setUncompressedExtensions( + ruleContext.getTokenizedStringListAttr("nocompress_extensions")) + .build(); + } + return resourceApk.getArtifact(); + } + @Immutable private static final class DexingOutput { private final Artifact classesDexZip; 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 d3755b32cd..83ac2aa738 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 @@ -180,6 +180,12 @@ public class AndroidConfiguration extends BuildConfiguration.Fragment { + " rules with deps. The depot needs to be cleaned up to roll this out by default.") public boolean allowAndroidLibraryDepsWithoutSrcs; + @Option(name = "experimental_android_resource_shrinking", + defaultValue = "false", + category = "undocumented", + help = "Enables resource shrinking for android_binary APKs that use proguard.") + public boolean useAndroidResourceShrinking; + @Override public void addAllLabels(Multimap<String, Label> labelMap) { if (androidCrosstoolTop != null) { @@ -249,6 +255,7 @@ public class AndroidConfiguration extends BuildConfiguration.Fragment { private final boolean useJackForDexing; private final boolean jackSanityChecks; private final boolean allowAndroidLibraryDepsWithoutSrcs; + private final boolean useAndroidResourceShrinking; AndroidConfiguration(Options options) { this.sdk = options.sdk; @@ -261,6 +268,7 @@ public class AndroidConfiguration extends BuildConfiguration.Fragment { this.useJackForDexing = options.useJackForDexing; this.jackSanityChecks = options.jackSanityChecks; this.allowAndroidLibraryDepsWithoutSrcs = options.allowAndroidLibraryDepsWithoutSrcs; + this.useAndroidResourceShrinking = options.useAndroidResourceShrinking; } public String getCpu() { @@ -306,6 +314,10 @@ public class AndroidConfiguration extends BuildConfiguration.Fragment { return allowAndroidLibraryDepsWithoutSrcs; } + public boolean useAndroidResourceShrinking() { + return useAndroidResourceShrinking; + } + @Override public void addGlobalMakeVariables(ImmutableMap.Builder<String, String> globalMakeEnvBuilder) { globalMakeEnvBuilder.put("ANDROID_CPU", cpu); diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLibrary.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLibrary.java index 8b3927459b..189c3fd793 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLibrary.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLibrary.java @@ -81,6 +81,7 @@ public abstract class AndroidLibrary implements RuleConfiguredTargetFactory { resourceApk = applicationManifest.packWithDataAndResources( ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_RESOURCES_APK), ruleContext, + true, /* isLibrary */ ResourceDependencies.fromRuleDeps(ruleContext, JavaCommon.isNeverLink(ruleContext)), ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_R_TXT), ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_SYMBOLS_TXT), @@ -92,8 +93,8 @@ public abstract class AndroidLibrary implements RuleConfiguredTargetFactory { null /* versionName */, false, null /* proguardCfgOut */, - ruleContext.getImplicitOutputArtifact( - AndroidRuleClasses.ANDROID_LIBRARY_MANIFEST)); + ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_LIBRARY_MANIFEST), + null /* mergedResourcesOut */); if (ruleContext.hasErrors()) { return null; } @@ -150,18 +151,17 @@ public abstract class AndroidLibrary implements RuleConfiguredTargetFactory { ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_R_TXT), null); primaryResources = new AndroidResourcesProcessorBuilder(ruleContext) - .setApkOut(apk) - .setRTxtOut(resourceContainer.getRTxt()) - .setManifestOut( - ruleContext.getImplicitOutputArtifact( - AndroidRuleClasses.ANDROID_LIBRARY_MANIFEST)) - .setSourceJarOut(resourceContainer.getJavaSourceJar()) - .setJavaPackage(resourceContainer.getJavaPackage()) - .withPrimary(resourceContainer) - .withDependencies(resourceApk.getResourceDependencies()) - .setDebug( - ruleContext.getConfiguration().getCompilationMode() != CompilationMode.OPT) - .build(ruleContext); + .setLibrary(true) + .setApkOut(apk) + .setRTxtOut(resourceContainer.getRTxt()) + .setManifestOut(ruleContext.getImplicitOutputArtifact( + AndroidRuleClasses.ANDROID_LIBRARY_MANIFEST)) + .setSourceJarOut(resourceContainer.getJavaSourceJar()) + .setJavaPackage(resourceContainer.getJavaPackage()) + .withPrimary(resourceContainer) + .withDependencies(resourceApk.getResourceDependencies()) + .setDebug(ruleContext.getConfiguration().getCompilationMode() != CompilationMode.OPT) + .build(ruleContext); } new AarGeneratorBuilder(ruleContext) diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourcesProcessorBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourcesProcessorBuilder.java index 465d2bfcfd..8b0b314ffc 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourcesProcessorBuilder.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourcesProcessorBuilder.java @@ -76,6 +76,8 @@ public class AndroidResourcesProcessorBuilder { private Artifact symbolsTxt; private Artifact manifestOut; + private Artifact mergedResourcesOut; + private boolean isLibrary; /** * @param ruleContext The RuleContext that was used to create the SpawnAction.Builder. @@ -156,6 +158,16 @@ public class AndroidResourcesProcessorBuilder { return this; } + public AndroidResourcesProcessorBuilder setMergedResourcesOut(Artifact mergedResourcesOut) { + this.mergedResourcesOut = mergedResourcesOut; + return this; + } + + public AndroidResourcesProcessorBuilder setLibrary(boolean isLibrary) { + this.isLibrary = isLibrary; + return this; + } + private static class ResourceContainerToArg implements Function<ResourceContainer, String> { private boolean includeSymbols; @@ -257,7 +269,7 @@ public class AndroidResourcesProcessorBuilder { Iterables.unmodifiableIterable( Iterables.transform(dependencies.getDirectResources(), RESOURCE_DEP_TO_ARG))); } - // This flattens the nested set. Since each ResourceContainer needs to be transformed into + // This flattens the nested set. Since each ResourceContainer needs to be transformed into // Artifacts, and the NestedSetBuilder.wrap doesn't support lazy Iterator evaluation // and SpawnActionBuilder.addInputs evaluates Iterables, it becomes necessary to make the // best effort and let it get flattened. @@ -268,13 +280,13 @@ public class AndroidResourcesProcessorBuilder { .transformAndConcat(RESOURCE_DEP_TO_ARTIFACTS))); } + if (isLibrary) { + builder.add("--packageType").add("LIBRARY"); + } + if (rTxtOut != null) { builder.addExecPath("--rOutput", rTxtOut); outs.add(rTxtOut); - // If R.txt is not null, dependency R.javas will not be regenerated from the R.txt found in - // the deps, which means the resource processor needs to be told it is creating a library so - // that it will generate the R.txt. - builder.add("--packageType").add("LIBRARY"); } if (symbolsTxt != null) { @@ -289,12 +301,17 @@ public class AndroidResourcesProcessorBuilder { builder.addExecPath("--proguardOutput", proguardOut); outs.add(proguardOut); } - + if (manifestOut != null) { builder.addExecPath("--manifestOutput", manifestOut); outs.add(manifestOut); } - + + if (mergedResourcesOut != null) { + builder.addExecPath("--resourcesOutput", mergedResourcesOut); + outs.add(mergedResourcesOut); + } + if (apkOut != null) { builder.addExecPath("--packagePath", apkOut); outs.add(apkOut); 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 8a7cc46643..96315719a9 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 @@ -80,6 +80,14 @@ public final class AndroidRuleClasses { fromTemplates("%{name}_resources.jar"); public static final SafeImplicitOutputsFunction ANDROID_RESOURCES_APK = fromTemplates("%{name}.ap_"); + public static final SafeImplicitOutputsFunction ANDROID_BINARY_SHRUNK_JAR = + fromTemplates("%{name}_shrunk.jar"); + public static final SafeImplicitOutputsFunction ANDROID_RESOURCES_SHRUNK_APK = + fromTemplates("%{name}_shrunk.ap_"); + public static final SafeImplicitOutputsFunction ANDROID_RESOURCES_ZIP = + fromTemplates("%{name}_files/resource_files.zip"); + public static final SafeImplicitOutputsFunction ANDROID_RESOURCES_SHRUNK_ZIP = + fromTemplates("%{name}_files/resource_files_shrunk.zip"); public static final SafeImplicitOutputsFunction ANDROID_INCREMENTAL_RESOURCES_APK = fromTemplates("%{name}_files/incremental.ap_"); public static final SafeImplicitOutputsFunction ANDROID_BINARY_APK = @@ -141,6 +149,8 @@ public final class AndroidRuleClasses { "//tools/android:incremental_split_stub_application"; public static final String DEFAULT_RESOURCES_PROCESSOR = "//tools/android:resources_processor"; + public static final String DEFAULT_RESOURCE_SHRINKER = + "//tools/android:resource_shrinker"; public static final String DEFAULT_AAR_GENERATOR = "//tools/android:aar_generator"; public static final Label DEFAULT_ANDROID_SDK = @@ -369,6 +379,8 @@ public final class AndroidRuleClasses { return builder .add(attr("$android_resources_processor", LABEL).cfg(HOST).exec().value( env.getToolsLabel(DEFAULT_RESOURCES_PROCESSOR))) + .add(attr("$android_resource_shrinker", LABEL).cfg(HOST).exec().value( + env.getToolsLabel(DEFAULT_RESOURCE_SHRINKER))) .add(attr("$android_aar_generator", LABEL).cfg(HOST).exec().value( env.getToolsLabel(DEFAULT_AAR_GENERATOR))) .build(); diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/ApplicationManifest.java b/src/main/java/com/google/devtools/build/lib/rules/android/ApplicationManifest.java index b20c8c2403..05df106d88 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/ApplicationManifest.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/ApplicationManifest.java @@ -163,7 +163,7 @@ public final class ApplicationManifest { public ApplicationManifest mergeWith(RuleContext ruleContext, ResourceDependencies resourceDeps) { - Iterable<Artifact> mergeeManifests = getMergeeManifests(resourceDeps.getResources()); + Iterable<Artifact> mergeeManifests = getMergeeManifests(resourceDeps.getResources()); if (!Iterables.isEmpty(mergeeManifests)) { Iterable<Artifact> exportedManifests = mergeeManifests; Artifact outputManifest = ruleContext.getUniqueDirectoryArtifact( @@ -188,7 +188,7 @@ public final class ApplicationManifest { return builder.build(); } - /** Packages up the manifest with assets from the rule and dependent resources. + /** Packages up the manifest with assets from the rule and dependent resources. * @throws InterruptedException */ public ResourceApk packWithAssets( Artifact resourceApk, @@ -208,6 +208,7 @@ public final class ApplicationManifest { return createApk(resourceApk, ruleContext, + false, /* isLibrary */ resourceDeps, rTxt, null, /* configurationFilters */ @@ -220,15 +221,17 @@ public final class ApplicationManifest { incremental, data, proguardCfg, - null); + null, /* Artifact manifestOut */ + null /* Artifact mergedResources */); } - /** Packages up the manifest with resource and assets from the rule and dependent resources. + /** Packages up the manifest with resource and assets from the rule and dependent resources. * @param manifestOut TODO(corysmith): * @throws InterruptedException */ public ResourceApk packWithDataAndResources( Artifact resourceApk, RuleContext ruleContext, + boolean isLibrary, ResourceDependencies resourceDeps, Artifact rTxt, Artifact symbolsTxt, @@ -240,7 +243,8 @@ public final class ApplicationManifest { String versionName, boolean incremental, Artifact proguardCfg, - Artifact manifestOut) throws InterruptedException { + Artifact manifestOut, + Artifact mergedResources) throws InterruptedException { LocalResourceContainer data = new LocalResourceContainer.Builder(ruleContext) .withAssets( AndroidCommon.getAssetDir(ruleContext), @@ -259,6 +263,7 @@ public final class ApplicationManifest { } return createApk(resourceApk, ruleContext, + isLibrary, resourceDeps, rTxt, symbolsTxt, @@ -271,11 +276,13 @@ public final class ApplicationManifest { incremental, data, proguardCfg, - manifestOut); + manifestOut, + mergedResources); } private ResourceApk createApk(Artifact resourceApk, RuleContext ruleContext, + boolean isLibrary, ResourceDependencies resourceDeps, Artifact rTxt, Artifact symbolsTxt, @@ -288,7 +295,8 @@ public final class ApplicationManifest { boolean incremental, LocalResourceContainer data, Artifact proguardCfg, - Artifact manifestOut) throws InterruptedException { + Artifact manifestOut, + Artifact mergedResources) throws InterruptedException { ResourceContainer resourceContainer = checkForInlinedResources( new AndroidResourceContainerBuilder() .withData(data) @@ -307,12 +315,14 @@ public final class ApplicationManifest { AndroidResourcesProcessorBuilder builder = new AndroidResourcesProcessorBuilder(ruleContext) + .setLibrary(isLibrary) .setApkOut(resourceContainer.getApk()) .setConfigurationFilters(configurationFilters) .setUncompressedExtensions(uncompressedExtensions) .setJavaPackage(resourceContainer.getJavaPackage()) .setDebug(ruleContext.getConfiguration().getCompilationMode() != CompilationMode.OPT) .setManifestOut(manifestOut) + .setMergedResourcesOut(mergedResources) .withPrimary(resourceContainer) .withDependencies(resourceDeps) .setDensities(densities) @@ -373,7 +383,7 @@ public final class ApplicationManifest { /** * Packages up the manifest with resources, and generates the R.java. - * @throws InterruptedException + * @throws InterruptedException * * @deprecated in favor of {@link ApplicationManifest#packWithDataAndResources}. */ diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/ResourceShrinkerActionBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/android/ResourceShrinkerActionBuilder.java new file mode 100644 index 0000000000..d51bad31be --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/android/ResourceShrinkerActionBuilder.java @@ -0,0 +1,208 @@ +// Copyright 2016 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.android; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.collect.ImmutableList; +import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode; +import com.google.devtools.build.lib.analysis.RuleContext; +import com.google.devtools.build.lib.analysis.actions.CustomCommandLine; +import com.google.devtools.build.lib.analysis.actions.SpawnAction; +import com.google.devtools.build.lib.analysis.config.CompilationMode; +import com.google.devtools.build.lib.rules.android.AndroidResourcesProvider.ResourceContainer; + +import java.util.Collections; +import java.util.List; + +/** + * Builder for creating resource shrinker actions. + */ +public class ResourceShrinkerActionBuilder { + private Artifact resourceFilesZip; + private Artifact shrunkJar; + private ResourceContainer primaryResources; + private ResourceDependencies dependencyResources; + private Artifact resourceApkOut; + private Artifact shrunkResourcesOut; + + private final RuleContext ruleContext; + private final SpawnAction.Builder spawnActionBuilder; + private final AndroidSdkProvider sdk; + + private List<String> uncompressedExtensions = Collections.emptyList(); + private List<String> assetsToIgnore = Collections.emptyList(); + private List<String> resourceConfigs = Collections.emptyList(); + + /** + * @param ruleContext The RuleContext of the owning rule. + */ + public ResourceShrinkerActionBuilder(RuleContext ruleContext) { + this.ruleContext = ruleContext; + this.spawnActionBuilder = new SpawnAction.Builder(); + this.sdk = AndroidSdkProvider.fromRuleContext(ruleContext); + } + + public ResourceShrinkerActionBuilder setUncompressedExtensions( + List<String> uncompressedExtensions) { + this.uncompressedExtensions = uncompressedExtensions; + return this; + } + + public ResourceShrinkerActionBuilder setAssetsToIgnore(List<String> assetsToIgnore) { + this.assetsToIgnore = assetsToIgnore; + return this; + } + + public ResourceShrinkerActionBuilder setConfigurationFilters(List<String> resourceConfigs) { + this.resourceConfigs = resourceConfigs; + return this; + } + + /** + * @param resourceFilesZip A zip file containing the merged assets and resources to be shrunk. + */ + public ResourceShrinkerActionBuilder withResourceFiles(Artifact resourceFilesZip) { + this.resourceFilesZip = resourceFilesZip; + return this; + } + + /** + * @param shrunkJar The deploy jar of the rule after a dead code removal Proguard pass. + */ + public ResourceShrinkerActionBuilder withShrunkJar(Artifact shrunkJar) { + this.shrunkJar = shrunkJar; + return this; + } + + /** + * @param primary The fully processed {@link ResourceContainer} of the resources to be shrunk. + * Must contain both an R.txt and merged manifest. + */ + public ResourceShrinkerActionBuilder withPrimary(ResourceContainer primary) { + checkNotNull(primary); + checkNotNull(primary.getManifest()); + checkNotNull(primary.getRTxt()); + this.primaryResources = primary; + return this; + } + + /** + * @param resourceDeps The full dependency tree of {@link ResourceContainer}s. + */ + public ResourceShrinkerActionBuilder withDependencies(ResourceDependencies resourceDeps) { + this.dependencyResources = resourceDeps; + return this; + } + + /** + * @param resourceApkOut The location to write the shrunk resource ap_ package. + */ + public ResourceShrinkerActionBuilder setResourceApkOut(Artifact resourceApkOut) { + this.resourceApkOut = resourceApkOut; + return this; + } + + /** + * @param shrunkResourcesOut The location to write the shrunk resource files zip. + */ + public ResourceShrinkerActionBuilder setShrunkResourcesOut(Artifact shrunkResourcesOut) { + this.shrunkResourcesOut = shrunkResourcesOut; + return this; + } + + public Artifact build() { + ImmutableList.Builder<Artifact> inputs = ImmutableList.builder(); + ImmutableList.Builder<Artifact> outputs = ImmutableList.builder(); + + CustomCommandLine.Builder commandLine = new CustomCommandLine.Builder(); + + inputs.addAll(ruleContext.getExecutablePrerequisite("$android_resource_shrinker", Mode.HOST) + .getRunfilesSupport() + .getRunfilesArtifactsWithoutMiddlemen()); + + commandLine.addExecPath("--aapt", sdk.getAapt().getExecutable()); + + commandLine.addExecPath("--annotationJar", sdk.getAnnotationsJar()); + inputs.add(sdk.getAnnotationsJar()); + + commandLine.addExecPath("--androidJar", sdk.getAndroidJar()); + inputs.add(sdk.getAndroidJar()); + + if (!uncompressedExtensions.isEmpty()) { + commandLine.addJoinStrings("--uncompressedExtensions", ",", uncompressedExtensions); + } + if (!assetsToIgnore.isEmpty()) { + commandLine.addJoinStrings("--assetsToIgnore", ",", assetsToIgnore); + } + if (ruleContext.getConfiguration().getCompilationMode() != CompilationMode.OPT) { + commandLine.add("--debug"); + } + if (!resourceConfigs.isEmpty()) { + commandLine.addJoinStrings("--resourceConfigs", ",", resourceConfigs); + } + + checkNotNull(resourceFilesZip); + checkNotNull(shrunkJar); + checkNotNull(primaryResources); + checkNotNull(resourceApkOut); + + commandLine.addExecPath("--resources", resourceFilesZip); + inputs.add(resourceFilesZip); + + commandLine.addExecPath("--shrunkJar", shrunkJar); + inputs.add(shrunkJar); + + commandLine.addExecPath("--rTxt", primaryResources.getRTxt()); + inputs.add(primaryResources.getRTxt()); + + commandLine.addExecPath("--primaryManifest", primaryResources.getManifest()); + inputs.add(primaryResources.getManifest()); + + List<Artifact> dependencyManifests = getManifests(dependencyResources); + commandLine.addJoinExecPaths("--dependencyManifests", ":", dependencyManifests); + inputs.addAll(dependencyManifests); + + commandLine.addExecPath("--shrunkResourceApk", resourceApkOut); + outputs.add(resourceApkOut); + + commandLine.addExecPath("--shrunkResources", shrunkResourcesOut); + outputs.add(shrunkResourcesOut); + + ruleContext.registerAction(spawnActionBuilder + .addTool(sdk.getAapt()) + .addInputs(inputs.build()) + .addOutputs(outputs.build()) + .setCommandLine(commandLine.build()) + .setExecutable(ruleContext.getExecutablePrerequisite( + "$android_resource_shrinker", Mode.HOST)) + .setProgressMessage("Shrinking resources") + .setMnemonic("ResourceShrinker") + .build(ruleContext)); + + return resourceApkOut; + } + + private List<Artifact> getManifests(ResourceDependencies resourceDependencies) { + ImmutableList.Builder<Artifact> manifests = ImmutableList.builder(); + for (ResourceContainer resources : resourceDependencies.getResources()) { + if (resources.getManifest() != null) { + manifests.add(resources.getManifest()); + } + } + return manifests.build(); + } +} + diff --git a/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java b/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java index 6304712c6c..5c0d4119ee 100644 --- a/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java +++ b/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java @@ -148,6 +148,7 @@ public final class BazelAnalysisMock extends AnalysisMock { .add(")") .add("sh_binary(name = 'aar_generator', srcs = ['empty.sh'])") .add("sh_binary(name = 'resources_processor', srcs = ['empty.sh'])") + .add("sh_binary(name = 'resource_shrinker', srcs = ['empty.sh'])") .add("android_library(name = 'incremental_stub_application')") .add("android_library(name = 'incremental_split_stub_application')") .add("sh_binary(name = 'stubify_manifest', srcs = ['empty.sh'])") diff --git a/src/test/shell/bazel/android/BUILD b/src/test/shell/bazel/android/BUILD index 9fa75cd11a..16243889ff 100644 --- a/src/test/shell/bazel/android/BUILD +++ b/src/test/shell/bazel/android/BUILD @@ -12,6 +12,7 @@ sh_test( "//external:android_sdk_for_testing", "//src/tools/android/java/com/google/devtools/build/android:AarGeneratorAction_deploy.jar", "//src/tools/android/java/com/google/devtools/build/android:AndroidResourceProcessingAction_deploy.jar", + "//src/tools/android/java/com/google/devtools/build/android:ResourceShrinkerAction_deploy.jar", "//src/tools/android/java/com/google/devtools/build/android/incrementaldeployment:srcs", "//src/tools/android/java/com/google/devtools/build/android/ziputils:mapper_deploy.jar", "//src/tools/android/java/com/google/devtools/build/android/ziputils:reducer_deploy.jar", diff --git a/src/test/shell/bazel/test-setup.sh b/src/test/shell/bazel/test-setup.sh index bcccc84209..15b1bb9645 100755 --- a/src/test/shell/bazel/test-setup.sh +++ b/src/test/shell/bazel/test-setup.sh @@ -51,6 +51,7 @@ function setup_android_support() { ln -s "${aargenerator_path}" ${ANDROID_TOOLS}/tools/android/aargenerator.jar ln -s "${androidresourceprocessor_path}" ${ANDROID_TOOLS}/tools/android/androidresourceprocessor.jar + ln -s "${resourceshrinker_path}" ${ANDROID_TOOLS}/tools/android/resourceshrinker.jar ln -s "${dexmapper_path}" ${ANDROID_TOOLS}/tools/android/dexmapper.jar ln -s "${dexreducer_path}" ${ANDROID_TOOLS}/tools/android/dexreducer.jar ln -s "${TEST_SRCDIR}/tools/android/bazel_debug.keystore" ${ANDROID_TOOLS}/tools/android/bazel_debug.keystore @@ -103,6 +104,12 @@ java_binary( ) java_binary( + name = "resource_shrinker", + srcs = ["resourceshrinker.jar"], + main_class = "com.google.devtools.build.android.ResourceShrinkerAction", +) + +java_binary( name = "merge_dexzips", srcs = ["dexreducer.jar"], main_class = "com.google.devtools.build.android.ziputils.DexReducer", diff --git a/src/test/shell/bazel/testenv.sh b/src/test/shell/bazel/testenv.sh index 4cb5fef8bf..19d0729ea7 100755 --- a/src/test/shell/bazel/testenv.sh +++ b/src/test/shell/bazel/testenv.sh @@ -52,6 +52,7 @@ namespace_sandbox="${TEST_SRCDIR}/src/main/tools/namespace-sandbox" # Android tooling aargenerator_path="${TEST_SRCDIR}/src/tools/android/java/com/google/devtools/build/android/AarGeneratorAction_deploy.jar" androidresourceprocessor_path="${TEST_SRCDIR}/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction_deploy.jar" +resourceshrinker_path="${TEST_SRCDIR}/src/tools/android/java/com/google/devtools/build/android/ResourceShrinkerAction_deploy.jar" dexmapper_path="${TEST_SRCDIR}/src/tools/android/java/com/google/devtools/build/android/ziputils/mapper_deploy.jar" dexreducer_path="${TEST_SRCDIR}/src/tools/android/java/com/google/devtools/build/android/ziputils/reducer_deploy.jar" incrementaldeployment_path="${TEST_SRCDIR}/src/tools/android/java/com/google/devtools/build/android/incrementaldeployment" diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java index daf553ed90..0e233743d6 100644 --- a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java +++ b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java @@ -286,7 +286,7 @@ public class AndroidResourceProcessingAction { options.packagePath, options.proguardOutput, options.resourcesOutput != null - ? filteredData.getResourceDir().resolve("values").resolve("public.xml") + ? processedManifestData.getResourceDir().resolve("values").resolve("public.xml") : null); LOGGER.fine(String.format("appt finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS))); diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessor.java b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessor.java index 51a7262f74..bca53a7761 100644 --- a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessor.java +++ b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessor.java @@ -272,7 +272,9 @@ public class AndroidResourceProcessor { public void createResourcesZip(Path resourcesRoot, Path assetsRoot, Path output) throws IOException { try (ZipOutputStream zout = new ZipOutputStream(new FileOutputStream(output.toFile()))) { - Files.walkFileTree(resourcesRoot, new ZipBuilderVisitor(zout, resourcesRoot, "res")); + if (Files.exists(resourcesRoot)) { + Files.walkFileTree(resourcesRoot, new ZipBuilderVisitor(zout, resourcesRoot, "res")); + } if (Files.exists(assetsRoot)) { Files.walkFileTree(assetsRoot, new ZipBuilderVisitor(zout, assetsRoot, "assets")); } @@ -310,6 +312,9 @@ public class AndroidResourceProcessor { Path androidManifest = primaryData.getManifest(); Path resourceDir = primaryData.getResourceDir(); Path assetsDir = primaryData.getAssetDir(); + if (publicResourcesOut != null) { + prepareOutputPath(publicResourcesOut.getParent()); + } AaptCommandBuilder commandBuilder = new AaptCommandBuilder(aapt, buildToolsVersion, variantType, "package") @@ -366,7 +371,7 @@ public class AndroidResourceProcessor { if (packageOut != null) { Files.setLastModifiedTime(packageOut, FileTime.fromMillis(0L)); } - if (publicResourcesOut != null) { + if (publicResourcesOut != null && Files.exists(publicResourcesOut)) { Files.setLastModifiedTime(publicResourcesOut, FileTime.fromMillis(0L)); } } |