|  | # Copyright 2013 The Flutter Authors. All rights reserved. | 
|  | # Use of this source code is governed by a BSD-style license that can be | 
|  | # found in the LICENSE file. | 
|  |  | 
|  | import argparse | 
|  | import os | 
|  | import subprocess | 
|  | import sys | 
|  | import zipfile | 
|  |  | 
|  |  | 
|  | def run_command_checked(command, env=None): | 
|  | try: | 
|  | env = env if env is not None else os.environ | 
|  | subprocess.check_output(command, stderr=subprocess.STDOUT, text=True, env=env) | 
|  | except subprocess.CalledProcessError as cpe: | 
|  | print(cpe.output) | 
|  | raise cpe | 
|  |  | 
|  |  | 
|  | def is_mac(): | 
|  | return sys.platform == 'darwin' | 
|  |  | 
|  |  | 
|  | def java_home(): | 
|  | script_path = os.path.dirname(os.path.realpath(__file__)) | 
|  | if is_mac(): | 
|  | return os.path.join( | 
|  | script_path, '..', '..', '..', 'third_party', 'java', 'openjdk', 'Contents', 'Home' | 
|  | ) | 
|  | return os.path.join(script_path, '..', 'third_party', 'java', 'openjdk') | 
|  |  | 
|  |  | 
|  | def java_bin(): | 
|  | return os.path.join(java_home(), 'bin') | 
|  |  | 
|  |  | 
|  | def main(): | 
|  | parser = argparse.ArgumentParser() | 
|  |  | 
|  | parser.add_argument('--aapt2-bin', type=str, required=True, help='The path to the aapt2 binary.') | 
|  | parser.add_argument( | 
|  | '--zipalign-bin', type=str, required=True, help='The path to the zipalign binary.' | 
|  | ) | 
|  | parser.add_argument( | 
|  | '--apksigner-bin', type=str, required=True, help='The path to the apksigner binary.' | 
|  | ) | 
|  | parser.add_argument( | 
|  | '--android-manifest', type=str, required=True, help='The path to the AndroidManifest.xml.' | 
|  | ) | 
|  | parser.add_argument('--android-jar', type=str, required=True, help='The path to android.jar.') | 
|  | parser.add_argument('--output-path', type=str, required=True, help='The path to the output apk.') | 
|  | parser.add_argument( | 
|  | '--library', type=str, required=True, help='The path to the library to put in the apk.' | 
|  | ) | 
|  | parser.add_argument( | 
|  | '--keystore', type=str, required=True, help='The path to the debug keystore to sign the apk.' | 
|  | ) | 
|  | parser.add_argument( | 
|  | '--gen-dir', type=str, required=True, help='The directory for generated files.' | 
|  | ) | 
|  | parser.add_argument( | 
|  | '--android-abi', type=str, required=True, help='The android ABI of the library.' | 
|  | ) | 
|  |  | 
|  | args = parser.parse_args() | 
|  |  | 
|  | library_file = os.path.basename(args.library) | 
|  | apk_name = os.path.basename(args.output_path) | 
|  |  | 
|  | unaligned_apk_path = os.path.join(args.gen_dir, '%s.unaligned' % apk_name) | 
|  | unsigned_apk_path = os.path.join(args.gen_dir, '%s.unsigned' % apk_name) | 
|  | apk_path = args.output_path | 
|  |  | 
|  | java_path = ':'.join([java_bin(), os.environ['PATH']]) | 
|  | env = dict(os.environ, PATH=java_path, JAVA_HOME=java_home()) | 
|  |  | 
|  | # Create the skeleton of the APK using aapt2. | 
|  | aapt2_command = [ | 
|  | args.aapt2_bin, | 
|  | 'link', | 
|  | '-I', | 
|  | args.android_jar, | 
|  | '--manifest', | 
|  | args.android_manifest, | 
|  | '-o', | 
|  | unaligned_apk_path, | 
|  | ] | 
|  | run_command_checked(aapt2_command, env=env) | 
|  |  | 
|  | # Stuff the library in the APK which is just a regular ZIP file. Libraries are not compressed. | 
|  | with zipfile.ZipFile(unaligned_apk_path, 'a', compression=zipfile.ZIP_STORED) as zipf: | 
|  | zipf.write(args.library, 'lib/%s/%s' % (args.android_abi, library_file)) | 
|  |  | 
|  | # Align the dylib to a page boundary. | 
|  | zipalign_command = [ | 
|  | args.zipalign_bin, | 
|  | '-p',  # Page align the dylib | 
|  | '-f',  # overwrite output if exists | 
|  | '4',  # 32-bit alignment | 
|  | unaligned_apk_path, | 
|  | unsigned_apk_path, | 
|  | ] | 
|  | run_command_checked(zipalign_command, env=env) | 
|  |  | 
|  | # Sign the APK. | 
|  | apksigner_command = [ | 
|  | args.apksigner_bin, 'sign', '--ks', args.keystore, '--ks-pass', 'pass:android', '--out', | 
|  | apk_path, unsigned_apk_path | 
|  | ] | 
|  | run_command_checked(apksigner_command, env=env) | 
|  |  | 
|  | # Remove the intermediates so the out directory isn't full of large files. | 
|  | os.remove(unaligned_apk_path) | 
|  | os.remove(unsigned_apk_path) | 
|  |  | 
|  | return 0 | 
|  |  | 
|  |  | 
|  | if __name__ == '__main__': | 
|  | sys.exit(main()) |