Преглед на файлове

Merge pull request #22688 from neikeq/lotsofgoodies

Mono: Editor and export template dependencies and fixes
Rémi Verschelde преди 7 години
родител
ревизия
aa85b7ed62

+ 10 - 13
modules/mono/SCsub

@@ -244,16 +244,13 @@ def mono_build_solution(source, target, env):
 
     copyfile(os.path.join(src_dir, asm_file), os.path.join(dst_dir, asm_file))
 
-output_dir = Dir('#bin').abspath
-assemblies_output_dir = Dir(env['mono_assemblies_output_dir']).abspath
-
-mono_sln_builder = Builder(action=mono_build_solution)
-env_mono.Append(BUILDERS={'MonoBuildSolution': mono_sln_builder})
-env_mono.MonoBuildSolution(
-    os.path.join(assemblies_output_dir, 'GodotSharpTools.dll'),
-    'editor/GodotSharpTools/GodotSharpTools.sln'
-)
-
-if os.path.normpath(output_dir) != os.path.normpath(assemblies_output_dir):
-    rel_assemblies_output_dir = os.path.relpath(assemblies_output_dir, output_dir)
-    env_mono.Append(CPPDEFINES={'GD_MONO_EDITOR_ASSEMBLIES_DIR': rel_assemblies_output_dir})
+if env['tools']:
+    output_dir = Dir('#bin').abspath
+    editor_tools_dir = os.path.join(output_dir, 'GodotSharp', 'Tools')
+
+    mono_sln_builder = Builder(action=mono_build_solution)
+    env_mono.Append(BUILDERS={'MonoBuildSolution': mono_sln_builder})
+    env_mono.MonoBuildSolution(
+        os.path.join(editor_tools_dir, 'GodotSharpTools.dll'),
+        'editor/GodotSharpTools/GodotSharpTools.sln'
+    )

+ 155 - 25
modules/mono/config.py

@@ -5,7 +5,7 @@ import sys
 import subprocess
 
 from distutils.version import LooseVersion
-from SCons.Script import BoolVariable, Dir, Environment, File, PathVariable, SCons, Variables
+from SCons.Script import BoolVariable, Dir, Environment, File, SCons, Variables
 
 
 monoreg = imp.load_source('mono_reg_utils', 'modules/mono/mono_reg_utils.py')
@@ -55,30 +55,20 @@ def copy_file(src_dir, dst_dir, name):
     copyfile(src_path, dst_path)
 
 
-def custom_path_is_dir_create(key, val, env):
-    """Validator to check if Path is a directory, creating it if it does not exist.
-       Similar to PathIsDirCreate, except it uses SCons.Script.Dir() and
-       SCons.Script.File() in order to support the '#' top level directory token.
-       """
-    # Dir constructor will throw an error if the path points to a file
-    fsDir = Dir(val)
-    if not fsDir.exists:
-        os.makedirs(fsDir.abspath)
-
-
 def configure(env):
     env.use_ptrcall = True
     env.add_module_version_string('mono')
 
     envvars = Variables()
     envvars.Add(BoolVariable('mono_static', 'Statically link mono', False))
-    envvars.Add(PathVariable('mono_assemblies_output_dir', 'Path to the assemblies output directory', '#bin', custom_path_is_dir_create))
+    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']
 
+    tools_enabled = env['tools']
     mono_static = env['mono_static']
-    assemblies_output_dir = Dir(env['mono_assemblies_output_dir']).abspath
+    copy_mono_root = env['copy_mono_root']
 
     mono_lib_names = ['mono-2.0-sgen', 'monosgen-2.0']
 
@@ -151,8 +141,6 @@ def configure(env):
                 raise RuntimeError('Could not find mono shared library in: ' + mono_bin_path)
 
             copy_file(mono_bin_path, 'bin', mono_dll_name + '.dll')
-
-        copy_file(os.path.join(mono_lib_path, 'mono', '4.5'), assemblies_output_dir, 'mscorlib.dll')
     else:
         sharedlib_ext = '.dylib' if sys.platform == 'darwin' else '.so'
 
@@ -204,16 +192,14 @@ def configure(env):
 
                 if sys.platform == 'darwin':
                     env.Append(LINKFLAGS=['-Wl,-force_load,' + mono_lib_file])
-                elif sys.platform == 'linux' or sys.platform == 'linux2':
-                    env.Append(LINKFLAGS=['-Wl,-whole-archive', mono_lib_file, '-Wl,-no-whole-archive'])
                 else:
-                    raise RuntimeError('mono-static: Not supported on this platform')
+                    env.Append(LINKFLAGS=['-Wl,-whole-archive', mono_lib_file, '-Wl,-no-whole-archive'])
             else:
                 env.Append(LIBS=[mono_lib])
 
             if sys.platform == 'darwin':
                 env.Append(LIBS=['iconv', 'pthread'])
-            elif sys.platform == 'linux' or sys.platform == 'linux2':
+            else:
                 env.Append(LIBS=['m', 'rt', 'dl', 'pthread'])
 
             if not mono_static:
@@ -223,8 +209,6 @@ def configure(env):
                     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(os.path.join(mono_lib_path, 'mono', '4.5'), assemblies_output_dir, 'mscorlib.dll')
         else:
             assert not mono_static
 
@@ -238,7 +222,6 @@ def configure(env):
 
             mono_lib_path = ''
             mono_so_name = ''
-            mono_prefix = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip()
 
             tmpenv = Environment()
             tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH'))
@@ -255,16 +238,163 @@ def configure(env):
                 raise RuntimeError('Could not find mono shared library in: ' + str(tmpenv['LIBPATH']))
 
             copy_file(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext)
-            copy_file(os.path.join(mono_prefix, 'lib', 'mono', '4.5'), assemblies_output_dir, 'mscorlib.dll')
 
         env.Append(LINKFLAGS='-rdynamic')
 
+    if not tools_enabled:
+        if not mono_root:
+            mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip()
+
+        make_template_dir(env, mono_root)
+
+    if copy_mono_root:
+        if not mono_root:
+            mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip()
+
+        if tools_enabled:
+           copy_mono_root_files(env, mono_root)
+        else:
+            print("Ignoring option: 'copy_mono_root'. Only available for builds with 'tools' enabled.")
+
+
+def make_template_dir(env, mono_root):
+    from shutil import rmtree
+
+    platform = env['platform']
+    target = env['target']
+
+    template_dir_name = ''
+
+    if platform == 'windows':
+        template_dir_name = 'data.mono.%s.%s.%s' % (platform, env['bits'], target)
+    elif platform == 'osx':
+        template_dir_name = 'data.mono.%s.%s' % (platform, target)
+    elif platform == 'x11':
+        template_dir_name = 'data.mono.%s.%s.%s' % (platform, env['bits'], target)
+    else:
+        assert False
+
+    output_dir = Dir('#bin').abspath
+    template_dir = os.path.join(output_dir, template_dir_name)
+
+    template_mono_root_dir = os.path.join(template_dir, 'Mono')
+
+    if os.path.isdir(template_mono_root_dir):
+        rmtree(template_mono_root_dir) # Clean first
+
+    # 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'])
+
+    # Copy the required shared libraries
+
+    copy_mono_shared_libs(mono_root, template_mono_root_dir, env['platform'])
+
+
+def copy_mono_root_files(env, mono_root):
+    from glob import glob
+    from shutil import copy
+    from shutil import rmtree
+
+    if not mono_root:
+        raise RuntimeError('Mono installation directory not found')
+
+    output_dir = Dir('#bin').abspath
+    editor_mono_root_dir = os.path.join(output_dir, 'GodotSharp', 'Mono')
+
+    if os.path.isdir(editor_mono_root_dir):
+        rmtree(editor_mono_root_dir) # Clean first
+
+    # Copy etc/mono/
+
+    editor_mono_config_dir = os.path.join(editor_mono_root_dir, 'etc', 'mono')
+    copy_mono_etc_dir(mono_root, editor_mono_config_dir, env['platform'])
+
+    # Copy the required shared libraries
+
+    copy_mono_shared_libs(mono_root, editor_mono_root_dir, env['platform'])
+
+    # Copy framework assemblies
+
+    mono_framework_dir = os.path.join(mono_root, 'lib', 'mono', '4.5')
+    mono_framework_facades_dir = os.path.join(mono_framework_dir, 'Facades')
+
+    editor_mono_framework_dir = os.path.join(editor_mono_root_dir, 'lib', 'mono', '4.5')
+    editor_mono_framework_facades_dir = os.path.join(editor_mono_framework_dir, 'Facades')
+
+    if not os.path.isdir(editor_mono_framework_dir):
+        os.makedirs(editor_mono_framework_dir)
+    if not os.path.isdir(editor_mono_framework_facades_dir):
+        os.makedirs(editor_mono_framework_facades_dir)
+
+    for assembly in glob(os.path.join(mono_framework_dir, '*.dll')):
+        copy(assembly, editor_mono_framework_dir)
+    for assembly in glob(os.path.join(mono_framework_facades_dir, '*.dll')):
+        copy(assembly, editor_mono_framework_facades_dir)
+
+
+def copy_mono_etc_dir(mono_root, target_mono_config_dir, platform):
+    from distutils.dir_util import copy_tree
+    from glob import glob
+    from shutil import copy
+
+    if not os.path.isdir(target_mono_config_dir):
+        os.makedirs(target_mono_config_dir)
+
+    mono_etc_dir = os.path.join(mono_root, 'etc', 'mono')
+    if not os.path.isdir(mono_etc_dir):
+        mono_etc_dir = ''
+        etc_hint_dirs = []
+        if platform != 'windows':
+            etc_hint_dirs += ['/etc/mono', '/usr/local/etc/mono']
+        if 'MONO_CFG_DIR' in os.environ:
+            etc_hint_dirs += [os.path.join(os.environ['MONO_CFG_DIR'], 'mono')]
+        for etc_hint_dir in etc_hint_dirs:
+            if os.path.isdir(etc_hint_dir):
+                mono_etc_dir = etc_hint_dir
+                break
+        if not mono_etc_dir:
+            raise RuntimeError('Mono installation etc directory not found')
+
+    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'))
+
+    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):
+    from shutil import copy
+
+    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'))
+    else:
+        target_mono_lib_dir = 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'))
+
 
 def configure_for_mono_version(env, mono_version):
     if mono_version is None:
         raise RuntimeError('Mono JIT compiler version not found')
     print('Found Mono JIT compiler version: ' + str(mono_version))
-    if mono_version >= LooseVersion("5.12.0"):
+    if mono_version >= LooseVersion('5.12.0'):
         env.Append(CPPFLAGS=['-DHAS_PENDING_EXCEPTIONS'])
 
 

+ 72 - 0
modules/mono/editor/GodotSharpTools/Editor/GodotSharpExport.cs

@@ -0,0 +1,72 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.CompilerServices;
+
+namespace GodotSharpTools.Editor
+{
+    public static class GodotSharpExport
+    {
+        public static void _ExportBegin(string[] features, bool debug, string path, int flags)
+        {
+            var featureSet = new HashSet<string>(features);
+
+            if (PlatformHasTemplateDir(featureSet))
+            {
+                string templateDirName = "data.mono";
+
+                if (featureSet.Contains("Windows"))
+                {
+                    templateDirName += ".windows";
+                    templateDirName += featureSet.Contains("64") ? ".64" : ".32";
+                }
+                else if (featureSet.Contains("X11"))
+                {
+                    templateDirName += ".x11";
+                    templateDirName += featureSet.Contains("64") ? ".64" : ".32";
+                }
+                else
+                {
+                    throw new NotSupportedException("Target platform not supported");
+                }
+
+                templateDirName += debug ? ".debug" : ".release";
+
+                string templateDirPath = Path.Combine(GetTemplatesDir(), templateDirName);
+
+                if (!Directory.Exists(templateDirPath))
+                    throw new FileNotFoundException("Data template directory not found");
+
+                string outputDir = new FileInfo(path).Directory.FullName;
+
+                string outputDataDir = Path.Combine(outputDir, GetDataDirName());
+
+                Directory.Delete(outputDataDir, recursive: true); // Clean first
+                Directory.CreateDirectory(outputDataDir);
+                
+                foreach (string dir in Directory.GetDirectories(templateDirPath, "*", SearchOption.AllDirectories))
+                {
+                    Directory.CreateDirectory(Path.Combine(outputDataDir, dir.Substring(templateDirPath.Length + 1)));
+                }
+
+                foreach (string file in Directory.GetFiles(templateDirPath, "*", SearchOption.AllDirectories))
+                {
+                    File.Copy(file, Path.Combine(outputDataDir, file.Substring(templateDirPath.Length + 1)));
+                }
+            }
+        }
+
+        public static bool PlatformHasTemplateDir(HashSet<string> featureSet)
+        {
+            // 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");
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        extern static string GetTemplatesDir();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        extern static string GetDataDirName();
+    }
+}

+ 1 - 0
modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj

@@ -41,6 +41,7 @@
     <Compile Include="Project\ProjectUtils.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Utils\OS.cs" />
+    <Compile Include="Editor\GodotSharpExport.cs" />
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
 </Project>

+ 4 - 4
modules/mono/editor/csharp_project.cpp

@@ -51,7 +51,7 @@ String generate_core_api_project(const String &p_dir, const Vector<String> &p_fi
 	MonoObject *ret = klass->get_method("GenCoreApiProject", 2)->invoke(NULL, args, &exc);
 
 	if (exc) {
-		GDMonoUtils::debug_unhandled_exception(exc);
+		GDMonoUtils::debug_print_unhandled_exception(exc);
 		ERR_FAIL_V(String());
 	}
 
@@ -72,7 +72,7 @@ String generate_editor_api_project(const String &p_dir, const String &p_core_dll
 	MonoObject *ret = klass->get_method("GenEditorApiProject", 3)->invoke(NULL, args, &exc);
 
 	if (exc) {
-		GDMonoUtils::debug_unhandled_exception(exc);
+		GDMonoUtils::debug_print_unhandled_exception(exc);
 		ERR_FAIL_V(String());
 	}
 
@@ -93,7 +93,7 @@ String generate_game_project(const String &p_dir, const String &p_name, const Ve
 	MonoObject *ret = klass->get_method("GenGameProject", 3)->invoke(NULL, args, &exc);
 
 	if (exc) {
-		GDMonoUtils::debug_unhandled_exception(exc);
+		GDMonoUtils::debug_print_unhandled_exception(exc);
 		ERR_FAIL_V(String());
 	}
 
@@ -114,7 +114,7 @@ void add_item(const String &p_project_path, const String &p_item_type, const Str
 	klass->get_method("AddItemToProjectChecked", 3)->invoke(NULL, args, &exc);
 
 	if (exc) {
-		GDMonoUtils::debug_unhandled_exception(exc);
+		GDMonoUtils::debug_print_unhandled_exception(exc);
 		ERR_FAIL();
 	}
 }

+ 47 - 28
modules/mono/editor/godotsharp_builds.cpp

@@ -249,6 +249,18 @@ bool GodotSharpBuilds::build_api_sln(const String &p_name, const String &p_api_s
 
 bool GodotSharpBuilds::copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name, APIAssembly::Type p_api_type) {
 
+	// Create destination directory if needed
+	if (!DirAccess::exists(p_dst_dir)) {
+		DirAccess *da = DirAccess::create_for_path(p_dst_dir);
+		Error err = da->make_dir_recursive(p_dst_dir);
+		memdelete(da);
+
+		if (err != OK) {
+			show_build_error_dialog("Failed to create destination directory for the API assemblies. Error: " + itos(err));
+			return false;
+		}
+	}
+
 	String assembly_file = p_assembly_name + ".dll";
 	String assembly_src = p_src_dir.plus_file(assembly_file);
 	String assembly_dst = p_dst_dir.plus_file(assembly_file);
@@ -296,9 +308,9 @@ bool GodotSharpBuilds::make_api_sln(APIAssembly::Type p_api_type) {
 	String api_name = p_api_type == APIAssembly::API_CORE ? API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME;
 	String api_build_config = "Release";
 
-	EditorProgress pr("mono_build_release_" + api_name, "Building " + api_name + " solution...", 4);
+	EditorProgress pr("mono_build_release_" + api_name, "Building " + api_name + " solution...", 3);
 
-	pr.step("Generating " + api_name + " solution");
+	pr.step("Generating " + api_name + " solution", 0);
 
 	String core_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir()
 									  .plus_file(_api_folder_name(APIAssembly::API_CORE))
@@ -336,34 +348,19 @@ bool GodotSharpBuilds::make_api_sln(APIAssembly::Type p_api_type) {
 		}
 	}
 
-	pr.step("Building " + api_name + " solution");
+	pr.step("Building " + api_name + " solution", 1);
 
 	if (!GodotSharpBuilds::build_api_sln(api_name, api_sln_dir, api_build_config))
 		return false;
 
-	pr.step("Copying " + api_name + " assembly");
-
-	String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir();
-
-	// Create assemblies directory if needed
-	if (!DirAccess::exists(res_assemblies_dir)) {
-		DirAccess *da = DirAccess::create_for_path(res_assemblies_dir);
-		Error err = da->make_dir_recursive(res_assemblies_dir);
-		memdelete(da);
-
-		if (err != OK) {
-			show_build_error_dialog("Failed to create assemblies directory. Error: " + itos(err));
-			return false;
-		}
-	}
+	pr.step("Copying " + api_name + " assembly", 2);
 
 	// Copy the built assembly to the assemblies directory
 	String api_assembly_dir = api_sln_dir.plus_file("bin").plus_file(api_build_config);
+	String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir();
 	if (!GodotSharpBuilds::copy_api_assembly(api_assembly_dir, res_assemblies_dir, api_name, p_api_type))
 		return false;
 
-	pr.step("Done");
-
 	return true;
 }
 
@@ -372,15 +369,39 @@ bool GodotSharpBuilds::build_project_blocking(const String &p_config) {
 	if (!FileAccess::exists(GodotSharpDirs::get_project_sln_path()))
 		return true; // No solution to build
 
-	if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_CORE))
-		return false;
+	String editor_prebuilt_api_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir();
+	String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir();
 
-	if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_EDITOR))
-		return false;
+	if (FileAccess::exists(editor_prebuilt_api_dir.plus_file(API_ASSEMBLY_NAME ".dll"))) {
+		EditorProgress pr("mono_copy_prebuilt_api_assemblies",
+				"Copying prebuilt " API_ASSEMBLY_NAME " assemblies...", 1);
+		pr.step("Copying " API_ASSEMBLY_NAME " assembly", 0);
 
-	EditorProgress pr("mono_project_debug_build", "Building project solution...", 2);
+		if (!GodotSharpBuilds::copy_api_assembly(editor_prebuilt_api_dir, res_assemblies_dir,
+					API_ASSEMBLY_NAME, APIAssembly::API_CORE)) {
+			return false;
+		}
+	} else {
+		if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_CORE))
+			return false;
+	}
 
-	pr.step("Building project solution");
+	if (DirAccess::exists(editor_prebuilt_api_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll"))) {
+		EditorProgress pr("mono_copy_prebuilt_api_assemblies",
+				"Copying prebuilt " EDITOR_API_ASSEMBLY_NAME " assemblies...", 1);
+		pr.step("Copying " EDITOR_API_ASSEMBLY_NAME " assembly", 0);
+
+		if (!GodotSharpBuilds::copy_api_assembly(editor_prebuilt_api_dir, res_assemblies_dir,
+					EDITOR_API_ASSEMBLY_NAME, APIAssembly::API_EDITOR)) {
+			return false;
+		}
+	} else {
+		if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_EDITOR))
+			return false;
+	}
+
+	EditorProgress pr("mono_project_debug_build", "Building project solution...", 1);
+	pr.step("Building project solution", 0);
 
 	MonoBuildInfo build_info(GodotSharpDirs::get_project_sln_path(), p_config);
 	if (!GodotSharpBuilds::get_singleton()->build(build_info)) {
@@ -388,8 +409,6 @@ bool GodotSharpBuilds::build_project_blocking(const String &p_config) {
 		return false;
 	}
 
-	pr.step("Done");
-
 	return true;
 }
 

+ 1 - 0
modules/mono/editor/godotsharp_editor.cpp

@@ -197,6 +197,7 @@ void GodotSharpEditor::register_internal_calls() {
 	mono_add_internal_call("GodotSharpTools.Utils.OS::GetPlatformName", (void *)godot_icall_Utils_OS_GetPlatformName);
 
 	GodotSharpBuilds::register_internal_calls();
+	GodotSharpExport::register_internal_calls();
 }
 
 void GodotSharpEditor::show_error_dialog(const String &p_message, const String &p_title) {

+ 110 - 36
modules/mono/editor/godotsharp_export.cpp

@@ -30,12 +30,37 @@
 
 #include "godotsharp_export.h"
 
+#include "core/version.h"
+
 #include "../csharp_script.h"
 #include "../godotsharp_defs.h"
 #include "../godotsharp_dirs.h"
+#include "../mono_gd/gd_mono_class.h"
+#include "../mono_gd/gd_mono_marshal.h"
 #include "godotsharp_builds.h"
 
-void GodotSharpExport::_export_file(const String &p_path, const String &p_type, const Set<String> &p_features) {
+static MonoString *godot_icall_GodotSharpExport_GetTemplatesDir() {
+	String current_version = VERSION_FULL_CONFIG;
+	String templates_dir = EditorSettings::get_singleton()->get_templates_dir().plus_file(current_version);
+	return GDMonoMarshal::mono_string_from_godot(ProjectSettings::get_singleton()->globalize_path(templates_dir));
+}
+
+static MonoString *godot_icall_GodotSharpExport_GetDataDirName() {
+	String appname = ProjectSettings::get_singleton()->get("application/config/name");
+	String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
+	return GDMonoMarshal::mono_string_from_godot("data_" + appname_safe);
+}
+
+void GodotSharpExport::register_internal_calls() {
+	static bool registered = false;
+	ERR_FAIL_COND(registered);
+	registered = true;
+
+	mono_add_internal_call("GodotSharpTools.Editor.GodotSharpExport::GetTemplatesDir", (void *)godot_icall_GodotSharpExport_GetTemplatesDir);
+	mono_add_internal_call("GodotSharpTools.Editor.GodotSharpExport::GetDataDirName", (void *)godot_icall_GodotSharpExport_GetDataDirName);
+}
+
+void GodotSharpExport::_export_file(const String &p_path, const String &p_type, const Set<String> &) {
 
 	if (p_type != CSharpLanguage::get_singleton()->get_type())
 		return;
@@ -56,59 +81,78 @@ void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug
 	// TODO right now there is no way to stop the export process with an error
 
 	ERR_FAIL_COND(!GDMono::get_singleton()->is_runtime_initialized());
-	ERR_FAIL_NULL(GDMono::get_singleton()->get_tools_domain());
+	ERR_FAIL_NULL(TOOLS_DOMAIN);
+	ERR_FAIL_NULL(GDMono::get_singleton()->get_editor_tools_assembly());
 
 	String build_config = p_debug ? "Debug" : "Release";
 
 	ERR_FAIL_COND(!GodotSharpBuilds::build_project_blocking(build_config));
 
-	// Add API assemblies
-
-	String core_api_dll_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(API_ASSEMBLY_NAME ".dll");
-	ERR_FAIL_COND(!_add_assembly(core_api_dll_path, core_api_dll_path));
-
-	String editor_api_dll_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
-	ERR_FAIL_COND(!_add_assembly(editor_api_dll_path, editor_api_dll_path));
+	// Add dependency assemblies
 
-	// Add project assembly
+	Map<String, String> dependencies;
 
 	String project_dll_name = ProjectSettings::get_singleton()->get("application/config/name");
 	if (project_dll_name.empty()) {
 		project_dll_name = "UnnamedProject";
 	}
 
-	String project_dll_src_path = GodotSharpDirs::get_res_temp_assemblies_base_dir().plus_file(build_config).plus_file(project_dll_name + ".dll");
-	String project_dll_dst_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(project_dll_name + ".dll");
-	ERR_FAIL_COND(!_add_assembly(project_dll_src_path, project_dll_dst_path));
-
-	// Add dependencies
-
-	MonoDomain *prev_domain = mono_domain_get();
-	MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.ProjectExportDomain");
+	String project_dll_src_dir = GodotSharpDirs::get_res_temp_assemblies_base_dir().plus_file(build_config);
+	String project_dll_src_path = project_dll_src_dir.plus_file(project_dll_name + ".dll");
+	dependencies.insert(project_dll_name, project_dll_src_path);
 
-	ERR_FAIL_COND(!export_domain);
-	ERR_FAIL_COND(!mono_domain_set(export_domain, false));
+	{
+		MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.ProjectExportDomain");
+		ERR_FAIL_NULL(export_domain);
+		_GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(export_domain);
 
-	Map<String, String> dependencies;
-	dependencies.insert("mscorlib", GDMono::get_singleton()->get_corlib_assembly()->get_path());
-
-	GDMonoAssembly *scripts_assembly = GDMonoAssembly::load_from(project_dll_name, project_dll_src_path, /* refonly: */ true);
+		_GDMONO_SCOPE_DOMAIN_(export_domain);
 
-	ERR_EXPLAIN("Cannot load refonly assembly: " + project_dll_name);
-	ERR_FAIL_COND(!scripts_assembly);
+		GDMonoAssembly *scripts_assembly = NULL;
+		bool load_success = GDMono::get_singleton()->load_assembly_from(project_dll_name,
+				project_dll_src_dir, &scripts_assembly, /* refonly: */ true);
 
-	Error depend_error = _get_assembly_dependencies(scripts_assembly, dependencies);
+		ERR_EXPLAIN("Cannot load refonly assembly: " + project_dll_name);
+		ERR_FAIL_COND(!load_success);
 
-	GDMono::get_singleton()->finalize_and_unload_domain(export_domain);
-	mono_domain_set(prev_domain, false);
-
-	ERR_FAIL_COND(depend_error != OK);
+		Vector<String> search_dirs;
+		GDMonoAssembly::fill_search_dirs(search_dirs);
+		Error depend_error = _get_assembly_dependencies(scripts_assembly, search_dirs, dependencies);
+		ERR_FAIL_COND(depend_error != OK);
+	}
 
 	for (Map<String, String>::Element *E = dependencies.front(); E; E = E->next()) {
 		String depend_src_path = E->value();
 		String depend_dst_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(depend_src_path.get_file());
 		ERR_FAIL_COND(!_add_assembly(depend_src_path, depend_dst_path));
 	}
+
+	// Mono specific export template extras (data dir)
+
+	GDMonoClass *export_class = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Editor", "GodotSharpExport");
+	ERR_FAIL_NULL(export_class);
+	GDMonoMethod *export_begin_method = export_class->get_method("_ExportBegin", 4);
+	ERR_FAIL_NULL(export_begin_method);
+
+	MonoArray *features = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(String), p_features.size());
+	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);
+		i++;
+	}
+
+	MonoBoolean debug = p_debug;
+	MonoString *path = GDMonoMarshal::mono_string_from_godot(p_path);
+	uint32_t flags = p_flags;
+	void *args[4] = { features, &debug, path, &flags };
+	MonoException *exc = NULL;
+	export_begin_method->invoke_raw(NULL, args, &exc);
+
+	if (exc) {
+		GDMonoUtils::debug_print_unhandled_exception(exc);
+		ERR_FAIL();
+	}
 }
 
 bool GodotSharpExport::_add_assembly(const String &p_src_path, const String &p_dst_path) {
@@ -125,7 +169,7 @@ bool GodotSharpExport::_add_assembly(const String &p_src_path, const String &p_d
 	return true;
 }
 
-Error GodotSharpExport::_get_assembly_dependencies(GDMonoAssembly *p_assembly, Map<String, String> &r_dependencies) {
+Error GodotSharpExport::_get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Map<String, String> &r_dependencies) {
 
 	MonoImage *image = p_assembly->get_image();
 
@@ -134,18 +178,48 @@ Error GodotSharpExport::_get_assembly_dependencies(GDMonoAssembly *p_assembly, M
 		mono_assembly_get_assemblyref(image, i, ref_aname);
 		String ref_name = mono_assembly_name_get_name(ref_aname);
 
-		if (ref_name == "mscorlib" || r_dependencies.find(ref_name))
+		if (r_dependencies.find(ref_name))
 			continue;
 
 		GDMonoAssembly *ref_assembly = NULL;
-		if (!GDMono::get_singleton()->load_assembly(ref_name, ref_aname, &ref_assembly, /* refonly: */ true)) {
-			ERR_EXPLAIN("Cannot load refonly assembly: " + ref_name);
+		String path;
+		bool has_extension = ref_name.ends_with(".dll") || ref_name.ends_with(".exe");
+
+		for (int i = 0; i < p_search_dirs.size(); i++) {
+			const String &search_dir = p_search_dirs[i];
+
+			if (has_extension) {
+				path = search_dir.plus_file(ref_name);
+				if (FileAccess::exists(path)) {
+					GDMono::get_singleton()->load_assembly_from(ref_name.get_basename(), search_dir, ref_aname, &ref_assembly, true);
+					if (ref_assembly != NULL)
+						break;
+				}
+			} else {
+				path = search_dir.plus_file(ref_name + ".dll");
+				if (FileAccess::exists(path)) {
+					GDMono::get_singleton()->load_assembly_from(ref_name, search_dir, ref_aname, &ref_assembly, true);
+					if (ref_assembly != NULL)
+						break;
+				}
+
+				path = search_dir.plus_file(ref_name + ".exe");
+				if (FileAccess::exists(path)) {
+					GDMono::get_singleton()->load_assembly_from(ref_name, search_dir, ref_aname, &ref_assembly, true);
+					if (ref_assembly != NULL)
+						break;
+				}
+			}
+		}
+
+		if (!ref_assembly) {
+			ERR_EXPLAIN("Cannot load assembly (refonly): " + ref_name);
 			ERR_FAIL_V(ERR_CANT_RESOLVE);
 		}
 
 		r_dependencies.insert(ref_name, ref_assembly->get_path());
 
-		Error err = _get_assembly_dependencies(ref_assembly, r_dependencies);
+		Error err = _get_assembly_dependencies(ref_assembly, p_search_dirs, r_dependencies);
 		if (err != OK)
 			return err;
 	}

+ 3 - 1
modules/mono/editor/godotsharp_export.h

@@ -43,13 +43,15 @@ class GodotSharpExport : public EditorExportPlugin {
 
 	bool _add_assembly(const String &p_src_path, const String &p_dst_path);
 
-	Error _get_assembly_dependencies(GDMonoAssembly *p_assembly, Map<String, String> &r_dependencies);
+	Error _get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Map<String, String> &r_dependencies);
 
 protected:
 	virtual void _export_file(const String &p_path, const String &p_type, const Set<String> &p_features);
 	virtual void _export_begin(const Set<String> &p_features, bool p_debug, const String &p_path, int p_flags);
 
 public:
+	static void register_internal_calls();
+
 	GodotSharpExport();
 	~GodotSharpExport();
 };

+ 84 - 5
modules/mono/godotsharp_dirs.cpp

@@ -30,11 +30,11 @@
 
 #include "godotsharp_dirs.h"
 
+#include "core/os/dir_access.h"
 #include "core/os/os.h"
+#include "core/project_settings.h"
 
 #ifdef TOOLS_ENABLED
-#include "core/os/dir_access.h"
-#include "core/project_settings.h"
 #include "core/version.h"
 #include "editor/editor_settings.h"
 #endif
@@ -95,10 +95,18 @@ public:
 #ifdef TOOLS_ENABLED
 	String mono_solutions_dir;
 	String build_logs_dir;
+
 	String sln_filepath;
 	String csproj_filepath;
+
+	String data_mono_bin_dir;
+	String data_editor_tools_dir;
+	String data_editor_prebuilt_api_dir;
 #endif
 
+	String data_mono_etc_dir;
+	String data_mono_lib_dir;
+
 private:
 	_GodotSharpDirs() {
 		res_data_dir = "res://.mono";
@@ -123,10 +131,60 @@ private:
 			name = "UnnamedProject";
 		}
 
-		String base_path = String("res://") + name;
+		String base_path = ProjectSettings::get_singleton()->globalize_path("res://");
+
+		sln_filepath = base_path.plus_file(name + ".sln");
+		csproj_filepath = base_path.plus_file(name + ".csproj");
+#endif
+
+		String exe_dir = OS::get_singleton()->get_executable_path().get_base_dir();
+
+#ifdef TOOLS_ENABLED
+
+		String data_dir_root = exe_dir.plus_file("GodotSharp");
+		data_editor_tools_dir = data_dir_root.plus_file("Tools");
+		data_editor_prebuilt_api_dir = data_dir_root.plus_file("Api");
+
+		String data_mono_root_dir = data_dir_root.plus_file("Mono");
+		data_mono_bin_dir = data_mono_root_dir.plus_file("bin");
+		data_mono_etc_dir = data_mono_root_dir.plus_file("etc");
+		data_mono_lib_dir = data_mono_root_dir.plus_file("lib");
+
+#ifdef OSX_ENABLED
+		if (!DirAccess::exists(data_editor_tools_dir)) {
+			data_editor_tools_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Tools");
+		}
+
+		if (!DirAccess::exists(data_editor_prebuilt_api_dir)) {
+			data_editor_prebuilt_api_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Api");
+		}
+
+		if (!DirAccess::exists(data_mono_root_dir)) {
+			data_mono_bin_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Mono/bin");
+			data_mono_etc_dir = exe_dir.plus_file("../Resources/GodotSharp/Mono/etc");
+			data_mono_lib_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Mono/lib");
+		}
+#endif
+
+#else
+
+		String appname = OS::get_singleton()->get_safe_dir_name(ProjectSettings::get_singleton()->get("application/config/name"));
+		String data_dir_root = exe_dir.plus_file("data_" + appname);
+		if (!DirAccess::exists(data_dir_root)) {
+			data_dir_root = exe_dir.plus_file("data_Godot");
+		}
+
+		String data_mono_root_dir = data_dir_root.plus_file("Mono");
+		data_mono_etc_dir = data_mono_root_dir.plus_file("etc");
+		data_mono_lib_dir = data_mono_root_dir.plus_file("lib");
+
+#ifdef OSX_ENABLED
+		if (!DirAccess::exists(data_mono_root_dir)) {
+			data_mono_etc_dir = exe_dir.plus_file("../Resources/GodotSharp/Mono/etc");
+			data_mono_lib_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Mono/lib");
+		}
+#endif
 
-		sln_filepath = ProjectSettings::get_singleton()->globalize_path(base_path + ".sln");
-		csproj_filepath = ProjectSettings::get_singleton()->globalize_path(base_path + ".csproj");
 #endif
 	}
 
@@ -192,5 +250,26 @@ String get_project_sln_path() {
 String get_project_csproj_path() {
 	return _GodotSharpDirs::get_singleton().csproj_filepath;
 }
+
+String get_data_mono_bin_dir() {
+	return _GodotSharpDirs::get_singleton().data_mono_bin_dir;
+}
+
+String get_data_editor_tools_dir() {
+	return _GodotSharpDirs::get_singleton().data_editor_tools_dir;
+}
+
+String get_data_editor_prebuilt_api_dir() {
+	return _GodotSharpDirs::get_singleton().data_editor_prebuilt_api_dir;
+}
 #endif
+
+String get_data_mono_etc_dir() {
+	return _GodotSharpDirs::get_singleton().data_mono_etc_dir;
+}
+
+String get_data_mono_lib_dir() {
+	return _GodotSharpDirs::get_singleton().data_mono_lib_dir;
+}
+
 } // namespace GodotSharpDirs

+ 9 - 2
modules/mono/godotsharp_dirs.h

@@ -49,11 +49,18 @@ String get_mono_logs_dir();
 #ifdef TOOLS_ENABLED
 String get_mono_solutions_dir();
 String get_build_logs_dir();
-String get_custom_project_settings_dir();
-#endif
 
 String get_project_sln_path();
 String get_project_csproj_path();
+
+String get_data_mono_bin_dir();
+String get_data_editor_tools_dir();
+String get_data_editor_prebuilt_api_dir();
+#endif
+
+String get_data_mono_etc_dir();
+String get_data_mono_lib_dir();
+
 } // namespace GodotSharpDirs
 
 #endif // GODOTSHARP_DIRS_H

+ 61 - 37
modules/mono/mono_gd/gd_mono.cpp

@@ -162,50 +162,63 @@ void GDMono::initialize() {
 	mono_trace_set_printerr_handler(gdmono_MonoPrintCallback);
 #endif
 
+	String assembly_rootdir;
+	String config_dir;
+
+#ifdef TOOLS_ENABLED
 #ifdef WINDOWS_ENABLED
 	mono_reg_info = MonoRegUtils::find_mono();
 
-	CharString assembly_dir;
-	CharString config_dir;
-
 	if (mono_reg_info.assembly_dir.length() && DirAccess::exists(mono_reg_info.assembly_dir)) {
-		assembly_dir = mono_reg_info.assembly_dir.utf8();
+		assembly_rootdir = mono_reg_info.assembly_dir;
 	}
 
 	if (mono_reg_info.config_dir.length() && DirAccess::exists(mono_reg_info.config_dir)) {
-		config_dir = mono_reg_info.config_dir.utf8();
+		config_dir = mono_reg_info.config_dir;
 	}
-
-	mono_set_dirs(assembly_dir.length() ? assembly_dir.get_data() : NULL,
-			config_dir.length() ? config_dir.get_data() : NULL);
 #elif OSX_ENABLED
-	mono_set_dirs(NULL, NULL);
-
-	{
-		const char *assembly_rootdir = mono_assembly_getrootdir();
-		const char *config_dir = mono_get_config_dir();
-
-		if (!assembly_rootdir || !config_dir || !DirAccess::exists(assembly_rootdir) || !DirAccess::exists(config_dir)) {
-			Vector<const char *> locations;
-			locations.push_back("/Library/Frameworks/Mono.framework/Versions/Current/");
-			locations.push_back("/usr/local/var/homebrew/linked/mono/");
-
-			for (int i = 0; i < locations.size(); i++) {
-				String hint_assembly_rootdir = path_join(locations[i], "lib");
-				String hint_mscorlib_path = path_join(hint_assembly_rootdir, "mono", "4.5", "mscorlib.dll");
-				String hint_config_dir = path_join(locations[i], "etc");
-
-				if (FileAccess::exists(hint_mscorlib_path) && DirAccess::exists(hint_config_dir)) {
-					mono_set_dirs(hint_assembly_rootdir.utf8().get_data(), hint_config_dir.utf8().get_data());
-					break;
-				}
+	const char *c_assembly_rootdir = mono_assembly_getrootdir();
+	const char *c_config_dir = mono_get_config_dir();
+
+	if (!c_assembly_rootdir || !c_config_dir || !DirAccess::exists(c_assembly_rootdir) || !DirAccess::exists(c_config_dir)) {
+		Vector<const char *> locations;
+		locations.push_back("/Library/Frameworks/Mono.framework/Versions/Current/");
+		locations.push_back("/usr/local/var/homebrew/linked/mono/");
+
+		for (int i = 0; i < locations.size(); i++) {
+			String hint_assembly_rootdir = path_join(locations[i], "lib");
+			String hint_mscorlib_path = path_join(hint_assembly_rootdir, "mono", "4.5", "mscorlib.dll");
+			String hint_config_dir = path_join(locations[i], "etc");
+
+			if (FileAccess::exists(hint_mscorlib_path) && DirAccess::exists(hint_config_dir)) {
+				need_set_mono_dirs = false;
+				assembly_rootdir = hint_assembly_rootdir;
+				config_dir = hint_config_dir;
+				break;
 			}
 		}
 	}
+#endif
+#endif // TOOLS_ENABLED
+
+	String bundled_assembly_rootdir = GodotSharpDirs::get_data_mono_lib_dir();
+	String bundled_config_dir = GodotSharpDirs::get_data_mono_etc_dir();
+
+#ifdef TOOLS_ENABLED
+	if (DirAccess::exists(bundled_assembly_rootdir) && DirAccess::exists(bundled_config_dir)) {
+		assembly_rootdir = bundled_assembly_rootdir;
+		config_dir = bundled_config_dir;
+	}
 #else
-	mono_set_dirs(NULL, NULL);
+	// These are always the directories in export templates
+	assembly_rootdir = bundled_assembly_rootdir;
+	config_dir = bundled_config_dir;
 #endif
 
+	// Leak if we call mono_set_dirs more than once
+	mono_set_dirs(assembly_rootdir.length() ? assembly_rootdir.utf8().get_data() : NULL,
+			config_dir.length() ? config_dir.utf8().get_data() : NULL);
+
 	GDMonoAssembly::initialize();
 
 #ifdef DEBUG_ENABLED
@@ -262,8 +275,11 @@ void GDMono::initialize() {
 		// Everything is fine with the api assemblies, load the project assembly
 		_load_project_assembly();
 	} else {
-		if ((core_api_assembly && (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated)) ||
-				(editor_api_assembly && editor_api_assembly_out_of_sync)) {
+		if ((core_api_assembly && (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated))
+#ifdef TOOLS_ENABLED
+				|| (editor_api_assembly && editor_api_assembly_out_of_sync)
+#endif
+		) {
 #ifdef TOOLS_ENABLED
 			// The assembly was successfully loaded, but the full api could not be cached.
 			// This is most likely an outdated assembly loaded because of an invalid version in the
@@ -362,24 +378,34 @@ GDMonoAssembly **GDMono::get_loaded_assembly(const String &p_name) {
 
 bool GDMono::load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly) {
 
+	return load_assembly_from(p_name, String(), r_assembly, p_refonly);
+}
+
+bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly) {
+
+	return load_assembly_from(p_name, String(), p_aname, r_assembly, p_refonly);
+}
+
+bool GDMono::load_assembly_from(const String &p_name, const String &p_basedir, GDMonoAssembly **r_assembly, bool p_refonly) {
+
 	CRASH_COND(!r_assembly);
 
 	MonoAssemblyName *aname = mono_assembly_name_new(p_name.utf8());
-	bool result = load_assembly(p_name, aname, r_assembly, p_refonly);
+	bool result = load_assembly_from(p_name, p_basedir, aname, r_assembly, p_refonly);
 	mono_assembly_name_free(aname);
 	mono_free(aname);
 
 	return result;
 }
 
-bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly) {
+bool GDMono::load_assembly_from(const String &p_name, const String &p_basedir, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly) {
 
 	CRASH_COND(!r_assembly);
 
 	print_verbose("Mono: Loading assembly " + p_name + (p_refonly ? " (refonly)" : "") + "...");
 
 	MonoImageOpenStatus status = MONO_IMAGE_OK;
-	MonoAssembly *assembly = mono_assembly_load_full(p_aname, NULL, &status, p_refonly);
+	MonoAssembly *assembly = mono_assembly_load_full(p_aname, p_basedir.length() ? p_basedir.utf8().get_data() : NULL, &status, p_refonly);
 
 	if (!assembly)
 		return false;
@@ -480,12 +506,10 @@ bool GDMono::_load_editor_api_assembly() {
 	if (editor_api_assembly)
 		return true;
 
-#ifdef TOOLS_ENABLED
 	if (metadata_is_api_assembly_invalidated(APIAssembly::API_EDITOR)) {
 		print_verbose("Mono: Skipping loading of Editor API assembly because it was invalidated");
 		return false;
 	}
-#endif
 
 	bool success = load_assembly(EDITOR_API_ASSEMBLY_NAME, &editor_api_assembly);
 
@@ -772,7 +796,7 @@ Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) {
 
 	print_verbose("Mono: Unloading domain `" + domain_name + "`...");
 
-	if (mono_domain_get() != root_domain)
+	if (mono_domain_get() == p_domain)
 		mono_domain_set(root_domain, true);
 
 	mono_gc_collect(mono_gc_max_generation());

+ 31 - 6
modules/mono/mono_gd/gd_mono.h

@@ -142,7 +142,7 @@ class GDMono {
 
 	GDMonoLog *gdmono_log;
 
-#ifdef WINDOWS_ENABLED
+#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED)
 	MonoRegInfo mono_reg_info;
 #endif
 
@@ -197,6 +197,9 @@ public:
 
 	bool load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly = false);
 	bool load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly = false);
+	bool load_assembly_from(const String &p_name, const String &p_basedir, GDMonoAssembly **r_assembly, bool p_refonly = false);
+	bool load_assembly_from(const String &p_name, const String &p_basedir, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly = false);
+
 	Error finalize_and_unload_domain(MonoDomain *p_domain);
 
 	void initialize();
@@ -205,12 +208,14 @@ public:
 	~GDMono();
 };
 
-class GDMonoScopeDomain {
+namespace gdmono {
+
+class ScopeDomain {
 
 	MonoDomain *prev_domain;
 
 public:
-	GDMonoScopeDomain(MonoDomain *p_domain) {
+	ScopeDomain(MonoDomain *p_domain) {
 		MonoDomain *prev_domain = mono_domain_get();
 		if (prev_domain != p_domain) {
 			this->prev_domain = prev_domain;
@@ -220,16 +225,36 @@ public:
 		}
 	}
 
-	~GDMonoScopeDomain() {
+	~ScopeDomain() {
 		if (prev_domain)
 			mono_domain_set(prev_domain, false);
 	}
 };
 
-#define _GDMONO_SCOPE_DOMAIN_(m_mono_domain)                    \
-	GDMonoScopeDomain __gdmono__scope__domain__(m_mono_domain); \
+class ScopeExitDomainUnload {
+	MonoDomain *domain;
+
+public:
+	ScopeExitDomainUnload(MonoDomain *p_domain) :
+			domain(p_domain) {
+	}
+
+	~ScopeExitDomainUnload() {
+		if (domain)
+			GDMono::get_singleton()->finalize_and_unload_domain(domain);
+	}
+};
+
+} // namespace gdmono
+
+#define _GDMONO_SCOPE_DOMAIN_(m_mono_domain)                      \
+	gdmono::ScopeDomain __gdmono__scope__domain__(m_mono_domain); \
 	(void)__gdmono__scope__domain__;
 
+#define _GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(m_mono_domain)                                  \
+	gdmono::ScopeExitDomainUnload __gdmono__scope__exit__domain__unload__(m_mono_domain); \
+	(void)__gdmono__scope__exit__domain__unload__;
+
 class _GodotSharp : public Object {
 	GDCLASS(_GodotSharp, Object)
 

+ 64 - 84
modules/mono/mono_gd/gd_mono_assembly.cpp

@@ -46,6 +46,29 @@ 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) {
+
+	const char *rootdir = mono_assembly_getrootdir();
+	if (rootdir) {
+		String framework_dir = String(rootdir).plus_file("mono").plus_file("4.5");
+		r_search_dirs.push_back(framework_dir);
+		r_search_dirs.push_back(framework_dir.plus_file("Facades"));
+	}
+
+	if (p_custom_config.length()) {
+		r_search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_base_dir().plus_file(p_custom_config));
+	} else {
+		r_search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_dir());
+	}
+
+	r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_dir());
+	r_search_dirs.push_back(OS::get_singleton()->get_resource_dir());
+	r_search_dirs.push_back(OS::get_singleton()->get_executable_path().get_base_dir());
+#ifdef TOOLS_ENABLED
+	r_search_dirs.push_back(GodotSharpDirs::get_data_editor_tools_dir());
+#endif
+}
+
 void GDMonoAssembly::assembly_load_hook(MonoAssembly *assembly, void *user_data) {
 
 	if (no_search)
@@ -93,35 +116,7 @@ MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_d
 
 	no_search = true; // Avoid the recursion madness
 
-	String path;
-	GDMonoAssembly *res = NULL;
-
-	for (int i = 0; i < search_dirs.size(); i++) {
-		const String &search_dir = search_dirs[i];
-
-		if (has_extension) {
-			path = search_dir.plus_file(name);
-			if (FileAccess::exists(path)) {
-				res = _load_assembly_from(name.get_basename(), path, refonly);
-				if (res != NULL)
-					break;
-			}
-		} else {
-			path = search_dir.plus_file(name + ".dll");
-			if (FileAccess::exists(path)) {
-				res = _load_assembly_from(name, path, refonly);
-				if (res != NULL)
-					break;
-			}
-
-			path = search_dir.plus_file(name + ".exe");
-			if (FileAccess::exists(path)) {
-				res = _load_assembly_from(name, path, refonly);
-				if (res != NULL)
-					break;
-			}
-		}
-	}
+	GDMonoAssembly *res = _load_assembly_search(name, search_dirs, refonly);
 
 	no_search = false;
 
@@ -130,31 +125,12 @@ MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_d
 
 static _THREAD_LOCAL_(MonoImage *) image_corlib_loading = NULL;
 
-MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data, bool refonly) {
+MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **, void *user_data, bool refonly) {
 
 	(void)user_data; // UNUSED
 
 	if (search_dirs.empty()) {
-		search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_dir());
-		search_dirs.push_back(GodotSharpDirs::get_res_assemblies_dir());
-		search_dirs.push_back(OS::get_singleton()->get_resource_dir());
-		search_dirs.push_back(OS::get_singleton()->get_executable_path().get_base_dir());
-#ifdef GD_MONO_EDITOR_ASSEMBLIES_DIR
-		search_dirs.push_back(OS::get_singleton()->get_executable_path().get_base_dir().plus_file(_MKSTR(GD_MONO_EDITOR_ASSEMBLIES_DIR)).simplify_path());
-#endif
-
-		const char *rootdir = mono_assembly_getrootdir();
-		if (rootdir) {
-			search_dirs.push_back(String(rootdir).plus_file("mono").plus_file("4.5"));
-			search_dirs.push_back(String(rootdir).plus_file("mono").plus_file("4.5").plus_file("Facades"));
-		}
-
-		if (assemblies_path) {
-			while (*assemblies_path) {
-				search_dirs.push_back(*assemblies_path);
-				++assemblies_path;
-			}
-		}
+		fill_search_dirs(search_dirs);
 	}
 
 	{
@@ -188,27 +164,7 @@ MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **asse
 		if (stored_assembly)
 			return (*stored_assembly)->get_assembly();
 
-		String path;
-
-		for (int i = 0; i < search_dirs.size(); i++) {
-			const String &search_dir = search_dirs[i];
-
-			if (has_extension) {
-				path = search_dir.plus_file(name);
-				if (FileAccess::exists(path)) {
-					res = _load_assembly_from(name.get_basename(), path, refonly);
-					if (res != NULL)
-						break;
-				}
-			} else {
-				path = search_dir.plus_file(name + ".dll");
-				if (FileAccess::exists(path)) {
-					res = _load_assembly_from(name, path, refonly);
-					if (res != NULL)
-						break;
-				}
-			}
-		}
+		res = _load_assembly_search("mscorlib.dll", search_dirs, refonly);
 	}
 
 	no_search = false;
@@ -217,6 +173,43 @@ MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **asse
 	return res ? res->get_assembly() : NULL;
 }
 
+GDMonoAssembly *GDMonoAssembly::_load_assembly_search(const String &p_name, const Vector<String> &p_search_dirs, bool p_refonly) {
+
+	GDMonoAssembly *res = NULL;
+	String path;
+
+	bool has_extension = p_name.ends_with(".dll") || p_name.ends_with(".exe");
+
+	for (int i = 0; i < p_search_dirs.size(); i++) {
+		const String &search_dir = p_search_dirs[i];
+
+		if (has_extension) {
+			path = search_dir.plus_file(p_name);
+			if (FileAccess::exists(path)) {
+				res = _load_assembly_from(p_name.get_basename(), path, p_refonly);
+				if (res != NULL)
+					return res;
+			}
+		} else {
+			path = search_dir.plus_file(p_name + ".dll");
+			if (FileAccess::exists(path)) {
+				res = _load_assembly_from(p_name, path, p_refonly);
+				if (res != NULL)
+					return res;
+			}
+
+			path = search_dir.plus_file(p_name + ".exe");
+			if (FileAccess::exists(path)) {
+				res = _load_assembly_from(p_name, path, p_refonly);
+				if (res != NULL)
+					return res;
+			}
+		}
+	}
+
+	return NULL;
+}
+
 GDMonoAssembly *GDMonoAssembly::_load_assembly_from(const String &p_name, const String &p_path, bool p_refonly) {
 
 	GDMonoAssembly *assembly = memnew(GDMonoAssembly(p_name, p_path));
@@ -464,19 +457,6 @@ GDMonoClass *GDMonoAssembly::get_object_derived_class(const StringName &p_class)
 	return match;
 }
 
-GDMonoAssembly *GDMonoAssembly::load_from(const String &p_name, const String &p_path, bool p_refonly) {
-
-	GDMonoAssembly **loaded_asm = GDMono::get_singleton()->get_loaded_assembly(p_name);
-	if (loaded_asm)
-		return *loaded_asm;
-
-	no_search = true;
-	GDMonoAssembly *res = _load_assembly_from(p_name, p_path, p_refonly);
-	no_search = false;
-
-	return res;
-}
-
 GDMonoAssembly::GDMonoAssembly(const String &p_name, const String &p_path) {
 
 	loaded = false;

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

@@ -102,6 +102,7 @@ class GDMonoAssembly {
 	static MonoAssembly *_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data, bool refonly);
 
 	static GDMonoAssembly *_load_assembly_from(const String &p_name, const String &p_path, bool p_refonly);
+	static GDMonoAssembly *_load_assembly_search(const String &p_name, const Vector<String> &p_search_dirs, bool p_refonly);
 	static void _wrap_mono_assembly(MonoAssembly *assembly);
 
 	friend class GDMono;
@@ -125,7 +126,7 @@ public:
 
 	GDMonoClass *get_object_derived_class(const StringName &p_class);
 
-	static GDMonoAssembly *load_from(const String &p_name, const String &p_path, bool p_refonly);
+	static void fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config = String());
 
 	GDMonoAssembly(const String &p_name, const String &p_path = String());
 	~GDMonoAssembly();