blob: 7af47716c5eaf351783fbee3fa2f2119de11316c [file] [log] [blame]
oprypin7a2d8ca2017-02-06 07:53:41 -08001#!/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"""WebRTC iOS FAT libraries build script.
12Each architecture is compiled separately before being merged together.
13By default, the library is created in out_ios_libs/. (Change with -o.)
14The headers will be copied to out_ios_libs/include.
15"""
16
17import argparse
18import distutils.dir_util
19import logging
20import os
21import shutil
22import subprocess
23import sys
24
25
26os.environ['PATH'] = '/usr/libexec' + os.pathsep + os.environ['PATH']
27
28SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
Henrik Kjellander368b5ff2017-02-15 20:51:26 +010029WEBRTC_SRC_DIR = os.path.abspath(os.path.join(SCRIPT_DIR, '..', '..'))
30SDK_OUTPUT_DIR = os.path.join(WEBRTC_SRC_DIR, 'out_ios_libs')
oprypin7a2d8ca2017-02-06 07:53:41 -080031SDK_LIB_NAME = 'librtc_sdk_objc.a'
32SDK_FRAMEWORK_NAME = 'WebRTC.framework'
33
oprypin9b0dbd42017-02-13 08:30:01 -080034DEFAULT_ARCHS = ENABLED_ARCHS = ['arm64', 'arm', 'x64', 'x86']
oprypin7a2d8ca2017-02-06 07:53:41 -080035IOS_DEPLOYMENT_TARGET = '8.0'
36LIBVPX_BUILD_VP9 = False
oprypin7a2d8ca2017-02-06 07:53:41 -080037
38
39def _ParseArgs():
40 parser = argparse.ArgumentParser(description=__doc__)
41 parser.add_argument('-b', '--build_type', default='framework',
42 choices=['framework', 'static_only'],
43 help='The build type. Can be "framework" or "static_only". '
44 'Defaults to "framework".')
45 parser.add_argument('--build_config', default='release',
46 choices=['debug', 'release'],
47 help='The build config. Can be "debug" or "release". '
48 'Defaults to "release".')
oprypin9b0dbd42017-02-13 08:30:01 -080049 parser.add_argument('--arch', nargs='+', default=DEFAULT_ARCHS,
50 choices=ENABLED_ARCHS,
51 help='Architectures to build. Defaults to %(default)s.')
oprypin7a2d8ca2017-02-06 07:53:41 -080052 parser.add_argument('-c', '--clean', action='store_true', default=False,
53 help='Removes the previously generated build output, if any.')
54 parser.add_argument('-o', '--output-dir', default=SDK_OUTPUT_DIR,
55 help='Specifies a directory to output the build artifacts to. '
56 'If specified together with -c, deletes the dir.')
57 parser.add_argument('-r', '--revision', type=int, default=0,
58 help='Specifies a revision number to embed if building the framework.')
59 parser.add_argument('-e', '--bitcode', action='store_true', default=False,
60 help='Compile with bitcode.')
61 parser.add_argument('--verbose', action='store_true', default=False,
62 help='Debug logging.')
mbonadei585209b2017-02-13 04:59:27 -080063 parser.add_argument('--use-goma', action='store_true', default=False,
64 help='Use goma to build.')
65 parser.add_argument('--extra-gn-args', default=[], nargs='*',
66 help='Additional GN args to be used during Ninja generation.')
67
oprypin7a2d8ca2017-02-06 07:53:41 -080068 return parser.parse_args()
69
70
71def _RunCommand(cmd):
72 logging.debug('Running: %r', cmd)
Henrik Kjellander368b5ff2017-02-15 20:51:26 +010073 subprocess.check_call(cmd, cwd=WEBRTC_SRC_DIR)
oprypin7a2d8ca2017-02-06 07:53:41 -080074
75
76def _CleanArtifacts(output_dir):
77 if os.path.isdir(output_dir):
78 logging.info('Deleting %s', output_dir)
79 shutil.rmtree(output_dir)
80
81
82def BuildWebRTC(output_dir, target_arch, flavor, build_type,
83 ios_deployment_target, libvpx_build_vp9, use_bitcode,
mbonadei585209b2017-02-13 04:59:27 -080084 use_goma, extra_gn_args):
oprypin7a2d8ca2017-02-06 07:53:41 -080085 output_dir = os.path.join(output_dir, target_arch + '_libs')
86 gn_args = ['target_os="ios"', 'ios_enable_code_signing=false',
87 'use_xcode_clang=true', 'is_component_build=false']
88
89 # Add flavor option.
90 if flavor == 'debug':
91 gn_args.append('is_debug=true')
92 elif flavor == 'release':
93 gn_args.append('is_debug=false')
94 else:
95 raise ValueError('Unexpected flavor type: %s' % flavor)
96
97 gn_args.append('target_cpu="%s"' % target_arch)
98
99 gn_args.append('ios_deployment_target="%s"' % ios_deployment_target)
100
101 gn_args.append('rtc_libvpx_build_vp9=' +
102 ('true' if libvpx_build_vp9 else 'false'))
103
104 gn_args.append('enable_ios_bitcode=' +
105 ('true' if use_bitcode else 'false'))
mbonadei585209b2017-02-13 04:59:27 -0800106 gn_args.append('use_goma=' + ('true' if use_goma else 'false'))
oprypin7a2d8ca2017-02-06 07:53:41 -0800107
108 # Generate static or dynamic.
109 if build_type == 'static_only':
110 gn_target_name = 'rtc_sdk_objc'
111 elif build_type == 'framework':
112 gn_target_name = 'rtc_sdk_framework_objc'
VladimirTechMand74517c2017-02-25 02:08:27 -0800113 if not use_bitcode:
114 gn_args.append('enable_dsyms=true')
oprypin7a2d8ca2017-02-06 07:53:41 -0800115 gn_args.append('enable_stripping=true')
116 else:
117 raise ValueError('Build type "%s" is not supported.' % build_type)
118
mbonadei7b2797e2017-02-16 01:40:59 -0800119 args_string = ' '.join(gn_args + extra_gn_args)
120 logging.info('Building WebRTC with args: %s', args_string)
mbonadei8714b8f2017-02-15 13:18:57 -0800121
mbonadei7b2797e2017-02-16 01:40:59 -0800122 cmd = ['gn', 'gen', output_dir, '--args=' + args_string]
oprypin7a2d8ca2017-02-06 07:53:41 -0800123 _RunCommand(cmd)
124 logging.info('Building target: %s', gn_target_name)
mbonadei8714b8f2017-02-15 13:18:57 -0800125
oprypin7a2d8ca2017-02-06 07:53:41 -0800126 cmd = ['ninja', '-C', output_dir, gn_target_name]
mbonadei8714b8f2017-02-15 13:18:57 -0800127 if use_goma:
128 cmd.extend(['-j', '200'])
oprypin7a2d8ca2017-02-06 07:53:41 -0800129 _RunCommand(cmd)
130
131 # Strip debug symbols to reduce size.
132 if build_type == 'static_only':
133 gn_target_path = os.path.join(output_dir, 'obj', 'webrtc', 'sdk',
134 'lib%s.a' % gn_target_name)
135 cmd = ['strip', '-S', gn_target_path, '-o',
136 os.path.join(output_dir, 'lib%s.a' % gn_target_name)]
137 _RunCommand(cmd)
138
139
140def main():
141 args = _ParseArgs()
142
143 logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO)
144
145 if args.clean:
146 _CleanArtifacts(args.output_dir)
147 return 0
148
oprypin9b0dbd42017-02-13 08:30:01 -0800149 architectures = list(args.arch)
150 # Ignoring x86 except for static libraries for now because of a GN build issue
151 # where the generated dynamic framework has the wrong architectures.
152 if 'x86' in architectures and args.build_type != 'static_only':
153 architectures.remove('x86')
154
oprypin7a2d8ca2017-02-06 07:53:41 -0800155 # Build all architectures.
oprypin9b0dbd42017-02-13 08:30:01 -0800156 for arch in architectures:
oprypin7a2d8ca2017-02-06 07:53:41 -0800157 BuildWebRTC(args.output_dir, arch, args.build_config, args.build_type,
158 IOS_DEPLOYMENT_TARGET, LIBVPX_BUILD_VP9, args.bitcode,
mbonadei585209b2017-02-13 04:59:27 -0800159 args.use_goma, args.extra_gn_args)
oprypin7a2d8ca2017-02-06 07:53:41 -0800160
oprypin7a2d8ca2017-02-06 07:53:41 -0800161 # Create FAT archive.
162 if args.build_type == 'static_only':
oprypin9b0dbd42017-02-13 08:30:01 -0800163 lib_paths = [os.path.join(args.output_dir, arch + '_libs', SDK_LIB_NAME)
164 for arch in architectures]
165 out_lib_path = os.path.join(args.output_dir, SDK_LIB_NAME)
oprypin7a2d8ca2017-02-06 07:53:41 -0800166 # Combine the slices.
oprypin9b0dbd42017-02-13 08:30:01 -0800167 cmd = ['lipo'] + lib_paths + ['-create', '-output', out_lib_path]
oprypin7a2d8ca2017-02-06 07:53:41 -0800168 _RunCommand(cmd)
169
170 elif args.build_type == 'framework':
oprypin9b0dbd42017-02-13 08:30:01 -0800171 lib_paths = [os.path.join(args.output_dir, arch + '_libs')
172 for arch in architectures]
oprypin7a2d8ca2017-02-06 07:53:41 -0800173
174 # Combine the slices.
175 dylib_path = os.path.join(SDK_FRAMEWORK_NAME, 'WebRTC')
oprypin9b0dbd42017-02-13 08:30:01 -0800176 # Dylibs will be combined, all other files are the same across archs.
oprypin7a2d8ca2017-02-06 07:53:41 -0800177 # Use distutils instead of shutil to support merging folders.
178 distutils.dir_util.copy_tree(
oprypin9b0dbd42017-02-13 08:30:01 -0800179 os.path.join(lib_paths[0], SDK_FRAMEWORK_NAME),
oprypin7a2d8ca2017-02-06 07:53:41 -0800180 os.path.join(args.output_dir, SDK_FRAMEWORK_NAME))
oprypin7a2d8ca2017-02-06 07:53:41 -0800181 logging.info('Merging framework slices.')
oprypin9b0dbd42017-02-13 08:30:01 -0800182 dylib_paths = [os.path.join(path, dylib_path) for path in lib_paths]
183 out_dylib_path = os.path.join(args.output_dir, dylib_path)
VladimirTechMand74517c2017-02-25 02:08:27 -0800184 try:
185 os.remove(out_dylib_path)
186 except OSError:
187 pass
oprypin9b0dbd42017-02-13 08:30:01 -0800188 cmd = ['lipo'] + dylib_paths + ['-create', '-output', out_dylib_path]
oprypin7a2d8ca2017-02-06 07:53:41 -0800189 _RunCommand(cmd)
190
191 # Merge the dSYM slices.
VladimirTechMand74517c2017-02-25 02:08:27 -0800192 lib_dsym_dir_path = os.path.join(lib_paths[0], 'WebRTC.dSYM')
193 if os.path.isdir(lib_dsym_dir_path):
194 distutils.dir_util.copy_tree(lib_dsym_dir_path,
195 os.path.join(args.output_dir, 'WebRTC.dSYM'))
196 logging.info('Merging dSYM slices.')
197 dsym_path = os.path.join('WebRTC.dSYM', 'Contents', 'Resources', 'DWARF',
198 'WebRTC')
199 lib_dsym_paths = [os.path.join(path, dsym_path) for path in lib_paths]
200 out_dsym_path = os.path.join(args.output_dir, dsym_path)
201 try:
202 os.remove(out_dsym_path)
203 except OSError:
204 pass
205 cmd = ['lipo'] + lib_dsym_paths + ['-create', '-output', out_dsym_path]
206 _RunCommand(cmd)
oprypin7a2d8ca2017-02-06 07:53:41 -0800207
208 # Modify the version number.
209 # Format should be <Branch cut MXX>.<Hotfix #>.<Rev #>.
210 # e.g. 55.0.14986 means branch cut 55, no hotfixes, and revision 14986.
211 infoplist_path = os.path.join(args.output_dir, SDK_FRAMEWORK_NAME,
212 'Info.plist')
213 cmd = ['PlistBuddy', '-c',
214 'Print :CFBundleShortVersionString', infoplist_path]
215 major_minor = subprocess.check_output(cmd).strip()
216 version_number = '%s.%s' % (major_minor, args.revision)
217 logging.info('Substituting revision number: %s', version_number)
218 cmd = ['PlistBuddy', '-c',
219 'Set :CFBundleVersion ' + version_number, infoplist_path]
220 _RunCommand(cmd)
221 _RunCommand(['plutil', '-convert', 'binary1', infoplist_path])
222
223 logging.info('Done.')
224 return 0
225
226
227if __name__ == '__main__':
228 sys.exit(main())