blob: 0081998ad301f7d1aee0bdb73c272d6bb9982fa3 [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
Kári Tristan Helgason1edbda02017-06-13 10:45:42 +020093def BuildWebRTC(output_dir, target_arch, flavor, gn_target_name,
oprypin7a2d8ca2017-02-06 07:53:41 -080094 ios_deployment_target, libvpx_build_vp9, use_bitcode,
Kári Tristan Helgason1edbda02017-06-13 10:45:42 +020095 use_goma, extra_gn_args, static_only):
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
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.
Kári Tristan Helgason1edbda02017-06-13 10:45:42 +0200132 if static_only:
oprypin7a2d8ca2017-02-06 07:53:41 -0800133 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)
Kári Tristan Helgason1edbda02017-06-13 10:45:42 +0200150 gn_args = args.extra_gn_args
VladimirTechMan7b188e82017-03-14 03:12:35 -0700151
152 if args.purify:
153 _CleanTemporary(args.output_dir, architectures)
154 return 0
155
oprypin9b0dbd42017-02-13 08:30:01 -0800156 # Ignoring x86 except for static libraries for now because of a GN build issue
157 # where the generated dynamic framework has the wrong architectures.
158 if 'x86' in architectures and args.build_type != 'static_only':
159 architectures.remove('x86')
160
Kári Tristan Helgason1edbda02017-06-13 10:45:42 +0200161 # Generate static or dynamic.
162 if args.build_type == 'static_only':
163 gn_target_name = 'rtc_sdk_objc'
164 elif args.build_type == 'framework':
165 gn_target_name = 'objc_framework'
166 if not args.bitcode:
167 gn_args.append('enable_dsyms=true')
168 gn_args.append('enable_stripping=true')
169 else:
170 raise ValueError('Build type "%s" is not supported.' % args.build_type)
171
172
oprypin7a2d8ca2017-02-06 07:53:41 -0800173 # Build all architectures.
oprypin9b0dbd42017-02-13 08:30:01 -0800174 for arch in architectures:
Kári Tristan Helgason1edbda02017-06-13 10:45:42 +0200175 BuildWebRTC(args.output_dir, arch, args.build_config, gn_target_name,
oprypin7a2d8ca2017-02-06 07:53:41 -0800176 IOS_DEPLOYMENT_TARGET, LIBVPX_BUILD_VP9, args.bitcode,
Kári Tristan Helgason1edbda02017-06-13 10:45:42 +0200177 args.use_goma, gn_args, args.build_type == 'static_only')
oprypin7a2d8ca2017-02-06 07:53:41 -0800178
oprypin7a2d8ca2017-02-06 07:53:41 -0800179 # Create FAT archive.
180 if args.build_type == 'static_only':
oprypin9b0dbd42017-02-13 08:30:01 -0800181 lib_paths = [os.path.join(args.output_dir, arch + '_libs', SDK_LIB_NAME)
182 for arch in architectures]
183 out_lib_path = os.path.join(args.output_dir, SDK_LIB_NAME)
oprypin7a2d8ca2017-02-06 07:53:41 -0800184 # Combine the slices.
oprypin9b0dbd42017-02-13 08:30:01 -0800185 cmd = ['lipo'] + lib_paths + ['-create', '-output', out_lib_path]
oprypin7a2d8ca2017-02-06 07:53:41 -0800186 _RunCommand(cmd)
187
188 elif args.build_type == 'framework':
oprypin9b0dbd42017-02-13 08:30:01 -0800189 lib_paths = [os.path.join(args.output_dir, arch + '_libs')
190 for arch in architectures]
oprypin7a2d8ca2017-02-06 07:53:41 -0800191
192 # Combine the slices.
193 dylib_path = os.path.join(SDK_FRAMEWORK_NAME, 'WebRTC')
oprypin9b0dbd42017-02-13 08:30:01 -0800194 # Dylibs will be combined, all other files are the same across archs.
oprypin7a2d8ca2017-02-06 07:53:41 -0800195 # Use distutils instead of shutil to support merging folders.
196 distutils.dir_util.copy_tree(
oprypin9b0dbd42017-02-13 08:30:01 -0800197 os.path.join(lib_paths[0], SDK_FRAMEWORK_NAME),
oprypin7a2d8ca2017-02-06 07:53:41 -0800198 os.path.join(args.output_dir, SDK_FRAMEWORK_NAME))
oprypin7a2d8ca2017-02-06 07:53:41 -0800199 logging.info('Merging framework slices.')
oprypin9b0dbd42017-02-13 08:30:01 -0800200 dylib_paths = [os.path.join(path, dylib_path) for path in lib_paths]
201 out_dylib_path = os.path.join(args.output_dir, dylib_path)
kthelgason28922442017-02-28 15:33:26 -0800202 try:
203 os.remove(out_dylib_path)
204 except OSError:
205 pass
oprypin9b0dbd42017-02-13 08:30:01 -0800206 cmd = ['lipo'] + dylib_paths + ['-create', '-output', out_dylib_path]
oprypin7a2d8ca2017-02-06 07:53:41 -0800207 _RunCommand(cmd)
208
209 # Merge the dSYM slices.
kthelgason28922442017-02-28 15:33:26 -0800210 lib_dsym_dir_path = os.path.join(lib_paths[0], 'WebRTC.dSYM')
211 if os.path.isdir(lib_dsym_dir_path):
212 distutils.dir_util.copy_tree(lib_dsym_dir_path,
213 os.path.join(args.output_dir, 'WebRTC.dSYM'))
214 logging.info('Merging dSYM slices.')
215 dsym_path = os.path.join('WebRTC.dSYM', 'Contents', 'Resources', 'DWARF',
216 'WebRTC')
217 lib_dsym_paths = [os.path.join(path, dsym_path) for path in lib_paths]
218 out_dsym_path = os.path.join(args.output_dir, dsym_path)
219 try:
220 os.remove(out_dsym_path)
221 except OSError:
222 pass
223 cmd = ['lipo'] + lib_dsym_paths + ['-create', '-output', out_dsym_path]
224 _RunCommand(cmd)
oprypin7a2d8ca2017-02-06 07:53:41 -0800225
kthelgason96feb2a2017-03-13 08:05:59 -0700226 # Generate the license file.
227 license_script_path = os.path.join(SCRIPT_DIR, 'generate_licenses.py')
228 ninja_dirs = [os.path.join(args.output_dir, arch + '_libs')
229 for arch in architectures]
Kári Tristan Helgason1edbda02017-06-13 10:45:42 +0200230 gn_target_full_name = '//webrtc/sdk:' + gn_target_name
kthelgason96feb2a2017-03-13 08:05:59 -0700231 cmd = [sys.executable, license_script_path, gn_target_full_name,
232 os.path.join(args.output_dir, SDK_FRAMEWORK_NAME)] + ninja_dirs
233 _RunCommand(cmd)
234
oprypin7a2d8ca2017-02-06 07:53:41 -0800235 # Modify the version number.
236 # Format should be <Branch cut MXX>.<Hotfix #>.<Rev #>.
237 # e.g. 55.0.14986 means branch cut 55, no hotfixes, and revision 14986.
238 infoplist_path = os.path.join(args.output_dir, SDK_FRAMEWORK_NAME,
239 'Info.plist')
240 cmd = ['PlistBuddy', '-c',
241 'Print :CFBundleShortVersionString', infoplist_path]
242 major_minor = subprocess.check_output(cmd).strip()
243 version_number = '%s.%s' % (major_minor, args.revision)
244 logging.info('Substituting revision number: %s', version_number)
245 cmd = ['PlistBuddy', '-c',
246 'Set :CFBundleVersion ' + version_number, infoplist_path]
247 _RunCommand(cmd)
248 _RunCommand(['plutil', '-convert', 'binary1', infoplist_path])
249
250 logging.info('Done.')
251 return 0
252
253
254if __name__ == '__main__':
255 sys.exit(main())