| #!/usr/bin/env python |
| |
| # Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. |
| # |
| # Use of this source code is governed by a BSD-style license |
| # that can be found in the LICENSE file in the root of the source |
| # tree. An additional intellectual property rights grant can be found |
| # in the file PATENTS. All contributing project authors may |
| # be found in the AUTHORS file in the root of the source tree. |
| |
| """Script to generate libwebrtc.aar for distribution. |
| |
| The script has to be run from the root src folder. |
| ./tools_webrtc/android/build_aar.py |
| |
| .aar-file is just a zip-archive containing the files of the library. The file |
| structure generated by this script looks like this: |
| - AndroidManifest.xml |
| - classes.jar |
| - libs/ |
| - armeabi-v7a/ |
| - libjingle_peerconnection_so.so |
| - x86/ |
| - libjingle_peerconnection_so.so |
| """ |
| |
| import argparse |
| import logging |
| import os |
| import shutil |
| import subprocess |
| import sys |
| import tempfile |
| import zipfile |
| |
| |
| SCRIPT_DIR = os.path.dirname(os.path.realpath(sys.argv[0])) |
| SRC_DIR = os.path.normpath(os.path.join(SCRIPT_DIR, os.pardir, os.pardir)) |
| DEFAULT_ARCHS = ['armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'] |
| NEEDED_SO_FILES = ['libjingle_peerconnection_so.so'] |
| JAR_FILE = 'lib.java/sdk/android/libwebrtc.jar' |
| MANIFEST_FILE = 'sdk/android/AndroidManifest.xml' |
| TARGETS = [ |
| 'sdk/android:libwebrtc', |
| 'sdk/android:libjingle_peerconnection_so', |
| ] |
| |
| sys.path.append(os.path.join(SCRIPT_DIR, '..', 'libs')) |
| from generate_licenses import LicenseBuilder |
| |
| sys.path.append(os.path.join(SRC_DIR, 'build')) |
| import find_depot_tools |
| |
| |
| |
| def _ParseArgs(): |
| parser = argparse.ArgumentParser(description='libwebrtc.aar generator.') |
| parser.add_argument('--build-dir', |
| help='Build dir. By default will create and use temporary dir.') |
| parser.add_argument('--output', default='libwebrtc.aar', |
| help='Output file of the script.') |
| parser.add_argument('--arch', default=DEFAULT_ARCHS, nargs='*', |
| help='Architectures to build. Defaults to %(default)s.') |
| parser.add_argument('--use-goma', action='store_true', default=False, |
| help='Use goma.') |
| parser.add_argument('--verbose', action='store_true', default=False, |
| help='Debug logging.') |
| parser.add_argument('--extra-gn-args', default=[], nargs='*', |
| help="""Additional GN arguments to be used during Ninja generation. |
| These are passed to gn inside `--args` switch and |
| applied after any other arguments and will |
| override any values defined by the script. |
| Example of building debug aar file: |
| build_aar.py --extra-gn-args='is_debug=true'""") |
| parser.add_argument('--extra-ninja-switches', default=[], nargs='*', |
| help="""Additional Ninja switches to be used during compilation. |
| These are applied after any other Ninja switches. |
| Example of enabling verbose Ninja output: |
| build_aar.py --extra-ninja-switches='-v'""") |
| parser.add_argument('--extra-gn-switches', default=[], nargs='*', |
| help="""Additional GN switches to be used during compilation. |
| These are applied after any other GN switches. |
| Example of enabling verbose GN output: |
| build_aar.py --extra-gn-switches='-v'""") |
| return parser.parse_args() |
| |
| |
| def _RunGN(args): |
| cmd = [sys.executable, |
| os.path.join(find_depot_tools.DEPOT_TOOLS_PATH, 'gn.py')] |
| cmd.extend(args) |
| logging.debug('Running: %r', cmd) |
| subprocess.check_call(cmd) |
| |
| |
| def _RunNinja(output_directory, args): |
| cmd = [os.path.join(find_depot_tools.DEPOT_TOOLS_PATH, 'ninja'), |
| '-C', output_directory] |
| cmd.extend(args) |
| logging.debug('Running: %r', cmd) |
| subprocess.check_call(cmd) |
| |
| |
| def _EncodeForGN(value): |
| """Encodes value as a GN literal.""" |
| if isinstance(value, str): |
| return '"' + value + '"' |
| elif isinstance(value, bool): |
| return repr(value).lower() |
| else: |
| return repr(value) |
| |
| |
| def _GetOutputDirectory(build_dir, arch): |
| """Returns the GN output directory for the target architecture.""" |
| return os.path.join(build_dir, arch) |
| |
| |
| def _GetTargetCpu(arch): |
| """Returns target_cpu for the GN build with the given architecture.""" |
| if arch in ['armeabi', 'armeabi-v7a']: |
| return 'arm' |
| elif arch == 'arm64-v8a': |
| return 'arm64' |
| elif arch == 'x86': |
| return 'x86' |
| elif arch == 'x86_64': |
| return 'x64' |
| else: |
| raise Exception('Unknown arch: ' + arch) |
| |
| |
| def _GetArmVersion(arch): |
| """Returns arm_version for the GN build with the given architecture.""" |
| if arch == 'armeabi': |
| return 6 |
| elif arch == 'armeabi-v7a': |
| return 7 |
| elif arch in ['arm64-v8a', 'x86', 'x86_64']: |
| return None |
| else: |
| raise Exception('Unknown arch: ' + arch) |
| |
| |
| def Build(build_dir, arch, use_goma, extra_gn_args, extra_gn_switches, |
| extra_ninja_switches): |
| """Generates target architecture using GN and builds it using ninja.""" |
| logging.info('Building: %s', arch) |
| output_directory = _GetOutputDirectory(build_dir, arch) |
| gn_args = { |
| 'target_os': 'android', |
| 'is_debug': False, |
| 'is_component_build': False, |
| 'rtc_include_tests': False, |
| 'target_cpu': _GetTargetCpu(arch), |
| 'use_goma': use_goma |
| } |
| arm_version = _GetArmVersion(arch) |
| if arm_version: |
| gn_args['arm_version'] = arm_version |
| gn_args_str = '--args=' + ' '.join([ |
| k + '=' + _EncodeForGN(v) for k, v in gn_args.items()] + extra_gn_args) |
| |
| gn_args = ['gen', output_directory, gn_args_str] |
| gn_args.extend(extra_gn_switches) |
| _RunGN(gn_args) |
| |
| ninja_args = TARGETS[:] |
| if use_goma: |
| ninja_args.extend(['-j', '200']) |
| ninja_args.extend(extra_ninja_switches) |
| _RunNinja(output_directory, ninja_args) |
| |
| |
| def CollectCommon(aar_file, build_dir, arch): |
| """Collects architecture independent files into the .aar-archive.""" |
| logging.info('Collecting common files.') |
| output_directory = _GetOutputDirectory(build_dir, arch) |
| aar_file.write(MANIFEST_FILE, 'AndroidManifest.xml') |
| aar_file.write(os.path.join(output_directory, JAR_FILE), 'classes.jar') |
| |
| |
| def Collect(aar_file, build_dir, arch): |
| """Collects architecture specific files into the .aar-archive.""" |
| logging.info('Collecting: %s', arch) |
| output_directory = _GetOutputDirectory(build_dir, arch) |
| |
| abi_dir = os.path.join('jni', arch) |
| for so_file in NEEDED_SO_FILES: |
| aar_file.write(os.path.join(output_directory, so_file), |
| os.path.join(abi_dir, so_file)) |
| |
| |
| def GenerateLicenses(output_dir, build_dir, archs): |
| builder = LicenseBuilder( |
| [_GetOutputDirectory(build_dir, arch) for arch in archs], TARGETS) |
| builder.GenerateLicenseText(output_dir) |
| |
| |
| def BuildAar(archs, output_file, use_goma=False, extra_gn_args=None, |
| ext_build_dir=None, extra_gn_switches=None, |
| extra_ninja_switches=None): |
| extra_gn_args = extra_gn_args or [] |
| extra_gn_switches = extra_gn_switches or [] |
| extra_ninja_switches = extra_ninja_switches or [] |
| build_dir = ext_build_dir if ext_build_dir else tempfile.mkdtemp() |
| |
| for arch in archs: |
| Build(build_dir, arch, use_goma, extra_gn_args, extra_gn_switches, |
| extra_ninja_switches) |
| |
| with zipfile.ZipFile(output_file, 'w') as aar_file: |
| # Architecture doesn't matter here, arbitrarily using the first one. |
| CollectCommon(aar_file, build_dir, archs[0]) |
| for arch in archs: |
| Collect(aar_file, build_dir, arch) |
| |
| license_dir = os.path.dirname(os.path.realpath(output_file)) |
| GenerateLicenses(license_dir, build_dir, archs) |
| |
| if not ext_build_dir: |
| shutil.rmtree(build_dir, True) |
| |
| |
| def main(): |
| args = _ParseArgs() |
| logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO) |
| |
| BuildAar(args.arch, args.output, args.use_goma, args.extra_gn_args, |
| args.build_dir, args.extra_gn_switches, args.extra_ninja_switches) |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main()) |