| # Copyright 2015 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. | 
 |  | 
 | """Construct a dex manifest from a set of input .dex.zip files. | 
 |  | 
 | Usage: %s <output manifest> <input zip file>* | 
 |        %s @<params file> | 
 |  | 
 | Input files must be either .zip files containing one or more .dex files or | 
 | .dex files. | 
 |  | 
 | A manifest file is written that contains one line for each input dex in the | 
 | following form: | 
 |  | 
 | <input zip> <path in input zip> <path in output zip> <MD5 checksum> | 
 |  | 
 | or | 
 |  | 
 | <input dex> - <path in output zip> <SHA-256 checksum> | 
 | """ | 
 |  | 
 | import hashlib | 
 | import os | 
 | import shutil | 
 | import sys | 
 | import tempfile | 
 | import zipfile | 
 |  | 
 |  | 
 | class DexmanifestBuilder(object): | 
 |   """Implementation of the dex manifest builder.""" | 
 |  | 
 |   def __init__(self): | 
 |     self.manifest_lines = [] | 
 |     self.dir_counter = 1 | 
 |     self.output_dex_counter = 1 | 
 |     self.checksums = set() | 
 |     self.tmpdir = None | 
 |  | 
 |   def __enter__(self): | 
 |     self.tmpdir = tempfile.mkdtemp() | 
 |     return self | 
 |  | 
 |   def __exit__(self, unused_type, unused_value, unused_traceback): | 
 |     shutil.rmtree(self.tmpdir, True) | 
 |  | 
 |   def Checksum(self, filename): | 
 |     """Compute the SHA-256 checksum of a file.""" | 
 |     h = hashlib.sha256() | 
 |     with open(filename, "rb") as f: | 
 |       while True: | 
 |         data = f.read(65536) | 
 |         if not data: | 
 |           break | 
 |  | 
 |         h.update(data) | 
 |  | 
 |     return h.hexdigest() | 
 |  | 
 |   def AddDex(self, input_dex_or_zip, zippath, dex): | 
 |     """Adds a dex file to the output. | 
 |  | 
 |     Args: | 
 |       input_dex_or_zip: the input file written to the manifest | 
 |       zippath: the zip path written to the manifest or None if the input file | 
 |           is not a .zip . | 
 |       dex: the dex file to be added | 
 |  | 
 |     Returns: | 
 |       None. | 
 |     """ | 
 |  | 
 |     fs_checksum = self.Checksum(dex) | 
 |     if fs_checksum in self.checksums: | 
 |       return | 
 |  | 
 |     self.checksums.add(fs_checksum) | 
 |     zip_dex = "incremental_classes%d.dex" % self.output_dex_counter | 
 |     self.output_dex_counter += 1 | 
 |     self.manifest_lines.append("%s %s %s %s" %( | 
 |         input_dex_or_zip, zippath if zippath else "-", zip_dex, fs_checksum)) | 
 |  | 
 |   def Run(self, argv): | 
 |     """Creates a dex manifest.""" | 
 |     if len(argv) < 1: | 
 |       raise Exception("At least one argument expected") | 
 |  | 
 |     if argv[0][0] == "@": | 
 |       if len(argv) != 1: | 
 |         raise IOError("A parameter file should be the only argument") | 
 |       with open(argv[0][1:]) as param_file: | 
 |         argv = [a.strip() for a in param_file.readlines()] | 
 |  | 
 |     for input_filename in argv[1:]: | 
 |       input_filename = input_filename.strip() | 
 |       if input_filename.endswith(".zip"): | 
 |         with zipfile.ZipFile(input_filename, "r") as input_dex_zip: | 
 |           input_dex_dir = os.path.join(self.tmpdir, str(self.dir_counter)) | 
 |           os.makedirs(input_dex_dir) | 
 |           self.dir_counter += 1 | 
 |  | 
 |           for input_dex_dex in input_dex_zip.namelist(): | 
 |             if not input_dex_dex.endswith(".dex"): | 
 |               continue | 
 |  | 
 |             input_dex_zip.extract(input_dex_dex, input_dex_dir) | 
 |             fs_dex = input_dex_dir + "/" + input_dex_dex | 
 |             self.AddDex(input_filename, input_dex_dex, fs_dex) | 
 |       elif input_filename.endswith(".dex"): | 
 |         self.AddDex(input_filename, None, input_filename) | 
 |  | 
 |     with open(argv[0], "wb") as manifest: | 
 |       manifest.write(("\n".join(self.manifest_lines)).encode("utf-8")) | 
 |  | 
 |  | 
 | def main(argv): | 
 |   with DexmanifestBuilder() as b: | 
 |     b.Run(argv[1:]) | 
 |  | 
 |  | 
 | if __name__ == "__main__": | 
 |   main(sys.argv) |