Add iOS build script and fix builds with OSXCROSS

This commit is contained in:
Ignacio Etcheverry 2020-03-11 01:23:59 +01:00
parent 9670946cf5
commit e8ce81bf23
11 changed files with 664 additions and 60 deletions

View File

@ -7,6 +7,8 @@ This repository contains scripts for building the Mono runtime to use with Godot
These scripts are based on the Mono [sdks](https://github.com/mono/mono/tree/master/sdks) makefiles, with some changes to work well with Godot. Some platforms or targets depend on files from the `sdks` directory in the Mono source repository. This directory may be missing from tarballs. If that's the case, cloning the git repository may be needed. [This table](https://www.mono-project.com/docs/about-mono/versioning/#mono-source-versioning) can be used to determine the branch for a specific version of Mono.
Some patches need to be applied to the Mono sources before building. This can be done by running `python ./patch_mono.py`.
Run `python SCRIPT.py --help` for the full list of command line options.
By default, the scripts will install the resulting files to `$HOME/mono-installs`.
@ -22,7 +24,7 @@ export MONO_SOURCE_ROOT=$HOME/git/mono
### Notes
- Python 3.7 or higher is required.
- Cross-compiling for macOS via osxcross is not yet supported.
- OSXCROSS is supported expect for building the Mono cross-compilers.
- Building on Windows is not supported. It's possible to use Cygwin or WSL (Windows Subsystem for Linux) but this hasn't been tested.
## Desktop
@ -45,8 +47,6 @@ _AOT cross-compilers for desktop platforms cannot be built with these scripts ye
## Android
Some patches may need to be applied to the Mono sources before building for Android. This can be done by running `./patch_mono.py`.
```bash
# These are the default values. This step can be omitted if SDK and NDK root are in this location.
export ANDROID_SDK_ROOT=$HOME/Android/Sdk
@ -67,6 +67,26 @@ export ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk-bundle
The option `--target=all-runtime` is a shortcut for `--target=armeabi-v7a --target=x86 --target=arm64-v8a --target=x86_64`. The equivalent applies for `all-cross` and `all-cross-win`.
# iOS
```bash
# Build the runtime for the iPhone simulator.
./ios.py configure --target=x86_64
./ios.py make --target=x86_64
# Build the runtime for the iPhone device.
./ios.py configure --target=arm64
./ios.py make --target=arm64
# Build the AOT cross-compiler targeting the iPhone device.
./ios.py configure --target=cross-arm64
./ios.py make --target=cross-arm64
```
The runtime can also be built an OSXCROSS iOS toolchain. The `--ios-toolchain` and `--ios-sdk` options
are the equivalent of the Godot SCons options `IPHONEPATH` and `IPHONESDK` respectively.
The cross compiler cannot be built with OSXCROSS yet.
## WebAssembly
Just like with Godot, an active Emscripten SDK is needed for building the Mono WebAssembly runtime.
@ -93,6 +113,9 @@ _AOT cross-compilers for WebAssembly cannot be built with this script yet._
# Build the Android BCL.
./bcl.py make --product=android
# Build the iOS BCL.
./bcl.py make --product=ios
# Build the WebAssembly BCL.
./bcl.py make --product=wasm
```

View File

@ -173,11 +173,16 @@ def setup_android_target_template(env: dict, opts: AndroidOpts, target: str):
# permission denied. Therefore we just override 'MONO_RELOC_LIBDIR' here to avoid the relocation.
CPPFLAGS += ['-DMONO_RELOC_LIBDIR=\\\".\\\"']
CFLAGS += ['-fstack-protector']
CFLAGS += ['-DMONODROID=1'] if opts.with_monodroid else []
CFLAGS += [
'-fstack-protector',
'-DMONODROID=1'
]
CFLAGS += ['-D__ANDROID_API__=' + api] if android_new_ndk else []
CXXFLAGS += ['-fstack-protector']
CXXFLAGS += ['-DMONODROID=1'] if opts.with_monodroid else []
CXXFLAGS += [
'-fstack-protector',
'-DMONODROID=1'
]
CXXFLAGS += ['-D__ANDROID_API__=' + api] if android_new_ndk else []
CPPFLAGS += ['-I%s/sysroot/usr/include' % toolchain_path]
@ -210,7 +215,7 @@ def setup_android_target_template(env: dict, opts: AndroidOpts, target: str):
'--with-btls-android-api=%s' % api,
]
CONFIGURE_FLAGS += ['--enable-monodroid'] if opts.with_monodroid else []
CONFIGURE_FLAGS += ['--enable-monodroid']
CONFIGURE_FLAGS += ['--with-btls-android-ndk-asm-workaround'] if android_new_ndk else []
CONFIGURE_FLAGS += [
@ -282,7 +287,7 @@ def get_android_libclang_path(opts):
def setup_android_cross_template(env: dict, opts: AndroidOpts, target: str, host_arch: str):
def get_host_triple():
if sys.platform == 'darwin':
return '%s-apple-darwin10' % host_arch
return '%s-apple-darwin11' % host_arch
elif sys.platform in ['linux', 'linux2']:
return '%s-linux-gnu' % host_arch
assert False
@ -319,7 +324,7 @@ def setup_android_cross_template(env: dict, opts: AndroidOpts, target: str, host
CXXFLAGS = []
CXXFLAGS += ['-DDEBUG_CROSS'] if not opts.release else []
CXXFLAGS += ['-mmacosx-version-min=10.9 -stdlib=libc++'] if is_darwin else []
CXXFLAGS += ['-mmacosx-version-min=10.9', '-stdlib=libc++'] if is_darwin else []
env['_android-%s_CXXFLAGS' % target] = CXXFLAGS
@ -469,7 +474,6 @@ def make(opts: AndroidOpts, product: str, target: str):
def clean(opts: AndroidOpts, product: str, target: str):
rm_rf(
path_join(opts.configure_dir, 'toolchains', '%s-%s' % (product, target)),
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))
@ -517,7 +521,6 @@ def main(raw_args):
parser.add_argument('--android-ndk', default=android_ndk_default, help=default_help)
parser.add_argument('--android-api-version', default='18', help=default_help)
parser.add_argument('--android-cmake-version', default='autodetect', help=default_help)
parser.add_argument('--with-monodroid', type=custom_bool, default=True, help=default_help)
cmd_utils.add_runtime_arguments(parser, default_help)

7
bcl.py
View File

@ -11,17 +11,19 @@ from options import *
from os_utils import *
product_values = ['desktop', 'desktop-win32', 'android', 'wasm']
product_values = ['desktop', 'desktop-win32', 'android', 'ios', 'wasm']
profiles_table = {
'desktop': ['net_4_x'],
'desktop-win32': ['net_4_x'],
'android': ['monodroid', 'monodroid_tools'],
'ios': ['monotouch', 'monotouch_runtime', 'monotouch_tools'],
'wasm': ['wasm', 'wasm_tools']
}
test_profiles_table = {
'desktop': [],
'desktop-win32': [],
'android': ['monodroid', 'monodroid_tools'],
'ios': ['monotouch'],
'wasm': ['wasm']
}
@ -94,9 +96,6 @@ def make_product(opts: BclOpts, product: str):
mkdir_p(install_dir)
for profile in profiles:
mkdir_p('%s/%s' % (install_dir, profile))
make_args = ['-C', build_dir, '-C', 'runtime', 'all-mcs', 'build_profiles=%s' % ' '.join(profiles)]
make_args += ['V=1'] if opts.verbose_make else []

52
desktop.py Normal file → Executable file
View File

@ -44,14 +44,10 @@ def is_cross_compiling(target_platform: str) -> bool:
(sys.platform in ['linux', 'linux2', 'cygwin'] and target_platform != 'linux')
def get_osxcross_sdk(target, osxcross_bin):
osxcross_sdk = os.environ.get('OSXCROSS_SDK', 14)
def get_osxcross_sdk(osxcross_bin, arch):
osxcross_sdk = os.environ.get('OSXCROSS_SDK', 18)
name_fmt = path_join(osxcross_bin, target + '-apple-darwin%s-%s')
if not 'OSXCROSS_SDK' in os.environ and not os.path.isfile(name_fmt % (osxcross_sdk, 'ar')):
# Default 14 wasn't it, try 15
osxcross_sdk = 15
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\'')
@ -64,16 +60,24 @@ def setup_desktop_template(env: dict, opts: DesktopOpts, product: str, target_pl
CONFIGURE_FLAGS = [
'--disable-boehm',
'--disable-iconv',
'--disable-mcs-build',
'--disable-nls',
'--enable-dynamic-btls',
'--enable-maintainer-mode',
'--with-sigaltstack=yes',
'--with-tls=pthread',
'--without-ikvm-native'
]
if target_platform == 'windows':
CONFIGURE_FLAGS += [
'--with-libgdiplus=%s' % opts.mxe_prefix
]
else:
CONFIGURE_FLAGS += [
'--disable-iconv',
'--disable-nls',
'--enable-dynamic-btls',
'--with-sigaltstack=yes',
]
if target_platform == 'windows':
mxe_bin = path_join(opts.mxe_prefix, 'bin')
@ -92,12 +96,13 @@ def setup_desktop_template(env: dict, opts: DesktopOpts, product: str, target_pl
env['_%s-%s_STRIP' % (product, target)] = name_fmt % 'strip'
CONFIGURE_FLAGS += [
'--enable-static-gcc-libs'
#'--enable-static-gcc-libs'
]
elif target_platform == 'osx' and 'OSXCROSS_ROOT' in os.environ:
elif target_platform == 'osx':
if is_cross_compiling(target_platform):
osxcross_root = os.environ['OSXCROSS_ROOT']
osxcross_bin = path_join(osxcross_root, 'target', 'bin')
osxcross_sdk = get_osxcross_sdk(target, osxcross_bin)
osxcross_sdk = get_osxcross_sdk(osxcross_bin, arch=target)
env['_%s-%s_PATH' % (product, target)] = osxcross_bin
@ -105,8 +110,8 @@ def setup_desktop_template(env: dict, opts: DesktopOpts, product: str, target_pl
env['_%s-%s_AR' % (product, target)] = name_fmt % 'ar'
env['_%s-%s_AS' % (product, target)] = name_fmt % 'as'
env['_%s-%s_CC' % (product, target)] = name_fmt % 'cc'
env['_%s-%s_CXX' % (product, target)] = name_fmt % 'c++'
env['_%s-%s_CC' % (product, target)] = name_fmt % 'clang'
env['_%s-%s_CXX' % (product, target)] = name_fmt % 'clang++'
env['_%s-%s_LD' % (product, target)] = name_fmt % 'ld'
env['_%s-%s_RANLIB' % (product, target)] = name_fmt % 'ranlib'
env['_%s-%s_CMAKE' % (product, target)] = name_fmt % 'cmake'
@ -122,19 +127,14 @@ def setup_desktop_template(env: dict, opts: DesktopOpts, product: str, target_pl
def strip_libs(opts: DesktopOpts, product: str, target_platform: str, target: str):
if is_cross_compiling(target_platform):
if target_platform == 'windows':
if target_platform == 'osx':
# 'strip' doesn't support '--strip-unneeded' on macOS
return
if is_cross_compiling(target_platform) and target_platform == 'windows':
mxe_bin = path_join(opts.mxe_prefix, 'bin')
name_fmt = path_join(mxe_bin, target + '-w64-mingw32-%s')
strip = name_fmt % 'strip'
elif target_platform == 'osx':
assert 'OSXCROSS_ROOT' in os.environ
osxcross_root = os.environ['OSXCROSS_ROOT']
osxcross_bin = path_join(osxcross_bin, 'target', 'bin')
osxcross_sdk = get_osxcross_sdk(target, osxcross_bin)
name_fmt = path_join(osxcross_bin, target + ('-apple-darwin%s-' % osxcross_sdk) + '%s')
strip = name_fmt % 'strip'
else:
strip = 'strip'

View File

@ -0,0 +1,14 @@
diff --git a/mono/utils/mono-log-darwin.c b/mono/utils/mono-log-darwin.c
index 3cb127bad59..30ff5edc307 100644
--- a/mono/utils/mono-log-darwin.c
+++ b/mono/utils/mono-log-darwin.c
@@ -5,7 +5,8 @@
*/
#include <config.h>
-#if defined(HOST_WATCHOS) && (__WATCH_OS_VERSION_MIN_REQUIRED >= __WATCHOS_3_0)
+#if (defined(HOST_WATCHOS) && (__WATCH_OS_VERSION_MIN_REQUIRED >= __WATCHOS_3_0)) \
+ || (defined(HOST_IOS) && (__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 100000))
/* emitted by clang:
* > /Users/lewurm/work/mono-watch4/mono/utils/mono-log-darwin.c:35:2: error: 'asl_log' is \
* > deprecated: first deprecated in watchOS 3.0 - os_log(3) has replaced \

508
ios.py Executable file
View File

@ -0,0 +1,508 @@
#!/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:])

12
llvm.py
View File

@ -8,6 +8,9 @@ from options import *
from os_utils import *
# TODO: OSXCROSS
target_values = ['llvm32', 'llvm64', 'llvmwin32', 'llvmwin64']
mxe_targets = {
'llvmwin32': {'arch': 'i686', 'mxe': 'mxe-Win32'},
@ -73,6 +76,15 @@ def make(opts: BaseOpts, target: str):
make_args += ['V=1'] if opts.verbose_make else []
# IMPORTANT: We must specify the jobs count for this Makefile.
# The Makefile itself runs Make as well with the '-j' option, which tells it to spawn as many jobs as possible.
# This can result in errors like 'posix_spawn failed: Resource temporarily unavailable' on macOS due to the process limit.
# The job count seems to be inherited from the parent Make process, so that fixes the issue.
make_args += ['-j', opts.jobs]
if not find_executable('cmake') and not 'CMAKE' in os.environ:
print('WARNING: Cannot find CMake. Required by the llvm Makefile.')
run_command('make', args=make_args, name='make')
touch(stamp_file)

22
options.py Normal file → Executable file
View File

@ -25,12 +25,20 @@ class AndroidOpts(RuntimeOpts):
android_toolchains_prefix: str
android_sdk_root: str
android_ndk_root: str
with_monodroid: bool
android_api_version: str
android_cmake_version: str
toolchain_name_fmt: str = '%s-api%s-clang'
@dataclass
class iOSOpts(RuntimeOpts):
ios_toolchain_path: str
ios_sdk_path: str
ios_version_min: str
osx_toolchain_path: str
osx_sdk_path: str
@dataclass
class DesktopOpts(RuntimeOpts):
with_llvm: bool
@ -72,12 +80,22 @@ def android_opts_from_args(args):
android_toolchains_prefix = abspath(args.toolchains_prefix),
android_sdk_root = abspath(args.android_sdk),
android_ndk_root = abspath(args.android_ndk),
with_monodroid = args.with_monodroid,
android_api_version = args.android_api_version,
android_cmake_version = args.android_cmake_version
)
def ios_opts_from_args(args):
return iOSOpts(
**vars(runtime_opts_from_args(args)),
ios_toolchain_path = abspath(args.ios_toolchain),
ios_sdk_path = abspath(args.ios_sdk) if args.ios_sdk else '',
ios_version_min = args.ios_version_min,
osx_toolchain_path = abspath(args.osx_toolchain),
osx_sdk_path = abspath(args.osx_sdk) if args.ios_sdk else ''
)
def bcl_opts_from_args(args):
return BclOpts(
**vars(base_opts_from_args(args)),

15
os_utils.py Normal file → Executable file
View File

@ -32,13 +32,17 @@ def run_command(command, args=[], cwd=None, env=None, name='command'):
raise BuildError('\'%s\' exited with error code: %s' % (name, e.returncode))
print_env_sh_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'print_env.sh')
def source(script: str, cwd=None) -> dict:
popen_args = {}
if cwd is not None:
popen_args['cwd'] = cwd
import subprocess
proc = subprocess.Popen('bash -c \'source %s; env -0\'' % script, stdout=subprocess.PIPE, shell=True, **popen_args)
cmd = 'bash -c \'source %s; bash %s\'' % (script, print_env_sh_path)
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True, **popen_args)
output = proc.communicate()[0]
return dict(line.split('=', 1) for line in output.decode().split('\x00') if line)
@ -135,3 +139,12 @@ def globs(pathnames, dirpath='.'):
for pathname in pathnames:
files.extend(glob.glob(os.path.join(dirpath, pathname)))
return files
def xcrun_find_sdk(sdk_name):
import subprocess
xcrun_output = subprocess.check_output(['xcrun', '--sdk', sdk_name, '--show-sdk-path']).decode().strip()
if xcrun_output.startswith('xcrun: error: SDK "%s" cannot be located' % sdk_name):
return ''
sdk_path = xcrun_output
return sdk_path

View File

@ -28,7 +28,8 @@ def main(raw_args):
patches = [
'fix-mono-android-tkill.diff',
'mono-dbg-agent-clear-tls-instead-of-abort.diff',
'bcl-profile-platform-override.diff'
'bcl-profile-platform-override.diff',
'mono_ios_asl_log_deprecated.diff'
]
from subprocess import Popen

13
print_env.sh Executable file
View File

@ -0,0 +1,13 @@
#!/bin/bash
# This is used in python to get the export environment variables from bash's 'source' command.
# The 'env' command option '-0' separates the 'name=value' results by 'null' instead of line breaks.
# This is required to parse the output because a variable value can contain line breaks as well.
# Unfortunately, the '-0' option is not supported on some platforms like macOS,
# hence why we need this script to print the environment variables instead.
unset IFS
for var in $(compgen -e); do
printf "$var=${!var}\0"
done