#!/usr/bin/env python3
# Copyright 2017 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""
Builds and packages ChromeWebView.framework.
"""
import argparse
import os
import platform
import shutil
import sys
def target_dir_name(build_config, target_device):
"""Returns a default output directory name string.
Args:
build_config: A string describing the build configuration. Ex: 'Debug'
target_device: A string describing the target device. Ex: 'simulator'
"""
return '%s-%s' % (build_config, target_device)
def build(build_config, target_device, extra_gn_options, extra_ninja_options):
"""Generates and builds ChromeWebView.framework.
Args:
build_config: A string describing the build configuration. Ex: 'Debug'
target_device: A string describing the target device. Ex: 'simulator'
extra_gn_options: A list of strings of gn args (key=value items) to
be appended to the gn gen command.
extra_ninja_options: A string of gn options to be appended to the ninja
command.
Returns:
The return code of generating ninja if it is non-zero, else the return code
of the ninja build command.
"""
gn_args = [
'target_os="ios"',
'enable_websockets=false',
'is_component_build=false',
'disable_file_support=true',
'disable_brotli_filter=true',
'ios_enable_code_signing=false',
'enable_dsyms=true',
]
if target_device == 'iphoneos':
gn_args.extend([
'target_cpu="arm64"',
'target_environment="device"',
])
else:
target_cpu = {'x86_64': 'x64', 'arm64': 'arm64'}[platform.machine()]
gn_args.extend([
'target_cpu="%s"' % target_cpu,
'target_environment="simulator"',
])
if build_config == 'Debug':
gn_args.append('is_debug=true')
else:
gn_args.extend([
'is_debug=false',
'enable_stripping=true',
'is_official_build=true',
])
if extra_gn_options:
gn_args.extend(extra_gn_options)
build_dir = os.path.join("out", target_dir_name(build_config, target_device))
gn_command = 'gn gen %s --args=\'%s\'' % (build_dir, ' '.join(gn_args))
print(gn_command)
gn_result = os.system(gn_command)
if gn_result != 0:
return gn_result
ninja_options = '-C %s' % build_dir
if extra_ninja_options:
ninja_options += ' %s' % extra_ninja_options
ninja_command = ('ninja %s ios/web_view:ios_web_view_package' %
ninja_options)
print(ninja_command)
return os.system(ninja_command)
def copy_build_products(build_config, target_device, out_dir, output_name):
"""Copies the resulting framework and symbols to out_dir.
Args:
build_config: A string describing the build configuration. Ex: 'Debug'
target_device: A string describing the target device. Ex: 'simulator'
out_dir: A string to the path which all build products will be copied.
"""
target_dir = target_dir_name(build_config, target_device)
build_dir = os.path.join("out", target_dir)
package_dir = os.path.join(build_dir, 'ios_web_view')
# # Copy framework.
framework_name = '%s.framework' % output_name
framework_source = os.path.join(build_dir, framework_name)
framework_dest = os.path.join(out_dir, target_dir, framework_name)
print('Copying %s to %s' % (framework_source, framework_dest))
shutil.copytree(framework_source, framework_dest)
# Copy symbols.
symbols_name = '%s.dSYM' % output_name
symbols_source = os.path.join(build_dir, symbols_name)
symbols_dest = os.path.join(out_dir, target_dir, symbols_name)
print('Copying %s to %s' % (symbols_source, symbols_dest))
shutil.copytree(symbols_source, symbols_dest)
def package_framework(build_config,
target_device,
out_dir,
output_name,
extra_gn_options,
extra_ninja_options):
"""Builds ChromeWebView.framework and copies the result to out_dir.
Args:
build_config: A string describing the build configuration. Ex: 'Debug'
target_device: A string describing the target device. Ex: 'simulator'
out_dir: A string to the path which all build products will be copied.
extra_gn_options: A list of strings of gn args (key=value items) to
be appended to the gn gen command.
extra_ninja_options: A string of gn options to be appended to the ninja
command.
Returns:
The return code of the build if it fails or 0 if the build was successful.
"""
print('\nBuilding for %s (%s)' % (target_device, build_config))
build_result = build(build_config,
target_device,
extra_gn_options,
extra_ninja_options)
if build_result != 0:
error = 'Building %s/%s failed with code: ' % (build_config, target_device)
print(error, build_result, file=sys.stderr)
return build_result
copy_build_products(build_config, target_device, out_dir, output_name)
return 0
def package_all_frameworks(out_dir, output_name, extra_gn_options,
build_configs, target_devices, extra_ninja_options):
"""Builds ChromeWebView.framework.
Builds Release and Debug versions of ChromeWebView.framework for both
iOS devices and simulator and copies the resulting frameworks into out_dir.
Args:
out_dir: A string to the path which all build products will be copied.
extra_gn_options: A list of strings of gn args (key=value items) to
be appended to the gn gen command.
build_configs: A list of configs to build.
target_devices: A list of devices to target.
extra_ninja_options: A string of gn options to be appended to the ninja
command.
Returns:
0 if all builds are successful or 1 if any build fails.
"""
print('Building ChromeWebView.framework...')
# Package all builds in the output directory
os.makedirs(out_dir)
configs_and_devices = [(a,b) for a in build_configs for b in target_devices]
for build_config, target_device in configs_and_devices:
if package_framework(build_config,
target_device,
out_dir,
output_name,
extra_gn_options,
extra_ninja_options) != 0:
return 1
# Copy common files from last built package to out_dir.
build_dir = os.path.join('out', target_dir_name('Release', 'iphoneos'))
package_dir = os.path.join(build_dir, 'ios_web_view')
shutil.copy2(os.path.join(package_dir, 'AUTHORS'), out_dir)
shutil.copy2(os.path.join(package_dir, 'LICENSE'), out_dir)
shutil.copy2(os.path.join(package_dir, 'VERSION'), out_dir)
print('\nSuccess! ChromeWebView.framework is packaged into %s' % out_dir)
return 0
def main():
description = 'Build and package //ios/web_view.'
parser = argparse.ArgumentParser(description=description)
parser.add_argument('out_dir', nargs='?', default='out/IOSWebViewBuild',
help='path to output directory')
parser.add_argument('--ninja_args',
help='Additional gn args to pass through to ninja.')
build_configs = ['Debug', 'Release']
target_devices = ['iphonesimulator', 'iphoneos']
parser.add_argument('--build_configs', nargs='+', default=build_configs,
choices=build_configs,
help='Specify which configs to build.')
parser.add_argument('--target_devices', nargs='+', default=target_devices,
choices=target_devices,
help='Specify which devices to target.')
options, extra_options = parser.parse_known_args()
print('Options:', options)
if len(extra_options):
print >>sys.stderr, 'Unknown options: ', extra_options
return 1
out_dir = options.out_dir
# Make sure that the output directory does not exist
if os.path.exists(out_dir):
print('The output directory already exists: ' + out_dir, file=sys.stderr)
return 1
output_name = 'ChromeWebView'
extra_gn_options = []
# This prevents Breakpad from being included in the final binary to avoid
# duplicate symbols with the client app.
extra_gn_options.append('use_crash_key_stubs=true')
return package_all_frameworks(out_dir, output_name, extra_gn_options,
set(options.build_configs),
set(options.target_devices),
options.ninja_args)
if __name__ == '__main__':
sys.exit(main())