1# This Source Code Form is subject to the terms of the Mozilla Public 2# License, v. 2.0. If a copy of the MPL was not distributed with this 3# file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 5''' 6Script to produce an Android package (.apk) for Fennec. 7''' 8 9from __future__ import absolute_import, print_function 10 11import argparse 12import buildconfig 13import os 14import subprocess 15import sys 16 17from mozpack.copier import Jarrer 18from mozpack.files import ( 19 DeflatedFile, 20 File, 21 FileFinder, 22) 23from mozpack.mozjar import JarReader 24import mozpack.path as mozpath 25 26 27def package_fennec_apk(inputs=[], omni_ja=None, classes_dex=None, 28 lib_dirs=[], 29 assets_dirs=[], 30 features_dirs=[], 31 root_files=[], 32 verbose=False): 33 jarrer = Jarrer(optimize=False) 34 35 # First, take input files. The contents of the later files overwrites the 36 # content of earlier files. 37 for input in inputs: 38 jar = JarReader(input) 39 for file in jar: 40 path = file.filename 41 if jarrer.contains(path): 42 jarrer.remove(path) 43 jarrer.add(path, DeflatedFile(file), compress=file.compressed) 44 45 def add(path, file, compress=None): 46 abspath = os.path.abspath(file.path) 47 if verbose: 48 print('Packaging %s from %s' % (path, file.path)) 49 if not os.path.exists(abspath): 50 raise ValueError('File %s not found (looked for %s)' % \ 51 (file.path, abspath)) 52 if jarrer.contains(path): 53 jarrer.remove(path) 54 jarrer.add(path, file, compress=compress) 55 56 for features_dir in features_dirs: 57 finder = FileFinder(features_dir, find_executables=False) 58 for p, f in finder.find('**'): 59 add(mozpath.join('assets', 'features', p), f, False) 60 61 for assets_dir in assets_dirs: 62 finder = FileFinder(assets_dir, find_executables=False) 63 for p, f in finder.find('**'): 64 compress = None # Take default from Jarrer. 65 if p.endswith('.so'): 66 # Asset libraries are special. 67 if f.open().read(5)[1:] == '7zXZ': 68 print('%s is already compressed' % p) 69 # We need to store (rather than deflate) compressed libraries 70 # (even if we don't compress them ourselves). 71 compress = False 72 elif buildconfig.substs.get('XZ'): 73 cmd = [buildconfig.substs.get('XZ'), '-zkf', 74 mozpath.join(finder.base, p)] 75 76 bcj = None 77 if buildconfig.substs.get('MOZ_THUMB2'): 78 bcj = '--armthumb' 79 elif buildconfig.substs.get('CPU_ARCH') == 'arm': 80 bcj = '--arm' 81 elif buildconfig.substs.get('CPU_ARCH') == 'x86': 82 bcj = '--x86' 83 84 if bcj: 85 cmd.extend([bcj, '--lzma2']) 86 print('xz-compressing %s with %s' % (p, ' '.join(cmd))) 87 subprocess.check_output(cmd) 88 os.rename(f.path + '.xz', f.path) 89 compress = False 90 91 add(mozpath.join('assets', p), f, compress=compress) 92 93 for lib_dir in lib_dirs: 94 finder = FileFinder(lib_dir, find_executables=False) 95 for p, f in finder.find('**'): 96 add(mozpath.join('lib', p), f) 97 98 for root_file in root_files: 99 add(os.path.basename(root_file), File(root_file)) 100 101 if omni_ja: 102 add(mozpath.join('assets', 'omni.ja'), File(omni_ja), compress=False) 103 104 if classes_dex: 105 add('classes.dex', File(classes_dex)) 106 107 return jarrer 108 109 110def main(args): 111 parser = argparse.ArgumentParser() 112 parser.add_argument('--verbose', '-v', default=False, action='store_true', 113 help='be verbose') 114 parser.add_argument('--inputs', nargs='+', 115 help='Input skeleton AP_ or APK file(s).') 116 parser.add_argument('-o', '--output', 117 help='Output APK file.') 118 parser.add_argument('--omnijar', default=None, 119 help='Optional omni.ja to pack into APK file.') 120 parser.add_argument('--classes-dex', default=None, 121 help='Optional classes.dex to pack into APK file.') 122 parser.add_argument('--lib-dirs', nargs='*', default=[], 123 help='Optional lib/ dirs to pack into APK file.') 124 parser.add_argument('--assets-dirs', nargs='*', default=[], 125 help='Optional assets/ dirs to pack into APK file.') 126 parser.add_argument('--features-dirs', nargs='*', default=[], 127 help='Optional features/ dirs to pack into APK file.') 128 parser.add_argument('--root-files', nargs='*', default=[], 129 help='Optional files to pack into APK file root.') 130 args = parser.parse_args(args) 131 132 if buildconfig.substs.get('OMNIJAR_NAME') != 'assets/omni.ja': 133 raise ValueError("Don't know how package Fennec APKs when " 134 " OMNIJAR_NAME is not 'assets/omni.jar'.") 135 136 jarrer = package_fennec_apk(inputs=args.inputs, 137 omni_ja=args.omnijar, 138 classes_dex=args.classes_dex, 139 lib_dirs=args.lib_dirs, 140 assets_dirs=args.assets_dirs, 141 features_dirs=args.features_dirs, 142 root_files=args.root_files, 143 verbose=args.verbose) 144 jarrer.copy(args.output) 145 146 return 0 147 148 149if __name__ == '__main__': 150 sys.exit(main(sys.argv[1:])) 151