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. 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. 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`. 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 ### Notes
- Python 3.7 or higher is required. - 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. - 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 ## Desktop
@ -45,8 +47,6 @@ _AOT cross-compilers for desktop platforms cannot be built with these scripts ye
## Android ## 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 ```bash
# These are the default values. This step can be omitted if SDK and NDK root are in this location. # 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 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`. 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 ## WebAssembly
Just like with Godot, an active Emscripten SDK is needed for building the Mono WebAssembly runtime. 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. # Build the Android BCL.
./bcl.py make --product=android ./bcl.py make --product=android
# Build the iOS BCL.
./bcl.py make --product=ios
# Build the WebAssembly BCL. # Build the WebAssembly BCL.
./bcl.py make --product=wasm ./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. # permission denied. Therefore we just override 'MONO_RELOC_LIBDIR' here to avoid the relocation.
CPPFLAGS += ['-DMONO_RELOC_LIBDIR=\\\".\\\"'] CPPFLAGS += ['-DMONO_RELOC_LIBDIR=\\\".\\\"']
CFLAGS += ['-fstack-protector'] CFLAGS += [
CFLAGS += ['-DMONODROID=1'] if opts.with_monodroid else [] '-fstack-protector',
'-DMONODROID=1'
]
CFLAGS += ['-D__ANDROID_API__=' + api] if android_new_ndk else [] 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 [] CXXFLAGS += ['-D__ANDROID_API__=' + api] if android_new_ndk else []
CPPFLAGS += ['-I%s/sysroot/usr/include' % toolchain_path] 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, '--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 += ['--with-btls-android-ndk-asm-workaround'] if android_new_ndk else []
CONFIGURE_FLAGS += [ 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 setup_android_cross_template(env: dict, opts: AndroidOpts, target: str, host_arch: str):
def get_host_triple(): def get_host_triple():
if sys.platform == 'darwin': if sys.platform == 'darwin':
return '%s-apple-darwin10' % host_arch return '%s-apple-darwin11' % host_arch
elif sys.platform in ['linux', 'linux2']: elif sys.platform in ['linux', 'linux2']:
return '%s-linux-gnu' % host_arch return '%s-linux-gnu' % host_arch
assert False assert False
@ -319,7 +324,7 @@ def setup_android_cross_template(env: dict, opts: AndroidOpts, target: str, host
CXXFLAGS = [] CXXFLAGS = []
CXXFLAGS += ['-DDEBUG_CROSS'] if not opts.release else [] 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 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): def clean(opts: AndroidOpts, product: str, target: str):
rm_rf( 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' % (product, target, opts.configuration)),
path_join(opts.configure_dir, '%s-%s-%s.config.cache' % (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)) 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-ndk', default=android_ndk_default, help=default_help)
parser.add_argument('--android-api-version', default='18', 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('--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) cmd_utils.add_runtime_arguments(parser, default_help)

7
bcl.py
View File

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

80
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') (sys.platform in ['linux', 'linux2', 'cygwin'] and target_platform != 'linux')
def get_osxcross_sdk(target, osxcross_bin): def get_osxcross_sdk(osxcross_bin, arch):
osxcross_sdk = os.environ.get('OSXCROSS_SDK', 14) osxcross_sdk = os.environ.get('OSXCROSS_SDK', 18)
name_fmt = path_join(osxcross_bin, target + '-apple-darwin%s-%s') name_fmt = path_join(osxcross_bin, arch + '-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
if not os.path.isfile(name_fmt % (osxcross_sdk, 'ar')): if not os.path.isfile(name_fmt % (osxcross_sdk, 'ar')):
raise BuildError('Specify a valid osxcross SDK with the environment variable \'OSXCROSS_SDK\'') 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 = [ CONFIGURE_FLAGS = [
'--disable-boehm', '--disable-boehm',
'--disable-iconv',
'--disable-mcs-build', '--disable-mcs-build',
'--disable-nls',
'--enable-dynamic-btls',
'--enable-maintainer-mode', '--enable-maintainer-mode',
'--with-sigaltstack=yes',
'--with-tls=pthread', '--with-tls=pthread',
'--without-ikvm-native' '--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': if target_platform == 'windows':
mxe_bin = path_join(opts.mxe_prefix, 'bin') mxe_bin = path_join(opts.mxe_prefix, 'bin')
@ -92,27 +96,28 @@ def setup_desktop_template(env: dict, opts: DesktopOpts, product: str, target_pl
env['_%s-%s_STRIP' % (product, target)] = name_fmt % 'strip' env['_%s-%s_STRIP' % (product, target)] = name_fmt % 'strip'
CONFIGURE_FLAGS += [ CONFIGURE_FLAGS += [
'--enable-static-gcc-libs' #'--enable-static-gcc-libs'
] ]
elif target_platform == 'osx' and 'OSXCROSS_ROOT' in os.environ: elif target_platform == 'osx':
osxcross_root = os.environ['OSXCROSS_ROOT'] if is_cross_compiling(target_platform):
osxcross_bin = path_join(osxcross_root, 'target', 'bin') osxcross_root = os.environ['OSXCROSS_ROOT']
osxcross_sdk = get_osxcross_sdk(target, osxcross_bin) osxcross_bin = path_join(osxcross_root, 'target', 'bin')
osxcross_sdk = get_osxcross_sdk(osxcross_bin, arch=target)
env['_%s-%s_PATH' % (product, target)] = osxcross_bin env['_%s-%s_PATH' % (product, target)] = osxcross_bin
name_fmt = path_join(osxcross_bin, target + ('-apple-darwin%s-' % osxcross_sdk) + '%s') name_fmt = path_join(osxcross_bin, target + ('-apple-darwin%s-' % osxcross_sdk) + '%s')
env['_%s-%s_AR' % (product, target)] = name_fmt % 'ar' env['_%s-%s_AR' % (product, target)] = name_fmt % 'ar'
env['_%s-%s_AS' % (product, target)] = name_fmt % 'as' env['_%s-%s_AS' % (product, target)] = name_fmt % 'as'
env['_%s-%s_CC' % (product, target)] = name_fmt % 'cc' env['_%s-%s_CC' % (product, target)] = name_fmt % 'clang'
env['_%s-%s_CXX' % (product, target)] = name_fmt % 'c++' env['_%s-%s_CXX' % (product, target)] = name_fmt % 'clang++'
env['_%s-%s_LD' % (product, target)] = name_fmt % 'ld' env['_%s-%s_LD' % (product, target)] = name_fmt % 'ld'
env['_%s-%s_RANLIB' % (product, target)] = name_fmt % 'ranlib' env['_%s-%s_RANLIB' % (product, target)] = name_fmt % 'ranlib'
env['_%s-%s_CMAKE' % (product, target)] = name_fmt % 'cmake' env['_%s-%s_CMAKE' % (product, target)] = name_fmt % 'cmake'
env['_%s-%s_STRIP' % (product, target)] = name_fmt % 'strip' env['_%s-%s_STRIP' % (product, target)] = name_fmt % 'strip'
else: else:
env['_%s-%s_CC' % (product, target)] = 'cc' env['_%s-%s_CC' % (product, target)] = 'cc'
env['_%s-%s_CONFIGURE_FLAGS' % (product, target)] = CONFIGURE_FLAGS env['_%s-%s_CONFIGURE_FLAGS' % (product, target)] = CONFIGURE_FLAGS
@ -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): def strip_libs(opts: DesktopOpts, product: str, target_platform: str, target: str):
if is_cross_compiling(target_platform): if target_platform == 'osx':
if target_platform == 'windows': # 'strip' doesn't support '--strip-unneeded' on macOS
mxe_bin = path_join(opts.mxe_prefix, 'bin') return
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') if is_cross_compiling(target_platform) and target_platform == 'windows':
strip = name_fmt % 'strip' mxe_bin = path_join(opts.mxe_prefix, 'bin')
name_fmt = path_join(mxe_bin, target + '-w64-mingw32-%s')
strip = name_fmt % 'strip'
else: else:
strip = 'strip' 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 * from os_utils import *
# TODO: OSXCROSS
target_values = ['llvm32', 'llvm64', 'llvmwin32', 'llvmwin64'] target_values = ['llvm32', 'llvm64', 'llvmwin32', 'llvmwin64']
mxe_targets = { mxe_targets = {
'llvmwin32': {'arch': 'i686', 'mxe': 'mxe-Win32'}, '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 [] 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') run_command('make', args=make_args, name='make')
touch(stamp_file) touch(stamp_file)

22
options.py Normal file → Executable file
View File

@ -25,12 +25,20 @@ class AndroidOpts(RuntimeOpts):
android_toolchains_prefix: str android_toolchains_prefix: str
android_sdk_root: str android_sdk_root: str
android_ndk_root: str android_ndk_root: str
with_monodroid: bool
android_api_version: str android_api_version: str
android_cmake_version: str android_cmake_version: str
toolchain_name_fmt: str = '%s-api%s-clang' 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 @dataclass
class DesktopOpts(RuntimeOpts): class DesktopOpts(RuntimeOpts):
with_llvm: bool with_llvm: bool
@ -72,12 +80,22 @@ def android_opts_from_args(args):
android_toolchains_prefix = abspath(args.toolchains_prefix), android_toolchains_prefix = abspath(args.toolchains_prefix),
android_sdk_root = abspath(args.android_sdk), android_sdk_root = abspath(args.android_sdk),
android_ndk_root = abspath(args.android_ndk), android_ndk_root = abspath(args.android_ndk),
with_monodroid = args.with_monodroid,
android_api_version = args.android_api_version, android_api_version = args.android_api_version,
android_cmake_version = args.android_cmake_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): def bcl_opts_from_args(args):
return BclOpts( return BclOpts(
**vars(base_opts_from_args(args)), **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)) 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: def source(script: str, cwd=None) -> dict:
popen_args = {} popen_args = {}
if cwd is not None: if cwd is not None:
popen_args['cwd'] = cwd popen_args['cwd'] = cwd
import subprocess 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] output = proc.communicate()[0]
return dict(line.split('=', 1) for line in output.decode().split('\x00') if line) 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: for pathname in pathnames:
files.extend(glob.glob(os.path.join(dirpath, pathname))) files.extend(glob.glob(os.path.join(dirpath, pathname)))
return files 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 = [ patches = [
'fix-mono-android-tkill.diff', 'fix-mono-android-tkill.diff',
'mono-dbg-agent-clear-tls-instead-of-abort.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 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