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