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