blob: b48304e0322ba3d5926dc023af8b1f0c67b544a3 [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 Kjellanderec57e052017-10-17 21:36:01 +020028SRC_DIR = os.path.abspath(os.path.join(SCRIPT_DIR, '..', '..'))
29sys.path.append(os.path.join(SRC_DIR, 'build'))
30import find_depot_tools
31
32SDK_OUTPUT_DIR = os.path.join(SRC_DIR, 'out_ios_libs')
oprypin7a2d8ca2017-02-06 07:53:41 -080033SDK_LIB_NAME = 'librtc_sdk_objc.a'
34SDK_FRAMEWORK_NAME = 'WebRTC.framework'
35
oprypin9b0dbd42017-02-13 08:30:01 -080036DEFAULT_ARCHS = ENABLED_ARCHS = ['arm64', 'arm', 'x64', 'x86']
Oleh Prypin32a11812017-10-05 11:11:42 +020037IOS_DEPLOYMENT_TARGET = '9.0'
oprypin7a2d8ca2017-02-06 07:53:41 -080038LIBVPX_BUILD_VP9 = False
oprypin7a2d8ca2017-02-06 07:53:41 -080039
sakal67e414c2017-09-05 00:16:15 -070040sys.path.append(os.path.join(SCRIPT_DIR, '..', 'libs'))
41from generate_licenses import LicenseBuilder
42
oprypin7a2d8ca2017-02-06 07:53:41 -080043
44def _ParseArgs():
45 parser = argparse.ArgumentParser(description=__doc__)
46 parser.add_argument('-b', '--build_type', default='framework',
47 choices=['framework', 'static_only'],
48 help='The build type. Can be "framework" or "static_only". '
49 'Defaults to "framework".')
50 parser.add_argument('--build_config', default='release',
51 choices=['debug', 'release'],
52 help='The build config. Can be "debug" or "release". '
53 'Defaults to "release".')
oprypin9b0dbd42017-02-13 08:30:01 -080054 parser.add_argument('--arch', nargs='+', default=DEFAULT_ARCHS,
55 choices=ENABLED_ARCHS,
56 help='Architectures to build. Defaults to %(default)s.')
oprypin7a2d8ca2017-02-06 07:53:41 -080057 parser.add_argument('-c', '--clean', action='store_true', default=False,
58 help='Removes the previously generated build output, if any.')
VladimirTechMan7b188e82017-03-14 03:12:35 -070059 parser.add_argument('-p', '--purify', action='store_true', default=False,
60 help='Purifies the previously generated build output by '
61 'removing the temporary results used when (re)building.')
oprypin7a2d8ca2017-02-06 07:53:41 -080062 parser.add_argument('-o', '--output-dir', default=SDK_OUTPUT_DIR,
63 help='Specifies a directory to output the build artifacts to. '
64 'If specified together with -c, deletes the dir.')
65 parser.add_argument('-r', '--revision', type=int, default=0,
66 help='Specifies a revision number to embed if building the framework.')
67 parser.add_argument('-e', '--bitcode', action='store_true', default=False,
68 help='Compile with bitcode.')
69 parser.add_argument('--verbose', action='store_true', default=False,
70 help='Debug logging.')
mbonadei585209b2017-02-13 04:59:27 -080071 parser.add_argument('--use-goma', action='store_true', default=False,
72 help='Use goma to build.')
73 parser.add_argument('--extra-gn-args', default=[], nargs='*',
74 help='Additional GN args to be used during Ninja generation.')
75
oprypin7a2d8ca2017-02-06 07:53:41 -080076 return parser.parse_args()
77
78
79def _RunCommand(cmd):
80 logging.debug('Running: %r', cmd)
Henrik Kjellanderec57e052017-10-17 21:36:01 +020081 subprocess.check_call(cmd, cwd=SRC_DIR)
oprypin7a2d8ca2017-02-06 07:53:41 -080082
83
84def _CleanArtifacts(output_dir):
85 if os.path.isdir(output_dir):
86 logging.info('Deleting %s', output_dir)
87 shutil.rmtree(output_dir)
88
89
VladimirTechMan7b188e82017-03-14 03:12:35 -070090def _CleanTemporary(output_dir, architectures):
91 if os.path.isdir(output_dir):
92 logging.info('Removing temporary build files.')
93 for arch in architectures:
94 arch_lib_path = os.path.join(output_dir, arch + '_libs')
95 if os.path.isdir(arch_lib_path):
96 shutil.rmtree(arch_lib_path)
97
98
Kári Tristan Helgason1edbda02017-06-13 10:45:42 +020099def BuildWebRTC(output_dir, target_arch, flavor, gn_target_name,
oprypin7a2d8ca2017-02-06 07:53:41 -0800100 ios_deployment_target, libvpx_build_vp9, use_bitcode,
Kári Tristan Helgason1edbda02017-06-13 10:45:42 +0200101 use_goma, extra_gn_args, static_only):
oprypin7a2d8ca2017-02-06 07:53:41 -0800102 output_dir = os.path.join(output_dir, target_arch + '_libs')
103 gn_args = ['target_os="ios"', 'ios_enable_code_signing=false',
104 'use_xcode_clang=true', 'is_component_build=false']
105
106 # Add flavor option.
107 if flavor == 'debug':
108 gn_args.append('is_debug=true')
109 elif flavor == 'release':
110 gn_args.append('is_debug=false')
111 else:
112 raise ValueError('Unexpected flavor type: %s' % flavor)
113
114 gn_args.append('target_cpu="%s"' % target_arch)
115
116 gn_args.append('ios_deployment_target="%s"' % ios_deployment_target)
117
118 gn_args.append('rtc_libvpx_build_vp9=' +
119 ('true' if libvpx_build_vp9 else 'false'))
120
121 gn_args.append('enable_ios_bitcode=' +
122 ('true' if use_bitcode else 'false'))
mbonadei585209b2017-02-13 04:59:27 -0800123 gn_args.append('use_goma=' + ('true' if use_goma else 'false'))
oprypin7a2d8ca2017-02-06 07:53:41 -0800124
mbonadei7b2797e2017-02-16 01:40:59 -0800125 args_string = ' '.join(gn_args + extra_gn_args)
126 logging.info('Building WebRTC with args: %s', args_string)
mbonadei8714b8f2017-02-15 13:18:57 -0800127
Henrik Kjellanderec57e052017-10-17 21:36:01 +0200128 cmd = [
129 sys.executable,
130 os.path.join(find_depot_tools.DEPOT_TOOLS_PATH, 'gn.py'),
131 'gen',
132 output_dir,
133 '--args=' + args_string,
134 ]
oprypin7a2d8ca2017-02-06 07:53:41 -0800135 _RunCommand(cmd)
136 logging.info('Building target: %s', gn_target_name)
mbonadei8714b8f2017-02-15 13:18:57 -0800137
Henrik Kjellanderec57e052017-10-17 21:36:01 +0200138 cmd = [
139 os.path.join(find_depot_tools.DEPOT_TOOLS_PATH, 'ninja'),
140 '-C',
141 output_dir,
142 gn_target_name,
143 ]
mbonadei8714b8f2017-02-15 13:18:57 -0800144 if use_goma:
145 cmd.extend(['-j', '200'])
oprypin7a2d8ca2017-02-06 07:53:41 -0800146 _RunCommand(cmd)
147
148 # Strip debug symbols to reduce size.
Kári Tristan Helgason1edbda02017-06-13 10:45:42 +0200149 if static_only:
Henrik Kjellander0e8f0532017-09-15 08:57:50 +0200150 gn_target_path = os.path.join(output_dir, 'obj', 'sdk',
oprypin7a2d8ca2017-02-06 07:53:41 -0800151 'lib%s.a' % gn_target_name)
152 cmd = ['strip', '-S', gn_target_path, '-o',
153 os.path.join(output_dir, 'lib%s.a' % gn_target_name)]
154 _RunCommand(cmd)
155
156
157def main():
158 args = _ParseArgs()
159
160 logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO)
161
162 if args.clean:
163 _CleanArtifacts(args.output_dir)
164 return 0
165
oprypin9b0dbd42017-02-13 08:30:01 -0800166 architectures = list(args.arch)
Kári Tristan Helgason1edbda02017-06-13 10:45:42 +0200167 gn_args = args.extra_gn_args
VladimirTechMan7b188e82017-03-14 03:12:35 -0700168
169 if args.purify:
170 _CleanTemporary(args.output_dir, architectures)
171 return 0
172
oprypin9b0dbd42017-02-13 08:30:01 -0800173 # Ignoring x86 except for static libraries for now because of a GN build issue
174 # where the generated dynamic framework has the wrong architectures.
175 if 'x86' in architectures and args.build_type != 'static_only':
176 architectures.remove('x86')
177
Kári Tristan Helgason1edbda02017-06-13 10:45:42 +0200178 # Generate static or dynamic.
179 if args.build_type == 'static_only':
180 gn_target_name = 'rtc_sdk_objc'
181 elif args.build_type == 'framework':
kthelgason72ba7952017-08-24 12:01:24 -0700182 gn_target_name = 'framework_objc'
Kári Tristan Helgason1edbda02017-06-13 10:45:42 +0200183 if not args.bitcode:
184 gn_args.append('enable_dsyms=true')
185 gn_args.append('enable_stripping=true')
186 else:
187 raise ValueError('Build type "%s" is not supported.' % args.build_type)
188
189
oprypin7a2d8ca2017-02-06 07:53:41 -0800190 # Build all architectures.
oprypin9b0dbd42017-02-13 08:30:01 -0800191 for arch in architectures:
Kári Tristan Helgason1edbda02017-06-13 10:45:42 +0200192 BuildWebRTC(args.output_dir, arch, args.build_config, gn_target_name,
oprypin7a2d8ca2017-02-06 07:53:41 -0800193 IOS_DEPLOYMENT_TARGET, LIBVPX_BUILD_VP9, args.bitcode,
Kári Tristan Helgason1edbda02017-06-13 10:45:42 +0200194 args.use_goma, gn_args, args.build_type == 'static_only')
oprypin7a2d8ca2017-02-06 07:53:41 -0800195
oprypin7a2d8ca2017-02-06 07:53:41 -0800196 # Create FAT archive.
197 if args.build_type == 'static_only':
oprypin9b0dbd42017-02-13 08:30:01 -0800198 lib_paths = [os.path.join(args.output_dir, arch + '_libs', SDK_LIB_NAME)
199 for arch in architectures]
200 out_lib_path = os.path.join(args.output_dir, SDK_LIB_NAME)
oprypin7a2d8ca2017-02-06 07:53:41 -0800201 # Combine the slices.
oprypin9b0dbd42017-02-13 08:30:01 -0800202 cmd = ['lipo'] + lib_paths + ['-create', '-output', out_lib_path]
oprypin7a2d8ca2017-02-06 07:53:41 -0800203 _RunCommand(cmd)
204
205 elif args.build_type == 'framework':
oprypin9b0dbd42017-02-13 08:30:01 -0800206 lib_paths = [os.path.join(args.output_dir, arch + '_libs')
207 for arch in architectures]
oprypin7a2d8ca2017-02-06 07:53:41 -0800208
209 # Combine the slices.
210 dylib_path = os.path.join(SDK_FRAMEWORK_NAME, 'WebRTC')
oprypin9b0dbd42017-02-13 08:30:01 -0800211 # Dylibs will be combined, all other files are the same across archs.
oprypin7a2d8ca2017-02-06 07:53:41 -0800212 # Use distutils instead of shutil to support merging folders.
213 distutils.dir_util.copy_tree(
oprypin9b0dbd42017-02-13 08:30:01 -0800214 os.path.join(lib_paths[0], SDK_FRAMEWORK_NAME),
oprypin7a2d8ca2017-02-06 07:53:41 -0800215 os.path.join(args.output_dir, SDK_FRAMEWORK_NAME))
oprypin7a2d8ca2017-02-06 07:53:41 -0800216 logging.info('Merging framework slices.')
oprypin9b0dbd42017-02-13 08:30:01 -0800217 dylib_paths = [os.path.join(path, dylib_path) for path in lib_paths]
218 out_dylib_path = os.path.join(args.output_dir, dylib_path)
kthelgason28922442017-02-28 15:33:26 -0800219 try:
220 os.remove(out_dylib_path)
221 except OSError:
222 pass
oprypin9b0dbd42017-02-13 08:30:01 -0800223 cmd = ['lipo'] + dylib_paths + ['-create', '-output', out_dylib_path]
oprypin7a2d8ca2017-02-06 07:53:41 -0800224 _RunCommand(cmd)
225
226 # Merge the dSYM slices.
kthelgason28922442017-02-28 15:33:26 -0800227 lib_dsym_dir_path = os.path.join(lib_paths[0], 'WebRTC.dSYM')
228 if os.path.isdir(lib_dsym_dir_path):
229 distutils.dir_util.copy_tree(lib_dsym_dir_path,
230 os.path.join(args.output_dir, 'WebRTC.dSYM'))
231 logging.info('Merging dSYM slices.')
232 dsym_path = os.path.join('WebRTC.dSYM', 'Contents', 'Resources', 'DWARF',
233 'WebRTC')
234 lib_dsym_paths = [os.path.join(path, dsym_path) for path in lib_paths]
235 out_dsym_path = os.path.join(args.output_dir, dsym_path)
236 try:
237 os.remove(out_dsym_path)
238 except OSError:
239 pass
240 cmd = ['lipo'] + lib_dsym_paths + ['-create', '-output', out_dsym_path]
241 _RunCommand(cmd)
oprypin7a2d8ca2017-02-06 07:53:41 -0800242
kthelgason96feb2a2017-03-13 08:05:59 -0700243 # Generate the license file.
kthelgason96feb2a2017-03-13 08:05:59 -0700244 ninja_dirs = [os.path.join(args.output_dir, arch + '_libs')
245 for arch in architectures]
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200246 gn_target_full_name = '//sdk:' + gn_target_name
sakal67e414c2017-09-05 00:16:15 -0700247 builder = LicenseBuilder(ninja_dirs, [gn_target_full_name])
248 builder.GenerateLicenseText(
249 os.path.join(args.output_dir, SDK_FRAMEWORK_NAME))
250
kthelgason96feb2a2017-03-13 08:05:59 -0700251
oprypin7a2d8ca2017-02-06 07:53:41 -0800252 # Modify the version number.
253 # Format should be <Branch cut MXX>.<Hotfix #>.<Rev #>.
254 # e.g. 55.0.14986 means branch cut 55, no hotfixes, and revision 14986.
255 infoplist_path = os.path.join(args.output_dir, SDK_FRAMEWORK_NAME,
256 'Info.plist')
257 cmd = ['PlistBuddy', '-c',
258 'Print :CFBundleShortVersionString', infoplist_path]
259 major_minor = subprocess.check_output(cmd).strip()
260 version_number = '%s.%s' % (major_minor, args.revision)
261 logging.info('Substituting revision number: %s', version_number)
262 cmd = ['PlistBuddy', '-c',
263 'Set :CFBundleVersion ' + version_number, infoplist_path]
264 _RunCommand(cmd)
265 _RunCommand(['plutil', '-convert', 'binary1', infoplist_path])
266
267 logging.info('Done.')
268 return 0
269
270
271if __name__ == '__main__':
272 sys.exit(main())