Browse Source

Android build and export for the mono module

Ignacio Etcheverry 6 years ago
parent
commit
14df9e5cb2

+ 5 - 30
modules/mono/build_scripts/godotsharptools_build.py

@@ -1,6 +1,5 @@
 # Build GodotSharpTools solution
 
-
 import os
 
 from SCons.Script import Builder, Dir
@@ -53,21 +52,9 @@ def find_nuget_windows(env):
         if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
             return hint_path
 
-    from . import mono_reg_utils as monoreg
-
-    mono_root = ''
-    bits = env['bits']
+    from . mono_reg_utils import find_mono_root_dir
 
-    if bits == '32':
-        if os.getenv('MONO32_PREFIX'):
-            mono_root = os.getenv('MONO32_PREFIX')
-        else:
-            mono_root = monoreg.find_mono_root_dir(bits)
-    else:
-        if os.getenv('MONO64_PREFIX'):
-            mono_root = os.getenv('MONO64_PREFIX')
-        else:
-            mono_root = monoreg.find_mono_root_dir(bits)
+    mono_root = env['mono_prefix'] or find_mono_root_dir(env['bits'])
 
     if mono_root:
         mono_bin_dir = os.path.join(mono_root, 'bin')
@@ -114,21 +101,9 @@ def find_msbuild_unix(filename):
 
 
 def find_msbuild_windows(env):
-    from . import mono_reg_utils as monoreg
+    from . mono_reg_utils import find_mono_root_dir, find_msbuild_tools_path_reg
 
-    mono_root = ''
-    bits = env['bits']
-
-    if bits == '32':
-        if os.getenv('MONO32_PREFIX'):
-            mono_root = os.getenv('MONO32_PREFIX')
-        else:
-            mono_root = monoreg.find_mono_root_dir(bits)
-    else:
-        if os.getenv('MONO64_PREFIX'):
-            mono_root = os.getenv('MONO64_PREFIX')
-        else:
-            mono_root = monoreg.find_mono_root_dir(bits)
+    mono_root = env['mono_prefix'] or find_mono_root_dir(env['bits'])
 
     if not mono_root:
         raise RuntimeError('Cannot find mono root directory')
@@ -148,7 +123,7 @@ def find_msbuild_windows(env):
         }
         return (msbuild_mono, framework_path, mono_msbuild_env)
 
-    msbuild_tools_path = monoreg.find_msbuild_tools_path_reg()
+    msbuild_tools_path = find_msbuild_tools_path_reg()
 
     if msbuild_tools_path:
         return (os.path.join(msbuild_tools_path, 'MSBuild.exe'), framework_path, {})

+ 85 - 46
modules/mono/build_scripts/mono_configure.py

@@ -1,15 +1,30 @@
 import imp
 import os
+import os.path
 import sys
 import subprocess
 
 from distutils.version import LooseVersion
-from SCons.Script import BoolVariable, Dir, Environment, Variables
+from SCons.Script import Dir, Environment
 
 if os.name == 'nt':
     from . import mono_reg_utils as monoreg
 
 
+android_arch_dirs = {
+    'armv7': 'armeabi-v7a',
+    'arm64v8': 'arm64-v8a',
+    'x86': 'x86',
+    'x86_64': 'x86_64'
+}
+
+
+def get_android_out_dir(env):
+    return os.path.join(Dir('#platform/android/java/libs').abspath,
+                        'release' if env['target'] == 'release' else 'debug',
+                        android_arch_dirs[env['android_arch']])
+
+
 def find_file_in_dir(directory, files, prefix='', extension=''):
     if not extension.startswith('.'):
         extension = '.' + extension
@@ -20,47 +35,55 @@ def find_file_in_dir(directory, files, prefix='', extension=''):
 
 
 def copy_file(src_dir, dst_dir, name):
-    from shutil import copyfile
+    from shutil import copy
 
-    src_path = os.path.join(src_dir, name)
-    dst_path = os.path.join(dst_dir, name)
+    src_path = os.path.join(Dir(src_dir).abspath, name)
+    dst_dir = Dir(dst_dir).abspath
 
     if not os.path.isdir(dst_dir):
         os.mkdir(dst_dir)
 
-    copyfile(src_path, dst_path)
+    copy(src_path, dst_dir)
 
 
 def configure(env, env_mono):
+    from SCons.Script import BoolVariable, PathVariable, Variables
+
     envvars = Variables()
+    envvars.Add(PathVariable('mono_prefix', 'Path to the mono installation directory for the target platform and architecture', '', PathVariable.PathAccept))
     envvars.Add(BoolVariable('mono_static', 'Statically link mono', False))
     envvars.Add(BoolVariable('copy_mono_root', 'Make a copy of the mono installation directory to bundle with the editor', False))
     envvars.Update(env)
 
     bits = env['bits']
+    is_android = env['platform'] == 'android'
 
     tools_enabled = env['tools']
     mono_static = env['mono_static']
     copy_mono_root = env['copy_mono_root']
 
+    mono_prefix = env['mono_prefix']
+
     mono_lib_names = ['mono-2.0-sgen', 'monosgen-2.0']
 
+    if is_android and not env['android_arch'] in android_arch_dirs:
+        raise RuntimeError('This module does not support for the specified \'android_arch\': ' + env['android_arch'])
+
+    if is_android and tools_enabled:
+        # TODO: Implement this. We have to add the data directory to the apk, concretely the Api and Tools folders.
+        raise RuntimeError('This module does not currently support building for android with tools enabled')
+
+    if (os.getenv('MONO32_PREFIX') or os.getenv('MONO64_PREFIX')) and not mono_prefix:
+        print("WARNING: The environment variables 'MONO32_PREFIX' and 'MONO64_PREFIX' are deprecated; use the 'mono_prefix' SCons parameter instead")
+
     if env['platform'] == 'windows':
-        mono_root = ''
+        mono_root = mono_prefix
 
-        if bits == '32':
-            if os.getenv('MONO32_PREFIX'):
-                mono_root = os.getenv('MONO32_PREFIX')
-            elif os.name == 'nt':
-                mono_root = monoreg.find_mono_root_dir(bits)
-        else:
-            if os.getenv('MONO64_PREFIX'):
-                mono_root = os.getenv('MONO64_PREFIX')
-            elif os.name == 'nt':
-                mono_root = monoreg.find_mono_root_dir(bits)
+        if not mono_root and os.name == 'nt':
+            mono_root = monoreg.find_mono_root_dir(bits)
 
         if not mono_root:
-            raise RuntimeError('Mono installation directory not found')
+            raise RuntimeError("Mono installation directory not found; specify one manually with the 'mono_prefix' SCons parameter")
 
         print('Found Mono root directory: ' + mono_root)
 
@@ -113,21 +136,18 @@ def configure(env, env_mono):
             if not mono_dll_name:
                 raise RuntimeError('Could not find mono shared library in: ' + mono_bin_path)
 
-            copy_file(mono_bin_path, 'bin', mono_dll_name + '.dll')
+            copy_file(mono_bin_path, '#bin', mono_dll_name + '.dll')
     else:
         is_apple = (sys.platform == 'darwin' or "osxcross" in env)
 
         sharedlib_ext = '.dylib' if is_apple else '.so'
 
-        mono_root = ''
+        mono_root = mono_prefix
         mono_lib_path = ''
+        mono_so_name = ''
 
-        if bits == '32':
-            if os.getenv('MONO32_PREFIX'):
-                mono_root = os.getenv('MONO32_PREFIX')
-        else:
-            if os.getenv('MONO64_PREFIX'):
-                mono_root = os.getenv('MONO64_PREFIX')
+        if not mono_root and is_android:
+            raise RuntimeError("Mono installation directory not found; specify one manually with the 'mono_prefix' SCons parameter")
 
         if not mono_root and is_apple:
             # Try with some known directories under OSX
@@ -142,7 +162,8 @@ def configure(env, env_mono):
         if not mono_root and mono_static:
             mono_root = pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext)
             if not mono_root:
-                raise RuntimeError('Building with mono_static=yes, but failed to find the mono prefix with pkg-config. Specify one manually')
+                raise RuntimeError("Building with mono_static=yes, but failed to find the mono prefix with pkg-config; " + \
+                    "specify one manually with the 'mono_prefix' SCons parameter")
 
         if mono_root:
             print('Found Mono root directory: ' + mono_root)
@@ -174,6 +195,8 @@ def configure(env, env_mono):
 
             if is_apple:
                 env.Append(LIBS=['iconv', 'pthread'])
+            elif is_android:
+                env.Append(LIBS=['m', 'dl'])
             else:
                 env.Append(LIBS=['m', 'rt', 'dl', 'pthread'])
 
@@ -183,7 +206,7 @@ def configure(env, env_mono):
                 if not mono_so_name:
                     raise RuntimeError('Could not find mono shared library in: ' + mono_lib_path)
 
-                copy_file(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext)
+                copy_file(mono_lib_path, '#bin', 'lib' + mono_so_name + sharedlib_ext)
         else:
             assert not mono_static
 
@@ -196,9 +219,6 @@ def configure(env, env_mono):
             env.ParseConfig('pkg-config monosgen-2 --libs')
             env_mono.ParseConfig('pkg-config monosgen-2 --cflags')
 
-            mono_lib_path = ''
-            mono_so_name = ''
-
             tmpenv = Environment()
             tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH'))
             tmpenv.ParseConfig('pkg-config monosgen-2 --libs-only-L')
@@ -213,11 +233,13 @@ def configure(env, env_mono):
             if not mono_so_name:
                 raise RuntimeError('Could not find mono shared library in: ' + str(tmpenv['LIBPATH']))
 
-            copy_file(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext)
+        if not mono_static:
+            libs_output_dir = get_android_out_dir(env) if is_android else '#bin'
+            copy_file(mono_lib_path, libs_output_dir, 'lib' + mono_so_name + sharedlib_ext)
 
         env.Append(LINKFLAGS='-rdynamic')
 
-    if not tools_enabled:
+    if not tools_enabled and not is_android:
         if not mono_root:
             mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip()
 
@@ -241,7 +263,7 @@ def make_template_dir(env, mono_root):
 
     template_dir_name = ''
 
-    if platform in ['windows', 'osx', 'x11']:
+    if platform in ['windows', 'osx', 'x11', 'android']:
         template_dir_name = 'data.mono.%s.%s.%s' % (platform, env['bits'], target)
     else:
         assert False
@@ -256,12 +278,13 @@ def make_template_dir(env, mono_root):
 
     # Copy etc/mono/
 
-    template_mono_config_dir = os.path.join(template_mono_root_dir, 'etc', 'mono')
-    copy_mono_etc_dir(mono_root, template_mono_config_dir, env['platform'])
+    if platform != 'android':
+        template_mono_config_dir = os.path.join(template_mono_root_dir, 'etc', 'mono')
+        copy_mono_etc_dir(mono_root, template_mono_config_dir, env['platform'])
 
     # Copy the required shared libraries
 
-    copy_mono_shared_libs(mono_root, template_mono_root_dir, env['platform'])
+    copy_mono_shared_libs(env, mono_root, template_mono_root_dir)
 
 
 def copy_mono_root_files(env, mono_root):
@@ -285,7 +308,7 @@ def copy_mono_root_files(env, mono_root):
 
     # Copy the required shared libraries
 
-    copy_mono_shared_libs(mono_root, editor_mono_root_dir, env['platform'])
+    copy_mono_shared_libs(env, mono_root, editor_mono_root_dir)
 
     # Copy framework assemblies
 
@@ -332,39 +355,55 @@ def copy_mono_etc_dir(mono_root, target_mono_config_dir, platform):
     copy_tree(os.path.join(mono_etc_dir, '2.0'), os.path.join(target_mono_config_dir, '2.0'))
     copy_tree(os.path.join(mono_etc_dir, '4.0'), os.path.join(target_mono_config_dir, '4.0'))
     copy_tree(os.path.join(mono_etc_dir, '4.5'), os.path.join(target_mono_config_dir, '4.5'))
-    copy_tree(os.path.join(mono_etc_dir, 'mconfig'), os.path.join(target_mono_config_dir, 'mconfig'))
+    if os.path.isdir(os.path.join(mono_etc_dir, 'mconfig')):
+        copy_tree(os.path.join(mono_etc_dir, 'mconfig'), os.path.join(target_mono_config_dir, 'mconfig'))
 
     for file in glob(os.path.join(mono_etc_dir, '*')):
         if os.path.isfile(file):
             copy(file, target_mono_config_dir)
 
 
-def copy_mono_shared_libs(mono_root, target_mono_root_dir, platform):
+def copy_mono_shared_libs(env, mono_root, target_mono_root_dir):
     from shutil import copy
 
+    def copy_if_exists(src, dst):
+        if os.path.isfile(src):
+            copy(src, dst)
+
+    platform = env['platform']
+
     if platform == 'windows':
         target_mono_bin_dir = os.path.join(target_mono_root_dir, 'bin')
 
         if not os.path.isdir(target_mono_bin_dir):
             os.makedirs(target_mono_bin_dir)
 
-        copy(os.path.join(mono_root, 'bin', 'MonoPosixHelper.dll'), os.path.join(target_mono_bin_dir, 'MonoPosixHelper.dll'))
+        copy(os.path.join(mono_root, 'bin', 'MonoPosixHelper.dll'), target_mono_bin_dir)
     else:
-        target_mono_lib_dir = os.path.join(target_mono_root_dir, 'lib')
+        target_mono_lib_dir = get_android_out_dir(env) if platform == 'android' else os.path.join(target_mono_root_dir, 'lib')
 
         if not os.path.isdir(target_mono_lib_dir):
             os.makedirs(target_mono_lib_dir)
 
         if platform == 'osx':
-            copy(os.path.join(mono_root, 'lib', 'libMonoPosixHelper.dylib'), os.path.join(target_mono_lib_dir, 'libMonoPosixHelper.dylib'))
-        elif platform == 'x11':
-            copy(os.path.join(mono_root, 'lib', 'libmono-btls-shared.so'), os.path.join(target_mono_lib_dir, 'libmono-btls-shared.so'))
-            copy(os.path.join(mono_root, 'lib', 'libMonoPosixHelper.so'), os.path.join(target_mono_lib_dir, 'libMonoPosixHelper.so'))
+            # TODO: Make sure nothing is missing
+            copy(os.path.join(mono_root, 'lib', 'libMonoPosixHelper.dylib'), target_mono_lib_dir)
+        elif platform == 'x11' or platform == 'android':
+            lib_file_names = [lib_name + '.so' for lib_name in [
+                'libmono-btls-shared', 'libmono-ee-interp', 'libmono-native', 'libMonoPosixHelper',
+                'libmono-profiler-aot', 'libmono-profiler-coverage', 'libmono-profiler-log', 'libMonoSupportW'
+            ]]
+
+            for lib_file_name in lib_file_names:
+                copy_if_exists(os.path.join(mono_root, 'lib', lib_file_name), target_mono_lib_dir)
 
 
 def configure_for_mono_version(env, mono_version):
     if mono_version is None:
-        raise RuntimeError('Mono JIT compiler version not found')
+        if os.getenv('MONO_VERSION'):
+            mono_version = os.getenv('MONO_VERSION')
+        else:
+            raise RuntimeError("Mono JIT compiler version not found; specify one manually with the 'MONO_VERSION' environment variable")
     print('Found Mono JIT compiler version: ' + str(mono_version))
     if mono_version >= LooseVersion('5.12.0'):
         env.Append(CPPFLAGS=['-DHAS_PENDING_EXCEPTIONS'])

+ 13 - 0
modules/mono/build_scripts/patches/fix-mono-android-pthread_mutexattr_setprotocol.diff

@@ -0,0 +1,13 @@
+diff --git a/mono/utils/mono-os-mutex.h b/mono/utils/mono-os-mutex.h
+index e8039bf4094..ee39c0330b3 100644
+--- a/mono/utils/mono-os-mutex.h
++++ b/mono/utils/mono-os-mutex.h
+@@ -57,7 +57,7 @@ mono_os_mutex_init_type (mono_mutex_t *mutex, int type)
+ 	if (G_UNLIKELY (res != 0))
+ 		g_error ("%s: pthread_mutexattr_settype failed with \"%s\" (%d)", __func__, g_strerror (res), res);
+ 
+-#ifdef PTHREAD_PRIO_INHERIT
++#if defined(PTHREAD_PRIO_INHERIT) && __ANDROID_API__ >= 28
+ 	/* use PTHREAD_PRIO_INHERIT if possible */
+ 	res = pthread_mutexattr_setprotocol (&attr, PTHREAD_PRIO_INHERIT);
+ 	if (G_UNLIKELY (res != 0 && res != ENOTSUP))

+ 70 - 0
modules/mono/build_scripts/patches/fix-mono-android-tkill.diff

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

+ 0 - 1
modules/mono/csharp_script.h

@@ -380,7 +380,6 @@ public:
 	virtual bool supports_builtin_mode() const;
 	/* TODO? */ virtual int find_function(const String &p_function, const String &p_code) const { return -1; }
 	virtual String make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const;
-	/* TODO? */ Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<String> *r_options, String &r_call_hint) { return ERR_UNAVAILABLE; }
 	virtual String _get_indentation() const;
 	/* TODO? */ virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const {}
 	/* TODO */ virtual void add_global_constant(const StringName &p_variable, const Variant &p_value) {}

+ 2 - 1
modules/mono/editor/GodotSharpTools/Editor/GodotSharpExport.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.IO;
+using System.Linq;
 using System.Runtime.CompilerServices;
 
 namespace GodotSharpTools.Editor
@@ -62,7 +63,7 @@ namespace GodotSharpTools.Editor
         {
             // OSX export templates are contained in a zip, so we place
             // our custom template inside it and let Godot do the rest.
-            return !featureSet.Contains("OSX");
+            return !featureSet.Any(f => new[] {"OSX", "Android"}.Contains(f));
         }
 
         [MethodImpl(MethodImplOptions.InternalCall)]

+ 17 - 5
modules/mono/editor/godotsharp_export.cpp

@@ -125,11 +125,21 @@ void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug
 			bool load_success = GDMono::get_singleton()->load_assembly_from(project_dll_name,
 					project_dll_src_path, &scripts_assembly, /* refonly: */ true);
 
-			ERR_EXPLAIN("Cannot load refonly assembly: " + project_dll_name);
+			ERR_EXPLAIN("Cannot load assembly (refonly): " + project_dll_name);
 			ERR_FAIL_COND(!load_success);
 
 			Vector<String> search_dirs;
-			GDMonoAssembly::fill_search_dirs(search_dirs, build_config);
+			String templates_dir = EditorSettings::get_singleton()->get_templates_dir().plus_file(VERSION_FULL_CONFIG);
+			String android_bcl_dir = templates_dir.plus_file("android-bcl");
+
+			String custom_lib_dir;
+
+			if (p_features.find("Android") && DirAccess::exists(android_bcl_dir)) {
+				custom_lib_dir = android_bcl_dir;
+			}
+
+			GDMonoAssembly::fill_search_dirs(search_dirs, build_config, custom_lib_dir);
+
 			Error depend_error = _get_assembly_dependencies(scripts_assembly, search_dirs, dependencies);
 			ERR_FAIL_COND(depend_error != OK);
 		}
@@ -152,7 +162,7 @@ void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug
 	int i = 0;
 	for (const Set<String>::Element *E = p_features.front(); E; E = E->next()) {
 		MonoString *boxed = GDMonoMarshal::mono_string_from_godot(E->get());
-		mono_array_set(features, MonoString *, i, boxed);
+		mono_array_setref(features, i, boxed);
 		i++;
 	}
 
@@ -234,8 +244,10 @@ Error GodotSharpExport::_get_assembly_dependencies(GDMonoAssembly *p_assembly, c
 		r_dependencies.insert(ref_name, ref_assembly->get_path());
 
 		Error err = _get_assembly_dependencies(ref_assembly, p_search_dirs, r_dependencies);
-		if (err != OK)
-			return err;
+		if (err != OK) {
+			ERR_EXPLAIN("Cannot load one of the dependencies for the assembly: " + ref_name);
+			ERR_FAIL_V(err);
+		}
 	}
 
 	return OK;

+ 1 - 1
modules/mono/glue/base_object_glue.cpp

@@ -166,7 +166,7 @@ MonoArray *godot_icall_DynamicGodotObject_SetMemberList(Object *p_ptr) {
 	int i = 0;
 	for (List<PropertyInfo>::Element *E = property_list.front(); E; E = E->next()) {
 		MonoString *boxed = GDMonoMarshal::mono_string_from_godot(E->get().name);
-		mono_array_set(result, MonoString *, i, boxed);
+		mono_array_setref(result, i, boxed);
 		i++;
 	}
 

+ 8 - 5
modules/mono/mono_gd/gd_mono.cpp

@@ -251,17 +251,19 @@ void GDMono::initialize() {
 	String bundled_config_dir = GodotSharpDirs::get_data_mono_etc_dir();
 
 #ifdef TOOLS_ENABLED
-	if (DirAccess::exists(bundled_assembly_rootdir) && DirAccess::exists(bundled_config_dir)) {
+	if (DirAccess::exists(bundled_assembly_rootdir)) {
 		assembly_rootdir = bundled_assembly_rootdir;
+	}
+
+	if (DirAccess::exists(bundled_config_dir)) {
 		config_dir = bundled_config_dir;
 	}
 
 #ifdef WINDOWS_ENABLED
 	if (assembly_rootdir.empty() || config_dir.empty()) {
+		ERR_PRINT("Cannot find Mono in the registry");
 		// Assertion: if they are not set, then they weren't found in the registry
 		CRASH_COND(mono_reg_info.assembly_dir.length() > 0 || mono_reg_info.config_dir.length() > 0);
-
-		ERR_PRINT("Cannot find Mono in the registry");
 	}
 #endif // WINDOWS_ENABLED
 
@@ -807,6 +809,8 @@ Error GDMono::_unload_scripts_domain() {
 
 	mono_gc_collect(mono_gc_max_generation());
 
+	GDMonoUtils::clear_godot_api_cache();
+
 	_domain_assemblies_cleanup(mono_domain_get_id(scripts_domain));
 
 	core_api_assembly = NULL;
@@ -926,6 +930,7 @@ Error GDMono::reload_scripts_domain() {
 Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) {
 
 	CRASH_COND(p_domain == NULL);
+	CRASH_COND(p_domain == SCRIPTS_DOMAIN); // Should use _unload_scripts_domain() instead
 
 	String domain_name = mono_domain_get_friendly_name(p_domain);
 
@@ -1078,8 +1083,6 @@ GDMono::~GDMono() {
 		}
 		assemblies.clear();
 
-		GDMonoUtils::clear_cache();
-
 		print_verbose("Mono: Runtime cleanup...");
 
 		mono_jit_cleanup(root_domain);

+ 10 - 4
modules/mono/mono_gd/gd_mono_assembly.cpp

@@ -46,11 +46,17 @@ bool GDMonoAssembly::in_preload = false;
 
 Vector<String> GDMonoAssembly::search_dirs;
 
-void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config) {
+void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config, const String &p_custom_bcl_dir) {
 
-	const char *rootdir = mono_assembly_getrootdir();
-	if (rootdir) {
-		String framework_dir = String::utf8(rootdir).plus_file("mono").plus_file("4.5");
+	String framework_dir;
+
+	if (!p_custom_bcl_dir.empty()) {
+		framework_dir = p_custom_bcl_dir;
+	} else if (mono_assembly_getrootdir()) {
+		framework_dir = String::utf8(mono_assembly_getrootdir()).plus_file("mono").plus_file("4.5");
+	}
+
+	if (!framework_dir.empty()) {
 		r_search_dirs.push_back(framework_dir);
 		r_search_dirs.push_back(framework_dir.plus_file("Facades"));
 	}

+ 1 - 1
modules/mono/mono_gd/gd_mono_assembly.h

@@ -122,7 +122,7 @@ public:
 
 	GDMonoClass *get_object_derived_class(const StringName &p_class);
 
-	static void fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config = String());
+	static void fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config = String(), const String &p_custom_bcl_dir = String());
 
 	static GDMonoAssembly *load_from(const String &p_name, const String &p_path, bool p_refonly);
 

+ 1 - 1
modules/mono/mono_gd/gd_mono_marshal.cpp

@@ -1019,7 +1019,7 @@ MonoArray *PoolStringArray_to_mono_array(const PoolStringArray &p_array) {
 
 	for (int i = 0; i < p_array.size(); i++) {
 		MonoString *boxed = mono_string_from_godot(r[i]);
-		mono_array_set(ret, MonoString *, i, boxed);
+		mono_array_setref(ret, i, boxed);
 	}
 
 	return ret;

+ 1 - 1
modules/mono/mono_gd/gd_mono_method.cpp

@@ -109,7 +109,7 @@ MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params,
 
 		for (int i = 0; i < params_count; i++) {
 			MonoObject *boxed_param = GDMonoMarshal::variant_to_mono_object(p_params[i], param_types[i]);
-			mono_array_set(params, MonoObject *, i, boxed_param);
+			mono_array_setref(params, i, boxed_param);
 		}
 
 		MonoException *exc = NULL;

+ 1 - 1
modules/mono/mono_gd/gd_mono_property.cpp

@@ -142,7 +142,7 @@ bool GDMonoProperty::has_setter() {
 void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoException **r_exc) {
 	MonoMethod *prop_method = mono_property_get_set_method(mono_property);
 	MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), 1);
-	mono_array_set(params, MonoObject *, 0, p_value);
+	mono_array_setref(params, 0, p_value);
 	MonoException *exc = NULL;
 	GDMonoUtils::runtime_invoke_array(prop_method, p_object, params, &exc);
 	if (exc) {

+ 10 - 16
modules/mono/mono_gd/gd_mono_utils.cpp

@@ -50,6 +50,7 @@ MonoCache mono_cache;
 
 #define CACHE_AND_CHECK(m_var, m_val)                             \
 	{                                                             \
+		CRASH_COND(m_var != NULL);                                \
 		m_var = m_val;                                            \
 		if (!m_var) {                                             \
 			ERR_EXPLAIN("Mono Cache: Member " #m_var " is null"); \
@@ -65,7 +66,9 @@ MonoCache mono_cache;
 #define CACHE_METHOD_THUNK_AND_CHECK(m_class, m_method, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.methodthunk_##m_class##_##m_method, m_val)
 #define CACHE_PROPERTY_AND_CHECK(m_class, m_property, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.property_##m_class##_##m_property, m_val)
 
-void MonoCache::clear_members() {
+void MonoCache::clear_corlib_cache() {
+
+	corlib_cache_updated = false;
 
 	class_MonoObject = NULL;
 	class_bool = NULL;
@@ -93,6 +96,11 @@ void MonoCache::clear_members() {
 #endif
 
 	class_KeyNotFoundException = NULL;
+}
+
+void MonoCache::clear_godot_api_cache() {
+
+	godot_api_cache_updated = false;
 
 	rawclass_Dictionary = NULL;
 
@@ -176,12 +184,6 @@ void MonoCache::clear_members() {
 	task_scheduler_handle = Ref<MonoGCHandle>();
 }
 
-void MonoCache::cleanup() {
-
-	corlib_cache_updated = false;
-	godot_api_cache_updated = false;
-}
-
 #define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class))
 #define GODOT_API_NS_CLAS(m_ns, m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(m_ns, #m_class))
 
@@ -281,13 +283,10 @@ void update_godot_api_cache() {
 	CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArrayGetElementType, (ArrayGetElementType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("ArrayGetElementType", 2));
 	CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryGetKeyValueTypes, (DictionaryGetKeyValueTypes)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("DictionaryGetKeyValueTypes", 3));
 
-	CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArrayGetElementType, (ArrayGetElementType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("ArrayGetElementType", 2));
-	CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryGetKeyValueTypes, (DictionaryGetKeyValueTypes)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("DictionaryGetKeyValueTypes", 3));
-
 	CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIEnumerableIsAssignableFromType, (GenericIEnumerableIsAssignableFromType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIEnumerableIsAssignableFromType", 1));
 	CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryIsAssignableFromType, (GenericIDictionaryIsAssignableFromType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIDictionaryIsAssignableFromType", 1));
 	CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIEnumerableIsAssignableFromType_with_info, (GenericIEnumerableIsAssignableFromType_with_info)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIEnumerableIsAssignableFromType", 2));
-	CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIEnumerableIsAssignableFromType_with_info, (GenericIEnumerableIsAssignableFromType_with_info)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIDictionaryIsAssignableFromType", 3));
+	CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryIsAssignableFromType_with_info, (GenericIDictionaryIsAssignableFromType_with_info)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIDictionaryIsAssignableFromType", 3));
 
 	CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericArrayType, (MakeGenericArrayType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("MakeGenericArrayType", 1));
 	CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericDictionaryType, (MakeGenericDictionaryType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("MakeGenericDictionaryType", 2));
@@ -310,11 +309,6 @@ void update_godot_api_cache() {
 	mono_cache.godot_api_cache_updated = true;
 }
 
-void clear_cache() {
-	mono_cache.cleanup();
-	mono_cache.clear_members();
-}
-
 MonoObject *unmanaged_get_managed(Object *unmanaged) {
 
 	if (!unmanaged)

+ 11 - 7
modules/mono/mono_gd/gd_mono_utils.h

@@ -218,14 +218,12 @@ struct MonoCache {
 	bool corlib_cache_updated;
 	bool godot_api_cache_updated;
 
-	void clear_members();
-	void cleanup();
+	void clear_corlib_cache();
+	void clear_godot_api_cache();
 
 	MonoCache() {
-		corlib_cache_updated = false;
-		godot_api_cache_updated = false;
-
-		clear_members();
+		clear_corlib_cache();
+		clear_godot_api_cache();
 	}
 };
 
@@ -233,7 +231,13 @@ extern MonoCache mono_cache;
 
 void update_corlib_cache();
 void update_godot_api_cache();
-void clear_cache();
+
+inline void clear_corlib_cache() {
+	mono_cache.clear_corlib_cache();
+}
+inline void clear_godot_api_cache() {
+	mono_cache.clear_godot_api_cache();
+}
 
 _FORCE_INLINE_ void hash_combine(uint32_t &p_hash, const uint32_t &p_with_hash) {
 	p_hash ^= p_with_hash + 0x9e3779b9 + (p_hash << 6) + (p_hash >> 2);

+ 1 - 1
modules/mono/signal_awaiter_utils.cpp

@@ -95,7 +95,7 @@ Variant SignalAwaiterHandle::_signal_callback(const Variant **p_args, int p_argc
 
 	for (int i = 0; i < signal_argc; i++) {
 		MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(*p_args[i]);
-		mono_array_set(signal_args, MonoObject *, i, boxed);
+		mono_array_setref(signal_args, i, boxed);
 	}
 
 	MonoException *exc = NULL;