BEGIN_PUBLIC
Move aar_import helper tools to rules_android
https://github.com/bazelbuild/rules_android/pull/234
END_PUBLIC
RELNOTES: Deleted Bazel's builtin aar_import helper tools. They live in rules_android now.
PiperOrigin-RevId: 640641470
Change-Id: I60f356bffa2fa932079a1a5fe448ad089ce04315
diff --git a/tools/android/BUILD b/tools/android/BUILD
index 965d768..3ea857e 100644
--- a/tools/android/BUILD
+++ b/tools/android/BUILD
@@ -1,4 +1,4 @@
-load("@rules_python//python:defs.bzl", "py_binary", "py_library", "py_test")
+load("@rules_python//python:defs.bzl", "py_binary", "py_test")
package(default_visibility = ["//tools:__pkg__"])
@@ -81,28 +81,6 @@
)
py_binary(
- name = "aar_native_libs_zip_creator",
- srcs = [
- "aar_native_libs_zip_creator.py",
- ],
- deps = [
- ":json_worker_wrapper",
- ":junction_lib",
- "//third_party/py/abseil",
- ],
-)
-
-py_test(
- name = "aar_native_libs_zip_creator_test",
- srcs = [
- "aar_native_libs_zip_creator_test.py",
- ],
- deps = [
- ":aar_native_libs_zip_creator",
- ],
-)
-
-py_binary(
name = "stubify_manifest",
srcs = ["stubify_manifest.py"],
deps = [
@@ -119,54 +97,6 @@
)
py_binary(
- name = "aar_embedded_proguard_extractor",
- srcs = ["aar_embedded_proguard_extractor.py"],
- deps = [
- ":json_worker_wrapper",
- ":junction_lib",
- "//third_party/py/abseil",
- ],
-)
-
-py_test(
- name = "aar_embedded_proguard_extractor_test",
- srcs = ["aar_embedded_proguard_extractor_test.py"],
- deps = [":aar_embedded_proguard_extractor"],
-)
-
-py_binary(
- name = "aar_embedded_jars_extractor",
- srcs = ["aar_embedded_jars_extractor.py"],
- deps = [
- ":json_worker_wrapper",
- ":junction_lib",
- "//third_party/py/abseil",
- ],
-)
-
-py_test(
- name = "aar_embedded_jars_extractor_test",
- srcs = ["aar_embedded_jars_extractor_test.py"],
- deps = [":aar_embedded_jars_extractor"],
-)
-
-py_binary(
- name = "aar_resources_extractor",
- srcs = ["aar_resources_extractor.py"],
- deps = [
- ":json_worker_wrapper",
- ":junction_lib",
- "//third_party/py/abseil",
- ],
-)
-
-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"],
)
@@ -191,35 +121,6 @@
deps = [":instrumentation_test_check"],
)
-py_library(
- name = "junction_lib",
- srcs = ["junction.py"],
- visibility = ["//visibility:private"],
-)
-
-py_library(
- name = "json_worker_wrapper",
- srcs = ["json_worker_wrapper.py"],
- srcs_version = "PY3",
- visibility = ["//visibility:private"],
-)
-
-py_test(
- name = "junction_test",
- srcs = select({
- "//src/conditions:windows": ["junction_test.py"],
- "//conditions:default": ["dummy_test.py"],
- }),
- main = select({
- "//src/conditions:windows": "junction_test.py",
- "//conditions:default": "dummy_test.py",
- }),
- deps = [
- ":junction_lib",
- "//src/test/py/bazel:test_base",
- ],
-)
-
filegroup(
name = "srcs",
srcs = glob(
diff --git a/tools/android/BUILD.tools b/tools/android/BUILD.tools
index 7e7d3f7..858b109 100644
--- a/tools/android/BUILD.tools
+++ b/tools/android/BUILD.tools
@@ -1,4 +1,4 @@
-load("@rules_python//python:defs.bzl", "py_binary", "py_library")
+load("@rules_python//python:defs.bzl", "py_binary")
load(":defs.bzl", "run_ijar", "run_singlejar")
package(default_visibility = ["//visibility:public"])
@@ -253,19 +253,6 @@
)
py_binary(
- name = "aar_native_libs_zip_creator",
- srcs = [
- "aar_native_libs_zip_creator.py",
- ],
- python_version = "PY3",
- deps = [
- ":junction_lib",
- "//third_party/py/abseil",
- ":json_worker_wrapper",
- ],
-)
-
-py_binary(
name = "stubify_manifest",
srcs = ["stubify_manifest.py"],
python_version = "PY3",
@@ -275,59 +262,11 @@
)
py_binary(
- name = "aar_embedded_proguard_extractor",
- srcs = ["aar_embedded_proguard_extractor.py"],
- python_version = "PY3",
- deps = [
- ":junction_lib",
- "//third_party/py/abseil",
- ":json_worker_wrapper",
- ],
-)
-
-py_binary(
- name = "aar_embedded_jars_extractor",
- srcs = ["aar_embedded_jars_extractor.py"],
- python_version = "PY3",
- deps = [
- ":junction_lib",
- "//third_party/py/abseil",
- ":json_worker_wrapper",
- ],
-)
-
-py_binary(
- name = "aar_resources_extractor",
- srcs = ["aar_resources_extractor.py"],
- python_version = "PY3",
- deps = [
- ":junction_lib",
- "//third_party/py/abseil",
- ":json_worker_wrapper",
- ],
-)
-
-py_binary(
name = "resource_extractor",
srcs = ["resource_extractor.py"],
python_version = "PY3",
)
-py_library(
- name = "junction_lib",
- srcs = ["junction.py"],
- # TODO(bazel-team): remove srcs_version = "PY2AND3" while fixing https://github.com/bazelbuild/bazel/issues/10127.
- srcs_version = "PY2AND3",
- visibility = ["//visibility:private"],
-)
-
-py_library(
- name = "json_worker_wrapper",
- srcs = ["json_worker_wrapper.py"],
- visibility = ["//visibility:private"],
- srcs_version = "PY3",
-)
-
alias(
name = "android_runtest",
actual = "fail.sh",
diff --git a/tools/android/aar_embedded_jars_extractor.py b/tools/android/aar_embedded_jars_extractor.py
deleted file mode 100644
index f6f6836..0000000
--- a/tools/android/aar_embedded_jars_extractor.py
+++ /dev/null
@@ -1,103 +0,0 @@
-# pylint: disable=g-direct-third-party-import
-# 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 all jar files from an AAR.
-
-An AAR may contain JARs at /classes.jar and /libs/*.jar. This tool extracts all
-of the jars and creates a param file for singlejar to merge them into one jar.
-"""
-
-import os
-import re
-import zipfile
-
-# Do not edit this line. Copybara replaces it with PY2 migration helper.
-from absl import app
-from absl import flags
-
-from tools.android import json_worker_wrapper
-from tools.android import junction
-
-FLAGS = flags.FLAGS
-
-flags.DEFINE_string("input_aar", None, "Input AAR")
-flags.mark_flag_as_required("input_aar")
-flags.DEFINE_string("output_singlejar_param_file", None,
- "Output parameter file for singlejar")
-flags.mark_flag_as_required("output_singlejar_param_file")
-flags.DEFINE_string("output_dir", None, "Output directory to extract jars in")
-flags.mark_flag_as_required("output_dir")
-
-
-def ExtractEmbeddedJars(aar,
- singlejar_param_file,
- output_dir,
- output_dir_orig=None):
- """Extracts all embedded jars from an AAR.
-
- Args:
- aar: The aar to extract from
- singlejar_param_file: The param file to pass to singlejar
- output_dir: Where to extract do
- output_dir_orig: The original unshortened path to the params file
- """
- if not output_dir_orig:
- output_dir_orig = output_dir
- jar_pattern = re.compile("^(classes|libs/.+)\\.jar$")
- singlejar_param_file.write(b"--exclude_build_data\n")
- for name in aar.namelist():
- if jar_pattern.match(name):
- singlejar_param_file.write(b"--sources\n")
- # output_dir may be a temporary junction, so write the original
- # (unshortened) path to the params file
- singlejar_param_file.write(
- (output_dir_orig + "/" + name + "\n").encode("utf-8"))
- aar.extract(name, output_dir)
-
-
-def _Main(input_aar,
- output_singlejar_param_file,
- output_dir,
- output_dir_orig=None):
- if not output_dir_orig:
- output_dir_orig = output_dir
- with zipfile.ZipFile(input_aar, "r") as aar:
- with open(output_singlejar_param_file, "wb") as singlejar_param_file:
- ExtractEmbeddedJars(aar, singlejar_param_file, output_dir,
- output_dir_orig)
-
-
-def main(unused_argv):
- if os.name == "nt":
- # Shorten paths unconditionally, because the extracted paths in
- # ExtractEmbeddedJars (which we cannot yet predict, because they depend on
- # the names of the Zip entries) may be longer than MAX_PATH.
- aar_long = os.path.abspath(FLAGS.input_aar)
- params_long = os.path.abspath(FLAGS.output_singlejar_param_file)
- out_long = os.path.abspath(FLAGS.output_dir)
- with junction.TempJunction(os.path.dirname(aar_long)) as aar_junc:
- with junction.TempJunction(os.path.dirname(params_long)) as params_junc:
- with junction.TempJunction(os.path.dirname(out_long)) as out_junc:
- _Main(
- os.path.join(aar_junc, os.path.basename(aar_long)),
- os.path.join(params_junc, os.path.basename(params_long)),
- os.path.join(out_junc, os.path.basename(out_long)),
- FLAGS.output_dir)
- else:
- _Main(FLAGS.input_aar, FLAGS.output_singlejar_param_file, FLAGS.output_dir)
-
-
-if __name__ == "__main__":
- json_worker_wrapper.wrap_worker(FLAGS, main, app.run)
diff --git a/tools/android/aar_embedded_jars_extractor_test.py b/tools/android/aar_embedded_jars_extractor_test.py
deleted file mode 100644
index 6b63520..0000000
--- a/tools/android/aar_embedded_jars_extractor_test.py
+++ /dev/null
@@ -1,87 +0,0 @@
-# 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 aar_embedded_jars_extractor."""
-
-import io
-import os
-import shutil
-import unittest
-import zipfile
-
-from tools.android import aar_embedded_jars_extractor
-
-
-class AarEmbeddedJarsExtractor(unittest.TestCase):
- """Unit tests for aar_embedded_jars_extractor.py."""
-
- # Python 2 alias
- if not hasattr(unittest.TestCase, "assertCountEqual"):
-
- def assertCountEqual(self, *args):
- return self.assertItemsEqual(*args)
-
- def setUp(self):
- os.chdir(os.environ["TEST_TMPDIR"])
-
- def tearDown(self):
- shutil.rmtree("out_dir")
-
- def testNoJars(self):
- aar = zipfile.ZipFile(io.BytesIO(), "w")
- param_file = io.BytesIO()
- os.makedirs("out_dir")
- aar_embedded_jars_extractor.ExtractEmbeddedJars(aar, param_file, "out_dir")
- self.assertEqual([], os.listdir("out_dir"))
- param_file.seek(0)
- self.assertEqual(b"--exclude_build_data\n", param_file.read())
-
- def testClassesJarAndLibsJars(self):
- aar = zipfile.ZipFile(io.BytesIO(), "w")
- aar.writestr("classes.jar", "")
- aar.writestr("libs/a.jar", "")
- aar.writestr("libs/b.jar", "")
- param_file = io.BytesIO()
- os.makedirs("out_dir")
- aar_embedded_jars_extractor.ExtractEmbeddedJars(aar, param_file, "out_dir")
- self.assertCountEqual(["classes.jar", "libs"], os.listdir("out_dir"))
- self.assertCountEqual(["a.jar", "b.jar"], os.listdir("out_dir/libs"))
- param_file.seek(0)
- self.assertEqual(
- [b"--exclude_build_data\n",
- b"--sources\n",
- b"out_dir/classes.jar\n",
- b"--sources\n",
- b"out_dir/libs/a.jar\n",
- b"--sources\n",
- b"out_dir/libs/b.jar\n"],
- param_file.readlines())
-
- def testOnlyClassesJar(self):
- aar = zipfile.ZipFile(io.BytesIO(), "w")
- aar.writestr("classes.jar", "")
- param_file = io.BytesIO()
- os.makedirs("out_dir")
- aar_embedded_jars_extractor.ExtractEmbeddedJars(aar, param_file, "out_dir")
- self.assertEqual(["classes.jar"], os.listdir("out_dir"))
- param_file.seek(0)
- self.assertEqual(
- [b"--exclude_build_data\n",
- b"--sources\n",
- b"out_dir/classes.jar\n"],
- param_file.readlines())
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/tools/android/aar_embedded_proguard_extractor.py b/tools/android/aar_embedded_proguard_extractor.py
deleted file mode 100644
index c8f5c2c..0000000
--- a/tools/android/aar_embedded_proguard_extractor.py
+++ /dev/null
@@ -1,74 +0,0 @@
-# pylint: disable=g-direct-third-party-import
-# Copyright 2021 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 the proguard spec file from an AAR."""
-
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
-import os
-import zipfile
-
-# Do not edit this line. Copybara replaces it with PY2 migration helper.
-from absl import app
-from absl import flags
-
-from tools.android import json_worker_wrapper
-from tools.android import junction
-
-FLAGS = flags.FLAGS
-
-flags.DEFINE_string("input_aar", None, "Input AAR")
-flags.mark_flag_as_required("input_aar")
-flags.DEFINE_string("output_proguard_file", None,
- "Output parameter file for proguard")
-flags.mark_flag_as_required("output_proguard_file")
-
-
-# Attempt to extract proguard spec from AAR. If the file doesn't exist, an empty
-# proguard spec file will be created
-def ExtractEmbeddedProguard(aar, output):
- proguard_spec = "proguard.txt"
-
- if proguard_spec in aar.namelist():
- output.write(aar.read(proguard_spec))
-
-
-def _Main(input_aar, output_proguard_file):
- with zipfile.ZipFile(input_aar, "r") as aar:
- with open(output_proguard_file, "wb") as output:
- ExtractEmbeddedProguard(aar, output)
-
-
-def main(unused_argv):
- if os.name == "nt":
- # Shorten paths unconditionally, because the extracted paths in
- # ExtractEmbeddedJars (which we cannot yet predict, because they depend on
- # the names of the Zip entries) may be longer than MAX_PATH.
- aar_long = os.path.abspath(FLAGS.input_aar)
- proguard_long = os.path.abspath(FLAGS.output_proguard_file)
-
- with junction.TempJunction(os.path.dirname(aar_long)) as aar_junc:
- with junction.TempJunction(
- os.path.dirname(proguard_long)) as proguard_junc:
- _Main(
- os.path.join(aar_junc, os.path.basename(aar_long)),
- os.path.join(proguard_junc, os.path.basename(proguard_long)))
- else:
- _Main(FLAGS.input_aar, FLAGS.output_proguard_file)
-
-
-if __name__ == "__main__":
- json_worker_wrapper.wrap_worker(FLAGS, main, app.run)
diff --git a/tools/android/aar_embedded_proguard_extractor_test.py b/tools/android/aar_embedded_proguard_extractor_test.py
deleted file mode 100644
index 457520f..0000000
--- a/tools/android/aar_embedded_proguard_extractor_test.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# Copyright 2021 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_embedded_proguard_extractor."""
-
-import io
-import os
-import unittest
-import zipfile
-
-from tools.android import aar_embedded_proguard_extractor
-
-
-class AarEmbeddedProguardExtractor(unittest.TestCase):
- """Unit tests for aar_embedded_proguard_extractor.py."""
-
- # Python 2 alias
- if not hasattr(unittest.TestCase, "assertCountEqual"):
-
- def assertCountEqual(self, *args):
- return self.assertItemsEqual(*args)
-
- def setUp(self):
- super(AarEmbeddedProguardExtractor, self).setUp()
- os.chdir(os.environ["TEST_TMPDIR"])
-
- def testNoProguardTxt(self):
- aar = zipfile.ZipFile(io.BytesIO(), "w")
- proguard_file = io.BytesIO()
- aar_embedded_proguard_extractor.ExtractEmbeddedProguard(aar, proguard_file)
- proguard_file.seek(0)
- self.assertEqual(b"", proguard_file.read())
-
- def testWithProguardTxt(self):
- aar = zipfile.ZipFile(io.BytesIO(), "w")
- aar.writestr("proguard.txt", "hello world")
- proguard_file = io.BytesIO()
- aar_embedded_proguard_extractor.ExtractEmbeddedProguard(aar, proguard_file)
- proguard_file.seek(0)
- self.assertEqual(b"hello world", proguard_file.read())
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/tools/android/aar_native_libs_zip_creator.py b/tools/android/aar_native_libs_zip_creator.py
deleted file mode 100644
index 6f6306e..0000000
--- a/tools/android/aar_native_libs_zip_creator.py
+++ /dev/null
@@ -1,108 +0,0 @@
-# pylint: disable=g-direct-third-party-import
-# 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 native libs from an AAR into a zip.
-
-The native libs for the requested cpu will be extracted into a zip. The paths
-are converted from the AAR directory structure of /jni/<cpu>/foo.so to the APK
-directory structure of /lib/<cpu>/foo.so.
-"""
-
-import os
-import re
-import sys
-import zipfile
-
-# Do not edit this line. Copybara replaces it with PY2 migration helper.
-from absl import app
-from absl import flags
-
-from tools.android import json_worker_wrapper
-from tools.android import junction
-
-FLAGS = flags.FLAGS
-
-flags.DEFINE_string("input_aar", None, "Input AAR")
-flags.mark_flag_as_required("input_aar")
-flags.DEFINE_string("cpu", None, "CPU architecture to include")
-flags.mark_flag_as_required("cpu")
-flags.DEFINE_string("output_zip", None, "Output ZIP of native libs")
-flags.mark_flag_as_required("output_zip")
-
-
-class UnsupportedArchitectureError(Exception):
- """Exception thrown when an AAR does not support the requested CPU."""
- pass
-
-
-def CreateNativeLibsZip(aar, cpu, native_libs_zip):
- """Creates a zip containing native libs for the requested CPU.
-
- Args:
- aar: aar file to extract
- cpu: The requested CPU architecture
- native_libs_zip: The zip file to package native libs into.
-
- Raises:
- UnsupportedArchitectureError: CPU architecture is invalid.
- """
- native_lib_pattern = re.compile("^jni/.+/.+\\.so$")
- if any(native_lib_pattern.match(filename) for filename in aar.namelist()):
- cpu_pattern = re.compile("^jni/" + cpu + "/.+\\.so$")
- libs = [name for name in aar.namelist() if cpu_pattern.match(name)]
- if not libs:
- raise UnsupportedArchitectureError()
- for lib in libs:
- # Only replaces the first instance of jni, in case the AAR contains
- # something like /jni/x86/jni.so.
- new_filename = lib.replace("jni", "lib", 1)
- # To guarantee reproducible zips we must specify a new zipinfo.
- # From writestr docs: "If its a name, the date and time is set to the
- # current date and time." which will break the HASH calculation and result
- # in a cache miss.
- old_zipinfo = aar.getinfo(lib)
- new_zipinfo = zipfile.ZipInfo(filename=new_filename)
- new_zipinfo.date_time = old_zipinfo.date_time
- new_zipinfo.compress_type = old_zipinfo.compress_type
-
- native_libs_zip.writestr(new_zipinfo, aar.read(lib))
-
-
-def Main(input_aar_path, output_zip_path, cpu, input_aar_path_for_error_msg):
- with zipfile.ZipFile(input_aar_path, "r") as input_aar:
- with zipfile.ZipFile(output_zip_path, "w") as native_libs_zip:
- try:
- CreateNativeLibsZip(input_aar, cpu, native_libs_zip)
- except UnsupportedArchitectureError:
- print("AAR " + input_aar_path_for_error_msg +
- " missing native libs for requested architecture: " +
- cpu)
- sys.exit(1)
-
-
-def main(unused_argv):
- if os.name == "nt":
- with junction.TempJunction(os.path.dirname(FLAGS.input_aar)) as j_in:
- with junction.TempJunction(os.path.dirname(FLAGS.output_zip)) as j_out:
- Main(
- os.path.join(j_in, os.path.basename(FLAGS.input_aar)),
- os.path.join(j_out, os.path.basename(FLAGS.output_zip)), FLAGS.cpu,
- FLAGS.input_aar)
- else:
- Main(FLAGS.input_aar, FLAGS.output_zip, FLAGS.cpu, FLAGS.input_aar)
-
-
-if __name__ == "__main__":
- json_worker_wrapper.wrap_worker(FLAGS, main, app.run)
diff --git a/tools/android/aar_native_libs_zip_creator_test.py b/tools/android/aar_native_libs_zip_creator_test.py
deleted file mode 100644
index e4d1e66..0000000
--- a/tools/android/aar_native_libs_zip_creator_test.py
+++ /dev/null
@@ -1,114 +0,0 @@
-# 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 aar_native_libs_zip_creator."""
-
-import hashlib
-import io
-import tempfile
-import time
-import unittest
-import zipfile
-
-from tools.android import aar_native_libs_zip_creator
-
-
-def md5(buf):
- hash_md5 = hashlib.md5()
- hash_md5.update(buf)
- return hash_md5.hexdigest()
-
-
-def md5_from_file(fname):
- hash_md5 = hashlib.md5()
- with open(fname, "rb") as f:
- for chunk in iter(lambda: f.read(4096), b""):
- hash_md5.update(chunk)
- return hash_md5.hexdigest()
-
-
-class AarNativeLibsZipCreatorTest(unittest.TestCase):
- """Unit tests for aar_native_libs_zip_creator.py."""
-
- def testAarWithNoLibs(self):
- aar = zipfile.ZipFile(io.BytesIO(), "w")
- outzip = zipfile.ZipFile(io.BytesIO(), "w")
- aar_native_libs_zip_creator.CreateNativeLibsZip(aar, "x86", outzip)
- self.assertEqual([], outzip.namelist())
-
- def testAarWithMissingLibs(self):
- aar = zipfile.ZipFile(io.BytesIO(), "w")
- aar.writestr("jni/armeabi/foo.so", "")
- outzip = zipfile.ZipFile(io.BytesIO(), "w")
- self.assertRaises(
- aar_native_libs_zip_creator.UnsupportedArchitectureError,
- aar_native_libs_zip_creator.CreateNativeLibsZip,
- aar, "x86", outzip)
-
- def testAarWithAllLibs(self):
- aar = zipfile.ZipFile(io.BytesIO(), "w")
- aar.writestr("jni/x86/foo.so", "")
- aar.writestr("jni/armeabi/foo.so", "")
- outzip = zipfile.ZipFile(io.BytesIO(), "w")
- aar_native_libs_zip_creator.CreateNativeLibsZip(aar, "x86", outzip)
- self.assertIn("lib/x86/foo.so", outzip.namelist())
- self.assertNotIn("lib/armeabi/foo.so", outzip.namelist())
-
- def testMultipleInvocationConsistency(self):
- input_aar = tempfile.NamedTemporaryFile(delete=False)
- aar = zipfile.ZipFile(input_aar.name, "w")
- aar.writestr(zipfile.ZipInfo(filename="jni/x86/foo.so"), "foo")
- aar.writestr(zipfile.ZipInfo(filename="jni/x86/bar.so"), "bar")
- aar.close()
- input_aar.close()
- # CreateNativeLibsZip expects a readonly file, this is not required but
- # more correct
- readonly_aar = zipfile.ZipFile(input_aar.name, "r")
-
- outfile1 = tempfile.NamedTemporaryFile(delete=False)
- outzip1 = zipfile.ZipFile(outfile1.name, "w")
- aar_native_libs_zip_creator.CreateNativeLibsZip(readonly_aar, "x86",
- outzip1)
- outfile1.close()
-
- # Must be more than 1 second because last modified date changes on second
- # basis
- time.sleep(2)
-
- outfile2 = tempfile.NamedTemporaryFile(delete=False)
- outzip2 = zipfile.ZipFile(outfile2.name, "w")
- aar_native_libs_zip_creator.CreateNativeLibsZip(readonly_aar, "x86",
- outzip2)
- outfile2.close()
-
- self.assertIn("lib/x86/foo.so", outzip1.namelist())
- self.assertIn("lib/x86/bar.so", outzip1.namelist())
- self.assertNotEqual(
- md5(outzip1.read("lib/x86/foo.so")),
- md5(outzip1.read("lib/x86/bar.so")))
-
- self.assertIn("lib/x86/foo.so", outzip2.namelist())
- self.assertIn("lib/x86/bar.so", outzip2.namelist())
- self.assertNotEqual(
- md5(outzip1.read("lib/x86/foo.so")),
- md5(outzip1.read("lib/x86/bar.so")))
-
- # The hash for the output zips must always match if the inputs match.
- # Otherwise, there will be a cache miss which will produce poort build
- # times.
- self.assertEqual(md5_from_file(outfile1.name), md5_from_file(outfile2.name))
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/tools/android/aar_resources_extractor.py b/tools/android/aar_resources_extractor.py
deleted file mode 100644
index f2e49ab..0000000
--- a/tools/android/aar_resources_extractor.py
+++ /dev/null
@@ -1,148 +0,0 @@
-# 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 zipfile
-
-# Do not edit this line. Copybara replaces it with PY2 migration helper.
-from absl import app
-from absl import flags
-
-from tools.android import json_worker_wrapper
-from tools.android import junction
-
-FLAGS = flags.FLAGS
-
-flags.DEFINE_string("input_aar", None, "Input AAR")
-flags.mark_flag_as_required("input_aar")
-flags.DEFINE_string("output_res_dir", None, "Output resources directory")
-flags.mark_flag_as_required("output_res_dir")
-flags.DEFINE_string("output_assets_dir", None, "Output assets directory")
-flags.DEFINE_string("output_databinding_br_dir", None,
- "Output directory for databinding br files")
-flags.DEFINE_string("output_databinding_setter_store_dir", None,
- "Output directory for databinding setter_store.json files")
-
-
-def ExtractResources(aar, output_res_dir):
- """Extract resource from an `aar` file to the `output_res_dir` directory."""
- aar_contains_no_resources = True
- output_res_dir_abs = os.path.abspath(output_res_dir)
- for name in aar.namelist():
- if name.startswith("res/") and not name.endswith("/"):
- ExtractOneFile(aar, name, output_res_dir_abs)
- aar_contains_no_resources = False
- if aar_contains_no_resources:
- empty_xml_filename = output_res_dir + "/res/values/empty.xml"
- WriteFileWithJunctions(empty_xml_filename, b"<resources/>")
-
-
-def ExtractAssets(aar, output_assets_dir):
- """Extracts assets from an `aar` file to the `output_assets_dir` directory."""
- aar_contains_no_assets = True
- output_assets_dir_abs = os.path.abspath(output_assets_dir)
- for name in aar.namelist():
- if name.startswith("assets/") and not name.endswith("/"):
- ExtractOneFile(aar, name, output_assets_dir_abs)
- aar_contains_no_assets = False
- if aar_contains_no_assets:
- # aapt will ignore this file and not print an error message, because it
- # thinks that it is a swap file. We need to create at least one file so that
- # Bazel does not complain that the output tree artifact was not created.
- empty_asset_filename = (
- output_assets_dir +
- "/assets/empty_asset_generated_by_bazel~")
- WriteFileWithJunctions(empty_asset_filename, b"")
-
-
-def ExtractDatabinding(aar, file_suffix, output_databinding_dir):
- """Extracts databinding metadata files from an `aar`."""
- output_databinding_dir_abs = os.path.abspath(output_databinding_dir)
- for name in aar.namelist():
- if name.startswith("data-binding/") and name.endswith(file_suffix):
- ExtractOneFile(aar, name, output_databinding_dir_abs)
-
-
-def WriteFileWithJunctions(filename, content):
- """Writes file including creating any junctions or directories necessary."""
- def _WriteFile(filename):
- with open(filename, "wb") as openfile:
- openfile.write(content)
-
- if os.name == "nt":
- # Create a junction to the parent directory, because its path might be too
- # long. Creating the junction also creates all parent directories.
- with junction.TempJunction(os.path.dirname(filename)) as junc:
- filename = os.path.join(junc, os.path.basename(filename))
- # Write the file within scope of the TempJunction, otherwise the path in
- # `filename` would no longer be valid.
- _WriteFile(filename)
- else:
- os.makedirs(os.path.dirname(filename))
- _WriteFile(filename)
-
-
-def ExtractOneFile(aar, name, abs_output_dir):
- """Extract one file from the aar to the output directory."""
- if os.name == "nt":
- fullpath = os.path.normpath(os.path.join(abs_output_dir, name))
- if name[-1] == "/":
- # The zip entry is a directory. Create a junction to it, which also
- # takes care of creating the directory and all of its parents in a
- # longpath-safe manner.
- # We must pretend to have extracted this directory, even if it's
- # empty, therefore we mustn't rely on creating it as a parent
- # directory of a subsequently extracted zip entry (because there may
- # be no such subsequent entry).
- with junction.TempJunction(fullpath.rstrip("/")) as juncpath:
- pass
- else:
- # The zip entry is a file. Create a junction to its parent directory,
- # then open the compressed entry as a file object, so we can extract
- # the data even if the extracted file's path would be too long.
- # The tradeoff is that we lose the permission bits of the compressed
- # file, but Unix permissions don't mean much on Windows anyway.
- with junction.TempJunction(os.path.dirname(fullpath)) as juncpath:
- extracted_path = os.path.join(juncpath, os.path.basename(fullpath))
- with aar.open(name) as src_fd:
- with open(extracted_path, "wb") as dest_fd:
- dest_fd.write(src_fd.read())
- else:
- aar.extract(name, abs_output_dir)
-
-
-def main(unused_argv):
- with zipfile.ZipFile(FLAGS.input_aar, "r") as aar:
- ExtractResources(aar, FLAGS.output_res_dir)
- if FLAGS.output_assets_dir is not None:
- ExtractAssets(aar, FLAGS.output_assets_dir)
- if FLAGS.output_databinding_br_dir is not None:
- ExtractDatabinding(aar, "br.bin", FLAGS.output_databinding_br_dir)
- if FLAGS.output_databinding_setter_store_dir is not None:
- ExtractDatabinding(aar, "setter_store.json",
- FLAGS.output_databinding_setter_store_dir)
-
-
-if __name__ == "__main__":
- json_worker_wrapper.wrap_worker(FLAGS, main, app.run)
diff --git a/tools/android/aar_resources_extractor_test.py b/tools/android/aar_resources_extractor_test.py
deleted file mode 100644
index e9ec661..0000000
--- a/tools/android/aar_resources_extractor_test.py
+++ /dev/null
@@ -1,137 +0,0 @@
-# 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 io
-import os
-import shutil
-import unittest
-import zipfile
-
-# Do not edit this line. Copybara replaces it with PY2 migration helper.
-
-from tools.android import aar_resources_extractor
-
-
-def _HostPath(path):
- return os.path.normpath(path)
-
-
-class AarResourcesExtractorTest(unittest.TestCase):
- """Unit tests for aar_resources_extractor.py."""
-
- # Python 2 alias
- if not hasattr(unittest.TestCase, "assertCountEqual"):
-
- def assertCountEqual(self, *args):
- return self.assertItemsEqual(*args)
-
- def setUp(self):
- os.chdir(os.environ["TEST_TMPDIR"])
-
- def tearDown(self):
- shutil.rmtree("out_dir")
-
- def DirContents(self, d):
- return [
- _HostPath(path + "/" + f)
- for (path, _, files) in os.walk(d)
- for f in files
- ]
-
- def testNoResources(self):
- aar = zipfile.ZipFile(io.BytesIO(), "w")
- aar.writestr("res/", "")
- os.makedirs("out_dir")
- aar_resources_extractor.ExtractResources(aar, "out_dir")
- self.assertEqual([_HostPath("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(io.BytesIO(), "w")
- aar.writestr("res/values/values.xml", "some values")
- aar.writestr("res/layouts/layout.xml", "some layout")
- aar.writestr("assets/a", "some asset")
- os.makedirs("out_dir")
- aar_resources_extractor.ExtractResources(aar, "out_dir")
- expected_resources = [
- _HostPath("out_dir/res/values/values.xml"),
- _HostPath("out_dir/res/layouts/layout.xml")
- ]
- self.assertCountEqual(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())
-
- def testNoAssets(self):
- aar = zipfile.ZipFile(io.BytesIO(), "w")
- aar.writestr("assets/", "")
- os.makedirs("out_dir")
- aar_resources_extractor.ExtractAssets(aar, "out_dir")
- expected_assets = [
- _HostPath("out_dir/assets/empty_asset_generated_by_bazel~")
- ]
- self.assertEqual(expected_assets, self.DirContents("out_dir"))
- self.assertEqual(
- os.stat("out_dir/assets/empty_asset_generated_by_bazel~").st_size, 0)
-
- def testContainsAssets(self):
- aar = zipfile.ZipFile(io.BytesIO(), "w")
- aar.writestr("res/values/values.xml", "some values")
- aar.writestr("assets/a", "some asset")
- aar.writestr("assets/b", "some other asset")
- os.makedirs("out_dir")
- aar_resources_extractor.ExtractAssets(aar, "out_dir")
- expected_resources = [
- _HostPath("out_dir/assets/a"),
- _HostPath("out_dir/assets/b")
- ]
- self.assertCountEqual(expected_resources, self.DirContents("out_dir"))
- with open("out_dir/assets/a", "r") as values_xml:
- self.assertEqual("some asset", values_xml.read())
- with open("out_dir/assets/b", "r") as layout_xml:
- self.assertEqual("some other asset", layout_xml.read())
-
- def testDatabinding(self):
- aar = zipfile.ZipFile(io.BytesIO(), "w")
-
- br_filepath = (
- "data-binding/com.android.databinding.library.baseAdapters--br.bin")
- setter_store_filepath = (
- "data-binding/" +
- "com.android.databinding.library.baseAdapters--setter_store.json")
-
- aar.writestr(br_filepath, "br data")
- aar.writestr(setter_store_filepath, "setter store data")
-
- os.makedirs("out_dir/br")
- os.makedirs("out_dir/setter_store")
-
- aar_resources_extractor.ExtractDatabinding(aar, "br.bin", "out_dir/br")
- aar_resources_extractor.ExtractDatabinding(aar, "setter_store.json",
- "out_dir/setter_store")
-
- with open("out_dir/br/" + br_filepath, "r") as f:
- self.assertEqual("br data", f.read())
-
- with open("out_dir/setter_store/" + setter_store_filepath, "r") as f:
- self.assertEqual("setter store data", f.read())
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/tools/android/json_worker_wrapper.py b/tools/android/json_worker_wrapper.py
deleted file mode 100644
index ca8e9f7..0000000
--- a/tools/android/json_worker_wrapper.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# pylint: disable=g-direct-third-party-import
-# Copyright 2023 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 wrapper for AAR extractors to support persistent worker."""
-
-import json
-import sys
-
-
-def wrap_worker(flags, main, app_run):
- """When the --persistent_worker flag is set, it runs in worker mode, otherwise it runs the main method directly.
-
- Args:
- flags: The FlagValues instance to parse command line arguments.
- main: The main function to execute.
- app_run: app.run() function in Abseil.
- """
- if sys.argv[1] == "--persistent_worker":
- if "--run_with_pdb" in sys.argv or "--run_with_profiling" in sys.argv:
- sys.stderr.write("pdb and profiling are not supported in worker mode.\n")
- sys.exit(1)
- while True:
- # parse work requests from stdin
- request_str = sys.stdin.readline()
- work_request = json.loads(request_str)
-
- args = [sys.argv[0]] + work_request["arguments"]
- flags(args)
- _wrap_result(main, args)
- else:
- flags(sys.argv)
- app_run(main)
-
-
-def _wrap_result(main, args):
- """Execute main function and return worker response.
-
- Args:
- main: The main function to execute.
- args: A non-empty list of the command line arguments including program name.
- """
- response = {
- "exitCode": 0,
- "output": "",
- }
- main(args)
- sys.stdout.write(json.dumps(response))
- sys.stdout.flush()
diff --git a/tools/android/junction.py b/tools/android/junction.py
deleted file mode 100644
index 19105bb..0000000
--- a/tools/android/junction.py
+++ /dev/null
@@ -1,173 +0,0 @@
-# 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 class that creates junctions in temp directories on Windows.
-
-Only use this class on Windows, do not use on other platforms. Other platforms
-support longer paths than Windows, and also support symlinks. Windows only
-supports junctions (directory symlinks).
-
-Junctions are useful if you need to shorten a long path. A long path is one that
-is at least MAX_PATH (260) letters long. This is a constant in Windows, see
-<windows.h> and API documentation for file-handling functions such as
-CreateFileA.
-"""
-
-import os
-import subprocess
-import tempfile
-
-
-class JunctionCreationError(Exception):
- """Raised when TempJunction fails to create an NTFS junction."""
-
- def __init__(self, path, target, stdout):
- Exception.__init__(
- self,
- "Could not create junction \"%s\" -> \"%s\"\nError from mklink:\n%s" %
- (path, target, stdout))
-
-
-class TempJunction(object):
- r"""Junction in a temp directory.
-
- This object creates a temp directory and a junction under it. The junction
- points to a user-specified path.
-
- Use this object if you want to write files under long paths (absolute path at
- least MAX_PATH (260) chars long). Pass the directory you want to "shorten" as
- the initializer's argument. This object will create a junction under a shorter
- path, that points to the long directory. The combined path of the junction and
- files under it are more likely to be short than the original paths were.
-
- Usage example:
- with TempJunction("C:/some/long/path") as junc:
- # `junc` created a temp directory and a junction in it that points to
- # \\?\C:\some\long\path, and is itself shorter than that
- shortpath = os.path.join(junc, "file.txt")
- with open(shortpath, "w") as f:
- ...do something with f...
- # `junc` deleted the junction and its parent temp directory upon leaving
- # the `with` statement's body
- ...do something else...
- """
-
- def __init__(self,
- junction_target,
- testonly_mkdtemp=None,
- testonly_maxpath=None):
- """Initialize this object.
-
- Args:
- junction_target: string; an absolute Windows path; the __enter__ method
- creates a junction that points to this path
- testonly_mkdtemp: function(); for testing only; a custom function that
- returns a temp directory path, you can use it to mock out
- tempfile.mkdtemp
- testonly_maxpath: int; for testing only; maximum path length before the
- path is a "long path" (typically MAX_PATH on Windows)
- """
- self._target = os.path.abspath(junction_target)
- self._junction = None
- self._mkdtemp = testonly_mkdtemp or tempfile.mkdtemp
- self._max_path = testonly_maxpath or 248
-
- @staticmethod
- def _Mklink(name, target):
- proc = subprocess.Popen(
- "cmd.exe /C mklink /J \"%s\" \"\\\\?\\%s\"" % (name, target),
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT)
- exitcode = proc.wait()
- if exitcode != 0:
- stdout = proc.communicate()[0]
- raise JunctionCreationError(name, target, stdout)
-
- @staticmethod
- def _TryMkdir(path):
- try:
- os.mkdir(path)
- except OSError as e:
- # Another process may have already created this directory.
- if not os.path.isdir(path):
- raise IOError("Could not create directory at '%s': %s" % (path, str(e)))
-
- @staticmethod
- def _MakeLinks(target, mkdtemp, max_path):
- """Creates a temp directory and a junction in it, pointing to `target`.
-
- Creates all parent directories of `target` if they don't exist.
-
- Args:
- target: string; path to the directory that is the junction's target
- mkdtemp: function():string; creates a temp directory and returns its
- absolute path
- max_path: int; maximum path length before the path is a "long path"
- (typically MAX_PATH on Windows)
- Returns:
- The full path to the junction.
- Raises:
- JunctionCreationError: if `mklink` fails to create a junction
- """
- segments = []
- dirpath = target
- while not os.path.isdir(dirpath):
- dirpath, child = os.path.split(dirpath)
- if child:
- segments.append(child)
- tmp = mkdtemp()
- juncpath = os.path.join(tmp, "j")
- for child in reversed(segments):
- childpath = os.path.join(dirpath, child)
- if len(childpath) >= max_path:
- try:
- TempJunction._Mklink(juncpath, dirpath)
- TempJunction._TryMkdir(os.path.join(juncpath, child))
- finally:
- os.rmdir(juncpath)
- else:
- TempJunction._TryMkdir(childpath)
- dirpath = childpath
- TempJunction._Mklink(juncpath, target)
- return juncpath
-
- def __enter__(self):
- """Creates a temp directory and a junction in it, pointing to self._target.
-
- Creates all parent directories of self._target if they don't exist.
-
- This method is automatically called upon entering a `with` statement's body.
-
- Returns:
- The full path to the junction.
- Raises:
- JunctionCreationError: if `mklink` fails to create a junction
- """
- self._junction = TempJunction._MakeLinks(self._target, self._mkdtemp,
- self._max_path)
- return self._junction
-
- def __exit__(self, unused_type, unused_value, unused_traceback):
- """Deletes the junction and its parent directory.
-
- This method is automatically called upon leaving a `with` statement's body.
-
- Args:
- unused_type: unused
- unused_value: unused
- unused_traceback: unused
- """
- if self._junction:
- os.rmdir(self._junction)
- os.rmdir(os.path.dirname(self._junction))