diff options
author | 2017-08-29 00:41:40 +0200 | |
---|---|---|
committer | 2017-08-29 13:33:04 +0200 | |
commit | 69ef625b464093aaefd3d8af9e947832d4385211 (patch) | |
tree | cbc8f4c3a8cfce9adc0f89e9269720076a5e7921 | |
parent | a4c0e1a84029b2bad2310f7259ba6757fe706ac2 (diff) |
aar_import creates res/values/empty.xml if it contains no resources.
To do this, add a new tool that is used instead of zipper to get the resources out of the AAR. This tool creates res/values/empty.xml if there are no resources in the AAR.
Also, some general cleaning of the code.
RELNOTES: None
PiperOrigin-RevId: 166768607
9 files changed, 207 insertions, 90 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AarImport.java b/src/main/java/com/google/devtools/build/lib/rules/android/AarImport.java index 19ff508e3f..61295fc8e6 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AarImport.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AarImport.java @@ -83,13 +83,9 @@ public class AarImport implements RuleConfiguredTargetFactory { ruleContext.registerAction(createSingleFileExtractorActions( ruleContext, aar, ANDROID_MANIFEST, androidManifestArtifact)); - Artifact resourcesManifest = createAarArtifact(ruleContext, "resource_manifest"); - ruleContext.registerAction( - createManifestExtractorActions(ruleContext, aar, "res/.*", resourcesManifest)); - Artifact resources = createAarTreeArtifact(ruleContext, "resources"); ruleContext.registerAction( - createManifestFileEntriesExtractorActions(ruleContext, aar, resourcesManifest, resources)); + createAarResourcesExtractorActions(ruleContext, aar, resources)); ApplicationManifest androidManifest = ApplicationManifest.fromExplicitManifest(ruleContext, androidManifestArtifact); @@ -110,8 +106,7 @@ public class AarImport implements RuleConfiguredTargetFactory { ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_R_TXT), ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_LOCAL_SYMBOLS), ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_PROCESSED_MANIFEST), - resourcesZip, - /* alwaysExportManifest = */ true); + resourcesZip); // There isn't really any use case for building an aar_import target on its own, so the files to // build could be empty. The resources zip and merged jars are added here as a sanity check for @@ -123,8 +118,8 @@ public class AarImport implements RuleConfiguredTargetFactory { Artifact nativeLibs = createAarArtifact(ruleContext, "native_libs.zip"); ruleContext.registerAction(createAarNativeLibsFilterActions(ruleContext, aar, nativeLibs)); - JavaRuleOutputJarsProvider.Builder jarProviderBuilder = new JavaRuleOutputJarsProvider.Builder() - .addOutputJar(mergedJar, null, ImmutableList.<Artifact>of()); + JavaRuleOutputJarsProvider.Builder jarProviderBuilder = + new JavaRuleOutputJarsProvider.Builder().addOutputJar(mergedJar, null, ImmutableList.of()); for (TransitiveInfoCollection export : ruleContext.getPrerequisites("exports", Mode.TARGET)) { for (OutputJar jar : JavaInfo.getProvider(JavaRuleOutputJarsProvider.class, export).getOutputJars()) { @@ -134,13 +129,12 @@ public class AarImport implements RuleConfiguredTargetFactory { } ImmutableList<TransitiveInfoCollection> targets = - ImmutableList.<TransitiveInfoCollection>copyOf( - ruleContext.getPrerequisites("exports", Mode.TARGET)); + ImmutableList.copyOf(ruleContext.getPrerequisites("exports", Mode.TARGET)); JavaCommon common = new JavaCommon( ruleContext, javaSemantics, - /* sources = */ ImmutableList.<Artifact>of(), + /* sources = */ ImmutableList.of(), /* compileDeps = */ targets, /* runtimeDeps = */ targets, /* bothDeps = */ targets); @@ -182,7 +176,7 @@ public class AarImport implements RuleConfiguredTargetFactory { private static Action[] createSingleFileExtractorActions(RuleContext ruleContext, Artifact aar, String filename, Artifact outputArtifact) { return new SpawnAction.Builder() - .setExecutable(ruleContext.getExecutablePrerequisite("$zipper", Mode.HOST)) + .setExecutable(ruleContext.getExecutablePrerequisite(AarImportBaseRule.ZIPPER, Mode.HOST)) .setMnemonic("AarFileExtractor") .setProgressMessage("Extracting %s from %s", filename, aar.getFilename()) .addInput(aar) @@ -196,20 +190,20 @@ public class AarImport implements RuleConfiguredTargetFactory { .build(ruleContext); } - private static Action[] createManifestFileEntriesExtractorActions(RuleContext ruleContext, - Artifact aar, Artifact manifest, Artifact outputTree) { + private static Action[] createAarResourcesExtractorActions( + RuleContext ruleContext, Artifact aar, Artifact outputTree) { return new SpawnAction.Builder() - .setExecutable(ruleContext.getExecutablePrerequisite("$zipper", Mode.HOST)) - .setMnemonic("AarManifestFileEntriesExtractor") + .setExecutable( + ruleContext.getExecutablePrerequisite( + AarImportBaseRule.AAR_RESOURCES_EXTRACTOR, Mode.HOST)) + .setMnemonic("AarResourcesExtractor") .addInput(aar) .addOutput(outputTree) .setCommandLine( CustomCommandLine.builder() - .addExecPath("x", aar) - .addExecPath("-d", outputTree) - .addPrefixedExecPath("@", manifest) + .addExecPath("--input_aar", aar) + .addExecPath("--output_res_dir", outputTree) .build()) - .addInput(manifest) .build(ruleContext); } @@ -217,7 +211,8 @@ public class AarImport implements RuleConfiguredTargetFactory { Artifact aar, Artifact jarsTreeArtifact, Artifact singleJarParamFile) { return new SpawnAction.Builder() .setExecutable( - ruleContext.getExecutablePrerequisite("$aar_embedded_jars_extractor", Mode.HOST)) + ruleContext.getExecutablePrerequisite( + AarImportBaseRule.AAR_EMBEDDED_JARS_EXTACTOR, Mode.HOST)) .setMnemonic("AarEmbeddedJarsExtractor") .setProgressMessage("Extracting classes.jar and libs/*.jar from %s", aar.getFilename()) .addInput(aar) @@ -249,30 +244,13 @@ public class AarImport implements RuleConfiguredTargetFactory { .build(ruleContext); } - private static Action[] createManifestExtractorActions(RuleContext ruleContext, Artifact aar, - String filenameRegexp, Artifact manifest) { - return new SpawnAction.Builder() - .setExecutable(ruleContext.getExecutablePrerequisite("$zip_manifest_creator", Mode.HOST)) - .setMnemonic("ZipManifestCreator") - .setProgressMessage( - "Creating manifest for %s matching %s", aar.getFilename(), filenameRegexp) - .addInput(aar) - .addOutput(manifest) - .setCommandLine( - CustomCommandLine.builder() - .addDynamicString(filenameRegexp) - .addExecPath(aar) - .addExecPath(manifest) - .build()) - .build(ruleContext); - } - private static Action[] createAarNativeLibsFilterActions(RuleContext ruleContext, Artifact aar, Artifact outputZip) { SpawnAction.Builder actionBuilder = new SpawnAction.Builder() .setExecutable( - ruleContext.getExecutablePrerequisite("$aar_native_libs_zip_creator", Mode.HOST)) + ruleContext.getExecutablePrerequisite( + AarImportBaseRule.AAR_NATIVE_LIBS_ZIP_CREATOR, Mode.HOST)) .setMnemonic("AarNativeLibsFilter") .setProgressMessage("Filtering AAR native libs by architecture") .addInput(aar) diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AarImportBaseRule.java b/src/main/java/com/google/devtools/build/lib/rules/android/AarImportBaseRule.java index 530a04cc7d..52262de62a 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AarImportBaseRule.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AarImportBaseRule.java @@ -21,7 +21,6 @@ import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST; import com.google.devtools.build.lib.analysis.RuleDefinition; import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment; -import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.packages.RuleClass; import com.google.devtools.build.lib.packages.RuleClass.Builder; import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType; @@ -32,15 +31,18 @@ import com.google.devtools.build.lib.util.FileType; /** Rule definition for the aar_import rule. */ public class AarImportBaseRule implements RuleDefinition { + static final String AAR_EMBEDDED_JARS_EXTACTOR = "$aar_embedded_jars_extractor"; + static final String AAR_NATIVE_LIBS_ZIP_CREATOR = "$aar_native_libs_zip_creator"; + static final String AAR_RESOURCES_EXTRACTOR = "$aar_resources_extractor"; + static final String ZIPPER = "$zipper"; + @Override - public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) { + public RuleClass build(Builder builder, RuleDefinitionEnvironment env) { return builder /* <!-- #BLAZE_RULE($aar_import_base).ATTRIBUTE(aar) --> The <code>.aar</code> file to provide to the Android targets that depend on this target. <!-- #END_BLAZE_RULE.ATTRIBUTE --> */ - .add(attr("aar", LABEL) - .mandatory() - .allowedFileTypes(FileType.of(".aar"))) + .add(attr("aar", LABEL).mandatory().allowedFileTypes(FileType.of(".aar"))) /* <!-- #BLAZE_RULE(aar_import).ATTRIBUTE(exports) --> Targets to export to rules that depend on this rule. See <a href="${link java_library.exports}">java_library.exports. @@ -49,26 +51,22 @@ public class AarImportBaseRule implements RuleDefinition { .allowedRuleClasses("aar_import", "java_import") .allowedFileTypes() .validityPredicate(ANY_EDGE)) - .add(attr("$aar_embedded_jars_extractor", LABEL) + .add(attr(AAR_EMBEDDED_JARS_EXTACTOR, LABEL) .cfg(HOST) .exec() - .value(Label.parseAbsoluteUnchecked( - environment.getToolsRepository() + "//tools/android:aar_embedded_jars_extractor"))) - .add(attr("$aar_native_libs_zip_creator", LABEL) + .value(env.getToolsLabel("//tools/android:aar_embedded_jars_extractor"))) + .add(attr(AAR_NATIVE_LIBS_ZIP_CREATOR, LABEL) .cfg(HOST) .exec() - .value(Label.parseAbsoluteUnchecked( - environment.getToolsRepository() + "//tools/android:aar_native_libs_zip_creator"))) - .add(attr("$zip_manifest_creator", LABEL) + .value(env.getToolsLabel("//tools/android:aar_native_libs_zip_creator"))) + .add(attr(AAR_RESOURCES_EXTRACTOR, LABEL) .cfg(HOST) .exec() - .value(Label.parseAbsoluteUnchecked( - environment.getToolsRepository() + "//tools/android:zip_manifest_creator"))) - .add(attr("$zipper", LABEL) + .value(env.getToolsLabel("//tools/android:aar_resources_extractor"))) + .add(attr(ZIPPER, LABEL) .cfg(HOST) .exec() - .value(Label.parseAbsoluteUnchecked( - environment.getToolsRepository() + "//tools/zip:zipper"))) + .value(env.getToolsLabel("//tools/zip:zipper"))) .advertiseProvider(JavaCompilationArgsProvider.class) .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 f15eba6697..f291995260 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 @@ -360,8 +360,7 @@ public final class ApplicationManifest { Artifact rTxt, Artifact symbols, Artifact manifestOut, - Artifact mergedResources, - boolean alwaysExportManifest) throws InterruptedException, RuleErrorException { + Artifact mergedResources) throws InterruptedException, RuleErrorException { if (ruleContext.hasErrors()) { return null; } @@ -369,16 +368,14 @@ public final class ApplicationManifest { ResourceContainer.builderFromRule(ruleContext) .setRTxt(rTxt) .setSymbols(symbols) - .setJavaPackageFrom(JavaPackageSource.MANIFEST); - if (alwaysExportManifest) { - builder.setManifestExported(true); - } + .setJavaPackageFrom(JavaPackageSource.MANIFEST) + .setManifestExported(true); return createApk( ruleContext, true, /* isLibrary */ resourceDeps, - ImmutableList.<String>of(), /* List<String> uncompressedExtensions */ + ImmutableList.of(), /* List<String> uncompressedExtensions */ false, /* crunchPng */ false, /* incremental */ builder, @@ -788,7 +785,7 @@ public final class ApplicationManifest { List<String> uncompressedExtensions = ruleContext.getTokenizedStringListAttr("nocompress_extensions"); - ImmutableList.Builder<String> additionalAaptOpts = ImmutableList.<String>builder(); + ImmutableList.Builder<String> additionalAaptOpts = ImmutableList.builder(); for (String extension : uncompressedExtensions) { additionalAaptOpts.add("-0").add(extension); 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 bd6ce313a4..f3525b3e4c 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 @@ -247,7 +247,7 @@ public final class BazelAnalysisMock extends AnalysisMock { .add("java_binary(name = 'IdlClass',") .add(" runtime_deps = [ ':idlclass_import' ],") .add(" main_class = 'com.google.devtools.build.android.idlclass.IdlClass')") - .add("sh_binary(name = 'zip_manifest_creator', srcs = ['empty.sh'])") + .add("sh_binary(name = 'aar_resources_extractor', srcs = ['empty.sh'])") .add("sh_binary(name = 'aar_embedded_jars_extractor', srcs = ['empty.sh'])") .add("java_import(name = 'idlclass_import',") .add(" jars = [ 'idlclass.jar' ])") diff --git a/src/test/java/com/google/devtools/build/lib/rules/android/AarImportTest.java b/src/test/java/com/google/devtools/build/lib/rules/android/AarImportTest.java index 245698d347..bb52347a22 100644 --- a/src/test/java/com/google/devtools/build/lib/rules/android/AarImportTest.java +++ b/src/test/java/com/google/devtools/build/lib/rules/android/AarImportTest.java @@ -13,6 +13,7 @@ // limitations under the License. package com.google.devtools.build.lib.rules.android; +import static com.google.common.collect.Streams.stream; import static com.google.common.truth.Truth.assertThat; import com.google.common.base.Predicates; @@ -21,6 +22,7 @@ import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.actions.util.ActionsTestUtil; import com.google.devtools.build.lib.analysis.ConfiguredTarget; import com.google.devtools.build.lib.analysis.FileConfiguredTarget; +import com.google.devtools.build.lib.analysis.FilesToRunProvider; import com.google.devtools.build.lib.analysis.actions.SpawnAction; import com.google.devtools.build.lib.analysis.util.BuildViewTestCase; import com.google.devtools.build.lib.collect.nestedset.NestedSet; @@ -86,6 +88,31 @@ public class AarImportTest extends BuildViewTestCase { } @Test + public void testResourcesExtractor() throws Exception { + AndroidResourcesProvider resourcesProvider = + getConfiguredTarget("//a:foo").getProvider(AndroidResourcesProvider.class); + + Artifact resourceTreeArtifact = + stream(resourcesProvider.getDirectAndroidResources()) + .flatMap(resourceContainer -> resourceContainer.getResources().stream()) + .findFirst() + .get(); + Artifact aarResourcesExtractor = + getHostConfiguredTarget( + ruleClassProvider.getToolsRepository() + "//tools/android:aar_resources_extractor") + .getProvider(FilesToRunProvider.class) + .getExecutable(); + + assertThat(getGeneratingSpawnAction(resourceTreeArtifact).getArguments()) + .containsExactly( + aarResourcesExtractor.getExecPathString(), + "--input_aar", + "a/foo.aar", + "--output_res_dir", + resourceTreeArtifact.getExecPathString()); + } + + @Test public void testNativeLibsProvided() throws Exception { ConfiguredTarget androidLibraryTarget = getConfiguredTarget("//java:lib"); diff --git a/tools/android/BUILD b/tools/android/BUILD index 19155c29f5..75a9956e67 100644 --- a/tools/android/BUILD +++ b/tools/android/BUILD @@ -120,23 +120,6 @@ py_test( ], ) -sh_binary( - name = "zip_manifest_creator", - srcs = ["zip_manifest_creator.sh"], - data = ["//tools/zip:zipper"], -) - -sh_test( - name = "zip_manifest_creator_test", - size = "small", - srcs = ["zip_manifest_creator_test.sh"], - args = ["$(location //tools/zip:zipper)"], - data = [ - ":zip_manifest_creator", - "//tools/zip:zipper", - ], -) - py_binary( name = "aar_embedded_jars_extractor", srcs = ["aar_embedded_jars_extractor.py"], @@ -150,6 +133,18 @@ py_test( ) py_binary( + name = "aar_resources_extractor", + srcs = ["aar_resources_extractor.py"], + deps = ["//third_party/py/gflags"], +) + +py_test( + name = "aar_resources_extractor_test", + srcs = ["aar_resources_extractor_test.py"], + deps = [":aar_resources_extractor"], +) + +py_binary( name = "resource_extractor", srcs = ["resource_extractor.py"], ) diff --git a/tools/android/BUILD.tools b/tools/android/BUILD.tools index 866d6afcee..bc43e41509 100644 --- a/tools/android/BUILD.tools +++ b/tools/android/BUILD.tools @@ -151,12 +151,6 @@ sh_binary( srcs = ["fail.sh"], ) -sh_binary( - name = "zip_manifest_creator", - srcs = ["zip_manifest_creator.sh"], - data = ["//tools/zip:zipper"], -) - py_binary( name = "aar_embedded_jars_extractor", srcs = ["aar_embedded_jars_extractor.py"], @@ -164,6 +158,12 @@ py_binary( ) py_binary( + name = "aar_resources_extractor", + srcs = ["aar_resources_extractor.py"], + deps = ["//third_party/py/gflags"], +) + +py_binary( name = "resource_extractor", srcs = ["resource_extractor.py"], ) diff --git a/tools/android/aar_resources_extractor.py b/tools/android/aar_resources_extractor.py new file mode 100644 index 0000000000..0e259b8028 --- /dev/null +++ b/tools/android/aar_resources_extractor.py @@ -0,0 +1,58 @@ +# pylint: disable=g-direct-third-party-import +# Copyright 2017 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. + +"""A tool for extracting resource files from an AAR. + +An AAR may contain resources under the /res directory. This tool extracts all +of the resources into a directory. If no resources exist, it creates an +empty.xml file that defines no resources. + +In the future, this script may be extended to also extract assets. +""" + +import os +import sys +import zipfile + +from third_party.py import gflags + +FLAGS = gflags.FLAGS + +gflags.DEFINE_string("input_aar", None, "Input AAR") +gflags.MarkFlagAsRequired("input_aar") +gflags.DEFINE_string("output_res_dir", None, "Output resources directory") +gflags.MarkFlagAsRequired("output_res_dir") + + +def ExtractResources(aar, output_res_dir): + aar_contains_no_resources = True + for name in aar.namelist(): + if name.startswith("res/"): + aar.extract(name, output_res_dir) + aar_contains_no_resources = False + if aar_contains_no_resources: + empty_xml_filename = output_res_dir + "/res/values/empty.xml" + os.makedirs(os.path.dirname(empty_xml_filename)) + with open(empty_xml_filename, "wb") as empty_xml: + empty_xml.write("<resources/>") + + +def main(): + with zipfile.ZipFile(FLAGS.input_aar, "r") as aar: + ExtractResources(aar, FLAGS.output_res_dir) + +if __name__ == "__main__": + FLAGS(sys.argv) + main() diff --git a/tools/android/aar_resources_extractor_test.py b/tools/android/aar_resources_extractor_test.py new file mode 100644 index 0000000000..b59e4160cb --- /dev/null +++ b/tools/android/aar_resources_extractor_test.py @@ -0,0 +1,64 @@ +# Copyright 2017 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. + +"""Tests for aar_resources_extractor.""" + +import os +import shutil +import StringIO +import unittest +import zipfile + +from tools.android import aar_resources_extractor + + +class AarResourcesExtractorTest(unittest.TestCase): + """Unit tests for aar_resources_extractor.py.""" + + def setUp(self): + os.chdir(os.environ["TEST_TMPDIR"]) + + def tearDown(self): + shutil.rmtree("out_dir") + + def DirContents(self, d): + return [path + "/" + f for (path, _, files) in os.walk(d) + for f in files] + + def testNoResources(self): + aar = zipfile.ZipFile(StringIO.StringIO(), "w") + os.makedirs("out_dir") + aar_resources_extractor.ExtractResources(aar, "out_dir") + self.assertEqual(["out_dir/res/values/empty.xml"], + self.DirContents("out_dir")) + with open("out_dir/res/values/empty.xml", "r") as empty_xml: + self.assertEqual("<resources/>", empty_xml.read()) + + def testContainsResources(self): + aar = zipfile.ZipFile(StringIO.StringIO(), "w") + aar.writestr("res/values/values.xml", "some values") + aar.writestr("res/layouts/layout.xml", "some layout") + os.makedirs("out_dir") + aar_resources_extractor.ExtractResources(aar, "out_dir") + expected_resources = ["out_dir/res/values/values.xml", + "out_dir/res/layouts/layout.xml"] + self.assertItemsEqual(expected_resources, self.DirContents("out_dir")) + with open("out_dir/res/values/values.xml", "r") as values_xml: + self.assertEqual("some values", values_xml.read()) + with open("out_dir/res/layouts/layout.xml", "r") as layout_xml: + self.assertEqual("some layout", layout_xml.read()) + + +if __name__ == "__main__": + unittest.main() |