|  | # pylint: disable=g-bad-file-header | 
|  | # 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. | 
|  | """Utils to the contents of a tar or zip file into another zip file.""" | 
|  |  | 
|  | import contextlib | 
|  | import os.path | 
|  | import stat | 
|  | import tarfile | 
|  | import zipfile | 
|  |  | 
|  |  | 
|  | def is_mode_executable(mode): | 
|  | """Returns true if `mode` has any of the executable bits set.""" | 
|  | return mode & (stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) > 0 | 
|  |  | 
|  |  | 
|  | def is_executable(path): | 
|  | """Returns true if `path` is an executable file/directory.""" | 
|  | return is_mode_executable(os.stat(path)[stat.ST_MODE]) | 
|  |  | 
|  |  | 
|  | def copy_tar_to_zip(output_zip, input_file, process_filename=None): | 
|  | """Copy a tar file's contents into a zip file. | 
|  |  | 
|  | This function unpacks every file from `input_file` and puts them into | 
|  | `output_zip`. The unpacking is performed in-memory. | 
|  |  | 
|  | Args: | 
|  | output_zip: zipfile.ZipFile; the destination archive | 
|  | input_file: string; path to the source tar file | 
|  | process_filename: function(str) -> str; optional; for a packed file entry in | 
|  | `input_file` it computes the path in `output_zip` | 
|  | """ | 
|  | with tarfile.open(input_file, 'r', errorlevel=2) as tar_file: | 
|  | while True: | 
|  | tar_entry = tar_file.next() | 
|  | if tar_entry is None: | 
|  | break | 
|  | filename = (process_filename(tar_entry.name) | 
|  | if process_filename else tar_entry.name) | 
|  | zipinfo = zipfile.ZipInfo(filename, (1980, 1, 1, 0, 0, 0)) | 
|  | if tar_entry.isreg(): | 
|  | if is_mode_executable(tar_entry.mode): | 
|  | zipinfo.external_attr = 0o755 << 16 | 
|  | else: | 
|  | zipinfo.external_attr = 0o644 << 16 | 
|  | zipinfo.compress_type = zipfile.ZIP_DEFLATED | 
|  | output_zip.writestr(zipinfo, tar_file.extractfile(tar_entry).read()) | 
|  | elif tar_entry.issym(): | 
|  | # 0120000 originally comes from the definition of S_IFLNK and | 
|  | # marks a symbolic link in the Zip file format. | 
|  | zipinfo.external_attr = 0o120000 << 16 | 
|  | output_zip.writestr(zipinfo, tar_entry.linkname) | 
|  | else: | 
|  | # Ignore directories, hard links, special files, ... | 
|  | pass | 
|  |  | 
|  |  | 
|  | def copy_zip_to_zip(output_zip, input_file, process_filename=None): | 
|  | """Copy a zip file's contents into another zip file. | 
|  |  | 
|  | This function unpacks every file from `input_file` and puts them into | 
|  | `output_zip`. The unpacking is performed in-memory. | 
|  |  | 
|  | Args: | 
|  | output_zip: zipfile.ZipFile; the destination archive | 
|  | input_file: string; path to the source tar file | 
|  | process_filename: function(str) -> str; optional; for a packed file entry in | 
|  | `input_file` it computes the path in `output_zip` | 
|  | """ | 
|  | # Adding contextlib.closing to be python 2.6 (for centos 6.7) compatible | 
|  | with contextlib.closing(zipfile.ZipFile(input_file, 'r')) as zip_file: | 
|  | for zip_entry in zip_file.infolist(): | 
|  | filename = (process_filename(zip_entry.filename) | 
|  | if process_filename else zip_entry.filename) | 
|  | zipinfo = zipfile.ZipInfo(filename, (1980, 1, 1, 0, 0, 0)) | 
|  | if is_mode_executable(zip_entry.external_attr >> 16 & 0xFFFF): | 
|  | zipinfo.external_attr = 0o755 << 16 | 
|  | else: | 
|  | zipinfo.external_attr = 0o644 << 16 | 
|  | zipinfo.compress_type = zip_entry.compress_type | 
|  | output_zip.writestr(zipinfo, zip_file.read(zip_entry)) |