blob: d78384ae76c33bb15899c63b5f78b29117f96737 [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
sakal67e414c2017-09-05 00:16:15 -070037sys.path.append(os.path.join(SCRIPT_DIR, '..', 'libs'))
38from generate_licenses import LicenseBuilder
39
oprypin7a2d8ca2017-02-06 07:53:41 -080040
41def _ParseArgs():
42 parser = argparse.ArgumentParser(description=__doc__)
43 parser.add_argument('-b', '--build_type', default='framework',
44 choices=['framework', 'static_only'],
45 help='The build type. Can be "framework" or "static_only". '
46 'Defaults to "framework".')
47 parser.add_argument('--build_config', default='release',
48 choices=['debug', 'release'],
49 help='The build config. Can be "debug" or "release". '
50 'Defaults to "release".')
oprypin9b0dbd42017-02-13 08:30:01 -080051 parser.add_argument('--arch', nargs='+', default=DEFAULT_ARCHS,
52 choices=ENABLED_ARCHS,
53 help='Architectures to build. Defaults to %(default)s.')
oprypin7a2d8ca2017-02-06 07:53:41 -080054 parser.add_argument('-c', '--clean', action='store_true', default=False,
55 help='Removes the previously generated build output, if any.')
VladimirTechMan7b188e82017-03-14 03:12:35 -070056 parser.add_argument('-p', '--purify', action='store_true', default=False,
57 help='Purifies the previously generated build output by '
58 'removing the temporary results used when (re)building.')
oprypin7a2d8ca2017-02-06 07:53:41 -080059 parser.add_argument('-o', '--output-dir', default=SDK_OUTPUT_DIR,
60 help='Specifies a directory to output the build artifacts to. '
61 'If specified together with -c, deletes the dir.')
62 parser.add_argument('-r', '--revision', type=int, default=0,
63 help='Specifies a revision number to embed if building the framework.')
64 parser.add_argument('-e', '--bitcode', action='store_true', default=False,
65 help='Compile with bitcode.')
66 parser.add_argument('--verbose', action='store_true', default=False,
67 help='Debug logging.')
mbonadei585209b2017-02-13 04:59:27 -080068 parser.add_argument('--use-goma', action='store_true', default=False,
69 help='Use goma to build.')
70 parser.add_argument('--extra-gn-args', default=[], nargs='*',
71 help='Additional GN args to be used during Ninja generation.')
72
oprypin7a2d8ca2017-02-06 07:53:41 -080073 return parser.parse_args()
74
75
76def _RunCommand(cmd):
77 logging.debug('Running: %r', cmd)
Henrik Kjellander368b5ff2017-02-15 20:51:26 +010078 subprocess.check_call(cmd, cwd=WEBRTC_SRC_DIR)
oprypin7a2d8ca2017-02-06 07:53:41 -080079
80
81def _CleanArtifacts(output_dir):
82 if os.path.isdir(output_dir):
83 logging.info('Deleting %s', output_dir)
84 shutil.rmtree(output_dir)
85
86
VladimirTechMan7b188e82017-03-14 03:12:35 -070087def _CleanTemporary(output_dir, architectures):
88 if os.path.isdir(output_dir):
89 logging.info('Removing temporary build files.')
90 for arch in architectures:
91 arch_lib_path = os.path.join(output_dir, arch + '_libs')
92 if os.path.isdir(arch_lib_path):
93 shutil.rmtree(arch_lib_path)
94
95
Kári Tristan Helgason1edbda02017-06-13 10:45:42 +020096def BuildWebRTC(output_dir, target_arch, flavor, gn_target_name,
oprypin7a2d8ca2017-02-06 07:53:41 -080097 ios_deployment_target, libvpx_build_vp9, use_bitcode,
Kári Tristan Helgason1edbda02017-06-13 10:45:42 +020098 use_goma, extra_gn_args, static_only):
oprypin7a2d8ca2017-02-06 07:53:41 -080099 output_dir = os.path.join(output_dir, target_arch + '_libs')
100 gn_args = ['target_os="ios"', 'ios_enable_code_signing=false',
101 'use_xcode_clang=true', 'is_component_build=false']
102
103 # Add flavor option.
104 if flavor == 'debug':
105 gn_args.append('is_debug=true')
106 elif flavor == 'release':
107 gn_args.append('is_debug=false')
108 else:
109 raise ValueError('Unexpected flavor type: %s' % flavor)
110
111 gn_args.append('target_cpu="%s"' % target_arch)
112
113 gn_args.append('ios_deployment_target="%s"' % ios_deployment_target)
114
115 gn_args.append('rtc_libvpx_build_vp9=' +
116 ('true' if libvpx_build_vp9 else 'false'))
117
118 gn_args.append('enable_ios_bitcode=' +
119 ('true' if use_bitcode else 'false'))
mbonadei585209b2017-02-13 04:59:27 -0800120 gn_args.append('use_goma=' + ('true' if use_goma else 'false'))
oprypin7a2d8ca2017-02-06 07:53:41 -0800121
mbonadei7b2797e2017-02-16 01:40:59 -0800122 args_string = ' '.join(gn_args + extra_gn_args)
123 logging.info('Building WebRTC with args: %s', args_string)
mbonadei8714b8f2017-02-15 13:18:57 -0800124
mbonadei7b2797e2017-02-16 01:40:59 -0800125 cmd = ['gn', 'gen', output_dir, '--args=' + args_string]
oprypin7a2d8ca2017-02-06 07:53:41 -0800126 _RunCommand(cmd)
127 logging.info('Building target: %s', gn_target_name)
mbonadei8714b8f2017-02-15 13:18:57 -0800128
oprypin7a2d8ca2017-02-06 07:53:41 -0800129 cmd = ['ninja', '-C', output_dir, gn_target_name]
mbonadei8714b8f2017-02-15 13:18:57 -0800130 if use_goma:
131 cmd.extend(['-j', '200'])
oprypin7a2d8ca2017-02-06 07:53:41 -0800132 _RunCommand(cmd)
133
134 # Strip debug symbols to reduce size.
Kári Tristan Helgason1edbda02017-06-13 10:45:42 +0200135 if static_only:
oprypin7a2d8ca2017-02-06 07:53:41 -0800136 gn_target_path = os.path.join(output_dir, 'obj', 'webrtc', 'sdk',
137 'lib%s.a' % gn_target_name)
138 cmd = ['strip', '-S', gn_target_path, '-o',
139 os.path.join(output_dir, 'lib%s.a' % gn_target_name)]
140 _RunCommand(cmd)
141
142
143def main():
144 args = _ParseArgs()
145
146 logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO)
147
148 if args.clean:
149 _CleanArtifacts(args.output_dir)
150 return 0
151
oprypin9b0dbd42017-02-13 08:30:01 -0800152 architectures = list(args.arch)
Kári Tristan Helgason1edbda02017-06-13 10:45:42 +0200153 gn_args = args.extra_gn_args
VladimirTechMan7b188e82017-03-14 03:12:35 -0700154
155 if args.purify:
156 _CleanTemporary(args.output_dir, architectures)
157 return 0
158
oprypin9b0dbd42017-02-13 08:30:01 -0800159 # Ignoring x86 except for static libraries for now because of a GN build issue
160 # where the generated dynamic framework has the wrong architectures.
161 if 'x86' in architectures and args.build_type != 'static_only':
162 architectures.remove('x86')
163
Kári Tristan Helgason1edbda02017-06-13 10:45:42 +0200164 # Generate static or dynamic.
165 if args.build_type == 'static_only':
166 gn_target_name = 'rtc_sdk_objc'
167 elif args.build_type == 'framework':
kthelgason72ba7952017-08-24 12:01:24 -0700168 gn_target_name = 'framework_objc'
Kári Tristan Helgason1edbda02017-06-13 10:45:42 +0200169 if not args.bitcode:
170 gn_args.append('enable_dsyms=true')
171 gn_args.append('enable_stripping=true')
172 else:
173 raise ValueError('Build type "%s" is not supported.' % args.build_type)
174
175
oprypin7a2d8ca2017-02-06 07:53:41 -0800176 # Build all architectures.
oprypin9b0dbd42017-02-13 08:30:01 -0800177 for arch in architectures:
Kári Tristan Helgason1edbda02017-06-13 10:45:42 +0200178 BuildWebRTC(args.output_dir, arch, args.build_config, gn_target_name,
oprypin7a2d8ca2017-02-06 07:53:41 -0800179 IOS_DEPLOYMENT_TARGET, LIBVPX_BUILD_VP9, args.bitcode,
Kári Tristan Helgason1edbda02017-06-13 10:45:42 +0200180 args.use_goma, gn_args, args.build_type == 'static_only')
oprypin7a2d8ca2017-02-06 07:53:41 -0800181
oprypin7a2d8ca2017-02-06 07:53:41 -0800182 # Create FAT archive.
183 if args.build_type == 'static_only':
oprypin9b0dbd42017-02-13 08:30:01 -0800184 lib_paths = [os.path.join(args.output_dir, arch + '_libs', SDK_LIB_NAME)
185 for arch in architectures]
186 out_lib_path = os.path.join(args.output_dir, SDK_LIB_NAME)
oprypin7a2d8ca2017-02-06 07:53:41 -0800187 # Combine the slices.
oprypin9b0dbd42017-02-13 08:30:01 -0800188 cmd = ['lipo'] + lib_paths + ['-create', '-output', out_lib_path]
oprypin7a2d8ca2017-02-06 07:53:41 -0800189 _RunCommand(cmd)
190
191 elif args.build_type == 'framework':
oprypin9b0dbd42017-02-13 08:30:01 -0800192 lib_paths = [os.path.join(args.output_dir, arch + '_libs')
193 for arch in architectures]
oprypin7a2d8ca2017-02-06 07:53:41 -0800194
195 # Combine the slices.
196 dylib_path = os.path.join(SDK_FRAMEWORK_NAME, 'WebRTC')
oprypin9b0dbd42017-02-13 08:30:01 -0800197 # Dylibs will be combined, all other files are the same across archs.
oprypin7a2d8ca2017-02-06 07:53:41 -0800198 # Use distutils instead of shutil to support merging folders.
199 distutils.dir_util.copy_tree(
oprypin9b0dbd42017-02-13 08:30:01 -0800200 os.path.join(lib_paths[0], SDK_FRAMEWORK_NAME),
oprypin7a2d8ca2017-02-06 07:53:41 -0800201 os.path.join(args.output_dir, SDK_FRAMEWORK_NAME))
oprypin7a2d8ca2017-02-06 07:53:41 -0800202 logging.info('Merging framework slices.')
oprypin9b0dbd42017-02-13 08:30:01 -0800203 dylib_paths = [os.path.join(path, dylib_path) for path in lib_paths]
204 out_dylib_path = os.path.join(args.output_dir, dylib_path)
kthelgason28922442017-02-28 15:33:26 -0800205 try:
206 os.remove(out_dylib_path)
207 except OSError:
208 pass
oprypin9b0dbd42017-02-13 08:30:01 -0800209 cmd = ['lipo'] + dylib_paths + ['-create', '-output', out_dylib_path]
oprypin7a2d8ca2017-02-06 07:53:41 -0800210 _RunCommand(cmd)
211
212 # Merge the dSYM slices.
kthelgason28922442017-02-28 15:33:26 -0800213 lib_dsym_dir_path = os.path.join(lib_paths[0], 'WebRTC.dSYM')
214 if os.path.isdir(lib_dsym_dir_path):
215 distutils.dir_util.copy_tree(lib_dsym_dir_path,
216 os.path.join(args.output_dir, 'WebRTC.dSYM'))
217 logging.info('Merging dSYM slices.')
218 dsym_path = os.path.join('WebRTC.dSYM', 'Contents', 'Resources', 'DWARF',
219 'WebRTC')
220 lib_dsym_paths = [os.path.join(path, dsym_path) for path in lib_paths]
221 out_dsym_path = os.path.join(args.output_dir, dsym_path)
222 try:
223 os.remove(out_dsym_path)
224 except OSError:
225 pass
226 cmd = ['lipo'] + lib_dsym_paths + ['-create', '-output', out_dsym_path]
227 _RunCommand(cmd)
oprypin7a2d8ca2017-02-06 07:53:41 -0800228
kthelgason96feb2a2017-03-13 08:05:59 -0700229 # Generate the license file.
kthelgason96feb2a2017-03-13 08:05:59 -0700230 ninja_dirs = [os.path.join(args.output_dir, arch + '_libs')
231 for arch in architectures]
Kári Tristan Helgason1edbda02017-06-13 10:45:42 +0200232 gn_target_full_name = '//webrtc/sdk:' + gn_target_name
sakal67e414c2017-09-05 00:16:15 -0700233 builder = LicenseBuilder(ninja_dirs, [gn_target_full_name])
234 builder.GenerateLicenseText(
235 os.path.join(args.output_dir, SDK_FRAMEWORK_NAME))
236
kthelgason96feb2a2017-03-13 08:05:59 -0700237
oprypin7a2d8ca2017-02-06 07:53:41 -0800238 # Modify the version number.
239 # Format should be <Branch cut MXX>.<Hotfix #>.<Rev #>.
240 # e.g. 55.0.14986 means branch cut 55, no hotfixes, and revision 14986.
241 infoplist_path = os.path.join(args.output_dir, SDK_FRAMEWORK_NAME,
242 'Info.plist')
243 cmd = ['PlistBuddy', '-c',
244 'Print :CFBundleShortVersionString', infoplist_path]
245 major_minor = subprocess.check_output(cmd).strip()
246 version_number = '%s.%s' % (major_minor, args.revision)
247 logging.info('Substituting revision number: %s', version_number)
248 cmd = ['PlistBuddy', '-c',
249 'Set :CFBundleVersion ' + version_number, infoplist_path]
250 _RunCommand(cmd)
251 _RunCommand(['plutil', '-convert', 'binary1', infoplist_path])
252
253 logging.info('Done.')
254 return 0
255
256
257if __name__ == '__main__':
258 sys.exit(main())