From f9a67ee9da1d6cc3562fa5a7443a2a66a673bd8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Rold=C3=A1n=20Etcheverry?= Date: Sun, 12 Sep 2021 20:23:05 +0200 Subject: C#: Begin move to .NET Core We're targeting .NET 5 for now to make development easier while .NET 6 is not yet released. TEMPORARY REGRESSIONS --------------------- Assembly unloading is not implemented yet. As such, many Godot resources are leaked at exit. This will be re-implemented later together with assembly hot-reloading. --- modules/mono/build_scripts/build_assemblies.py | 6 + .../mono/build_scripts/make_android_mono_config.py | 55 -- modules/mono/build_scripts/mono_android_config.xml | 28 - modules/mono/build_scripts/mono_configure.py | 624 ++++------------- modules/mono/build_scripts/mono_reg_utils.py | 112 --- modules/mono/config.py | 30 +- modules/mono/csharp_script.cpp | 397 +++-------- modules/mono/csharp_script.h | 8 +- modules/mono/doc_classes/GodotSharp.xml | 47 +- .../mono/editor/Godot.NET.Sdk/Godot.NET.Sdk.sln | 2 +- .../Godot.SourceGenerators.Sample.csproj | 2 +- .../GodotTools.BuildLogger/GodotBuildLogger.cs | 2 - .../GodotTools.BuildLogger.csproj | 2 +- .../GodotTools.Core/GodotTools.Core.csproj | 2 +- .../GodotTools.IdeMessaging.CLI.csproj | 2 +- .../GodotTools.OpenVisualStudio.csproj | 2 +- .../GodotTools.ProjectEditor.csproj | 24 +- .../GodotTools.ProjectEditor/MSBuild.exe | 0 .../GodotTools.ProjectEditor/ProjectGenerator.cs | 3 +- .../GodotTools.ProjectEditor/ProjectUtils.cs | 6 +- .../GodotTools.Shared/GodotTools.Shared.csproj | 2 +- modules/mono/editor/GodotTools/GodotTools.sln | 2 +- .../GodotTools/GodotTools/Build/BuildManager.cs | 4 +- .../GodotTools/GodotTools/Build/BuildOutputView.cs | 2 +- .../GodotTools/GodotTools/Build/BuildSystem.cs | 10 +- .../GodotTools/GodotTools/Build/MSBuildPanel.cs | 4 - .../GodotTools/GodotTools/Export/ExportPlugin.cs | 14 +- .../GodotTools/GodotTools/GodotSharpEditor.cs | 19 +- .../editor/GodotTools/GodotTools/GodotTools.csproj | 5 +- .../GodotTools/HotReloadAssemblyWatcher.cs | 2 + .../GodotTools/Ides/Rider/RiderPathLocator.cs | 12 +- .../GodotTools/Internals/EditorProgress.cs | 19 +- .../GodotTools/GodotTools/Internals/Globals.cs | 29 +- .../GodotTools/Internals/GodotSharpDirs.cs | 39 +- .../GodotTools/GodotTools/Internals/Internal.cs | 155 ++-- .../GodotTools/GodotTools/Utils/FsPathUtils.cs | 4 +- .../mono/editor/GodotTools/GodotTools/Utils/OS.cs | 23 +- modules/mono/editor/bindings_generator.cpp | 30 +- modules/mono/editor/editor_internal_calls.cpp | 175 +++-- modules/mono/editor/godotsharp_export.cpp | 144 ---- modules/mono/editor/godotsharp_export.h | 44 -- .../GodotSharp/GodotPlugins/GodotPlugins.csproj | 17 + modules/mono/glue/GodotSharp/GodotPlugins/Main.cs | 197 ++++++ .../GodotSharp/GodotPlugins/PluginLoadContext.cs | 61 ++ modules/mono/glue/GodotSharp/GodotSharp.sln | 6 + .../mono/glue/GodotSharp/GodotSharp/Core/Array.cs | 2 +- .../GodotSharp/Core/Bridge/CSharpInstanceBridge.cs | 205 +++--- .../GodotSharp/Core/Bridge/GCHandleBridge.cs | 15 +- .../GodotSharp/Core/Bridge/ManagedCallbacks.cs | 72 ++ .../GodotSharp/Core/Bridge/ScriptManagerBridge.cs | 653 ++++++++++------- .../GodotSharp/GodotSharp/Core/DebuggingUtils.cs | 20 +- .../GodotSharp/GodotSharp/Core/DelegateUtils.cs | 62 +- .../glue/GodotSharp/GodotSharp/Core/Dictionary.cs | 27 +- .../glue/GodotSharp/GodotSharp/Core/Dispatcher.cs | 16 +- .../Core/Extensions/SceneTreeExtensions.cs | 18 +- modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs | 4 +- .../Core/NativeInterop/ExceptionUtils.cs | 74 ++ .../Core/NativeInterop/InteropStructs.cs | 34 +- .../GodotSharp/Core/NativeInterop/InteropUtils.cs | 51 +- .../GodotSharp/Core/NativeInterop/Marshaling.cs | 72 +- .../GodotSharp/Core/NativeInterop/NativeFuncs.cs | 63 +- .../GodotSharp/Core/NativeInterop/VariantUtils.cs | 134 ++-- .../glue/GodotSharp/GodotSharp/Core/NodePath.cs | 2 +- .../glue/GodotSharp/GodotSharp/Core/Object.base.cs | 39 +- .../GodotSharp/GodotSharp/Core/SignalAwaiter.cs | 47 +- .../glue/GodotSharp/GodotSharp/GodotSharp.csproj | 4 +- .../GodotSharp/Properties/AssemblyInfo.cs | 1 + .../GodotSharpEditor/GodotSharpEditor.csproj | 2 +- modules/mono/glue/base_object_glue.cpp | 137 ---- modules/mono/glue/placeholder_glue.cpp | 201 ------ modules/mono/glue/runtime_interop.cpp | 245 ++++++- modules/mono/managed_callable.cpp | 29 +- modules/mono/managed_callable.h | 2 - modules/mono/mono_gc_handle.cpp | 10 +- modules/mono/mono_gc_handle.h | 6 +- modules/mono/mono_gd/gd_mono.cpp | 779 ++++----------------- modules/mono/mono_gd/gd_mono.h | 163 +---- modules/mono/mono_gd/gd_mono_assembly.cpp | 388 ---------- modules/mono/mono_gd/gd_mono_assembly.h | 97 --- modules/mono/mono_gd/gd_mono_cache.cpp | 113 ++- modules/mono/mono_gd/gd_mono_cache.h | 112 +-- modules/mono/mono_gd/gd_mono_internals.cpp | 71 -- modules/mono/mono_gd/gd_mono_internals.h | 52 -- modules/mono/mono_gd/gd_mono_log.cpp | 209 ------ modules/mono/mono_gd/gd_mono_log.h | 71 -- modules/mono/mono_gd/gd_mono_method_thunk.h | 126 ---- modules/mono/mono_gd/gd_mono_utils.cpp | 253 ------- modules/mono/mono_gd/gd_mono_utils.h | 144 ---- modules/mono/mono_gd/gd_mono_wasm_m2n.cpp | 79 --- modules/mono/mono_gd/gd_mono_wasm_m2n.h | 263 ------- modules/mono/signal_awaiter_utils.cpp | 19 +- modules/mono/utils/mono_reg_utils.cpp | 242 ------- modules/mono/utils/mono_reg_utils.h | 54 -- 93 files changed, 2369 insertions(+), 5486 deletions(-) delete mode 100644 modules/mono/build_scripts/make_android_mono_config.py delete mode 100644 modules/mono/build_scripts/mono_android_config.xml delete mode 100644 modules/mono/build_scripts/mono_reg_utils.py delete mode 100644 modules/mono/editor/GodotTools/GodotTools.ProjectEditor/MSBuild.exe delete mode 100644 modules/mono/editor/godotsharp_export.cpp delete mode 100644 modules/mono/editor/godotsharp_export.h create mode 100644 modules/mono/glue/GodotSharp/GodotPlugins/GodotPlugins.csproj create mode 100644 modules/mono/glue/GodotSharp/GodotPlugins/Main.cs create mode 100644 modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs delete mode 100644 modules/mono/glue/base_object_glue.cpp delete mode 100644 modules/mono/glue/placeholder_glue.cpp delete mode 100644 modules/mono/mono_gd/gd_mono_assembly.cpp delete mode 100644 modules/mono/mono_gd/gd_mono_assembly.h delete mode 100644 modules/mono/mono_gd/gd_mono_internals.cpp delete mode 100644 modules/mono/mono_gd/gd_mono_internals.h delete mode 100644 modules/mono/mono_gd/gd_mono_log.cpp delete mode 100644 modules/mono/mono_gd/gd_mono_log.h delete mode 100644 modules/mono/mono_gd/gd_mono_method_thunk.h delete mode 100644 modules/mono/mono_gd/gd_mono_utils.cpp delete mode 100644 modules/mono/mono_gd/gd_mono_utils.h delete mode 100644 modules/mono/mono_gd/gd_mono_wasm_m2n.cpp delete mode 100644 modules/mono/mono_gd/gd_mono_wasm_m2n.h delete mode 100644 modules/mono/utils/mono_reg_utils.cpp delete mode 100644 modules/mono/utils/mono_reg_utils.h (limited to 'modules') diff --git a/modules/mono/build_scripts/build_assemblies.py b/modules/mono/build_scripts/build_assemblies.py index dd96f40f6b..67c2f7f70e 100755 --- a/modules/mono/build_scripts/build_assemblies.py +++ b/modules/mono/build_scripts/build_assemblies.py @@ -203,6 +203,9 @@ def build_godot_api(msbuild_tool, module_dir, output_dir): "GodotSharpEditor.dll", "GodotSharpEditor.pdb", "GodotSharpEditor.xml", + "GodotPlugins.dll", + "GodotPlugins.pdb", + "GodotPlugins.runtimeconfig.json", ] for build_config in ["Debug", "Release"]: @@ -223,6 +226,7 @@ def build_godot_api(msbuild_tool, module_dir, output_dir): core_src_dir = os.path.abspath(os.path.join(sln, os.pardir, "GodotSharp", "bin", build_config)) editor_src_dir = os.path.abspath(os.path.join(sln, os.pardir, "GodotSharpEditor", "bin", build_config)) + plugins_src_dir = os.path.abspath(os.path.join(sln, os.pardir, "GodotPlugins", "bin", build_config, "net5.0")) if not os.path.isdir(editor_api_dir): assert not os.path.isfile(editor_api_dir) @@ -236,6 +240,8 @@ def build_godot_api(msbuild_tool, module_dir, output_dir): src_path = os.path.join(core_src_dir, filename) if not os.path.isfile(src_path): src_path = os.path.join(editor_src_dir, filename) + if not os.path.isfile(src_path): + src_path = os.path.join(plugins_src_dir, filename) print(f"Copying assembly to {target_path}...") copy(src_path, target_path) diff --git a/modules/mono/build_scripts/make_android_mono_config.py b/modules/mono/build_scripts/make_android_mono_config.py deleted file mode 100644 index 3459244bc2..0000000000 --- a/modules/mono/build_scripts/make_android_mono_config.py +++ /dev/null @@ -1,55 +0,0 @@ -def generate_compressed_config(config_src, output_dir): - import os.path - - # Source file - with open(os.path.join(output_dir, "android_mono_config.gen.cpp"), "w") as cpp: - with open(config_src, "rb") as f: - buf = f.read() - decompr_size = len(buf) - import zlib - - # Use maximum zlib compression level to further reduce file size - # (at the cost of initial build times). - buf = zlib.compress(buf, zlib.Z_BEST_COMPRESSION) - compr_size = len(buf) - - bytes_seq_str = "" - for i, buf_idx in enumerate(range(compr_size)): - if i > 0: - bytes_seq_str += ", " - bytes_seq_str += str(buf[buf_idx]) - - cpp.write( - """/* THIS FILE IS GENERATED DO NOT EDIT */ -#include "android_mono_config.h" - -#ifdef ANDROID_ENABLED - -#include "core/io/compression.h" - - -namespace { - -// config -static const int config_compressed_size = %d; -static const int config_uncompressed_size = %d; -static const unsigned char config_compressed_data[] = { %s }; -} // namespace - -String get_godot_android_mono_config() { - Vector data; - data.resize(config_uncompressed_size); - uint8_t* w = data.ptrw(); - Compression::decompress(w, config_uncompressed_size, config_compressed_data, - config_compressed_size, Compression::MODE_DEFLATE); - String s; - if (s.parse_utf8((const char *)w, data.size()) != OK) { - ERR_FAIL_V(String()); - } - return s; -} - -#endif // ANDROID_ENABLED -""" - % (compr_size, decompr_size, bytes_seq_str) - ) diff --git a/modules/mono/build_scripts/mono_android_config.xml b/modules/mono/build_scripts/mono_android_config.xml deleted file mode 100644 index e79670afd2..0000000000 --- a/modules/mono/build_scripts/mono_android_config.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py index e69904c54b..071693d758 100644 --- a/modules/mono/build_scripts/mono_configure.py +++ b/modules/mono/build_scripts/mono_configure.py @@ -1,65 +1,5 @@ import os import os.path -import subprocess - -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/lib/libs").abspath, - "release" if env["target"] == "release" else "debug", - android_arch_dirs[env["android_arch"]], - ) - - -def find_name_in_dir_files(directory, names, prefixes=[""], extensions=[""]): - for extension in extensions: - if extension and not extension.startswith("."): - extension = "." + extension - for prefix in prefixes: - for curname in names: - if os.path.isfile(os.path.join(directory, prefix + curname + extension)): - return curname - return "" - - -def find_file_in_dir(directory, names, prefixes=[""], extensions=[""]): - for extension in extensions: - if extension and not extension.startswith("."): - extension = "." + extension - for prefix in prefixes: - for curname in names: - filename = prefix + curname + extension - if os.path.isfile(os.path.join(directory, filename)): - return filename - return "" - - -def copy_file(src_dir, dst_dir, src_name, dst_name=""): - from shutil import copy - - src_path = os.path.join(Dir(src_dir).abspath, src_name) - dst_dir = Dir(dst_dir).abspath - - if not os.path.isdir(dst_dir): - os.makedirs(dst_dir) - - if dst_name: - copy(src_path, os.path.join(dst_dir, dst_name)) - else: - copy(src_path, dst_dir) def is_desktop(platform): @@ -71,504 +11,176 @@ def is_unix_like(platform): def module_supports_tools_on(platform): - return platform not in ["android", "javascript", "ios"] - - -def find_wasm_src_dir(mono_root): - hint_dirs = [ - os.path.join(mono_root, "src"), - os.path.join(mono_root, "../src"), - ] - for hint_dir in hint_dirs: - if os.path.isfile(os.path.join(hint_dir, "driver.c")): - return hint_dir - return "" + return is_desktop(platform) def configure(env, env_mono): - bits = env["bits"] - is_android = env["platform"] == "android" - is_javascript = env["platform"] == "javascript" - is_ios = env["platform"] == "ios" - is_ios_sim = is_ios and env["arch"] in ["x86", "x86_64"] + # is_android = env["platform"] == "android" + # is_javascript = env["platform"] == "javascript" + # is_ios = env["platform"] == "ios" + # is_ios_sim = is_ios and env["arch"] in ["x86", "x86_64"] tools_enabled = env["tools"] - mono_static = env["mono_static"] - copy_mono_root = env["copy_mono_root"] - - mono_prefix = env["mono_prefix"] - mono_bcl = env["mono_bcl"] - - 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 the specified 'android_arch': " + env["android_arch"]) if tools_enabled and not module_supports_tools_on(env["platform"]): - # TODO: - # Android: 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 this platform with tools enabled") - if is_android and mono_static: - # FIXME: When static linking and doing something that requires libmono-native, we get a dlopen error as 'libmono-native' - # seems to depend on 'libmonosgen-2.0'. Could be fixed by re-directing to '__Internal' with a dllmap or in the dlopen hook. - raise RuntimeError("Statically linking Mono is not currently supported for this platform") - - if not mono_static and (is_javascript or is_ios): - raise RuntimeError("Dynamically linking Mono is not currently supported for this platform") - - if not mono_prefix and (os.getenv("MONO32_PREFIX") or os.getenv("MONO64_PREFIX")): - print( - "WARNING: The environment variables 'MONO32_PREFIX' and 'MONO64_PREFIX' are deprecated; use the" - " 'mono_prefix' SCons parameter instead" - ) - - # Although we don't support building with tools for any platform where we currently use static AOT, - # if these are supported in the future, we won't be using static AOT for them as that would be - # too restrictive for the editor. These builds would probably be made to only use the interpreter. - mono_aot_static = (is_ios and not is_ios_sim) and not env["tools"] - - # Static AOT is only supported on the root domain - mono_single_appdomain = mono_aot_static - - if mono_single_appdomain: - env_mono.Append(CPPDEFINES=["GD_MONO_SINGLE_APPDOMAIN"]) - - if (env["tools"] or env["target"] != "release") and not mono_single_appdomain: + if env["tools"] or env["target"] != "release": env_mono.Append(CPPDEFINES=["GD_MONO_HOT_RELOAD"]) - if env["platform"] == "windows": - mono_root = mono_prefix - - 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; specify one manually with the 'mono_prefix' SCons parameter" - ) - - print("Found Mono root directory: " + mono_root) - - mono_lib_path = os.path.join(mono_root, "lib") - - env.Append(LIBPATH=mono_lib_path) - env_mono.Prepend(CPPPATH=os.path.join(mono_root, "include", "mono-2.0")) + app_host_dir = find_dotnet_app_host_dir(env) - lib_suffixes = [".lib"] + def check_app_host_file_exists(file): + file_path = os.path.join(app_host_dir, file) + if not os.path.isfile(file_path): + raise RuntimeError("File not found: " + file_path) - if not env.msvc: - # MingW supports both '.a' and '.lib' - lib_suffixes.insert(0, ".a") + # TODO: + # All libnethost does for us is provide a function to find hostfxr. + # If we could handle that logic ourselves we could void linking it. - if mono_static: - if env.msvc: - mono_static_lib_name = "libmono-static-sgen" - else: - mono_static_lib_name = "libmonosgen-2.0" + # nethost file names: + # static: libnethost.a/lib + # shared: libnethost.a/dylib and nethost.dll + check_app_host_file_exists("libnethost.lib" if os.name == "nt" else "libnethost.a") + check_app_host_file_exists("nethost.h") + check_app_host_file_exists("hostfxr.h") + check_app_host_file_exists("coreclr_delegates.h") - mono_static_lib_file = find_file_in_dir(mono_lib_path, [mono_static_lib_name], extensions=lib_suffixes) + env.Append(LIBPATH=[app_host_dir]) + env_mono.Prepend(CPPPATH=app_host_dir) - if not mono_static_lib_file: - raise RuntimeError("Could not find static mono library in: " + mono_lib_path) + libnethost_path = os.path.join(app_host_dir, "libnethost.lib" if os.name == "nt" else "libnethost.a") - if env.msvc: - env.Append(LINKFLAGS=mono_static_lib_file) - - env.Append(LINKFLAGS="Mincore.lib") - env.Append(LINKFLAGS="msvcrt.lib") - env.Append(LINKFLAGS="LIBCMT.lib") - env.Append(LINKFLAGS="Psapi.lib") - else: - mono_static_lib_file_path = os.path.join(mono_lib_path, mono_static_lib_file) - env.Append(LINKFLAGS=["-Wl,-whole-archive", mono_static_lib_file_path, "-Wl,-no-whole-archive"]) + if env["platform"] == "windows": + env_mono.Append(CPPDEFINES=["NETHOST_USE_AS_STATIC"]) - env.Append(LIBS=["psapi"]) - env.Append(LIBS=["version"]) + if env.msvc: + env.Append(LINKFLAGS="libnethost.lib") else: - mono_lib_file = find_file_in_dir(mono_lib_path, mono_lib_names, extensions=lib_suffixes) - - if not mono_lib_file: - raise RuntimeError("Could not find mono library in: " + mono_lib_path) - - if env.msvc: - env.Append(LINKFLAGS=mono_lib_file) - else: - mono_lib_file_path = os.path.join(mono_lib_path, mono_lib_file) - env.Append(LINKFLAGS=mono_lib_file_path) - - mono_bin_path = os.path.join(mono_root, "bin") - - mono_dll_file = find_file_in_dir(mono_bin_path, mono_lib_names, prefixes=["", "lib"], extensions=[".dll"]) - - if not mono_dll_file: - raise RuntimeError("Could not find mono shared library in: " + mono_bin_path) - - copy_file(mono_bin_path, "#bin", mono_dll_file) + env.Append(LINKFLAGS=["-Wl,-whole-archive", libnethost_path, "-Wl,-no-whole-archive"]) else: is_apple = env["platform"] in ["macos", "ios"] - is_macos = is_apple and not is_ios - - sharedlib_ext = ".dylib" if is_apple else ".so" - - mono_root = mono_prefix - mono_lib_path = "" - mono_so_file = "" - - if not mono_root and (is_android or is_javascript or is_ios): - raise RuntimeError( - "Mono installation directory not found; specify one manually with the 'mono_prefix' SCons parameter" - ) - - if not mono_root and is_macos: - # Try with some known directories under macOS - hint_dirs = ["/Library/Frameworks/Mono.framework/Versions/Current", "/usr/local/var/homebrew/linked/mono"] - for hint_dir in hint_dirs: - if os.path.isdir(hint_dir): - mono_root = hint_dir - break - - # We can't use pkg-config to link mono statically, - # but we can still use it to find the mono root directory - 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 with the 'mono_prefix' SCons parameter" - ) - - if is_ios and not is_ios_sim: - env_mono.Append(CPPDEFINES=["IOS_DEVICE"]) - - if mono_root: - print("Found Mono root directory: " + mono_root) - - mono_lib_path = os.path.join(mono_root, "lib") - - env.Append(LIBPATH=[mono_lib_path]) - env_mono.Prepend(CPPPATH=os.path.join(mono_root, "include", "mono-2.0")) - - mono_lib = find_name_in_dir_files(mono_lib_path, mono_lib_names, prefixes=["lib"], extensions=[".a"]) - - if not mono_lib: - raise RuntimeError("Could not find mono library in: " + mono_lib_path) - - env_mono.Append(CPPDEFINES=["_REENTRANT"]) - - if mono_static: - if not is_javascript: - env.Append(LINKFLAGS=["-rdynamic"]) - - mono_lib_file = os.path.join(mono_lib_path, "lib" + mono_lib + ".a") - - if is_apple: - if is_macos: - env.Append(LINKFLAGS=["-Wl,-force_load," + mono_lib_file]) - else: - arch = env["arch"] - - def copy_mono_lib(libname_wo_ext): - copy_file( - mono_lib_path, "#bin", libname_wo_ext + ".a", "%s.ios.%s.a" % (libname_wo_ext, arch) - ) - - # Copy Mono libraries to the output folder. These are meant to be bundled with - # the export templates and added to the Xcode project when exporting a game. - copy_mono_lib("lib" + mono_lib) - copy_mono_lib("libmono-native") - copy_mono_lib("libmono-profiler-log") - - if not is_ios_sim: - copy_mono_lib("libmono-ee-interp") - copy_mono_lib("libmono-icall-table") - copy_mono_lib("libmono-ilgen") - else: - assert is_desktop(env["platform"]) or is_android or is_javascript - env.Append(LINKFLAGS=["-Wl,-whole-archive", mono_lib_file, "-Wl,-no-whole-archive"]) - - if is_javascript: - env.Append(LIBS=["mono-icall-table", "mono-native", "mono-ilgen", "mono-ee-interp"]) - - wasm_src_dir = os.path.join(mono_root, "src") - if not os.path.isdir(wasm_src_dir): - raise RuntimeError("Could not find mono wasm src directory") - - # Ideally this should be defined only for 'driver.c', but I can't fight scons for another 2 hours - env_mono.Append(CPPDEFINES=["CORE_BINDINGS"]) - - env_mono.add_source_files( - env.modules_sources, - [ - os.path.join(wasm_src_dir, "driver.c"), - os.path.join(wasm_src_dir, "zlib-helper.c"), - os.path.join(wasm_src_dir, "corebindings.c"), - ], - ) - - env.Append( - LINKFLAGS=[ - "--js-library", - os.path.join(wasm_src_dir, "library_mono.js"), - "--js-library", - os.path.join(wasm_src_dir, "binding_support.js"), - "--js-library", - os.path.join(wasm_src_dir, "dotnet_support.js"), - ] - ) - else: - env.Append(LIBS=[mono_lib]) - - if is_macos: - env.Append(LIBS=["iconv", "pthread"]) - elif is_android: - pass # Nothing - elif is_ios: - pass # Nothing, linking is delegated to the exported Xcode project - elif is_javascript: - env.Append(LIBS=["m", "rt", "dl", "pthread"]) - else: - env.Append(LIBS=["m", "rt", "dl", "pthread"]) - - if not mono_static: - mono_so_file = find_file_in_dir( - mono_lib_path, mono_lib_names, prefixes=["lib"], extensions=[sharedlib_ext] - ) - - if not mono_so_file: - raise RuntimeError("Could not find mono shared library in: " + mono_lib_path) - else: - assert not mono_static + # is_macos = is_apple and not is_ios - # TODO: Add option to force using pkg-config - print("Mono root directory not found. Using pkg-config instead") + # if is_ios and not is_ios_sim: + # env_mono.Append(CPPDEFINES=["IOS_DEVICE"]) - env.ParseConfig("pkg-config monosgen-2 --libs") - env_mono.ParseConfig("pkg-config monosgen-2 --cflags") + if is_apple: + env.Append(LINKFLAGS=["-Wl,-force_load," + libnethost_path]) + else: + env.Append(LINKFLAGS=["-Wl,-whole-archive", libnethost_path, "-Wl,-no-whole-archive"]) - tmpenv = Environment() - tmpenv.AppendENVPath("PKG_CONFIG_PATH", os.getenv("PKG_CONFIG_PATH")) - tmpenv.ParseConfig("pkg-config monosgen-2 --libs-only-L") - for hint_dir in tmpenv["LIBPATH"]: - file_found = find_file_in_dir(hint_dir, mono_lib_names, prefixes=["lib"], extensions=[sharedlib_ext]) - if file_found: - mono_lib_path = hint_dir - mono_so_file = file_found - break +def find_dotnet_app_host_dir(env): + dotnet_root = env["dotnet_root"] - if not mono_so_file: - raise RuntimeError("Could not find mono shared library in: " + str(tmpenv["LIBPATH"])) + if not dotnet_root: + dotnet_exe = find_executable("dotnet") + if dotnet_exe: + dotnet_exe_realpath = os.path.realpath(dotnet_exe) # Eliminate symbolic links + dotnet_root = os.path.abspath(os.path.join(dotnet_exe_realpath, os.pardir)) + else: + raise RuntimeError("Cannot find .NET Core Sdk") - 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, mono_so_file) + print("Found .NET Core Sdk root directory: " + dotnet_root) - if not tools_enabled: - if is_desktop(env["platform"]): - if not mono_root: - mono_root = ( - subprocess.check_output(["pkg-config", "mono-2", "--variable=prefix"]).decode("utf8").strip() - ) + dotnet_cmd = os.path.join(dotnet_root, "dotnet.exe" if os.name == "nt" else "dotnet") - make_template_dir(env, mono_root) - elif is_android: - # Compress Android Mono Config - from . import make_android_mono_config + runtime_identifier = determine_runtime_identifier(env) - module_dir = os.getcwd() - config_file_path = os.path.join(module_dir, "build_scripts", "mono_android_config.xml") - make_android_mono_config.generate_compressed_config(config_file_path, "mono_gd/") + # TODO: In the future, if it can't be found this way, we want to obtain it + # from the runtime.{runtime_identifier}.Microsoft.NETCore.DotNetAppHost NuGet package. + app_host_search_version = "5.0" + app_host_version = find_app_host_version(dotnet_cmd, app_host_search_version) + if not app_host_version: + raise RuntimeError("Cannot find .NET app host for version: " + app_host_search_version) - # Copy the required shared libraries - copy_mono_shared_libs(env, mono_root, None) - elif is_javascript: - pass # No data directory for this platform - elif is_ios: - pass # No data directory for this platform + app_host_dir = os.path.join( + dotnet_root, + "packs", + "Microsoft.NETCore.App.Host." + runtime_identifier, + app_host_version, + "runtimes", + runtime_identifier, + "native", + ) - if copy_mono_root: - if not mono_root: - mono_root = subprocess.check_output(["pkg-config", "mono-2", "--variable=prefix"]).decode("utf8").strip() + return app_host_dir - if tools_enabled: - # Only supported for editor builds. - copy_mono_root_files(env, mono_root, mono_bcl) +def determine_runtime_identifier(env): + names_map = { + "windows": "win", + "macos": "osx", + "linuxbsd": "linux", + "server": "linux", # FIXME: Is server linux only, or also macos? + } -def make_template_dir(env, mono_root): - from shutil import rmtree + # .NET RID architectures: x86, x64, arm, or arm64 platform = env["platform"] - target = env["target"] - - template_dir_name = "" - - assert is_desktop(platform) - - template_dir_name = "data.mono.%s.%s.%s" % (platform, env["bits"], target) - - 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, platform) - - # Copy the required shared libraries - - copy_mono_shared_libs(env, mono_root, template_mono_root_dir) - - -def copy_mono_root_files(env, mono_root, mono_bcl): - 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(env, mono_root, editor_mono_root_dir) + if is_desktop(platform): + if env["arch"] in ["arm", "arm32"]: + rid = "arm" + elif env["arch"] == "arm64": + rid = "arm64" + else: + bits = env["bits"] + bit_arch_map = {"64": "x64", "32": "x86"} + rid = bit_arch_map[bits] + return "%s-%s" % (names_map[platform], rid) + else: + raise NotImplementedError() - # Copy framework assemblies - mono_framework_dir = mono_bcl or os.path.join(mono_root, "lib", "mono", "4.5") - mono_framework_facades_dir = os.path.join(mono_framework_dir, "Facades") +def find_app_host_version(dotnet_cmd, search_version): + import subprocess - 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") + try: + lines = subprocess.check_output([dotnet_cmd, "--list-runtimes"]).splitlines() - 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 line_bytes in lines: + line = line_bytes.decode("utf-8") + if not line.startswith("Microsoft.NETCore.App "): + continue - 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) + parts = line.split(" ") + if len(parts) < 2: + continue + version = parts[1] -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 + # Look for 6.0.0 or 6.0.0-* + if version.startswith(search_version + "."): + return version + except (subprocess.CalledProcessError, OSError): + pass + return "" - 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") +ENV_PATH_SEP = ";" if os.name == "nt" else ":" - 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")) - 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 find_executable(name): + is_windows = os.name == "nt" + windows_exts = os.environ["PATHEXT"].split(ENV_PATH_SEP) if is_windows else None + path_dirs = os.environ["PATH"].split(ENV_PATH_SEP) + search_dirs = path_dirs + [os.getcwd()] # cwd is last in the list -def copy_mono_shared_libs(env, mono_root, target_mono_root_dir): - from shutil import copy + for dir in search_dirs: + path = os.path.join(dir, name) - def copy_if_exists(src, dst): - if os.path.isfile(src): - copy(src, dst) + if is_windows: + for extension in windows_exts: + path_with_ext = path + extension - platform = env["platform"] + if os.path.isfile(path_with_ext) and os.access(path_with_ext, os.X_OK): + return path_with_ext + else: + if os.path.isfile(path) and os.access(path, os.X_OK): + return path - 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) - - mono_posix_helper_file = find_file_in_dir( - src_mono_bin_dir, ["MonoPosixHelper"], prefixes=["", "lib"], extensions=[".dll"] - ) - copy( - os.path.join(src_mono_bin_dir, mono_posix_helper_file), - os.path.join(target_mono_bin_dir, "MonoPosixHelper.dll"), - ) - - # For newer versions - 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: - 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) - - lib_file_names = [] - if platform == "macos": - lib_file_names = [ - lib_name + ".dylib" - for lib_name in ["libmono-btls-shared", "libmono-native-compat", "libMonoPosixHelper"] - ] - elif is_unix_like(platform): - 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 pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext): - tmpenv = Environment() - tmpenv.AppendENVPath("PKG_CONFIG_PATH", os.getenv("PKG_CONFIG_PATH")) - tmpenv.ParseConfig("pkg-config monosgen-2 --libs-only-L") - for hint_dir in tmpenv["LIBPATH"]: - name_found = find_name_in_dir_files(hint_dir, mono_lib_names, prefixes=["lib"], extensions=[sharedlib_ext]) - if name_found and os.path.isdir(os.path.join(hint_dir, "..", "include", "mono-2.0")): - return os.path.join(hint_dir, "..") return "" diff --git a/modules/mono/build_scripts/mono_reg_utils.py b/modules/mono/build_scripts/mono_reg_utils.py deleted file mode 100644 index 43c1ec8f8a..0000000000 --- a/modules/mono/build_scripts/mono_reg_utils.py +++ /dev/null @@ -1,112 +0,0 @@ -import os -import platform - -if os.name == "nt": - import winreg - - -def _reg_open_key(key, subkey): - try: - return winreg.OpenKey(key, subkey) - except OSError: - if platform.architecture()[0] == "32bit": - bitness_sam = winreg.KEY_WOW64_64KEY - else: - bitness_sam = winreg.KEY_WOW64_32KEY - return winreg.OpenKey(key, subkey, 0, winreg.KEY_READ | bitness_sam) - - -def _reg_open_key_bits(key, subkey, bits): - sam = winreg.KEY_READ - - if platform.architecture()[0] == "32bit": - if bits == "64": - # Force 32bit process to search in 64bit registry - sam |= winreg.KEY_WOW64_64KEY - else: - if bits == "32": - # Force 64bit process to search in 32bit registry - sam |= winreg.KEY_WOW64_32KEY - - return winreg.OpenKey(key, subkey, 0, sam) - - -def _find_mono_in_reg(subkey, bits): - try: - with _reg_open_key_bits(winreg.HKEY_LOCAL_MACHINE, subkey, bits) as hKey: - value = winreg.QueryValueEx(hKey, "SdkInstallRoot")[0] - return value - except OSError: - return None - - -def _find_mono_in_reg_old(subkey, bits): - try: - with _reg_open_key_bits(winreg.HKEY_LOCAL_MACHINE, subkey, bits) as hKey: - default_clr = winreg.QueryValueEx(hKey, "DefaultCLR")[0] - if default_clr: - return _find_mono_in_reg(subkey + "\\" + default_clr, bits) - return None - except OSError: - return None - - -def find_mono_root_dir(bits): - root_dir = _find_mono_in_reg(r"SOFTWARE\Mono", bits) - if root_dir is not None: - return str(root_dir) - root_dir = _find_mono_in_reg_old(r"SOFTWARE\Novell\Mono", bits) - if root_dir is not None: - return str(root_dir) - return "" - - -def find_msbuild_tools_path_reg(): - import subprocess - - vswhere = os.getenv("PROGRAMFILES(X86)") - if not vswhere: - vswhere = os.getenv("PROGRAMFILES") - vswhere += r"\Microsoft Visual Studio\Installer\vswhere.exe" - - vswhere_args = ["-latest", "-products", "*", "-requires", "Microsoft.Component.MSBuild"] - - try: - lines = subprocess.check_output([vswhere] + vswhere_args).splitlines() - - for line in lines: - parts = line.decode("utf-8").split(":", 1) - - if len(parts) < 2 or parts[0] != "installationPath": - continue - - val = parts[1].strip() - - if not val: - raise ValueError("Value of `installationPath` entry is empty") - - # Since VS2019, the directory is simply named "Current" - msbuild_dir = os.path.join(val, "MSBuild\\Current\\Bin") - if os.path.isdir(msbuild_dir): - return msbuild_dir - - # Directory name "15.0" is used in VS 2017 - return os.path.join(val, "MSBuild\\15.0\\Bin") - - raise ValueError("Cannot find `installationPath` entry") - except ValueError as e: - print("Error reading output from vswhere: " + e.message) - except subprocess.CalledProcessError as e: - print(e.output) - except OSError as e: - print(e) - - # Try to find 14.0 in the Registry - - try: - subkey = r"SOFTWARE\Microsoft\MSBuild\ToolsVersions\14.0" - with _reg_open_key(winreg.HKEY_LOCAL_MACHINE, subkey) as hKey: - value = winreg.QueryValueEx(hKey, "MSBuildToolsPath")[0] - return value - except OSError: - return "" diff --git a/modules/mono/config.py b/modules/mono/config.py index 010f1ef1e5..d156877929 100644 --- a/modules/mono/config.py +++ b/modules/mono/config.py @@ -1,4 +1,6 @@ -supported_platforms = ["windows", "macos", "linuxbsd", "server", "android", "haiku", "javascript", "ios"] +# Prior to .NET Core, we supported these: ["windows", "macos", "linuxbsd", "server", "android", "haiku", "javascript", "ios"] +# Eventually support for each them should be added back (except Haiku if not supported by .NET Core) +supported_platforms = ["windows", "macos", "linuxbsd", "server"] def can_build(env, platform): @@ -13,26 +15,11 @@ def get_opts(platform): return [ PathVariable( - "mono_prefix", - "Path to the Mono installation directory for the target platform and architecture", + "dotnet_root", + "Path to the .NET Sdk installation directory for the target platform and architecture", "", PathVariable.PathAccept, ), - PathVariable( - "mono_bcl", - "Path to a custom Mono BCL (Base Class Library) directory for the target platform", - "", - PathVariable.PathAccept, - ), - BoolVariable("mono_static", "Statically link Mono", default_mono_static), - BoolVariable("build_cil", "Build C# solutions", True), - BoolVariable( - "copy_mono_root", "Make a copy of the Mono installation directory to bundle with the editor", True - ), - # TODO: It would be great if this could be detected automatically instead - BoolVariable( - "mono_bundles_zlib", "Specify if the Mono runtime was built with bundled zlib", default_mono_bundles_zlib - ), ] @@ -44,13 +31,6 @@ def configure(env): env.add_module_version_string("mono") - if env["mono_bundles_zlib"]: - # Mono may come with zlib bundled for WASM or on newer version when built with MinGW. - print("This Mono runtime comes with zlib bundled. Disabling 'builtin_zlib'...") - env["builtin_zlib"] = False - thirdparty_zlib_dir = "#thirdparty/zlib/" - env.Prepend(CPPPATH=[thirdparty_zlib_dir]) - def get_doc_classes(): return [ diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index b088271b18..772c705981 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -30,8 +30,6 @@ #include "csharp_script.h" -#include -#include #include #include "core/config/project_settings.h" @@ -59,7 +57,6 @@ #include "godotsharp_dirs.h" #include "managed_callable.h" #include "mono_gd/gd_mono_cache.h" -#include "mono_gd/gd_mono_utils.h" #include "signal_awaiter_utils.h" #include "utils/macros.h" #include "utils/string_utils.h" @@ -107,8 +104,12 @@ Error CSharpLanguage::execute_file(const String &p_path) { return OK; } -extern void *godotsharp_pinvoke_funcs[164]; +extern void *godotsharp_pinvoke_funcs[176]; [[maybe_unused]] volatile void **do_not_strip_godotsharp_pinvoke_funcs; +#ifdef TOOLS_ENABLED +extern void *godotsharp_editor_pinvoke_funcs[32]; +[[maybe_unused]] volatile void **do_not_strip_godotsharp_editor_pinvoke_funcs; +#endif void CSharpLanguage::init() { #ifdef DEBUG_METHODS_ENABLED @@ -122,6 +123,9 @@ void CSharpLanguage::init() { // Hopefully this will be enough for all compilers. Otherwise we could use the printf on fake getenv trick. do_not_strip_godotsharp_pinvoke_funcs = (volatile void **)godotsharp_pinvoke_funcs; +#ifdef TOOLS_ENABLED + do_not_strip_godotsharp_editor_pinvoke_funcs = (volatile void **)godotsharp_editor_pinvoke_funcs; +#endif #if defined(TOOLS_ENABLED) && defined(DEBUG_METHODS_ENABLED) // Generate the bindings here, before loading assemblies. The Godot assemblies @@ -709,19 +713,14 @@ void CSharpLanguage::pre_unsafe_unreference(Object *p_obj) { } void CSharpLanguage::frame() { - if (gdmono && gdmono->is_runtime_initialized() && gdmono->get_core_api_assembly() != nullptr) { - MonoException *exc = nullptr; - GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_FrameCallback.invoke(&exc); - if (exc) { - GDMonoUtils::debug_unhandled_exception(exc); - } + if (gdmono && gdmono->is_runtime_initialized() && GDMonoCache::godot_api_cache_updated) { + GDMonoCache::managed_callbacks.ScriptManagerBridge_FrameCallback(); } } void CSharpLanguage::reload_all_scripts() { #ifdef GD_MONO_HOT_RELOAD if (is_assembly_reloading_needed()) { - GD_MONO_SCOPE_THREAD_ATTACH; reload_assemblies(false); } #endif @@ -738,7 +737,6 @@ void CSharpLanguage::reload_tool_script(const Ref