diff options
author | Adam Michael <ajmichael@google.com> | 2016-10-06 21:31:57 +0000 |
---|---|---|
committer | Damien Martin-Guillerez <dmarting@google.com> | 2016-10-07 08:08:19 +0000 |
commit | 67d736b55a495ec1ac46a59a006d428ced97de96 (patch) | |
tree | a17e31af4d6603735afc68fc7db94b65b56ed420 | |
parent | 87c6d91555eee45ab0ee6f1cd0cb8e93f12bbc5d (diff) |
Handles missing classes.jar in aar_import gracefully, by creating an empty jar file instead of crashing Bazel.
--
MOS_MIGRATED_REVID=135405636
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/bazel/rules/android/AarImport.java | 34 | ||||
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/bazel/rules/android/AarImportRule.java | 5 | ||||
-rw-r--r-- | tools/zip/BUILD | 12 | ||||
-rw-r--r-- | tools/zip/BUILD.tools | 6 | ||||
-rw-r--r-- | tools/zip/embedded_jar_extractor.py | 53 | ||||
-rw-r--r-- | tools/zip/embedded_jar_extractor_test.py | 50 |
6 files changed, 152 insertions, 8 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AarImport.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AarImport.java index 8803d86b8d..5b3b7f778e 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AarImport.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AarImport.java @@ -62,19 +62,20 @@ public class AarImport implements RuleConfiguredTargetFactory { // classes.jar is required in every AAR. Artifact classesJar = createAarArtifact(ruleContext, CLASSES_JAR); ruleContext.registerAction( - createSingleFileExtractor(ruleContext, aar, CLASSES_JAR, classesJar)); + createEmbeddedJarExtractorActions(ruleContext, aar, CLASSES_JAR, classesJar)); // AndroidManifest.xml is required in every AAR. Artifact androidManifestArtifact = createAarArtifact(ruleContext, ANDROID_MANIFEST); - ruleContext.registerAction( - createSingleFileExtractor(ruleContext, aar, ANDROID_MANIFEST, androidManifestArtifact)); + ruleContext.registerAction(createSingleFileExtractorActions( + ruleContext, aar, ANDROID_MANIFEST, androidManifestArtifact)); Artifact resourcesManifest = createAarArtifact(ruleContext, "resource_manifest"); ruleContext.registerAction( - createManifestExtractor(ruleContext, aar, "res/.*", resourcesManifest)); + createManifestExtractorActions(ruleContext, aar, "res/.*", resourcesManifest)); Artifact resources = createResourcesTreeArtifact(ruleContext); - ruleContext.registerAction(createTreePopulater(ruleContext, aar, resourcesManifest, resources)); + ruleContext.registerAction( + createTreePopulaterActions(ruleContext, aar, resourcesManifest, resources)); ApplicationManifest androidManifest = ApplicationManifest.fromExplicitManifest(ruleContext, androidManifestArtifact); @@ -114,7 +115,7 @@ public class AarImport implements RuleConfiguredTargetFactory { .build(); } - private static Action[] createSingleFileExtractor(RuleContext ruleContext, Artifact aar, + private static Action[] createSingleFileExtractorActions(RuleContext ruleContext, Artifact aar, String filename, Artifact outputArtifact) { return new SpawnAction.Builder() .setExecutable(ruleContext.getExecutablePrerequisite("$zipper", Mode.HOST)) @@ -129,7 +130,24 @@ public class AarImport implements RuleConfiguredTargetFactory { .build(ruleContext); } - private static Action createTreePopulater(RuleContext ruleContext, Artifact aar, + // Extracts a jar file from the aar if it exists, otherwise outputs an empty jar file. + private static Action[] createEmbeddedJarExtractorActions(RuleContext ruleContext, Artifact aar, + String filename, Artifact outputArtifact) { + return new SpawnAction.Builder() + .setExecutable(ruleContext.getExecutablePrerequisite("$embedded_jar_extractor", Mode.HOST)) + .setMnemonic("AarJarExtractor") + .setProgressMessage("Extracting " + filename + " from " + aar.getFilename()) + .addArgument("--input_archive") + .addInputArgument(aar) + .addArgument("--filename") + .addArgument(filename) + .addArgument("--output_dir") + .addOutput(outputArtifact) + .addArgument(outputArtifact.getExecPath().getParentDirectory().getPathString()) + .build(ruleContext); + } + + private static Action createTreePopulaterActions(RuleContext ruleContext, Artifact aar, Artifact manifest, Artifact outputTree) { return new PopulateTreeArtifactAction( ruleContext.getActionOwner(), @@ -139,7 +157,7 @@ public class AarImport implements RuleConfiguredTargetFactory { ruleContext.getExecutablePrerequisite("$zipper", Mode.HOST)); } - private static Action[] createManifestExtractor(RuleContext ruleContext, Artifact aar, + private static Action[] createManifestExtractorActions(RuleContext ruleContext, Artifact aar, String filenameRegexp, Artifact manifest) { return new SpawnAction.Builder() .setExecutable(ruleContext.getExecutablePrerequisite("$zip_manifest_creator", Mode.HOST)) diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AarImportRule.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AarImportRule.java index 61618bdb6a..2d09378c12 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AarImportRule.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AarImportRule.java @@ -43,6 +43,11 @@ public class AarImportRule implements RuleDefinition { .allowedRuleClasses("aar_import", "java_import") .allowedFileTypes() .validityPredicate(ANY_EDGE)) + .add(attr("$embedded_jar_extractor", LABEL) + .cfg(HOST) + .exec() + .value(Label.parseAbsoluteUnchecked( + environment.getToolsRepository() + "//tools/zip:embedded_jar_extractor"))) .add(attr("$zip_manifest_creator", LABEL) .cfg(HOST) .exec() diff --git a/tools/zip/BUILD b/tools/zip/BUILD index cfb986b993..4ccf9f7273 100644 --- a/tools/zip/BUILD +++ b/tools/zip/BUILD @@ -17,3 +17,15 @@ sh_test( srcs = ["zip_manifest_creator_test.sh"], data = [":zip_manifest_creator"], ) + +py_binary( + name = "embedded_jar_extractor", + srcs = ["embedded_jar_extractor.py"], + deps = ["//third_party/py/gflags"], +) + +py_test( + name = "embedded_jar_extractor_test", + srcs = ["embedded_jar_extractor_test.py"], + deps = [":embedded_jar_extractor"], +) diff --git a/tools/zip/BUILD.tools b/tools/zip/BUILD.tools index 58f9267f2f..542ddce946 100644 --- a/tools/zip/BUILD.tools +++ b/tools/zip/BUILD.tools @@ -10,3 +10,9 @@ sh_binary( srcs = ["zip_manifest_creator.sh"], data = [":zipper"], ) + +py_binary( + name = "embedded_jar_extractor", + srcs = ["embedded_jar_extractor.py"], + deps = ["//third_party/py/gflags"], +) diff --git a/tools/zip/embedded_jar_extractor.py b/tools/zip/embedded_jar_extractor.py new file mode 100644 index 0000000000..1111b2993e --- /dev/null +++ b/tools/zip/embedded_jar_extractor.py @@ -0,0 +1,53 @@ +# 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. + +"""A tool for extracting a jar file from an archive and failing gracefully. + +If the jar file is present within the archive, it is extracted into the output +directory. If not, an empty jar is created in the output directory. +""" + +import os +import sys +import zipfile + +from third_party.py import gflags + +FLAGS = gflags.FLAGS + +gflags.DEFINE_string("input_archive", None, "Input archive") +gflags.MarkFlagAsRequired("input_archive") +gflags.DEFINE_string("filename", None, "Filename of JAR to extract") +gflags.MarkFlagAsRequired("filename") +gflags.DEFINE_string("output_dir", None, "Output directory") +gflags.MarkFlagAsRequired("output_dir") + + +def ExtractEmbeddedJar(input_archive, filename, output_dir): + with zipfile.ZipFile(input_archive, "r") as archive: + if filename in archive.namelist(): + archive.extract(filename, output_dir) + else: + with zipfile.ZipFile(os.path.join(output_dir, filename), "w") as jar: + # All jar files must contain META-INF/MANIFEST.MF. + jar.writestr("META-INF/MANIFEST.MF", ("Manifest-Version: 1.0\n" + "Created-By: Bazel\n")) + + +def main(): + ExtractEmbeddedJar(FLAGS.input_archive, FLAGS.filename, FLAGS.output_dir) + +if __name__ == "__main__": + FLAGS(sys.argv) + main() diff --git a/tools/zip/embedded_jar_extractor_test.py b/tools/zip/embedded_jar_extractor_test.py new file mode 100644 index 0000000000..568e0b3366 --- /dev/null +++ b/tools/zip/embedded_jar_extractor_test.py @@ -0,0 +1,50 @@ +# 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. + +"""Tests for embedded_jar_extractor.""" + +import filecmp +import os +import unittest +import zipfile + +from tools.zip import embedded_jar_extractor + + +class EmbeddedJarExtractorTest(unittest.TestCase): + """Unit tests for embedded_jar_extractor.py.""" + + def testPassingJarFile(self): + bjar = zipfile.ZipFile("b.jar", "w") + bjar.close() + azip = zipfile.ZipFile("a.zip", "w") + azip.write("b.jar") + azip.close() + if not os.path.exists("output"): + os.mkdir("output") + embedded_jar_extractor.ExtractEmbeddedJar("a.zip", "b.jar", "output") + self.assertTrue(filecmp.cmp("b.jar", "output/b.jar")) + + def testMissingJarFile(self): + azip = zipfile.ZipFile("a.zip", "w") + azip.close() + if not os.path.exists("output"): + os.mkdir("output") + embedded_jar_extractor.ExtractEmbeddedJar("a.zip", "b.jar", "output") + bjar = zipfile.ZipFile("output/b.jar", "r") + self.assertEqual(["META-INF/MANIFEST.MF"], bjar.namelist()) + + +if __name__ == "__main__": + unittest.main() |