diff options
author | Laszlo Csomor <laszlocsomor@google.com> | 2017-09-27 07:38:03 -0400 |
---|---|---|
committer | John Cater <jcater@google.com> | 2017-09-27 10:01:41 -0400 |
commit | c60bfdf2b665888cbc22d8f35ea312a439e136b3 (patch) | |
tree | 7f12bc7267eb34c7cf48ec2e05b682657cde3651 | |
parent | ec91d20a0960ad9d67d27db7c8d1ecdd425d8085 (diff) |
Windows, aar_resources_extractor: longpath bugfix
Fix a bug in the Android tool
aar_resources_extractor: open the compressed data
and the target file as file descriptores, rather
than using aar.extract.
This way we can shorten the directories in the
relative path of the extracted file, because we
can open the target file via a junction and thus
work around long path limitations.
//tools/android:aar_resources_extractor_test now
passes on Windows.
Change-Id: I9f5c7de2279ad8769e53e015b8334a58a76cd989
PiperOrigin-RevId: 170181143
-rw-r--r-- | tools/android/aar_resources_extractor.py | 46 | ||||
-rw-r--r-- | tools/android/aar_resources_extractor_test.py | 18 |
2 files changed, 49 insertions, 15 deletions
diff --git a/tools/android/aar_resources_extractor.py b/tools/android/aar_resources_extractor.py index eb0d432027..61f9f4d397 100644 --- a/tools/android/aar_resources_extractor.py +++ b/tools/android/aar_resources_extractor.py @@ -40,23 +40,49 @@ gflags.MarkFlagAsRequired("output_res_dir") 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.normpath( - os.path.join(os.getcwd(), output_res_dir)) + output_res_dir_abs = os.path.abspath(output_res_dir) for name in aar.namelist(): if name.startswith("res/"): - fullpath = os.path.normpath(os.path.join(output_res_dir_abs, name)) - if os.name == "nt" and len(fullpath) >= 260: # MAX_PATH in <windows.h> - with junction.TempJunction(os.path.dirname(fullpath)) as juncpath: - shortpath = os.path.join(juncpath, os.path.basename(fullpath)) - aar.extract(name, shortpath) + if os.name == "nt": + fullpath = os.path.normpath(os.path.join(output_res_dir_abs, 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, 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/>") + 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(empty_xml_filename)) as junc: + xmlpath = os.path.join(junc, os.path.basename(empty_xml_filename)) + with open(xmlpath, "wb") as empty_xml: + empty_xml.write("<resources/>") + else: + os.makedirs(os.path.dirname(empty_xml_filename)) + with open(empty_xml_filename, "wb") as empty_xml: + empty_xml.write("<resources/>") def main(): diff --git a/tools/android/aar_resources_extractor_test.py b/tools/android/aar_resources_extractor_test.py index b59e4160cb..d5f630b352 100644 --- a/tools/android/aar_resources_extractor_test.py +++ b/tools/android/aar_resources_extractor_test.py @@ -23,6 +23,10 @@ import zipfile 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.""" @@ -33,14 +37,16 @@ class AarResourcesExtractorTest(unittest.TestCase): shutil.rmtree("out_dir") def DirContents(self, d): - return [path + "/" + f for (path, _, files) in os.walk(d) - for f in files] + return [ + _HostPath(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.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()) @@ -51,8 +57,10 @@ class AarResourcesExtractorTest(unittest.TestCase): 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"] + expected_resources = [ + _HostPath("out_dir/res/values/values.xml"), + _HostPath("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()) |