1*8975f5c5SAndroid Build Coastguard Worker#!/usr/bin/env python3 2*8975f5c5SAndroid Build Coastguard Worker# 3*8975f5c5SAndroid Build Coastguard Worker# Copyright 2015 The Chromium Authors 4*8975f5c5SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be 5*8975f5c5SAndroid Build Coastguard Worker# found in the LICENSE file. 6*8975f5c5SAndroid Build Coastguard Worker 7*8975f5c5SAndroid Build Coastguard Worker"""Adds the code parts to a resource APK.""" 8*8975f5c5SAndroid Build Coastguard Worker 9*8975f5c5SAndroid Build Coastguard Workerimport argparse 10*8975f5c5SAndroid Build Coastguard Workerimport logging 11*8975f5c5SAndroid Build Coastguard Workerimport os 12*8975f5c5SAndroid Build Coastguard Workerimport posixpath 13*8975f5c5SAndroid Build Coastguard Workerimport shutil 14*8975f5c5SAndroid Build Coastguard Workerimport sys 15*8975f5c5SAndroid Build Coastguard Workerimport tempfile 16*8975f5c5SAndroid Build Coastguard Workerimport zipfile 17*8975f5c5SAndroid Build Coastguard Workerimport zlib 18*8975f5c5SAndroid Build Coastguard Worker 19*8975f5c5SAndroid Build Coastguard Workerimport finalize_apk 20*8975f5c5SAndroid Build Coastguard Worker 21*8975f5c5SAndroid Build Coastguard Workerfrom util import build_utils 22*8975f5c5SAndroid Build Coastguard Workerfrom util import diff_utils 23*8975f5c5SAndroid Build Coastguard Workerimport action_helpers # build_utils adds //build to sys.path. 24*8975f5c5SAndroid Build Coastguard Workerimport zip_helpers 25*8975f5c5SAndroid Build Coastguard Worker 26*8975f5c5SAndroid Build Coastguard Worker 27*8975f5c5SAndroid Build Coastguard Worker# Taken from aapt's Package.cpp: 28*8975f5c5SAndroid Build Coastguard Worker_NO_COMPRESS_EXTENSIONS = ('.jpg', '.jpeg', '.png', '.gif', '.wav', '.mp2', 29*8975f5c5SAndroid Build Coastguard Worker '.mp3', '.ogg', '.aac', '.mpg', '.mpeg', '.mid', 30*8975f5c5SAndroid Build Coastguard Worker '.midi', '.smf', '.jet', '.rtttl', '.imy', '.xmf', 31*8975f5c5SAndroid Build Coastguard Worker '.mp4', '.m4a', '.m4v', '.3gp', '.3gpp', '.3g2', 32*8975f5c5SAndroid Build Coastguard Worker '.3gpp2', '.amr', '.awb', '.wma', '.wmv', '.webm') 33*8975f5c5SAndroid Build Coastguard Worker 34*8975f5c5SAndroid Build Coastguard Worker 35*8975f5c5SAndroid Build Coastguard Workerdef _ParseArgs(args): 36*8975f5c5SAndroid Build Coastguard Worker parser = argparse.ArgumentParser() 37*8975f5c5SAndroid Build Coastguard Worker action_helpers.add_depfile_arg(parser) 38*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('--assets', 39*8975f5c5SAndroid Build Coastguard Worker action='append', 40*8975f5c5SAndroid Build Coastguard Worker help='GYP-list of files to add as assets in the form ' 41*8975f5c5SAndroid Build Coastguard Worker '"srcPath:zipPath", where ":zipPath" is optional.') 42*8975f5c5SAndroid Build Coastguard Worker parser.add_argument( 43*8975f5c5SAndroid Build Coastguard Worker '--java-resources', help='GYP-list of java_resources JARs to include.') 44*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('--write-asset-list', 45*8975f5c5SAndroid Build Coastguard Worker action='store_true', 46*8975f5c5SAndroid Build Coastguard Worker help='Whether to create an assets/assets_list file.') 47*8975f5c5SAndroid Build Coastguard Worker parser.add_argument( 48*8975f5c5SAndroid Build Coastguard Worker '--uncompressed-assets', 49*8975f5c5SAndroid Build Coastguard Worker help='Same as --assets, except disables compression.') 50*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('--resource-apk', 51*8975f5c5SAndroid Build Coastguard Worker help='An .ap_ file built using aapt', 52*8975f5c5SAndroid Build Coastguard Worker required=True) 53*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('--output-apk', 54*8975f5c5SAndroid Build Coastguard Worker help='Path to the output file', 55*8975f5c5SAndroid Build Coastguard Worker required=True) 56*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('--format', choices=['apk', 'bundle-module'], 57*8975f5c5SAndroid Build Coastguard Worker default='apk', help='Specify output format.') 58*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('--dex-file', 59*8975f5c5SAndroid Build Coastguard Worker help='Path to the classes.dex to use') 60*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('--uncompress-dex', action='store_true', 61*8975f5c5SAndroid Build Coastguard Worker help='Store .dex files uncompressed in the APK') 62*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('--native-libs', 63*8975f5c5SAndroid Build Coastguard Worker action='append', 64*8975f5c5SAndroid Build Coastguard Worker help='GYP-list of native libraries to include. ' 65*8975f5c5SAndroid Build Coastguard Worker 'Can be specified multiple times.', 66*8975f5c5SAndroid Build Coastguard Worker default=[]) 67*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('--secondary-native-libs', 68*8975f5c5SAndroid Build Coastguard Worker action='append', 69*8975f5c5SAndroid Build Coastguard Worker help='GYP-list of native libraries for secondary ' 70*8975f5c5SAndroid Build Coastguard Worker 'android-abi. Can be specified multiple times.', 71*8975f5c5SAndroid Build Coastguard Worker default=[]) 72*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('--android-abi', 73*8975f5c5SAndroid Build Coastguard Worker help='Android architecture to use for native libraries') 74*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('--secondary-android-abi', 75*8975f5c5SAndroid Build Coastguard Worker help='The secondary Android architecture to use for' 76*8975f5c5SAndroid Build Coastguard Worker 'secondary native libraries') 77*8975f5c5SAndroid Build Coastguard Worker parser.add_argument( 78*8975f5c5SAndroid Build Coastguard Worker '--is-multi-abi', 79*8975f5c5SAndroid Build Coastguard Worker action='store_true', 80*8975f5c5SAndroid Build Coastguard Worker help='Will add a placeholder for the missing ABI if no native libs or ' 81*8975f5c5SAndroid Build Coastguard Worker 'placeholders are set for either the primary or secondary ABI. Can only ' 82*8975f5c5SAndroid Build Coastguard Worker 'be set if both --android-abi and --secondary-android-abi are set.') 83*8975f5c5SAndroid Build Coastguard Worker parser.add_argument( 84*8975f5c5SAndroid Build Coastguard Worker '--native-lib-placeholders', 85*8975f5c5SAndroid Build Coastguard Worker help='GYP-list of native library placeholders to add.') 86*8975f5c5SAndroid Build Coastguard Worker parser.add_argument( 87*8975f5c5SAndroid Build Coastguard Worker '--secondary-native-lib-placeholders', 88*8975f5c5SAndroid Build Coastguard Worker help='GYP-list of native library placeholders to add ' 89*8975f5c5SAndroid Build Coastguard Worker 'for the secondary ABI') 90*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('--uncompress-shared-libraries', default='False', 91*8975f5c5SAndroid Build Coastguard Worker choices=['true', 'True', 'false', 'False'], 92*8975f5c5SAndroid Build Coastguard Worker help='Whether to uncompress native shared libraries. Argument must be ' 93*8975f5c5SAndroid Build Coastguard Worker 'a boolean value.') 94*8975f5c5SAndroid Build Coastguard Worker parser.add_argument( 95*8975f5c5SAndroid Build Coastguard Worker '--apksigner-jar', help='Path to the apksigner executable.') 96*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('--zipalign-path', 97*8975f5c5SAndroid Build Coastguard Worker help='Path to the zipalign executable.') 98*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('--key-path', 99*8975f5c5SAndroid Build Coastguard Worker help='Path to keystore for signing.') 100*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('--key-passwd', 101*8975f5c5SAndroid Build Coastguard Worker help='Keystore password') 102*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('--key-name', 103*8975f5c5SAndroid Build Coastguard Worker help='Keystore name') 104*8975f5c5SAndroid Build Coastguard Worker parser.add_argument( 105*8975f5c5SAndroid Build Coastguard Worker '--min-sdk-version', required=True, help='Value of APK\'s minSdkVersion') 106*8975f5c5SAndroid Build Coastguard Worker parser.add_argument( 107*8975f5c5SAndroid Build Coastguard Worker '--best-compression', 108*8975f5c5SAndroid Build Coastguard Worker action='store_true', 109*8975f5c5SAndroid Build Coastguard Worker help='Use zip -9 rather than zip -1') 110*8975f5c5SAndroid Build Coastguard Worker parser.add_argument( 111*8975f5c5SAndroid Build Coastguard Worker '--library-always-compress', 112*8975f5c5SAndroid Build Coastguard Worker action='append', 113*8975f5c5SAndroid Build Coastguard Worker help='The list of library files that we always compress.') 114*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('--warnings-as-errors', 115*8975f5c5SAndroid Build Coastguard Worker action='store_true', 116*8975f5c5SAndroid Build Coastguard Worker help='Treat all warnings as errors.') 117*8975f5c5SAndroid Build Coastguard Worker diff_utils.AddCommandLineFlags(parser) 118*8975f5c5SAndroid Build Coastguard Worker options = parser.parse_args(args) 119*8975f5c5SAndroid Build Coastguard Worker options.assets = action_helpers.parse_gn_list(options.assets) 120*8975f5c5SAndroid Build Coastguard Worker options.uncompressed_assets = action_helpers.parse_gn_list( 121*8975f5c5SAndroid Build Coastguard Worker options.uncompressed_assets) 122*8975f5c5SAndroid Build Coastguard Worker options.native_lib_placeholders = action_helpers.parse_gn_list( 123*8975f5c5SAndroid Build Coastguard Worker options.native_lib_placeholders) 124*8975f5c5SAndroid Build Coastguard Worker options.secondary_native_lib_placeholders = action_helpers.parse_gn_list( 125*8975f5c5SAndroid Build Coastguard Worker options.secondary_native_lib_placeholders) 126*8975f5c5SAndroid Build Coastguard Worker options.java_resources = action_helpers.parse_gn_list(options.java_resources) 127*8975f5c5SAndroid Build Coastguard Worker options.native_libs = action_helpers.parse_gn_list(options.native_libs) 128*8975f5c5SAndroid Build Coastguard Worker options.secondary_native_libs = action_helpers.parse_gn_list( 129*8975f5c5SAndroid Build Coastguard Worker options.secondary_native_libs) 130*8975f5c5SAndroid Build Coastguard Worker options.library_always_compress = action_helpers.parse_gn_list( 131*8975f5c5SAndroid Build Coastguard Worker options.library_always_compress) 132*8975f5c5SAndroid Build Coastguard Worker 133*8975f5c5SAndroid Build Coastguard Worker if not options.android_abi and (options.native_libs or 134*8975f5c5SAndroid Build Coastguard Worker options.native_lib_placeholders): 135*8975f5c5SAndroid Build Coastguard Worker raise Exception('Must specify --android-abi with --native-libs') 136*8975f5c5SAndroid Build Coastguard Worker if not options.secondary_android_abi and (options.secondary_native_libs or 137*8975f5c5SAndroid Build Coastguard Worker options.secondary_native_lib_placeholders): 138*8975f5c5SAndroid Build Coastguard Worker raise Exception('Must specify --secondary-android-abi with' 139*8975f5c5SAndroid Build Coastguard Worker ' --secondary-native-libs') 140*8975f5c5SAndroid Build Coastguard Worker if options.is_multi_abi and not (options.android_abi 141*8975f5c5SAndroid Build Coastguard Worker and options.secondary_android_abi): 142*8975f5c5SAndroid Build Coastguard Worker raise Exception('Must specify --is-multi-abi with both --android-abi ' 143*8975f5c5SAndroid Build Coastguard Worker 'and --secondary-android-abi.') 144*8975f5c5SAndroid Build Coastguard Worker return options 145*8975f5c5SAndroid Build Coastguard Worker 146*8975f5c5SAndroid Build Coastguard Worker 147*8975f5c5SAndroid Build Coastguard Workerdef _SplitAssetPath(path): 148*8975f5c5SAndroid Build Coastguard Worker """Returns (src, dest) given an asset path in the form src[:dest].""" 149*8975f5c5SAndroid Build Coastguard Worker path_parts = path.split(':') 150*8975f5c5SAndroid Build Coastguard Worker src_path = path_parts[0] 151*8975f5c5SAndroid Build Coastguard Worker if len(path_parts) > 1: 152*8975f5c5SAndroid Build Coastguard Worker dest_path = path_parts[1] 153*8975f5c5SAndroid Build Coastguard Worker else: 154*8975f5c5SAndroid Build Coastguard Worker dest_path = os.path.basename(src_path) 155*8975f5c5SAndroid Build Coastguard Worker return src_path, dest_path 156*8975f5c5SAndroid Build Coastguard Worker 157*8975f5c5SAndroid Build Coastguard Worker 158*8975f5c5SAndroid Build Coastguard Workerdef _ExpandPaths(paths): 159*8975f5c5SAndroid Build Coastguard Worker """Converts src:dst into tuples and enumerates files within directories. 160*8975f5c5SAndroid Build Coastguard Worker 161*8975f5c5SAndroid Build Coastguard Worker Args: 162*8975f5c5SAndroid Build Coastguard Worker paths: Paths in the form "src_path:dest_path" 163*8975f5c5SAndroid Build Coastguard Worker 164*8975f5c5SAndroid Build Coastguard Worker Returns: 165*8975f5c5SAndroid Build Coastguard Worker A list of (src_path, dest_path) tuples sorted by dest_path (for stable 166*8975f5c5SAndroid Build Coastguard Worker ordering within output .apk). 167*8975f5c5SAndroid Build Coastguard Worker """ 168*8975f5c5SAndroid Build Coastguard Worker ret = [] 169*8975f5c5SAndroid Build Coastguard Worker for path in paths: 170*8975f5c5SAndroid Build Coastguard Worker src_path, dest_path = _SplitAssetPath(path) 171*8975f5c5SAndroid Build Coastguard Worker if os.path.isdir(src_path): 172*8975f5c5SAndroid Build Coastguard Worker for f in build_utils.FindInDirectory(src_path, '*'): 173*8975f5c5SAndroid Build Coastguard Worker ret.append((f, os.path.join(dest_path, f[len(src_path) + 1:]))) 174*8975f5c5SAndroid Build Coastguard Worker else: 175*8975f5c5SAndroid Build Coastguard Worker ret.append((src_path, dest_path)) 176*8975f5c5SAndroid Build Coastguard Worker ret.sort(key=lambda t:t[1]) 177*8975f5c5SAndroid Build Coastguard Worker return ret 178*8975f5c5SAndroid Build Coastguard Worker 179*8975f5c5SAndroid Build Coastguard Worker 180*8975f5c5SAndroid Build Coastguard Workerdef _GetAssetsToAdd(path_tuples, 181*8975f5c5SAndroid Build Coastguard Worker fast_align, 182*8975f5c5SAndroid Build Coastguard Worker disable_compression=False, 183*8975f5c5SAndroid Build Coastguard Worker allow_reads=True, 184*8975f5c5SAndroid Build Coastguard Worker apk_root_dir=''): 185*8975f5c5SAndroid Build Coastguard Worker """Returns the list of file_detail tuples for assets in the apk. 186*8975f5c5SAndroid Build Coastguard Worker 187*8975f5c5SAndroid Build Coastguard Worker Args: 188*8975f5c5SAndroid Build Coastguard Worker path_tuples: List of src_path, dest_path tuples to add. 189*8975f5c5SAndroid Build Coastguard Worker fast_align: Whether to perform alignment in python zipfile (alternatively 190*8975f5c5SAndroid Build Coastguard Worker alignment can be done using the zipalign utility out of band). 191*8975f5c5SAndroid Build Coastguard Worker disable_compression: Whether to disable compression. 192*8975f5c5SAndroid Build Coastguard Worker allow_reads: If false, we do not try to read the files from disk (to find 193*8975f5c5SAndroid Build Coastguard Worker their size for example). 194*8975f5c5SAndroid Build Coastguard Worker 195*8975f5c5SAndroid Build Coastguard Worker Returns: A list of (src_path, apk_path, compress, alignment) tuple 196*8975f5c5SAndroid Build Coastguard Worker representing what and how assets are added. 197*8975f5c5SAndroid Build Coastguard Worker """ 198*8975f5c5SAndroid Build Coastguard Worker assets_to_add = [] 199*8975f5c5SAndroid Build Coastguard Worker 200*8975f5c5SAndroid Build Coastguard Worker # Group all uncompressed assets together in the hope that it will increase 201*8975f5c5SAndroid Build Coastguard Worker # locality of mmap'ed files. 202*8975f5c5SAndroid Build Coastguard Worker for target_compress in (False, True): 203*8975f5c5SAndroid Build Coastguard Worker for src_path, dest_path in path_tuples: 204*8975f5c5SAndroid Build Coastguard Worker compress = not disable_compression and ( 205*8975f5c5SAndroid Build Coastguard Worker os.path.splitext(src_path)[1] not in _NO_COMPRESS_EXTENSIONS) 206*8975f5c5SAndroid Build Coastguard Worker 207*8975f5c5SAndroid Build Coastguard Worker if target_compress == compress: 208*8975f5c5SAndroid Build Coastguard Worker # add_to_zip_hermetic() uses this logic to avoid growing small files. 209*8975f5c5SAndroid Build Coastguard Worker # We need it here in order to set alignment correctly. 210*8975f5c5SAndroid Build Coastguard Worker if allow_reads and compress and os.path.getsize(src_path) < 16: 211*8975f5c5SAndroid Build Coastguard Worker compress = False 212*8975f5c5SAndroid Build Coastguard Worker 213*8975f5c5SAndroid Build Coastguard Worker if dest_path.startswith('../'): 214*8975f5c5SAndroid Build Coastguard Worker # posixpath.join('', 'foo') == 'foo' 215*8975f5c5SAndroid Build Coastguard Worker apk_path = posixpath.join(apk_root_dir, dest_path[3:]) 216*8975f5c5SAndroid Build Coastguard Worker else: 217*8975f5c5SAndroid Build Coastguard Worker apk_path = 'assets/' + dest_path 218*8975f5c5SAndroid Build Coastguard Worker alignment = 0 if compress and not fast_align else 4 219*8975f5c5SAndroid Build Coastguard Worker assets_to_add.append((apk_path, src_path, compress, alignment)) 220*8975f5c5SAndroid Build Coastguard Worker return assets_to_add 221*8975f5c5SAndroid Build Coastguard Worker 222*8975f5c5SAndroid Build Coastguard Worker 223*8975f5c5SAndroid Build Coastguard Workerdef _AddFiles(apk, details): 224*8975f5c5SAndroid Build Coastguard Worker """Adds files to the apk. 225*8975f5c5SAndroid Build Coastguard Worker 226*8975f5c5SAndroid Build Coastguard Worker Args: 227*8975f5c5SAndroid Build Coastguard Worker apk: path to APK to add to. 228*8975f5c5SAndroid Build Coastguard Worker details: A list of file detail tuples (src_path, apk_path, compress, 229*8975f5c5SAndroid Build Coastguard Worker alignment) representing what and how files are added to the APK. 230*8975f5c5SAndroid Build Coastguard Worker """ 231*8975f5c5SAndroid Build Coastguard Worker for apk_path, src_path, compress, alignment in details: 232*8975f5c5SAndroid Build Coastguard Worker # This check is only relevant for assets, but it should not matter if it is 233*8975f5c5SAndroid Build Coastguard Worker # checked for the whole list of files. 234*8975f5c5SAndroid Build Coastguard Worker try: 235*8975f5c5SAndroid Build Coastguard Worker apk.getinfo(apk_path) 236*8975f5c5SAndroid Build Coastguard Worker # Should never happen since write_build_config.py handles merging. 237*8975f5c5SAndroid Build Coastguard Worker raise Exception( 238*8975f5c5SAndroid Build Coastguard Worker 'Multiple targets specified the asset path: %s' % apk_path) 239*8975f5c5SAndroid Build Coastguard Worker except KeyError: 240*8975f5c5SAndroid Build Coastguard Worker zip_helpers.add_to_zip_hermetic(apk, 241*8975f5c5SAndroid Build Coastguard Worker apk_path, 242*8975f5c5SAndroid Build Coastguard Worker src_path=src_path, 243*8975f5c5SAndroid Build Coastguard Worker compress=compress, 244*8975f5c5SAndroid Build Coastguard Worker alignment=alignment) 245*8975f5c5SAndroid Build Coastguard Worker 246*8975f5c5SAndroid Build Coastguard Worker 247*8975f5c5SAndroid Build Coastguard Workerdef _GetAbiAlignment(android_abi): 248*8975f5c5SAndroid Build Coastguard Worker if '64' in android_abi: 249*8975f5c5SAndroid Build Coastguard Worker return 0x4000 # 16k alignment 250*8975f5c5SAndroid Build Coastguard Worker return 0x1000 # 4k alignment 251*8975f5c5SAndroid Build Coastguard Worker 252*8975f5c5SAndroid Build Coastguard Worker 253*8975f5c5SAndroid Build Coastguard Workerdef _GetNativeLibrariesToAdd(native_libs, android_abi, fast_align, 254*8975f5c5SAndroid Build Coastguard Worker lib_always_compress): 255*8975f5c5SAndroid Build Coastguard Worker """Returns the list of file_detail tuples for native libraries in the apk. 256*8975f5c5SAndroid Build Coastguard Worker 257*8975f5c5SAndroid Build Coastguard Worker Returns: A list of (src_path, apk_path, compress, alignment) tuple 258*8975f5c5SAndroid Build Coastguard Worker representing what and how native libraries are added. 259*8975f5c5SAndroid Build Coastguard Worker """ 260*8975f5c5SAndroid Build Coastguard Worker libraries_to_add = [] 261*8975f5c5SAndroid Build Coastguard Worker 262*8975f5c5SAndroid Build Coastguard Worker 263*8975f5c5SAndroid Build Coastguard Worker for path in native_libs: 264*8975f5c5SAndroid Build Coastguard Worker basename = os.path.basename(path) 265*8975f5c5SAndroid Build Coastguard Worker compress = any(lib_name in basename for lib_name in lib_always_compress) 266*8975f5c5SAndroid Build Coastguard Worker lib_android_abi = android_abi 267*8975f5c5SAndroid Build Coastguard Worker if path.startswith('android_clang_arm64_hwasan/'): 268*8975f5c5SAndroid Build Coastguard Worker lib_android_abi = 'arm64-v8a-hwasan' 269*8975f5c5SAndroid Build Coastguard Worker 270*8975f5c5SAndroid Build Coastguard Worker apk_path = 'lib/%s/%s' % (lib_android_abi, basename) 271*8975f5c5SAndroid Build Coastguard Worker if compress and not fast_align: 272*8975f5c5SAndroid Build Coastguard Worker alignment = 0 273*8975f5c5SAndroid Build Coastguard Worker else: 274*8975f5c5SAndroid Build Coastguard Worker alignment = _GetAbiAlignment(android_abi) 275*8975f5c5SAndroid Build Coastguard Worker libraries_to_add.append((apk_path, path, compress, alignment)) 276*8975f5c5SAndroid Build Coastguard Worker 277*8975f5c5SAndroid Build Coastguard Worker return libraries_to_add 278*8975f5c5SAndroid Build Coastguard Worker 279*8975f5c5SAndroid Build Coastguard Worker 280*8975f5c5SAndroid Build Coastguard Workerdef _CreateExpectationsData(native_libs, assets): 281*8975f5c5SAndroid Build Coastguard Worker """Creates list of native libraries and assets.""" 282*8975f5c5SAndroid Build Coastguard Worker native_libs = sorted(native_libs) 283*8975f5c5SAndroid Build Coastguard Worker assets = sorted(assets) 284*8975f5c5SAndroid Build Coastguard Worker 285*8975f5c5SAndroid Build Coastguard Worker ret = [] 286*8975f5c5SAndroid Build Coastguard Worker for apk_path, _, compress, alignment in native_libs + assets: 287*8975f5c5SAndroid Build Coastguard Worker ret.append('apk_path=%s, compress=%s, alignment=%s\n' % 288*8975f5c5SAndroid Build Coastguard Worker (apk_path, compress, alignment)) 289*8975f5c5SAndroid Build Coastguard Worker return ''.join(ret) 290*8975f5c5SAndroid Build Coastguard Worker 291*8975f5c5SAndroid Build Coastguard Worker 292*8975f5c5SAndroid Build Coastguard Workerdef main(args): 293*8975f5c5SAndroid Build Coastguard Worker build_utils.InitLogging('APKBUILDER_DEBUG') 294*8975f5c5SAndroid Build Coastguard Worker args = build_utils.ExpandFileArgs(args) 295*8975f5c5SAndroid Build Coastguard Worker options = _ParseArgs(args) 296*8975f5c5SAndroid Build Coastguard Worker 297*8975f5c5SAndroid Build Coastguard Worker # Until Python 3.7, there's no better way to set compression level. 298*8975f5c5SAndroid Build Coastguard Worker # The default is 6. 299*8975f5c5SAndroid Build Coastguard Worker if options.best_compression: 300*8975f5c5SAndroid Build Coastguard Worker # Compresses about twice as slow as the default. 301*8975f5c5SAndroid Build Coastguard Worker zlib.Z_DEFAULT_COMPRESSION = 9 302*8975f5c5SAndroid Build Coastguard Worker else: 303*8975f5c5SAndroid Build Coastguard Worker # Compresses about twice as fast as the default. 304*8975f5c5SAndroid Build Coastguard Worker zlib.Z_DEFAULT_COMPRESSION = 1 305*8975f5c5SAndroid Build Coastguard Worker 306*8975f5c5SAndroid Build Coastguard Worker # Python's zip implementation duplicates file comments in the central 307*8975f5c5SAndroid Build Coastguard Worker # directory, whereas zipalign does not, so use zipalign for official builds. 308*8975f5c5SAndroid Build Coastguard Worker requires_alignment = options.format == 'apk' 309*8975f5c5SAndroid Build Coastguard Worker # TODO(crbug.com/40286668): Re-enable zipalign once we are using Android V 310*8975f5c5SAndroid Build Coastguard Worker # SDK. 311*8975f5c5SAndroid Build Coastguard Worker run_zipalign = requires_alignment and options.best_compression and False 312*8975f5c5SAndroid Build Coastguard Worker fast_align = bool(requires_alignment and not run_zipalign) 313*8975f5c5SAndroid Build Coastguard Worker 314*8975f5c5SAndroid Build Coastguard Worker native_libs = sorted(options.native_libs) 315*8975f5c5SAndroid Build Coastguard Worker 316*8975f5c5SAndroid Build Coastguard Worker # Include native libs in the depfile_deps since GN doesn't know about the 317*8975f5c5SAndroid Build Coastguard Worker # dependencies when is_component_build=true. 318*8975f5c5SAndroid Build Coastguard Worker depfile_deps = list(native_libs) 319*8975f5c5SAndroid Build Coastguard Worker 320*8975f5c5SAndroid Build Coastguard Worker # For targets that depend on static library APKs, dex paths are created by 321*8975f5c5SAndroid Build Coastguard Worker # the static library's dexsplitter target and GN doesn't know about these 322*8975f5c5SAndroid Build Coastguard Worker # paths. 323*8975f5c5SAndroid Build Coastguard Worker if options.dex_file: 324*8975f5c5SAndroid Build Coastguard Worker depfile_deps.append(options.dex_file) 325*8975f5c5SAndroid Build Coastguard Worker 326*8975f5c5SAndroid Build Coastguard Worker secondary_native_libs = [] 327*8975f5c5SAndroid Build Coastguard Worker if options.secondary_native_libs: 328*8975f5c5SAndroid Build Coastguard Worker secondary_native_libs = sorted(options.secondary_native_libs) 329*8975f5c5SAndroid Build Coastguard Worker depfile_deps += secondary_native_libs 330*8975f5c5SAndroid Build Coastguard Worker 331*8975f5c5SAndroid Build Coastguard Worker if options.java_resources: 332*8975f5c5SAndroid Build Coastguard Worker # Included via .build_config.json, so need to write it to depfile. 333*8975f5c5SAndroid Build Coastguard Worker depfile_deps.extend(options.java_resources) 334*8975f5c5SAndroid Build Coastguard Worker 335*8975f5c5SAndroid Build Coastguard Worker assets = _ExpandPaths(options.assets) 336*8975f5c5SAndroid Build Coastguard Worker uncompressed_assets = _ExpandPaths(options.uncompressed_assets) 337*8975f5c5SAndroid Build Coastguard Worker 338*8975f5c5SAndroid Build Coastguard Worker # Included via .build_config.json, so need to write it to depfile. 339*8975f5c5SAndroid Build Coastguard Worker depfile_deps.extend(x[0] for x in assets) 340*8975f5c5SAndroid Build Coastguard Worker depfile_deps.extend(x[0] for x in uncompressed_assets) 341*8975f5c5SAndroid Build Coastguard Worker depfile_deps.append(options.resource_apk) 342*8975f5c5SAndroid Build Coastguard Worker 343*8975f5c5SAndroid Build Coastguard Worker # Bundle modules have a structure similar to APKs, except that resources 344*8975f5c5SAndroid Build Coastguard Worker # are compiled in protobuf format (instead of binary xml), and that some 345*8975f5c5SAndroid Build Coastguard Worker # files are located into different top-level directories, e.g.: 346*8975f5c5SAndroid Build Coastguard Worker # AndroidManifest.xml -> manifest/AndroidManifest.xml 347*8975f5c5SAndroid Build Coastguard Worker # classes.dex -> dex/classes.dex 348*8975f5c5SAndroid Build Coastguard Worker # res/ -> res/ (unchanged) 349*8975f5c5SAndroid Build Coastguard Worker # assets/ -> assets/ (unchanged) 350*8975f5c5SAndroid Build Coastguard Worker # <other-file> -> root/<other-file> 351*8975f5c5SAndroid Build Coastguard Worker # 352*8975f5c5SAndroid Build Coastguard Worker # Hence, the following variables are used to control the location of files in 353*8975f5c5SAndroid Build Coastguard Worker # the final archive. 354*8975f5c5SAndroid Build Coastguard Worker if options.format == 'bundle-module': 355*8975f5c5SAndroid Build Coastguard Worker apk_manifest_dir = 'manifest/' 356*8975f5c5SAndroid Build Coastguard Worker apk_root_dir = 'root/' 357*8975f5c5SAndroid Build Coastguard Worker apk_dex_dir = 'dex/' 358*8975f5c5SAndroid Build Coastguard Worker else: 359*8975f5c5SAndroid Build Coastguard Worker apk_manifest_dir = '' 360*8975f5c5SAndroid Build Coastguard Worker apk_root_dir = '' 361*8975f5c5SAndroid Build Coastguard Worker apk_dex_dir = '' 362*8975f5c5SAndroid Build Coastguard Worker 363*8975f5c5SAndroid Build Coastguard Worker def _GetAssetDetails(assets, uncompressed_assets, fast_align, allow_reads): 364*8975f5c5SAndroid Build Coastguard Worker ret = _GetAssetsToAdd(assets, 365*8975f5c5SAndroid Build Coastguard Worker fast_align, 366*8975f5c5SAndroid Build Coastguard Worker disable_compression=False, 367*8975f5c5SAndroid Build Coastguard Worker allow_reads=allow_reads, 368*8975f5c5SAndroid Build Coastguard Worker apk_root_dir=apk_root_dir) 369*8975f5c5SAndroid Build Coastguard Worker ret.extend( 370*8975f5c5SAndroid Build Coastguard Worker _GetAssetsToAdd(uncompressed_assets, 371*8975f5c5SAndroid Build Coastguard Worker fast_align, 372*8975f5c5SAndroid Build Coastguard Worker disable_compression=True, 373*8975f5c5SAndroid Build Coastguard Worker allow_reads=allow_reads, 374*8975f5c5SAndroid Build Coastguard Worker apk_root_dir=apk_root_dir)) 375*8975f5c5SAndroid Build Coastguard Worker return ret 376*8975f5c5SAndroid Build Coastguard Worker 377*8975f5c5SAndroid Build Coastguard Worker libs_to_add = _GetNativeLibrariesToAdd(native_libs, options.android_abi, 378*8975f5c5SAndroid Build Coastguard Worker fast_align, 379*8975f5c5SAndroid Build Coastguard Worker options.library_always_compress) 380*8975f5c5SAndroid Build Coastguard Worker if options.secondary_android_abi: 381*8975f5c5SAndroid Build Coastguard Worker libs_to_add.extend( 382*8975f5c5SAndroid Build Coastguard Worker _GetNativeLibrariesToAdd(secondary_native_libs, 383*8975f5c5SAndroid Build Coastguard Worker options.secondary_android_abi, 384*8975f5c5SAndroid Build Coastguard Worker fast_align, options.library_always_compress)) 385*8975f5c5SAndroid Build Coastguard Worker 386*8975f5c5SAndroid Build Coastguard Worker if options.expected_file: 387*8975f5c5SAndroid Build Coastguard Worker # We compute expectations without reading the files. This allows us to check 388*8975f5c5SAndroid Build Coastguard Worker # expectations for different targets by just generating their build_configs 389*8975f5c5SAndroid Build Coastguard Worker # and not have to first generate all the actual files and all their 390*8975f5c5SAndroid Build Coastguard Worker # dependencies (for example by just passing --only-verify-expectations). 391*8975f5c5SAndroid Build Coastguard Worker asset_details = _GetAssetDetails(assets, 392*8975f5c5SAndroid Build Coastguard Worker uncompressed_assets, 393*8975f5c5SAndroid Build Coastguard Worker fast_align, 394*8975f5c5SAndroid Build Coastguard Worker allow_reads=False) 395*8975f5c5SAndroid Build Coastguard Worker 396*8975f5c5SAndroid Build Coastguard Worker actual_data = _CreateExpectationsData(libs_to_add, asset_details) 397*8975f5c5SAndroid Build Coastguard Worker diff_utils.CheckExpectations(actual_data, options) 398*8975f5c5SAndroid Build Coastguard Worker 399*8975f5c5SAndroid Build Coastguard Worker if options.only_verify_expectations: 400*8975f5c5SAndroid Build Coastguard Worker if options.depfile: 401*8975f5c5SAndroid Build Coastguard Worker action_helpers.write_depfile(options.depfile, 402*8975f5c5SAndroid Build Coastguard Worker options.actual_file, 403*8975f5c5SAndroid Build Coastguard Worker inputs=depfile_deps) 404*8975f5c5SAndroid Build Coastguard Worker return 405*8975f5c5SAndroid Build Coastguard Worker 406*8975f5c5SAndroid Build Coastguard Worker # If we are past this point, we are going to actually create the final apk so 407*8975f5c5SAndroid Build Coastguard Worker # we should recompute asset details again but maybe perform some optimizations 408*8975f5c5SAndroid Build Coastguard Worker # based on the size of the files on disk. 409*8975f5c5SAndroid Build Coastguard Worker assets_to_add = _GetAssetDetails( 410*8975f5c5SAndroid Build Coastguard Worker assets, uncompressed_assets, fast_align, allow_reads=True) 411*8975f5c5SAndroid Build Coastguard Worker 412*8975f5c5SAndroid Build Coastguard Worker # Targets generally do not depend on apks, so no need for only_if_changed. 413*8975f5c5SAndroid Build Coastguard Worker with action_helpers.atomic_output(options.output_apk, 414*8975f5c5SAndroid Build Coastguard Worker only_if_changed=False) as f: 415*8975f5c5SAndroid Build Coastguard Worker with zipfile.ZipFile(options.resource_apk) as resource_apk, \ 416*8975f5c5SAndroid Build Coastguard Worker zipfile.ZipFile(f, 'w') as out_apk: 417*8975f5c5SAndroid Build Coastguard Worker 418*8975f5c5SAndroid Build Coastguard Worker def add_to_zip(zip_path, data, compress=True, alignment=4): 419*8975f5c5SAndroid Build Coastguard Worker zip_helpers.add_to_zip_hermetic( 420*8975f5c5SAndroid Build Coastguard Worker out_apk, 421*8975f5c5SAndroid Build Coastguard Worker zip_path, 422*8975f5c5SAndroid Build Coastguard Worker data=data, 423*8975f5c5SAndroid Build Coastguard Worker compress=compress, 424*8975f5c5SAndroid Build Coastguard Worker alignment=0 if compress and not fast_align else alignment) 425*8975f5c5SAndroid Build Coastguard Worker 426*8975f5c5SAndroid Build Coastguard Worker def copy_resource(zipinfo, out_dir=''): 427*8975f5c5SAndroid Build Coastguard Worker add_to_zip( 428*8975f5c5SAndroid Build Coastguard Worker out_dir + zipinfo.filename, 429*8975f5c5SAndroid Build Coastguard Worker resource_apk.read(zipinfo.filename), 430*8975f5c5SAndroid Build Coastguard Worker compress=zipinfo.compress_type != zipfile.ZIP_STORED) 431*8975f5c5SAndroid Build Coastguard Worker 432*8975f5c5SAndroid Build Coastguard Worker # Make assets come before resources in order to maintain the same file 433*8975f5c5SAndroid Build Coastguard Worker # ordering as GYP / aapt. http://crbug.com/561862 434*8975f5c5SAndroid Build Coastguard Worker resource_infos = resource_apk.infolist() 435*8975f5c5SAndroid Build Coastguard Worker 436*8975f5c5SAndroid Build Coastguard Worker # 1. AndroidManifest.xml 437*8975f5c5SAndroid Build Coastguard Worker logging.debug('Adding AndroidManifest.xml') 438*8975f5c5SAndroid Build Coastguard Worker copy_resource( 439*8975f5c5SAndroid Build Coastguard Worker resource_apk.getinfo('AndroidManifest.xml'), out_dir=apk_manifest_dir) 440*8975f5c5SAndroid Build Coastguard Worker 441*8975f5c5SAndroid Build Coastguard Worker # 2. Assets 442*8975f5c5SAndroid Build Coastguard Worker logging.debug('Adding assets/') 443*8975f5c5SAndroid Build Coastguard Worker _AddFiles(out_apk, assets_to_add) 444*8975f5c5SAndroid Build Coastguard Worker 445*8975f5c5SAndroid Build Coastguard Worker # 3. DEX and META-INF/services/ 446*8975f5c5SAndroid Build Coastguard Worker logging.debug('Adding classes.dex') 447*8975f5c5SAndroid Build Coastguard Worker if options.dex_file: 448*8975f5c5SAndroid Build Coastguard Worker with open(options.dex_file, 'rb') as dex_file_obj: 449*8975f5c5SAndroid Build Coastguard Worker if options.dex_file.endswith('.dex'): 450*8975f5c5SAndroid Build Coastguard Worker # This is the case for incremental_install=true. 451*8975f5c5SAndroid Build Coastguard Worker add_to_zip( 452*8975f5c5SAndroid Build Coastguard Worker apk_dex_dir + 'classes.dex', 453*8975f5c5SAndroid Build Coastguard Worker dex_file_obj.read(), 454*8975f5c5SAndroid Build Coastguard Worker compress=not options.uncompress_dex) 455*8975f5c5SAndroid Build Coastguard Worker else: 456*8975f5c5SAndroid Build Coastguard Worker with zipfile.ZipFile(dex_file_obj) as dex_zip: 457*8975f5c5SAndroid Build Coastguard Worker # Add META-INF/services. 458*8975f5c5SAndroid Build Coastguard Worker for name in sorted(dex_zip.namelist()): 459*8975f5c5SAndroid Build Coastguard Worker if name.startswith('META-INF/services/'): 460*8975f5c5SAndroid Build Coastguard Worker # proguard.py does not bundle these files (dex.py does) 461*8975f5c5SAndroid Build Coastguard Worker # because R8 optimizes all ServiceLoader calls. 462*8975f5c5SAndroid Build Coastguard Worker if options.dex_file.endswith('.r8dex.jar'): 463*8975f5c5SAndroid Build Coastguard Worker raise Exception( 464*8975f5c5SAndroid Build Coastguard Worker f'Expected no META-INF/services, but found: {name}' + 465*8975f5c5SAndroid Build Coastguard Worker f'in {options.dex_file}') 466*8975f5c5SAndroid Build Coastguard Worker add_to_zip(apk_root_dir + name, 467*8975f5c5SAndroid Build Coastguard Worker dex_zip.read(name), 468*8975f5c5SAndroid Build Coastguard Worker compress=False) 469*8975f5c5SAndroid Build Coastguard Worker # Add classes.dex. 470*8975f5c5SAndroid Build Coastguard Worker for name in dex_zip.namelist(): 471*8975f5c5SAndroid Build Coastguard Worker if name.endswith('.dex'): 472*8975f5c5SAndroid Build Coastguard Worker add_to_zip(apk_dex_dir + name, 473*8975f5c5SAndroid Build Coastguard Worker dex_zip.read(name), 474*8975f5c5SAndroid Build Coastguard Worker compress=not options.uncompress_dex) 475*8975f5c5SAndroid Build Coastguard Worker 476*8975f5c5SAndroid Build Coastguard Worker # 4. Native libraries. 477*8975f5c5SAndroid Build Coastguard Worker logging.debug('Adding lib/') 478*8975f5c5SAndroid Build Coastguard Worker _AddFiles(out_apk, libs_to_add) 479*8975f5c5SAndroid Build Coastguard Worker 480*8975f5c5SAndroid Build Coastguard Worker # Add a placeholder lib if the APK should be multi ABI but is missing libs 481*8975f5c5SAndroid Build Coastguard Worker # for one of the ABIs. 482*8975f5c5SAndroid Build Coastguard Worker native_lib_placeholders = options.native_lib_placeholders 483*8975f5c5SAndroid Build Coastguard Worker secondary_native_lib_placeholders = ( 484*8975f5c5SAndroid Build Coastguard Worker options.secondary_native_lib_placeholders) 485*8975f5c5SAndroid Build Coastguard Worker if options.is_multi_abi: 486*8975f5c5SAndroid Build Coastguard Worker if ((secondary_native_libs or secondary_native_lib_placeholders) 487*8975f5c5SAndroid Build Coastguard Worker and not native_libs and not native_lib_placeholders): 488*8975f5c5SAndroid Build Coastguard Worker native_lib_placeholders += ['libplaceholder.so'] 489*8975f5c5SAndroid Build Coastguard Worker if ((native_libs or native_lib_placeholders) 490*8975f5c5SAndroid Build Coastguard Worker and not secondary_native_libs 491*8975f5c5SAndroid Build Coastguard Worker and not secondary_native_lib_placeholders): 492*8975f5c5SAndroid Build Coastguard Worker secondary_native_lib_placeholders += ['libplaceholder.so'] 493*8975f5c5SAndroid Build Coastguard Worker 494*8975f5c5SAndroid Build Coastguard Worker # Add placeholder libs. 495*8975f5c5SAndroid Build Coastguard Worker for name in sorted(native_lib_placeholders): 496*8975f5c5SAndroid Build Coastguard Worker # Note: Empty libs files are ignored by md5check (can cause issues 497*8975f5c5SAndroid Build Coastguard Worker # with stale builds when the only change is adding/removing 498*8975f5c5SAndroid Build Coastguard Worker # placeholders). 499*8975f5c5SAndroid Build Coastguard Worker apk_path = 'lib/%s/%s' % (options.android_abi, name) 500*8975f5c5SAndroid Build Coastguard Worker alignment = _GetAbiAlignment(options.android_abi) 501*8975f5c5SAndroid Build Coastguard Worker add_to_zip(apk_path, '', alignment=alignment) 502*8975f5c5SAndroid Build Coastguard Worker 503*8975f5c5SAndroid Build Coastguard Worker for name in sorted(secondary_native_lib_placeholders): 504*8975f5c5SAndroid Build Coastguard Worker # Note: Empty libs files are ignored by md5check (can cause issues 505*8975f5c5SAndroid Build Coastguard Worker # with stale builds when the only change is adding/removing 506*8975f5c5SAndroid Build Coastguard Worker # placeholders). 507*8975f5c5SAndroid Build Coastguard Worker apk_path = 'lib/%s/%s' % (options.secondary_android_abi, name) 508*8975f5c5SAndroid Build Coastguard Worker alignment = _GetAbiAlignment(options.secondary_android_abi) 509*8975f5c5SAndroid Build Coastguard Worker add_to_zip(apk_path, '', alignment=alignment) 510*8975f5c5SAndroid Build Coastguard Worker 511*8975f5c5SAndroid Build Coastguard Worker # 5. Resources 512*8975f5c5SAndroid Build Coastguard Worker logging.debug('Adding res/') 513*8975f5c5SAndroid Build Coastguard Worker for info in sorted(resource_infos, key=lambda i: i.filename): 514*8975f5c5SAndroid Build Coastguard Worker if info.filename != 'AndroidManifest.xml': 515*8975f5c5SAndroid Build Coastguard Worker copy_resource(info) 516*8975f5c5SAndroid Build Coastguard Worker 517*8975f5c5SAndroid Build Coastguard Worker # 6. Java resources that should be accessible via 518*8975f5c5SAndroid Build Coastguard Worker # Class.getResourceAsStream(), in particular parts of Emma jar. 519*8975f5c5SAndroid Build Coastguard Worker # Prebuilt jars may contain class files which we shouldn't include. 520*8975f5c5SAndroid Build Coastguard Worker logging.debug('Adding Java resources') 521*8975f5c5SAndroid Build Coastguard Worker for java_resource in options.java_resources: 522*8975f5c5SAndroid Build Coastguard Worker with zipfile.ZipFile(java_resource, 'r') as java_resource_jar: 523*8975f5c5SAndroid Build Coastguard Worker for apk_path in sorted(java_resource_jar.namelist()): 524*8975f5c5SAndroid Build Coastguard Worker apk_path_lower = apk_path.lower() 525*8975f5c5SAndroid Build Coastguard Worker 526*8975f5c5SAndroid Build Coastguard Worker if apk_path_lower.startswith('meta-inf/'): 527*8975f5c5SAndroid Build Coastguard Worker continue 528*8975f5c5SAndroid Build Coastguard Worker if apk_path_lower.endswith('/'): 529*8975f5c5SAndroid Build Coastguard Worker continue 530*8975f5c5SAndroid Build Coastguard Worker if apk_path_lower.endswith('.class'): 531*8975f5c5SAndroid Build Coastguard Worker continue 532*8975f5c5SAndroid Build Coastguard Worker 533*8975f5c5SAndroid Build Coastguard Worker add_to_zip(apk_root_dir + apk_path, 534*8975f5c5SAndroid Build Coastguard Worker java_resource_jar.read(apk_path)) 535*8975f5c5SAndroid Build Coastguard Worker 536*8975f5c5SAndroid Build Coastguard Worker if options.format == 'apk' and options.key_path: 537*8975f5c5SAndroid Build Coastguard Worker zipalign_path = None if fast_align else options.zipalign_path 538*8975f5c5SAndroid Build Coastguard Worker finalize_apk.FinalizeApk(options.apksigner_jar, 539*8975f5c5SAndroid Build Coastguard Worker zipalign_path, 540*8975f5c5SAndroid Build Coastguard Worker f.name, 541*8975f5c5SAndroid Build Coastguard Worker f.name, 542*8975f5c5SAndroid Build Coastguard Worker options.key_path, 543*8975f5c5SAndroid Build Coastguard Worker options.key_passwd, 544*8975f5c5SAndroid Build Coastguard Worker options.key_name, 545*8975f5c5SAndroid Build Coastguard Worker int(options.min_sdk_version), 546*8975f5c5SAndroid Build Coastguard Worker warnings_as_errors=options.warnings_as_errors) 547*8975f5c5SAndroid Build Coastguard Worker logging.debug('Moving file into place') 548*8975f5c5SAndroid Build Coastguard Worker 549*8975f5c5SAndroid Build Coastguard Worker if options.depfile: 550*8975f5c5SAndroid Build Coastguard Worker action_helpers.write_depfile(options.depfile, 551*8975f5c5SAndroid Build Coastguard Worker options.output_apk, 552*8975f5c5SAndroid Build Coastguard Worker inputs=depfile_deps) 553*8975f5c5SAndroid Build Coastguard Worker 554*8975f5c5SAndroid Build Coastguard Worker 555*8975f5c5SAndroid Build Coastguard Workerif __name__ == '__main__': 556*8975f5c5SAndroid Build Coastguard Worker main(sys.argv[1:]) 557