Extract a library from create_embedded_tools.py

We'll use the extracted functions in the upcoming
Python-based reincarnation of the
//:bazel-distfile rule.

Change-Id: I5140938ae4af50f62fb695b5b96cd41f3cd939ef
PiperOrigin-RevId: 164950515
diff --git a/src/create_embedded_tools_lib.py b/src/create_embedded_tools_lib.py
new file mode 100644
index 0000000..a730ce6
--- /dev/null
+++ b/src/create_embedded_tools_lib.py
@@ -0,0 +1,94 @@
+# 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))