godot-mono-builds/ios.py

509 lines
16 KiB
Python
Raw Normal View History

#!/usr/bin/python
import os
import os.path
import sys
from os.path import join as path_join
from options import *
from os_utils import *
import runtime
this_script_dir = os.path.dirname(os.path.realpath(__file__))
device_targets = ['armv7', 'arm64']
sim_targets = ['i386', 'x86_64']
cross_targets = ['cross-armv7', 'cross-arm64']
def is_cross(target) -> bool:
return target in cross_targets
class iOSTargetTable:
archs = {
'armv7': 'arm',
'arm64': 'arm64',
'i386': 'i386',
'x86_64': 'x86_64'
}
host_triples = {
'armv7': 'arm-apple-darwin11',
'arm64': 'aarch64-apple-darwin11',
'i386': 'i386-apple-darwin11',
'x86_64': 'x86_64-apple-darwin11'
}
osxcross_tool_triples = {
'armv7': 'arm-apple-darwin11', # TODO: ?
'arm64': 'arm-apple-darwin11',
'i386': 'i386-apple-darwin11', # TODO: ?
'x86_64': 'x86_64-apple-darwin11'
}
def setup_ios_device_template(env: dict, opts: iOSOpts, target: str):
ios_sysroot_path = opts.ios_sdk_path
if not ios_sysroot_path and sys.platform == 'darwin':
# Auto-detect on macOS
ios_sysroot_path = xcrun_find_sdk('iphoneos')
if not ios_sysroot_path:
raise RuntimeError('Cannot find iOS SDK; specify one manually with \'--ios-sdk\'.')
sysroot_flags = ['-isysroot', ios_sysroot_path, '-miphoneos-version-min=%s' % opts.ios_version_min]
arch = iOSTargetTable.archs[target]
host_triple = iOSTargetTable.host_triples[target]
osxcross_tool_triple = iOSTargetTable.osxcross_tool_triples[target]
tools_path = path_join(opts.ios_toolchain_path, 'usr', 'bin')
if sys.platform != 'darwin':
name_fmt = path_join(tools_path, osxcross_tool_triple + '-%s')
else:
name_fmt = path_join(tools_path, '%s')
AR = name_fmt % 'ar'
AS = name_fmt % 'as'
CC = name_fmt % 'clang'
CXX = name_fmt % 'clang++'
LD = name_fmt % 'ld'
RANLIB = name_fmt % 'ranlib'
STRIP = name_fmt % 'strip'
ccache_path = os.environ.get('CCACHE', '')
if ccache_path:
CC = '%s %s' % (ccache_path, CC)
CXX = '%s %s' % (ccache_path, CXX)
AC_VARS = [
'ac_cv_c_bigendian=no',
'ac_cv_func_fstatat=no',
'ac_cv_func_readlinkat=no',
'ac_cv_func_getpwuid_r=no',
'ac_cv_func_posix_getpwuid_r=yes',
'ac_cv_header_curses_h=no',
'ac_cv_header_localcharset_h=no',
'ac_cv_header_sys_user_h=no',
'ac_cv_func_getentropy=no',
'ac_cv_func_futimens=no',
'ac_cv_func_utimensat=no',
'ac_cv_func_shm_open_working_with_mmap=no',
'mono_cv_sizeof_sunpath=104',
'mono_cv_uscore=yes'
]
bitcode_marker = env.get('ios-%s_BITCODE_MARKER' % target, '')
CFLAGS = sysroot_flags + [
'-arch %s' % arch,
'-Wl,-application_extension',
'-fexceptions'
]
CFLAGS += [bitcode_marker] if bitcode_marker else []
CXXFLAGS = sysroot_flags + [
'-arch %s' % arch,
'-Wl,-application_extension'
]
CXXFLAGS += [bitcode_marker] if bitcode_marker else []
CPPFLAGS = sysroot_flags + [
'-DMONOTOUCH=1',
'-arch %s' % arch,
'-DSMALL_CONFIG', '-D_XOPEN_SOURCE', '-DHOST_IOS', '-DHAVE_LARGE_FILE_SUPPORT=1'
]
LDFLAGS = [
'-Wl,-no_weak_imports',
'-arch %s' % arch,
'-framework', 'CoreFoundation',
'-lobjc', '-lc++'
]
CONFIGURE_FLAGS = [
'--disable-boehm',
'--disable-btls',
'--disable-executables',
'--disable-icall-tables',
'--disable-iconv',
'--disable-mcs-build',
'--disable-nls',
'--disable-visibility-hidden',
'--enable-dtrace=no',
'--enable-icall-export',
'--enable-maintainer-mode',
'--enable-minimal=ssa,com,interpreter,jit,portability,assembly_remapping,attach,verifier,' +
'full_messages,appdomains,security,sgen_remset,sgen_marksweep_par,sgen_marksweep_fixed,' +
'sgen_marksweep_fixed_par,sgen_copying,logging,remoting,shared_perfcounters,gac',
'--enable-monotouch',
# We don't need this. Comment it so we don't have to call 'mono_gc_init_finalizer_thread' from Godot.
#'--with-lazy-gc-thread-creation=yes',
'--with-tls=pthread',
'--without-ikvm-native',
'--without-sigaltstack',
'--disable-cooperative-suspend',
'--disable-hybrid-suspend',
'--disable-crash-reporting'
]
env['_ios-%s_AR' % target] = AR
env['_ios-%s_AS' % target] = AS
env['_ios-%s_CC' % target] = CC
env['_ios-%s_CXX' % target] = CXX
env['_ios-%s_LD' % target] = LD
env['_ios-%s_RANLIB' % target] = RANLIB
env['_ios-%s_STRIP' % target] = STRIP
env['_ios-%s_AC_VARS' % target] = AC_VARS
env['_ios-%s_CFLAGS' % target] = CFLAGS
env['_ios-%s_CXXFLAGS' % target] = CXXFLAGS
env['_ios-%s_CPPFLAGS' % target] = CPPFLAGS
env['_ios-%s_LDFLAGS' % target] = LDFLAGS
env['_ios-%s_CONFIGURE_FLAGS' % target] = CONFIGURE_FLAGS
# Runtime template
runtime.setup_runtime_template(env, opts, 'ios', target, host_triple)
def setup_ios_simulator_template(env: dict, opts: iOSOpts, target: str):
ios_sysroot_path = opts.ios_sdk_path
if not ios_sysroot_path and sys.platform == 'darwin':
# Auto-detect on macOS
ios_sysroot_path = xcrun_find_sdk('iphonesimulator')
if not ios_sysroot_path:
raise RuntimeError('Cannot find iOS SDK; specify one manually with \'--ios-sdk\'.')
sysroot_flags = ['-isysroot', ios_sysroot_path, '-miphoneos-version-min=%s' % opts.ios_version_min]
arch = iOSTargetTable.archs[target]
host_triple = iOSTargetTable.host_triples[target]
osxcross_tool_triple = iOSTargetTable.osxcross_tool_triples[target]
tools_path = path_join(opts.ios_toolchain_path, 'usr', 'bin')
if sys.platform != 'darwin':
name_fmt = path_join(tools_path, osxcross_tool_triple + '-%s')
else:
name_fmt = path_join(tools_path, '%s')
AR = name_fmt % 'ar'
AS = name_fmt % 'as'
CC = name_fmt % 'clang'
CXX = name_fmt % 'clang++'
LD = name_fmt % 'ld'
RANLIB = name_fmt % 'ranlib'
STRIP = name_fmt % 'strip'
ccache_path = os.environ.get('CCACHE', '')
if ccache_path:
CC = '%s %s' % (ccache_path, CC)
CXX = '%s %s' % (ccache_path, CXX)
AC_VARS = [
'ac_cv_func_clock_nanosleep=no',
'ac_cv_func_fstatat=no',
'ac_cv_func_readlinkat=no',
'ac_cv_func_system=no',
'ac_cv_func_getentropy=no',
'ac_cv_func_futimens=no',
'ac_cv_func_utimensat=no',
'ac_cv_func_shm_open_working_with_mmap=no',
'mono_cv_uscore=yes'
]
CFLAGS = sysroot_flags + [
'-arch %s' % arch,
'-Wl,-application_extension'
]
CXXFLAGS = sysroot_flags + [
'-arch %s' % arch,
'-Wl,-application_extension'
]
CPPFLAGS = sysroot_flags + [
'-DMONOTOUCH=1',
'-arch %s' % arch,
'-Wl,-application_extension',
'-DHOST_IOS'
]
LDFLAGS = []
CONFIGURE_FLAGS = [
'--disable-boehm',
'--disable-btls',
'--disable-executables',
'--disable-iconv',
'--disable-mcs-build',
'--disable-nls',
'--disable-visibility-hidden',
'--enable-maintainer-mode',
'--enable-minimal=com,remoting,shared_perfcounters,gac',
'--enable-monotouch',
'--with-tls=pthread',
'--without-ikvm-native',
'--disable-cooperative-suspend',
'--disable-hybrid-suspend',
'--disable-crash-reporting'
]
env['_ios-%s_AR' % target] = AR
env['_ios-%s_AS' % target] = AS
env['_ios-%s_CC' % target] = CC
env['_ios-%s_CXX' % target] = CXX
env['_ios-%s_LD' % target] = LD
env['_ios-%s_RANLIB' % target] = RANLIB
env['_ios-%s_STRIP' % target] = STRIP
env['_ios-%s_AC_VARS' % target] = AC_VARS
env['_ios-%s_CFLAGS' % target] = CFLAGS
env['_ios-%s_CXXFLAGS' % target] = CXXFLAGS
env['_ios-%s_CPPFLAGS' % target] = CPPFLAGS
env['_ios-%s_LDFLAGS' % target] = LDFLAGS
env['_ios-%s_CONFIGURE_FLAGS' % target] = CONFIGURE_FLAGS
# Runtime template
runtime.setup_runtime_template(env, opts, 'ios', target, host_triple)
class iOSCrossTable:
target_triples = {
'cross-armv7': 'arm-apple-darwin',
'cross-arm64': 'aarch64-apple-darwin'
}
device_targets = {
'cross-armv7': 'armv7',
'cross-arm64': 'arm64'
}
# 'darwin10' is hard-coded in 'offsets-tool.py', hence why we use 'darwin10' here
offsets_dumper_abis = {
'cross-armv7': 'arm-apple-darwin10',
'cross-arm64': 'aarch64-apple-darwin10'
}
def get_osxcross_sdk(osxcross_bin, arch):
osxcross_sdk = os.environ.get('OSXCROSS_SDK', 18)
name_fmt = path_join(osxcross_bin, arch + '-apple-darwin%s-%s')
if not os.path.isfile(name_fmt % (osxcross_sdk, 'ar')):
raise BuildError('Specify a valid osxcross SDK with the environment variable \'OSXCROSS_SDK\'')
return osxcross_sdk
def setup_ios_cross_template(env: dict, opts: iOSOpts, target: str, host_arch: str):
target_triple = iOSCrossTable.target_triples[target]
device_target = iOSCrossTable.device_targets[target]
offsets_dumper_abi = iOSCrossTable.offsets_dumper_abis[target]
host_triple = '%s-apple-darwin11' % host_arch
is_sim = device_target in sim_targets
ios_sysroot_path = opts.ios_sdk_path
if not ios_sysroot_path and sys.platform == 'darwin':
# Auto-detect on macOS
ios_sysroot_path = xcrun_find_sdk('iphonesimulator' if is_sim else 'iphoneos')
if not ios_sysroot_path:
raise RuntimeError('Cannot find iOS SDK; specify one manually with \'--ios-sdk\'.')
osx_sysroot_path = opts.osx_sdk_path
if not osx_sysroot_path and sys.platform == 'darwin':
# Auto-detect on macOS
osx_sysroot_path = xcrun_find_sdk('macosx')
if not osx_sysroot_path:
raise RuntimeError('Cannot find MacOSX SDK; specify one manually with \'--osx-sdk\'.')
if sys.platform != 'darwin':
if not 'OSXCROSS_ROOT' in os.environ:
raise RuntimeError('The \'OSXCROSS_ROOT\' environment variable is required for cross-compiling to macOS')
osxcross_root = os.environ['OSXCROSS_ROOT']
osxcross_bin = path_join(osxcross_root, 'target', 'bin')
osxcross_sdk = get_osxcross_sdk(osxcross_bin, arch=host_arch)
env['_ios-%s_PATH' % target] = osxcross_bin
name_fmt = path_join(osxcross_bin, target + ('-apple-darwin%s-' % osxcross_sdk) + '%s')
else:
tools_path = path_join(opts.osx_toolchain_path, 'usr', 'bin')
name_fmt = path_join(tools_path, '%s')
env['_ios-%s_AR' % target] = name_fmt % 'ar'
env['_ios-%s_AS' % target] = name_fmt % 'as'
env['_ios-%s_CC' % target] = name_fmt % 'clang'
env['_ios-%s_CXX' % target] = name_fmt % 'clang++'
env['_ios-%s_LD' % target] = name_fmt % 'ld'
env['_ios-%s_RANLIB' % target] = name_fmt % 'ranlib'
env['_ios-%s_STRIP' % target] = name_fmt % 'strip'
libclang = path_join(opts.ios_toolchain_path, 'usr', 'lib', 'libclang.dylib') if sys.platform == 'darwin' else os.environ['LIBCLANG_PATH']
env['_ios-%s_OFFSETS_DUMPER_ARGS' % target] = [
'--libclang=%s' % libclang,
'--sysroot=%s' % ios_sysroot_path
]
AC_VARS = ['ac_cv_func_shm_open_working_with_mmap=no']
CFLAGS = ['-isysroot', osx_sysroot_path, '-mmacosx-version-min=10.9', '-Qunused-arguments']
CXXFLAGS = ['-isysroot', osx_sysroot_path, '-mmacosx-version-min=10.9', '-Qunused-arguments', '-stdlib=libc++']
CPPFLAGS = ['-DMONOTOUCH=1']
LDFLAGS = ['-stdlib=libc++']
CONFIGURE_FLAGS = [
'--disable-boehm',
'--disable-btls',
'--disable-iconv',
'--disable-libraries',
'--disable-mcs-build',
'--disable-nls',
'--enable-dtrace=no',
'--enable-icall-symbol-map',
'--enable-minimal=com,remoting',
'--enable-monotouch',
'--disable-crash-reporting'
]
env['_ios-%s_AC_VARS' % target] = AC_VARS
env['_ios-%s_CFLAGS' % target] = CFLAGS
env['_ios-%s_CXXFLAGS' % target] = CXXFLAGS
env['_ios-%s_CPPFLAGS' % target] = CPPFLAGS
env['_ios-%s_LDFLAGS' % target] = LDFLAGS
env['_ios-%s_CONFIGURE_FLAGS' % target] = CONFIGURE_FLAGS
# Runtime cross template
runtime.setup_runtime_cross_template(env, opts, 'ios', target, host_triple, target_triple, device_target, 'llvm64', offsets_dumper_abi)
def strip_libs(opts: iOSOpts, product: str, target: str):
# 'strip' doesn't support '--strip-unneeded' on macOS
return
def configure(opts: iOSOpts, product: str, target: str):
env = {}
is_sim = target in sim_targets
if is_cross(target):
import llvm
llvm.make(opts, 'llvm64')
setup_ios_cross_template(env, opts, target, host_arch='x86_64')
else:
if is_sim:
setup_ios_simulator_template(env, opts, target)
else:
setup_ios_device_template(env, opts, target)
if not os.path.isfile(path_join(opts.mono_source_root, 'configure')):
runtime.run_autogen(opts)
runtime.run_configure(env, opts, product, target)
def make(opts: iOSOpts, product: str, target: str):
env = {}
build_dir = path_join(opts.configure_dir, '%s-%s-%s' % (product, target, opts.configuration))
make_args = ['-C', build_dir]
make_args += ['V=1'] if opts.verbose_make else []
run_command('make', args=make_args, name='make')
run_command('make', args=['-C', '%s/mono' % build_dir, 'install'], name='make install mono')
run_command('make', args=['-C', '%s/support' % build_dir, 'install'], name='make install support')
run_command('make', args=['-C', '%s/data' % build_dir, 'install'], name='make install data')
if opts.strip_libs and not is_cross(target):
strip_libs(opts, product, target)
def clean(opts: iOSOpts, product: str, target: str):
rm_rf(
path_join(opts.configure_dir, '%s-%s-%s' % (product, target, opts.configuration)),
path_join(opts.configure_dir, '%s-%s-%s.config.cache' % (product, target, opts.configuration)),
path_join(opts.install_dir, '%s-%s-%s' % (product, target, opts.configuration))
)
def main(raw_args):
import cmd_utils
from cmd_utils import custom_bool
from collections import OrderedDict
from typing import Callable
target_shortcuts = {
'all-device': device_targets,
'all-sim': sim_targets,
'all-cross': cross_targets
}
target_values = device_targets + sim_targets + cross_targets + list(target_shortcuts)
actions = OrderedDict()
actions['configure'] = configure
actions['make'] = make
actions['clean'] = clean
parser = cmd_utils.build_arg_parser(description='Builds the Mono runtime for iOS')
default_help = 'default: %(default)s'
default_ios_toolchain = '/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain'
default_osx_toolchain = '/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain'
default_ios_version_min = '10.0' # Same as Godot
parser.add_argument('action', choices=['configure', 'make', 'clean'])
parser.add_argument('--target', choices=target_values, action='append', required=True)
parser.add_argument('--ios-toolchain', default=default_ios_toolchain, help=default_help)
parser.add_argument('--ios-sdk', default='', help=default_help)
parser.add_argument('--ios-version-min', default=default_ios_version_min, help=default_help)
parser.add_argument('--osx-toolchain', default=default_osx_toolchain, help=default_help)
parser.add_argument('--osx-sdk', default='', help=default_help)
cmd_utils.add_runtime_arguments(parser, default_help)
args = parser.parse_args(raw_args)
input_action = args.action
input_targets = args.target
opts = ios_opts_from_args(args)
targets = cmd_utils.expand_input_targets(input_targets, target_shortcuts)
if not os.path.isdir(opts.mono_source_root):
print('Mono sources directory not found: ' + opts.mono_source_root)
sys.exit(1)
action = actions[input_action]
try:
for target in targets:
action(opts, 'ios', target)
except BuildError as e:
sys.exit(e.message)
if __name__ == '__main__':
from sys import argv
main(argv[1:])