Home | History | Annotate | Download | only in gyp
      1 #!/usr/bin/env python
      2 #
      3 # Copyright 2013 The Chromium Authors. All rights reserved.
      4 # Use of this source code is governed by a BSD-style license that can be
      5 # found in the LICENSE file.
      6 """Signs and zipaligns APK.
      7 
      8 """
      9 
     10 import optparse
     11 import shutil
     12 import sys
     13 import tempfile
     14 
     15 from util import build_utils
     16 
     17 def RenameInflateAndAddPageAlignment(
     18     rezip_apk_jar_path, in_zip_file, out_zip_file):
     19   rezip_apk_cmd = [
     20       'java',
     21       '-classpath',
     22       rezip_apk_jar_path,
     23       'RezipApk',
     24       'renamealign',
     25       in_zip_file,
     26       out_zip_file,
     27     ]
     28   build_utils.CheckOutput(rezip_apk_cmd)
     29 
     30 
     31 def ReorderAndAlignApk(rezip_apk_jar_path, in_zip_file, out_zip_file):
     32   rezip_apk_cmd = [
     33       'java',
     34       '-classpath',
     35       rezip_apk_jar_path,
     36       'RezipApk',
     37       'reorder',
     38       in_zip_file,
     39       out_zip_file,
     40     ]
     41   build_utils.CheckOutput(rezip_apk_cmd)
     42 
     43 
     44 def JarSigner(key_path, key_name, key_passwd, unsigned_path, signed_path):
     45   shutil.copy(unsigned_path, signed_path)
     46   sign_cmd = [
     47       'jarsigner',
     48       '-sigalg', 'MD5withRSA',
     49       '-digestalg', 'SHA1',
     50       '-keystore', key_path,
     51       '-storepass', key_passwd,
     52       signed_path,
     53       key_name,
     54     ]
     55   build_utils.CheckOutput(sign_cmd)
     56 
     57 
     58 def AlignApk(zipalign_path, package_align, unaligned_path, final_path):
     59   align_cmd = [
     60       zipalign_path,
     61       '-f'
     62       ]
     63 
     64   if package_align:
     65     align_cmd += ['-p']
     66 
     67   align_cmd += [
     68       '4',  # 4 bytes
     69       unaligned_path,
     70       final_path,
     71       ]
     72   build_utils.CheckOutput(align_cmd)
     73 
     74 
     75 def main(args):
     76   args = build_utils.ExpandFileArgs(args)
     77 
     78   parser = optparse.OptionParser()
     79   build_utils.AddDepfileOption(parser)
     80 
     81   parser.add_option('--rezip-apk-jar-path',
     82                     help='Path to the RezipApk jar file.')
     83   parser.add_option('--zipalign-path', help='Path to the zipalign tool.')
     84   parser.add_option('--page-align-shared-libraries',
     85                     action='store_true',
     86                     help='Page align shared libraries.')
     87   parser.add_option('--unsigned-apk-path', help='Path to input unsigned APK.')
     88   parser.add_option('--final-apk-path',
     89       help='Path to output signed and aligned APK.')
     90   parser.add_option('--key-path', help='Path to keystore for signing.')
     91   parser.add_option('--key-passwd', help='Keystore password')
     92   parser.add_option('--key-name', help='Keystore name')
     93   parser.add_option('--stamp', help='Path to touch on success.')
     94   parser.add_option('--load-library-from-zip', type='int',
     95       help='If non-zero, build the APK such that the library can be loaded ' +
     96            'directly from the zip file using the crazy linker. The library ' +
     97            'will be renamed, uncompressed and page aligned.')
     98 
     99   options, _ = parser.parse_args()
    100 
    101   input_paths = [
    102     options.unsigned_apk_path,
    103     options.key_path,
    104   ]
    105 
    106   if options.load_library_from_zip:
    107     input_paths.append(options.rezip_apk_jar_path)
    108 
    109   input_strings = [
    110     options.load_library_from_zip,
    111     options.key_name,
    112     options.key_passwd,
    113     options.page_align_shared_libraries,
    114   ]
    115 
    116   build_utils.CallAndWriteDepfileIfStale(
    117       lambda: FinalizeApk(options),
    118       options,
    119       record_path=options.unsigned_apk_path + '.finalize.md5.stamp',
    120       input_paths=input_paths,
    121       input_strings=input_strings,
    122       output_paths=[options.final_apk_path])
    123 
    124 
    125 def FinalizeApk(options):
    126   with tempfile.NamedTemporaryFile() as signed_apk_path_tmp, \
    127       tempfile.NamedTemporaryFile() as apk_to_sign_tmp:
    128 
    129     if options.load_library_from_zip:
    130       # We alter the name of the library so that the Android Package Manager
    131       # does not extract it into a separate file. This must be done before
    132       # signing, as the filename is part of the signed manifest. At the same
    133       # time we uncompress the library, which is necessary so that it can be
    134       # loaded directly from the APK.
    135       # Move the library to a page boundary by adding a page alignment file.
    136       apk_to_sign = apk_to_sign_tmp.name
    137       RenameInflateAndAddPageAlignment(
    138           options.rezip_apk_jar_path, options.unsigned_apk_path, apk_to_sign)
    139     else:
    140       apk_to_sign = options.unsigned_apk_path
    141 
    142     signed_apk_path = signed_apk_path_tmp.name
    143     JarSigner(options.key_path, options.key_name, options.key_passwd,
    144               apk_to_sign, signed_apk_path)
    145 
    146     if options.load_library_from_zip:
    147       # Reorder the contents of the APK. This re-establishes the canonical
    148       # order which means the library will be back at its page aligned location.
    149       # This step also aligns uncompressed items to 4 bytes.
    150       ReorderAndAlignApk(
    151           options.rezip_apk_jar_path, signed_apk_path, options.final_apk_path)
    152     else:
    153       # Align uncompressed items to 4 bytes
    154       AlignApk(options.zipalign_path,
    155                options.page_align_shared_libraries,
    156                signed_apk_path,
    157                options.final_apk_path)
    158 
    159 
    160 if __name__ == '__main__':
    161   sys.exit(main(sys.argv[1:]))
    162