blob: 734f3e216d045b39ca51c28375e3510264adca6a [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.)
oprypin7a2d8ca2017-02-06 07:53:41 -080014"""
15
16import argparse
17import distutils.dir_util
18import logging
19import os
20import shutil
21import subprocess
22import sys
23
24
25os.environ['PATH'] = '/usr/libexec' + os.pathsep + os.environ['PATH']
26
27SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
Henrik Kjellander368b5ff2017-02-15 20:51:26 +010028WEBRTC_SRC_DIR = os.path.abspath(os.path.join(SCRIPT_DIR, '..', '..'))
29SDK_OUTPUT_DIR = os.path.join(WEBRTC_SRC_DIR, 'out_ios_libs')
oprypin7a2d8ca2017-02-06 07:53:41 -080030SDK_LIB_NAME = 'librtc_sdk_objc.a'
31SDK_FRAMEWORK_NAME = 'WebRTC.framework'
32
oprypin9b0dbd42017-02-13 08:30:01 -080033DEFAULT_ARCHS = ENABLED_ARCHS = ['arm64', 'arm', 'x64', 'x86']
oprypin7a2d8ca2017-02-06 07:53:41 -080034IOS_DEPLOYMENT_TARGET = '8.0'
35LIBVPX_BUILD_VP9 = False
oprypin7a2d8ca2017-02-06 07:53:41 -080036
37
38def _ParseArgs():
39 parser = argparse.ArgumentParser(description=__doc__)
40 parser.add_argument('-b', '--build_type', default='framework',
41 choices=['framework', 'static_only'],
42 help='The build type. Can be "framework" or "static_only". '
43 'Defaults to "framework".')
44 parser.add_argument('--build_config', default='release',
45 choices=['debug', 'release'],
46 help='The build config. Can be "debug" or "release". '
47 'Defaults to "release".')
oprypin9b0dbd42017-02-13 08:30:01 -080048 parser.add_argument('--arch', nargs='+', default=DEFAULT_ARCHS,
49 choices=ENABLED_ARCHS,
50 help='Architectures to build. Defaults to %(default)s.')
oprypin7a2d8ca2017-02-06 07:53:41 -080051 parser.add_argument('-c', '--clean', action='store_true', default=False,
52 help='Removes the previously generated build output, if any.')
VladimirTechMan7b188e82017-03-14 03:12:35 -070053 parser.add_argument('-p', '--purify', action='store_true', default=False,
54 help='Purifies the previously generated build output by '
55 'removing the temporary results used when (re)building.')
oprypin7a2d8ca2017-02-06 07:53:41 -080056 parser.add_argument('-o', '--output-dir', default=SDK_OUTPUT_DIR,
57 help='Specifies a directory to output the build artifacts to. '
58 'If specified together with -c, deletes the dir.')
59 parser.add_argument('-r', '--revision', type=int, default=0,
60 help='Specifies a revision number to embed if building the framework.')
61 parser.add_argument('-e', '--bitcode', action='store_true', default=False,
62 help='Compile with bitcode.')
63 parser.add_argument('--verbose', action='store_true', default=False,
64 help='Debug logging.')
mbonadei585209b2017-02-13 04:59:27 -080065 parser.add_argument('--use-goma', action='store_true', default=False,
66 help='Use goma to build.')
67 parser.add_argument('--extra-gn-args', default=[], nargs='*',
68 help='Additional GN args to be used during Ninja generation.')
69
oprypin7a2d8ca2017-02-06 07:53:41 -080070 return parser.parse_args()
71
72
73def _RunCommand(cmd):
74 logging.debug('Running: %r', cmd)
Henrik Kjellander368b5ff2017-02-15 20:51:26 +010075 subprocess.check_call(cmd, cwd=WEBRTC_SRC_DIR)
oprypin7a2d8ca2017-02-06 07:53:41 -080076
77
78def _CleanArtifacts(output_dir):
79 if os.path.isdir(output_dir):
80 logging.info('Deleting %s', output_dir)
81 shutil.rmtree(output_dir)
82
83
VladimirTechMan7b188e82017-03-14 03:12:35 -070084def _CleanTemporary(output_dir, architectures):
85 if os.path.isdir(output_dir):
86 logging.info('Removing temporary build files.')
87 for arch in architectures:
88 arch_lib_path = os.path.join(output_dir, arch + '_libs')
89 if os.path.isdir(arch_lib_path):
90 shutil.rmtree(arch_lib_path)
91
92
oprypin7a2d8ca2017-02-06 07:53:41 -080093def BuildWebRTC(output_dir, target_arch, flavor, build_type,
94 ios_deployment_target, libvpx_build_vp9, use_bitcode,
mbonadei585209b2017-02-13 04:59:27 -080095 use_goma, extra_gn_args):
oprypin7a2d8ca2017-02-06 07:53:41 -080096 output_dir = os.path.join(output_dir, target_arch + '_libs')
97 gn_args = ['target_os="ios"', 'ios_enable_code_signing=false',
98 'use_xcode_clang=true', 'is_component_build=false']
99
100 # Add flavor option.
101 if flavor == 'debug':
102 gn_args.append('is_debug=true')
103 elif flavor == 'release':
104 gn_args.append('is_debug=false')
105 else:
106 raise ValueError('Unexpected flavor type: %s' % flavor)
107
108 gn_args.append('target_cpu="%s"' % target_arch)
109
110 gn_args.append('ios_deployment_target="%s"' % ios_deployment_target)
111
112 gn_args.append('rtc_libvpx_build_vp9=' +
113 ('true' if libvpx_build_vp9 else 'false'))
114
115 gn_args.append('enable_ios_bitcode=' +
116 ('true' if use_bitcode else 'false'))
mbonadei585209b2017-02-13 04:59:27 -0800117 gn_args.append('use_goma=' + ('true' if use_goma else 'false'))
oprypin7a2d8ca2017-02-06 07:53:41 -0800118
119 # Generate static or dynamic.
120 if build_type == 'static_only':
121 gn_target_name = 'rtc_sdk_objc'
122 elif build_type == 'framework':
mbonadeiaf5c0552017-05-29 09:57:37 -0700123 gn_target_name = 'rtc_sdk_framework_objc'
kthelgason28922442017-02-28 15:33:26 -0800124 if not use_bitcode:
125 gn_args.append('enable_dsyms=true')
oprypin7a2d8ca2017-02-06 07:53:41 -0800126 gn_args.append('enable_stripping=true')
127 else:
128 raise ValueError('Build type "%s" is not supported.' % build_type)
129
mbonadei7b2797e2017-02-16 01:40:59 -0800130 args_string = ' '.join(gn_args + extra_gn_args)
131 logging.info('Building WebRTC with args: %s', args_string)
mbonadei8714b8f2017-02-15 13:18:57 -0800132
mbonadei7b2797e2017-02-16 01:40:59 -0800133 cmd = ['gn', 'gen', output_dir, '--args=' + args_string]
oprypin7a2d8ca2017-02-06 07:53:41 -0800134 _RunCommand(cmd)
135 logging.info('Building target: %s', gn_target_name)
mbonadei8714b8f2017-02-15 13:18:57 -0800136
oprypin7a2d8ca2017-02-06 07:53:41 -0800137 cmd = ['ninja', '-C', output_dir, gn_target_name]
mbonadei8714b8f2017-02-15 13:18:57 -0800138 if use_goma:
139 cmd.extend(['-j', '200'])
oprypin7a2d8ca2017-02-06 07:53:41 -0800140 _RunCommand(cmd)
141
142 # Strip debug symbols to reduce size.
143 if build_type == 'static_only':
144 gn_target_path = os.path.join(output_dir, 'obj', 'webrtc', 'sdk',
145 'lib%s.a' % gn_target_name)
146 cmd = ['strip', '-S', gn_target_path, '-o',
147 os.path.join(output_dir, 'lib%s.a' % gn_target_name)]
148 _RunCommand(cmd)
149
150
151def main():
152 args = _ParseArgs()
153
154 logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO)
155
156 if args.clean:
157 _CleanArtifacts(args.output_dir)
158 return 0
159
oprypin9b0dbd42017-02-13 08:30:01 -0800160 architectures = list(args.arch)
VladimirTechMan7b188e82017-03-14 03:12:35 -0700161
162 if args.purify:
163 _CleanTemporary(args.output_dir, architectures)
164 return 0
165
oprypin9b0dbd42017-02-13 08:30:01 -0800166 # Ignoring x86 except for static libraries for now because of a GN build issue
167 # where the generated dynamic framework has the wrong architectures.
168 if 'x86' in architectures and args.build_type != 'static_only':
169 architectures.remove('x86')
170
oprypin7a2d8ca2017-02-06 07:53:41 -0800171 # Build all architectures.
oprypin9b0dbd42017-02-13 08:30:01 -0800172 for arch in architectures:
oprypin7a2d8ca2017-02-06 07:53:41 -0800173 BuildWebRTC(args.output_dir, arch, args.build_config, args.build_type,
174 IOS_DEPLOYMENT_TARGET, LIBVPX_BUILD_VP9, args.bitcode,
mbonadei585209b2017-02-13 04:59:27 -0800175 args.use_goma, args.extra_gn_args)
oprypin7a2d8ca2017-02-06 07:53:41 -0800176
oprypin7a2d8ca2017-02-06 07:53:41 -0800177 # Create FAT archive.
178 if args.build_type == 'static_only':
oprypin9b0dbd42017-02-13 08:30:01 -0800179 lib_paths = [os.path.join(args.output_dir, arch + '_libs', SDK_LIB_NAME)
180 for arch in architectures]
181 out_lib_path = os.path.join(args.output_dir, SDK_LIB_NAME)
oprypin7a2d8ca2017-02-06 07:53:41 -0800182 # Combine the slices.
oprypin9b0dbd42017-02-13 08:30:01 -0800183 cmd = ['lipo'] + lib_paths + ['-create', '-output', out_lib_path]
oprypin7a2d8ca2017-02-06 07:53:41 -0800184 _RunCommand(cmd)
185
186 elif args.build_type == 'framework':
oprypin9b0dbd42017-02-13 08:30:01 -0800187 lib_paths = [os.path.join(args.output_dir, arch + '_libs')
188 for arch in architectures]
oprypin7a2d8ca2017-02-06 07:53:41 -0800189
190 # Combine the slices.
191 dylib_path = os.path.join(SDK_FRAMEWORK_NAME, 'WebRTC')
oprypin9b0dbd42017-02-13 08:30:01 -0800192 # Dylibs will be combined, all other files are the same across archs.
oprypin7a2d8ca2017-02-06 07:53:41 -0800193 # Use distutils instead of shutil to support merging folders.
194 distutils.dir_util.copy_tree(
oprypin9b0dbd42017-02-13 08:30:01 -0800195 os.path.join(lib_paths[0], SDK_FRAMEWORK_NAME),
oprypin7a2d8ca2017-02-06 07:53:41 -0800196 os.path.join(args.output_dir, SDK_FRAMEWORK_NAME))
oprypin7a2d8ca2017-02-06 07:53:41 -0800197 logging.info('Merging framework slices.')
oprypin9b0dbd42017-02-13 08:30:01 -0800198 dylib_paths = [os.path.join(path, dylib_path) for path in lib_paths]
199 out_dylib_path = os.path.join(args.output_dir, dylib_path)
kthelgason28922442017-02-28 15:33:26 -0800200 try:
201 os.remove(out_dylib_path)
202 except OSError:
203 pass
oprypin9b0dbd42017-02-13 08:30:01 -0800204 cmd = ['lipo'] + dylib_paths + ['-create', '-output', out_dylib_path]
oprypin7a2d8ca2017-02-06 07:53:41 -0800205 _RunCommand(cmd)
206
207 # Merge the dSYM slices.
kthelgason28922442017-02-28 15:33:26 -0800208 lib_dsym_dir_path = os.path.join(lib_paths[0], 'WebRTC.dSYM')
209 if os.path.isdir(lib_dsym_dir_path):
210 distutils.dir_util.copy_tree(lib_dsym_dir_path,
211 os.path.join(args.output_dir, 'WebRTC.dSYM'))
212 logging.info('Merging dSYM slices.')
213 dsym_path = os.path.join('WebRTC.dSYM', 'Contents', 'Resources', 'DWARF',
214 'WebRTC')
215 lib_dsym_paths = [os.path.join(path, dsym_path) for path in lib_paths]
216 out_dsym_path = os.path.join(args.output_dir, dsym_path)
217 try:
218 os.remove(out_dsym_path)
219 except OSError:
220 pass
221 cmd = ['lipo'] + lib_dsym_paths + ['-create', '-output', out_dsym_path]
222 _RunCommand(cmd)
oprypin7a2d8ca2017-02-06 07:53:41 -0800223
kthelgason96feb2a2017-03-13 08:05:59 -0700224 # Generate the license file.
225 license_script_path = os.path.join(SCRIPT_DIR, 'generate_licenses.py')
226 ninja_dirs = [os.path.join(args.output_dir, arch + '_libs')
227 for arch in architectures]
228 gn_target_full_name = '//webrtc/sdk:rtc_sdk_framework_objc'
229 cmd = [sys.executable, license_script_path, gn_target_full_name,
230 os.path.join(args.output_dir, SDK_FRAMEWORK_NAME)] + ninja_dirs
231 _RunCommand(cmd)
232
oprypin7a2d8ca2017-02-06 07:53:41 -0800233 # Modify the version number.
234 # Format should be <Branch cut MXX>.<Hotfix #>.<Rev #>.
235 # e.g. 55.0.14986 means branch cut 55, no hotfixes, and revision 14986.
236 infoplist_path = os.path.join(args.output_dir, SDK_FRAMEWORK_NAME,
237 'Info.plist')
238 cmd = ['PlistBuddy', '-c',
239 'Print :CFBundleShortVersionString', infoplist_path]
240 major_minor = subprocess.check_output(cmd).strip()
241 version_number = '%s.%s' % (major_minor, args.revision)
242 logging.info('Substituting revision number: %s', version_number)
243 cmd = ['PlistBuddy', '-c',
244 'Set :CFBundleVersion ' + version_number, infoplist_path]
245 _RunCommand(cmd)
246 _RunCommand(['plutil', '-convert', 'binary1', infoplist_path])
247
248 logging.info('Done.')
249 return 0
250
251
252if __name__ == '__main__':
253 sys.exit(main())