Add build scripts for WASM, AOT cross-compilers, BCL and desktop

This commit is contained in:
Ignacio Etcheverry 2019-11-10 04:58:04 +01:00
parent 9efec19e6b
commit 09fbbb7d61
16 changed files with 1984 additions and 609 deletions

View File

@ -1,26 +1,101 @@
# Mono build scripts for Godot # Mono build scripts for Godot
This repository contains scripts for building the Mono runtime to use with Godot Engine This repository contains scripts for building the Mono runtime to use with Godot Engine
## Android instructions ## Command-line options
Run `python build_mono_android.py --help` for the full list of command line options. **Requires Python 3.7 or higher**
You may need to tweak some of those if the default values do not fit your needs.
Example: 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.
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`.
A custom output directory can be specified with the `--install-dir` option.
When cross-compiling to Windows, `--mxe-prefix` must be specified. For example, with the `mingw-w64` package installed on Ubuntu, one can pass `--mxe-prefix=/usr`.
A path to the Mono source tree must be provided with the `--mono-sources` option or with the `MONO_SOURCE_ROOT` environment variable:
```bash ```bash
# These are the default values. You can omit them if they apply to your system 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.
- 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
```bash
# Build the runtimes for 32-bit and 64-bit Linux.
./desktop.py linux configure --target=i686 --target=x86_64
./desktop.py linux make --target=i686 --target=x86_64
# Build the runtimes for 32-bit and 64-bit Windows.
./desktop.py windows configure --target=i686 --target=x86_64 --mxe-prefix=/usr
./desktop.py windows make --target=i686 --target=x86_64 --mxe-prefix=/usr
# Build the runtime for 64-bit macOS.
./desktop.py osx configure --target=x86_64
./desktop.py osx make --target=x86_64
```
_AOT cross-compilers for desktop platforms cannot be built with this script yet._
## 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 export ANDROID_SDK_ROOT=$HOME/Android/Sdk
export ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk-bundle export ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk-bundle
# The mono sources may be in a different location on your system # Build the runtime for all supported Android ABIs.
export MONO_SOURCE_ROOT=$HOME/git/mono ./android.py configure --target=all-runtime
./android.py make --target=all-runtime
./build_mono_android.py configure --target=all # Build the AOT cross-compilers targeting all supported Android ABIs.
./build_mono_android.py make --target=all ./android.py configure --target=all-cross
./android.py make --target=all-cross
# Build the AOT cross-compilers for Windows targeting all supported Android ABIs.
./android.py configure --target=all-cross-win --mxe-prefix=/usr
./android.py make --target=all-cross-win --mxe-prefix=/usr
``` ```
The option `--target=all` is a shortcut for `--target=armeabi-v7a --target=x86 --target=arm64-v8a --target=x86_64`. 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`.
By default, the script will install the resulting files to `$HOME/mono-installs`. ## WebAssembly
You can specify a custom output directory with the `--install-dir` option.
Just like with Godot, an active Emscripten SDK is needed for building the Mono WebAssembly runtime.
Some patches may need to be applied to the Emscripten SDK before building Mono. This can be done by running `./patch_emscripten.py`.
```bash
# Build the runtime for WebAssembly.
./wasm.py configure --target=runtime
./wasm.py make --target=runtime
```
_AOT cross-compilers for WebAssembly cannot be built with this script yet._
## Base Class library
```bash
# Build the Desktop BCL.
./bcl.py make --product=desktop
# Build the Android BCL.
./bcl.py make --product=android
# Build the WebAssembly BCL.
./bcl.py make --product=wasm
```
## Reference Assemblies
```bash
./reference_assemblies.py install
```

0
__init__.py Normal file
View File

546
android.py Executable file
View File

@ -0,0 +1,546 @@
#!/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
runtime_targets = ['armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64']
cross_targets = ['cross-arm', 'cross-arm64', 'cross-x86', 'cross-x86_64']
cross_mxe_targets = ['cross-arm-win', 'cross-arm64-win', 'cross-x86-win', 'cross-x86_64-win']
def is_cross(target) -> bool:
return target in cross_targets or is_cross_mxe(target)
def is_cross_mxe(target) -> bool:
return target in cross_mxe_targets
def android_autodetect_cmake(opts: AndroidOpts) -> str:
from distutils.version import LooseVersion
from os import listdir
sdk_cmake_basedir = path_join(opts.android_sdk_root, 'cmake')
versions = []
for entry in listdir(sdk_cmake_basedir):
if os.path.isdir(path_join(sdk_cmake_basedir, entry)):
try:
version = LooseVersion(entry)
versions += [version]
except ValueError:
continue # Not a version folder
if len(versions) == 0:
raise BuildError('Cannot auto-detect Android CMake version')
lattest_version = str(sorted(versions)[-1])
print('Auto-detected Android CMake version: ' + lattest_version)
return lattest_version
def get_api_version_or_min(opts: AndroidOpts, target: str) -> str:
min_versions = { 'arm64-v8a': '21', 'x86_64': '21' }
if target in min_versions and int(opts.android_api_version) < int(min_versions[target]):
print('WARNING: %s is less than minimum platform for %s; using %s' % (opts.android_api_version, target, min_versions[target]))
return min_versions[target]
return opts.android_api_version
def get_android_cmake_version(opts: AndroidOpts) -> str:
return opts.android_cmake_version if opts.android_cmake_version != 'autodetect' else android_autodetect_cmake(opts)
class AndroidTargetTable:
archs = {
'armeabi-v7a': 'arm',
'arm64-v8a': 'arm64',
'x86': 'x86',
'x86_64': 'x86_64'
}
abi_names = {
'armeabi-v7a': 'arm-linux-androideabi',
'arm64-v8a': 'aarch64-linux-android',
'x86': 'i686-linux-android',
'x86_64': 'x86_64-linux-android'
}
host_triples = {
'armeabi-v7a': 'armv5-linux-androideabi',
'arm64-v8a': 'aarch64-linux-android',
'x86': 'i686-linux-android',
'x86_64': 'x86_64-linux-android'
}
def setup_android_target_template(env: dict, opts: AndroidOpts, target: str):
extra_target_envs = {
'armeabi-v7a': {
'android-armeabi-v7a_CFLAGS': ['-D__POSIX_VISIBLE=201002', '-DSK_RELEASE', '-DNDEBUG', '-UDEBUG', '-fpic', '-march=armv7-a', '-mtune=cortex-a8', '-mfpu=vfp', '-mfloat-abi=softfp'],
'android-armeabi-v7a_CXXFLAGS': ['-D__POSIX_VISIBLE=201002', '-DSK_RELEASE', '-DNDEBUG', '-UDEBUG', '-fpic', '-march=armv7-a', '-mtune=cortex-a8', '-mfpu=vfp', '-mfloat-abi=softfp'],
'android-armeabi-v7a_LDFLAGS': ['-Wl,--fix-cortex-a8']
},
'arm64-v8a': {
'android-arm64-v8a_CFLAGS': ['-D__POSIX_VISIBLE=201002', '-DSK_RELEASE', '-DNDEBUG', '-UDEBUG', '-fpic', '-DL_cuserid=9', '-DANDROID64'],
'android-arm64-v8a_CXXFLAGS': ['-D__POSIX_VISIBLE=201002', '-DSK_RELEASE', '-DNDEBUG', '-UDEBUG', '-fpic', '-DL_cuserid=9', '-DANDROID64']
},
'x86': {},
'x86_64': {
'android-x86_64_CFLAGS': ['-DL_cuserid=9'],
'android-x86_64_CXXFLAGS': ['-DL_cuserid=9']
}
}
if target in extra_target_envs:
env.update(extra_target_envs[target])
android_new_ndk = True
with open(path_join(opts.android_ndk_root, 'source.properties')) as file:
for line in file:
line = line.strip()
if line.startswith('Pkg.Revision ') or line.startswith('Pkg.Revision='):
pkg_revision = line.split('=')[1].strip()
mayor = int(pkg_revision.split('.')[0])
android_new_ndk = mayor >= 18
break
arch = AndroidTargetTable.archs[target]
abi_name = AndroidTargetTable.abi_names[target]
host_triple = AndroidTargetTable.host_triples[target]
api = env['ANDROID_API_VERSION']
toolchain_path = path_join(opts.android_toolchains_prefix, opts.toolchain_name_fmt % (target, api))
tools_path = path_join(toolchain_path, 'bin')
name_fmt = abi_name + '-%s'
sdk_cmake_dir = path_join(opts.android_sdk_root, 'cmake', get_android_cmake_version(opts))
if not os.path.isdir(sdk_cmake_dir):
print('Android CMake directory \'%s\' not found' % sdk_cmake_dir)
AR = path_join(tools_path, name_fmt % 'ar')
AS = path_join(tools_path, name_fmt % 'as')
CC = path_join(tools_path, name_fmt % 'clang')
CXX = path_join(tools_path, name_fmt % 'clang++')
DLLTOOL = ''
LD = path_join(tools_path, name_fmt % 'ld')
OBJDUMP = path_join(tools_path, name_fmt % 'objdump')
RANLIB = path_join(tools_path, name_fmt % 'ranlib')
CMAKE = path_join(sdk_cmake_dir, 'bin', 'cmake')
STRIP = path_join(tools_path, name_fmt % 'strip')
CPP = path_join(tools_path, name_fmt % 'cpp')
if not os.path.isfile(CPP):
CPP = path_join(tools_path, (name_fmt % 'clang'))
CPP += ' -E'
CXXCPP = path_join(tools_path, name_fmt % 'cpp')
if not os.path.isfile(CXXCPP):
CXXCPP = path_join(tools_path, (name_fmt % 'clang++'))
CXXCPP += ' -E'
ccache_path = os.environ.get('CCACHE', '')
if ccache_path:
CC = '%s %s' % (ccache_path, CC)
CXX = '%s %s' % (ccache_path, CXX)
CPP = '%s %s' % (ccache_path, CPP)
CXXCPP = '%s %s' % (ccache_path, CXXCPP)
AC_VARS = [
'mono_cv_uscore=yes',
'ac_cv_func_sched_getaffinity=no',
'ac_cv_func_sched_setaffinity=no',
'ac_cv_func_shm_open_working_with_mmap=no'
]
CFLAGS, CXXFLAGS, CPPFLAGS, CXXCPPFLAGS, LDFLAGS = [], [], [], [], []
# On Android we use 'getApplicationInfo().nativeLibraryDir' as the libdir where Mono will look for shared objects.
# This path looks something like this: '/data/app-lib/{package_name-{num}}'. However, Mono does some relocation
# and the actual path it will look at will be '/data/app-lib/{package_name}-{num}/../lib', which doesn't exist.
# Cannot use '/data/data/{package_name}/lib' either, as '/data/data/{package_name}/lib/../lib' may result in
# 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 += ['-D__ANDROID_API__=' + api] if android_new_ndk else []
CXXFLAGS += ['-fstack-protector']
CXXFLAGS += ['-DMONODROID=1'] if opts.with_monodroid else []
CXXFLAGS += ['-D__ANDROID_API__=' + api] if android_new_ndk else []
CPPFLAGS += ['-I%s/sysroot/usr/include' % toolchain_path]
CXXCPPFLAGS += ['-I%s/sysroot/usr/include' % toolchain_path]
path_link = '%s/platforms/android-%s/arch-%s/usr/lib' % (opts.android_ndk_root, api, arch)
LDFLAGS += [
'-z', 'now', '-z', 'relro', '-z', 'noexecstack',
'-ldl', '-lm', '-llog', '-lc', '-lgcc',
'-Wl,-rpath-link=%s,-dynamic-linker=/system/bin/linker' % path_link,
'-L' + path_link
]
# Fixes this error: DllImport unable to load library 'dlopen failed: empty/missing DT_HASH in "libmono-native.so" (built with --hash-style=gnu?)'.
LDFLAGS += ['-Wl,--hash-style=both']
CONFIGURE_FLAGS = [
'--disable-boehm',
'--disable-executables',
'--disable-iconv',
'--disable-mcs-build',
'--disable-nls',
'--enable-dynamic-btls',
'--enable-maintainer-mode',
'--enable-minimal=ssa,portability,attach,verifier,full_messages,sgen_remset'
',sgen_marksweep_par,sgen_marksweep_fixed,sgen_marksweep_fixed_par'
',sgen_copying,logging,security,shared_handles,interpreter',
'--with-btls-android-ndk=%s' % opts.android_ndk_root,
'--with-btls-android-api=%s' % api,
]
CONFIGURE_FLAGS += ['--enable-monodroid'] if opts.with_monodroid else []
CONFIGURE_FLAGS += ['--with-btls-android-ndk-asm-workaround'] if android_new_ndk else []
CONFIGURE_FLAGS += [
'--with-btls-android-cmake-toolchain=%s/build/cmake/android.toolchain.cmake' % opts.android_ndk_root,
'--with-sigaltstack=yes',
'--with-tls=pthread',
'--without-ikvm-native',
'--disable-cooperative-suspend',
'--disable-hybrid-suspend',
'--disable-crash-reporting'
]
env['_android-%s_AR' % target] = AR
env['_android-%s_AS' % target] = AS
env['_android-%s_CC' % target] = CC
env['_android-%s_CXX' % target] = CXX
env['_android-%s_CPP' % target] = CPP
env['_android-%s_CXXCPP' % target] = CXXCPP
env['_android-%s_DLLTOOL' % target] = DLLTOOL
env['_android-%s_LD' % target] = LD
env['_android-%s_OBJDUMP' % target] = OBJDUMP
env['_android-%s_RANLIB' % target] = RANLIB
env['_android-%s_CMAKE' % target] = CMAKE
env['_android-%s_STRIP' % target] = STRIP
env['_android-%s_AC_VARS' % target] = AC_VARS
env['_android-%s_CFLAGS' % target] = CFLAGS
env['_android-%s_CXXFLAGS' % target] = CXXFLAGS
env['_android-%s_CPPFLAGS' % target] = CPPFLAGS
env['_android-%s_CXXCPPFLAGS' % target] = CXXCPPFLAGS
env['_android-%s_LDFLAGS' % target] = LDFLAGS
env['_android-%s_CONFIGURE_FLAGS' % target] = CONFIGURE_FLAGS
# Runtime template
runtime.setup_runtime_template(env, opts, 'android', target, host_triple)
class AndroidCrossTable:
target_archs = {
'cross-arm': 'armv7',
'cross-arm64': 'aarch64-v8a',
'cross-x86': 'i686',
'cross-x86_64': 'x86_64'
}
device_targets = {
'cross-arm': 'armeabi-v7a',
'cross-arm64': 'arm64-v8a',
'cross-x86': 'x86',
'cross-x86_64': 'x86_64'
}
offsets_dumper_abis = {
'cross-arm': 'armv7-none-linux-androideabi',
'cross-arm64': 'aarch64-v8a-linux-android',
'cross-x86': 'i686-none-linux-android',
'cross-x86_64': 'x86_64-none-linux-android'
}
def get_android_libclang_path(opts):
if sys.platform == 'darwin':
return '%s/toolchains/llvm/prebuilt/darwin-x86_64/lib64/libclang.dylib' % opts.android_ndk_root
elif sys.platform in ['linux', 'linux2']:
return '%s/toolchains/llvm/prebuilt/linux-x86_64/lib64/libclang.so.8svn' % opts.android_ndk_root
assert False
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
elif sys.platform in ['linux', 'linux2']:
return '%s-linux-gnu' % host_arch
assert False
target_arch = AndroidCrossTable.target_archs[target]
device_target = AndroidCrossTable.device_targets[target]
offsets_dumper_abi = AndroidCrossTable.offsets_dumper_abis[target]
host_triple = get_host_triple()
target_triple = '%s-linux-android' % target_arch
android_libclang = get_android_libclang_path(opts)
env['_android-%s_OFFSETS_DUMPER_ARGS' % target] = [
'--libclang=%s' % android_libclang,
'--sysroot=%s/sysroot' % opts.android_ndk_root
]
env['_android-%s_AR' % target] = 'ar'
env['_android-%s_AS' % target] = 'as'
env['_android-%s_CC' % target] = 'cc'
env['_android-%s_CXX' % target] = 'c++'
env['_android-%s_CXXCPP' % target] = 'cpp'
env['_android-%s_LD' % target] = 'ld'
env['_android-%s_RANLIB' % target] = 'ranlib'
env['_android-%s_STRIP' % target] = 'strip'
is_darwin = sys.platform == 'darwin'
CFLAGS = []
CFLAGS += ['-DDEBUG_CROSS'] if not opts.release else []
CFLAGS += ['-mmacosx-version-min=10.9'] if is_darwin else []
env['_android-%s_CFLAGS' % target] = CFLAGS
CXXFLAGS = []
CXXFLAGS += ['-DDEBUG_CROSS'] if not opts.release else []
CXXFLAGS += ['-mmacosx-version-min=10.9 -stdlib=libc++'] if is_darwin else []
env['_android-%s_CXXFLAGS' % target] = CXXFLAGS
env['_android-%s_CONFIGURE_FLAGS' % target] = [
'--disable-boehm',
'--disable-mcs-build',
'--disable-nls',
'--enable-maintainer-mode',
'--with-tls=pthread'
]
# Runtime cross template
runtime.setup_runtime_cross_template(env, opts, 'android', target, host_triple, target_triple, device_target, 'llvm-llvm64', offsets_dumper_abi)
def setup_android_cross_mxe_template(env: dict, opts: AndroidOpts, target: str, host_arch: str):
table_target = cross_targets[cross_mxe_targets.index(target)] # Re-use the non-mxe table
target_arch = AndroidCrossTable.target_archs[table_target]
device_target = AndroidCrossTable.device_targets[table_target]
offsets_dumper_abi = AndroidCrossTable.offsets_dumper_abis[table_target]
host_triple = '%s-w64-mingw32' % host_arch
target_triple = '%s-linux-android' % target_arch
android_libclang = get_android_libclang_path(opts)
env['_android-%s_OFFSETS_DUMPER_ARGS' % target] = [
'--libclang=%s' % android_libclang,
'--sysroot=%s/sysroot' % opts.android_ndk_root
]
mxe_bin = path_join(opts.mxe_prefix, 'bin')
env['_android-%s_PATH' % target] = mxe_bin
name_fmt = host_arch + '-w64-mingw32-%s'
env['_android-%s_AR' % target] = path_join(mxe_bin, name_fmt % 'ar')
env['_android-%s_AS' % target] = path_join(mxe_bin, name_fmt % 'as')
env['_android-%s_CC' % target] = path_join(mxe_bin, name_fmt % 'gcc')
env['_android-%s_CXX' % target] = path_join(mxe_bin, name_fmt % 'g++')
env['_android-%s_DLLTOOL' % target] = path_join(mxe_bin, name_fmt % 'dlltool')
env['_android-%s_LD' % target] = path_join(mxe_bin, name_fmt % 'ld')
env['_android-%s_OBJDUMP' % target] = path_join(mxe_bin, name_fmt % 'objdump')
env['_android-%s_RANLIB' % target] = path_join(mxe_bin, name_fmt % 'ranlib')
env['_android-%s_STRIP' % target] = path_join(mxe_bin, name_fmt % 'strip')
mingw_zlib_prefix = '%s/opt/mingw-zlib/usr' % opts.mxe_prefix
if not os.path.isdir(mingw_zlib_prefix):
mingw_zlib_prefix = opts.mxe_prefix
CFLAGS = []
CFLAGS += ['-DDEBUG_CROSS'] if not opts.release else []
CFLAGS += ['-I%s/%s-w64-mingw32/include' % (mingw_zlib_prefix, host_arch)]
env['_android-%s_CFLAGS' % target] = CFLAGS
CXXFLAGS = []
CXXFLAGS += ['-DDEBUG_CROSS'] if not opts.release else []
CXXFLAGS += ['-I%s/%s-w64-mingw32/include' % (mingw_zlib_prefix, host_arch)]
env['_android-%s_CXXFLAGS' % target] = CXXFLAGS
env['_android-%s_LDFLAGS' % target] = []
CONFIGURE_FLAGS = [
'--disable-boehm',
'--disable-mcs-build',
'--disable-nls',
'--enable-static-gcc-libs',
'--enable-maintainer-mode',
'--with-tls=pthread'
]
CONFIGURE_FLAGS += ['--with-static-zlib=%s/%s-w64-mingw32/lib/libz.a' % (mingw_zlib_prefix, host_arch)]
env['_android-%s_CONFIGURE_FLAGS' % target] = CONFIGURE_FLAGS
# Runtime cross template
runtime.setup_runtime_cross_template(env, opts, 'android', target, host_triple, target_triple, device_target, 'llvm-llvmwin64', offsets_dumper_abi)
def make_standalone_toolchain(opts: AndroidOpts, target: str, api: str):
install_dir = path_join(opts.android_toolchains_prefix, opts.toolchain_name_fmt % (target, api))
if os.path.isdir(path_join(install_dir, 'bin')):
return # Looks like it's already there, so no need to re-create it
command = path_join(opts.android_ndk_root, 'build', 'tools', 'make_standalone_toolchain.py')
args = ['--verbose', '--force', '--api=' + api, '--arch=' + AndroidTargetTable.archs[target],
'--install-dir=' + install_dir]
run_command(command, args=args, name='make_standalone_toolchain')
def strip_libs(opts: AndroidOpts, product: str, target: str, api: str):
toolchain_path = path_join(opts.android_toolchains_prefix, opts.toolchain_name_fmt % (target, api))
tools_path = path_join(toolchain_path, 'bin')
name_fmt = AndroidTargetTable.abi_names[target] + '-%s'
strip = path_join(tools_path, name_fmt % 'strip')
install_dir = path_join(opts.install_dir, '%s-%s-%s' % (product, target, opts.configuration))
out_libs_dir = path_join(install_dir, 'lib')
lib_files = globs(('*.a', '*.so'), dirpath=out_libs_dir)
if len(lib_files):
run_command(strip, args=['--strip-unneeded'] + lib_files, name='strip')
def configure(opts: AndroidOpts, product: str, target: str):
env = { 'ANDROID_API_VERSION': get_api_version_or_min(opts, target) }
if is_cross(target):
import llvm
if is_cross_mxe(target):
llvm.make(opts, 'llvmwin64')
setup_android_cross_mxe_template(env, opts, target, host_arch='x86_64')
else:
llvm.make(opts, 'llvm64')
setup_android_cross_template(env, opts, target, host_arch='x86_64')
else:
make_standalone_toolchain(opts, target, env['ANDROID_API_VERSION'])
setup_android_target_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: AndroidOpts, product: str, target: str):
env = { 'ANDROID_API_VERSION': get_api_version_or_min(opts, target) }
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')
if opts.strip_libs and not is_cross(target):
strip_libs(opts, product, target, env['ANDROID_API_VERSION'])
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))
)
def main(raw_args):
import cmd_utils
from cmd_utils import custom_bool
from collections import OrderedDict
from typing import Callable
target_shortcuts = {
'all-runtime': runtime_targets,
'all-cross': cross_targets,
'all-cross-win': cross_mxe_targets
}
target_values = runtime_targets + cross_targets + cross_mxe_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 Android',
env_vars={
'ANDROID_SDK_ROOT': 'Overrides default value for --android-sdk',
'ANDROID_NDK_ROOT': 'Overrides default value for --android-ndk',
'ANDROID_HOME': 'Same as ANDROID_SDK_ROOT'
}
)
home = os.environ.get('HOME')
android_sdk_default = os.environ.get('ANDROID_HOME', os.environ.get('ANDROID_SDK_ROOT', path_join(home, 'Android/Sdk')))
android_ndk_default = os.environ.get('ANDROID_NDK_ROOT', path_join(android_sdk_default, 'ndk-bundle'))
default_help = 'default: %(default)s'
parser.add_argument('action', choices=['configure', 'make', 'clean'])
parser.add_argument('--target', choices=target_values, action='append', required=True)
parser.add_argument('--toolchains-prefix', default=path_join(home, 'android-toolchains'), help=default_help)
parser.add_argument('--android-sdk', default=android_sdk_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-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)
args = parser.parse_args(raw_args)
input_action = args.action
input_targets = args.target
opts = android_opts_from_args(args)
if not os.path.isdir(opts.mono_source_root):
print('Mono sources directory not found: ' + opts.mono_source_root)
sys.exit(1)
targets = cmd_utils.expand_input_targets(input_targets, target_shortcuts)
action = actions[input_action]
try:
for target in targets:
action(opts, 'android', target)
except BuildError as e:
sys.exit(e.message)
if __name__ == '__main__':
from sys import argv
main(argv[1:])

151
bcl.py Executable file
View File

@ -0,0 +1,151 @@
#!/usr/bin/python
#!/usr/bin/python
import os
import os.path
from os.path import join as path_join
from options import *
from os_utils import *
product_values = ['desktop', 'android', 'wasm']
profiles_table = {
'desktop': ['net_4_x'],
'android': ['monodroid', 'monodroid_tools'],
'wasm': ['wasm', 'wasm_tools']
}
test_profiles_table = {
'desktop': [],
'android': ['monodroid', 'monodroid_tools'],
'wasm': ['wasm']
}
def configure_bcl(opts: BclOpts):
stamp_file = path_join(opts.configure_dir, '.stamp-bcl-configure')
if os.path.isfile(stamp_file):
return
build_dir = path_join(opts.configure_dir, 'bcl')
mkdir_p(build_dir)
CONFIGURE_FLAGS = [
'--disable-boehm',
'--disable-btls-lib',
'--disable-nls',
'--disable-support-build',
'--with-mcs-docs=no'
]
configure = path_join(opts.mono_source_root, 'configure')
configure_args = CONFIGURE_FLAGS
run_command(configure, args=configure_args, cwd=build_dir, name='configure bcl')
touch(stamp_file)
def make_bcl(opts: BclOpts):
stamp_file = path_join(opts.configure_dir, '.stamp-bcl-make')
if os.path.isfile(stamp_file):
return
build_dir = path_join(opts.configure_dir, 'bcl')
make_args = ['-C', build_dir, '-C', 'mono']
make_args += ['V=1'] if opts.verbose_make else []
run_command('make', args=make_args, name='make bcl')
touch(stamp_file)
def build_bcl(opts: BclOpts):
configure_bcl(opts)
make_bcl(opts)
def clean_bcl(opts: BclOpts):
configure_stamp_file = path_join(opts.configure_dir, '.stamp-bcl-configure')
make_stamp_file = path_join(opts.configure_dir, '.stamp-bcl-make')
build_dir = path_join(opts.configure_dir, 'bcl')
rm_rf(configure_stamp_file, make_stamp_file, build_dir)
def make_product(opts: BclOpts, product: str):
build_bcl(opts)
build_dir = path_join(opts.configure_dir, 'bcl')
profiles = profiles_table[product]
test_profiles = test_profiles_table[product]
install_dir = path_join(opts.install_dir, '%s-bcl' % product)
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 []
run_command('make', args=make_args, name='make profiles')
if opts.tests and len(test_profiles) > 0:
test_make_args = ['-C', build_dir, '-C', 'runtime', 'test', 'xunit-test', 'test_profiles=%s' % ' '.join(test_profiles)]
test_make_args += ['V=1'] if opts.verbose_make else []
run_command('make', args=test_make_args, name='make tests')
# Copy the bcl profiles to the output directory
from distutils.dir_util import copy_tree
for profile in profiles:
copy_tree('%s/mcs/class/lib/%s' % (opts.mono_source_root, profile), '%s/%s' % (install_dir, profile))
# Recursively remove hidden files we shoudln't have copied (e.g.: .stamp)
import glob
hidden_file_pattern = '%s/**/.*' % install_dir
for profile in profiles:
[rm_rf(x) for x in glob.iglob(hidden_file_pattern, recursive=True)]
def clean_product(opts: BclOpts, product: str):
clean_bcl(opts)
install_dir = path_join(opts.install_dir, '%s-bcl' % product)
rm_rf(install_dir)
def main(raw_args):
import cmd_utils
actions = {
'make': make_product,
'clean': clean_product
}
parser = cmd_utils.build_arg_parser(description='Builds the Mono BCL')
default_help = 'default: %(default)s'
parser.add_argument('action', choices=actions.keys())
parser.add_argument('--product', choices=product_values, action='append', required=True)
parser.add_argument('--tests', action='store_true', default=False, help=default_help)
cmd_utils.add_base_arguments(parser, default_help)
args = parser.parse_args(raw_args)
opts = bcl_opts_from_args(args)
products = args.product
for product in products:
action = actions[args.action]
action(opts, product)
if __name__ == '__main__':
from sys import argv
main(argv[1:])

View File

@ -1,597 +0,0 @@
#!/usr/bin/python
from os import environ
from os.path import exists as path_exists, join as path_join, isfile, isdir, abspath
from sys import exit
class MonoBuildError(Exception):
'''Generic exception for custom build errors'''
def __init__(self, msg):
super(MonoBuildError, self).__init__(msg)
self.message = msg
def run_command(command, args=[], custom_env=None, name='command'):
def cmd_args_to_str(cmd_args):
return ' '.join([arg if not ' ' in arg else '"%s"' % arg for arg in cmd_args])
assert isinstance(command, str) and isinstance(args, list)
args = [command] + args
import subprocess
try:
print('Running command \'%s\': %s' % (name, cmd_args_to_str(args)))
if custom_env is None:
subprocess.check_call(args)
else:
subprocess.check_call(args, env=custom_env)
print('Command \'%s\' completed successfully' % name)
except subprocess.CalledProcessError as e:
raise MonoBuildError('\'%s\' exited with error code: %s' % (name, e.returncode))
# Creates the directory if no other file or directory with the same path exists
def mkdir_p(path):
from os import makedirs
if not path_exists(path):
print('creating directory: ' + path)
makedirs(path)
def chdir(path):
from os import chdir as os_chdir
print('entering directory: ' + path)
os_chdir(path)
# Remove files and/or directories recursively
def rm_rf(*paths):
from os import remove
from shutil import rmtree
for path in paths:
if isfile(path):
print('removing file: ' + path)
remove(path)
elif isdir(path):
print('removing directory and its contents: ' + path)
rmtree(path)
def globs(pathnames, dirpath='.'):
import glob
files = []
for pathname in pathnames:
files.extend(glob.glob(path_join(dirpath, pathname)))
return files
TOOLCHAIN_NAME_FMT = '%s-api%s-clang'
CONFIGURATION = None
RELEASE = None
ANDROID_TOOLCHAINS_PREFIX = None
ANDROID_SDK_ROOT = None
ANDROID_NDK_ROOT = None
WITH_MONODROID = None
ENABLE_CXX = None
VERBOSE_MAKE = None
STRIP_LIBS = None
CONFIGURE_DIR = None
INSTALL_DIR = None
MONO_SOURCE_ROOT = None
_ANDROID_API_VERSION = None
_ANDROID_CMAKE_VERSION = None
class AndroidTargetInfo:
archs = {
'armeabi-v7a': 'arm',
'arm64-v8a': 'arm64',
'x86': 'x86',
'x86_64': 'x86_64'
}
abi_names = {
'armeabi-v7a': 'arm-linux-androideabi',
'arm64-v8a': 'aarch64-linux-android',
'x86': 'i686-linux-android',
'x86_64': 'x86_64-linux-android'
}
host_triples = {
'armeabi-v7a': 'armv5-linux-androideabi',
'arm64-v8a': 'aarch64-linux-android',
'x86': 'i686-linux-android',
'x86_64': 'x86_64-linux-android'
}
def android_autodetect_cmake():
from distutils.version import LooseVersion
from os import listdir
sdk_cmake_basedir = path_join(ANDROID_SDK_ROOT, 'cmake')
versions = []
for entry in listdir(sdk_cmake_basedir):
if isdir(path_join(sdk_cmake_basedir, entry)):
try:
version = LooseVersion(entry)
versions += [version]
except ValueError:
continue # Not a version folder
if len(versions) == 0:
raise MonoBuildError('Cannot auto-detect Android CMake version')
lattest_version = str(sorted(versions)[-1])
print('Auto-detected Android CMake version: ' + lattest_version)
return lattest_version
def get_api_version_or_min(target):
min_versions = { 'arm64-v8a': '21', 'x86_64': '21' }
if target in min_versions and int(_ANDROID_API_VERSION) < int(min_versions[target]):
print('WARNING: %s is less than minimum platform for %s; using %s' % (_ANDROID_API_VERSION, target, min_versions[target]))
return min_versions[target]
return _ANDROID_API_VERSION
def get_android_cmake_version():
return _ANDROID_CMAKE_VERSION if _ANDROID_CMAKE_VERSION != 'autodetect' else android_autodetect_cmake()
def setup_runtime_template(env, product, target, host_triple):
BITNESS = ''
if any(s in host_triple for s in ['i686', 'i386']):
BITNESS = '-m32'
elif 'x86_64' in host_triple:
BITNESS = '-m64'
CFLAGS = []
CFLAGS += ['-O2', '-g'] if RELEASE else ['-O0', '-ggdb3', '-fno-omit-frame-pointer']
CFLAGS += env.get('_%s-%s_CFLAGS' % (product, target), [])
CFLAGS += env.get('%s-%s_CFLAGS' % (product, target), [])
CFLAGS += [BITNESS] if BITNESS else []
CXXFLAGS = []
CXXFLAGS += ['-O2', '-g'] if RELEASE else ['-O0', '-ggdb3', '-fno-omit-frame-pointer']
CXXFLAGS += env.get('_%s-%s_CXXFLAGS' % (product, target), [])
CXXFLAGS += env.get('%s-%s_CXXFLAGS' % (product, target), [])
CXXFLAGS += [BITNESS] if BITNESS else []
CPPFLAGS = []
CPPFLAGS += ['-O2', '-g'] if RELEASE else ['-O0', '-ggdb3', '-fno-omit-frame-pointer']
CPPFLAGS += env.get('_%s-%s_CPPFLAGS' % (product, target), [])
CPPFLAGS += env.get('%s-%s_CPPFLAGS' % (product, target), [])
CPPFLAGS += [BITNESS] if BITNESS else []
CXXCPPFLAGS = []
CXXCPPFLAGS += ['-O2', '-g'] if RELEASE else ['-O0', '-ggdb3', '-fno-omit-frame-pointer']
CXXCPPFLAGS += env.get('_%s-%s_CXXCPPFLAGS' % (product, target), [])
CXXCPPFLAGS += env.get('%s-%s_CXXCPPFLAGS' % (product, target), [])
CXXCPPFLAGS += [BITNESS] if BITNESS else []
LDFLAGS = []
LDFLAGS += env.get('_%s-%s_LDFLAGS' % (product, target), [])
LDFLAGS += env.get('%s-%s_LDFLAGS' % (product, target), [])
AC_VARS = []
AC_VARS += env.get('_%s-%s_AC_VARS' % (product, target), [])
AC_VARS += env.get('%s-%s_AC_VARS' % (product, target), [])
CONFIGURE_ENVIRONMENT = {}
def append_product_env_var(var_name):
val = env.get('_%s-%s_%s' % (product, target, var_name), '')
if val:
CONFIGURE_ENVIRONMENT[var_name] = val
append_product_env_var('AR')
append_product_env_var('AS')
append_product_env_var('CC')
append_product_env_var('CPP')
append_product_env_var('CXX')
append_product_env_var('CXXCPP')
append_product_env_var('DLLTOOL')
append_product_env_var('LD')
append_product_env_var('OBJDUMP')
append_product_env_var('RANLIB')
append_product_env_var('CMAKE')
append_product_env_var('STRIP')
CONFIGURE_ENVIRONMENT['CFLAGS'] = CFLAGS
CONFIGURE_ENVIRONMENT['CXXFLAGS'] = CXXFLAGS
CONFIGURE_ENVIRONMENT['CPPFLAGS'] = CPPFLAGS
CONFIGURE_ENVIRONMENT['CXXCPPFLAGS'] = CXXCPPFLAGS
CONFIGURE_ENVIRONMENT['LDFLAGS'] = LDFLAGS
CONFIGURE_ENVIRONMENT.update(env.get('_%s-%s_CONFIGURE_ENVIRONMENT' % (product, target), {}))
CONFIGURE_ENVIRONMENT.update(env.get('%s-%s_CONFIGURE_ENVIRONMENT' % (product, target), {}))
CONFIGURE_FLAGS = []
CONFIGURE_FLAGS += ['--host=%s' % host_triple] if host_triple else []
CONFIGURE_FLAGS += ['--cache-file=%s/%s-%s-%s.config.cache' % (CONFIGURE_DIR, product, target, CONFIGURATION)]
CONFIGURE_FLAGS += ['--prefix=%s/%s-%s-%s' % (INSTALL_DIR, product, target, CONFIGURATION)]
CONFIGURE_FLAGS += ['--enable-cxx'] if ENABLE_CXX else []
# CONFIGURE_FLAGS += env['_cross-runtime_%s-%s_CONFIGURE_FLAGS' % (product, target)]
CONFIGURE_FLAGS += env.get('_%s-%s_CONFIGURE_FLAGS' % (product, target), [])
CONFIGURE_FLAGS += env.get('%s-%s_CONFIGURE_FLAGS' % (product, target), [])
env['_runtime_%s-%s_AC_VARS' % (product, target)] = AC_VARS
env['_runtime_%s-%s_CONFIGURE_ENVIRONMENT' % (product, target)] = CONFIGURE_ENVIRONMENT
env['_runtime_%s-%s_CONFIGURE_FLAGS' % (product, target)] = CONFIGURE_FLAGS
def setup_android_target_template(env, target):
extra_target_envs = {
'armeabi-v7a': {
'android-armeabi-v7a_CFLAGS': ['-D__POSIX_VISIBLE=201002', '-DSK_RELEASE', '-DNDEBUG', '-UDEBUG', '-fpic', '-march=armv7-a', '-mtune=cortex-a8', '-mfpu=vfp', '-mfloat-abi=softfp'],
'android-armeabi-v7a_CXXFLAGS': ['-D__POSIX_VISIBLE=201002', '-DSK_RELEASE', '-DNDEBUG', '-UDEBUG', '-fpic', '-march=armv7-a', '-mtune=cortex-a8', '-mfpu=vfp', '-mfloat-abi=softfp'],
'android-armeabi-v7a_LDFLAGS': ['-Wl,--fix-cortex-a8']
},
'arm64-v8a': {
'android-arm64-v8a_CFLAGS': ['-D__POSIX_VISIBLE=201002', '-DSK_RELEASE', '-DNDEBUG', '-UDEBUG', '-fpic', '-DL_cuserid=9', '-DANDROID64'],
'android-arm64-v8a_CXXFLAGS': ['-D__POSIX_VISIBLE=201002', '-DSK_RELEASE', '-DNDEBUG', '-UDEBUG', '-fpic', '-DL_cuserid=9', '-DANDROID64']
},
'x86': {},
'x86_64': {
'android-x86_64_CFLAGS': ['-DL_cuserid=9'],
'android-x86_64_CXXFLAGS': ['-DL_cuserid=9']
}
}
env.update(extra_target_envs[target])
android_new_ndk = True
with open(path_join(ANDROID_NDK_ROOT, 'source.properties')) as file:
for line in file:
line = line.strip()
if line.startswith('Pkg.Revision ') or line.startswith('Pkg.Revision='):
pkg_revision = line.split('=')[1].strip()
mayor = int(pkg_revision.split('.')[0])
android_new_ndk = mayor >= 18
break
arch = AndroidTargetInfo.archs[target]
abi_name = AndroidTargetInfo.abi_names[target]
host_triple = AndroidTargetInfo.host_triples[target]
api = env['ANDROID_API_VERSION']
toolchain_path = path_join(ANDROID_TOOLCHAINS_PREFIX, TOOLCHAIN_NAME_FMT % (target, api))
tools_path = path_join(toolchain_path, 'bin')
name_fmt = abi_name + '-%s'
sdk_cmake_dir = path_join(ANDROID_SDK_ROOT, 'cmake', get_android_cmake_version())
if not isdir(sdk_cmake_dir):
print('Android CMake directory \'%s\' not found' % sdk_cmake_dir)
AR = path_join(tools_path, name_fmt % 'ar')
AS = path_join(tools_path, name_fmt % 'as')
CC = path_join(tools_path, name_fmt % 'clang')
CXX = path_join(tools_path, name_fmt % 'clang++')
DLLTOOL = ''
LD = path_join(tools_path, name_fmt % 'ld')
OBJDUMP = path_join(tools_path, name_fmt % 'objdump')
RANLIB = path_join(tools_path, name_fmt % 'ranlib')
CMAKE = path_join(sdk_cmake_dir, 'bin', 'cmake')
STRIP = path_join(tools_path, name_fmt % 'strip')
CPP = path_join(tools_path, name_fmt % 'cpp')
if not isfile(CPP):
CPP = path_join(tools_path, (name_fmt % 'clang'))
CPP += ' -E'
CXXCPP = path_join(tools_path, name_fmt % 'cpp')
if not isfile(CXXCPP):
CXXCPP = path_join(tools_path, (name_fmt % 'clang++'))
CXXCPP += ' -E'
ccache_path = environ.get('CCACHE', '')
if ccache_path:
CC = '%s %s' % (ccache_path, CC)
CXX = '%s %s' % (ccache_path, CXX)
CPP = '%s %s' % (ccache_path, CPP)
CXXCPP = '%s %s' % (ccache_path, CXXCPP)
AC_VARS = [
'mono_cv_uscore=yes',
'ac_cv_func_sched_getaffinity=no',
'ac_cv_func_sched_setaffinity=no',
'ac_cv_func_shm_open_working_with_mmap=no'
]
CFLAGS, CXXFLAGS, CPPFLAGS, CXXCPPFLAGS, LDFLAGS = [], [], [], [], []
# On Android we use 'getApplicationInfo().nativeLibraryDir' as the libdir where Mono will look for shared objects.
# This path looks something like this: '/data/app-lib/{package_name-{num}}'. However, Mono does some relocation
# and the actual path it will look at will be '/data/app-lib/{package_name}-{num}/../lib', which doesn't exist.
# Cannot use '/data/data/{package_name}/lib' either, as '/data/data/{package_name}/lib/../lib' may result in
# 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 WITH_MONODROID else []
CFLAGS += ['-D__ANDROID_API__=' + api] if android_new_ndk else []
CXXFLAGS += ['-fstack-protector']
CXXFLAGS += ['-DMONODROID=1'] if WITH_MONODROID else []
CXXFLAGS += ['-D__ANDROID_API__=' + api] if android_new_ndk else []
CPPFLAGS += ['-I%s/sysroot/usr/include' % toolchain_path]
CXXCPPFLAGS += ['-I%s/sysroot/usr/include' % toolchain_path]
path_link = '%s/platforms/android-%s/arch-%s/usr/lib' % (ANDROID_NDK_ROOT, api, arch)
LDFLAGS += [
'-z', 'now', '-z', 'relro', '-z', 'noexecstack',
'-ldl', '-lm', '-llog', '-lc', '-lgcc',
'-Wl,-rpath-link=%s,-dynamic-linker=/system/bin/linker' % path_link,
'-L' + path_link
]
# Fixes this error: DllImport unable to load library 'dlopen failed: empty/missing DT_HASH in "libmono-native.so" (built with --hash-style=gnu?)'.
LDFLAGS += ['-Wl,--hash-style=both']
CONFIGURE_FLAGS = [
'--disable-boehm',
'--disable-executables',
'--disable-iconv',
'--disable-mcs-build',
'--disable-nls',
'--enable-dynamic-btls',
'--enable-maintainer-mode',
'--enable-minimal=ssa,portability,attach,verifier,full_messages,sgen_remset'
',sgen_marksweep_par,sgen_marksweep_fixed,sgen_marksweep_fixed_par'
',sgen_copying,logging,security,shared_handles,interpreter',
'--with-btls-android-ndk=%s' % ANDROID_NDK_ROOT,
'--with-btls-android-api=%s' % api,
]
CONFIGURE_FLAGS += ['--enable-monodroid'] if WITH_MONODROID else []
CONFIGURE_FLAGS += ['--with-btls-android-ndk-asm-workaround'] if android_new_ndk else []
CONFIGURE_FLAGS += [
'--with-btls-android-cmake-toolchain=%s/build/cmake/android.toolchain.cmake' % ANDROID_NDK_ROOT,
'--with-sigaltstack=yes',
'--with-tls=pthread',
'--without-ikvm-native',
'--disable-cooperative-suspend',
'--disable-hybrid-suspend',
'--disable-crash-reporting'
]
env['_android-%s_AR' % target] = AR
env['_android-%s_AS' % target] = AS
env['_android-%s_CC' % target] = CC
env['_android-%s_CXX' % target] = CXX
env['_android-%s_CPP' % target] = CPP
env['_android-%s_CXXCPP' % target] = CXXCPP
env['_android-%s_DLLTOOL' % target] = DLLTOOL
env['_android-%s_LD' % target] = LD
env['_android-%s_OBJDUMP' % target] = OBJDUMP
env['_android-%s_RANLIB' % target] = RANLIB
env['_android-%s_CMAKE' % target] = CMAKE
env['_android-%s_STRIP' % target] = STRIP
env['_android-%s_AC_VARS' % target] = AC_VARS
env['_android-%s_CFLAGS' % target] = CFLAGS
env['_android-%s_CXXFLAGS' % target] = CXXFLAGS
env['_android-%s_CPPFLAGS' % target] = CPPFLAGS
env['_android-%s_CXXCPPFLAGS' % target] = CXXCPPFLAGS
env['_android-%s_LDFLAGS' % target] = LDFLAGS
env['_android-%s_CONFIGURE_FLAGS' % target] = CONFIGURE_FLAGS
setup_runtime_template(env, 'android', target, host_triple)
def make_standalone_toolchain(target, api):
install_dir = path_join(ANDROID_TOOLCHAINS_PREFIX, TOOLCHAIN_NAME_FMT % (target, api))
if isdir(path_join(install_dir, 'bin')):
return # Looks like it's already there, so no need to re-create it
command = path_join(ANDROID_NDK_ROOT, 'build', 'tools', 'make_standalone_toolchain.py')
args = ['--verbose', '--force', '--api=' + api, '--arch=' + AndroidTargetInfo.archs[target],
'--install-dir=' + install_dir]
run_command(command, args=args, name='make_standalone_toolchain')
def strip_libs(product, target, api):
toolchain_path = path_join(ANDROID_TOOLCHAINS_PREFIX, TOOLCHAIN_NAME_FMT % (target, api))
tools_path = path_join(toolchain_path, 'bin')
name_fmt = AndroidTargetInfo.abi_names[target] + '-%s'
STRIP = path_join(tools_path, name_fmt % 'strip')
install_dir = '%s/%s-%s-%s' % (INSTALL_DIR, product, target, CONFIGURATION)
out_libs_dir = path_join(install_dir, 'lib')
lib_files = globs(('*.a', '*.so'), dirpath=out_libs_dir)
if len(lib_files):
run_command(STRIP, args=['--strip-unneeded'] + lib_files, name='strip')
def configure(product, target):
env = { 'ANDROID_API_VERSION': get_api_version_or_min(target) }
make_standalone_toolchain(target, env['ANDROID_API_VERSION'])
setup_android_target_template(env, target)
chdir(MONO_SOURCE_ROOT)
autogen_env = environ.copy()
autogen_env['NOCONFIGURE'] = '1'
run_command(path_join(MONO_SOURCE_ROOT, 'autogen.sh'), custom_env=autogen_env, name='autogen')
build_dir = CONFIGURE_DIR + '/%s-%s-%s' % (product, target, CONFIGURATION)
mkdir_p(build_dir)
chdir(build_dir)
def str_dict_val(val):
if isinstance(val, list):
return ' '.join(val) # No need for quotes
return val
ac_vars = env['_runtime_%s-%s_AC_VARS' % (product, target)]
configure_env = env['_runtime_%s-%s_CONFIGURE_ENVIRONMENT' % (product, target)]
configure_env = [('%s=%s' % (key, str_dict_val(value))) for (key, value) in configure_env.items()]
configure_flags = env['_runtime_%s-%s_CONFIGURE_FLAGS' % (product, target)]
command = path_join(MONO_SOURCE_ROOT, 'configure')
configure_args = ac_vars + configure_env + configure_flags
run_command(command, args=configure_args, name='configure')
def make(product, target):
env = { 'ANDROID_API_VERSION': get_api_version_or_min(target) }
build_dir = CONFIGURE_DIR + '/%s-%s-%s' % (product, target, CONFIGURATION)
chdir(build_dir)
make_args = ['V=1'] if VERBOSE_MAKE else []
run_command('make', args=make_args, name='make')
run_command('make', args=['install'], name='make install')
if STRIP_LIBS:
strip_libs(product, target, env['ANDROID_API_VERSION'])
def clean(product, target):
rm_rf(
CONFIGURE_DIR + '/toolchains/%s-%s' % (product, target),
CONFIGURE_DIR + '/%s-%s-%s' % (product, target, CONFIGURATION),
CONFIGURE_DIR + '/%s-%s-%s.config.cache' % (product, target, CONFIGURATION),
INSTALL_DIR + '/%s-%s-%s' % (product, target, CONFIGURATION)
)
def set_arguments(args):
global CONFIGURATION, RELEASE, ANDROID_TOOLCHAINS_PREFIX, ANDROID_SDK_ROOT, ANDROID_NDK_ROOT, \
_ANDROID_API_VERSION, _ANDROID_CMAKE_VERSION, WITH_MONODROID, ENABLE_CXX, \
VERBOSE_MAKE, STRIP_LIBS, CONFIGURE_DIR, INSTALL_DIR, MONO_SOURCE_ROOT, CONFIGURATION
# Need to make paths absolute as we change cwd later
CONFIGURATION = args.configuration
RELEASE = (CONFIGURATION == 'release')
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
ENABLE_CXX = args.enable_cxx
VERBOSE_MAKE = args.verbose_make
STRIP_LIBS = args.strip_libs
CONFIGURE_DIR = abspath(args.configure_dir)
INSTALL_DIR = abspath(args.install_dir)
MONO_SOURCE_ROOT = abspath(args.mono_sources)
_ANDROID_API_VERSION = args.android_api_version
_ANDROID_CMAKE_VERSION = args.android_cmake_version
def main(raw_args):
import argparse
from collections import OrderedDict
from textwrap import dedent
target_indiv_values = ['armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64']
target_values = target_indiv_values + ['all']
actions = OrderedDict()
actions['configure'] = configure
actions['make'] = make
actions['clean'] = clean
parser = argparse.ArgumentParser(
description='Builds the Mono runtime for Android',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=dedent('''\
environment variables:
ANDROID_SDK_ROOT: Overrides default value for --android-sdk
ANDROID_NDK_ROOT: Overrides default value for --android-ndk
MONO_SOURCE_ROOT: Overrides default value for --mono-sources
ANDROID_HOME: Same as ANDROID_SDK_ROOT
''')
)
def custom_bool(val):
if isinstance(val, bool):
return val
if val.lower() in ('yes', 'true', 't', 'y', '1'):
return True
elif val.lower() in ('no', 'false', 'f', 'n', '0'):
return False
else:
raise argparse.ArgumentTypeError('Boolean value expected.')
home = environ.get('HOME')
android_sdk_default = environ.get('ANDROID_HOME', environ.get('ANDROID_SDK_ROOT', path_join(home, 'Android/Sdk')))
android_ndk_default = environ.get('ANDROID_NDK_ROOT', path_join(android_sdk_default, 'ndk-bundle'))
mono_sources_default = environ.get('MONO_SOURCE_ROOT', '')
default_help = dedent('default: %(default)s')
parser.add_argument('action', choices=['configure', 'make', 'clean'])
parser.add_argument('--target', choices=target_values, action='append', required=True)
parser.add_argument('--configuration', choices=['release', 'debug'], default='release', help=default_help)
parser.add_argument('--toolchains-prefix', default=path_join(home, 'android-toolchains'), help=default_help)
parser.add_argument('--android-sdk', default=android_sdk_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-cmake-version', default='autodetect', help=default_help)
parser.add_argument('--enable-cxx', action='store_true', default=False, help=default_help)
parser.add_argument('--verbose-make', action='store_true', default=False, help=default_help)
parser.add_argument('--strip-libs', type=custom_bool, default=True, help=default_help)
parser.add_argument('--with-monodroid', type=custom_bool, default=True, help=default_help)
parser.add_argument('--configure-dir', default=path_join(home, 'mono-configs'), help=default_help)
parser.add_argument('--install-dir', default=path_join(home, 'mono-installs'), help=default_help)
if mono_sources_default:
parser.add_argument('--mono-sources', default=mono_sources_default, help=default_help)
else:
parser.add_argument('--mono-sources', required=True)
args = parser.parse_args(raw_args)
action = args.action
targets = args.target
set_arguments(args)
if not isdir(MONO_SOURCE_ROOT):
print('Mono sources directory not found: ' + MONO_SOURCE_ROOT)
exit(1)
android_targets = []
if 'all' in targets:
android_targets = target_indiv_values[:]
else:
for target in targets:
if not target in android_targets:
android_targets += [target]
action_fn = actions[action]
try:
for target in android_targets:
action_fn('android', target)
except MonoBuildError as e:
exit(e.message)
if __name__ == '__main__':
from sys import argv
main(argv[1:])

80
cmd_utils.py Normal file
View File

@ -0,0 +1,80 @@
def custom_bool(val):
if isinstance(val, bool):
return val
if val.lower() in ('yes', 'true', 't', 'y', '1'):
return True
elif val.lower() in ('no', 'false', 'f', 'n', '0'):
return False
else:
from argparse import ArgumentTypeError
raise ArgumentTypeError('Boolean value expected.')
def build_arg_parser(description, env_vars={}):
from argparse import ArgumentParser, RawDescriptionHelpFormatter
from textwrap import dedent
base_env_vars = {
'MONO_SOURCE_ROOT': 'Overrides default value for --mono-sources',
}
env_vars_text = '\n'.join([' %s: %s' % (var, desc) for var, desc in env_vars.items()])
base_env_vars_text = '\n'.join([' %s: %s' % (var, desc) for var, desc in base_env_vars.items()])
epilog=dedent('''\
environment variables:
%s
%s
''' % (env_vars_text, base_env_vars_text))
return ArgumentParser(
description=description,
formatter_class=RawDescriptionHelpFormatter,
epilog=epilog
)
def add_base_arguments(parser, default_help):
import os
from os.path import join as path_join
home = os.environ.get('HOME')
mono_sources_default = os.environ.get('MONO_SOURCE_ROOT', '')
parser.add_argument('--verbose-make', action='store_true', default=False, help=default_help)
parser.add_argument('--configure-dir', default=path_join(home, 'mono-configs'), help=default_help)
parser.add_argument('--install-dir', default=path_join(home, 'mono-installs'), help=default_help)
if mono_sources_default:
parser.add_argument('--mono-sources', default=mono_sources_default, help=default_help)
else:
parser.add_argument('--mono-sources', required=True)
parser.add_argument('--mxe-prefix', default='/usr', help=default_help)
def add_runtime_arguments(parser, default_help):
add_base_arguments(parser, default_help)
parser.add_argument('--configuration', choices=['release', 'debug'], default='release', help=default_help)
parser.add_argument('--enable-cxx', action='store_true', default=False, help=default_help)
parser.add_argument('--strip-libs', type=custom_bool, default=True, help='Strip the libraries if possible after running make.\n' + default_help)
def expand_input_targets(input_targets, target_shortcuts=[]):
targets = []
for shortcut in target_shortcuts.keys():
if shortcut in input_targets:
targets += target_shortcuts[shortcut][:]
# The shortcuts options ('all-*') have already been handled. Remove them this way as there may be duplicates.
input_targets = [t for t in input_targets if not t in target_shortcuts]
for target in input_targets:
if not target in targets:
targets += [target]
return targets

188
desktop.py Executable file
View File

@ -0,0 +1,188 @@
#!/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
# TODO: mono cross-compilers
target_platforms = ['linux', 'windows', 'osx']
targets = {
'linux': ['i686', 'x86_64'],
'windows': ['i686', 'x86_64'],
'osx': ['x86_64']
}
host_triples = {
'linux': '%s-linux-gnu',
'windows': '%s-w64-mingw32',
'osx': '%s-apple-darwin',
}
def is_cross_compiling(target_platform: str) -> bool:
return (sys.platform == 'darwin' and target_platform != 'osx') or \
(sys.platform in ['linux', 'linux2', 'cygwin'] and target_platform != 'linux')
def setup_desktop_template(env: dict, opts: RuntimeOpts, product: str, target_platform: str, target: str):
host_triple = host_triples[target_platform] % target
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':
mxe_bin = path_join(opts.mxe_prefix, 'bin')
env['_%s-%s_PATH' % (product, target)] = mxe_bin
name_fmt = target + '-w64-mingw32-%s'
env['_%s-%s_AR' % (product, target)] = path_join(mxe_bin, name_fmt % 'ar')
env['_%s-%s_AS' % (product, target)] = path_join(mxe_bin, name_fmt % 'as')
env['_%s-%s_CC' % (product, target)] = path_join(mxe_bin, name_fmt % 'gcc')
env['_%s-%s_CXX' % (product, target)] = path_join(mxe_bin, name_fmt % 'g++')
env['_%s-%s_DLLTOOL' % (product, target)] = path_join(mxe_bin, name_fmt % 'dlltool')
env['_%s-%s_LD' % (product, target)] = path_join(mxe_bin, name_fmt % 'ld')
env['_%s-%s_OBJDUMP' % (product, target)] = path_join(mxe_bin, name_fmt % 'objdump')
env['_%s-%s_RANLIB' % (product, target)] = path_join(mxe_bin, name_fmt % 'ranlib')
env['_%s-%s_STRIP' % (product, target)] = path_join(mxe_bin, name_fmt % 'strip')
CONFIGURE_FLAGS += [
'--enable-static-gcc-libs'
]
else:
env['_%s-%s_CC' % (product, target)] = 'cc'
env['_%s-%s_CONFIGURE_FLAGS' % (product, target)] = CONFIGURE_FLAGS
runtime.setup_runtime_template(env, opts, product, target, host_triple)
def strip_libs(opts: RuntimeOpts, product: str, target_platform: str, target: str):
if is_cross_compiling(target_platform):
if target_platform == 'windows':
mxe_bin = path_join(opts.mxe_prefix, 'bin')
name_fmt = target + '-w64-mingw32-%s'
strip = path_join(mxe_bin, name_fmt % 'strip')
elif target_platform == 'osx':
assert False # TODO osxcross
else:
strip = 'strip'
install_dir = path_join(opts.install_dir, '%s-%s-%s' % (product, target, opts.configuration))
out_libs_dir = path_join(install_dir, 'lib')
lib_files = globs(('*.a', '*.so'), dirpath=out_libs_dir)
if len(lib_files):
run_command(strip, args=['--strip-unneeded'] + lib_files, name='strip')
if target_platform == 'windows':
out_bin_dir = path_join(install_dir, 'bin')
dll_files = globs(('*.dll',), dirpath=out_bin_dir)
if len(dll_files):
run_command(strip, args=['--strip-unneeded'] + dll_files, name='strip')
def configure(opts: RuntimeOpts, product: str, target_platform: str, target: str):
env = {}
setup_desktop_template(env, opts, product, target_platform, 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: RuntimeOpts, product: str, target_platform: str, target: str):
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')
if opts.strip_libs:
strip_libs(opts, product, target_platform, target)
def clean(opts: RuntimeOpts, product: str, target_platform: 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 collections import OrderedDict
from typing import Callable
actions = OrderedDict()
actions['configure'] = configure
actions['make'] = make
actions['clean'] = clean
parser = cmd_utils.build_arg_parser(description='Builds the Mono runtime for the Desktop')
subparsers = parser.add_subparsers(dest='platform')
default_help = 'default: %(default)s'
for target_platform in target_platforms:
target_platform_subparser = subparsers.add_parser(target_platform)
target_platform_subparser.add_argument('action', choices=['configure', 'make', 'clean'])
target_platform_subparser.add_argument('--target', choices=targets[target_platform], action='append', required=True)
cmd_utils.add_runtime_arguments(parser, default_help)
args = parser.parse_args(raw_args)
input_action = args.action
input_target_platform = args.platform
input_targets = args.target
opts = runtime_opts_from_args(args)
if not os.path.isdir(opts.mono_source_root):
print('Mono sources directory not found: ' + opts.mono_source_root)
sys.exit(1)
if is_cross_compiling(input_target_platform) and input_target_platform == 'osx':
raise RuntimeError('Cross-compiling for macOS is not currently supported') # TODO: osxcross
if is_cross_compiling(input_target_platform) and sys.platform == 'darwin':
raise RuntimeError('Cross-compiling from macOS is not supported')
action = actions[input_action]
try:
for target in input_targets:
action(opts, 'desktop-%s' % input_target_platform, input_target_platform, target)
except BuildError as e:
sys.exit(e.message)
if __name__ == '__main__':
from sys import argv
main(argv[1:])

118
llvm.py Executable file
View File

@ -0,0 +1,118 @@
#!/usr/bin/python
import os
import sys
from os.path import join as path_join
from options import *
from os_utils import *
target_values = ['llvm64', 'llvmwin64']
mxe_targets = {'llvmwin64': {'arch': 'x86_64', 'mxe': 'mxe-Win64'}}
def make(opts: BaseOpts, target: str):
stamp_file = path_join(opts.configure_dir, '.stamp-%s-make' % target)
if os.path.isfile(stamp_file):
return
build_dir = path_join(opts.configure_dir, 'llvm-%s' % target)
install_dir = path_join(opts.install_dir, 'llvm-%s' % target)
mkdir_p(build_dir)
mkdir_p(install_dir)
CMAKE_ARGS = []
if target in mxe_targets:
mxe = mxe_targets[target]['mxe']
arch = mxe_targets[target]['arch']
CMAKE_ARGS += [
'-DCMAKE_EXE_LINKER_FLAGS="-static"',
'-DCROSS_TOOLCHAIN_FLAGS_NATIVE=-DCMAKE_TOOLCHAIN_FILE=%s/external/llvm/cmake/modules/NATIVE.cmake' % opts.mono_source_root,
'-DCMAKE_TOOLCHAIN_FILE=%s/external/llvm/cmake/modules/%s.cmake' % (opts.mono_source_root, mxe),
'-DLLVM_ENABLE_THREADS=Off',
'-DLLVM_BUILD_EXECUTION_ENGINE=Off'
]
if sys.platform == 'darwin':
mingw_zlib_prefix = '%s/opt/mingw-zlib/usr' % opts.mxe_prefix
if not os.path.isfile(mingw_zlib_prefix):
mingw_zlib_prefix = opts.mxe_prefix
CMAKE_ARGS += [
'-DZLIB_ROOT=%s/%s-w64-mingw32' % (mingw_zlib_prefix, arch),
'-DZLIB_LIBRARY=%s/%s-w64-mingw32/lib/libz.a' % (mingw_zlib_prefix, arch),
'-DZLIB_INCLUDE_DIR=%s/%s-w64-mingw32/include' % (mingw_zlib_prefix, arch)
]
replace_in_new_file(
src_file='%s/sdks/builds/%s.cmake.in' % (opts.mono_source_root, mxe),
search='@MXE_PATH@', replace=opts.mxe_prefix,
dst_file='%s/external/llvm/cmake/modules/%s.cmake' % (opts.mono_source_root, mxe)
)
CMAKE_ARGS += [os.environ.get('llvm-%s_CMAKE_ARGS' % target, '')]
make_args = [
'-C', '%s/llvm' % opts.mono_source_root,
'-f', 'build.mk', 'install-llvm',
'LLVM_BUILD=%s' % build_dir,
'LLVM_PREFIX=%s' % install_dir,
'LLVM_CMAKE_ARGS=%s' % ' '.join([a for a in CMAKE_ARGS if a])
]
make_args += ['V=1'] if opts.verbose_make else []
run_command('make', args=make_args, name='make')
touch(stamp_file)
def clean(opts: BaseOpts, target: str):
build_dir = path_join(opts.configure_dir, 'llvm-%s' % target)
install_dir = path_join(opts.install_dir, 'llvm-%s' % target)
stamp_file = path_join(opts.configure_dir, '.stamp-%s-make' % target)
rm_rf(stamp_file)
make_args = [
'-C', '%s/llvm' % opts.mono_source_root,
'-f', 'build.mk', 'clean-llvm',
'LLVM_BUILD=%s' % build_dir,
'LLVM_PREFIX=%s' % install_dir
]
make_args += ['V=1'] if opts.verbose_make else []
run_command('make', args=make_args, name='make clean')
def main(raw_args):
import cmd_utils
parser = cmd_utils.build_arg_parser(description='Builds LLVM for Mono')
default_help = 'default: %(default)s'
parser.add_argument('action', choices=['make', 'clean'])
parser.add_argument('--target', choices=target_values, action='append', required=True)
cmd_utils.add_base_arguments(parser, default_help)
args = parser.parse_args(raw_args)
opts = base_opts_from_args(args)
targets = args.target
for target in targets:
action = { 'make': make, 'clean': clean }[args.action]
action(opts, target)
if __name__ == '__main__':
from sys import argv
main(argv[1:])

79
options.py Normal file
View File

@ -0,0 +1,79 @@
from dataclasses import dataclass
from os.path import abspath
@dataclass
class BaseOpts:
verbose_make: bool
configure_dir: str
install_dir: str
mono_source_root: str
mxe_prefix: str
@dataclass
class RuntimeOpts(BaseOpts):
configuration: str
release: bool
enable_cxx: bool
strip_libs: bool
@dataclass
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 BclOpts(BaseOpts):
tests: bool
# Need to make paths absolute as we change cwd
def base_opts_from_args(args):
from os.path import abspath
return BaseOpts(
verbose_make = args.verbose_make,
configure_dir = abspath(args.configure_dir),
install_dir = abspath(args.install_dir),
mono_source_root = abspath(args.mono_sources),
mxe_prefix = args.mxe_prefix
)
def runtime_opts_from_args(args):
return RuntimeOpts(
**vars(base_opts_from_args(args)),
configuration = args.configuration,
release = (args.configuration == 'release'),
enable_cxx = args.enable_cxx,
strip_libs = args.strip_libs
)
def android_opts_from_args(args):
return AndroidOpts(
**vars(runtime_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 bcl_opts_from_args(args):
return BclOpts(
**vars(base_opts_from_args(args)),
tests = args.tests
)

137
os_utils.py Normal file
View File

@ -0,0 +1,137 @@
import os
import os.path
class BuildError(Exception):
'''Generic exception for custom build errors'''
def __init__(self, msg):
super(BuildError, self).__init__(msg)
self.message = msg
def run_command(command, args=[], cwd=None, env=None, name='command'):
def cmd_args_to_str(cmd_args):
return ' '.join([arg if not ' ' in arg else '"%s"' % arg for arg in cmd_args])
assert isinstance(command, str) and isinstance(args, list)
args = [command] + args
check_call_args = {}
if cwd is not None:
check_call_args['cwd'] = cwd
if env is not None:
check_call_args['env'] = env
import subprocess
try:
print('Running command \'%s\': %s' % (name, subprocess.list2cmdline(args)))
subprocess.check_call(args, **check_call_args)
print('Command \'%s\' completed successfully' % name)
except subprocess.CalledProcessError as e:
raise BuildError('\'%s\' exited with error code: %s' % (name, e.returncode))
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)
output = proc.communicate()[0]
return dict(line.split('=', 1) for line in output.decode().split('\x00') if line)
# Creates the directory if no other file or directory with the same path exists
def mkdir_p(path):
if not os.path.exists(path):
print('creating directory: ' + path)
os.makedirs(path)
# Remove files and/or directories recursively
def rm_rf(*paths):
from shutil import rmtree
for path in paths:
if os.path.isfile(path):
print('removing file: ' + path)
os.remove(path)
elif os.path.isdir(path):
print('removing directory and its contents: ' + path)
rmtree(path)
ENV_PATH_SEP = ';' if os.name == 'nt' else ':'
def find_executable(name) -> str:
is_windows = os.name == 'nt'
windows_exts = ENV_PATH_SEP.split(os.environ['PATHEXT']) if is_windows else None
path_dirs = ENV_PATH_SEP.split(os.environ['PATH'])
search_dirs = path_dirs + [os.getcwd()] # cwd is last in the list
for dir in search_dirs:
path = os.path.join(dir, name)
if is_windows:
for extension in windows_exts:
path_with_ext = path + extension
if os.path.isfile(path_with_ext) and os.access(path_with_ext, os.X_OK):
return path_with_ext
else:
if os.path.isfile(path) and os.access(path, os.X_OK):
return path
return ''
def replace_in_new_file(src_file, search, replace, dst_file):
with open(src_file, 'r') as file:
content = file.read()
content = content.replace(search, replace)
with open(dst_file, 'w') as file:
file.write(content)
def replace_in_file(filepath, search, replace):
replace_in_new_file(src_file=filepath, search=search, replace=replace, dst_file=filepath)
def touch(filepath: str):
import pathlib
pathlib.Path(filepath).touch()
def get_emsdk_root():
# Shamelessly copied from Godot's detect.py
em_config_file = os.getenv('EM_CONFIG') or os.path.expanduser('~/.emscripten')
if not os.path.exists(em_config_file):
raise BuildError("Emscripten configuration file '%s' does not exist" % em_config_file)
with open(em_config_file) as f:
em_config = {}
try:
# Emscripten configuration file is a Python file with simple assignments.
exec(f.read(), em_config)
except StandardError as e:
raise BuildError("Emscripten configuration file '%s' is invalid:\n%s" % (em_config_file, e))
if 'BINARYEN_ROOT' in em_config and os.path.isdir(os.path.join(em_config.get('BINARYEN_ROOT'), 'emscripten')):
# New style, emscripten path as a subfolder of BINARYEN_ROOT
return os.path.join(em_config.get('BINARYEN_ROOT'), 'emscripten')
elif 'EMSCRIPTEN_ROOT' in em_config:
# Old style (but can be there as a result from previous activation, so do last)
return em_config.get('EMSCRIPTEN_ROOT')
else:
raise BuildError("'BINARYEN_ROOT' or 'EMSCRIPTEN_ROOT' missing in Emscripten configuration file '%s'" % em_config_file)
def globs(pathnames, dirpath='.'):
import glob
files = []
for pathname in pathnames:
files.extend(glob.glob(os.path.join(dirpath, pathname)))
return files

37
patch_emscripten.py Executable file
View File

@ -0,0 +1,37 @@
#!/usr/bin/python
def main(raw_args):
import os
import cmd_utils
from os_utils import get_emsdk_root
parser = cmd_utils.build_arg_parser(description='Apply patches to the active Emscripten SDK')
default_help = 'default: %(default)s'
mono_sources_default = os.environ.get('MONO_SOURCE_ROOT', '')
if mono_sources_default:
parser.add_argument('--mono-sources', default=mono_sources_default, help=default_help)
else:
parser.add_argument('--mono-sources', required=True)
args = parser.parse_args(raw_args)
mono_source_root = args.mono_sources
emsdk_root = get_emsdk_root()
patches = [
'%s/sdks/builds/fix-emscripten-8511.diff' % mono_source_root,
'%s/sdks/builds/emscripten-pr-8457.diff' % mono_source_root
]
from subprocess import Popen
for patch in patches:
proc = Popen('bash -c \'patch -N -p1 < %s; exit 0\'' % patch, cwd=emsdk_root, shell=True)
if __name__ == '__main__':
from sys import argv
main(argv[1:])

39
patch_mono.py Executable file
View File

@ -0,0 +1,39 @@
#!/usr/bin/python
def main(raw_args):
import cmd_utils
import os
import os.path
from os_utils import get_emsdk_root
parser = cmd_utils.build_arg_parser(description='Apply patches to the Mono source tree')
default_help = 'default: %(default)s'
mono_sources_default = os.environ.get('MONO_SOURCE_ROOT', '')
if mono_sources_default:
parser.add_argument('--mono-sources', default=mono_sources_default, help=default_help)
else:
parser.add_argument('--mono-sources', required=True)
args = parser.parse_args(raw_args)
this_script_dir = os.path.dirname(os.path.realpath(__file__))
patches_dir = os.path.join(this_script_dir, 'patches')
mono_source_root = args.mono_sources
patches = [
'fix-mono-android-tkill.diff'
]
from subprocess import Popen
for patch in patches:
proc = Popen('bash -c \'patch -N -p1 < %s; exit 0\'' % os.path.join(patches_dir, patch), cwd=mono_source_root, shell=True)
if __name__ == '__main__':
from sys import argv
main(argv[1:])

View File

@ -0,0 +1,70 @@
diff --git a/libgc/include/private/gcconfig.h b/libgc/include/private/gcconfig.h
index e2bdf13ac3e..f962200ba4e 100644
--- a/libgc/include/private/gcconfig.h
+++ b/libgc/include/private/gcconfig.h
@@ -2255,6 +2255,14 @@
# define GETPAGESIZE() getpagesize()
# endif
+#if defined(HOST_ANDROID) && !(__ANDROID_API__ >= 23) \
+ && ((defined(MIPS) && (CPP_WORDSZ == 32)) \
+ || defined(ARM32) || defined(I386) /* but not x32 */)
+ /* tkill() exists only on arm32/mips(32)/x86. */
+ /* NDK r11+ deprecates tkill() but keeps it for Mono clients. */
+# define USE_TKILL_ON_ANDROID
+#endif
+
# if defined(SUNOS5) || defined(DRSNX) || defined(UTS4)
/* OS has SVR4 generic features. Probably others also qualify. */
# define SVR4
diff --git a/libgc/pthread_stop_world.c b/libgc/pthread_stop_world.c
index f93ce26b562..4a49a6d578c 100644
--- a/libgc/pthread_stop_world.c
+++ b/libgc/pthread_stop_world.c
@@ -336,7 +336,7 @@ void GC_push_all_stacks()
pthread_t GC_stopping_thread;
int GC_stopping_pid;
-#ifdef HOST_ANDROID
+#ifdef USE_TKILL_ON_ANDROID
static
int android_thread_kill(pid_t tid, int sig)
{
diff --git a/mono/metadata/threads.c b/mono/metadata/threads.c
index ad9b8823f8f..3542b32b540 100644
--- a/mono/metadata/threads.c
+++ b/mono/metadata/threads.c
@@ -77,8 +77,12 @@ mono_native_thread_join_handle (HANDLE thread_handle, gboolean close_handle);
#include <zircon/syscalls.h>
#endif
-#if defined(HOST_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64)
-#define USE_TKILL_ON_ANDROID 1
+#if defined(HOST_ANDROID) && !(__ANDROID_API__ >= 23) \
+ && ((defined(MIPS) && (CPP_WORDSZ == 32)) \
+ || defined(ARM32) || defined(I386) /* but not x32 */)
+ /* tkill() exists only on arm32/mips(32)/x86. */
+ /* NDK r11+ deprecates tkill() but keeps it for Mono clients. */
+# define USE_TKILL_ON_ANDROID
#endif
#ifdef HOST_ANDROID
diff --git a/mono/utils/mono-threads-posix.c b/mono/utils/mono-threads-posix.c
index 3e4bf93de5f..79c9f731fe7 100644
--- a/mono/utils/mono-threads-posix.c
+++ b/mono/utils/mono-threads-posix.c
@@ -31,8 +31,12 @@
#include <errno.h>
-#if defined(HOST_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64)
-#define USE_TKILL_ON_ANDROID 1
+#if defined(HOST_ANDROID) && !(__ANDROID_API__ >= 23) \
+ && ((defined(MIPS) && (CPP_WORDSZ == 32)) \
+ || defined(ARM32) || defined(I386) /* but not x32 */)
+ /* tkill() exists only on arm32/mips(32)/x86. */
+ /* NDK r11+ deprecates tkill() but keeps it for Mono clients. */
+# define USE_TKILL_ON_ANDROID
#endif
#ifdef USE_TKILL_ON_ANDROID

62
reference_assemblies.py Executable file
View File

@ -0,0 +1,62 @@
#!/usr/bin/python
from os.path import join as path_join
from options import *
from os_utils import *
def build(opts: BaseOpts):
build_dir = '%s/mcs/class/reference-assemblies' % opts.mono_source_root
install_dir = path_join(opts.install_dir, 'reference-assemblies')
mkdir_p(install_dir)
make_args = ['-C', build_dir, 'build-reference-assemblies']
make_args += ['V=1'] if opts.verbose_make else []
run_command('make', args=make_args, name='make build-reference-assemblies')
def install(opts: BaseOpts):
build_dir = '%s/mcs/class/reference-assemblies' % opts.mono_source_root
install_dir = path_join(opts.install_dir, 'reference-assemblies')
mkdir_p(install_dir)
make_args = ['-C', build_dir, 'install-local', 'DESTDIR=%s' % install_dir, 'prefix=/']
make_args += ['V=1'] if opts.verbose_make else []
run_command('make', args=make_args, name='make install-local')
def clean(opts: BaseOpts):
install_dir = path_join(opts.install_dir, 'reference-assemblies')
rm_rf(install_dir)
def main(raw_args):
import cmd_utils
actions = {
'build': build,
'install': install,
'clean': clean
}
parser = cmd_utils.build_arg_parser(description='Copy the reference assemblies')
default_help = 'default: %(default)s'
parser.add_argument('action', choices=actions.keys())
cmd_utils.add_base_arguments(parser, default_help)
args = parser.parse_args(raw_args)
opts = base_opts_from_args(args)
action = actions[args.action]
action(opts)
if __name__ == '__main__':
from sys import argv
main(argv[1:])

159
runtime.py Normal file
View File

@ -0,0 +1,159 @@
import os
from os.path import join as path_join
from options import RuntimeOpts
from os_utils import *
def setup_runtime_template(env: dict, opts: RuntimeOpts, product: str, target: str, host_triple: str):
BITNESS = ''
if any(s in host_triple for s in ['i686', 'i386']):
BITNESS = '-m32'
elif 'x86_64' in host_triple:
BITNESS = '-m64'
CFLAGS = []
CFLAGS += ['-O2', '-g'] if opts.release else ['-O0', '-ggdb3', '-fno-omit-frame-pointer']
CFLAGS += env.get('_%s-%s_CFLAGS' % (product, target), [])
CFLAGS += env.get('%s-%s_CFLAGS' % (product, target), [])
CFLAGS += [BITNESS] if BITNESS else []
CXXFLAGS = []
CXXFLAGS += ['-O2', '-g'] if opts.release else ['-O0', '-ggdb3', '-fno-omit-frame-pointer']
CXXFLAGS += env.get('_%s-%s_CXXFLAGS' % (product, target), [])
CXXFLAGS += env.get('%s-%s_CXXFLAGS' % (product, target), [])
CXXFLAGS += [BITNESS] if BITNESS else []
CPPFLAGS = []
CPPFLAGS += ['-O2', '-g'] if opts.release else ['-O0', '-ggdb3', '-fno-omit-frame-pointer']
CPPFLAGS += env.get('_%s-%s_CPPFLAGS' % (product, target), [])
CPPFLAGS += env.get('%s-%s_CPPFLAGS' % (product, target), [])
CPPFLAGS += [BITNESS] if BITNESS else []
CXXCPPFLAGS = []
CXXCPPFLAGS += ['-O2', '-g'] if opts.release else ['-O0', '-ggdb3', '-fno-omit-frame-pointer']
CXXCPPFLAGS += env.get('_%s-%s_CXXCPPFLAGS' % (product, target), [])
CXXCPPFLAGS += env.get('%s-%s_CXXCPPFLAGS' % (product, target), [])
CXXCPPFLAGS += [BITNESS] if BITNESS else []
LDFLAGS = []
LDFLAGS += env.get('_%s-%s_LDFLAGS' % (product, target), [])
LDFLAGS += env.get('%s-%s_LDFLAGS' % (product, target), [])
AC_VARS = []
AC_VARS += env.get('_%s-%s_AC_VARS' % (product, target), [])
AC_VARS += env.get('%s-%s_AC_VARS' % (product, target), [])
CONFIGURE_ENVIRONMENT = {}
def set_product_env_var(var_name):
val = env.get('_%s-%s_%s' % (product, target, var_name), '')
if val:
CONFIGURE_ENVIRONMENT[var_name] = val
set_product_env_var('AR')
set_product_env_var('AS')
set_product_env_var('CC')
set_product_env_var('CPP')
set_product_env_var('CXX')
set_product_env_var('CXXCPP')
set_product_env_var('DLLTOOL')
set_product_env_var('LD')
set_product_env_var('OBJDUMP')
set_product_env_var('RANLIB')
set_product_env_var('CMAKE')
set_product_env_var('STRIP')
CONFIGURE_ENVIRONMENT['CFLAGS'] = CFLAGS
CONFIGURE_ENVIRONMENT['CXXFLAGS'] = CXXFLAGS
CONFIGURE_ENVIRONMENT['CPPFLAGS'] = CPPFLAGS
CONFIGURE_ENVIRONMENT['CXXCPPFLAGS'] = CXXCPPFLAGS
CONFIGURE_ENVIRONMENT['LDFLAGS'] = LDFLAGS
CONFIGURE_ENVIRONMENT.update(env.get('_%s-%s_CONFIGURE_ENVIRONMENT' % (product, target), {}))
CONFIGURE_ENVIRONMENT.update(env.get('%s-%s_CONFIGURE_ENVIRONMENT' % (product, target), {}))
CONFIGURE_FLAGS = []
CONFIGURE_FLAGS += ['--host=%s' % host_triple] if host_triple else []
CONFIGURE_FLAGS += ['--cache-file=%s/%s-%s-%s.config.cache' % (opts.configure_dir, product, target, opts.configuration)]
CONFIGURE_FLAGS += ['--prefix=%s/%s-%s-%s' % (opts.install_dir, product, target, opts.configuration)]
CONFIGURE_FLAGS += ['--enable-cxx'] if opts.enable_cxx else []
CONFIGURE_FLAGS += env.get('_cross-runtime_%s-%s_CONFIGURE_FLAGS' % (product, target), [])
CONFIGURE_FLAGS += env.get('_%s-%s_CONFIGURE_FLAGS' % (product, target), [])
CONFIGURE_FLAGS += env.get('%s-%s_CONFIGURE_FLAGS' % (product, target), [])
env['_runtime_%s-%s_AC_VARS' % (product, target)] = AC_VARS
env['_runtime_%s-%s_CONFIGURE_ENVIRONMENT' % (product, target)] = CONFIGURE_ENVIRONMENT
env['_runtime_%s-%s_CONFIGURE_FLAGS' % (product, target)] = CONFIGURE_FLAGS
def setup_runtime_cross_template(env: dict, opts: RuntimeOpts, product: str, target: str, host_triple: str,
target_triple: str, device_target: str, llvm: str, offsets_dumper_abi: str):
CONFIGURE_FLAGS = [
'--target=%s' % target_triple,
'--with-cross-offsets=%s.h' % target_triple,
'--with-llvm=%s/%s' % (opts.install_dir, llvm)
]
env['_cross-runtime_%s-%s_CONFIGURE_FLAGS' % (product, target)] = CONFIGURE_FLAGS
# Setup offsets-tool-py
run_command('make', ['-C', '%s/tools/offsets-tool-py' % opts.mono_source_root, 'setup'], name='make offsets-tool-py')
# Run offsets-tool in its virtual env
virtualenv_vars = source('%s/tools/offsets-tool-py/offtool/bin/activate' % opts.mono_source_root)
offsets_tool_env = os.environ.copy()
offsets_tool_env.update(virtualenv_vars)
build_dir = '%s/%s-%s-%s' % (opts.configure_dir, product, target, opts.configuration)
mkdir_p(build_dir)
run_command('python3', [
'%s/tools/offsets-tool-py/offsets-tool.py' % opts.mono_source_root,
'--targetdir=%s/%s-%s-%s' % (opts.configure_dir, product, device_target, opts.configuration),
'--abi=%s' % offsets_dumper_abi,
'--monodir=%s' % opts.mono_source_root,
'--outfile=%s/%s.h' % (build_dir, target_triple)
] + env['_%s-%s_OFFSETS_DUMPER_ARGS' % (product, target)],
env=offsets_tool_env, name='offsets-tool')
# Runtime template
setup_runtime_template(env, opts, product, target, host_triple)
def run_autogen(opts: RuntimeOpts):
autogen_env = os.environ.copy()
autogen_env['NOCONFIGURE'] = '1'
if not find_executable('glibtoolize') and 'CUSTOM_GLIBTOOLIZE_PATH' in os.environ:
autogen_env['PATH'] = os.environ['CUSTOM_GLIBTOOLIZE_PATH'] + ':' + autogen_env['PATH']
run_command(os.path.join(opts.mono_source_root, 'autogen.sh'), cwd=opts.mono_source_root, env=autogen_env, name='autogen')
def run_configure(env: dict, opts: RuntimeOpts, product: str, target: str):
build_dir = path_join(opts.configure_dir, '%s-%s-%s' % (product, target, opts.configuration))
mkdir_p(build_dir)
def str_dict_val(val):
if isinstance(val, list):
return ' '.join(val) # Don't need to surround with quotes
return val
ac_vars = env['_runtime_%s-%s_AC_VARS' % (product, target)]
configure_env_args = env['_runtime_%s-%s_CONFIGURE_ENVIRONMENT' % (product, target)]
configure_env_args = [('%s=%s' % (key, str_dict_val(value))) for (key, value) in configure_env_args.items()]
configure_flags = env['_runtime_%s-%s_CONFIGURE_FLAGS' % (product, target)]
configure = path_join(opts.mono_source_root, 'configure')
configure_args = ac_vars + configure_env_args + configure_flags
configure_env = os.environ.copy()
target_extra_path = env.get('_%s-%s_PATH' % (product, target), '')
if target_extra_path:
configure_env['PATH'] += ':' + target_extra_path
run_command(configure, args=configure_args, cwd=build_dir, env=configure_env, name='configure')

231
wasm.py Executable file
View File

@ -0,0 +1,231 @@
#!/usr/bin/python
import os
import os.path
import runtime
import sys
from options import *
from os_utils import *
from os.path import join as path_join
runtime_targets = ['runtime', 'runtime-threads', 'runtime-dynamic']
cross_targets = [] # ['cross'] # TODO
cross_mxe_targets = [] # ['cross-win'] # TODO
def is_cross(target) -> bool:
return target in cross_targets or is_cross_mxe(target)
def is_cross_mxe(target) -> bool:
return target in cross_mxe_targets
def setup_wasm_target_template(env: dict, opts: RuntimeOpts, target: str):
extra_target_envs = {
'runtime-threads': {
'wasm_runtime-threads_CFLAGS': ['-s', 'USE_PTHREADS=1', '-pthread'],
'wasm_runtime-threads_CXXFLAGS': ['-s', 'USE_PTHREADS=1', '-pthread']
},
'runtime-dynamic': {
'wasm_runtime-dynamic_CFLAGS': ['-s', 'WASM_OBJECT_FILES=0'],
'wasm_runtime-dynamic_CXXFLAGS': ['-s', 'WASM_OBJECT_FILES=0']
}
}
if target in extra_target_envs:
env.update(extra_target_envs[target])
CFLAGS = ['-fexceptions']
CFLAGS += ['-Os', '-g'] if opts.release else ['-O0', '-ggdb3', '-fno-omit-frame-pointer']
CXXFLAGS = CFLAGS + ['-s', 'DISABLE_EXCEPTION_CATCHING=0']
CONFIGURE_FLAGS = [
'--disable-mcs-build',
'--disable-nls',
'--disable-boehm',
'--disable-btls',
'--with-lazy-gc-thread-creation=yes',
'--with-libgc=none',
'--disable-executables',
'--disable-support-build',
'--disable-visibility-hidden',
'--enable-maintainer-mode',
'--enable-minimal=ssa,com,jit,reflection_emit_save,portability,assembly_remapping,attach,verifier,full_messages,appdomains,security,sgen_marksweep_conc,sgen_split_nursery,sgen_gc_bridge,logging,remoting,shared_perfcounters,sgen_debug_helpers,soft_debug,interpreter,assert_messages,cleanup,mdb,gac',
'--host=wasm32',
'--enable-llvm-runtime',
'--enable-icall-export',
'--disable-icall-tables',
'--disable-crash-reporting',
'--with-bitcode=yes'
]
CONFIGURE_FLAGS += ['--enable-cxx'] if opts.enable_cxx else []
CONFIGURE_FLAGS += [
'--cache-file=%s/wasm-%s-%s.config.cache' % (opts.configure_dir, target, opts.configuration),
'--prefix=%s/wasm-%s-%s' % (opts.install_dir, target, opts.configuration),
'CFLAGS=%s %s' % (' '.join(CFLAGS), env.get('wasm_%s_CFLAGS' % target, '')),
'CXXFLAGS=%s %s' % (' '.join(CXXFLAGS), env.get('$$(wasm_%s_CXXFLAGS' % target, ''))
]
CONFIGURE_FLAGS += env.get('wasm_%s_CONFIGURE_FLAGS' % target, [])
env['_wasm_%s_CONFIGURE_FLAGS' % target] = CONFIGURE_FLAGS
env['_wasm_%s_AC_VARS' % target] = ['ac_cv_func_shm_open_working_with_mmap=no']
def wasm_run_configure(env: dict, opts: RuntimeOpts, product: str, target: str, emsdk_root: str):
build_dir = path_join(opts.configure_dir, '%s-%s-%s' % (product, target, opts.configuration))
mkdir_p(build_dir)
def str_dict_val(val):
if isinstance(val, list):
return ' '.join(val) # Don't need to surround with quotes
return val
ac_vars = env['_%s_%s_AC_VARS' % (product, target)]
configure_flags = env['_%s_%s_CONFIGURE_FLAGS' % (product, target)]
configure = path_join(opts.mono_source_root, 'configure')
configure_args = ac_vars + configure_flags
configure_env = os.environ.copy()
target_extra_path = env.get('_%s-%s_PATH' % (product, target), '')
if target_extra_path:
configure_env['PATH'] += ':' + target_extra_path
configure_env['PATH'] = emsdk_root + ':' + configure_env['PATH']
run_command('emconfigure', args=[configure] + configure_args, cwd=build_dir, env=configure_env, name='configure')
def configure(opts: RuntimeOpts, product: str, target: str):
env = {}
if is_cross(target):
if is_cross_mxe(target):
raise RuntimeError('TODO')
else:
raise RuntimeError('TODO')
else:
setup_wasm_target_template(env, opts, target)
if not os.path.isfile(path_join(opts.mono_source_root, 'configure')):
runtime.run_autogen(opts)
wasm_run_configure(env, opts, product, target, get_emsdk_root())
def make(opts: RuntimeOpts, product: str, target: str):
env = {}
emsdk_root = get_emsdk_root()
build_dir = path_join(opts.configure_dir, '%s-%s-%s' % (product, target, opts.configuration))
install_dir = path_join(opts.install_dir, '%s-%s-%s' % (product, target, opts.configuration))
make_args = ['-C', build_dir]
make_args += ['V=1'] if opts.verbose_make else []
make_env = os.environ.copy()
make_env['PATH'] = emsdk_root + ':' + make_env['PATH']
run_command('emmake', args=['make'] + make_args, env=make_env, name='make')
run_command('make', args=['-C', '%s/mono' % build_dir, 'install'], name='make install mono')
# Copy support headers
from shutil import copy
headers = ['crc32.h', 'deflate.h', 'inffast.h', 'inffixed.h', 'inflate.h', 'inftrees.h', 'trees.h', 'zconf.h', 'zlib.h', 'zutil.h']
src_support_dir = '%s/support' % opts.mono_source_root
dst_support_dir = '%s/include/support' % install_dir
mkdir_p(dst_support_dir)
for header in headers:
copy(path_join(src_support_dir, header), dst_support_dir)
# Copy wasm src files
wasm_src_files = [
'driver.c',
'zlib-helper.c',
'corebindings.c',
'pinvoke-tables-default.h',
'pinvoke-tables-default-netcore.h',
'library_mono.js',
'binding_support.js',
'dotnet_support.js'
]
dst_wasm_src_dir = path_join(install_dir, 'src')
mkdir_p(dst_wasm_src_dir)
for wasm_src_file in wasm_src_files:
copy(path_join(opts.mono_source_root, 'sdks/wasm/src', wasm_src_file), dst_wasm_src_dir)
def clean(opts: RuntimeOpts, 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 collections import OrderedDict
from typing import Callable
target_shortcuts = {'all-runtime': runtime_targets}
target_values = runtime_targets + cross_targets + cross_mxe_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 WebAssembly')
emsdk_root_default = os.environ.get('EMSDK_ROOT', default='')
default_help = 'default: %(default)s'
parser.add_argument('action', choices=['configure', 'make', 'clean'])
parser.add_argument('--target', choices=target_values, action='append', required=True)
cmd_utils.add_runtime_arguments(parser, default_help)
args = parser.parse_args(raw_args)
input_action = args.action
input_targets = args.target
opts = runtime_opts_from_args(args)
if not os.path.isdir(opts.mono_source_root):
print('Mono sources directory not found: ' + opts.mono_source_root)
sys.exit(1)
targets = cmd_utils.expand_input_targets(input_targets, target_shortcuts)
action = actions[input_action]
try:
for target in targets:
action(opts, 'wasm', target)
except BuildError as e:
sys.exit(e.message)
if __name__ == '__main__':
from sys import argv
main(argv[1:])