Kaynağa Gözat

Merge pull request #33982 from neikeq/issue-29349

Mono/C#: Add option to export assemblies outside of PCK
Rémi Verschelde 5 yıl önce
ebeveyn
işleme
fa0e682027

+ 8 - 6
modules/mono/build_scripts/mono_configure.py

@@ -120,9 +120,9 @@ def configure(env, env_mono):
         env.Append(LIBPATH=mono_lib_path)
         env_mono.Prepend(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0'))
 
-        if mono_static:
-            lib_suffix = Environment()['LIBSUFFIX']
+        lib_suffix = Environment()['LIBSUFFIX']
 
+        if mono_static:
             if env.msvc:
                 mono_static_lib_name = 'libmono-static-sgen'
             else:
@@ -144,13 +144,13 @@ def configure(env, env_mono):
                 env.Append(LIBS=['psapi'])
                 env.Append(LIBS=['version'])
         else:
-            mono_lib_name = find_file_in_dir(mono_lib_path, mono_lib_names, extension='.lib')
+            mono_lib_name = find_file_in_dir(mono_lib_path, mono_lib_names, extension=lib_suffix)
 
             if not mono_lib_name:
                 raise RuntimeError('Could not find mono library in: ' + mono_lib_path)
 
             if env.msvc:
-                env.Append(LINKFLAGS=mono_lib_name + Environment()['LIBSUFFIX'])
+                env.Append(LINKFLAGS=mono_lib_name + lib_suffix)
             else:
                 env.Append(LIBS=[mono_lib_name])
 
@@ -426,15 +426,17 @@ def copy_mono_shared_libs(env, mono_root, target_mono_root_dir):
     platform = env['platform']
 
     if platform == 'windows':
+        src_mono_bin_dir = os.path.join(mono_root, 'bin')
         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'), target_mono_bin_dir)
+        mono_posix_helper_name = find_file_in_dir(src_mono_bin_dir, ['MonoPosixHelper', 'libMonoPosixHelper'], extension='.dll')
+        copy(os.path.join(src_mono_bin_dir, mono_posix_helper_name + '.dll'), os.path.join(target_mono_bin_dir, 'MonoPosixHelper.dll'))
 
         # For newer versions
-        btls_dll_path = os.path.join(mono_root, 'bin', 'libmono-btls-shared.dll')
+        btls_dll_path = os.path.join(src_mono_bin_dir, 'libmono-btls-shared.dll')
         if os.path.isfile(btls_dll_path):
             copy(btls_dll_path, target_mono_bin_dir)
     else:

+ 26 - 8
modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs

@@ -22,6 +22,7 @@ namespace GodotTools.Export
             // TODO: These would be better as export preset options, but that doesn't seem to be supported yet
 
             GlobalDef("mono/export/include_scripts_content", false);
+            GlobalDef("mono/export/export_assemblies_inside_pck", true);
 
             GlobalDef("mono/export/aot/enabled", false);
             GlobalDef("mono/export/aot/full_aot", false);
@@ -130,21 +131,38 @@ namespace GodotTools.Export
                 internal_GetExportedAssemblyDependencies(projectDllName, projectDllSrcPath, buildConfig, platformBclDir, dependencies);
             }
 
+            string outputDataDir = null;
+
+            if (PlatformHasTemplateDir(platform))
+                outputDataDir = ExportDataDirectory(features, platform, isDebug, outputDir);
+
             string apiConfig = isDebug ? "Debug" : "Release";
             string resAssembliesDir = Path.Combine(GodotSharpDirs.ResAssembliesBaseDir, apiConfig);
 
-            foreach (var dependency in dependencies)
+            bool assembliesInsidePck = (bool) ProjectSettings.GetSetting("mono/export/export_assemblies_inside_pck") || outputDataDir == null;
+
+            if (!assembliesInsidePck)
             {
-                string dependSrcPath = dependency.Value;
-                string dependDstPath = Path.Combine(resAssembliesDir, dependSrcPath.GetFile());
-                AddFile(dependSrcPath, dependDstPath);
+                string outputDataGameAssembliesDir = Path.Combine(outputDataDir, "Assemblies");
+                if (!Directory.Exists(outputDataGameAssembliesDir))
+                    Directory.CreateDirectory(outputDataGameAssembliesDir);
             }
 
-            // Mono specific export template extras (data dir)
-            string outputDataDir = null;
+            foreach (var dependency in dependencies)
+            {
+                string dependSrcPath = dependency.Value;
 
-            if (PlatformHasTemplateDir(platform))
-                outputDataDir = ExportDataDirectory(features, platform, isDebug, outputDir);
+                if (assembliesInsidePck)
+                {
+                    string dependDstPath = Path.Combine(resAssembliesDir, dependSrcPath.GetFile());
+                    AddFile(dependSrcPath, dependDstPath);
+                }
+                else
+                {
+                    string dependDstPath = Path.Combine(outputDataDir, "Assemblies", dependSrcPath.GetFile());
+                    File.Copy(dependSrcPath, dependDstPath);
+                }
+            }
 
             // AOT
 

+ 13 - 0
modules/mono/godotsharp_dirs.cpp

@@ -108,6 +108,10 @@ public:
 
 	String data_editor_tools_dir;
 	String data_editor_prebuilt_api_dir;
+#else
+	// Equivalent of res_assemblies_dir, but in the data directory rather than in 'res://'.
+	// Only defined on export templates. Used when exporting assemblies outside of PCKs.
+	String data_game_assemblies_dir;
 #endif
 
 	String data_mono_etc_dir;
@@ -205,6 +209,7 @@ private:
 		data_mono_lib_dir = GDMonoAndroid::get_app_native_lib_dir();
 #else
 		data_mono_lib_dir = data_mono_root_dir.plus_file("lib");
+		data_game_assemblies_dir = data_dir_root.plus_file("Assemblies");
 #endif
 
 #ifdef WINDOWS_ENABLED
@@ -216,6 +221,10 @@ private:
 			data_mono_etc_dir = exe_dir.plus_file("../Resources/GodotSharp/Mono/etc");
 			data_mono_lib_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Mono/lib");
 		}
+
+		if (!DirAccess::exists(data_game_assemblies_dir)) {
+			data_game_assemblies_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Assemblies");
+		}
 #endif
 
 #endif
@@ -295,6 +304,10 @@ String get_data_editor_tools_dir() {
 String get_data_editor_prebuilt_api_dir() {
 	return _GodotSharpDirs::get_singleton().data_editor_prebuilt_api_dir;
 }
+#else
+String get_data_game_assemblies_dir() {
+	return _GodotSharpDirs::get_singleton().data_game_assemblies_dir;
+}
 #endif
 
 String get_data_mono_etc_dir() {

+ 2 - 0
modules/mono/godotsharp_dirs.h

@@ -56,6 +56,8 @@ String get_project_csproj_path();
 
 String get_data_editor_tools_dir();
 String get_data_editor_prebuilt_api_dir();
+#else
+String get_data_game_assemblies_dir();
 #endif
 
 String get_data_mono_etc_dir();

+ 55 - 52
modules/mono/mono_gd/gd_mono.cpp

@@ -239,35 +239,22 @@ void GDMono::add_mono_shared_libs_dir_to_path() {
 #endif // WINDOWS_ENABLED || UNIX_ENABLED
 }
 
-void GDMono::initialize() {
-
-	ERR_FAIL_NULL(Engine::get_singleton());
-
-	print_verbose("Mono: Initializing module...");
-
-	char *runtime_build_info = mono_get_runtime_build_info();
-	print_verbose("Mono JIT compiler version " + String(runtime_build_info));
-	mono_free(runtime_build_info);
-
-#ifdef DEBUG_METHODS_ENABLED
-	_initialize_and_check_api_hashes();
-#endif
+void GDMono::determine_mono_dirs(String &r_assembly_rootdir, String &r_config_dir) {
 
-	GDMonoLog::get_singleton()->initialize();
-
-	String assembly_rootdir;
-	String config_dir;
+	String bundled_assembly_rootdir = GodotSharpDirs::get_data_mono_lib_dir();
+	String bundled_config_dir = GodotSharpDirs::get_data_mono_etc_dir();
 
 #ifdef TOOLS_ENABLED
+
 #if defined(WINDOWS_ENABLED)
 	mono_reg_info = MonoRegUtils::find_mono();
 
 	if (mono_reg_info.assembly_dir.length() && DirAccess::exists(mono_reg_info.assembly_dir)) {
-		assembly_rootdir = mono_reg_info.assembly_dir;
+		r_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;
+		r_config_dir = mono_reg_info.config_dir;
 	}
 #elif defined(OSX_ENABLED)
 	const char *c_assembly_rootdir = mono_assembly_getrootdir();
@@ -284,29 +271,24 @@ void GDMono::initialize() {
 			String hint_config_dir = path::join(locations[i], "etc");
 
 			if (FileAccess::exists(hint_mscorlib_path) && DirAccess::exists(hint_config_dir)) {
-				assembly_rootdir = hint_assembly_rootdir;
-				config_dir = hint_config_dir;
+				r_assembly_rootdir = hint_assembly_rootdir;
+				r_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)) {
-		assembly_rootdir = bundled_assembly_rootdir;
+		r_assembly_rootdir = bundled_assembly_rootdir;
 	}
 
 	if (DirAccess::exists(bundled_config_dir)) {
-		config_dir = bundled_config_dir;
+		r_config_dir = bundled_config_dir;
 	}
 
 #ifdef WINDOWS_ENABLED
-	if (assembly_rootdir.empty() || config_dir.empty()) {
+	if (r_assembly_rootdir.empty() || r_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);
@@ -314,12 +296,32 @@ void GDMono::initialize() {
 #endif // WINDOWS_ENABLED
 
 #else
-	// These are always the directories in export templates
-	assembly_rootdir = bundled_assembly_rootdir;
-	config_dir = bundled_config_dir;
-#endif // TOOLS_ENABLED
+	// Export templates always use the bundled directories
+	r_assembly_rootdir = bundled_assembly_rootdir;
+	r_config_dir = bundled_config_dir;
+#endif
+}
+
+void GDMono::initialize() {
+
+	ERR_FAIL_NULL(Engine::get_singleton());
+
+	print_verbose("Mono: Initializing module...");
+
+	char *runtime_build_info = mono_get_runtime_build_info();
+	print_verbose("Mono JIT compiler version " + String(runtime_build_info));
+	mono_free(runtime_build_info);
+
+	_init_godot_api_hashes();
+	_init_exception_policy();
+
+	GDMonoLog::get_singleton()->initialize();
 
 #if !defined(JAVASCRIPT_ENABLED)
+	String assembly_rootdir;
+	String config_dir;
+	determine_mono_dirs(assembly_rootdir, config_dir);
+
 	// 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);
@@ -331,18 +333,6 @@ void GDMono::initialize() {
 	GDMonoAndroid::register_android_dl_fallback();
 #endif
 
-	{
-		PropertyInfo exc_policy_prop = PropertyInfo(Variant::INT, "mono/unhandled_exception_policy", PROPERTY_HINT_ENUM,
-				vformat("Terminate Application:%s,Log Error:%s", (int)POLICY_TERMINATE_APP, (int)POLICY_LOG_ERROR));
-		unhandled_exception_policy = (UnhandledExceptionPolicy)(int)GLOBAL_DEF(exc_policy_prop.name, (int)POLICY_TERMINATE_APP);
-		ProjectSettings::get_singleton()->set_custom_property_info(exc_policy_prop.name, exc_policy_prop);
-
-		if (Engine::get_singleton()->is_editor_hint()) {
-			// Unhandled exceptions should not terminate the editor
-			unhandled_exception_policy = POLICY_LOG_ERROR;
-		}
-	}
-
 	GDMonoAssembly::initialize();
 
 #if !defined(JAVASCRIPT_ENABLED)
@@ -358,9 +348,12 @@ void GDMono::initialize() {
 	mono_install_unhandled_exception_hook(&unhandled_exception_hook, NULL);
 
 #ifndef TOOLS_ENABLED
-	// Export templates only load the Mono runtime if the project uses it
-	if (!DirAccess::exists("res://.mono"))
+	// Exported games that don't use C# must still work. They likely don't ship with mscorlib.
+	// We only initialize the Mono runtime if we can find mscorlib. Otherwise it would crash.
+	if (GDMonoAssembly::find_assembly("mscorlib.dll").empty()) {
+		print_verbose("Mono: Skipping runtime initialization because 'mscorlib.dll' could not be found");
 		return;
+	}
 #endif
 
 #if !defined(WINDOWS_ENABLED) && !defined(NO_MONO_THREADS_SUSPEND_WORKAROUND)
@@ -475,9 +468,8 @@ void GDMono::_register_internal_calls() {
 	GodotSharpBindings::register_generated_icalls();
 }
 
-void GDMono::_initialize_and_check_api_hashes() {
-#ifdef MONO_GLUE_ENABLED
-#ifdef DEBUG_METHODS_ENABLED
+void GDMono::_init_godot_api_hashes() {
+#if defined(MONO_GLUE_ENABLED) && defined(DEBUG_METHODS_ENABLED)
 	if (get_api_core_hash() != GodotSharpBindings::get_core_api_hash()) {
 		ERR_PRINT("Mono: Core API hash mismatch.");
 	}
@@ -487,8 +479,19 @@ void GDMono::_initialize_and_check_api_hashes() {
 		ERR_PRINT("Mono: Editor API hash mismatch.");
 	}
 #endif // TOOLS_ENABLED
-#endif // DEBUG_METHODS_ENABLED
-#endif // MONO_GLUE_ENABLED
+#endif // MONO_GLUE_ENABLED && DEBUG_METHODS_ENABLED
+}
+
+void GDMono::_init_exception_policy() {
+	PropertyInfo exc_policy_prop = PropertyInfo(Variant::INT, "mono/unhandled_exception_policy", PROPERTY_HINT_ENUM,
+			vformat("Terminate Application:%s,Log Error:%s", (int)POLICY_TERMINATE_APP, (int)POLICY_LOG_ERROR));
+	unhandled_exception_policy = (UnhandledExceptionPolicy)(int)GLOBAL_DEF(exc_policy_prop.name, (int)POLICY_TERMINATE_APP);
+	ProjectSettings::get_singleton()->set_custom_property_info(exc_policy_prop.name, exc_policy_prop);
+
+	if (Engine::get_singleton()->is_editor_hint()) {
+		// Unhandled exceptions should not terminate the editor
+		unhandled_exception_policy = POLICY_LOG_ERROR;
+	}
 }
 
 void GDMono::add_assembly(uint32_t p_domain_id, GDMonoAssembly *p_assembly) {

+ 3 - 1
modules/mono/mono_gd/gd_mono.h

@@ -153,7 +153,8 @@ private:
 #ifdef TOOLS_ENABLED
 	uint64_t api_editor_hash;
 #endif
-	void _initialize_and_check_api_hashes();
+	void _init_godot_api_hashes();
+	void _init_exception_policy();
 
 	GDMonoLog *gdmono_log;
 
@@ -162,6 +163,7 @@ private:
 #endif
 
 	void add_mono_shared_libs_dir_to_path();
+	void determine_mono_dirs(String &r_assembly_rootdir, String &r_config_dir);
 
 protected:
 	static GDMono *singleton;

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

@@ -62,6 +62,13 @@ void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const Strin
 		r_search_dirs.push_back(framework_dir.plus_file("Facades"));
 	}
 
+#if !defined(TOOLS_ENABLED)
+	String data_game_assemblies_dir = GodotSharpDirs::get_data_game_assemblies_dir();
+	if (!data_game_assemblies_dir.empty()) {
+		r_search_dirs.push_back(data_game_assemblies_dir);
+	}
+#endif
+
 	if (p_custom_config.length()) {
 		r_search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_base_dir().plus_file(p_custom_config));
 	} else {
@@ -147,10 +154,6 @@ MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **, vo
 
 	(void)user_data; // UNUSED
 
-	if (search_dirs.empty()) {
-		fill_search_dirs(search_dirs);
-	}
-
 	{
 		// If we find the assembly here, we load it with 'mono_assembly_load_from_full',
 		// which in turn invokes load hooks before returning the MonoAssembly to us.
@@ -228,6 +231,33 @@ GDMonoAssembly *GDMonoAssembly::_load_assembly_search(const String &p_name, cons
 	return NULL;
 }
 
+String GDMonoAssembly::find_assembly(const String &p_name) {
+
+	String path;
+
+	bool has_extension = p_name.ends_with(".dll") || p_name.ends_with(".exe");
+
+	for (int i = 0; i < search_dirs.size(); i++) {
+		const String &search_dir = search_dirs[i];
+
+		if (has_extension) {
+			path = search_dir.plus_file(p_name);
+			if (FileAccess::exists(path))
+				return path;
+		} else {
+			path = search_dir.plus_file(p_name + ".dll");
+			if (FileAccess::exists(path))
+				return path;
+
+			path = search_dir.plus_file(p_name + ".exe");
+			if (FileAccess::exists(path))
+				return path;
+		}
+	}
+
+	return String();
+}
+
 GDMonoAssembly *GDMonoAssembly::_load_assembly_from(const String &p_name, const String &p_path, bool p_refonly) {
 
 	GDMonoAssembly *assembly = memnew(GDMonoAssembly(p_name, p_path));
@@ -264,6 +294,8 @@ void GDMonoAssembly::_wrap_mono_assembly(MonoAssembly *assembly) {
 
 void GDMonoAssembly::initialize() {
 
+	fill_search_dirs(search_dirs);
+
 	mono_install_assembly_search_hook(&assembly_search_hook, NULL);
 	mono_install_assembly_refonly_search_hook(&assembly_refonly_search_hook, NULL);
 	mono_install_assembly_preload_hook(&assembly_preload_hook, NULL);

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

@@ -122,6 +122,8 @@ public:
 
 	GDMonoClass *get_object_derived_class(const StringName &p_class);
 
+	static String find_assembly(const String &p_name);
+
 	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);