├── __init__.py ├── .gitignore ├── .github ├── FUNDING.yml ├── dependabot.yml ├── workflows │ ├── static_checks.yml │ └── build.yml └── ISSUE_TEMPLATE │ ├── feature---enhancement-request.md │ └── bug-report.md ├── files ├── xi.snk ├── patches │ ├── llvm-osx-regex-conflict.diff │ ├── mono-dbg-agent-clear-tls-instead-of-abort.diff │ ├── bcl-profile-platform-override.diff │ ├── mono_ios_asl_log_deprecated.diff │ ├── btls-cmake-android-16kb-page-size.diff │ ├── emscripten-python-3.12.diff │ ├── offsets-tool-extra-cflags.diff │ ├── btls-cmake-args-linux-mingw.diff │ ├── offsets-tool-duplicate-fields.diff │ ├── fix-mono-android-tkill.diff │ ├── fix-mono-log-spam.diff │ ├── btls-cmake-arm64.diff │ ├── wasm_m2n_trampolines_hook.diff │ └── offsets-tool-newer-clang.diff ├── xi.cs └── godot-AndroidEnvironment.cs ├── osx.py ├── linux.py ├── windows.py ├── print_env.sh ├── LICENSE ├── patch_emscripten.py ├── format.sh ├── msbuild_helper.py ├── reference_assemblies.py ├── patch_mono.py ├── cmd_utils.py ├── options.py ├── llvm.py ├── README.md ├── runtime.py ├── os_utils.py ├── bcl.py ├── wasm.py ├── desktop.py ├── android.py └── ios.py /__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | *.pyc 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | patreon: godotengine 2 | custom: https://godotengine.org/donate 3 | -------------------------------------------------------------------------------- /files/xi.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotengine/godot-mono-builds/HEAD/files/xi.snk -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | -------------------------------------------------------------------------------- /osx.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from desktop import run_main 4 | 5 | 6 | if __name__ == '__main__': 7 | from sys import argv 8 | run_main(argv[1:], target_platform='osx') 9 | -------------------------------------------------------------------------------- /linux.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from desktop import run_main 4 | 5 | 6 | if __name__ == '__main__': 7 | from sys import argv 8 | run_main(argv[1:], target_platform='linux') 9 | -------------------------------------------------------------------------------- /windows.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from desktop import run_main 4 | 5 | 6 | if __name__ == '__main__': 7 | from sys import argv 8 | run_main(argv[1:], target_platform='windows') 9 | -------------------------------------------------------------------------------- /.github/workflows/static_checks.yml: -------------------------------------------------------------------------------- 1 | name: Continuous integration 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | build: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - name: Checkout 9 | uses: actions/checkout@v5 10 | 11 | - name: Lint repo 12 | run: | 13 | sudo apt-get update -qq 14 | sudo apt-get install -qq dos2unix recode 15 | bash ./format.sh 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature---enhancement-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature / Enhancement Request 3 | about: Adding new features or improving existing ones. 4 | title: '' 5 | labels: enhancement 6 | assignees: neikeq 7 | 8 | --- 9 | 10 | 14 | -------------------------------------------------------------------------------- /print_env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This is used in python to get the export environment variables from bash's 'source' command. 4 | 5 | # The 'env' command option '-0' separates the 'name=value' results by 'null' instead of line breaks. 6 | # This is required to parse the output because a variable value can contain line breaks as well. 7 | # Unfortunately, the '-0' option is not supported on some platforms like macOS, 8 | # hence why we need this script to print the environment variables instead. 9 | 10 | unset IFS 11 | for var in $(compgen -e); do 12 | printf "$var=${!var}\0" 13 | done 14 | -------------------------------------------------------------------------------- /files/patches/llvm-osx-regex-conflict.diff: -------------------------------------------------------------------------------- 1 | diff --git a/external/llvm-project/llvm/lib/Support/regex_impl.h b/external/llvm-project/llvm/lib/Support/regex_impl.h 2 | index f8296c9ff75e..45bdb469a0b3 100644 3 | --- a/external/llvm-project/llvm/lib/Support/regex_impl.h 4 | +++ b/external/llvm-project/llvm/lib/Support/regex_impl.h 5 | @@ -35,8 +35,8 @@ 6 | * @(#)regex.h 8.1 (Berkeley) 6/2/93 7 | */ 8 | 9 | -#ifndef _REGEX_H_ 10 | -#define _REGEX_H_ 11 | +#ifndef _REGEX_IMPL_H_ 12 | +#define _REGEX_IMPL_H_ 13 | 14 | #include 15 | typedef off_t llvm_regoff_t; 16 | @@ -105,4 +105,4 @@ size_t llvm_strlcpy(char *dst, const char *src, size_t siz); 17 | } 18 | #endif 19 | 20 | -#endif /* !_REGEX_H_ */ 21 | +#endif /* !_REGEX_IMPL_H_ */ 22 | -------------------------------------------------------------------------------- /files/xi.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http; 2 | 3 | [assembly:System.Reflection.AssemblyVersionAttribute ("0.0.0.0")] 4 | [assembly:System.Runtime.CompilerServices.InternalsVisibleTo ("System.Net.Http, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] 5 | 6 | namespace ObjCRuntime 7 | { 8 | internal class RuntimeOptions 9 | { 10 | internal static HttpMessageHandler GetHttpMessageHandler() 11 | { 12 | return new HttpClientHandler(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /files/patches/mono-dbg-agent-clear-tls-instead-of-abort.diff: -------------------------------------------------------------------------------- 1 | diff --git a/mono/mini/debugger-agent.c b/mono/mini/debugger-agent.c 2 | index 32bfc471ea2..5087405eed5 100644 3 | --- a/mono/mini/debugger-agent.c 4 | +++ b/mono/mini/debugger-agent.c 5 | @@ -4172,8 +4172,12 @@ thread_startup (MonoProfiler *prof, uintptr_t tid) 6 | } 7 | 8 | tls = (DebuggerTlsData *)mono_native_tls_get_value (debugger_tls_id); 9 | - g_assert (!tls); 10 | - // FIXME: Free this somewhere 11 | + if (tls) { 12 | + if (!tls->terminated) { 13 | + MONO_GC_UNREGISTER_ROOT(tls->thread); 14 | + } 15 | + g_free (tls); 16 | + } 17 | tls = g_new0 (DebuggerTlsData, 1); 18 | MONO_GC_REGISTER_ROOT_SINGLE (tls->thread, MONO_ROOT_SOURCE_DEBUGGER, NULL, "Debugger Thread Reference"); 19 | tls->thread = thread; 20 | -------------------------------------------------------------------------------- /files/patches/bcl-profile-platform-override.diff: -------------------------------------------------------------------------------- 1 | diff --git a/mcs/build/rules.make b/mcs/build/rules.make 2 | index e57a636ef4d..60c60a8fc17 100644 3 | --- a/mcs/build/rules.make 4 | +++ b/mcs/build/rules.make 5 | @@ -94,7 +94,7 @@ include $(topdir)/build/config-default.make 6 | 7 | include $(topdir)/build/platforms/$(BUILD_PLATFORM).make 8 | 9 | -PROFILE_PLATFORM = $(if $(PLATFORMS),$(if $(filter $(PLATFORMS),$(HOST_PLATFORM)),$(HOST_PLATFORM),$(error Unknown platform "$(HOST_PLATFORM)" for profile "$(PROFILE)"))) 10 | +PROFILE_PLATFORM ?= $(if $(PLATFORMS),$(if $(filter $(PLATFORMS),$(HOST_PLATFORM)),$(HOST_PLATFORM),$(error Unknown platform "$(HOST_PLATFORM)" for profile "$(PROFILE)"))) 11 | PROFILE_DIRECTORY = $(PROFILE)$(if $(PROFILE_PLATFORM),-$(PROFILE_PLATFORM)) 12 | 13 | # Useful 14 | -------------------------------------------------------------------------------- /files/patches/mono_ios_asl_log_deprecated.diff: -------------------------------------------------------------------------------- 1 | diff --git a/mono/utils/mono-log-darwin.c b/mono/utils/mono-log-darwin.c 2 | index 2cb41b1481e..4f566ddb90a 100644 3 | --- a/mono/utils/mono-log-darwin.c 4 | +++ b/mono/utils/mono-log-darwin.c 5 | @@ -5,7 +5,8 @@ 6 | */ 7 | #include 8 | 9 | -#if (defined(HOST_WATCHOS) && (__WATCH_OS_VERSION_MIN_REQUIRED >= __WATCHOS_3_0)) || defined(HOST_MACCAT) 10 | +#if (defined(HOST_WATCHOS) && (__WATCH_OS_VERSION_MIN_REQUIRED >= __WATCHOS_3_0)) || defined(HOST_MACCAT) \ 11 | + || (defined(HOST_IOS) && (__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 100000)) 12 | /* emitted by clang: 13 | * > /Users/lewurm/work/mono-watch4/mono/utils/mono-log-darwin.c:35:2: error: 'asl_log' is \ 14 | * > deprecated: first deprecated in watchOS 3.0 - os_log(3) has replaced \ 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Report a bug with this repo. 4 | title: '' 5 | labels: bug 6 | assignees: neikeq 7 | 8 | --- 9 | 10 | 16 | 17 | **OS/device including version:** 18 | 19 | 20 | 21 | **Issue description:** 22 | 23 | 24 | 25 | **Screenshots of issue:** 26 | 31 | -------------------------------------------------------------------------------- /files/patches/btls-cmake-android-16kb-page-size.diff: -------------------------------------------------------------------------------- 1 | diff --git a/configure.ac b/configure.ac 2 | index 2c052530..6bf193fd 100644 3 | --- a/configure.ac 4 | +++ b/configure.ac 5 | @@ -5976,13 +5976,13 @@ if test "x$enable_btls" = "xyes"; then 6 | BTLS_CMAKE_ARGS="-DANDROID_ABI=\"armeabi-v7a\" -DANDROID_NATIVE_API_LEVEL=$with_btls_android_api" 7 | ;; 8 | android-v8a) 9 | - BTLS_CMAKE_ARGS="-DANDROID_ABI=\"arm64-v8a\" -DANDROID_NATIVE_API_LEVEL=$with_btls_android_api" 10 | + BTLS_CMAKE_ARGS="-DANDROID_ABI=\"arm64-v8a\" -DANDROID_NATIVE_API_LEVEL=$with_btls_android_api -DCMAKE_SHARED_LINKER_FLAGS=\"-Wl,-z,max-page-size=16384\"" 11 | ;; 12 | android-x86) 13 | BTLS_CMAKE_ARGS="-DANDROID_ABI=\"x86\" -DANDROID_NATIVE_API_LEVEL=$with_btls_android_api" 14 | ;; 15 | android-x64) 16 | - BTLS_CMAKE_ARGS="-DANDROID_ABI=\"x86_64\" -DANDROID_NATIVE_API_LEVEL=$with_btls_android_api" 17 | + BTLS_CMAKE_ARGS="-DANDROID_ABI=\"x86_64\" -DANDROID_NATIVE_API_LEVEL=$with_btls_android_api -DCMAKE_SHARED_LINKER_FLAGS=\"-Wl,-z,max-page-size=16384\"" 18 | ;; 19 | riscv32) 20 | btls_arch=riscv32 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Godot Engine 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /files/patches/emscripten-python-3.12.diff: -------------------------------------------------------------------------------- 1 | diff -aur a/tools/shared.py b/tools/shared.py 2 | --- a/tools/shared.py 2025-10-14 09:16:13.210070677 +0000 3 | +++ b/tools/shared.py 2025-10-14 09:15:07.106178680 +0000 4 | @@ -5,7 +5,7 @@ 5 | 6 | from __future__ import print_function 7 | 8 | -from distutils.spawn import find_executable 9 | +from shutil import which 10 | from subprocess import PIPE, STDOUT 11 | import atexit 12 | import base64 13 | @@ -212,10 +212,10 @@ 14 | config_file = '\n'.join(config_file) 15 | # autodetect some default paths 16 | config_file = config_file.replace('\'{{{ EMSCRIPTEN_ROOT }}}\'', repr(__rootpath__)) 17 | - llvm_root = os.path.dirname(find_executable('llvm-dis') or '/usr/bin/llvm-dis') 18 | + llvm_root = os.path.dirname(which('llvm-dis') or '/usr/bin/llvm-dis') 19 | config_file = config_file.replace('\'{{{ LLVM_ROOT }}}\'', repr(llvm_root)) 20 | 21 | - node = find_executable('nodejs') or find_executable('node') or 'node' 22 | + node = which('nodejs') or which('node') or 'node' 23 | config_file = config_file.replace('\'{{{ NODE }}}\'', repr(node)) 24 | 25 | abspath = os.path.abspath(os.path.expanduser(path)) 26 | -------------------------------------------------------------------------------- /files/patches/offsets-tool-extra-cflags.diff: -------------------------------------------------------------------------------- 1 | diff --git a/mono/tools/offsets-tool/offsets-tool.py b/mono/tools/offsets-tool/offsets-tool.py 2 | index f06799cba12..adf7eb4bbd6 100644 3 | --- a/mono/tools/offsets-tool/offsets-tool.py 4 | +++ b/mono/tools/offsets-tool/offsets-tool.py 5 | @@ -54,6 +54,7 @@ class OffsetsTool: 6 | sys.exit (1) 7 | 8 | parser = argparse.ArgumentParser () 9 | + parser.add_argument ('--extra-cflag=', dest='extra_cflags', action='append', help='extra flags for clang') 10 | parser.add_argument ('--libclang', dest='libclang', help='path to shared library of libclang.{so,dylib}') 11 | parser.add_argument ('--emscripten-sdk', dest='emscripten_path', help='path to emscripten sdk') 12 | parser.add_argument ('--outfile', dest='outfile', help='path to output file', required=True) 13 | @@ -78,6 +79,9 @@ class OffsetsTool: 14 | self.target_args = [] 15 | android_api_level = "-D__ANDROID_API=21" 16 | 17 | + if args.extra_cflags: 18 | + self.target_args += args.extra_cflags 19 | + 20 | if "wasm" in args.abi: 21 | require_emscipten_path (args) 22 | self.sys_includes = [args.emscripten_path + "/system/include", args.emscripten_path + "/system/include/libc", args.emscripten_path + "/system/lib/libc/musl/arch/emscripten"] 23 | -------------------------------------------------------------------------------- /files/patches/btls-cmake-args-linux-mingw.diff: -------------------------------------------------------------------------------- 1 | diff --git a/configure.ac b/configure.ac 2 | index 19b20127ca2..d07afde5e05 100644 3 | --- a/configure.ac 4 | +++ b/configure.ac 5 | @@ -5924,6 +5924,8 @@ if test "x$enable_btls" = "xyes"; then 6 | if test "x$HAVE_YASM" != "xyes"; then 7 | BTLS_CMAKE_ARGS="-DOPENSSL_NO_ASM=1" 8 | fi 9 | + mono_btls_dir_abs=`cd $srcdir && pwd`/mono/btls 10 | + BTLS_CMAKE_ARGS="$BTLS_CMAKE_ARGS -DCYGWIN=TRUE -DCMAKE_TOOLCHAIN_FILE=\"$mono_btls_dir_abs/mxe-Win32.cmake\"" 11 | ;; 12 | esac 13 | ;; 14 | @@ -5935,6 +5937,8 @@ if test "x$enable_btls" = "xyes"; then 15 | if test "x$HAVE_YASM" != "xyes"; then 16 | BTLS_CMAKE_ARGS="-DOPENSSL_NO_ASM=1" 17 | fi 18 | + mono_btls_dir_abs=`cd $srcdir && pwd`/mono/btls 19 | + BTLS_CMAKE_ARGS="$BTLS_CMAKE_ARGS -DCYGWIN=TRUE -DCMAKE_TOOLCHAIN_FILE=\"$mono_btls_dir_abs/mxe-Win64.cmake\"" 20 | ;; 21 | esac 22 | ;; 23 | diff --git a/mono/btls/CMakeLists.txt b/mono/btls/CMakeLists.txt 24 | index 992f41e4c7f..9946f5d21a4 100644 25 | --- a/mono/btls/CMakeLists.txt 26 | +++ b/mono/btls/CMakeLists.txt 27 | @@ -129,4 +129,5 @@ endif () 28 | 29 | if (CYGWIN) 30 | target_link_libraries (mono-btls-shared wsock32 ws2_32) 31 | + target_link_options (mono-btls-shared PRIVATE -static-libgcc) 32 | endif () 33 | \ No newline at end of file 34 | -------------------------------------------------------------------------------- /files/patches/offsets-tool-duplicate-fields.diff: -------------------------------------------------------------------------------- 1 | Extracted from https://github.com/dotnet/runtime/commit/087651ee0c2e315f7fe1cbecab5f507af364ab5c 2 | -- 3 | diff --git a/mono/tools/offsets-tool/offsets-tool.py b/mono/tools/offsets-tool/offsets-tool.py 4 | index adf7eb4b..46214429 100644 5 | --- a/mono/tools/offsets-tool/offsets-tool.py 6 | +++ b/mono/tools/offsets-tool/offsets-tool.py 7 | @@ -303,8 +303,11 @@ class OffsetsTool: 8 | if type.size == -1: 9 | continue 10 | f.write ("DECL_SIZE2(%s,%s)\n" % (type.name, type.size)) 11 | + done_fields = {} 12 | for field in type.fields: 13 | - f.write ("DECL_OFFSET2(%s,%s,%s)\n" % (type.name, field.name, field.offset)) 14 | + if field.name not in done_fields: 15 | + f.write ("DECL_OFFSET2(%s,%s,%s)\n" % (type.name, field.name, field.offset)) 16 | + done_fields [field.name] = field.name 17 | f.write ("#endif //disable metadata check\n") 18 | 19 | f.write ("#ifndef DISABLE_JIT_OFFSETS\n") 20 | @@ -314,8 +317,11 @@ class OffsetsTool: 21 | if type.size == -1: 22 | continue 23 | f.write ("DECL_SIZE2(%s,%s)\n" % (type.name, type.size)) 24 | + done_fields = {} 25 | for field in type.fields: 26 | - f.write ("DECL_OFFSET2(%s,%s,%s)\n" % (type.name, field.name, field.offset)) 27 | + if field.name not in done_fields: 28 | + f.write ("DECL_OFFSET2(%s,%s,%s)\n" % (type.name, field.name, field.offset)) 29 | + done_fields [field.name] = field.name 30 | f.write ("#endif //disable jit check\n") 31 | 32 | f.write ("#endif //cross compiler checks\n") 33 | -------------------------------------------------------------------------------- /patch_emscripten.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | def main(raw_args): 4 | import os 5 | import cmd_utils 6 | from os_utils import get_emsdk_root 7 | 8 | parser = cmd_utils.build_arg_parser(description='Apply patches to the active Emscripten SDK') 9 | 10 | default_help = 'default: %(default)s' 11 | 12 | mono_sources_default = os.environ.get('MONO_SOURCE_ROOT', '') 13 | 14 | if mono_sources_default: 15 | parser.add_argument('--mono-sources', default=mono_sources_default, help=default_help) 16 | else: 17 | parser.add_argument('--mono-sources', required=True) 18 | 19 | args = parser.parse_args(raw_args) 20 | 21 | this_script_dir = os.path.dirname(os.path.realpath(__file__)) 22 | patches_dir = os.path.join(this_script_dir, 'files', 'patches') 23 | 24 | mono_source_root = args.mono_sources 25 | emsdk_root = get_emsdk_root() 26 | 27 | patches = [ 28 | '%s/sdks/builds/fix-emscripten-8511.diff' % mono_source_root, 29 | '%s/emscripten-python-3.12.diff' % patches_dir, 30 | ] 31 | 32 | from subprocess import Popen 33 | from sys import exit 34 | for patch in patches: 35 | patch_cmd = 'patch -N -p1 < %s' % patch 36 | print('Running: %s' % patch_cmd) 37 | proc = Popen('bash -c \'%s; exit $?\'' % patch_cmd, cwd=emsdk_root, shell=True) 38 | exit_code = proc.wait() 39 | if exit_code != 0: 40 | exit('patch exited with error code: %s' % exit_code) 41 | 42 | 43 | if __name__ == '__main__': 44 | from sys import argv 45 | main(argv[1:]) 46 | -------------------------------------------------------------------------------- /files/patches/fix-mono-android-tkill.diff: -------------------------------------------------------------------------------- 1 | diff --git a/mono/metadata/threads.c b/mono/metadata/threads.c 2 | index 8d6753f2e01..1c1bd460123 100644 3 | --- a/mono/metadata/threads.c 4 | +++ b/mono/metadata/threads.c 5 | @@ -78,8 +78,12 @@ mono_native_thread_join_handle (HANDLE thread_handle, gboolean close_handle); 6 | #include 7 | #endif 8 | 9 | -#if defined(HOST_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64) 10 | -#define USE_TKILL_ON_ANDROID 1 11 | +#if defined(HOST_ANDROID) && !(__ANDROID_API__ >= 23) \ 12 | + && ((defined(MIPS) && (CPP_WORDSZ == 32)) \ 13 | + || defined(ARM32) || defined(I386) /* but not x32 */) 14 | + /* tkill() exists only on arm32/mips(32)/x86. */ 15 | + /* NDK r11+ deprecates tkill() but keeps it for Mono clients. */ 16 | +# define USE_TKILL_ON_ANDROID 17 | #endif 18 | 19 | #ifdef HOST_ANDROID 20 | diff --git a/mono/utils/mono-threads-posix.c b/mono/utils/mono-threads-posix.c 21 | index cd32e6b042d..5e2fd4618b8 100644 22 | --- a/mono/utils/mono-threads-posix.c 23 | +++ b/mono/utils/mono-threads-posix.c 24 | @@ -32,8 +32,12 @@ 25 | 26 | #include 27 | 28 | -#if defined(HOST_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64) 29 | -#define USE_TKILL_ON_ANDROID 1 30 | +#if defined(HOST_ANDROID) && !(__ANDROID_API__ >= 23) \ 31 | + && ((defined(MIPS) && (CPP_WORDSZ == 32)) \ 32 | + || defined(ARM32) || defined(I386) /* but not x32 */) 33 | + /* tkill() exists only on arm32/mips(32)/x86. */ 34 | + /* NDK r11+ deprecates tkill() but keeps it for Mono clients. */ 35 | +# define USE_TKILL_ON_ANDROID 36 | #endif 37 | 38 | #ifdef USE_TKILL_ON_ANDROID 39 | -------------------------------------------------------------------------------- /format.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -uo pipefail 4 | IFS=$'\n\t' 5 | 6 | # Loops through all text files tracked by Git. 7 | git grep -zIl '' | 8 | while IFS= read -rd '' f; do 9 | # Exclude csproj and hdr files, and patches. 10 | if [[ "$f" == *"csproj" ]]; then 11 | continue 12 | elif [[ "$f" == *"hdr" ]]; then 13 | continue 14 | elif [[ "$f" == *"diff" ]]; then 15 | continue 16 | elif [[ "$f" == *"patch" ]]; then 17 | continue 18 | fi 19 | # Ensures that files are UTF-8 formatted. 20 | recode UTF-8 "$f" 2> /dev/null 21 | # Ensures that files have LF line endings. 22 | dos2unix "$f" 2> /dev/null 23 | # Ensures that files do not contain a BOM. 24 | sed -i '1s/^\xEF\xBB\xBF//' "$f" 25 | # Ensures that files end with newline characters. 26 | tail -c1 < "$f" | read -r _ || echo >> "$f"; 27 | # Remove trailing space characters. 28 | sed -z -i 's/\x20\x0A/\x0A/g' "$f" 29 | done 30 | 31 | git diff > patch.patch 32 | FILESIZE="$(stat -c%s patch.patch)" 33 | MAXSIZE=5 34 | 35 | # If no patch has been generated all is OK, clean up, and exit. 36 | if (( FILESIZE < MAXSIZE )); then 37 | printf "Files in this commit comply with the formatting rules.\n" 38 | rm -f patch.patch 39 | exit 0 40 | fi 41 | 42 | # A patch has been created, notify the user, clean up, and exit. 43 | printf "\n*** The following differences were found between the code " 44 | printf "and the formatting rules:\n\n" 45 | cat patch.patch 46 | printf "\n*** Aborting, please fix your commit(s) with 'git commit --amend' or 'git rebase -i '\n" 47 | rm -f patch.patch 48 | exit 1 49 | -------------------------------------------------------------------------------- /msbuild_helper.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from os_utils import * 4 | 5 | 6 | def find_dotnet_cli(): 7 | import os.path 8 | 9 | for hint_dir in os.environ["PATH"].split(os.pathsep): 10 | hint_dir = hint_dir.strip('"') 11 | hint_path = os.path.join(hint_dir, "dotnet") 12 | if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): 13 | return hint_path 14 | 15 | 16 | def find_msbuild(): 17 | import os.path 18 | import sys 19 | 20 | hint_dirs = [] 21 | if sys.platform == "darwin": 22 | hint_dirs[:0] = [ 23 | "/Library/Frameworks/Mono.framework/Versions/Current/bin", 24 | "/usr/local/var/homebrew/linked/mono/bin", 25 | ] 26 | 27 | for hint_dir in hint_dirs: 28 | hint_path = os.path.join(hint_dir, "msbuild") 29 | if os.path.isfile(hint_path): 30 | return hint_path 31 | 32 | for hint_dir in os.environ["PATH"].split(os.pathsep): 33 | hint_dir = hint_dir.strip('"') 34 | hint_path = os.path.join(hint_dir, "msbuild") 35 | if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): 36 | return hint_path 37 | 38 | return None 39 | 40 | 41 | def build_solution(solution_path, build_config, extra_msbuild_args=[]): 42 | msbuild_args = [] 43 | 44 | dotnet_cli = find_dotnet_cli() 45 | 46 | if dotnet_cli: 47 | msbuild_path = dotnet_cli 48 | msbuild_args += ["msbuild"] # `dotnet msbuild` command 49 | else: 50 | msbuild_path = find_msbuild() 51 | if msbuild_path is None: 52 | raise BuildError("Cannot find MSBuild executable") 53 | 54 | print("MSBuild path: " + msbuild_path) 55 | 56 | # Build solution 57 | 58 | msbuild_args += [solution_path, "/restore", "/t:Build", "/p:Configuration=" + build_config] 59 | msbuild_args += extra_msbuild_args 60 | 61 | run_command(msbuild_path, msbuild_args, name="msbuild") 62 | -------------------------------------------------------------------------------- /files/patches/fix-mono-log-spam.diff: -------------------------------------------------------------------------------- 1 | diff --git a/mono/metadata/threadpool-io.c b/mono/metadata/threadpool-io.c 2 | index a8a947e7dea..b87b59ed5b6 100644 3 | --- a/mono/metadata/threadpool-io.c 4 | +++ b/mono/metadata/threadpool-io.c 5 | @@ -179,6 +179,7 @@ selector_thread_wakeup_drain_pipes (void) 6 | { 7 | gchar buffer [128]; 8 | gint received; 9 | + static gint warnings_issued = 0; 10 | 11 | for (;;) { 12 | #if !defined(HOST_WIN32) 13 | @@ -191,11 +192,16 @@ selector_thread_wakeup_drain_pipes (void) 14 | * some unices (like AIX) send ERESTART, which doesn't 15 | * exist on some other OSes errno 16 | */ 17 | - if (errno != EINTR && errno != EAGAIN && errno != ERESTART) 18 | + if (errno != EINTR && errno != EAGAIN && errno != ERESTART) { 19 | #else 20 | - if (errno != EINTR && errno != EAGAIN) 21 | + if (errno != EINTR && errno != EAGAIN) { 22 | #endif 23 | - g_warning ("selector_thread_wakeup_drain_pipes: read () failed, error (%d) %s\n", errno, g_strerror (errno)); 24 | + // limit amount of spam we write 25 | + if (warnings_issued < 100) { 26 | + g_warning ("selector_thread_wakeup_drain_pipes: read () failed, error (%d) %s\n", errno, g_strerror (errno)); 27 | + warnings_issued++; 28 | + } 29 | + } 30 | break; 31 | } 32 | #else 33 | @@ -203,8 +209,13 @@ selector_thread_wakeup_drain_pipes (void) 34 | if (received == 0) 35 | break; 36 | if (received == SOCKET_ERROR) { 37 | - if (WSAGetLastError () != WSAEINTR && WSAGetLastError () != WSAEWOULDBLOCK) 38 | - g_warning ("selector_thread_wakeup_drain_pipes: recv () failed, error (%d)\n", WSAGetLastError ()); 39 | + if (WSAGetLastError () != WSAEINTR && WSAGetLastError () != WSAEWOULDBLOCK) { 40 | + // limit amount of spam we write 41 | + if (warnings_issued < 100) { 42 | + g_warning ("selector_thread_wakeup_drain_pipes: recv () failed, error (%d)\n", WSAGetLastError ()); 43 | + warnings_issued++; 44 | + } 45 | + } 46 | break; 47 | } 48 | #endif 49 | -------------------------------------------------------------------------------- /reference_assemblies.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | 5 | from os.path import join as path_join 6 | from options import * 7 | from os_utils import * 8 | 9 | 10 | def build(opts: BaseOpts): 11 | build_dir = '%s/mcs/class/reference-assemblies' % opts.mono_source_root 12 | install_dir = path_join(opts.install_dir, 'reference-assemblies') 13 | 14 | mkdir_p(install_dir) 15 | 16 | make_args = make_default_args(opts) 17 | make_args += ['-C', build_dir, 'build-reference-assemblies'] 18 | 19 | run_command('make', args=make_args, name='make build-reference-assemblies') 20 | 21 | 22 | def install(opts: BaseOpts): 23 | build_dir = '%s/mcs/class/reference-assemblies' % opts.mono_source_root 24 | install_dir = path_join(opts.install_dir, 'reference-assemblies') 25 | 26 | mkdir_p(install_dir) 27 | 28 | make_args = make_default_args(opts) 29 | make_args += ['-C', build_dir, 'install-local', 'DESTDIR=%s' % install_dir, 'prefix=/'] 30 | 31 | run_command('make', args=make_args, name='make install-local') 32 | 33 | 34 | def clean(opts: BaseOpts): 35 | install_dir = path_join(opts.install_dir, 'reference-assemblies') 36 | rm_rf(install_dir) 37 | 38 | 39 | def main(raw_args): 40 | import cmd_utils 41 | 42 | actions = { 43 | 'build': build, 44 | 'install': install, 45 | 'clean': clean 46 | } 47 | 48 | parser = cmd_utils.build_arg_parser(description='Copy the reference assemblies') 49 | 50 | default_help = 'default: %(default)s' 51 | 52 | parser.add_argument('action', choices=actions.keys()) 53 | 54 | cmd_utils.add_base_arguments(parser, default_help) 55 | 56 | args = parser.parse_args(raw_args) 57 | 58 | opts = base_opts_from_args(args) 59 | 60 | try: 61 | action = actions[args.action] 62 | action(opts) 63 | except BuildError as e: 64 | sys.exit(e.message) 65 | 66 | 67 | if __name__ == '__main__': 68 | from sys import argv 69 | main(argv[1:]) 70 | -------------------------------------------------------------------------------- /patch_mono.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | 4 | def main(raw_args): 5 | import cmd_utils 6 | import os 7 | import os.path 8 | from os_utils import get_emsdk_root 9 | 10 | parser = cmd_utils.build_arg_parser(description='Apply patches to the Mono source tree') 11 | 12 | default_help = 'default: %(default)s' 13 | 14 | mono_sources_default = os.environ.get('MONO_SOURCE_ROOT', '') 15 | 16 | if mono_sources_default: 17 | parser.add_argument('--mono-sources', default=mono_sources_default, help=default_help) 18 | else: 19 | parser.add_argument('--mono-sources', required=True) 20 | 21 | args = parser.parse_args(raw_args) 22 | 23 | this_script_dir = os.path.dirname(os.path.realpath(__file__)) 24 | patches_dir = os.path.join(this_script_dir, 'files', 'patches') 25 | 26 | mono_source_root = args.mono_sources 27 | 28 | patches = [ 29 | 'fix-mono-android-tkill.diff', 30 | 'fix-mono-log-spam.diff', 31 | 'mono-dbg-agent-clear-tls-instead-of-abort.diff', 32 | 'bcl-profile-platform-override.diff', 33 | 'mono_ios_asl_log_deprecated.diff', 34 | 'wasm_m2n_trampolines_hook.diff', 35 | 'btls-cmake-args-linux-mingw.diff', 36 | 'btls-cmake-arm64.diff', 37 | 'btls-cmake-android-16kb-page-size.diff', 38 | 'offsets-tool-extra-cflags.diff', 39 | 'offsets-tool-newer-clang.diff', 40 | 'offsets-tool-duplicate-fields.diff', 41 | 'llvm-osx-regex-conflict.diff', 42 | ] 43 | 44 | from subprocess import Popen 45 | from sys import exit 46 | for patch in patches: 47 | patch_cmd = 'patch -N -p1 < %s' % os.path.join(patches_dir, patch) 48 | print('Running: %s' % patch_cmd) 49 | proc = Popen('bash -c \'%s; exit $?\'' % patch_cmd, cwd=mono_source_root, shell=True) 50 | exit_code = proc.wait() 51 | if exit_code != 0: 52 | exit('patch exited with error code: %s' % exit_code) 53 | 54 | 55 | if __name__ == '__main__': 56 | from sys import argv 57 | main(argv[1:]) 58 | -------------------------------------------------------------------------------- /files/patches/btls-cmake-arm64.diff: -------------------------------------------------------------------------------- 1 | diff --git a/configure.ac b/configure.ac 2 | index d07afde5e05..2c052530f2c 100644 3 | --- a/configure.ac 4 | +++ b/configure.ac 5 | @@ -4927,9 +4927,6 @@ if test "x$target_mach" = "xyes"; then 6 | CPPFLAGS_FOR_LIBGC="$CPPFLAGS_FOR_LIBGC -DTARGET_OSX" 7 | CFLAGS_FOR_LIBGC="$CFLAGS_FOR_LIBGC -DTARGET_OSX" 8 | target_osx=yes 9 | - if test "x$TARGET" = "xARM64"; then 10 | - BTLS_SUPPORTED=no 11 | - fi 12 | ], [ 13 | AC_DEFINE(TARGET_IOS,1,[The JIT/AOT targets iOS]) 14 | CPPFLAGS_FOR_LIBGC="$CPPFLAGS_FOR_LIBGC -DTARGET_IOS" 15 | @@ -5952,7 +5949,11 @@ if test "x$enable_btls" = "xyes"; then 16 | ;; 17 | aarch64) 18 | btls_arch=aarch64 19 | - btls_cflags="-march=armv8-a+crypto" 20 | + if test "x$target_mach" = "xyes"; then 21 | + btls_cflags="-arch arm64" 22 | + else 23 | + btls_cflags="-march=armv8-a+crypto" 24 | + fi 25 | ;; 26 | s390x) 27 | btls_arch=s390x 28 | diff --git a/mono/btls/CMakeLists.txt b/mono/btls/CMakeLists.txt 29 | index 9946f5d21a4..a8aa3d07b0e 100644 30 | --- a/mono/btls/CMakeLists.txt 31 | +++ b/mono/btls/CMakeLists.txt 32 | @@ -21,6 +21,15 @@ if (MSVC OR CYGWIN) 33 | set(BTLS_HOST_WIN32 1) 34 | endif () 35 | 36 | +if (NOT "${BTLS_ARCH}" STREQUAL "") 37 | + message (STATUS "SET ARCH: ${BTLS_ARCH}") 38 | + set (CMAKE_SYSTEM_PROCESSOR "${BTLS_ARCH}") 39 | +endif () 40 | + 41 | +if ((("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "arm64") OR ("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "aarch64")) AND APPLE AND NOT IOS) 42 | + set(OPENSSL_NO_ASM 1) 43 | +endif () 44 | + 45 | if (NOT OPENSSL_NO_ASM) 46 | if (BTLS_HOST_WIN32) 47 | if (CYGWIN AND "${BTLS_ARCH}" STREQUAL "i386") 48 | @@ -38,11 +47,6 @@ if (NOT OPENSSL_NO_ASM) 49 | endif () 50 | endif () 51 | 52 | -if (NOT "${BTLS_ARCH}" STREQUAL "") 53 | - message (STATUS "SET ARCH: ${BTLS_ARCH}") 54 | - set (CMAKE_SYSTEM_PROCESSOR "${BTLS_ARCH}") 55 | -endif () 56 | - 57 | if (NOT MSVC) 58 | if(${CMAKE_SYSTEM_NAME} MATCHES "AIX" OR ${CMAKE_SYSTEM_NAME} MATCHES "OS400") 59 | # GCC+XCOFF doesn't support -fvisibility=hidden, and we would prefer XCOFF debugging info. 60 | -------------------------------------------------------------------------------- /files/patches/wasm_m2n_trampolines_hook.diff: -------------------------------------------------------------------------------- 1 | diff --git a/mono/mini/aot-runtime-wasm.c b/mono/mini/aot-runtime-wasm.c 2 | index ccc5a26510c..2f7001be00b 100644 3 | --- a/mono/mini/aot-runtime-wasm.c 4 | +++ b/mono/mini/aot-runtime-wasm.c 5 | @@ -112,6 +112,22 @@ mono_wasm_interp_to_native_trampoline (void *target_func, InterpMethodArguments 6 | icall_trampoline_dispatch (cookie, target_func, margs); 7 | } 8 | 9 | +typedef mono_bool (*GodotMonoM2nIcallTrampolineDispatch)(const char *cookie, void *target_func, InterpMethodArguments *margs); 10 | + 11 | +GodotMonoM2nIcallTrampolineDispatch m2n_icall_trampoline_dispatch_hook = NULL; 12 | + 13 | +GodotMonoM2nIcallTrampolineDispatch 14 | +godot_mono_get_m2n_icall_trampoline_dispatch_hook (void) 15 | +{ 16 | + return m2n_icall_trampoline_dispatch_hook; 17 | +} 18 | + 19 | +MONO_API void 20 | +godot_mono_register_m2n_icall_trampoline_dispatch_hook (GodotMonoM2nIcallTrampolineDispatch hook) 21 | +{ 22 | + m2n_icall_trampoline_dispatch_hook = hook; 23 | +} 24 | + 25 | #else /* TARGET_WASM */ 26 | 27 | MONO_EMPTY_SOURCE_FILE (aot_runtime_wasm); 28 | diff --git a/mono/mini/wasm_m2n_invoke.g.h b/mono/mini/wasm_m2n_invoke.g.h 29 | index aea7e9698ad..3bb715f9fd8 100644 30 | --- a/mono/mini/wasm_m2n_invoke.g.h 31 | +++ b/mono/mini/wasm_m2n_invoke.g.h 32 | @@ -1017,6 +1017,10 @@ wasm_invoke_vil (void *target_func, InterpMethodArguments *margs) 33 | 34 | } 35 | 36 | +typedef mono_bool (*GodotMonoM2nIcallTrampolineDispatch)(const char *cookie, void *target_func, InterpMethodArguments *margs); 37 | + 38 | +GodotMonoM2nIcallTrampolineDispatch godot_mono_get_m2n_icall_trampoline_dispatch_hook (void); 39 | + 40 | static void 41 | icall_trampoline_dispatch (const char *cookie, void *target_func, InterpMethodArguments *margs) 42 | { 43 | @@ -1820,5 +1824,9 @@ icall_trampoline_dispatch (const char *cookie, void *target_func, InterpMethodAr 44 | } 45 | } 46 | } 47 | + GodotMonoM2nIcallTrampolineDispatch trampoline_dispatch_hook = godot_mono_get_m2n_icall_trampoline_dispatch_hook (); 48 | + if (trampoline_dispatch_hook != NULL && trampoline_dispatch_hook (cookie, target_func, margs)) { 49 | + return; 50 | + } 51 | g_error ("CANNOT HANDLE COOKIE %s\n", cookie); 52 | } 53 | -------------------------------------------------------------------------------- /cmd_utils.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | def custom_bool(val): 4 | if isinstance(val, bool): 5 | return val 6 | if val.lower() in ('yes', 'true', 't', 'y', '1'): 7 | return True 8 | elif val.lower() in ('no', 'false', 'f', 'n', '0'): 9 | return False 10 | else: 11 | from argparse import ArgumentTypeError 12 | raise ArgumentTypeError('Boolean value expected.') 13 | 14 | 15 | def build_arg_parser(description, env_vars={}): 16 | from argparse import ArgumentParser, RawDescriptionHelpFormatter 17 | from textwrap import dedent 18 | 19 | base_env_vars = { 20 | 'MONO_SOURCE_ROOT': 'Overrides default value for --mono-sources', 21 | } 22 | 23 | env_vars_text = '\n'.join([' %s: %s' % (var, desc) for var, desc in env_vars.items()]) 24 | base_env_vars_text = '\n'.join([' %s: %s' % (var, desc) for var, desc in base_env_vars.items()]) 25 | 26 | epilog=dedent('''\ 27 | environment variables: 28 | %s 29 | %s 30 | ''' % (env_vars_text, base_env_vars_text)) 31 | 32 | return ArgumentParser( 33 | description=description, 34 | formatter_class=RawDescriptionHelpFormatter, 35 | epilog=epilog 36 | ) 37 | 38 | 39 | def add_base_arguments(parser, default_help): 40 | import os 41 | from os.path import join as path_join 42 | 43 | home = os.environ.get('HOME') 44 | mono_sources_default = os.environ.get('MONO_SOURCE_ROOT', '') 45 | 46 | parser.add_argument('--verbose-make', action='store_true', default=False, help=default_help) 47 | # --jobs supports not passing an argument, in which case the 'const' is used, 48 | # which is the number of CPU cores on the host system. 49 | parser.add_argument('--jobs', '-j', nargs='?', const=str(os.cpu_count()), default='1', help=default_help) 50 | parser.add_argument('--configure-dir', default=path_join(home, 'mono-configs'), help=default_help) 51 | parser.add_argument('--install-dir', default=path_join(home, 'mono-installs'), help=default_help) 52 | 53 | if mono_sources_default: 54 | parser.add_argument('--mono-sources', default=mono_sources_default, help=default_help) 55 | else: 56 | parser.add_argument('--mono-sources', required=True) 57 | 58 | parser.add_argument('--mxe-prefix', default='/usr', help=default_help) 59 | 60 | 61 | def add_runtime_arguments(parser, default_help): 62 | add_base_arguments(parser, default_help) 63 | 64 | parser.add_argument('--configuration', choices=['release', 'debug'], default='release', help=default_help) 65 | parser.add_argument('--enable-cxx', action='store_true', default=False, help=default_help) 66 | parser.add_argument('--strip-libs', type=custom_bool, default=True, help='Strip the libraries if possible after running make.\n' + default_help) 67 | 68 | 69 | def expand_input_targets(input_targets, target_shortcuts=[]): 70 | targets = [] 71 | 72 | for shortcut in target_shortcuts.keys(): 73 | if shortcut in input_targets: 74 | targets += target_shortcuts[shortcut][:] 75 | 76 | # The shortcuts options ('all-*') have already been handled. Remove them this way as there may be duplicates. 77 | input_targets = [t for t in input_targets if not t in target_shortcuts] 78 | 79 | for target in input_targets: 80 | if not target in targets: 81 | targets += [target] 82 | 83 | return targets 84 | -------------------------------------------------------------------------------- /options.py: -------------------------------------------------------------------------------- 1 | 2 | from dataclasses import dataclass 3 | from os.path import abspath 4 | 5 | 6 | @dataclass 7 | class BaseOpts: 8 | verbose_make: bool 9 | jobs: str 10 | configure_dir: str 11 | install_dir: str 12 | mono_source_root: str 13 | mxe_prefix: str 14 | 15 | 16 | @dataclass 17 | class RuntimeOpts(BaseOpts): 18 | configuration: str 19 | release: bool 20 | enable_cxx: bool 21 | strip_libs: bool 22 | 23 | 24 | @dataclass 25 | class AndroidOpts(RuntimeOpts): 26 | android_sdk_root: str 27 | android_ndk_version: str 28 | android_api_version: str 29 | android_cmake_version: str 30 | toolchain_name_fmt: str = '%s-api%s-clang' 31 | 32 | 33 | @dataclass 34 | class iOSOpts(RuntimeOpts): 35 | ios_toolchain_path: str 36 | ios_sdk_path: str 37 | ios_version_min: str 38 | osx_toolchain_path: str 39 | osx_sdk_path: str 40 | osx_triple_abi: str 41 | 42 | 43 | @dataclass 44 | class DesktopOpts(RuntimeOpts): 45 | with_llvm: bool 46 | 47 | 48 | @dataclass 49 | class BclOpts(BaseOpts): 50 | tests: bool 51 | remove_pdb: bool 52 | 53 | 54 | # Need to make paths absolute as we change cwd 55 | 56 | 57 | def base_opts_from_args(args): 58 | from os.path import abspath 59 | return BaseOpts( 60 | verbose_make = args.verbose_make, 61 | jobs = args.jobs, 62 | configure_dir = abspath(args.configure_dir), 63 | install_dir = abspath(args.install_dir), 64 | mono_source_root = abspath(args.mono_sources), 65 | mxe_prefix = args.mxe_prefix 66 | ) 67 | 68 | 69 | def runtime_opts_from_args(args): 70 | return RuntimeOpts( 71 | **vars(base_opts_from_args(args)), 72 | configuration = args.configuration, 73 | release = (args.configuration == 'release'), 74 | enable_cxx = args.enable_cxx, 75 | strip_libs = args.strip_libs 76 | ) 77 | 78 | 79 | def android_opts_from_args(args): 80 | return AndroidOpts( 81 | **vars(runtime_opts_from_args(args)), 82 | android_sdk_root = abspath(args.android_sdk), 83 | android_ndk_version = args.android_ndk_version, 84 | android_api_version = args.android_api_version, 85 | android_cmake_version = args.android_cmake_version 86 | ) 87 | 88 | 89 | def ios_opts_from_args(args): 90 | return iOSOpts( 91 | **vars(runtime_opts_from_args(args)), 92 | ios_toolchain_path = abspath(args.ios_toolchain), 93 | ios_sdk_path = abspath(args.ios_sdk) if args.ios_sdk else '', 94 | ios_version_min = args.ios_version_min, 95 | osx_toolchain_path = abspath(args.osx_toolchain), 96 | osx_sdk_path = abspath(args.osx_sdk) if args.ios_sdk else '', 97 | osx_triple_abi = args.osx_triple_abi 98 | ) 99 | 100 | 101 | def bcl_opts_from_args(args): 102 | return BclOpts( 103 | **vars(base_opts_from_args(args)), 104 | tests = args.tests, 105 | remove_pdb = args.remove_pdb 106 | ) 107 | 108 | 109 | def desktop_opts_from_args(args): 110 | return DesktopOpts( 111 | **vars(runtime_opts_from_args(args)), 112 | with_llvm = args.with_llvm 113 | ) 114 | 115 | 116 | def make_default_args(opts: BaseOpts): 117 | make_args = ['-j%s' % opts.jobs] 118 | make_args += ['V=1'] if opts.verbose_make else [] 119 | return make_args 120 | -------------------------------------------------------------------------------- /files/godot-AndroidEnvironment.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net; 4 | using System.Runtime.CompilerServices; 5 | using System.Runtime.InteropServices; 6 | using System.Threading; 7 | 8 | // See 'mono/sdks/android/managed/fake-monodroid.cs' and 'xamarin-android/src/Mono.Android/Android.Runtime/AndroidEnvironment.cs' 9 | 10 | namespace Android.Runtime 11 | { 12 | public static class AndroidEnvironment 13 | { 14 | public const string AndroidLogAppName = "Mono.Android"; 15 | 16 | static object lock_ = new object(); 17 | 18 | [DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)] 19 | internal extern static void monodroid_free(IntPtr ptr); 20 | 21 | [DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)] 22 | static extern IntPtr _monodroid_timezone_get_default_id(); 23 | 24 | [DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)] 25 | static extern int _monodroid_getifaddrs(out IntPtr ifap); 26 | 27 | [DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)] 28 | static extern void _monodroid_freeifaddrs(IntPtr ifap); 29 | 30 | static string GetDefaultTimeZone() 31 | { 32 | IntPtr id = _monodroid_timezone_get_default_id(); 33 | 34 | try 35 | { 36 | return Marshal.PtrToStringAnsi(id); 37 | } 38 | finally 39 | { 40 | monodroid_free(id); 41 | } 42 | } 43 | 44 | static SynchronizationContext GetDefaultSyncContext() 45 | { 46 | // Not needed 47 | return null; 48 | } 49 | 50 | static IWebProxy GetDefaultProxy() 51 | { 52 | // Not needed 53 | return null; 54 | } 55 | 56 | static int GetInterfaceAddresses(out IntPtr ifap) 57 | { 58 | return _monodroid_getifaddrs(out ifap); 59 | } 60 | 61 | static void FreeInterfaceAddresses(IntPtr ifap) 62 | { 63 | _monodroid_freeifaddrs(ifap); 64 | } 65 | 66 | static void DetectCPUAndArchitecture(out ushort builtForCPU, out ushort runningOnCPU, out bool is64bit) 67 | { 68 | // Not needed (For now? The BCL code that uses the result is commented...) 69 | builtForCPU = 0; 70 | runningOnCPU = 0; 71 | is64bit = Environment.Is64BitProcess; 72 | } 73 | 74 | static bool TrustEvaluateSsl(List certsRawData) 75 | { 76 | // This is legacy. Not needed as BTLS is used by default instead. 77 | throw new NotImplementedException(); 78 | } 79 | 80 | [MethodImpl(MethodImplOptions.InternalCall)] 81 | static extern bool _gd_mono_init_cert_store(); 82 | 83 | [MethodImpl(MethodImplOptions.InternalCall)] 84 | static extern byte[] _gd_mono_android_cert_store_lookup(string alias); 85 | 86 | static bool certStoreInitOk = false; 87 | 88 | static void InitCertStore() 89 | { 90 | if (certStoreInitOk) 91 | return; 92 | 93 | lock (lock_) 94 | { 95 | certStoreInitOk = _gd_mono_init_cert_store(); 96 | } 97 | } 98 | 99 | static byte[] CertStoreLookup(long hash, bool userStore) 100 | { 101 | InitCertStore(); 102 | 103 | if (!certStoreInitOk) 104 | return null; 105 | 106 | string alias = string.Format("{0}:{1:x8}.0", userStore ? "user" : "system", hash); 107 | 108 | return _gd_mono_android_cert_store_lookup(alias); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /llvm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import sys 5 | 6 | from os.path import join as path_join 7 | from options import * 8 | from os_utils import * 9 | 10 | 11 | # TODO: OSXCROSS 12 | 13 | 14 | target_values = ['llvm32', 'llvm64', 'llvmarm64', 'llvmwin32', 'llvmwin64'] 15 | mxe_targets = { 16 | 'llvmwin32': {'arch': 'i686', 'mxe': 'mxe-Win32'}, 17 | 'llvmwin64': {'arch': 'x86_64', 'mxe': 'mxe-Win64'} 18 | } 19 | 20 | 21 | def make(opts: BaseOpts, target: str): 22 | stamp_file = path_join(opts.configure_dir, '.stamp-%s-make' % target) 23 | 24 | if os.path.isfile(stamp_file): 25 | return 26 | 27 | build_dir = path_join(opts.configure_dir, 'llvm-%s' % target) 28 | install_dir = path_join(opts.install_dir, 'llvm-%s' % target) 29 | 30 | mkdir_p(build_dir) 31 | mkdir_p(install_dir) 32 | 33 | CMAKE_ARGS = [] 34 | 35 | if target in mxe_targets: 36 | mxe = mxe_targets[target]['mxe'] 37 | arch = mxe_targets[target]['arch'] 38 | 39 | CMAKE_ARGS += [ 40 | '-DCMAKE_EXE_LINKER_FLAGS="-static"', 41 | '-DCROSS_TOOLCHAIN_FLAGS_NATIVE=-DCMAKE_TOOLCHAIN_FILE=%s/external/llvm-project/llvm/cmake/modules/NATIVE.cmake' % opts.mono_source_root, 42 | '-DCMAKE_TOOLCHAIN_FILE=%s/external/llvm-project/llvm/cmake/modules/%s.cmake' % (opts.mono_source_root, mxe), 43 | '-DLLVM_ENABLE_THREADS=Off', 44 | '-DLLVM_BUILD_EXECUTION_ENGINE=Off' 45 | ] 46 | 47 | if sys.platform == 'darwin': 48 | mingw_zlib_prefix = '%s/opt/mingw-zlib/usr' % opts.mxe_prefix 49 | if not os.path.isfile(mingw_zlib_prefix): 50 | mingw_zlib_prefix = opts.mxe_prefix 51 | 52 | CMAKE_ARGS += [ 53 | '-DZLIB_ROOT=%s/%s-w64-mingw32' % (mingw_zlib_prefix, arch), 54 | '-DZLIB_LIBRARY=%s/%s-w64-mingw32/lib/libz.a' % (mingw_zlib_prefix, arch), 55 | '-DZLIB_INCLUDE_DIR=%s/%s-w64-mingw32/include' % (mingw_zlib_prefix, arch) 56 | ] 57 | 58 | replace_in_new_file( 59 | src_file='%s/sdks/builds/%s.cmake.in' % (opts.mono_source_root, mxe), 60 | search='@MXE_PATH@', replace=opts.mxe_prefix, 61 | dst_file='%s/external/llvm-project/llvm/cmake/modules/%s.cmake' % (opts.mono_source_root, mxe) 62 | ) 63 | 64 | if target in ['llvm32', 'llvmwin32']: 65 | CMAKE_ARGS += ['-DLLVM_BUILD_32_BITS=On'] 66 | 67 | CMAKE_ARGS += [os.environ.get('llvm-%s_CMAKE_ARGS' % target, '')] 68 | 69 | # IMPORTANT: We must specify the jobs count for this Makefile. 70 | # The Makefile itself runs Make as well with the '-j' option, which tells it to spawn as many jobs as possible. 71 | # This can result in errors like 'posix_spawn failed: Resource temporarily unavailable' on macOS due to the process limit. 72 | # The job count seems to be inherited from the parent Make process, so that fixes the issue. 73 | # Note: This is handle automatically in make_default_args. 74 | make_args = make_default_args(opts) 75 | make_args += [ 76 | '-C', '%s/llvm' % opts.mono_source_root, 77 | '-f', 'build.mk', 'install-llvm', 78 | 'LLVM_BUILD=%s' % build_dir, 79 | 'LLVM_PREFIX=%s' % install_dir, 80 | 'LLVM_CMAKE_ARGS=%s' % ' '.join([a for a in CMAKE_ARGS if a]) 81 | ] 82 | 83 | if not find_executable('cmake') and not 'CMAKE' in os.environ: 84 | print('WARNING: Cannot find CMake. Required by the llvm Makefile.') 85 | 86 | run_command('make', args=make_args, name='make') 87 | 88 | touch(stamp_file) 89 | 90 | 91 | def clean(opts: BaseOpts, target: str): 92 | build_dir = path_join(opts.configure_dir, 'llvm-%s' % target) 93 | install_dir = path_join(opts.install_dir, 'llvm-%s' % target) 94 | stamp_file = path_join(opts.configure_dir, '.stamp-%s-make' % target) 95 | 96 | rm_rf(stamp_file) 97 | 98 | make_args = make_default_args(opts) 99 | make_args += [ 100 | '-C', '%s/llvm' % opts.mono_source_root, 101 | '-f', 'build.mk', 'clean-llvm', 102 | 'LLVM_BUILD=%s' % build_dir, 103 | 'LLVM_PREFIX=%s' % install_dir 104 | ] 105 | 106 | run_command('make', args=make_args, name='make clean') 107 | 108 | 109 | def main(raw_args): 110 | import cmd_utils 111 | 112 | parser = cmd_utils.build_arg_parser(description='Builds LLVM for Mono') 113 | 114 | default_help = 'default: %(default)s' 115 | 116 | parser.add_argument('action', choices=['make', 'clean']) 117 | parser.add_argument('--target', choices=target_values, action='append', required=True) 118 | 119 | cmd_utils.add_base_arguments(parser, default_help) 120 | 121 | args = parser.parse_args(raw_args) 122 | 123 | opts = base_opts_from_args(args) 124 | targets = args.target 125 | 126 | try: 127 | for target in targets: 128 | action = { 'make': make, 'clean': clean }[args.action] 129 | action(opts, target) 130 | except BuildError as e: 131 | sys.exit(e.message) 132 | 133 | 134 | if __name__ == '__main__': 135 | from sys import argv 136 | main(argv[1:]) 137 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mono build scripts for Godot 2 | 3 | [![Build](https://github.com/godotengine/godot-mono-builds/workflows/Build/badge.svg)](https://github.com/godotengine/godot-mono-builds/actions) 4 | 5 | This repository contains scripts for building the Mono runtime to use with Godot Engine. 6 | 7 | ## Supported versions 8 | 9 | The scripts are tested against specific versions of the toolchains used by Godot. 10 | While they may work with other versions, you might have issues applying patches or compiling, so we recommend using the versions below. 11 | 12 | - Mono: 6.12.0.206. 13 | - Emscripten: 1.39.9. 14 | - Android NDK: 23.2.8568313 15 | 16 | ## Command-line options 17 | 18 | **Requires Python 3.7 or higher** 19 | 20 | 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. 21 | 22 | Some patches need to be applied to the Mono sources before building. This can be done by running `python3 ./patch_mono.py`. 23 | 24 | Run `python3 SCRIPT.py --help` for the full list of command line options. 25 | 26 | By default, the scripts will install the resulting files to `$HOME/mono-installs`. 27 | A custom output directory can be specified with the `--install-dir` option. 28 | 29 | 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`. 30 | 31 | A path to the Mono source tree must be provided with the `--mono-sources` option or with the `MONO_SOURCE_ROOT` environment variable: 32 | 33 | ```bash 34 | export MONO_SOURCE_ROOT=$HOME/git/mono 35 | ``` 36 | 37 | ### Notes 38 | - Python 3.7 or higher is required. 39 | - OSXCROSS is supported except for building the Mono cross-compilers. 40 | - Building on Windows is not supported. It's possible to use Cygwin or WSL (Windows Subsystem for Linux) but this hasn't been tested. 41 | 42 | ## Compiling Godot for Desktop with this Runtime 43 | 44 | In order to compile mono into Godot for deskop you will need to first build for desktop (see 'Desktop' below), and then Base Class Libraries (see 'Base Class library' below). 45 | 46 | Then run the 'copy-bcl' action of the same desktop script you ran configure and make on, specifying the same target platforms you used before. This will copy the bcl runtime into the runtime directories that the subsequent Godot build (using `copy_mono_root=yes`) expects to find them in. 47 | e.g. 48 | `./linux.py copy-bcl --target=x86 --target=x86_64` 49 | 50 | Then you'll need to compile Godot using `copy_mono_root=yes` 51 | e.g. 52 | `scons -j6 target=release_debug tools=yes module_mono_enabled=yes copy_mono_root=yes mono_prefix="$HOME/mono-installs/desktop-linux-x86_64-release"` 53 | 54 | 55 | ## Desktop 56 | 57 | ```bash 58 | # Build the runtimes for 32-bit and 64-bit Linux. 59 | ./linux.py configure --target=x86 --target=x86_64 60 | ./linux.py make --target=x86 --target=x86_64 61 | 62 | # Build the runtimes for 32-bit and 64-bit Windows. 63 | ./windows.py configure --target=x86 --target=x86_64 --mxe-prefix=/usr 64 | ./windows.py make --target=x86 --target=x86_64 --mxe-prefix=/usr 65 | 66 | # Build the runtime for 64-bit macOS. 67 | ./osx.py configure --target=x86_64 68 | ./osx.py make --target=x86_64 69 | ``` 70 | 71 | _AOT cross-compilers for desktop platforms cannot be built with these scripts yet._ 72 | 73 | ## Android 74 | 75 | Building for Android requires the Android SDK cmdline-tools to be installed in the Android SDK folder. 76 | 77 | ```bash 78 | # The default location for the Android SDK is $HOME/Android/Sdk. This step can be omitted if SDK is in this location. 79 | export ANDROID_SDK_ROOT=$HOME/Android/Sdk 80 | 81 | # Build the runtime for all supported Android ABIs. 82 | ./android.py configure --target=all-targets 83 | ./android.py make --target=all-targets 84 | ``` 85 | 86 | The option `--target=all-targets` is a shortcut for `--target=armv7 --target=arm64v8 --target=x86 --target=x86_64`. 87 | 88 | # iOS 89 | 90 | ```bash 91 | # Build the runtime for the iPhone simulator. 92 | ./ios.py configure --target=x86_64 93 | ./ios.py make --target=x86_64 94 | 95 | # Build the runtime for the iPhone device. 96 | ./ios.py configure --target=arm64 97 | ./ios.py make --target=arm64 98 | 99 | # Build the AOT cross-compiler targeting the iPhone device. 100 | ./ios.py configure --target=cross-arm64 101 | ./ios.py make --target=cross-arm64 102 | ``` 103 | 104 | The runtime can also be built an OSXCROSS iOS toolchain. The `--ios-toolchain` and `--ios-sdk` options 105 | are the equivalent of the Godot SCons options `IPHONEPATH` and `IPHONESDK` respectively. 106 | The cross compiler cannot be built with OSXCROSS yet. 107 | 108 | ## WebAssembly 109 | 110 | Just like with Godot, an active Emscripten SDK is needed for building the Mono WebAssembly runtime. 111 | 112 | Some patches may need to be applied to the Emscripten SDK before building Mono. This can be done by running `./patch_emscripten.py`. 113 | 114 | ```bash 115 | # Build the runtime for WebAssembly. 116 | ./wasm.py configure --target=runtime 117 | ./wasm.py make --target=runtime 118 | ``` 119 | 120 | _AOT cross-compilers for WebAssembly cannot be built with this script yet._ 121 | 122 | ## Base Class library 123 | 124 | ```bash 125 | # Build the Desktop BCL. 126 | ./bcl.py make --product=desktop 127 | 128 | # Build the Desktop BCL for Windows. 129 | ./bcl.py make --product=desktop-win32 130 | 131 | # Build the Android BCL. 132 | ./bcl.py make --product=android 133 | 134 | # Build the iOS BCL. 135 | ./bcl.py make --product=ios 136 | 137 | # Build the WebAssembly BCL. 138 | ./bcl.py make --product=wasm 139 | ``` 140 | 141 | **NOTE:** Building the Desktop BCL for the current system is required first to be able to build the Desktop BCL for Windows. 142 | 143 | ## Reference Assemblies 144 | 145 | ```bash 146 | ./reference_assemblies.py install 147 | ``` 148 | -------------------------------------------------------------------------------- /runtime.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | from os.path import join as path_join 4 | 5 | from options import RuntimeOpts 6 | from os_utils import * 7 | 8 | 9 | def setup_runtime_template(env: dict, opts: RuntimeOpts, product: str, target: str, host_triple: str, llvm: str=''): 10 | BITNESS = '' 11 | if any(s in host_triple for s in ['i686', 'i386']): 12 | BITNESS = '-m32' 13 | elif 'x86_64' in host_triple: 14 | BITNESS = '-m64' 15 | 16 | CFLAGS = [] 17 | CFLAGS += ['-O2', '-g'] if opts.release else ['-O0', '-ggdb3', '-fno-omit-frame-pointer'] 18 | CFLAGS += env.get('_%s-%s_CFLAGS' % (product, target), []) 19 | CFLAGS += env.get('%s-%s_CFLAGS' % (product, target), []) 20 | CFLAGS += [BITNESS] if BITNESS else [] 21 | 22 | CXXFLAGS = [] 23 | CXXFLAGS += ['-O2', '-g'] if opts.release else ['-O0', '-ggdb3', '-fno-omit-frame-pointer'] 24 | CXXFLAGS += env.get('_%s-%s_CXXFLAGS' % (product, target), []) 25 | CXXFLAGS += env.get('%s-%s_CXXFLAGS' % (product, target), []) 26 | CXXFLAGS += [BITNESS] if BITNESS else [] 27 | 28 | CPPFLAGS = [] 29 | CPPFLAGS += ['-O2', '-g'] if opts.release else ['-O0', '-ggdb3', '-fno-omit-frame-pointer'] 30 | CPPFLAGS += env.get('_%s-%s_CPPFLAGS' % (product, target), []) 31 | CPPFLAGS += env.get('%s-%s_CPPFLAGS' % (product, target), []) 32 | CPPFLAGS += [BITNESS] if BITNESS else [] 33 | 34 | CXXCPPFLAGS = [] 35 | CXXCPPFLAGS += ['-O2', '-g'] if opts.release else ['-O0', '-ggdb3', '-fno-omit-frame-pointer'] 36 | CXXCPPFLAGS += env.get('_%s-%s_CXXCPPFLAGS' % (product, target), []) 37 | CXXCPPFLAGS += env.get('%s-%s_CXXCPPFLAGS' % (product, target), []) 38 | CXXCPPFLAGS += [BITNESS] if BITNESS else [] 39 | 40 | LDFLAGS = [] 41 | LDFLAGS += env.get('_%s-%s_LDFLAGS' % (product, target), []) 42 | LDFLAGS += env.get('%s-%s_LDFLAGS' % (product, target), []) 43 | 44 | AC_VARS = [] 45 | AC_VARS += env.get('_%s-%s_AC_VARS' % (product, target), []) 46 | AC_VARS += env.get('%s-%s_AC_VARS' % (product, target), []) 47 | 48 | CONFIGURE_ENVIRONMENT = {} 49 | 50 | def set_product_env_var(var_name): 51 | val = env.get('_%s-%s_%s' % (product, target, var_name), '') 52 | if val: 53 | CONFIGURE_ENVIRONMENT[var_name] = val 54 | 55 | set_product_env_var('AR') 56 | set_product_env_var('AS') 57 | set_product_env_var('CC') 58 | set_product_env_var('CPP') 59 | set_product_env_var('CXX') 60 | set_product_env_var('CXXCPP') 61 | set_product_env_var('DLLTOOL') 62 | set_product_env_var('LD') 63 | set_product_env_var('OBJDUMP') 64 | set_product_env_var('RANLIB') 65 | set_product_env_var('CMAKE') 66 | set_product_env_var('STRIP') 67 | 68 | CONFIGURE_ENVIRONMENT['CFLAGS'] = CFLAGS 69 | CONFIGURE_ENVIRONMENT['CXXFLAGS'] = CXXFLAGS 70 | CONFIGURE_ENVIRONMENT['CPPFLAGS'] = CPPFLAGS 71 | CONFIGURE_ENVIRONMENT['CXXCPPFLAGS'] = CXXCPPFLAGS 72 | CONFIGURE_ENVIRONMENT['LDFLAGS'] = LDFLAGS 73 | 74 | CONFIGURE_ENVIRONMENT.update(env.get('_%s-%s_CONFIGURE_ENVIRONMENT' % (product, target), {})) 75 | CONFIGURE_ENVIRONMENT.update(env.get('%s-%s_CONFIGURE_ENVIRONMENT' % (product, target), {})) 76 | 77 | CONFIGURE_FLAGS = [] 78 | CONFIGURE_FLAGS += ['--host=%s' % host_triple] if host_triple else [] 79 | CONFIGURE_FLAGS += ['--cache-file=%s/%s-%s-%s.config.cache' % (opts.configure_dir, product, target, opts.configuration)] 80 | CONFIGURE_FLAGS += ['--prefix=%s/%s-%s-%s' % (opts.install_dir, product, target, opts.configuration)] 81 | CONFIGURE_FLAGS += ['--enable-cxx'] if opts.enable_cxx else [] 82 | CONFIGURE_FLAGS += env.get('_cross-runtime_%s-%s_CONFIGURE_FLAGS' % (product, target), []) 83 | CONFIGURE_FLAGS += env.get('_%s-%s_CONFIGURE_FLAGS' % (product, target), []) 84 | CONFIGURE_FLAGS += env.get('%s-%s_CONFIGURE_FLAGS' % (product, target), []) 85 | 86 | if llvm: 87 | CONFIGURE_FLAGS += ['--with-llvm=%s/llvm-%s' % (opts.install_dir, llvm)] 88 | 89 | env['_runtime_%s-%s_AC_VARS' % (product, target)] = AC_VARS 90 | env['_runtime_%s-%s_CONFIGURE_ENVIRONMENT' % (product, target)] = CONFIGURE_ENVIRONMENT 91 | env['_runtime_%s-%s_CONFIGURE_FLAGS' % (product, target)] = CONFIGURE_FLAGS 92 | 93 | 94 | def setup_runtime_cross_template(env: dict, opts: RuntimeOpts, product: str, target: str, host_triple: str, 95 | target_triple: str, device_target: str, llvm: str, offsets_dumper_abi: str): 96 | CONFIGURE_FLAGS = [ 97 | '--target=%s' % target_triple, 98 | '--with-cross-offsets=%s.h' % target_triple, 99 | '--with-llvm=%s/llvm-%s' % (opts.install_dir, llvm) 100 | ] 101 | 102 | env['_cross-runtime_%s-%s_CONFIGURE_FLAGS' % (product, target)] = CONFIGURE_FLAGS 103 | 104 | new_offsets_tool_path = '%s/mono/tools/offsets-tool/offsets-tool.py' % opts.mono_source_root 105 | old_offsets_tool_path = '%s/tools/offsets-tool-py/offsets-tool.py' % opts.mono_source_root 106 | 107 | old_offsets_tool = not os.path.isfile(new_offsets_tool_path) 108 | 109 | offsets_tool_env = None 110 | 111 | if old_offsets_tool: 112 | # Setup old offsets-tool-py if present (new location doesn't require setup) 113 | run_command('make', ['-C', '%s/tools/offsets-tool-py' % opts.mono_source_root, 'setup'], name='make offsets-tool-py') 114 | 115 | # Run offsets-tool in its virtual env 116 | virtualenv_vars = source('%s/tools/offsets-tool-py/offtool/bin/activate' % opts.mono_source_root) 117 | 118 | offsets_tool_env = os.environ.copy() 119 | offsets_tool_env.update(virtualenv_vars) 120 | 121 | build_dir = '%s/%s-%s-%s' % (opts.configure_dir, product, target, opts.configuration) 122 | mkdir_p(build_dir) 123 | 124 | run_command('python3', [ 125 | old_offsets_tool_path if old_offsets_tool else new_offsets_tool_path, 126 | '--targetdir=%s/%s-%s-%s' % (opts.configure_dir, product, device_target, opts.configuration), 127 | '--abi=%s' % offsets_dumper_abi, 128 | '--monodir=%s' % opts.mono_source_root, 129 | '--outfile=%s/%s.h' % (build_dir, target_triple) 130 | ] + env['_%s-%s_OFFSETS_DUMPER_ARGS' % (product, target)], 131 | env=offsets_tool_env, name='offsets-tool') 132 | 133 | # Runtime template 134 | setup_runtime_template(env, opts, product, target, host_triple) 135 | 136 | 137 | def run_autogen(opts: RuntimeOpts): 138 | autogen_env = os.environ.copy() 139 | autogen_env['NOCONFIGURE'] = '1' 140 | 141 | if not find_executable('glibtoolize') and 'CUSTOM_GLIBTOOLIZE_PATH' in os.environ: 142 | autogen_env['PATH'] = os.environ['CUSTOM_GLIBTOOLIZE_PATH'] + ':' + autogen_env['PATH'] 143 | 144 | run_command(os.path.join(opts.mono_source_root, 'autogen.sh'), cwd=opts.mono_source_root, env=autogen_env, name='autogen') 145 | 146 | 147 | def run_configure(env: dict, opts: RuntimeOpts, product: str, target: str): 148 | build_dir = path_join(opts.configure_dir, '%s-%s-%s' % (product, target, opts.configuration)) 149 | mkdir_p(build_dir) 150 | 151 | def str_dict_val(val): 152 | if isinstance(val, list): 153 | return ' '.join(val) # Don't need to surround with quotes 154 | return val 155 | 156 | ac_vars = env['_runtime_%s-%s_AC_VARS' % (product, target)] 157 | configure_env_args = env['_runtime_%s-%s_CONFIGURE_ENVIRONMENT' % (product, target)] 158 | configure_env_args = [('%s=%s' % (key, str_dict_val(value))) for (key, value) in configure_env_args.items()] 159 | configure_flags = env['_runtime_%s-%s_CONFIGURE_FLAGS' % (product, target)] 160 | 161 | configure = path_join(opts.mono_source_root, 'configure') 162 | configure_args = ac_vars + configure_env_args + configure_flags 163 | 164 | configure_env = os.environ.copy() 165 | target_extra_path = env.get('_%s-%s_PATH' % (product, target), '') 166 | if target_extra_path: 167 | configure_env['PATH'] += ':' + target_extra_path 168 | 169 | run_command(configure, args=configure_args, cwd=build_dir, env=configure_env, name='configure') 170 | -------------------------------------------------------------------------------- /files/patches/offsets-tool-newer-clang.diff: -------------------------------------------------------------------------------- 1 | From 63e677701d35c7e2dad4fe74a84eb935cd396155 Mon Sep 17 00:00:00 2001 2 | From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= 3 | Date: Mon, 15 May 2023 20:46:12 +0200 4 | Subject: [PATCH] [mono] Update offsets-tool python clang binding so it works 5 | with newer clang (#86256) 6 | 7 | They introduced a breaking change to CursorKind.TRANSLATION_UNIT in https://github.com/llvm/llvm-project/commit/bb83f8e70bd1d56152f02307adacd718cd67e312, which means we hit an issue when using the binding against a newer clang. Update the binding to the latest upstream and add a tweak so it still works with older clang versions. 8 | --- 9 | mono/tools/offsets-tool/clang/cindex.py | 137 +++++++++++++++++- 10 | 1 file changed, 135 insertions(+), 2 deletions(-) 11 | 12 | diff --git a/mono/tools/offsets-tool/clang/cindex.py b/mono/tools/offsets-tool/clang/cindex.py 13 | index 44c6f49096f495..0ed1c199ba44dd 100644 14 | --- a/mono/tools/offsets-tool/clang/cindex.py 15 | +++ b/mono/tools/offsets-tool/clang/cindex.py 16 | @@ -286,6 +286,11 @@ def offset(self): 17 | """Get the file offset represented by this source location.""" 18 | return self._get_instantiation()[3] 19 | 20 | + #@property 21 | + #def is_in_system_header(self): 22 | + # """Returns true if the given source location is in a system header.""" 23 | + # return conf.lib.clang_Location_isInSystemHeader(self) 24 | + 25 | def __eq__(self, other): 26 | return conf.lib.clang_equalLocations(self, other) 27 | 28 | @@ -646,6 +651,11 @@ def name(self): 29 | 30 | @classmethod 31 | def from_id(cls, id): 32 | + if cls == CursorKind and id == 300: 33 | + # --- DOTNET change --- 34 | + # The id of CursorKind.TRANSLATION_UNIT changed in https://github.com/llvm/llvm-project/commit/bb83f8e70bd1d56152f02307adacd718cd67e312, 35 | + # add mapping from the old to the new value so using the binding with an older clang still works. 36 | + return cls._kinds[350] 37 | if id >= len(cls._kinds) or cls._kinds[id] is None: 38 | raise ValueError('Unknown template argument kind %d' % id) 39 | return cls._kinds[id] 40 | @@ -1152,7 +1162,7 @@ def __repr__(self): 41 | # Objective-C's @synchronized statement. 42 | CursorKind.OBJC_AT_SYNCHRONIZED_STMT = CursorKind(220) 43 | 44 | -# Objective-C's autorealease pool statement. 45 | +# Objective-C's autorelease pool statement. 46 | CursorKind.OBJC_AUTORELEASE_POOL_STMT = CursorKind(221) 47 | 48 | # Objective-C's for collection statement. 49 | @@ -1312,7 +1322,7 @@ def __repr__(self): 50 | # 51 | # The translation unit cursor exists primarily to act as the root cursor for 52 | # traversing the contents of a translation unit. 53 | -CursorKind.TRANSLATION_UNIT = CursorKind(300) 54 | +CursorKind.TRANSLATION_UNIT = CursorKind(350) 55 | 56 | ### 57 | # Attributes 58 | @@ -1473,6 +1483,107 @@ def is_default_method(self): 59 | """ 60 | return conf.lib.clang_CXXMethod_isDefaulted(self) 61 | 62 | + #def is_deleted_method(self): 63 | + # """Returns True if the cursor refers to a C++ member function or member 64 | + # function template that is declared '= delete'. 65 | + # """ 66 | + # return conf.lib.clang_CXXMethod_isDeleted(self) 67 | + 68 | + #def is_copy_assignment_operator_method(self): 69 | + # """Returnrs True if the cursor refers to a copy-assignment operator. 70 | + 71 | + # A copy-assignment operator `X::operator=` is a non-static, 72 | + # non-template member function of _class_ `X` with exactly one 73 | + # parameter of type `X`, `X&`, `const X&`, `volatile X&` or `const 74 | + # volatile X&`. 75 | + 76 | + 77 | + # That is, for example, the `operator=` in: 78 | + 79 | + # class Foo { 80 | + # bool operator=(const volatile Foo&); 81 | + # }; 82 | + 83 | + # Is a copy-assignment operator, while the `operator=` in: 84 | + 85 | + # class Bar { 86 | + # bool operator=(const int&); 87 | + # }; 88 | + 89 | + # Is not. 90 | + # """ 91 | + # return conf.lib.clang_CXXMethod_isCopyAssignmentOperator(self) 92 | + 93 | + #def is_move_assignment_operator_method(self): 94 | + # """Returnrs True if the cursor refers to a move-assignment operator. 95 | + 96 | + # A move-assignment operator `X::operator=` is a non-static, 97 | + # non-template member function of _class_ `X` with exactly one 98 | + # parameter of type `X&&`, `const X&&`, `volatile X&&` or `const 99 | + # volatile X&&`. 100 | + 101 | + 102 | + # That is, for example, the `operator=` in: 103 | + 104 | + # class Foo { 105 | + # bool operator=(const volatile Foo&&); 106 | + # }; 107 | + 108 | + # Is a move-assignment operator, while the `operator=` in: 109 | + 110 | + # class Bar { 111 | + # bool operator=(const int&&); 112 | + # }; 113 | + 114 | + # Is not. 115 | + # """ 116 | + # return conf.lib.clang_CXXMethod_isMoveAssignmentOperator(self) 117 | + 118 | + #def is_explicit_method(self): 119 | + # """Determines if a C++ constructor or conversion function is 120 | + # explicit, returning 1 if such is the case and 0 otherwise. 121 | + 122 | + # Constructors or conversion functions are declared explicit through 123 | + # the use of the explicit specifier. 124 | + 125 | + # For example, the following constructor and conversion function are 126 | + # not explicit as they lack the explicit specifier: 127 | + 128 | + # class Foo { 129 | + # Foo(); 130 | + # operator int(); 131 | + # }; 132 | + 133 | + # While the following constructor and conversion function are 134 | + # explicit as they are declared with the explicit specifier. 135 | + 136 | + # class Foo { 137 | + # explicit Foo(); 138 | + # explicit operator int(); 139 | + # }; 140 | + 141 | + # This method will return 0 when given a cursor pointing to one of 142 | + # the former declarations and it will return 1 for a cursor pointing 143 | + # to the latter declarations. 144 | + 145 | + # The explicit specifier allows the user to specify a 146 | + # conditional compile-time expression whose value decides 147 | + # whether the marked element is explicit or not. 148 | + 149 | + # For example: 150 | + 151 | + # constexpr bool foo(int i) { return i % 2 == 0; } 152 | + 153 | + # class Foo { 154 | + # explicit(foo(1)) Foo(); 155 | + # explicit(foo(2)) operator int(); 156 | + # } 157 | + 158 | + # This method will return 0 for the constructor and 1 for 159 | + # the conversion function. 160 | + # """ 161 | + # return conf.lib.clang_CXXMethod_isExplicit(self) 162 | + 163 | def is_mutable_field(self): 164 | """Returns True if the cursor refers to a C++ field that is declared 165 | 'mutable'. 166 | @@ -2059,6 +2170,7 @@ def __repr__(self): 167 | TypeKind.OBJCSEL = TypeKind(29) 168 | TypeKind.FLOAT128 = TypeKind(30) 169 | TypeKind.HALF = TypeKind(31) 170 | +TypeKind.IBM128 = TypeKind(40) 171 | TypeKind.COMPLEX = TypeKind(100) 172 | TypeKind.POINTER = TypeKind(101) 173 | TypeKind.BLOCKPOINTER = TypeKind(102) 174 | @@ -2122,6 +2234,7 @@ def __repr__(self): 175 | TypeKind.OCLRESERVEID = TypeKind(160) 176 | 177 | TypeKind.EXTVECTOR = TypeKind(176) 178 | +TypeKind.ATOMIC = TypeKind(177) 179 | 180 | class RefQualifierKind(BaseEnumeration): 181 | """Describes a specific ref-qualifier of a type.""" 182 | @@ -3424,6 +3537,22 @@ def cursor(self): 183 | [Cursor], 184 | bool), 185 | 186 | + #("clang_CXXMethod_isDeleted", 187 | + # [Cursor], 188 | + # bool), 189 | + 190 | + #("clang_CXXMethod_isCopyAssignmentOperator", 191 | + # [Cursor], 192 | + # bool), 193 | + 194 | + #("clang_CXXMethod_isMoveAssignmentOperator", 195 | + # [Cursor], 196 | + # bool), 197 | + 198 | + #("clang_CXXMethod_isExplicit", 199 | + # [Cursor], 200 | + # bool), 201 | + 202 | ("clang_CXXMethod_isPureVirtual", 203 | [Cursor], 204 | bool), 205 | @@ -4012,6 +4141,10 @@ def cursor(self): 206 | [Cursor], 207 | c_longlong), 208 | 209 | + #("clang_Location_isInSystemHeader", 210 | + # [SourceLocation], 211 | + # bool), 212 | + 213 | ("clang_Type_getAlignOf", 214 | [Type], 215 | c_longlong), 216 | -------------------------------------------------------------------------------- /os_utils.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import os.path 4 | from options import * 5 | 6 | 7 | class BuildError(Exception): 8 | '''Generic exception for custom build errors''' 9 | def __init__(self, msg): 10 | super(BuildError, self).__init__(msg) 11 | self.message = msg 12 | 13 | 14 | def run_command(command, args=[], cwd=None, env=None, name='command'): 15 | def cmd_args_to_str(cmd_args): 16 | return ' '.join([arg if not ' ' in arg else '"%s"' % arg for arg in cmd_args]) 17 | 18 | assert isinstance(command, str) and isinstance(args, list) 19 | args = [command] + args 20 | 21 | check_call_args = {} 22 | if cwd is not None: 23 | check_call_args['cwd'] = cwd 24 | if env is not None: 25 | check_call_args['env'] = env 26 | 27 | import subprocess 28 | try: 29 | print('Running command \'%s\': %s' % (name, subprocess.list2cmdline(args))) 30 | subprocess.check_call(args, **check_call_args) 31 | print('Command \'%s\' completed successfully' % name) 32 | except subprocess.CalledProcessError as e: 33 | raise BuildError('\'%s\' exited with error code: %s' % (name, e.returncode)) 34 | 35 | 36 | print_env_sh_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'print_env.sh') 37 | 38 | 39 | def source(script: str, cwd=None) -> dict: 40 | popen_args = {} 41 | if cwd is not None: 42 | popen_args['cwd'] = cwd 43 | 44 | import subprocess 45 | cmd = 'bash -c \'source %s; bash %s\'' % (script, print_env_sh_path) 46 | proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True, **popen_args) 47 | output = proc.communicate()[0] 48 | return dict(line.split('=', 1) for line in output.decode().split('\x00') if line) 49 | 50 | 51 | # Creates the directory if no other file or directory with the same path exists 52 | def mkdir_p(path): 53 | if not os.path.exists(path): 54 | print('creating directory: ' + path) 55 | os.makedirs(path) 56 | 57 | 58 | # Remove files and/or directories recursively 59 | def rm_rf(*paths): 60 | from shutil import rmtree 61 | for path in paths: 62 | if os.path.isfile(path): 63 | print('removing file: ' + path) 64 | os.remove(path) 65 | elif os.path.isdir(path): 66 | print('removing directory and its contents: ' + path) 67 | rmtree(path) 68 | 69 | 70 | ENV_PATH_SEP = ';' if os.name == 'nt' else ':' 71 | 72 | 73 | def find_executable(name) -> str: 74 | is_windows = os.name == 'nt' 75 | windows_exts = os.environ['PATHEXT'].split(ENV_PATH_SEP) if is_windows else None 76 | path_dirs = os.environ['PATH'].split(ENV_PATH_SEP) 77 | 78 | search_dirs = path_dirs + [os.getcwd()] # cwd is last in the list 79 | 80 | for dir in search_dirs: 81 | path = os.path.join(dir, name) 82 | 83 | if is_windows: 84 | for extension in windows_exts: 85 | path_with_ext = path + extension 86 | 87 | if os.path.isfile(path_with_ext) and os.access(path_with_ext, os.X_OK): 88 | return path_with_ext 89 | else: 90 | if os.path.isfile(path) and os.access(path, os.X_OK): 91 | return path 92 | 93 | return '' 94 | 95 | 96 | def replace_in_new_file(src_file, search, replace, dst_file): 97 | with open(src_file, 'r') as file: 98 | content = file.read() 99 | 100 | content = content.replace(search, replace) 101 | 102 | with open(dst_file, 'w') as file: 103 | file.write(content) 104 | 105 | 106 | def replace_in_file(filepath, search, replace): 107 | replace_in_new_file(src_file=filepath, search=search, replace=replace, dst_file=filepath) 108 | 109 | 110 | def touch(filepath: str): 111 | import pathlib 112 | pathlib.Path(filepath).touch() 113 | 114 | 115 | def get_emsdk_root(): 116 | # Shamelessly copied from Godot's detect.py 117 | em_config_file = os.getenv('EM_CONFIG') or os.path.expanduser('~/.emscripten') 118 | if not os.path.exists(em_config_file): 119 | raise BuildError("Emscripten configuration file '%s' does not exist" % em_config_file) 120 | with open(em_config_file) as f: 121 | em_config = {} 122 | try: 123 | # Emscripten configuration file is a Python file with simple assignments. 124 | exec(f.read(), em_config) 125 | except StandardError as e: 126 | raise BuildError("Emscripten configuration file '%s' is invalid:\n%s" % (em_config_file, e)) 127 | if 'BINARYEN_ROOT' in em_config and os.path.isdir(os.path.join(em_config.get('BINARYEN_ROOT'), 'emscripten')): 128 | # New style, emscripten path as a subfolder of BINARYEN_ROOT 129 | return os.path.join(em_config.get('BINARYEN_ROOT'), 'emscripten') 130 | elif 'EMSCRIPTEN_ROOT' in em_config: 131 | # Old style (but can be there as a result from previous activation, so do last) 132 | return em_config.get('EMSCRIPTEN_ROOT') 133 | else: 134 | raise BuildError("'BINARYEN_ROOT' or 'EMSCRIPTEN_ROOT' missing in Emscripten configuration file '%s'" % em_config_file) 135 | 136 | 137 | def globs(pathnames, dirpath='.'): 138 | import glob 139 | files = [] 140 | for pathname in pathnames: 141 | files.extend(glob.glob(os.path.join(dirpath, pathname))) 142 | return files 143 | 144 | 145 | def xcrun_find_sdk(sdk_name): 146 | import subprocess 147 | xcrun_output = subprocess.check_output(['xcrun', '--sdk', sdk_name, '--show-sdk-path']).decode().strip() 148 | if xcrun_output.startswith('xcrun: error: SDK "%s" cannot be located' % sdk_name): 149 | return '' 150 | sdk_path = xcrun_output 151 | return sdk_path 152 | 153 | 154 | def chmod_plus_x(file): 155 | import os 156 | import stat 157 | umask = os.umask(0) 158 | os.umask(umask) 159 | st = os.stat(file) 160 | os.chmod(file, st.st_mode | ((stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) & ~umask)) 161 | 162 | 163 | def get_clang_resource_dir(clang_command): 164 | import shlex 165 | from subprocess import check_output 166 | return check_output(shlex.split(clang_command) + ['-print-resource-dir']).strip().decode('utf-8') 167 | 168 | 169 | def try_find_libclang(toolchain_path: str = '', llvm_config=''): 170 | import sys 171 | from subprocess import check_output 172 | 173 | hint_paths = [] 174 | 175 | if toolchain_path: 176 | libclang = os.path.join(toolchain_path, 'usr', 'lib', 'libclang.dylib') 177 | if os.path.isfile(libclang): 178 | print('Found libclang at: \'%s\'' % libclang) 179 | return libclang 180 | 181 | if not llvm_config: 182 | llvm_config = find_executable('llvm-config') 183 | if not llvm_config: 184 | print('WARNING: llvm-config not found') 185 | return '' 186 | elif not os.path.isfile(llvm_config): 187 | raise RuntimeError('Specified llvm-config file not found: \'%s\'' % llvm_config) 188 | 189 | llvm_libdir = check_output([llvm_config, '--libdir']).strip().decode('utf-8') 190 | if llvm_libdir: 191 | libsuffix = '.dylib' if sys.platform == 'darwin' else '.so' 192 | hints = ['libclang', 'clang'] 193 | libclang = next((p for p in [os.path.join(llvm_libdir, h + libsuffix) for h in hints] if os.path.isfile(p)), '') 194 | if libclang: 195 | print('Found libclang at: \'%s\'' % libclang) 196 | return libclang 197 | 198 | return '' 199 | 200 | 201 | def create_osxcross_wrapper(opts: RuntimeOpts, product: str, target: str, toolchain_path : str): 202 | # OSXCROSS toolchain executables use rpath to locate the toolchain's shared libraries. 203 | # However, when moving the toolchain without care, the rpaths can be broken. 204 | # Since fixing the rpaths can be tedious, we use this wrapper to override LD_LIBRARY_PATH. 205 | # The reason we don't just run configure and make with LD_LIBRARY_PATH is because 206 | # we want the resulting configuration to be independent from out python scripts. 207 | 208 | wrapper_src = """#!/bin/bash 209 | OSXCROSS_COMMAND=$1; 210 | shift; 211 | export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:%s"; 212 | ${OSXCROSS_COMMAND} "$@"; 213 | exit $?; 214 | """ % os.path.join(toolchain_path, 'lib') 215 | 216 | build_dir = os.path.join(opts.configure_dir, '%s-%s-%s' % (product, target, opts.configuration)) 217 | wrapper_path = os.path.join(build_dir, 'osxcross_cmd_wrapper.sh') 218 | 219 | mkdir_p(build_dir) 220 | 221 | with open(wrapper_path, 'w') as f: 222 | f.write(wrapper_src) 223 | 224 | chmod_plus_x(wrapper_path) 225 | 226 | return wrapper_path 227 | -------------------------------------------------------------------------------- /bcl.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import os.path 5 | import runtime 6 | import sys 7 | 8 | from os.path import join as path_join 9 | from options import * 10 | from os_utils import * 11 | 12 | 13 | product_values = ['desktop', 'desktop-win32', 'android', 'ios', 'wasm'] 14 | profiles_table = { 15 | 'desktop': ['net_4_x'], 16 | 'desktop-win32': ['net_4_x'], 17 | 'android': ['monodroid', 'monodroid_tools'], 18 | 'ios': ['monotouch', 'monotouch_runtime', 'monotouch_tools'], 19 | 'wasm': ['wasm', 'wasm_tools'] 20 | } 21 | test_profiles_table = { 22 | 'desktop': [], 23 | 'desktop-win32': [], 24 | 'android': ['monodroid', 'monodroid_tools'], 25 | 'ios': ['monotouch'], 26 | 'wasm': ['wasm'] 27 | } 28 | 29 | def get_install_dir(opts: BaseOpts, product: str): 30 | return path_join(opts.install_dir, '%s-bcl' % product) 31 | 32 | def get_profile_dir(profile: str, product: str): 33 | if product == 'desktop-win32': 34 | return profile + '-win32' 35 | else: 36 | return profile 37 | 38 | def get_profile_install_dirs(opts: BaseOpts, product: str): 39 | install_dir = get_install_dir(opts, product) 40 | profiles = profiles_table[product] 41 | return [path_join(install_dir, get_profile_dir(profile, product)) for profile in profiles] 42 | 43 | def configure_bcl(opts: BclOpts, product: str): 44 | stamp_file = path_join(opts.configure_dir, '.stamp-bcl-configure') 45 | 46 | if os.path.isfile(stamp_file): 47 | return 48 | 49 | if not os.path.isfile(path_join(opts.mono_source_root, 'configure')): 50 | runtime.run_autogen(opts) 51 | 52 | build_dir = path_join(opts.configure_dir, 'bcl') 53 | mkdir_p(build_dir) 54 | 55 | CONFIGURE_FLAGS = [ 56 | '--disable-boehm', 57 | '--disable-nls', 58 | '--disable-support-build', 59 | '--with-mcs-docs=no' 60 | ] 61 | 62 | if product == 'desktop-win32': 63 | CONFIGURE_FLAGS += [ 64 | '--enable-btls', 65 | '--enable-btls-lib' 66 | ] 67 | else: 68 | CONFIGURE_FLAGS += [ 69 | '--disable-btls-lib' 70 | ] 71 | 72 | configure = path_join(opts.mono_source_root, 'configure') 73 | configure_args = CONFIGURE_FLAGS 74 | 75 | run_command(configure, args=configure_args, cwd=build_dir, name='configure bcl') 76 | 77 | touch(stamp_file) 78 | 79 | 80 | def make_bcl(opts: BclOpts): 81 | stamp_file = path_join(opts.configure_dir, '.stamp-bcl-make') 82 | 83 | if os.path.isfile(stamp_file): 84 | return 85 | 86 | build_dir = path_join(opts.configure_dir, 'bcl') 87 | 88 | make_args = make_default_args(opts) 89 | make_args += ['-C', build_dir, '-C', 'mono'] 90 | 91 | run_command('make', args=make_args, name='make bcl') 92 | 93 | touch(stamp_file) 94 | 95 | 96 | def build_bcl(opts: BclOpts, product: str): 97 | configure_bcl(opts, product) 98 | make_bcl(opts) 99 | 100 | 101 | def clean_bcl(opts: BclOpts): 102 | configure_stamp_file = path_join(opts.configure_dir, '.stamp-bcl-configure') 103 | make_stamp_file = path_join(opts.configure_dir, '.stamp-bcl-make') 104 | build_dir = path_join(opts.configure_dir, 'bcl') 105 | rm_rf(configure_stamp_file, make_stamp_file, build_dir) 106 | 107 | 108 | def make_product(opts: BclOpts, product: str): 109 | build_bcl(opts, product) 110 | 111 | build_dir = path_join(opts.configure_dir, 'bcl') 112 | 113 | profiles = profiles_table[product] 114 | test_profiles = test_profiles_table[product] 115 | 116 | install_dir = get_install_dir(opts, product) 117 | 118 | mkdir_p(install_dir) 119 | 120 | make_args = make_default_args(opts) 121 | make_args += ['-C', build_dir, '-C', 'runtime', 'all-mcs', 'build_profiles=%s' % ' '.join(profiles)] 122 | 123 | if product == 'desktop-win32': 124 | make_args += ['PROFILE_PLATFORM=win32'] # Requires patch: 'bcl-profile-platform-override.diff' 125 | 126 | run_command('make', args=make_args, name='make profiles') 127 | 128 | if opts.tests and len(test_profiles) > 0: 129 | test_make_args = make_default_args(opts) 130 | test_make_args += ['-C', build_dir, '-C', 'runtime', 'test', 'xunit-test', 'test_profiles=%s' % ' '.join(test_profiles)] 131 | 132 | run_command('make', args=test_make_args, name='make tests') 133 | 134 | # Copy the bcl profiles to the output directory 135 | from shutil import copytree 136 | for profile in profiles: 137 | profile_dir = get_profile_dir(profile, product) 138 | copytree('%s/mcs/class/lib/%s' % (opts.mono_source_root, profile_dir), '%s/%s' % (install_dir, profile_dir)) 139 | 140 | # Remove unneeded files 141 | import glob 142 | file_patterns = [] 143 | file_patterns += ['.*'] # Recursively remove hidden files we shoudln't have copied (e.g.: .stamp) 144 | file_patterns += ['*.dll.so', '*.exe.so'] # Remove pre-built AOT modules. We don't need them and they take a lot of space. 145 | file_patterns += ['*.pdb'] if opts.remove_pdb else [] 146 | for profile in profiles: 147 | for file_pattern in file_patterns: 148 | file_pattern_recursive = '%s/**/%s' % (install_dir, file_pattern) 149 | [rm_rf(x) for x in glob.iglob(file_pattern_recursive, recursive=True)] 150 | 151 | # WebAssembly.Framework.sln 152 | if product == 'wasm': 153 | wasm_fx_output_dir = '%s/sdks/wasm/framework/netstandard2.0' % opts.mono_source_root 154 | wasm_fx_sln_file = '%s/sdks/wasm/framework/src/WebAssembly.Framework.sln' % opts.mono_source_root 155 | output_dir = path_join(install_dir, 'wasm') 156 | 157 | from msbuild_helper import build_solution 158 | build_solution(wasm_fx_sln_file, 'Release') 159 | 160 | import shutil 161 | from glob import glob 162 | 163 | fglob = glob(path_join(wasm_fx_output_dir, '*.dll')) 164 | 165 | if not opts.remove_pdb: 166 | fglob.extend(glob(path_join(wasm_fx_output_dir, '*.pdb'))) 167 | 168 | for file in fglob: 169 | if os.path.isfile(file): 170 | shutil.copy(file, output_dir) 171 | 172 | # godot_android_ext profile (custom 'Mono.Android.dll') 173 | if product == 'android': 174 | this_script_dir = os.path.dirname(os.path.realpath(__file__)) 175 | monodroid_profile_dir = '%s/%s' % (install_dir, 'monodroid') 176 | godot_profile_dir = '%s/%s' % (install_dir, 'godot_android_ext') 177 | refs = ['mscorlib.dll', 'System.Core.dll', 'System.dll'] 178 | 179 | mkdir_p(godot_profile_dir) 180 | 181 | android_env_csc_args = [ 182 | path_join(this_script_dir, 'files', 'godot-AndroidEnvironment.cs'), 183 | '-target:library', '-out:%s' % path_join(godot_profile_dir, 'Mono.Android.dll'), 184 | '-nostdlib', '-noconfig', '-langversion:latest' 185 | ] 186 | android_env_csc_args += ['-r:%s' % path_join(monodroid_profile_dir, r) for r in refs] 187 | 188 | run_command('csc', android_env_csc_args) 189 | 190 | # (custom 'Xamarin.iOS.dll') 191 | if product == 'ios': 192 | this_script_dir = os.path.dirname(os.path.realpath(__file__)) 193 | monotouch_profile_dir = '%s/%s' % (install_dir, 'monotouch') 194 | refs = ['mscorlib.dll', 'System.Net.Http.dll'] 195 | 196 | mkdir_p(monotouch_profile_dir) 197 | 198 | ios_env_csc_args = [ 199 | path_join(this_script_dir, 'files', 'xi.cs'), 200 | '-keyfile:' + path_join(this_script_dir, 'files', 'xi.snk'), 201 | '-out:%s' % path_join(monotouch_profile_dir, 'Xamarin.iOS.dll'), 202 | '-optimize', '-deterministic', '-publicsign', '-target:library', 203 | '-nostdlib', '-noconfig', '-langversion:latest' 204 | ] 205 | ios_env_csc_args += ['-r:%s' % path_join(monotouch_profile_dir, r) for r in refs] 206 | 207 | run_command('csc', ios_env_csc_args) 208 | 209 | 210 | def clean_product(opts: BclOpts, product: str): 211 | clean_bcl(opts) 212 | 213 | install_dir = get_install_dir(opts, product) 214 | rm_rf(install_dir) 215 | 216 | 217 | def main(raw_args): 218 | import cmd_utils 219 | from cmd_utils import custom_bool 220 | 221 | actions = { 222 | 'make': make_product, 223 | 'clean': clean_product 224 | } 225 | 226 | parser = cmd_utils.build_arg_parser(description='Builds the Mono BCL') 227 | 228 | default_help = 'default: %(default)s' 229 | 230 | parser.add_argument('action', choices=actions.keys()) 231 | parser.add_argument('--product', choices=product_values, action='append', required=True) 232 | parser.add_argument('--tests', action='store_true', default=False, help=default_help) 233 | parser.add_argument('--remove-pdb', type=custom_bool, default=True, help=default_help) 234 | 235 | cmd_utils.add_base_arguments(parser, default_help) 236 | 237 | args = parser.parse_args(raw_args) 238 | 239 | opts = bcl_opts_from_args(args) 240 | products = args.product 241 | 242 | try: 243 | for product in products: 244 | action = actions[args.action] 245 | action(opts, product) 246 | except BuildError as e: 247 | sys.exit(e.message) 248 | 249 | 250 | if __name__ == '__main__': 251 | from sys import argv 252 | main(argv[1:]) 253 | -------------------------------------------------------------------------------- /wasm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import os.path 5 | import runtime 6 | import sys 7 | 8 | from options import * 9 | from os_utils import * 10 | from os.path import join as path_join 11 | 12 | 13 | runtime_targets = ['runtime', 'runtime-threads', 'runtime-dynamic'] 14 | cross_targets = [] # ['cross'] # TODO 15 | cross_mxe_targets = [] # ['cross-win'] # TODO 16 | 17 | 18 | def is_cross(target) -> bool: 19 | return target in cross_targets or is_cross_mxe(target) 20 | 21 | 22 | def is_cross_mxe(target) -> bool: 23 | return target in cross_mxe_targets 24 | 25 | 26 | def setup_wasm_target_template(env: dict, opts: RuntimeOpts, target: str): 27 | extra_target_envs = { 28 | 'runtime-threads': { 29 | 'wasm_runtime-threads_CFLAGS': ['-s', 'USE_PTHREADS=1', '-pthread'], 30 | 'wasm_runtime-threads_CXXFLAGS': ['-s', 'USE_PTHREADS=1', '-pthread'] 31 | }, 32 | 'runtime-dynamic': { 33 | 'wasm_runtime-dynamic_CFLAGS': ['-s', 'WASM_OBJECT_FILES=0'], 34 | 'wasm_runtime-dynamic_CXXFLAGS': ['-s', 'WASM_OBJECT_FILES=0'] 35 | } 36 | } 37 | 38 | if target in extra_target_envs: 39 | env.update(extra_target_envs[target]) 40 | 41 | CFLAGS = ['-fexceptions'] 42 | CFLAGS += ['-Os', '-g'] if opts.release else ['-O0', '-ggdb3', '-fno-omit-frame-pointer'] 43 | CXXFLAGS = CFLAGS + ['-s', 'DISABLE_EXCEPTION_CATCHING=0'] 44 | 45 | CONFIGURE_FLAGS = [ 46 | '--disable-mcs-build', 47 | '--disable-nls', 48 | '--disable-boehm', 49 | '--disable-btls', 50 | '--with-lazy-gc-thread-creation=yes', 51 | '--with-libgc=none', 52 | '--disable-executables', 53 | '--disable-support-build', 54 | '--disable-visibility-hidden', 55 | '--enable-maintainer-mode', 56 | '--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', 57 | '--host=wasm32', 58 | '--enable-llvm-runtime', 59 | '--enable-icall-export', 60 | '--disable-icall-tables', 61 | '--disable-crash-reporting', 62 | '--with-bitcode=yes' 63 | ] 64 | 65 | CONFIGURE_FLAGS += ['--enable-cxx'] if opts.enable_cxx else [] 66 | 67 | CONFIGURE_FLAGS += [ 68 | '--cache-file=%s/wasm-%s-%s.config.cache' % (opts.configure_dir, target, opts.configuration), 69 | '--prefix=%s/wasm-%s-%s' % (opts.install_dir, target, opts.configuration), 70 | 'CFLAGS=%s %s' % (' '.join(CFLAGS), ' '.join(env.get('wasm_%s_CFLAGS' % target, ''))), 71 | 'CXXFLAGS=%s %s' % (' '.join(CXXFLAGS), ' '.join(env.get('wasm_%s_CXXFLAGS' % target, ''))) 72 | ] 73 | 74 | CONFIGURE_FLAGS += env.get('wasm_%s_CONFIGURE_FLAGS' % target, []) 75 | 76 | env['_wasm_%s_CONFIGURE_FLAGS' % target] = CONFIGURE_FLAGS 77 | env['_wasm_%s_AC_VARS' % target] = ['ac_cv_func_shm_open_working_with_mmap=no'] 78 | 79 | 80 | def wasm_run_configure(env: dict, opts: RuntimeOpts, product: str, target: str, emsdk_root: str): 81 | build_dir = path_join(opts.configure_dir, '%s-%s-%s' % (product, target, opts.configuration)) 82 | mkdir_p(build_dir) 83 | 84 | def str_dict_val(val): 85 | if isinstance(val, list): 86 | return ' '.join(val) # Don't need to surround with quotes 87 | return val 88 | 89 | ac_vars = env['_%s_%s_AC_VARS' % (product, target)] 90 | configure_flags = env['_%s_%s_CONFIGURE_FLAGS' % (product, target)] 91 | 92 | configure = path_join(opts.mono_source_root, 'configure') 93 | configure_args = ac_vars + configure_flags 94 | 95 | configure_env = os.environ.copy() 96 | 97 | target_extra_path = env.get('_%s-%s_PATH' % (product, target), '') 98 | if target_extra_path: 99 | configure_env['PATH'] += ':' + target_extra_path 100 | 101 | configure_env['PATH'] = emsdk_root + ':' + configure_env['PATH'] 102 | 103 | run_command('emconfigure', args=[configure] + configure_args, cwd=build_dir, env=configure_env, name='configure') 104 | 105 | 106 | def configure(opts: RuntimeOpts, product: str, target: str): 107 | env = {} 108 | 109 | if is_cross(target): 110 | if is_cross_mxe(target): 111 | raise RuntimeError('TODO') 112 | else: 113 | raise RuntimeError('TODO') 114 | else: 115 | setup_wasm_target_template(env, opts, target) 116 | 117 | if not os.path.isfile(path_join(opts.mono_source_root, 'configure')): 118 | runtime.run_autogen(opts) 119 | 120 | wasm_run_configure(env, opts, product, target, get_emsdk_root()) 121 | 122 | 123 | def make(opts: RuntimeOpts, product: str, target: str): 124 | env = {} 125 | 126 | emsdk_root = get_emsdk_root() 127 | 128 | build_dir = path_join(opts.configure_dir, '%s-%s-%s' % (product, target, opts.configuration)) 129 | install_dir = path_join(opts.install_dir, '%s-%s-%s' % (product, target, opts.configuration)) 130 | 131 | make_args = make_default_args(opts) 132 | make_args += ['-C', build_dir] 133 | 134 | make_env = os.environ.copy() 135 | make_env['PATH'] = emsdk_root + ':' + make_env['PATH'] 136 | 137 | run_command('emmake', args=['make'] + make_args, env=make_env, name='make') 138 | 139 | run_command('make', args=['-C', '%s/mono' % build_dir, 'install'], name='make install mono') 140 | run_command('make', args=['-C', '%s/data' % build_dir, 'install'], name='make install data') 141 | 142 | # Copy support headers 143 | 144 | from shutil import copy 145 | 146 | headers = ['crc32.h', 'deflate.h', 'inffast.h', 'inffixed.h', 'inflate.h', 'inftrees.h', 'trees.h', 'zconf.h', 'zlib.h', 'zutil.h'] 147 | dst_zlib_dir = '%s/include/support' % install_dir 148 | 149 | src_zlib_dir = None 150 | src_zlib_dir_hints = ['%s/mono/zlib' % opts.mono_source_root, '%s/support' % opts.mono_source_root] 151 | 152 | for src_zlib_dir_hint in src_zlib_dir_hints: 153 | if os.path.isfile(path_join(src_zlib_dir_hint, 'zlib.h')): 154 | src_zlib_dir = src_zlib_dir_hint 155 | break 156 | 157 | if src_zlib_dir is None: 158 | raise BuildError('Cannot find the support zlib headers in the Mono source tree. Tried the following locations: ' + str(src_zlib_dir_hints)) 159 | 160 | mkdir_p(dst_zlib_dir) 161 | 162 | for header in headers: 163 | copy(path_join(src_zlib_dir, header), dst_zlib_dir) 164 | 165 | # Copy wasm src files 166 | 167 | wasm_src_files = [ 168 | 'driver.c', 169 | 'corebindings.c', 170 | 'zlib-helper.c', 171 | 'pinvoke-tables-default.h', 172 | 'library_mono.js', 173 | 'binding_support.js', 174 | 'dotnet_support.js' 175 | ] 176 | 177 | dst_wasm_src_dir = path_join(install_dir, 'src') 178 | 179 | mkdir_p(dst_wasm_src_dir) 180 | 181 | src_dir_hints = [ 182 | '%s/sdks/wasm/src' % opts.mono_source_root, 183 | '%s/sdks/wasm/support' % opts.mono_source_root, 184 | '%s/sdks/wasm' % opts.mono_source_root 185 | ] 186 | 187 | def dir_with_file(dirs, file): 188 | return (d for d in dirs if os.path.isfile(path_join(d, file))) 189 | 190 | for wasm_src_file in wasm_src_files: 191 | src_dir = next(dir_with_file(src_dir_hints, wasm_src_file), '') 192 | if not src_dir: 193 | raise BuildError('File \'%s\' not found. Probed locations: %s' % (wasm_src_file, str(src_dir_hints))) 194 | copy(path_join(src_dir, wasm_src_file), dst_wasm_src_dir) 195 | 196 | # Older versions didn't have .NET Core support 197 | src_dir = next(dir_with_file(src_dir_hints, 'pinvoke-tables-default-netcore.h'), '') 198 | if src_dir: 199 | copy(path_join(src_dir, 'pinvoke-tables-default-netcore.h'), dst_wasm_src_dir) 200 | 201 | 202 | def clean(opts: RuntimeOpts, product: str, target: str): 203 | rm_rf( 204 | path_join(opts.configure_dir, '%s-%s-%s' % (product, target, opts.configuration)), 205 | path_join(opts.configure_dir, '%s-%s-%s.config.cache' % (product, target, opts.configuration)), 206 | path_join(opts.install_dir, '%s-%s-%s' % (product, target, opts.configuration)) 207 | ) 208 | 209 | 210 | def main(raw_args): 211 | import cmd_utils 212 | from collections import OrderedDict 213 | from typing import Callable 214 | 215 | target_shortcuts = {'all-runtime': runtime_targets} 216 | 217 | target_values = runtime_targets + cross_targets + cross_mxe_targets + list(target_shortcuts) 218 | 219 | actions = OrderedDict() 220 | actions['configure'] = configure 221 | actions['make'] = make 222 | actions['clean'] = clean 223 | 224 | parser = cmd_utils.build_arg_parser(description='Builds the Mono runtime for WebAssembly') 225 | 226 | emsdk_root_default = os.environ.get('EMSDK_ROOT', default='') 227 | 228 | default_help = 'default: %(default)s' 229 | 230 | parser.add_argument('action', choices=['configure', 'make', 'clean']) 231 | parser.add_argument('--target', choices=target_values, action='append', required=True) 232 | 233 | cmd_utils.add_runtime_arguments(parser, default_help) 234 | 235 | args = parser.parse_args(raw_args) 236 | 237 | input_action = args.action 238 | input_targets = args.target 239 | 240 | opts = runtime_opts_from_args(args) 241 | 242 | if not os.path.isdir(opts.mono_source_root): 243 | print('Mono sources directory not found: ' + opts.mono_source_root) 244 | sys.exit(1) 245 | 246 | targets = cmd_utils.expand_input_targets(input_targets, target_shortcuts) 247 | action = actions[input_action] 248 | 249 | try: 250 | for target in targets: 251 | action(opts, 'wasm', target) 252 | except BuildError as e: 253 | sys.exit(e.message) 254 | 255 | 256 | if __name__ == '__main__': 257 | from sys import argv 258 | main(argv[1:]) 259 | -------------------------------------------------------------------------------- /desktop.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import os.path 4 | import sys 5 | 6 | from os.path import join as path_join 7 | 8 | from options import * 9 | from os_utils import * 10 | import runtime 11 | 12 | 13 | # TODO: mono cross-compilers 14 | 15 | targets = { 16 | 'linux': ['x86', 'x86_64'], 17 | 'windows': ['x86', 'x86_64'], 18 | 'osx': ['arm64', 'x86_64'] 19 | } 20 | 21 | target_arch = { 22 | 'linux': { 23 | 'x86': 'i686', 24 | 'x86_64': 'x86_64' 25 | }, 26 | 'windows': { 27 | 'x86': 'i686', 28 | 'x86_64': 'x86_64' 29 | }, 30 | 'osx': { 31 | 'arm64': 'arm64', 32 | 'x86_64': 'x86_64' 33 | } 34 | } 35 | 36 | host_triples = { 37 | 'linux': { 38 | 'x86': 'i686-linux-gnu', 39 | 'x86_64': 'x86_64-linux-gnu' 40 | }, 41 | 'windows': { 42 | 'x86': 'i686-w64-mingw32', 43 | 'x86_64': 'x86_64-w64-mingw32' 44 | }, 45 | 'osx': { 46 | 'arm64': 'aarch64-apple-darwin20', 47 | 'x86_64': 'x86_64-apple-darwin' 48 | } 49 | } 50 | 51 | llvm_table = { 52 | 'linux': { 53 | 'x86': 'llvm32', 54 | 'x86_64': 'llvm64' 55 | }, 56 | 'windows': { 57 | 'x86': 'llvm32', 58 | 'x86_64': 'llvm64' 59 | }, 60 | 'osx': { 61 | 'x86_64': 'llvm64' 62 | } 63 | } 64 | 65 | 66 | def is_cross_compiling(target_platform: str) -> bool: 67 | return (sys.platform == 'darwin' and target_platform != 'osx') or \ 68 | (sys.platform in ['linux', 'linux2', 'cygwin'] and target_platform != 'linux') 69 | 70 | 71 | def get_osxcross_sdk(osxcross_bin, arch): 72 | osxcross_sdk = os.environ.get('OSXCROSS_SDK', 18) 73 | 74 | name_fmt = path_join(osxcross_bin, arch + '-apple-darwin%s-%s') 75 | 76 | if not os.path.isfile(name_fmt % (osxcross_sdk, 'ar')): 77 | raise BuildError('Specify a valid osxcross SDK with the environment variable \'OSXCROSS_SDK\'') 78 | 79 | return osxcross_sdk 80 | 81 | 82 | def setup_desktop_template(env: dict, opts: DesktopOpts, product: str, target_platform: str, target: str): 83 | host_triple = host_triples[target_platform][target] 84 | 85 | CONFIGURE_FLAGS = [ 86 | '--disable-boehm', 87 | '--disable-mcs-build', 88 | '--enable-maintainer-mode', 89 | '--with-tls=pthread', 90 | '--without-ikvm-native', 91 | '--enable-btls', 92 | ] 93 | 94 | if target_platform == 'windows': 95 | CONFIGURE_FLAGS += [ 96 | '--with-libgdiplus=%s' % opts.mxe_prefix, 97 | '--enable-btls-lib', 98 | ] 99 | else: 100 | CONFIGURE_FLAGS += [ 101 | '--disable-iconv', 102 | '--disable-nls', 103 | '--with-sigaltstack=yes', 104 | ] 105 | 106 | if target_platform == 'windows': 107 | mxe_bin = path_join(opts.mxe_prefix, 'bin') 108 | 109 | env['_%s-%s_PATH' % (product, target)] = mxe_bin 110 | 111 | name_fmt = path_join(mxe_bin, target_arch[target_platform][target] + '-w64-mingw32-%s') 112 | 113 | env['_%s-%s_AR' % (product, target)] = name_fmt % 'ar' 114 | env['_%s-%s_AS' % (product, target)] = name_fmt % 'as' 115 | env['_%s-%s_CC' % (product, target)] = name_fmt % 'gcc' 116 | env['_%s-%s_CXX' % (product, target)] = name_fmt % 'g++' 117 | env['_%s-%s_DLLTOOL' % (product, target)] = name_fmt % 'dlltool' 118 | env['_%s-%s_LD' % (product, target)] = name_fmt % 'ld' 119 | env['_%s-%s_OBJDUMP' % (product, target)] = name_fmt % 'objdump' 120 | env['_%s-%s_RANLIB' % (product, target)] = name_fmt % 'ranlib' 121 | env['_%s-%s_STRIP' % (product, target)] = name_fmt % 'strip' 122 | 123 | CONFIGURE_FLAGS += [ 124 | #'--enable-static-gcc-libs' 125 | ] 126 | elif target_platform == 'osx': 127 | if is_cross_compiling(target_platform): 128 | osxcross_root = os.environ['OSXCROSS_ROOT'] 129 | osx_toolchain_path = path_join(osxcross_root, 'target') 130 | osxcross_bin = path_join(osx_toolchain_path, 'bin') 131 | osx_triple_abi = 'darwin%s' % get_osxcross_sdk(osxcross_bin, arch=target_arch[target_platform][target]) # TODO: Replace with '--osx-triple-abi' as in ios.py 132 | 133 | env['_%s-%s_PATH' % (product, target)] = osxcross_bin 134 | 135 | wrapper_path = create_osxcross_wrapper(opts, product, target, osx_toolchain_path) 136 | name_fmt = path_join(osxcross_bin, target_arch[target_platform][target] + '-apple-' + osx_triple_abi + '-%s') 137 | name_fmt = "%s %s" % (wrapper_path, name_fmt) 138 | 139 | env['_%s-%s_AR' % (product, target)] = name_fmt % 'ar' 140 | env['_%s-%s_AS' % (product, target)] = name_fmt % 'as' 141 | env['_%s-%s_CC' % (product, target)] = name_fmt % 'clang' 142 | env['_%s-%s_CXX' % (product, target)] = name_fmt % 'clang++' 143 | env['_%s-%s_LD' % (product, target)] = name_fmt % 'ld' 144 | env['_%s-%s_RANLIB' % (product, target)] = name_fmt % 'ranlib' 145 | env['_%s-%s_CMAKE' % (product, target)] = name_fmt % 'cmake' 146 | env['_%s-%s_STRIP' % (product, target)] = name_fmt % 'strip' 147 | 148 | # DTrace is not available when building with OSXCROSS 149 | CONFIGURE_FLAGS += ['--enable-dtrace=no'] 150 | else: 151 | osx_toolchain = '/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain' 152 | 153 | env['_%s-%s_CC' % (product, target)] = '%s/usr/bin/clang' % osx_toolchain 154 | env['_%s-%s_CXX' % (product, target)] = '%s/usr/bin/clang++' % osx_toolchain 155 | 156 | osx_sysroot = '/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk' 157 | 158 | CFLAGS = [ 159 | '-isysroot', osx_sysroot, 160 | '-arch', target_arch[target_platform][target] 161 | ] 162 | 163 | env['_%s-%s_CFLAGS' % (product, target)] = CFLAGS 164 | env['_%s-%s_CXXFLAGS' % (product, target)] = CFLAGS 165 | 166 | env['_%s-%s_CONFIGURE_FLAGS' % (product, target)] = CONFIGURE_FLAGS 167 | 168 | llvm = llvm_table[target_platform][target] if opts.with_llvm else '' 169 | 170 | runtime.setup_runtime_template(env, opts, product, target, host_triple, llvm=llvm) 171 | 172 | 173 | def strip_libs(opts: DesktopOpts, product: str, target_platform: str, target: str): 174 | if target_platform == 'osx': 175 | # 'strip' doesn't support '--strip-unneeded' on macOS 176 | return 177 | 178 | if is_cross_compiling(target_platform) and target_platform == 'windows': 179 | mxe_bin = path_join(opts.mxe_prefix, 'bin') 180 | name_fmt = path_join(mxe_bin, target_arch[target_platform][target] + '-w64-mingw32-%s') 181 | strip = name_fmt % 'strip' 182 | else: 183 | strip = 'strip' 184 | 185 | install_dir = path_join(opts.install_dir, '%s-%s-%s' % (product, target, opts.configuration)) 186 | out_libs_dir = path_join(install_dir, 'lib') 187 | 188 | lib_files = globs(('*.a', '*.so'), dirpath=out_libs_dir) 189 | if len(lib_files): 190 | run_command(strip, args=['--strip-unneeded'] + lib_files, name='strip') 191 | 192 | if target_platform == 'windows': 193 | out_bin_dir = path_join(install_dir, 'bin') 194 | 195 | dll_files = globs(('*.dll',), dirpath=out_bin_dir) 196 | if len(dll_files): 197 | run_command(strip, args=['--strip-unneeded'] + dll_files, name='strip') 198 | 199 | 200 | def configure(opts: DesktopOpts, product: str, target_platform: str, target: str): 201 | env = {} 202 | 203 | setup_desktop_template(env, opts, product, target_platform, target) 204 | 205 | if not os.path.isfile(path_join(opts.mono_source_root, 'configure')): 206 | runtime.run_autogen(opts) 207 | 208 | runtime.run_configure(env, opts, product, target) 209 | 210 | 211 | def make(opts: DesktopOpts, product: str, target_platform: str, target: str): 212 | build_dir = path_join(opts.configure_dir, '%s-%s-%s' % (product, target, opts.configuration)) 213 | 214 | if target_platform == 'windows': 215 | mxe = 'mxe-Win64' if target == 'x86_64' else 'mxe-Win32' 216 | replace_in_new_file( 217 | src_file='%s/sdks/builds/%s.cmake.in' % (opts.mono_source_root, mxe), 218 | search='@MXE_PATH@', replace=opts.mxe_prefix, 219 | dst_file='%s/mono/btls/%s.cmake' % (opts.mono_source_root, mxe) 220 | ) 221 | 222 | make_args = make_default_args(opts) 223 | make_args += ['-C', build_dir] 224 | 225 | run_command('make', args=make_args, name='make') 226 | run_command('make', args=['-C', '%s/mono' % build_dir, 'install'], name='make install mono') 227 | run_command('make', args=['-C', '%s/support' % build_dir, 'install'], name='make install support') 228 | run_command('make', args=['-C', '%s/data' % build_dir, 'install'], name='make install data') 229 | 230 | if opts.strip_libs: 231 | strip_libs(opts, product, target_platform, target) 232 | 233 | def copy_bcl(opts: DesktopOpts, product: str, target_platform: str, target: str): 234 | from shutil import copytree 235 | from bcl import get_profile_install_dirs 236 | dest_dir = path_join(opts.install_dir, '%s-%s-%s' % (product, target, opts.configuration), 'lib/mono/4.5') 237 | for src_dir in get_profile_install_dirs(opts, 'desktop-win32' if target_platform == 'windows' else 'desktop'): 238 | if not os.path.isdir(src_dir): 239 | raise BuildError('BCL source directory does not exist: %s. The BCL must be built prior to this.' % src_dir) 240 | copytree(src_dir, dest_dir) 241 | 242 | def clean(opts: DesktopOpts, product: str, target_platform: str, target: str): 243 | rm_rf( 244 | path_join(opts.configure_dir, '%s-%s-%s' % (product, target, opts.configuration)), 245 | path_join(opts.configure_dir, '%s-%s-%s.config.cache' % (product, target, opts.configuration)), 246 | path_join(opts.install_dir, '%s-%s-%s' % (product, target, opts.configuration)) 247 | ) 248 | 249 | 250 | def run_main(raw_args, target_platform): 251 | import cmd_utils 252 | from collections import OrderedDict 253 | from typing import Callable 254 | 255 | actions = OrderedDict() 256 | actions['configure'] = configure 257 | actions['make'] = make 258 | actions['copy-bcl'] = copy_bcl 259 | actions['clean'] = clean 260 | 261 | parser = cmd_utils.build_arg_parser(description='Builds the Mono runtime for the Desktop') 262 | 263 | default_help = 'default: %(default)s' 264 | 265 | parser.add_argument('action', choices=['configure', 'make', 'copy-bcl', 'clean']) 266 | parser.add_argument('--target', choices=targets[target_platform], action='append', required=True) 267 | parser.add_argument('--with-llvm', action='store_true', default=False, help=default_help) 268 | 269 | cmd_utils.add_runtime_arguments(parser, default_help) 270 | 271 | args = parser.parse_args(raw_args) 272 | 273 | input_action = args.action 274 | input_targets = args.target 275 | 276 | opts = desktop_opts_from_args(args) 277 | 278 | if not os.path.isdir(opts.mono_source_root): 279 | print('Mono sources directory not found: ' + opts.mono_source_root) 280 | sys.exit(1) 281 | 282 | if target_platform == 'osx' and sys.platform != 'darwin' and not 'OSXCROSS_ROOT' in os.environ: 283 | raise RuntimeError('The \'OSXCROSS_ROOT\' environment variable is required for cross-compiling to macOS') 284 | 285 | if is_cross_compiling(target_platform) and sys.platform == 'darwin': 286 | raise RuntimeError('Cross-compiling from macOS is not supported') 287 | 288 | action = actions[input_action] 289 | 290 | try: 291 | for target in input_targets: 292 | action(opts, 'desktop-%s' % target_platform, target_platform, target) 293 | except BuildError as e: 294 | sys.exit(e.message) 295 | -------------------------------------------------------------------------------- /android.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import os.path 5 | import subprocess 6 | import sys 7 | 8 | from options import * 9 | from os_utils import * 10 | import runtime 11 | 12 | 13 | DEFAULT_NDK_VERSION = '23.2.8568313' 14 | DEFAULT_CMAKE_VERSION = '3.18.1' 15 | targets = ['armv7', 'arm64v8', 'x86', 'x86_64'] 16 | 17 | 18 | def get_min_api_version(target) -> str: 19 | # Minimum API version should be in sync with Godot's platform/android/detect.py. 20 | # Note: The minimum API version for arm64v8 and x86_64 is '21' 21 | min_versions = { 22 | 'armv7': '19', 23 | 'arm64v8': '21', 24 | 'x86': '19', 25 | 'x86_64': '21', 26 | } 27 | return min_versions[target] 28 | 29 | 30 | def check_for_android_ndk(opts: AndroidOpts): 31 | if not os.path.exists(os.path.join(opts.android_sdk_root, 'ndk', opts.android_ndk_version)): 32 | print("Attempting to install Android NDK version %s" % (opts.android_ndk_version)) 33 | sdkmanager = opts.android_sdk_root + "/cmdline-tools/latest/bin/sdkmanager" 34 | if os.path.exists(sdkmanager): 35 | sdk_args = "ndk;" + opts.android_ndk_version 36 | subprocess.check_call([sdkmanager, sdk_args]) 37 | else: 38 | print("ERROR: Cannot find %s. Please ensure ANDROID_SDK_ROOT is correct and cmdline-tools are installed" % (sdkmanager)) 39 | sys.exit(1) 40 | 41 | 42 | def check_for_cmake(opts: AndroidOpts): 43 | if not os.path.exists(os.path.join(opts.android_sdk_root, 'cmake', opts.android_cmake_version)): 44 | print("Attempting to install CMake version %s" % (opts.android_cmake_version)) 45 | sdkmanager = opts.android_sdk_root + "/cmdline-tools/latest/bin/sdkmanager" 46 | if os.path.exists(sdkmanager): 47 | sdk_args = "cmake;" + opts.android_cmake_version 48 | subprocess.check_call([sdkmanager, sdk_args]) 49 | else: 50 | print("ERROR: Cannot find %s. Please ensure ANDROID_SDK_ROOT is correct and cmdline-tools are installed" % (sdkmanager)) 51 | sys.exit(1) 52 | 53 | 54 | def get_api_version_or_min(opts: AndroidOpts, target: str) -> str: 55 | min_api_version = get_min_api_version(target) 56 | if int(opts.android_api_version) < int(min_api_version): 57 | print('WARNING: API version %s is less than the minimum for platform %s; using %s' % (opts.android_api_version, target, min_api_version)) 58 | return min_api_version 59 | return opts.android_api_version 60 | 61 | 62 | def setup_android_target_template(env: dict, opts: AndroidOpts, target: str): 63 | extra_target_envs = { 64 | 'armv7': { 65 | 'android-armv7_CFLAGS': ['-D__POSIX_VISIBLE=201002', '-DSK_RELEASE', '-DNDEBUG', '-UDEBUG', '-fpic', '-march=armv7-a', '-mtune=cortex-a8', '-mfpu=vfp', '-mfloat-abi=softfp'], 66 | 'android-armv7_CXXFLAGS': ['-D__POSIX_VISIBLE=201002', '-DSK_RELEASE', '-DNDEBUG', '-UDEBUG', '-fpic', '-march=armv7-a', '-mtune=cortex-a8', '-mfpu=vfp', '-mfloat-abi=softfp'], 67 | 'android-armv7_LDFLAGS': ['-Wl,--fix-cortex-a8'] 68 | }, 69 | 'arm64v8': { 70 | 'android-arm64v8_CFLAGS': ['-D__POSIX_VISIBLE=201002', '-DSK_RELEASE', '-DNDEBUG', '-UDEBUG', '-fpic', '-DL_cuserid=9', '-DANDROID64'], 71 | 'android-arm64v8_CXXFLAGS': ['-D__POSIX_VISIBLE=201002', '-DSK_RELEASE', '-DNDEBUG', '-UDEBUG', '-fpic', '-DL_cuserid=9', '-DANDROID64'], 72 | 'android-arm64v8_LDFLAGS': ['-Wl,-z,max-page-size=16384', '-Wl,-z,common-page-size=16384'] 73 | }, 74 | 'x86': {}, 75 | 'x86_64': { 76 | 'android-x86_64_CFLAGS': ['-DL_cuserid=9'], 77 | 'android-x86_64_CXXFLAGS': ['-DL_cuserid=9'], 78 | 'android-x86_64_LDFLAGS': ['-Wl,-z,max-page-size=16384', '-Wl,-z,common-page-size=16384'] 79 | } 80 | } 81 | env.update(extra_target_envs[target]) 82 | 83 | if target == "armv7": 84 | target_triple = "armv7a-linux-androideabi" 85 | bin_utils = "arm-linux-androideabi" 86 | elif target == "arm64v8": 87 | target_triple = "aarch64-linux-android" 88 | bin_utils = target_triple 89 | elif target == "x86": 90 | target_triple = "i686-linux-android" 91 | bin_utils = target_triple 92 | elif target == "x86_64": 93 | target_triple = "x86_64-linux-android" 94 | bin_utils = target_triple 95 | 96 | if sys.platform.startswith("linux"): 97 | host_subpath = "linux-x86_64" 98 | elif sys.platform.startswith("darwin"): 99 | host_subpath = "darwin-x86_64" 100 | elif sys.platform.startswith("win"): 101 | if platform.machine().endswith("64"): 102 | host_subpath = "windows-x86_64" 103 | else: 104 | host_subpath = "windows" 105 | 106 | cmake_path = os.path.join(opts.android_sdk_root, 'cmake', opts.android_cmake_version, 'bin') 107 | ndk_path = os.path.join(opts.android_sdk_root, 'ndk', opts.android_ndk_version) 108 | toolchain_path = os.path.join(ndk_path, 'toolchains/llvm/prebuilt', host_subpath) 109 | compiler_path = os.path.join(toolchain_path, 'bin') 110 | compiler_wrapper = target_triple + env['ANDROID_API_VERSION'] + '-' 111 | bin_utils_path = os.path.join(toolchain_path, bin_utils, 'bin') 112 | android_api = env['ANDROID_API_VERSION'] 113 | 114 | AR = os.path.join(compiler_path, 'llvm-ar') 115 | AS = os.path.join(bin_utils_path, 'as') 116 | CC = os.path.join(compiler_path, compiler_wrapper + 'clang') 117 | CXX = os.path.join(compiler_path, compiler_wrapper + 'clang++') 118 | LD = os.path.join(compiler_path, 'ld') 119 | DLLTOOL = '' 120 | OBJDUMP = os.path.join(compiler_path, 'llvm-objdump') 121 | RANLIB = os.path.join(compiler_path, 'llvm-ranlib') 122 | CMAKE = os.path.join(cmake_path, 'cmake') 123 | STRIP = os.path.join(compiler_path, 'llvm-strip') 124 | CPP = CC + ' -E' 125 | CXXCPP = CXX + ' -E' 126 | 127 | ccache_path = os.environ.get('CCACHE', '') 128 | if ccache_path: 129 | CC = '%s %s' % (ccache_path, CC) 130 | CXX = '%s %s' % (ccache_path, CXX) 131 | CPP = '%s %s' % (ccache_path, CPP) 132 | CXXCPP = '%s %s' % (ccache_path, CXXCPP) 133 | 134 | AC_VARS = [ 135 | 'mono_cv_uscore=yes', 136 | 'ac_cv_func_sched_getaffinity=no', 137 | 'ac_cv_func_sched_setaffinity=no', 138 | 'ac_cv_func_shm_open_working_with_mmap=no' 139 | ] 140 | 141 | CFLAGS, CXXFLAGS, CPPFLAGS, CXXCPPFLAGS, LDFLAGS = [], [], [], [], [] 142 | 143 | # On Android we use 'getApplicationInfo().nativeLibraryDir' as the libdir where Mono will look for shared objects. 144 | # This path looks something like this: '/data/app-lib/{package_name-{num}}'. However, Mono does some relocation 145 | # and the actual path it will look at will be '/data/app-lib/{package_name}-{num}/../lib', which doesn't exist. 146 | # Cannot use '/data/data/{package_name}/lib' either, as '/data/data/{package_name}/lib/../lib' may result in 147 | # permission denied. Therefore we just override 'MONO_RELOC_LIBDIR' here to avoid the relocation. 148 | CPPFLAGS += ['-DMONO_RELOC_LIBDIR=\\\".\\\"'] 149 | 150 | CFLAGS += [ 151 | '-fstack-protector', 152 | '-DMONODROID=1' 153 | '-D__ANDROID_API__=' + android_api 154 | ] 155 | 156 | CXXFLAGS += [ 157 | '-fstack-protector', 158 | '-DMONODROID=1' 159 | '-D__ANDROID_API__=' + android_api, 160 | ] 161 | 162 | LDFLAGS += [ 163 | '-z', 'now', '-z', 'relro', '-z', 'noexecstack', 164 | '-ldl', '-lm', '-llog', '-lc' 165 | ] 166 | 167 | # Fixes this error: DllImport unable to load library 'dlopen failed: empty/missing DT_HASH in "libmono-native.so" (built with --hash-style=gnu?)'. 168 | LDFLAGS += ['-Wl,--hash-style=both'] 169 | 170 | CONFIGURE_FLAGS = [ 171 | '--disable-boehm', 172 | '--disable-executables', 173 | '--disable-iconv', 174 | '--disable-mcs-build', 175 | '--disable-nls', 176 | '--enable-dynamic-btls', 177 | '--enable-maintainer-mode', 178 | '--enable-minimal=ssa,portability,attach,verifier,full_messages,sgen_remset' 179 | ',sgen_marksweep_par,sgen_marksweep_fixed,sgen_marksweep_fixed_par' 180 | ',sgen_copying,logging,security,shared_handles,interpreter', 181 | '--with-btls-android-ndk=%s' % ndk_path, 182 | '--with-btls-android-api=%s' % android_api, 183 | ] 184 | 185 | CONFIGURE_FLAGS += ['--enable-monodroid'] 186 | CONFIGURE_FLAGS += ['--with-btls-android-ndk-asm-workaround'] 187 | 188 | CONFIGURE_FLAGS += [ 189 | '--with-btls-android-cmake-toolchain=%s/build/cmake/android.toolchain.cmake' % ndk_path, 190 | '--with-sigaltstack=yes', 191 | '--with-tls=pthread', 192 | '--without-ikvm-native', 193 | '--disable-cooperative-suspend', 194 | '--disable-hybrid-suspend', 195 | '--disable-crash-reporting' 196 | ] 197 | 198 | env['_android-%s_AR' % target] = AR 199 | env['_android-%s_AS' % target] = AS 200 | env['_android-%s_CC' % target] = CC 201 | env['_android-%s_CXX' % target] = CXX 202 | env['_android-%s_CPP' % target] = CPP 203 | env['_android-%s_CXXCPP' % target] = CXXCPP 204 | env['_android-%s_DLLTOOL' % target] = DLLTOOL 205 | env['_android-%s_LD' % target] = LD 206 | env['_android-%s_OBJDUMP' % target] = OBJDUMP 207 | env['_android-%s_RANLIB' % target] = RANLIB 208 | env['_android-%s_CMAKE' % target] = CMAKE 209 | env['_android-%s_STRIP' % target] = STRIP 210 | 211 | env['_android-%s_AC_VARS' % target] = AC_VARS 212 | env['_android-%s_CFLAGS' % target] = CFLAGS 213 | env['_android-%s_CXXFLAGS' % target] = CXXFLAGS 214 | env['_android-%s_CPPFLAGS' % target] = CPPFLAGS 215 | env['_android-%s_CXXCPPFLAGS' % target] = CXXCPPFLAGS 216 | env['_android-%s_LDFLAGS' % target] = LDFLAGS 217 | env['_android-%s_CONFIGURE_FLAGS' % target] = CONFIGURE_FLAGS 218 | 219 | # Runtime template 220 | runtime.setup_runtime_template(env, opts, 'android', target, target_triple) 221 | 222 | 223 | def strip_libs(opts: AndroidOpts, product: str, target: str): 224 | ndk_path = os.path.join(opts.android_sdk_root, 'ndk', opts.android_ndk_version) 225 | toolchain_path = os.path.join(ndk_path, 'toolchains/llvm/prebuilt/linux-x86_64') 226 | strip = os.path.join(toolchain_path, 'bin', 'llvm-strip') 227 | 228 | install_dir = os.path.join(opts.install_dir, '%s-%s-%s' % (product, target, opts.configuration)) 229 | out_libs_dir = os.path.join(install_dir, 'lib') 230 | 231 | lib_files = globs(('*.a', '*.so'), dirpath=out_libs_dir) 232 | if len(lib_files): 233 | run_command(strip, args=['--strip-unneeded'] + lib_files, name='strip') 234 | 235 | 236 | def configure(opts: AndroidOpts, product: str, target: str): 237 | env = { 'ANDROID_API_VERSION': get_api_version_or_min(opts, target) } 238 | 239 | setup_android_target_template(env, opts, target) 240 | 241 | if not os.path.isfile(os.path.join(opts.mono_source_root, 'configure')): 242 | runtime.run_autogen(opts) 243 | 244 | runtime.run_configure(env, opts, product, target) 245 | 246 | 247 | def make(opts: AndroidOpts, product: str, target: str): 248 | env = { 'ANDROID_API_VERSION': get_api_version_or_min(opts, target) } 249 | 250 | build_dir = os.path.join(opts.configure_dir, '%s-%s-%s' % (product, target, opts.configuration)) 251 | 252 | make_args = make_default_args(opts) 253 | make_args += ['-C', build_dir] 254 | 255 | run_command('make', args=make_args, name='make') 256 | run_command('make', args=['-C', '%s/mono' % build_dir, 'install'], name='make install mono') 257 | run_command('make', args=['-C', '%s/support' % build_dir, 'install'], name='make install support') 258 | run_command('make', args=['-C', '%s/data' % build_dir, 'install'], name='make install data') 259 | 260 | if opts.strip_libs: 261 | strip_libs(opts, product, target) 262 | 263 | 264 | def clean(opts: AndroidOpts, product: str, target: str): 265 | rm_rf( 266 | os.path.join(opts.configure_dir, '%s-%s-%s' % (product, target, opts.configuration)), 267 | os.path.join(opts.configure_dir, '%s-%s-%s.config.cache' % (product, target, opts.configuration)), 268 | os.path.join(opts.install_dir, '%s-%s-%s' % (product, target, opts.configuration)) 269 | ) 270 | 271 | 272 | def main(raw_args): 273 | import cmd_utils 274 | from cmd_utils import custom_bool 275 | from collections import OrderedDict 276 | from typing import Callable 277 | 278 | target_choices = targets + ['all-targets'] 279 | 280 | actions = OrderedDict() 281 | actions['configure'] = configure 282 | actions['make'] = make 283 | actions['clean'] = clean 284 | 285 | parser = cmd_utils.build_arg_parser( 286 | description='Builds the Mono runtime for Android', 287 | env_vars={ 'ANDROID_SDK_ROOT': 'Overrides default value for --android-sdk' } 288 | ) 289 | 290 | home = os.environ.get('HOME') 291 | android_sdk_default = os.environ.get('ANDROID_SDK_ROOT', os.path.join(home, 'Android/Sdk')) 292 | 293 | default_help = 'default: %(default)s' 294 | 295 | parser.add_argument('action', choices=['configure', 'make', 'clean']) 296 | parser.add_argument('--target', choices=target_choices, action='append', required=True) 297 | parser.add_argument('--android-sdk', default=android_sdk_default, help=default_help) 298 | parser.add_argument('--android-ndk-version', default=DEFAULT_NDK_VERSION, help=default_help) 299 | parser.add_argument('--android-api-version', default=get_min_api_version(targets[0]), help=default_help) 300 | parser.add_argument('--android-cmake-version', default=DEFAULT_CMAKE_VERSION, help=default_help) 301 | 302 | cmd_utils.add_runtime_arguments(parser, default_help) 303 | 304 | args = parser.parse_args(raw_args) 305 | 306 | input_action = args.action 307 | input_targets = args.target 308 | 309 | opts = android_opts_from_args(args) 310 | 311 | if not os.path.isdir(opts.mono_source_root): 312 | print('Mono sources directory not found: ' + opts.mono_source_root) 313 | sys.exit(1) 314 | 315 | check_for_android_ndk(opts) 316 | check_for_cmake(opts) 317 | 318 | build_targets = cmd_utils.expand_input_targets(input_targets, { 'all-targets': targets }) 319 | action = actions[input_action] 320 | 321 | try: 322 | for target in build_targets: 323 | action(opts, 'android', target) 324 | except BuildError as e: 325 | sys.exit(e.message) 326 | 327 | 328 | if __name__ == '__main__': 329 | from sys import argv 330 | main(argv[1:]) 331 | -------------------------------------------------------------------------------- /ios.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import os.path 5 | import sys 6 | 7 | from os.path import join as path_join 8 | 9 | from options import * 10 | from os_utils import * 11 | import runtime 12 | 13 | 14 | this_script_dir = os.path.dirname(os.path.realpath(__file__)) 15 | 16 | device_targets = ['armv7', 'arm64'] 17 | sim_targets = ['i386', 'x86_64', 'arm64-sim'] 18 | cross_targets = ['cross-armv7', 'cross-arm64'] 19 | 20 | 21 | def is_cross(target) -> bool: 22 | return target in cross_targets 23 | 24 | 25 | class iOSTargetTable: 26 | archs = { 27 | 'armv7': 'arm', 28 | 'arm64': 'arm64', 29 | 'arm64-sim': 'arm64', 30 | 'i386': 'i386', 31 | 'x86_64': 'x86_64' 32 | } 33 | 34 | host_triples = { 35 | 'armv7': 'arm-apple-darwin11', 36 | 'arm64': 'aarch64-apple-darwin11', 37 | 'arm64-sim': 'aarch64-apple-darwin11', 38 | 'i386': 'i386-apple-darwin11', 39 | 'x86_64': 'x86_64-apple-darwin11' 40 | } 41 | 42 | osxcross_tool_triples = { 43 | 'armv7': 'arm-apple-darwin11', # TODO: ? 44 | 'arm64': 'arm-apple-darwin11', 45 | 'arm64-sim': 'arm-apple-darwin11', 46 | 'i386': 'i386-apple-darwin11', # TODO: ? 47 | 'x86_64': 'x86_64-apple-darwin11' 48 | } 49 | 50 | 51 | def setup_ios_device_template(env: dict, opts: iOSOpts, target: str): 52 | ios_sysroot_path = opts.ios_sdk_path 53 | 54 | if not ios_sysroot_path and sys.platform == 'darwin': 55 | # Auto-detect on macOS 56 | ios_sysroot_path = xcrun_find_sdk('iphoneos') 57 | 58 | if not ios_sysroot_path: 59 | raise RuntimeError('Cannot find iOS SDK; specify one manually with \'--ios-sdk\'.') 60 | 61 | sysroot_flags = ['-isysroot', ios_sysroot_path, '-miphoneos-version-min=%s' % opts.ios_version_min] 62 | 63 | arch = iOSTargetTable.archs[target] 64 | host_triple = iOSTargetTable.host_triples[target] 65 | osxcross_tool_triple = iOSTargetTable.osxcross_tool_triples[target] 66 | 67 | tools_path = path_join(opts.ios_toolchain_path, 'usr', 'bin') 68 | 69 | if sys.platform != 'darwin': 70 | wrapper_path = create_osxcross_wrapper(opts, 'ios', target, opts.ios_toolchain_path) 71 | name_fmt = path_join(tools_path, osxcross_tool_triple + '-%s') 72 | name_fmt = "%s %s" % (wrapper_path, name_fmt) 73 | else: 74 | name_fmt = path_join(tools_path, '%s') 75 | 76 | AR = name_fmt % 'ar' 77 | AS = name_fmt % 'as' 78 | CC = name_fmt % 'clang' 79 | CXX = name_fmt % 'clang++' 80 | LD = name_fmt % 'ld' 81 | RANLIB = name_fmt % 'ranlib' 82 | STRIP = name_fmt % 'strip' 83 | 84 | ccache_path = os.environ.get('CCACHE', '') 85 | if ccache_path: 86 | CC = '%s %s' % (ccache_path, CC) 87 | CXX = '%s %s' % (ccache_path, CXX) 88 | 89 | AC_VARS = [ 90 | 'ac_cv_c_bigendian=no', 91 | 'ac_cv_func_fstatat=no', 92 | 'ac_cv_func_readlinkat=no', 93 | 'ac_cv_func_getpwuid_r=no', 94 | 'ac_cv_func_posix_getpwuid_r=yes', 95 | 'ac_cv_header_curses_h=no', 96 | 'ac_cv_header_localcharset_h=no', 97 | 'ac_cv_header_sys_user_h=no', 98 | 'ac_cv_func_getentropy=no', 99 | 'ac_cv_func_futimens=no', 100 | 'ac_cv_func_utimensat=no', 101 | 'ac_cv_func_shm_open_working_with_mmap=no', 102 | 'ac_cv_func_pthread_jit_write_protect_np=no', 103 | 'ac_cv_func_preadv=no', 104 | 'ac_cv_func_pwritev=no', 105 | 'mono_cv_sizeof_sunpath=104', 106 | 'mono_cv_uscore=yes' 107 | ] 108 | 109 | bitcode_marker = env.get('ios-%s_BITCODE_MARKER' % target, '') 110 | 111 | CFLAGS = sysroot_flags + [ 112 | '-arch %s' % arch, 113 | '-Wl,-application_extension', 114 | '-fexceptions' 115 | ] 116 | CFLAGS += [bitcode_marker] if bitcode_marker else [] 117 | 118 | CXXFLAGS = sysroot_flags + [ 119 | '-arch %s' % arch, 120 | '-Wl,-application_extension' 121 | ] 122 | CXXFLAGS += [bitcode_marker] if bitcode_marker else [] 123 | 124 | CPPFLAGS = sysroot_flags + [ 125 | '-DMONOTOUCH=1', 126 | '-arch %s' % arch, 127 | '-DSMALL_CONFIG', '-D_XOPEN_SOURCE', '-DHOST_IOS', '-DHAVE_LARGE_FILE_SUPPORT=1' 128 | ] 129 | 130 | LDFLAGS = [ 131 | '-arch %s' % arch, 132 | '-framework', 'CoreFoundation', 133 | '-lobjc', '-lc++' 134 | ] 135 | 136 | CONFIGURE_FLAGS = [ 137 | '--disable-boehm', 138 | '--disable-btls', 139 | '--disable-executables', 140 | '--disable-icall-tables', 141 | '--disable-iconv', 142 | '--disable-mcs-build', 143 | '--disable-nls', 144 | '--disable-visibility-hidden', 145 | '--enable-dtrace=no', 146 | '--enable-icall-export', 147 | '--enable-maintainer-mode', 148 | '--enable-minimal=ssa,com,interpreter,jit,portability,assembly_remapping,attach,verifier,' + 149 | 'full_messages,appdomains,security,sgen_remset,sgen_marksweep_par,sgen_marksweep_fixed,' + 150 | 'sgen_marksweep_fixed_par,sgen_copying,logging,remoting,shared_perfcounters,gac', 151 | '--enable-monotouch', 152 | # We don't need this. Comment it so we don't have to call 'mono_gc_init_finalizer_thread' from Godot. 153 | #'--with-lazy-gc-thread-creation=yes', 154 | '--with-tls=pthread', 155 | '--without-ikvm-native', 156 | '--without-sigaltstack', 157 | '--disable-cooperative-suspend', 158 | '--disable-hybrid-suspend', 159 | '--disable-crash-reporting' 160 | ] 161 | 162 | env['_ios-%s_AR' % target] = AR 163 | env['_ios-%s_AS' % target] = AS 164 | env['_ios-%s_CC' % target] = CC 165 | env['_ios-%s_CXX' % target] = CXX 166 | env['_ios-%s_LD' % target] = LD 167 | env['_ios-%s_RANLIB' % target] = RANLIB 168 | env['_ios-%s_STRIP' % target] = STRIP 169 | 170 | env['_ios-%s_AC_VARS' % target] = AC_VARS 171 | env['_ios-%s_CFLAGS' % target] = CFLAGS 172 | env['_ios-%s_CXXFLAGS' % target] = CXXFLAGS 173 | env['_ios-%s_CPPFLAGS' % target] = CPPFLAGS 174 | env['_ios-%s_LDFLAGS' % target] = LDFLAGS 175 | env['_ios-%s_CONFIGURE_FLAGS' % target] = CONFIGURE_FLAGS 176 | 177 | # Runtime template 178 | runtime.setup_runtime_template(env, opts, 'ios', target, host_triple) 179 | 180 | 181 | def setup_ios_simulator_template(env: dict, opts: iOSOpts, target: str): 182 | ios_sysroot_path = opts.ios_sdk_path 183 | 184 | if not ios_sysroot_path and sys.platform == 'darwin': 185 | # Auto-detect on macOS 186 | ios_sysroot_path = xcrun_find_sdk('iphonesimulator') 187 | 188 | if not ios_sysroot_path: 189 | raise RuntimeError('Cannot find iOS SDK; specify one manually with \'--ios-sdk\'.') 190 | 191 | sysroot_flags = ['-isysroot', ios_sysroot_path, '-mios-simulator-version-min=%s' % opts.ios_version_min] 192 | 193 | arch = iOSTargetTable.archs[target] 194 | host_triple = iOSTargetTable.host_triples[target] 195 | osxcross_tool_triple = iOSTargetTable.osxcross_tool_triples[target] 196 | 197 | tools_path = path_join(opts.ios_toolchain_path, 'usr', 'bin') 198 | 199 | if sys.platform != 'darwin': 200 | wrapper_path = create_osxcross_wrapper(opts, 'ios', target, opts.ios_toolchain_path) 201 | name_fmt = path_join(tools_path, osxcross_tool_triple + '-%s') 202 | name_fmt = "%s %s" % (wrapper_path, name_fmt) 203 | else: 204 | name_fmt = path_join(tools_path, '%s') 205 | 206 | AR = name_fmt % 'ar' 207 | AS = name_fmt % 'as' 208 | CC = name_fmt % 'clang' 209 | CXX = name_fmt % 'clang++' 210 | LD = name_fmt % 'ld' 211 | RANLIB = name_fmt % 'ranlib' 212 | STRIP = name_fmt % 'strip' 213 | 214 | ccache_path = os.environ.get('CCACHE', '') 215 | if ccache_path: 216 | CC = '%s %s' % (ccache_path, CC) 217 | CXX = '%s %s' % (ccache_path, CXX) 218 | 219 | AC_VARS = [ 220 | 'ac_cv_func_clock_nanosleep=no', 221 | 'ac_cv_func_fstatat=no', 222 | 'ac_cv_func_readlinkat=no', 223 | 'ac_cv_func_system=no', 224 | 'ac_cv_func_getentropy=no', 225 | 'ac_cv_func_futimens=no', 226 | 'ac_cv_func_utimensat=no', 227 | 'ac_cv_func_shm_open_working_with_mmap=no', 228 | 'ac_cv_func_pthread_jit_write_protect_np=no', 229 | 'ac_cv_func_preadv=no', 230 | 'ac_cv_func_pwritev=no', 231 | 'mono_cv_uscore=yes' 232 | ] 233 | 234 | CFLAGS = sysroot_flags + [ 235 | '-arch %s' % arch, 236 | '-Wl,-application_extension' 237 | ] 238 | 239 | CXXFLAGS = sysroot_flags + [ 240 | '-arch %s' % arch, 241 | '-Wl,-application_extension' 242 | ] 243 | 244 | CPPFLAGS = sysroot_flags + [ 245 | '-DMONOTOUCH=1', 246 | '-arch %s' % arch, 247 | '-Wl,-application_extension', 248 | '-DHOST_IOS' 249 | ] 250 | 251 | LDFLAGS = [] 252 | 253 | CONFIGURE_FLAGS = [ 254 | '--disable-boehm', 255 | '--disable-btls', 256 | '--disable-executables', 257 | '--disable-iconv', 258 | '--disable-mcs-build', 259 | '--disable-nls', 260 | '--disable-visibility-hidden', 261 | '--enable-maintainer-mode', 262 | '--enable-minimal=com,remoting,shared_perfcounters,gac', 263 | '--enable-monotouch', 264 | '--with-tls=pthread', 265 | '--without-ikvm-native', 266 | '--disable-cooperative-suspend', 267 | '--disable-hybrid-suspend', 268 | '--disable-crash-reporting' 269 | ] 270 | 271 | if sys.platform != 'darwin': 272 | # DTrace is not available when building with OSXCROSS 273 | CONFIGURE_FLAGS += ['--enable-dtrace=no'] 274 | 275 | env['_ios-%s_AR' % target] = AR 276 | env['_ios-%s_AS' % target] = AS 277 | env['_ios-%s_CC' % target] = CC 278 | env['_ios-%s_CXX' % target] = CXX 279 | env['_ios-%s_LD' % target] = LD 280 | env['_ios-%s_RANLIB' % target] = RANLIB 281 | env['_ios-%s_STRIP' % target] = STRIP 282 | 283 | env['_ios-%s_AC_VARS' % target] = AC_VARS 284 | env['_ios-%s_CFLAGS' % target] = CFLAGS 285 | env['_ios-%s_CXXFLAGS' % target] = CXXFLAGS 286 | env['_ios-%s_CPPFLAGS' % target] = CPPFLAGS 287 | env['_ios-%s_LDFLAGS' % target] = LDFLAGS 288 | env['_ios-%s_CONFIGURE_FLAGS' % target] = CONFIGURE_FLAGS 289 | 290 | # Runtime template 291 | runtime.setup_runtime_template(env, opts, 'ios', target, host_triple) 292 | 293 | 294 | class iOSCrossTable: 295 | target_triples = { 296 | 'cross-armv7': 'arm-apple-darwin', 297 | 'cross-arm64': 'aarch64-apple-darwin' 298 | } 299 | 300 | device_targets = { 301 | 'cross-armv7': 'armv7', 302 | 'cross-arm64': 'arm64' 303 | } 304 | 305 | # 'darwin10' is hard-coded in 'offsets-tool.py', hence why we use 'darwin10' here 306 | offsets_dumper_abis = { 307 | 'cross-armv7': 'arm-apple-darwin10', 308 | 'cross-arm64': 'aarch64-apple-darwin10' 309 | } 310 | 311 | 312 | def llvm_for(host_arch: str) -> str: 313 | return 'llvmarm64' if host_arch == 'arm64' else 'llvm64' 314 | 315 | 316 | def setup_ios_cross_template(env: dict, opts: iOSOpts, target: str, host_arch: str): 317 | target_triple = iOSCrossTable.target_triples[target] 318 | device_target = iOSCrossTable.device_targets[target] 319 | offsets_dumper_abi = iOSCrossTable.offsets_dumper_abis[target] 320 | host_triple = '%s-apple-darwin11' % host_arch 321 | 322 | is_sim = device_target in sim_targets 323 | 324 | ios_sysroot_path = opts.ios_sdk_path 325 | 326 | if not ios_sysroot_path and sys.platform == 'darwin': 327 | # Auto-detect on macOS 328 | ios_sysroot_path = xcrun_find_sdk('iphonesimulator' if is_sim else 'iphoneos') 329 | 330 | if not ios_sysroot_path: 331 | raise RuntimeError('Cannot find iOS SDK; specify one manually with \'--ios-sdk\'.') 332 | 333 | osx_sysroot_path = opts.osx_sdk_path 334 | 335 | if not osx_sysroot_path and sys.platform == 'darwin': 336 | # Auto-detect on macOS 337 | osx_sysroot_path = xcrun_find_sdk('macosx') 338 | 339 | if not osx_sysroot_path: 340 | raise RuntimeError('Cannot find MacOSX SDK; specify one manually with \'--osx-sdk\'.') 341 | 342 | if sys.platform != 'darwin': 343 | osxcross_root = opts.osx_toolchain_path 344 | osxcross_bin = path_join(osxcross_root, 'bin') 345 | 346 | env['_ios-%s_PATH' % target] = osxcross_bin 347 | 348 | wrapper_path = create_osxcross_wrapper(opts, 'ios', target, opts.osx_toolchain_path) 349 | name_fmt = path_join(osxcross_bin, host_arch + '-apple-' + opts.osx_triple_abi + '-%s') 350 | name_fmt = "%s %s" % (wrapper_path, name_fmt) 351 | else: 352 | tools_path = path_join(opts.osx_toolchain_path, 'usr', 'bin') 353 | name_fmt = path_join(tools_path, '%s') 354 | 355 | env['_ios-%s_AR' % target] = name_fmt % 'ar' 356 | env['_ios-%s_AS' % target] = name_fmt % 'as' 357 | env['_ios-%s_CC' % target] = name_fmt % 'clang' 358 | env['_ios-%s_CXX' % target] = name_fmt % 'clang++' 359 | env['_ios-%s_LD' % target] = name_fmt % 'ld' 360 | env['_ios-%s_RANLIB' % target] = name_fmt % 'ranlib' 361 | env['_ios-%s_STRIP' % target] = name_fmt % 'strip' 362 | 363 | libclang = os.environ.get('LIBCLANG_PATH', '') 364 | if libclang and not os.path.isfile(libclang): 365 | raise RuntimeError('Specified libclang file not found: \'%s\'' % libclang) 366 | 367 | if not libclang: 368 | libclang = try_find_libclang(toolchain_path=opts.ios_toolchain_path) 369 | 370 | if not libclang: 371 | raise RuntimeError('Cannot find libclang shared library; specify a path manually with the \'LIBCLANG_PATH\' environment variable.') 372 | 373 | offsets_dumper_args = [ 374 | '--libclang=%s' % libclang, 375 | '--sysroot=%s' % ios_sysroot_path 376 | ] 377 | 378 | if sys.platform != 'darwin': 379 | # Needed in order to locate the iOS toolchain's clang to use with offsets-tool 380 | setup_ios_device_template(env, opts, device_target) 381 | ios_clang = env['_ios-%s_CC' % device_target] 382 | 383 | # Extra CFLAGS needed in order to make offsets-tool work with OSXCross. 384 | offsets_dumper_args += ['--extra-cflag=' + cflag for cflag in [ 385 | '-target', 'aarch64-apple-darwin', 386 | '-resource-dir', get_clang_resource_dir(ios_clang) 387 | ]] 388 | 389 | env['_ios-%s_OFFSETS_DUMPER_ARGS' % target] = offsets_dumper_args 390 | 391 | AC_VARS = ['ac_cv_func_shm_open_working_with_mmap=no'] 392 | 393 | CFLAGS = ['-isysroot', osx_sysroot_path, '-mmacosx-version-min=10.9', '-Qunused-arguments'] 394 | CXXFLAGS = ['-isysroot', osx_sysroot_path, '-mmacosx-version-min=10.9', '-Qunused-arguments', '-stdlib=libc++'] 395 | CPPFLAGS = ['-DMONOTOUCH=1'] 396 | LDFLAGS = ['-stdlib=libc++'] 397 | 398 | CONFIGURE_FLAGS = [ 399 | '--disable-boehm', 400 | '--disable-btls', 401 | '--disable-iconv', 402 | '--disable-libraries', 403 | '--disable-mcs-build', 404 | '--disable-nls', 405 | '--enable-dtrace=no', 406 | '--enable-icall-symbol-map', 407 | '--enable-minimal=com,remoting', 408 | '--enable-monotouch', 409 | '--disable-crash-reporting' 410 | ] 411 | 412 | env['_ios-%s_AC_VARS' % target] = AC_VARS 413 | env['_ios-%s_CFLAGS' % target] = CFLAGS 414 | env['_ios-%s_CXXFLAGS' % target] = CXXFLAGS 415 | env['_ios-%s_CPPFLAGS' % target] = CPPFLAGS 416 | env['_ios-%s_LDFLAGS' % target] = LDFLAGS 417 | env['_ios-%s_CONFIGURE_FLAGS' % target] = CONFIGURE_FLAGS 418 | 419 | # Runtime cross template 420 | runtime.setup_runtime_cross_template(env, opts, 'ios', target, host_triple, target_triple, device_target, llvm_for(host_arch), offsets_dumper_abi) 421 | 422 | 423 | def strip_libs(opts: iOSOpts, product: str, target: str): 424 | # 'strip' doesn't support '--strip-unneeded' on macOS 425 | return 426 | 427 | 428 | def configure(opts: iOSOpts, product: str, target: str): 429 | env = {} 430 | 431 | is_sim = target in sim_targets 432 | 433 | if is_cross(target): 434 | import llvm 435 | 436 | host_arch='x86_64' 437 | 438 | llvm.make(opts, llvm_for(host_arch)) 439 | 440 | setup_ios_cross_template(env, opts, target, host_arch) 441 | else: 442 | if is_sim: 443 | setup_ios_simulator_template(env, opts, target) 444 | else: 445 | setup_ios_device_template(env, opts, target) 446 | 447 | if not os.path.isfile(path_join(opts.mono_source_root, 'configure')): 448 | runtime.run_autogen(opts) 449 | 450 | runtime.run_configure(env, opts, product, target) 451 | 452 | 453 | def make(opts: iOSOpts, product: str, target: str): 454 | env = {} 455 | 456 | build_dir = path_join(opts.configure_dir, '%s-%s-%s' % (product, target, opts.configuration)) 457 | 458 | make_args = make_default_args(opts) 459 | make_args += ['-C', build_dir] 460 | 461 | run_command('make', args=make_args, name='make') 462 | run_command('make', args=['-C', '%s/mono' % build_dir, 'install'], name='make install mono') 463 | run_command('make', args=['-C', '%s/support' % build_dir, 'install'], name='make install support') 464 | run_command('make', args=['-C', '%s/data' % build_dir, 'install'], name='make install data') 465 | 466 | if opts.strip_libs and not is_cross(target): 467 | strip_libs(opts, product, target) 468 | 469 | 470 | def clean(opts: iOSOpts, product: str, target: str): 471 | rm_rf( 472 | path_join(opts.configure_dir, '%s-%s-%s' % (product, target, opts.configuration)), 473 | path_join(opts.configure_dir, '%s-%s-%s.config.cache' % (product, target, opts.configuration)), 474 | path_join(opts.install_dir, '%s-%s-%s' % (product, target, opts.configuration)) 475 | ) 476 | 477 | 478 | def main(raw_args): 479 | import cmd_utils 480 | from cmd_utils import custom_bool 481 | from collections import OrderedDict 482 | from typing import Callable 483 | 484 | target_shortcuts = { 485 | 'all-device': device_targets, 486 | 'all-sim': sim_targets, 487 | 'all-cross': cross_targets 488 | } 489 | 490 | target_values = device_targets + sim_targets + cross_targets + list(target_shortcuts) 491 | 492 | actions = OrderedDict() 493 | actions['configure'] = configure 494 | actions['make'] = make 495 | actions['clean'] = clean 496 | 497 | parser = cmd_utils.build_arg_parser(description='Builds the Mono runtime for iOS') 498 | 499 | default_help = 'default: %(default)s' 500 | 501 | default_ios_toolchain = '/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain' 502 | default_osx_toolchain = '/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain' 503 | default_ios_version_min = '10.0' # Same as Godot 504 | 505 | parser.add_argument('action', choices=['configure', 'make', 'clean']) 506 | parser.add_argument('--target', choices=target_values, action='append', required=True) 507 | parser.add_argument('--ios-toolchain', default=default_ios_toolchain, help=default_help) 508 | parser.add_argument('--ios-sdk', default='', help=default_help) 509 | parser.add_argument('--ios-version-min', default=default_ios_version_min, help=default_help) 510 | parser.add_argument('--osx-toolchain', default=default_osx_toolchain, help=default_help) 511 | parser.add_argument('--osx-sdk', default='', help=default_help) 512 | parser.add_argument('--osx-triple-abi', default='darwin18', help=default_help) 513 | 514 | cmd_utils.add_runtime_arguments(parser, default_help) 515 | 516 | args = parser.parse_args(raw_args) 517 | 518 | input_action = args.action 519 | input_targets = args.target 520 | 521 | opts = ios_opts_from_args(args) 522 | 523 | targets = cmd_utils.expand_input_targets(input_targets, target_shortcuts) 524 | 525 | if not os.path.isdir(opts.mono_source_root): 526 | print('Mono sources directory not found: ' + opts.mono_source_root) 527 | sys.exit(1) 528 | 529 | action = actions[input_action] 530 | 531 | try: 532 | for target in targets: 533 | action(opts, 'ios', target) 534 | except BuildError as e: 535 | sys.exit(e.message) 536 | 537 | 538 | if __name__ == '__main__': 539 | from sys import argv 540 | main(argv[1:]) 541 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - '**' 7 | - '!release/**' 8 | paths-ignore: 9 | - '.gitignore' 10 | - 'LICENSE' 11 | - 'README.md' 12 | pull_request: 13 | branches: 14 | - '**' 15 | - '!release/**' 16 | paths-ignore: 17 | - '.gitignore' 18 | - 'LICENSE' 19 | - 'README.md' 20 | create: 21 | branches: 22 | - 'release/**' 23 | 24 | env: 25 | # Use SHA or tag instead of the branch for caching purposes. 26 | MONO_TAG: mono-6.12.0.206 27 | PYTHON_VERSION: '3.11' 28 | # Should match the version that Mono supports. 29 | EMSDK_VERSION: 1.39.9 30 | # platform/iphone/detect.py 31 | IOS_VERSION_MIN: 12.0 32 | # Supporting Xcode 15+ requires backporting a bunch of stuff. 33 | XCODE_VERSION: 14.3.1 34 | 35 | jobs: 36 | linux: 37 | name: Linux 38 | runs-on: ubuntu-22.04 39 | strategy: 40 | matrix: 41 | target: [x86, x86_64] 42 | steps: 43 | - name: Set Environment Variables 44 | run: | 45 | echo "MONO_SOURCE_ROOT=$GITHUB_WORKSPACE/mono_sources" >> $GITHUB_ENV 46 | - name: Install Dependencies 47 | run: | 48 | sudo apt-get -y update 49 | sudo apt-get -y install git autoconf libtool libtool-bin automake gettext cmake python3 curl 50 | - name: Install Linux SDK (x86_64) 51 | if: matrix.target == 'x86_64' 52 | run: | 53 | cd /home/runner 54 | curl -LO https://github.com/godotengine/buildroot/releases/download/godot-2023.08.x-4/x86_64-godot-linux-gnu_sdk-buildroot.tar.bz2 55 | tar xf x86_64-godot-linux-gnu_sdk-buildroot.tar.bz2 56 | rm -f x86_64-godot-linux-gnu_sdk-buildroot.tar.bz2 57 | cd x86_64-godot-linux-gnu_sdk-buildroot 58 | ./relocate-sdk.sh 59 | cd bin 60 | for file in x86_64-godot-*; do alias=$(echo $file | sed "s/godot-//"); ln -s $file $alias; done 61 | echo "PATH=/home/runner/x86_64-godot-linux-gnu_sdk-buildroot/bin:${PATH}" >> $GITHUB_ENV 62 | echo "LD_LIBRARY_PATH=/home/runner/x86_64-godot-linux-gnu_sdk-buildroot/x86_64-godot-linux-gnu/lib64:${LD_LIBRARY_PATH}" >> $GITHUB_ENV 63 | - name: Install Linux SDK (x86) 64 | if: matrix.target == 'x86' 65 | run: | 66 | cd /home/runner 67 | curl -LO https://github.com/godotengine/buildroot/releases/download/godot-2023.08.x-4/i686-godot-linux-gnu_sdk-buildroot.tar.bz2 68 | tar xf i686-godot-linux-gnu_sdk-buildroot.tar.bz2 69 | rm -f i686-godot-linux-gnu_sdk-buildroot.tar.bz2 70 | cd i686-godot-linux-gnu_sdk-buildroot 71 | ./relocate-sdk.sh 72 | cd bin 73 | for file in i686-godot-*; do alias=$(echo $file | sed "s/godot-//"); ln -s $file $alias; done 74 | echo "PATH=/home/runner/i686-godot-linux-gnu_sdk-buildroot/bin:${PATH}" >> $GITHUB_ENV 75 | echo "LD_LIBRARY_PATH=/home/runner/i686-godot-linux-gnu_sdk-buildroot/i686-godot-linux-gnu/lib:${LD_LIBRARY_PATH}" >> $GITHUB_ENV 76 | - name: Cache Mono Sources 77 | id: cache_mono_sources 78 | uses: actions/cache@v4 79 | with: 80 | path: ${{ env.MONO_SOURCE_ROOT }} 81 | key: ${{ runner.os }}-${{ env.MONO_TAG }}-mono-sources 82 | - name: Checkout Mono Sources 83 | if: steps.cache_mono_sources.outputs.cache-hit != 'true' 84 | uses: actions/checkout@v5 85 | with: 86 | repository: mono/mono 87 | ref: ${{ env.MONO_TAG }} 88 | submodules: true 89 | path: ${{ env.MONO_SOURCE_ROOT }} 90 | - name: Clean Mono 91 | run: pushd ${{ env.MONO_SOURCE_ROOT }} && git reset --hard && git clean -xffd && git submodule foreach --recursive git reset --hard && git submodule foreach --recursive git clean -xffd && git submodule update --init --recursive && popd 92 | - name: Checkout 93 | uses: actions/checkout@v5 94 | with: 95 | path: godot-mono-builds 96 | - name: Setup Python 97 | uses: actions/setup-python@v6 98 | with: 99 | python-version: ${{ env.PYTHON_VERSION }} 100 | - name: Patch Mono 101 | run: 102 | python3 godot-mono-builds/patch_mono.py 103 | - name: Configure 104 | run: 105 | python3 godot-mono-builds/linux.py configure --target=${{ matrix.target }} -j 2 106 | - name: Make 107 | run: 108 | python3 godot-mono-builds/linux.py make --target=${{ matrix.target }} -j 2 109 | - name: Compress Output 110 | run: | 111 | mkdir -p $HOME/mono-installs-artifacts 112 | (cd $HOME/mono-installs && zip -ry $HOME/mono-installs-artifacts/linux-${{ matrix.target }}.zip desktop-linux-${{ matrix.target }}-release) 113 | - name: Upload Artifact 114 | uses: actions/upload-artifact@v4 115 | with: 116 | name: linux-${{ matrix.target }} 117 | path: ~/mono-installs-artifacts/linux-${{ matrix.target }}.zip 118 | - name: Clean Mono 119 | run: pushd ${{ env.MONO_SOURCE_ROOT }} && git reset --hard && git clean -xffd && git submodule foreach --recursive git reset --hard && git submodule foreach --recursive git clean -xffd && git submodule update --init --recursive && popd 120 | - name: Upload config.log After Error 121 | if: ${{ failure() }} 122 | uses: actions/upload-artifact@v4 123 | with: 124 | name: linux-${{ matrix.target }}-config.log 125 | path: ~/mono-configs/desktop-linux-${{ matrix.target }}-release/config.log 126 | 127 | windows: 128 | name: Windows 129 | runs-on: ubuntu-22.04 130 | strategy: 131 | matrix: 132 | target: [x86, x86_64] 133 | steps: 134 | - name: Set Environment Variables 135 | run: | 136 | echo "MONO_SOURCE_ROOT=$GITHUB_WORKSPACE/mono_sources" >> $GITHUB_ENV 137 | - name: Install Dependencies (x86) 138 | if: matrix.target == 'x86' 139 | run: | 140 | sudo dpkg --add-architecture i386 141 | sudo apt-get -y update 142 | sudo apt-get -y install git autoconf libtool libtool-bin automake build-essential gettext cmake python3 curl 143 | sudo apt-get -y install mingw-w64 144 | - name: Install Dependencies (x86_64) 145 | if: matrix.target == 'x86_64' 146 | run: | 147 | sudo apt-get -y update 148 | sudo apt-get -y install git autoconf libtool libtool-bin automake build-essential gettext cmake python3 curl 149 | sudo apt-get -y install mingw-w64 libz-mingw-w64-dev 150 | - name: Cache Mono Sources 151 | id: cache_mono_sources 152 | uses: actions/cache@v4 153 | with: 154 | path: ${{ env.MONO_SOURCE_ROOT }} 155 | key: ${{ runner.os }}-${{ env.MONO_TAG }}-mono-sources 156 | - name: Checkout Mono Sources 157 | if: steps.cache_mono_sources.outputs.cache-hit != 'true' 158 | uses: actions/checkout@v5 159 | with: 160 | repository: mono/mono 161 | ref: ${{ env.MONO_TAG }} 162 | submodules: true 163 | path: ${{ env.MONO_SOURCE_ROOT }} 164 | - name: Clean Mono 165 | run: pushd ${{ env.MONO_SOURCE_ROOT }} && git reset --hard && git clean -xffd && git submodule foreach --recursive git reset --hard && git submodule foreach --recursive git clean -xffd && git submodule update --init --recursive && popd 166 | - name: Checkout 167 | uses: actions/checkout@v5 168 | with: 169 | path: godot-mono-builds 170 | - name: Setup Python 171 | uses: actions/setup-python@v6 172 | with: 173 | python-version: ${{ env.PYTHON_VERSION }} 174 | - name: Patch Mono 175 | run: 176 | python3 godot-mono-builds/patch_mono.py 177 | - name: Configure 178 | run: 179 | python3 godot-mono-builds/windows.py configure --target=${{ matrix.target }} -j 2 180 | - name: Make 181 | run: 182 | python3 godot-mono-builds/windows.py make --target=${{ matrix.target }} -j 2 183 | - name: Compress Output 184 | run: | 185 | mkdir -p $HOME/mono-installs-artifacts 186 | (cd $HOME/mono-installs && zip -ry $HOME/mono-installs-artifacts/windows-${{ matrix.target }}.zip desktop-windows-${{ matrix.target }}-release) 187 | - name: Upload Artifact 188 | uses: actions/upload-artifact@v4 189 | with: 190 | name: windows-${{ matrix.target }} 191 | path: ~/mono-installs-artifacts/windows-${{ matrix.target }}.zip 192 | - name: Clean Mono 193 | run: pushd ${{ env.MONO_SOURCE_ROOT }} && git reset --hard && git clean -xffd && git submodule foreach --recursive git reset --hard && git submodule foreach --recursive git clean -xffd && git submodule update --init --recursive && popd 194 | - name: Upload config.log After Error 195 | if: ${{ failure() }} 196 | uses: actions/upload-artifact@v4 197 | with: 198 | name: windows-${{ matrix.target }}-config.log 199 | path: ~/mono-configs/desktop-windows-${{ matrix.target }}-release/config.log 200 | 201 | osx: 202 | name: macOS 203 | runs-on: macos-13 204 | strategy: 205 | matrix: 206 | target: [arm64, x86_64] 207 | steps: 208 | - name: Set Environment Variables 209 | run: | 210 | echo "MONO_SOURCE_ROOT=$GITHUB_WORKSPACE/mono_sources" >> $GITHUB_ENV 211 | - name: Setup compatible Xcode version 212 | uses: maxim-lobanov/setup-xcode@v1 213 | with: 214 | xcode-version: ${{ env.XCODE_VERSION }} 215 | - name: Install Dependencies 216 | run: | 217 | brew install automake 218 | - name: Install cmake 3 219 | uses: jwlawson/actions-setup-cmake@v2 220 | with: 221 | cmake-version: '3.31.7' 222 | - name: Cache Mono Sources 223 | id: cache_mono_sources 224 | uses: actions/cache@v4 225 | with: 226 | path: ${{ env.MONO_SOURCE_ROOT }} 227 | key: ${{ runner.os }}-${{ env.MONO_TAG }}-mono-sources 228 | - name: Checkout Mono Sources 229 | if: steps.cache_mono_sources.outputs.cache-hit != 'true' 230 | uses: actions/checkout@v5 231 | with: 232 | repository: mono/mono 233 | ref: ${{ env.MONO_TAG }} 234 | submodules: true 235 | path: ${{ env.MONO_SOURCE_ROOT }} 236 | - name: Clean Mono 237 | run: pushd ${{ env.MONO_SOURCE_ROOT }} && git reset --hard && git clean -xffd && git submodule foreach --recursive git reset --hard && git submodule foreach --recursive git clean -xffd && git submodule update --init --recursive && popd 238 | - name: Checkout 239 | uses: actions/checkout@v5 240 | with: 241 | path: godot-mono-builds 242 | - name: Setup Python 243 | uses: actions/setup-python@v6 244 | with: 245 | python-version: ${{ env.PYTHON_VERSION }} 246 | - name: Patch Mono 247 | run: | 248 | python3 godot-mono-builds/patch_mono.py 249 | - name: Configure 250 | run: 251 | python3 godot-mono-builds/osx.py configure --target=${{ matrix.target }} -j 2 252 | - name: Make 253 | run: 254 | python3 godot-mono-builds/osx.py make --target=${{ matrix.target }} -j 2 255 | - name: Compress Output 256 | run: | 257 | mkdir -p $HOME/mono-installs-artifacts 258 | (cd $HOME/mono-installs && zip -ry $HOME/mono-installs-artifacts/osx-${{ matrix.target }}.zip desktop-osx-${{ matrix.target }}-release) 259 | - name: Upload Artifact 260 | uses: actions/upload-artifact@v4 261 | with: 262 | name: osx-${{ matrix.target }} 263 | path: ~/mono-installs-artifacts/osx-${{ matrix.target }}.zip 264 | - name: Clean Mono 265 | run: pushd ${{ env.MONO_SOURCE_ROOT }} && git reset --hard && git clean -xffd && git submodule foreach --recursive git reset --hard && git submodule foreach --recursive git clean -xffd && git submodule update --init --recursive && popd 266 | - name: Upload config.log After Error 267 | if: ${{ failure() }} 268 | uses: actions/upload-artifact@v4 269 | with: 270 | name: osx-${{ matrix.target }}-config.log 271 | path: ~/mono-configs/desktop-osx-${{ matrix.target }}-release/config.log 272 | 273 | ios: 274 | name: iOS 275 | runs-on: macos-13 276 | strategy: 277 | matrix: 278 | target: [arm64, x86_64, arm64-sim] 279 | steps: 280 | - name: Set Environment Variables 281 | run: | 282 | echo "MONO_SOURCE_ROOT=$GITHUB_WORKSPACE/mono_sources" >> $GITHUB_ENV 283 | - name: Setup compatible Xcode version 284 | uses: maxim-lobanov/setup-xcode@v1 285 | with: 286 | xcode-version: ${{ env.XCODE_VERSION }} 287 | - name: Install Dependencies 288 | run: | 289 | brew install automake 290 | - name: Install cmake 3 291 | uses: jwlawson/actions-setup-cmake@v2 292 | with: 293 | cmake-version: '3.31.7' 294 | - name: Cache Mono Sources 295 | id: cache_mono_sources 296 | uses: actions/cache@v4 297 | with: 298 | path: ${{ env.MONO_SOURCE_ROOT }} 299 | key: ${{ runner.os }}-${{ env.MONO_TAG }}-mono-sources 300 | - name: Checkout Mono Sources 301 | if: steps.cache_mono_sources.outputs.cache-hit != 'true' 302 | uses: actions/checkout@v5 303 | with: 304 | repository: mono/mono 305 | ref: ${{ env.MONO_TAG }} 306 | submodules: true 307 | path: ${{ env.MONO_SOURCE_ROOT }} 308 | - name: Clean Mono 309 | run: pushd ${{ env.MONO_SOURCE_ROOT }} && git reset --hard && git clean -xffd && git submodule foreach --recursive git reset --hard && git submodule foreach --recursive git clean -xffd && git submodule update --init --recursive && popd 310 | - name: Checkout 311 | uses: actions/checkout@v5 312 | with: 313 | path: godot-mono-builds 314 | - name: Setup Python 315 | uses: actions/setup-python@v6 316 | with: 317 | python-version: ${{ env.PYTHON_VERSION }} 318 | - name: Patch Mono 319 | run: | 320 | python3 godot-mono-builds/patch_mono.py 321 | - name: Configure 322 | run: | 323 | export DISABLE_NO_WEAK_IMPORTS=1 324 | python3 godot-mono-builds/ios.py configure --target=${{ matrix.target }} -j 2 --ios-version-min=${IOS_VERSION_MIN} 325 | - name: Make 326 | run: 327 | python3 godot-mono-builds/ios.py make --target=${{ matrix.target }} -j 2 328 | - name: Compress Output 329 | run: | 330 | mkdir -p $HOME/mono-installs-artifacts 331 | (cd $HOME/mono-installs && zip -ry $HOME/mono-installs-artifacts/ios-${{ matrix.target }}.zip ios-${{ matrix.target }}-release) 332 | - name: Upload Artifact 333 | uses: actions/upload-artifact@v4 334 | with: 335 | name: ios-${{ matrix.target }} 336 | path: ~/mono-installs-artifacts/ios-${{ matrix.target }}.zip 337 | - name: Clean Mono 338 | run: pushd ${{ env.MONO_SOURCE_ROOT }} && git reset --hard && git clean -xffd && git submodule foreach --recursive git reset --hard && git submodule foreach --recursive git clean -xffd && git submodule update --init --recursive && popd 339 | - name: Upload config.log After Error 340 | if: ${{ failure() }} 341 | uses: actions/upload-artifact@v4 342 | with: 343 | name: ios-${{ matrix.target }}-config.log 344 | path: ~/mono-configs/ios-${{ matrix.target }}-release/config.log 345 | 346 | ios-cross: 347 | # Disable for now as it doesn't build. 348 | needs: llvm 349 | name: iOS Cross-compiler 350 | runs-on: macos-13 351 | strategy: 352 | matrix: 353 | target: [cross-arm64] 354 | include: 355 | - target: cross-arm64 356 | llvm: llvm64 357 | runtime_target: arm64 358 | steps: 359 | - name: Set Environment Variables 360 | run: | 361 | echo "MONO_SOURCE_ROOT=$GITHUB_WORKSPACE/mono_sources" >> $GITHUB_ENV 362 | - name: Setup compatible Xcode version 363 | uses: maxim-lobanov/setup-xcode@v1 364 | with: 365 | xcode-version: ${{ env.XCODE_VERSION }} 366 | - name: Install Dependencies 367 | run: | 368 | brew install automake 369 | - name: Install cmake 3 370 | uses: jwlawson/actions-setup-cmake@v2 371 | with: 372 | cmake-version: '3.31.7' 373 | - name: Cache Mono Sources 374 | id: cache_mono_sources 375 | uses: actions/cache@v4 376 | with: 377 | path: ${{ env.MONO_SOURCE_ROOT }} 378 | key: ${{ runner.os }}-${{ env.MONO_TAG }}-mono-sources 379 | - name: Checkout Mono Sources 380 | if: steps.cache_mono_sources.outputs.cache-hit != 'true' 381 | uses: actions/checkout@v5 382 | with: 383 | repository: mono/mono 384 | ref: ${{ env.MONO_TAG }} 385 | submodules: true 386 | path: ${{ env.MONO_SOURCE_ROOT }} 387 | - name: Clean Mono 388 | run: pushd ${{ env.MONO_SOURCE_ROOT }} && git reset --hard && git clean -xffd && git submodule foreach --recursive git reset --hard && git submodule foreach --recursive git clean -xffd && git submodule update --init --recursive && popd 389 | - name: Checkout 390 | uses: actions/checkout@v5 391 | with: 392 | path: godot-mono-builds 393 | - name: Setup Python 394 | uses: actions/setup-python@v6 395 | with: 396 | python-version: ${{ env.PYTHON_VERSION }} 397 | - name: Patch Mono 398 | run: | 399 | python3 godot-mono-builds/patch_mono.py 400 | - name: Download LLVM artifact 401 | uses: actions/download-artifact@v5 402 | with: 403 | name: llvm-${{ matrix.llvm }}-macos-13 404 | # Tilde ~/ not supported when downloading yet: https://github.com/actions/download-artifact/issues/37 405 | # File permissions are also messed up: https://github.com/actions/upload-artifact/issues/38 406 | # We have to manually move the folder and restore the file permissions in the next step. 407 | path: ./llvm-${{ matrix.llvm }} 408 | - name: Stamp LLVM 409 | run: | 410 | mkdir -p $HOME/mono-installs/ && mv ./llvm-${{ matrix.llvm }} $HOME/mono-installs/ 411 | chmod 755 $HOME/mono-installs/llvm-${{ matrix.llvm }}/bin/* 412 | mkdir -p $HOME/mono-configs/ && touch $HOME/mono-configs/.stamp-${{ matrix.llvm }}-make 413 | - name: Configure Runtime 414 | run: | 415 | export DISABLE_NO_WEAK_IMPORTS=1 416 | python3 godot-mono-builds/ios.py configure --target=${{ matrix.runtime_target }} -j 2 417 | - name: Configure 418 | run: | 419 | export DISABLE_NO_WEAK_IMPORTS=1 420 | python3 godot-mono-builds/ios.py configure --target=${{ matrix.target }} -j 2 421 | - name: Make 422 | run: 423 | python3 godot-mono-builds/ios.py make --target=${{ matrix.target }} -j 2 424 | - name: Compress Output 425 | run: | 426 | mkdir -p $HOME/mono-installs-artifacts 427 | (cd $HOME/mono-installs && zip -ry $HOME/mono-installs-artifacts/ios-${{ matrix.target }}.zip ios-${{ matrix.target }}-release) 428 | - name: Upload Artifact 429 | uses: actions/upload-artifact@v4 430 | with: 431 | name: ios-${{ matrix.target }} 432 | path: ~/mono-installs-artifacts/ios-${{ matrix.target }}.zip 433 | - name: Clean Mono 434 | run: pushd ${{ env.MONO_SOURCE_ROOT }} && git reset --hard && git clean -xffd && git submodule foreach --recursive git reset --hard && git submodule foreach --recursive git clean -xffd && git submodule update --init --recursive && popd 435 | - name: Upload Runtime config.log After Error 436 | if: ${{ failure() }} 437 | uses: actions/upload-artifact@v4 438 | with: 439 | name: ios-${{ matrix.target }}-runtime-config.log 440 | path: ~/mono-configs/ios-${{ matrix.runtime_target }}-release/config.log 441 | - name: Upload Cross config.log After Error 442 | if: ${{ failure() }} 443 | uses: actions/upload-artifact@v4 444 | with: 445 | name: ios-${{ matrix.target }}-config.log 446 | path: ~/mono-configs/ios-${{ matrix.target }}-release/config.log 447 | 448 | android: 449 | name: Android 450 | runs-on: ubuntu-22.04 451 | strategy: 452 | matrix: 453 | target: [armv7, arm64v8, x86, x86_64] 454 | steps: 455 | - name: Set Environment Variables 456 | run: | 457 | echo "MONO_SOURCE_ROOT=$GITHUB_WORKSPACE/mono_sources" >> $GITHUB_ENV 458 | - name: Install Dependencies 459 | run: | 460 | sudo apt-get -y update 461 | sudo apt-get -y install git autoconf libtool libtool-bin automake build-essential gettext cmake python3 curl 462 | - name: Cache Mono Sources 463 | id: cache_mono_sources 464 | uses: actions/cache@v4 465 | with: 466 | path: ${{ env.MONO_SOURCE_ROOT }} 467 | key: ${{ runner.os }}-${{ env.MONO_TAG }}-mono-sources 468 | - name: Checkout Mono Sources 469 | if: steps.cache_mono_sources.outputs.cache-hit != 'true' 470 | uses: actions/checkout@v5 471 | with: 472 | repository: mono/mono 473 | ref: ${{ env.MONO_TAG }} 474 | submodules: true 475 | path: ${{ env.MONO_SOURCE_ROOT }} 476 | - name: Clean Mono 477 | run: pushd ${{ env.MONO_SOURCE_ROOT }} && git reset --hard && git clean -xffd && git submodule foreach --recursive git reset --hard && git submodule foreach --recursive git clean -xffd && git submodule update --init --recursive && popd 478 | - name: Checkout 479 | uses: actions/checkout@v5 480 | with: 481 | path: godot-mono-builds 482 | - name: Setup Python 483 | uses: actions/setup-python@v6 484 | with: 485 | python-version: ${{ env.PYTHON_VERSION }} 486 | - name: Patch Mono 487 | run: 488 | python3 godot-mono-builds/patch_mono.py 489 | - name: Configure 490 | run: 491 | python3 godot-mono-builds/android.py configure --target=${{ matrix.target }} -j 2 492 | - name: Make 493 | run: 494 | python3 godot-mono-builds/android.py make --target=${{ matrix.target }} -j 2 495 | - name: Compress Output 496 | run: | 497 | mkdir -p $HOME/mono-installs-artifacts 498 | (cd $HOME/mono-installs && zip -ry $HOME/mono-installs-artifacts/android-${{ matrix.target }}.zip android-${{ matrix.target }}-release) 499 | - name: Upload Artifact 500 | uses: actions/upload-artifact@v4 501 | with: 502 | name: android-${{ matrix.target }} 503 | path: ~/mono-installs-artifacts/android-${{ matrix.target }}.zip 504 | - name: Clean Mono 505 | run: pushd ${{ env.MONO_SOURCE_ROOT }} && git reset --hard && git clean -xffd && git submodule foreach --recursive git reset --hard && git submodule foreach --recursive git clean -xffd && git submodule update --init --recursive && popd 506 | - name: Upload config.log After Error 507 | if: ${{ failure() }} 508 | uses: actions/upload-artifact@v4 509 | with: 510 | name: android-${{ matrix.target }}-config.log 511 | path: ~/mono-configs/android-${{ matrix.target }}-release/config.log 512 | 513 | wasm: 514 | name: WebAssembly 515 | runs-on: ubuntu-22.04 516 | strategy: 517 | matrix: 518 | target: [runtime, runtime-threads] 519 | steps: 520 | - name: Set Environment Variables 521 | run: | 522 | echo "MONO_SOURCE_ROOT=$GITHUB_WORKSPACE/mono_sources" >> $GITHUB_ENV 523 | - name: Install Dependencies 524 | run: | 525 | sudo apt-get -y update 526 | sudo apt-get -y install git autoconf libtool libtool-bin automake build-essential gettext cmake python3 curl 527 | - name: Cache Mono Sources 528 | id: cache_mono_sources 529 | uses: actions/cache@v4 530 | with: 531 | path: ${{ env.MONO_SOURCE_ROOT }} 532 | key: ${{ runner.os }}-${{ env.MONO_TAG }}-mono-sources 533 | - name: Checkout Mono Sources 534 | if: steps.cache_mono_sources.outputs.cache-hit != 'true' 535 | uses: actions/checkout@v5 536 | with: 537 | repository: mono/mono 538 | ref: ${{ env.MONO_TAG }} 539 | submodules: true 540 | path: ${{ env.MONO_SOURCE_ROOT }} 541 | - name: Clean Mono 542 | run: pushd ${{ env.MONO_SOURCE_ROOT }} && git reset --hard && git clean -xffd && git submodule foreach --recursive git reset --hard && git submodule foreach --recursive git clean -xffd && git submodule update --init --recursive && popd 543 | - name: Checkout 544 | uses: actions/checkout@v5 545 | with: 546 | path: godot-mono-builds 547 | - name: Setup Python 548 | uses: actions/setup-python@v6 549 | with: 550 | python-version: ${{ env.PYTHON_VERSION }} 551 | - name: Setup Emscripten SDK 552 | uses: mymindstorm/setup-emsdk@v14 553 | with: 554 | version: ${{ env.EMSDK_VERSION }} 555 | - name: Patch Mono 556 | run: 557 | python3 godot-mono-builds/patch_mono.py 558 | - name: Configure 559 | run: 560 | python3 godot-mono-builds/wasm.py configure --target=${{ matrix.target }} -j 2 561 | - name: Make 562 | run: 563 | python3 godot-mono-builds/wasm.py make --target=${{ matrix.target }} -j 2 564 | - name: Compress Output 565 | run: | 566 | mkdir -p $HOME/mono-installs-artifacts 567 | (cd $HOME/mono-installs && zip -ry $HOME/mono-installs-artifacts/wasm-${{ matrix.target }}.zip wasm-${{ matrix.target }}-release) 568 | - name: Upload Artifact 569 | uses: actions/upload-artifact@v4 570 | with: 571 | name: wasm-${{ matrix.target }} 572 | path: ~/mono-installs-artifacts/wasm-${{ matrix.target }}.zip 573 | - name: Clean Mono 574 | run: pushd ${{ env.MONO_SOURCE_ROOT }} && git reset --hard && git clean -xffd && git submodule foreach --recursive git reset --hard && git submodule foreach --recursive git clean -xffd && git submodule update --init --recursive && popd 575 | - name: Upload config.log After Error 576 | if: ${{ failure() }} 577 | uses: actions/upload-artifact@v4 578 | with: 579 | name: wasm-${{ matrix.target }}-config.log 580 | path: ~/mono-configs/wasm-${{ matrix.target }}-release/config.log 581 | 582 | llvm: 583 | name: LLVM 584 | runs-on: ${{ matrix.os }} 585 | strategy: 586 | matrix: 587 | os: [ubuntu-22.04, macos-13] 588 | target: [llvm64, llvmwin64] 589 | exclude: 590 | # We already build for Windows on ubuntu-22.04 591 | - os: macos-13 592 | target: llvmwin64 593 | steps: 594 | - name: Cache LLVM 595 | id: cache_llvm 596 | uses: actions/cache@v4 597 | with: 598 | path: ~/mono-installs/llvm-${{ matrix.target }} 599 | key: ${{ runner.os }}-${{ env.MONO_TAG }}-llvm-${{ matrix.target }} 600 | - name: Set Environment Variables 601 | run: | 602 | echo "MONO_SOURCE_ROOT=$GITHUB_WORKSPACE/mono_sources" >> $GITHUB_ENV 603 | - name: Setup compatible Xcode version 604 | if: steps.cache_llvm.outputs.cache-hit != 'true' && matrix.os == 'macos-13' 605 | uses: maxim-lobanov/setup-xcode@v1 606 | with: 607 | xcode-version: ${{ env.XCODE_VERSION }} 608 | - name: Install Dependencies (Linux) 609 | if: steps.cache_llvm.outputs.cache-hit != 'true' && runner.os == 'Linux' 610 | run: | 611 | sudo apt-get -y update 612 | sudo apt-get -y install git autoconf libtool libtool-bin automake gettext cmake python3 curl 613 | - name: Install Linux SDK (x86_64) 614 | if: steps.cache_llvm.outputs.cache-hit != 'true' && runner.os == 'Linux' 615 | run: | 616 | cd /home/runner 617 | curl -LO https://github.com/godotengine/buildroot/releases/download/godot-2023.08.x-4/x86_64-godot-linux-gnu_sdk-buildroot.tar.bz2 618 | tar xf x86_64-godot-linux-gnu_sdk-buildroot.tar.bz2 619 | rm -f x86_64-godot-linux-gnu_sdk-buildroot.tar.bz2 620 | cd x86_64-godot-linux-gnu_sdk-buildroot 621 | ./relocate-sdk.sh 622 | cd bin 623 | for file in x86_64-godot-*; do alias=$(echo $file | sed "s/godot-//"); ln -s $file $alias; done 624 | echo "PATH=/home/runner/x86_64-godot-linux-gnu_sdk-buildroot/bin:${PATH}" >> $GITHUB_ENV 625 | echo "LD_LIBRARY_PATH=/home/runner/x86_64-godot-linux-gnu_sdk-buildroot/x86_64-godot-linux-gnu/lib64:${LD_LIBRARY_PATH}" >> $GITHUB_ENV 626 | - name: Install Dependencies (Linux Targeting Windows) 627 | if: steps.cache_llvm.outputs.cache-hit != 'true' && runner.os == 'Linux' && matrix.target == 'llvmwin64' 628 | run: | 629 | sudo apt-get -y install mingw-w64 libz-mingw-w64-dev 630 | - name: Install Dependencies (macOS) 631 | if: steps.cache_llvm.outputs.cache-hit != 'true' && matrix.os == 'macos-13' 632 | run: | 633 | brew install automake 634 | - name: Install cmake 3 (macOS) 635 | if: steps.cache_llvm.outputs.cache-hit != 'true' && matrix.os == 'macos-13' 636 | uses: jwlawson/actions-setup-cmake@v2 637 | with: 638 | cmake-version: '3.31.7' 639 | - name: Cache Mono Sources 640 | if: steps.cache_llvm.outputs.cache-hit != 'true' 641 | id: cache_mono_sources 642 | uses: actions/cache@v4 643 | with: 644 | path: ${{ env.MONO_SOURCE_ROOT }} 645 | key: ${{ runner.os }}-${{ env.MONO_TAG }}-mono-sources 646 | - name: Checkout Mono Sources 647 | if: steps.cache_mono_sources.outputs.cache-hit != 'true' && steps.cache_llvm.outputs.cache-hit != 'true' 648 | uses: actions/checkout@v5 649 | with: 650 | repository: mono/mono 651 | ref: ${{ env.MONO_TAG }} 652 | submodules: true 653 | path: ${{ env.MONO_SOURCE_ROOT }} 654 | - name: Clean Mono 655 | if: steps.cache_llvm.outputs.cache-hit != 'true' 656 | run: pushd ${{ env.MONO_SOURCE_ROOT }} && git reset --hard && git clean -xffd && git submodule foreach --recursive git reset --hard && git submodule foreach --recursive git clean -xffd && git submodule update --init --recursive && popd 657 | - name: Checkout 658 | if: steps.cache_llvm.outputs.cache-hit != 'true' 659 | uses: actions/checkout@v5 660 | with: 661 | path: godot-mono-builds 662 | - name: Setup Python 663 | if: steps.cache_llvm.outputs.cache-hit != 'true' 664 | uses: actions/setup-python@v6 665 | with: 666 | python-version: ${{ env.PYTHON_VERSION }} 667 | - name: Patch Mono 668 | if: steps.cache_llvm.outputs.cache-hit != 'true' 669 | run: | 670 | python3 godot-mono-builds/patch_mono.py 671 | - name: Make 672 | if: steps.cache_llvm.outputs.cache-hit != 'true' 673 | run: 674 | python3 godot-mono-builds/llvm.py make --target=${{ matrix.target }} -j 2 675 | - name: Upload LLVM Artifact 676 | uses: actions/upload-artifact@v4 677 | with: 678 | name: llvm-${{ matrix.target }}-${{ matrix.os }} 679 | path: ~/mono-installs/llvm-${{ matrix.target }} 680 | - name: Clean Mono 681 | if: steps.cache_llvm.outputs.cache-hit != 'true' 682 | run: pushd ${{ env.MONO_SOURCE_ROOT }} && git reset --hard && git clean -xffd && git submodule foreach --recursive git reset --hard && git submodule foreach --recursive git clean -xffd && git submodule update --init --recursive && popd 683 | - name: Upload config.log After Error 684 | if: ${{ failure() }} 685 | uses: actions/upload-artifact@v4 686 | with: 687 | name: llvm-${{ matrix.target }}-${{ matrix.os }}-config.log 688 | path: ~/mono-configs/llvm-${{ matrix.target }}/config.log 689 | 690 | bcl: 691 | name: BCL 692 | runs-on: ubuntu-22.04 693 | strategy: 694 | matrix: 695 | product: [desktop, desktop-win32, android, ios, wasm] 696 | steps: 697 | - name: Set Environment Variables 698 | run: | 699 | echo "MONO_SOURCE_ROOT=$GITHUB_WORKSPACE/mono_sources" >> $GITHUB_ENV 700 | - name: Install Dependencies 701 | run: | 702 | sudo apt-get -y update 703 | sudo apt-get -y install git autoconf libtool libtool-bin automake gettext cmake python3 curl 704 | - name: Install Linux SDK (x86_64) 705 | run: | 706 | cd /home/runner 707 | curl -LO https://github.com/godotengine/buildroot/releases/download/godot-2023.08.x-4/x86_64-godot-linux-gnu_sdk-buildroot.tar.bz2 708 | tar xf x86_64-godot-linux-gnu_sdk-buildroot.tar.bz2 709 | rm -f x86_64-godot-linux-gnu_sdk-buildroot.tar.bz2 710 | cd x86_64-godot-linux-gnu_sdk-buildroot 711 | ./relocate-sdk.sh 712 | cd bin 713 | for file in x86_64-godot-*; do alias=$(echo $file | sed "s/godot-//"); ln -s $file $alias; done 714 | echo "PATH=/home/runner/x86_64-godot-linux-gnu_sdk-buildroot/bin:${PATH}" >> $GITHUB_ENV 715 | echo "LD_LIBRARY_PATH=/home/runner/x86_64-godot-linux-gnu_sdk-buildroot/x86_64-godot-linux-gnu/lib64:${LD_LIBRARY_PATH}" >> $GITHUB_ENV 716 | - name: Cache Mono Sources 717 | id: cache_mono_sources 718 | uses: actions/cache@v4 719 | with: 720 | path: ${{ env.MONO_SOURCE_ROOT }} 721 | key: ${{ runner.os }}-${{ env.MONO_TAG }}-mono-sources 722 | - name: Checkout Mono Sources 723 | if: steps.cache_mono_sources.outputs.cache-hit != 'true' 724 | uses: actions/checkout@v5 725 | with: 726 | repository: mono/mono 727 | ref: ${{ env.MONO_TAG }} 728 | submodules: true 729 | path: ${{ env.MONO_SOURCE_ROOT }} 730 | - name: Clean Mono 731 | run: pushd ${{ env.MONO_SOURCE_ROOT }} && git reset --hard && git clean -xffd && git submodule foreach --recursive git reset --hard && git submodule foreach --recursive git clean -xffd && git submodule update --init --recursive && popd 732 | - name: Checkout 733 | uses: actions/checkout@v5 734 | with: 735 | path: godot-mono-builds 736 | - name: Setup Python 737 | uses: actions/setup-python@v6 738 | with: 739 | python-version: ${{ env.PYTHON_VERSION }} 740 | - name: Patch Mono 741 | run: 742 | python3 godot-mono-builds/patch_mono.py 743 | - name: Make Desktop BCL for this Build Platform 744 | if: matrix.product == 'desktop-win32' 745 | run: 746 | python3 godot-mono-builds/bcl.py make --product=desktop -j 2 747 | - name: Make 748 | run: 749 | python3 godot-mono-builds/bcl.py make --product=${{ matrix.product }} -j 2 750 | - name: Compress Output 751 | run: | 752 | mkdir -p $HOME/mono-installs-artifacts 753 | (cd $HOME/mono-installs && zip -ry $HOME/mono-installs-artifacts/bcl-${{ matrix.product }}.zip ${{ matrix.product }}-bcl) 754 | - name: Upload Artifact 755 | uses: actions/upload-artifact@v4 756 | with: 757 | name: bcl-${{ matrix.product }} 758 | path: ~/mono-installs-artifacts/bcl-${{ matrix.product }}.zip 759 | - name: Clean Mono 760 | run: pushd ${{ env.MONO_SOURCE_ROOT }} && git reset --hard && git clean -xffd && git submodule foreach --recursive git reset --hard && git submodule foreach --recursive git clean -xffd && git submodule update --init --recursive && popd 761 | - name: Upload config.log After Error 762 | if: ${{ failure() }} 763 | uses: actions/upload-artifact@v4 764 | with: 765 | name: bcl-${{ matrix.product }}-config.log 766 | path: ~/mono-configs/bcl/config.log 767 | 768 | create-release: 769 | if: success() && github.event_name == 'create' && startsWith(github.ref, 'refs/heads/release/') 770 | needs: [linux, windows, osx, ios, ios-cross, android, wasm, bcl] 771 | name: Create Release 772 | runs-on: ubuntu-22.04 773 | outputs: 774 | release_upload_url: ${{ steps.create_release.outputs.upload_url }} 775 | steps: 776 | - name: Short SHA 777 | id: short-sha 778 | run: echo "::set-output name=sha7::$(echo ${GITHUB_SHA} | cut -c1-7)" 779 | - name: Create Release 780 | id: create_release 781 | uses: actions/create-release@v1 782 | env: 783 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 784 | with: 785 | tag_name: release-${{ steps.short-sha.outputs.sha7 }} 786 | release_name: Release ${{ steps.short-sha.outputs.sha7 }} with ${{ env.MONO_TAG }} 787 | body: | 788 | Mono Version: ${{ env.MONO_TAG }} 789 | 790 | EMSDK Version: ${{ env.EMSDK_VERSION }} 791 | iOS Min Version: ${{ env.IOS_VERSION_MIN }} 792 | draft: false 793 | prerelease: false 794 | 795 | upload-release-artifacts: 796 | if: success() && github.event_name == 'create' && startsWith(github.ref, 'refs/heads/release/') 797 | needs: create-release 798 | name: Upload Release Artifacts 799 | runs-on: ubuntu-22.04 800 | strategy: 801 | matrix: 802 | artifact_name: [linux-x86, linux-x86_64, windows-x86, windows-x86_64, osx-arm64, osx-x86_64, 803 | ios-arm64, ios-x86_64, ios-cross-arm64, 804 | android-armv7, android-arm64v8, android-x86, android-x86_64, 805 | wasm-runtime, wasm-runtime-threads, 806 | bcl-desktop, bcl-desktop-win32, bcl-android, bcl-ios, bcl-wasm] 807 | steps: 808 | - name: Download Artifact 809 | uses: actions/download-artifact@v5 810 | with: 811 | name: ${{ matrix.artifact_name }} 812 | path: ./ 813 | - name: Upload linux-x86 814 | id: upload-release-asset 815 | uses: actions/upload-release-asset@v1 816 | env: 817 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 818 | with: 819 | upload_url: ${{ needs.create-release.outputs.release_upload_url }} 820 | asset_path: ./${{ matrix.artifact_name }}.zip 821 | asset_name: ${{ matrix.artifact_name }}.zip 822 | asset_content_type: application/zip 823 | --------------------------------------------------------------------------------