1#!/usr/bin/env python3 2# 3# Copyright 2017 The Chromium Authors 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7"""Creates an Android .aar file.""" 8 9import argparse 10import os 11import posixpath 12import shutil 13import sys 14import tempfile 15import zipfile 16 17import filter_zip 18from util import build_utils 19import action_helpers # build_utils adds //build to sys.path. 20import zip_helpers 21 22 23_ANDROID_BUILD_DIR = os.path.dirname(os.path.dirname(__file__)) 24 25 26def _MergeRTxt(r_paths, include_globs, exclude_globs): 27 """Merging the given R.txt files and returns them as a string.""" 28 all_lines = set() 29 for r_path in r_paths: 30 if include_globs and not build_utils.MatchesGlob(r_path, include_globs) or \ 31 exclude_globs and build_utils.MatchesGlob(r_path, exclude_globs): 32 continue 33 with open(r_path) as f: 34 all_lines.update(f.readlines()) 35 return ''.join(sorted(all_lines)) 36 37 38def _MergeProguardConfigs(proguard_configs): 39 """Merging the given proguard config files and returns them as a string.""" 40 ret = [] 41 for config in proguard_configs: 42 ret.append('# FROM: {}'.format(config)) 43 with open(config) as f: 44 ret.append(f.read()) 45 return '\n'.join(ret) 46 47 48def _AddResources(aar_zip, resource_zips, include_globs, exclude_globs): 49 """Adds all resource zips to the given aar_zip. 50 51 Ensures all res/values/* files have unique names by prefixing them. 52 """ 53 for i, path in enumerate(resource_zips): 54 if include_globs and not build_utils.MatchesGlob(path, include_globs) or \ 55 exclude_globs and build_utils.MatchesGlob(path, exclude_globs): 56 continue 57 with zipfile.ZipFile(path) as res_zip: 58 for info in res_zip.infolist(): 59 data = res_zip.read(info) 60 dirname, basename = posixpath.split(info.filename) 61 if 'values' in dirname: 62 root, ext = os.path.splitext(basename) 63 basename = '{}_{}{}'.format(root, i, ext) 64 info.filename = posixpath.join(dirname, basename) 65 info.filename = posixpath.join('res', info.filename) 66 aar_zip.writestr(info, data) 67 68 69def main(args): 70 args = build_utils.ExpandFileArgs(args) 71 parser = argparse.ArgumentParser() 72 action_helpers.add_depfile_arg(parser) 73 parser.add_argument('--output', required=True, help='Path to output aar.') 74 parser.add_argument('--jars', required=True, help='GN list of jar inputs.') 75 parser.add_argument('--dependencies-res-zips', required=True, 76 help='GN list of resource zips') 77 parser.add_argument('--r-text-files', required=True, 78 help='GN list of R.txt files to merge') 79 parser.add_argument('--proguard-configs', required=True, 80 help='GN list of ProGuard flag files to merge.') 81 parser.add_argument( 82 '--android-manifest', 83 help='Path to AndroidManifest.xml to include.', 84 default=os.path.join(_ANDROID_BUILD_DIR, 'AndroidManifest.xml')) 85 parser.add_argument('--native-libraries', default='', 86 help='GN list of native libraries. If non-empty then ' 87 'ABI must be specified.') 88 parser.add_argument('--abi', 89 help='ABI (e.g. armeabi-v7a) for native libraries.') 90 parser.add_argument( 91 '--jar-excluded-globs', 92 help='GN-list of globs for paths to exclude in jar.') 93 parser.add_argument( 94 '--jar-included-globs', 95 help='GN-list of globs for paths to include in jar.') 96 parser.add_argument( 97 '--resource-included-globs', 98 help='GN-list of globs for paths to include in R.txt and resources zips.') 99 parser.add_argument( 100 '--resource-excluded-globs', 101 help='GN-list of globs for paths to exclude in R.txt and resources zips.') 102 103 options = parser.parse_args(args) 104 105 if options.native_libraries and not options.abi: 106 parser.error('You must provide --abi if you have native libs') 107 108 options.jars = action_helpers.parse_gn_list(options.jars) 109 options.dependencies_res_zips = action_helpers.parse_gn_list( 110 options.dependencies_res_zips) 111 options.r_text_files = action_helpers.parse_gn_list(options.r_text_files) 112 options.proguard_configs = action_helpers.parse_gn_list( 113 options.proguard_configs) 114 options.native_libraries = action_helpers.parse_gn_list( 115 options.native_libraries) 116 options.jar_excluded_globs = action_helpers.parse_gn_list( 117 options.jar_excluded_globs) 118 options.jar_included_globs = action_helpers.parse_gn_list( 119 options.jar_included_globs) 120 options.resource_included_globs = action_helpers.parse_gn_list( 121 options.resource_included_globs) 122 options.resource_excluded_globs = action_helpers.parse_gn_list( 123 options.resource_excluded_globs) 124 125 with tempfile.NamedTemporaryFile(delete=False) as staging_file: 126 try: 127 with zipfile.ZipFile(staging_file.name, 'w') as z: 128 zip_helpers.add_to_zip_hermetic(z, 129 'AndroidManifest.xml', 130 src_path=options.android_manifest) 131 132 path_transform = filter_zip.CreatePathTransform( 133 options.jar_excluded_globs, options.jar_included_globs) 134 with tempfile.NamedTemporaryFile() as jar_file: 135 zip_helpers.merge_zips(jar_file.name, 136 options.jars, 137 path_transform=path_transform) 138 zip_helpers.add_to_zip_hermetic(z, 139 'classes.jar', 140 src_path=jar_file.name) 141 zip_helpers.add_to_zip_hermetic(z, 142 'R.txt', 143 data=_MergeRTxt( 144 options.r_text_files, 145 options.resource_included_globs, 146 options.resource_excluded_globs)) 147 zip_helpers.add_to_zip_hermetic(z, 'public.txt', data='') 148 149 if options.proguard_configs: 150 zip_helpers.add_to_zip_hermetic(z, 151 'proguard.txt', 152 data=_MergeProguardConfigs( 153 options.proguard_configs)) 154 155 _AddResources(z, options.dependencies_res_zips, 156 options.resource_included_globs, 157 options.resource_excluded_globs) 158 159 for native_library in options.native_libraries: 160 libname = os.path.basename(native_library) 161 zip_helpers.add_to_zip_hermetic(z, 162 os.path.join('jni', options.abi, 163 libname), 164 src_path=native_library) 165 except: 166 os.unlink(staging_file.name) 167 raise 168 shutil.move(staging_file.name, options.output) 169 170 if options.depfile: 171 all_inputs = (options.jars + options.dependencies_res_zips + 172 options.r_text_files + options.proguard_configs) 173 action_helpers.write_depfile(options.depfile, options.output, all_inputs) 174 175 176if __name__ == '__main__': 177 main(sys.argv[1:]) 178