1#!/usr/bin/env vpython3 2 3# Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. 4# 5# Use of this source code is governed by a BSD-style license 6# that can be found in the LICENSE file in the root of the source 7# tree. An additional intellectual property rights grant can be found 8# in the file PATENTS. All contributing project authors may 9# be found in the AUTHORS file in the root of the source tree. 10"""Script to generate libwebrtc.aar for distribution. 11 12The script has to be run from the root src folder. 13./tools_webrtc/android/build_aar.py 14 15.aar-file is just a zip-archive containing the files of the library. The file 16structure generated by this script looks like this: 17 - AndroidManifest.xml 18 - classes.jar 19 - libs/ 20 - armeabi-v7a/ 21 - libjingle_peerconnection_so.so 22 - x86/ 23 - libjingle_peerconnection_so.so 24""" 25 26import argparse 27import logging 28import os 29import shutil 30import subprocess 31import sys 32import tempfile 33import zipfile 34 35SCRIPT_DIR = os.path.dirname(os.path.realpath(sys.argv[0])) 36SRC_DIR = os.path.normpath(os.path.join(SCRIPT_DIR, os.pardir, os.pardir)) 37DEFAULT_ARCHS = ['armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'] 38NEEDED_SO_FILES = ['libjingle_peerconnection_so.so'] 39JAR_FILE = 'lib.java/sdk/android/libwebrtc.jar' 40MANIFEST_FILE = 'sdk/android/AndroidManifest.xml' 41TARGETS = [ 42 'sdk/android:libwebrtc', 43 'sdk/android:libjingle_peerconnection_so', 44] 45 46sys.path.append(os.path.join(SCRIPT_DIR, '..', 'libs')) 47from generate_licenses import LicenseBuilder 48 49sys.path.append(os.path.join(SRC_DIR, 'build')) 50import find_depot_tools 51 52 53def _ParseArgs(): 54 parser = argparse.ArgumentParser(description='libwebrtc.aar generator.') 55 parser.add_argument( 56 '--build-dir', 57 type=os.path.abspath, 58 help='Build dir. By default will create and use temporary dir.') 59 parser.add_argument('--output', 60 default='libwebrtc.aar', 61 type=os.path.abspath, 62 help='Output file of the script.') 63 parser.add_argument('--arch', 64 default=DEFAULT_ARCHS, 65 nargs='*', 66 help='Architectures to build. Defaults to %(default)s.') 67 parser.add_argument('--use-goma', 68 action='store_true', 69 default=False, 70 help='Use goma.') 71 parser.add_argument('--use-remoteexec', 72 action='store_true', 73 default=False, 74 help='Use RBE.') 75 parser.add_argument('--use-unstripped-libs', 76 action='store_true', 77 default=False, 78 help='Use unstripped .so files within libwebrtc.aar') 79 parser.add_argument('--verbose', 80 action='store_true', 81 default=False, 82 help='Debug logging.') 83 parser.add_argument( 84 '--extra-gn-args', 85 default=[], 86 nargs='*', 87 help="""Additional GN arguments to be used during Ninja generation. 88 These are passed to gn inside `--args` switch and 89 applied after any other arguments and will 90 override any values defined by the script. 91 Example of building debug aar file: 92 build_aar.py --extra-gn-args='is_debug=true'""") 93 parser.add_argument( 94 '--extra-ninja-switches', 95 default=[], 96 nargs='*', 97 help="""Additional Ninja switches to be used during compilation. 98 These are applied after any other Ninja switches. 99 Example of enabling verbose Ninja output: 100 build_aar.py --extra-ninja-switches='-v'""") 101 parser.add_argument( 102 '--extra-gn-switches', 103 default=[], 104 nargs='*', 105 help="""Additional GN switches to be used during compilation. 106 These are applied after any other GN switches. 107 Example of enabling verbose GN output: 108 build_aar.py --extra-gn-switches='-v'""") 109 return parser.parse_args() 110 111 112def _RunGN(args): 113 cmd = [ 114 sys.executable, 115 os.path.join(find_depot_tools.DEPOT_TOOLS_PATH, 'gn.py') 116 ] 117 cmd.extend(args) 118 logging.debug('Running: %r', cmd) 119 subprocess.check_call(cmd) 120 121 122def _RunNinja(output_directory, args): 123 cmd = [ 124 os.path.join(find_depot_tools.DEPOT_TOOLS_PATH, 'ninja'), '-C', 125 output_directory 126 ] 127 cmd.extend(args) 128 logging.debug('Running: %r', cmd) 129 subprocess.check_call(cmd) 130 131 132def _EncodeForGN(value): 133 """Encodes value as a GN literal.""" 134 if isinstance(value, str): 135 return '"' + value + '"' 136 if isinstance(value, bool): 137 return repr(value).lower() 138 return repr(value) 139 140 141def _GetOutputDirectory(build_dir, arch): 142 """Returns the GN output directory for the target architecture.""" 143 return os.path.join(build_dir, arch) 144 145 146def _GetTargetCpu(arch): 147 """Returns target_cpu for the GN build with the given architecture.""" 148 if arch in ['armeabi', 'armeabi-v7a']: 149 return 'arm' 150 if arch == 'arm64-v8a': 151 return 'arm64' 152 if arch == 'x86': 153 return 'x86' 154 if arch == 'x86_64': 155 return 'x64' 156 raise Exception('Unknown arch: ' + arch) 157 158 159def _GetArmVersion(arch): 160 """Returns arm_version for the GN build with the given architecture.""" 161 if arch == 'armeabi': 162 return 6 163 if arch == 'armeabi-v7a': 164 return 7 165 if arch in ['arm64-v8a', 'x86', 'x86_64']: 166 return None 167 raise Exception('Unknown arch: ' + arch) 168 169 170def Build(build_dir, arch, use_goma, use_remoteexec, extra_gn_args, 171 extra_gn_switches, extra_ninja_switches): 172 """Generates target architecture using GN and builds it using ninja.""" 173 logging.info('Building: %s', arch) 174 output_directory = _GetOutputDirectory(build_dir, arch) 175 gn_args = { 176 'target_os': 'android', 177 'is_debug': False, 178 'is_component_build': False, 179 'rtc_include_tests': False, 180 'target_cpu': _GetTargetCpu(arch), 181 'use_goma': use_goma, 182 'use_remoteexec': use_remoteexec, 183 } 184 arm_version = _GetArmVersion(arch) 185 if arm_version: 186 gn_args['arm_version'] = arm_version 187 gn_args_str = '--args=' + ' '.join( 188 [k + '=' + _EncodeForGN(v) for k, v in gn_args.items()] + extra_gn_args) 189 190 gn_args_list = ['gen', output_directory, gn_args_str] 191 gn_args_list.extend(extra_gn_switches) 192 _RunGN(gn_args_list) 193 194 ninja_args = TARGETS[:] 195 if use_goma or use_remoteexec: 196 ninja_args.extend(['-j', '200']) 197 ninja_args.extend(extra_ninja_switches) 198 _RunNinja(output_directory, ninja_args) 199 200 201def CollectCommon(aar_file, build_dir, arch): 202 """Collects architecture independent files into the .aar-archive.""" 203 logging.info('Collecting common files.') 204 output_directory = _GetOutputDirectory(build_dir, arch) 205 aar_file.write(MANIFEST_FILE, 'AndroidManifest.xml') 206 aar_file.write(os.path.join(output_directory, JAR_FILE), 'classes.jar') 207 208 209def Collect(aar_file, build_dir, arch, unstripped): 210 """Collects architecture specific files into the .aar-archive.""" 211 logging.info('Collecting: %s', arch) 212 output_directory = _GetOutputDirectory(build_dir, arch) 213 214 abi_dir = os.path.join('jni', arch) 215 for so_file in NEEDED_SO_FILES: 216 source_so_file = os.path.join("lib.unstripped", 217 so_file) if unstripped else so_file 218 aar_file.write(os.path.join(output_directory, source_so_file), 219 os.path.join(abi_dir, so_file)) 220 221 222def GenerateLicenses(output_dir, build_dir, archs): 223 builder = LicenseBuilder( 224 [_GetOutputDirectory(build_dir, arch) for arch in archs], TARGETS) 225 builder.GenerateLicenseText(output_dir) 226 227 228def BuildAar(archs, 229 output_file, 230 use_goma=False, 231 use_remoteexec=False, 232 extra_gn_args=None, 233 ext_build_dir=None, 234 extra_gn_switches=None, 235 extra_ninja_switches=None, 236 unstripped=False): 237 extra_gn_args = extra_gn_args or [] 238 extra_gn_switches = extra_gn_switches or [] 239 extra_ninja_switches = extra_ninja_switches or [] 240 build_dir = ext_build_dir if ext_build_dir else tempfile.mkdtemp() 241 242 for arch in archs: 243 Build(build_dir, arch, use_goma, use_remoteexec, extra_gn_args, 244 extra_gn_switches, extra_ninja_switches) 245 246 with zipfile.ZipFile(output_file, 'w') as aar_file: 247 # Architecture doesn't matter here, arbitrarily using the first one. 248 CollectCommon(aar_file, build_dir, archs[0]) 249 for arch in archs: 250 Collect(aar_file, build_dir, arch, unstripped) 251 252 license_dir = os.path.dirname(os.path.realpath(output_file)) 253 GenerateLicenses(license_dir, build_dir, archs) 254 255 if not ext_build_dir: 256 shutil.rmtree(build_dir, True) 257 258 259def main(): 260 args = _ParseArgs() 261 logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO) 262 263 BuildAar(args.arch, args.output, args.use_goma, args.use_remoteexec, 264 args.extra_gn_args, args.build_dir, args.extra_gn_switches, 265 args.extra_ninja_switches, args.use_unstripped_libs) 266 267 268if __name__ == '__main__': 269 sys.exit(main()) 270