Mono: Better handling of missing/outdated API assemblies
Remove the old API assembly invalidation system. It's pretty simple since now the editor has a hard dependency on the API assemblies and SCons takes care of prebuilding them. If we fail to load a project's API assembly because it was either missing or outdated, we just copy the prebuilt assemblies to the project and try again. We also do this when creating the solution and before building, just in case the user removed them from the disk after they were loaded. This way the API assemblies will be always loaded successfully. If they are not, it's a bug. Also fixed: - EditorDef was behaving like GlobalDef in GodotTools. - NullReferenceException because we can't serialize System.WeakReference yet. Use Godot.WeakRef in the mean time.
This commit is contained in:
parent
4061e132ff
commit
e59ac40712
@ -114,15 +114,20 @@ void CSharpLanguage::init() {
|
||||
gdmono = memnew(GDMono);
|
||||
gdmono->initialize();
|
||||
|
||||
#ifndef MONO_GLUE_ENABLED
|
||||
WARN_PRINT("This binary is built with `mono_glue=no` and cannot be used for scripting");
|
||||
#endif
|
||||
|
||||
#if defined(TOOLS_ENABLED) && defined(DEBUG_METHODS_ENABLED)
|
||||
// Generate bindings here, before loading assemblies. `initialize_load_assemblies` aborts
|
||||
// the applications if the api assemblies or the main tools assembly is missing, but this
|
||||
// is not a problem for BindingsGenerator as it only needs the tools project editor assembly.
|
||||
List<String> cmdline_args = OS::get_singleton()->get_cmdline_args();
|
||||
BindingsGenerator::handle_cmdline_args(cmdline_args);
|
||||
#endif
|
||||
|
||||
#ifndef MONO_GLUE_ENABLED
|
||||
print_line("Run this binary with `--generate-mono-glue path/to/modules/mono/glue`");
|
||||
#endif
|
||||
|
||||
gdmono->initialize_load_assemblies();
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
EditorNode::add_init_callback(&_editor_init_callback);
|
||||
|
||||
@ -710,14 +715,6 @@ bool CSharpLanguage::is_assembly_reloading_needed() {
|
||||
return false; // No assembly to load
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (!gdmono->get_core_api_assembly() && gdmono->metadata_is_api_assembly_invalidated(APIAssembly::API_CORE))
|
||||
return false; // The core API assembly to load is invalidated
|
||||
|
||||
if (!gdmono->get_editor_api_assembly() && gdmono->metadata_is_api_assembly_invalidated(APIAssembly::API_EDITOR))
|
||||
return false; // The editor API assembly to load is invalidated
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -193,134 +193,14 @@ namespace GodotTools
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool CopyApiAssembly(string srcDir, string dstDir, string assemblyName, ApiAssemblyType apiType)
|
||||
{
|
||||
// Create destination directory if needed
|
||||
if (!Directory.Exists(dstDir))
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(dstDir);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
ShowBuildErrorDialog($"Failed to create destination directory for the API assemblies. Exception message: {e.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
string assemblyFile = assemblyName + ".dll";
|
||||
string assemblySrc = Path.Combine(srcDir, assemblyFile);
|
||||
string assemblyDst = Path.Combine(dstDir, assemblyFile);
|
||||
|
||||
if (!File.Exists(assemblyDst) || File.GetLastWriteTime(assemblySrc) > File.GetLastWriteTime(assemblyDst) ||
|
||||
Internal.MetadataIsApiAssemblyInvalidated(apiType))
|
||||
{
|
||||
string xmlFile = $"{assemblyName}.xml";
|
||||
string pdbFile = $"{assemblyName}.pdb";
|
||||
|
||||
try
|
||||
{
|
||||
File.Copy(Path.Combine(srcDir, xmlFile), Path.Combine(dstDir, xmlFile));
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
Godot.GD.PushWarning(e.ToString());
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
File.Copy(Path.Combine(srcDir, pdbFile), Path.Combine(dstDir, pdbFile));
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
Godot.GD.PushWarning(e.ToString());
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
File.Copy(assemblySrc, assemblyDst);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
ShowBuildErrorDialog($"Failed to copy {assemblyFile}. Exception message: {e.Message}");
|
||||
return false;
|
||||
}
|
||||
|
||||
Internal.MetadataSetApiAssemblyInvalidated(apiType, false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool MakeApiAssembly(ApiAssemblyType apiType, string config)
|
||||
{
|
||||
string apiName = apiType == ApiAssemblyType.Core ? ApiAssemblyNames.Core : ApiAssemblyNames.Editor;
|
||||
|
||||
string editorPrebuiltApiDir = Path.Combine(GodotSharpDirs.DataEditorPrebuiltApiDir, config);
|
||||
string resAssembliesDir = Path.Combine(GodotSharpDirs.ResAssembliesBaseDir, config);
|
||||
|
||||
if (File.Exists(Path.Combine(editorPrebuiltApiDir, $"{apiName}.dll")))
|
||||
{
|
||||
using (var copyProgress = new EditorProgress("mono_copy_prebuilt_api_assembly", $"Copying prebuilt {apiName} assembly...", 1))
|
||||
{
|
||||
copyProgress.Step($"Copying {apiName} assembly", 0);
|
||||
return CopyApiAssembly(editorPrebuiltApiDir, resAssembliesDir, apiName, apiType);
|
||||
}
|
||||
}
|
||||
|
||||
const string apiSolutionName = ApiAssemblyNames.SolutionName;
|
||||
|
||||
using (var pr = new EditorProgress($"mono_build_release_{apiSolutionName}", $"Building {apiSolutionName} solution...", 3))
|
||||
{
|
||||
pr.Step($"Generating {apiSolutionName} solution", 0);
|
||||
|
||||
string apiSlnDir = Path.Combine(GodotSharpDirs.MonoSolutionsDir, _ApiFolderName(ApiAssemblyType.Core));
|
||||
string apiSlnFile = Path.Combine(apiSlnDir, $"{apiSolutionName}.sln");
|
||||
|
||||
if (!Directory.Exists(apiSlnDir) || !File.Exists(apiSlnFile))
|
||||
{
|
||||
var bindingsGenerator = new BindingsGenerator();
|
||||
|
||||
if (!Godot.OS.IsStdoutVerbose())
|
||||
bindingsGenerator.LogPrintEnabled = false;
|
||||
|
||||
Error err = bindingsGenerator.GenerateCsApi(apiSlnDir);
|
||||
if (err != Error.Ok)
|
||||
{
|
||||
ShowBuildErrorDialog($"Failed to generate {apiSolutionName} solution. Error: {err}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
pr.Step($"Building {apiSolutionName} solution", 1);
|
||||
|
||||
if (!BuildApiSolution(apiSlnDir, config))
|
||||
return false;
|
||||
|
||||
pr.Step($"Copying {apiName} assembly", 2);
|
||||
|
||||
// Copy the built assembly to the assemblies directory
|
||||
string apiAssemblyDir = Path.Combine(apiSlnDir, apiName, "bin", config);
|
||||
if (!CopyApiAssembly(apiAssemblyDir, resAssembliesDir, apiName, apiType))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool BuildProjectBlocking(string config, IEnumerable<string> godotDefines)
|
||||
{
|
||||
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
|
||||
return true; // No solution to build
|
||||
|
||||
string apiConfig = config == "Release" ? "Release" : "Debug";
|
||||
|
||||
if (!MakeApiAssembly(ApiAssemblyType.Core, apiConfig))
|
||||
return false;
|
||||
|
||||
if (!MakeApiAssembly(ApiAssemblyType.Editor, apiConfig))
|
||||
return false;
|
||||
// Make sure to update the API assemblies if they happen to be missing. Just in
|
||||
// case the user decided to delete them at some point after they were loaded.
|
||||
Internal.UpdateApiAssembliesFromPrebuilt();
|
||||
|
||||
using (var pr = new EditorProgress("mono_project_debug_build", "Building project solution...", 1))
|
||||
{
|
||||
|
@ -27,7 +27,7 @@ namespace GodotTools
|
||||
private MonoDevelopInstance monoDevelopInstance;
|
||||
private MonoDevelopInstance visualStudioForMacInstance;
|
||||
|
||||
private WeakReference<GodotSharpExport> exportPluginWeak;
|
||||
private WeakRef exportPluginWeak; // TODO Use WeakReference once we have proper serialization
|
||||
|
||||
public MonoBottomPanel MonoBottomPanel { get; private set; }
|
||||
|
||||
@ -72,13 +72,9 @@ namespace GodotTools
|
||||
return false;
|
||||
}
|
||||
|
||||
string apiConfig = "Debug";
|
||||
|
||||
if (!GodotSharpBuilds.MakeApiAssembly(ApiAssemblyType.Core, apiConfig))
|
||||
return false;
|
||||
|
||||
if (!GodotSharpBuilds.MakeApiAssembly(ApiAssemblyType.Editor, apiConfig))
|
||||
return false;
|
||||
// Make sure to update the API assemblies if they happen to be missing. Just in
|
||||
// case the user decided to delete them at some point after they were loaded.
|
||||
Internal.UpdateApiAssembliesFromPrebuilt();
|
||||
|
||||
pr.Step("Done".TTR());
|
||||
|
||||
@ -94,70 +90,6 @@ namespace GodotTools
|
||||
}
|
||||
}
|
||||
|
||||
private static int _makeApiSolutionsAttempts = 100;
|
||||
private static bool _makeApiSolutionsRecursionGuard = false;
|
||||
|
||||
private void _MakeApiSolutionsIfNeeded()
|
||||
{
|
||||
// I'm sick entirely of ProgressDialog
|
||||
|
||||
if (Internal.IsMessageQueueFlushing() || Engine.GetMainLoop() == null)
|
||||
{
|
||||
if (_makeApiSolutionsAttempts == 0) // This better never happen or I swear...
|
||||
throw new TimeoutException();
|
||||
|
||||
if (Engine.GetMainLoop() != null)
|
||||
{
|
||||
if (!Engine.GetMainLoop().IsConnected("idle_frame", this, nameof(_MakeApiSolutionsIfNeeded)))
|
||||
Engine.GetMainLoop().Connect("idle_frame", this, nameof(_MakeApiSolutionsIfNeeded));
|
||||
}
|
||||
else
|
||||
{
|
||||
CallDeferred(nameof(_MakeApiSolutionsIfNeededImpl));
|
||||
}
|
||||
|
||||
_makeApiSolutionsAttempts--;
|
||||
return;
|
||||
}
|
||||
|
||||
// Recursion guard needed because signals don't play well with ProgressDialog either, but unlike
|
||||
// the message queue, with signals the collateral damage should be minimal in the worst case.
|
||||
if (!_makeApiSolutionsRecursionGuard)
|
||||
{
|
||||
_makeApiSolutionsRecursionGuard = true;
|
||||
|
||||
// Oneshot signals don't play well with ProgressDialog either, so we do it this way instead
|
||||
if (Engine.GetMainLoop().IsConnected("idle_frame", this, nameof(_MakeApiSolutionsIfNeeded)))
|
||||
Engine.GetMainLoop().Disconnect("idle_frame", this, nameof(_MakeApiSolutionsIfNeeded));
|
||||
|
||||
_MakeApiSolutionsIfNeededImpl();
|
||||
|
||||
_makeApiSolutionsRecursionGuard = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void _MakeApiSolutionsIfNeededImpl()
|
||||
{
|
||||
// If the project has a solution and C# project make sure the API assemblies are present and up to date
|
||||
|
||||
string api_config = "Debug";
|
||||
string resAssembliesDir = Path.Combine(GodotSharpDirs.ResAssembliesBaseDir, api_config);
|
||||
|
||||
if (!File.Exists(Path.Combine(resAssembliesDir, $"{ApiAssemblyNames.Core}.dll")) ||
|
||||
Internal.MetadataIsApiAssemblyInvalidated(ApiAssemblyType.Core))
|
||||
{
|
||||
if (!GodotSharpBuilds.MakeApiAssembly(ApiAssemblyType.Core, api_config))
|
||||
return;
|
||||
}
|
||||
|
||||
if (!File.Exists(Path.Combine(resAssembliesDir, $"{ApiAssemblyNames.Editor}.dll")) ||
|
||||
Internal.MetadataIsApiAssemblyInvalidated(ApiAssemblyType.Editor))
|
||||
{
|
||||
if (!GodotSharpBuilds.MakeApiAssembly(ApiAssemblyType.Editor, api_config))
|
||||
return; // Redundant? I don't think so!
|
||||
}
|
||||
}
|
||||
|
||||
private void _RemoveCreateSlnMenuOption()
|
||||
{
|
||||
menuPopup.RemoveItem(menuPopup.GetItemIndex((int) MenuOptions.CreateSln));
|
||||
@ -465,9 +397,6 @@ namespace GodotTools
|
||||
|
||||
if (File.Exists(GodotSharpDirs.ProjectSlnPath) && File.Exists(GodotSharpDirs.ProjectCsProjPath))
|
||||
{
|
||||
// Defer this task because EditorProgress calls Main::iterarion() and the main loop is not yet initialized.
|
||||
CallDeferred(nameof(_MakeApiSolutionsIfNeeded));
|
||||
|
||||
// Make sure the existing project has Api assembly references configured correctly
|
||||
CSharpProject.FixApiHintPath(GodotSharpDirs.ProjectCsProjPath);
|
||||
}
|
||||
@ -521,7 +450,7 @@ namespace GodotTools
|
||||
// Export plugin
|
||||
var exportPlugin = new GodotSharpExport();
|
||||
AddExportPlugin(exportPlugin);
|
||||
exportPluginWeak = new WeakReference<GodotSharpExport>(exportPlugin);
|
||||
exportPluginWeak = WeakRef(exportPlugin);
|
||||
|
||||
GodotSharpBuilds.Initialize();
|
||||
}
|
||||
@ -530,13 +459,15 @@ namespace GodotTools
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (exportPluginWeak.TryGetTarget(out var exportPlugin))
|
||||
if (exportPluginWeak != null)
|
||||
{
|
||||
// We need to dispose our export plugin before the editor destroys EditorSettings.
|
||||
// Otherwise, if the GC disposes it at a later time, EditorExportPlatformAndroid
|
||||
// will be freed after EditorSettings already was, and its device polling thread
|
||||
// will try to access the EditorSettings singleton, resulting in null dereferencing.
|
||||
exportPlugin.Dispose();
|
||||
(exportPluginWeak.GetRef() as GodotSharpExport)?.Dispose();
|
||||
|
||||
exportPluginWeak.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,9 @@ namespace GodotTools.Internals
|
||||
public const string CSharpLanguageType = "CSharpScript";
|
||||
public const string CSharpLanguageExtension = "cs";
|
||||
|
||||
public static string UpdateApiAssembliesFromPrebuilt() =>
|
||||
internal_UpdateApiAssembliesFromPrebuilt();
|
||||
|
||||
public static string FullTemplatesDir =>
|
||||
internal_FullTemplatesDir();
|
||||
|
||||
@ -17,14 +20,6 @@ namespace GodotTools.Internals
|
||||
|
||||
public static bool IsOsxAppBundleInstalled(string bundleId) => internal_IsOsxAppBundleInstalled(bundleId);
|
||||
|
||||
public static bool MetadataIsApiAssemblyInvalidated(ApiAssemblyType apiType) =>
|
||||
internal_MetadataIsApiAssemblyInvalidated(apiType);
|
||||
|
||||
public static void MetadataSetApiAssemblyInvalidated(ApiAssemblyType apiType, bool invalidated) =>
|
||||
internal_MetadataSetApiAssemblyInvalidated(apiType, invalidated);
|
||||
|
||||
public static bool IsMessageQueueFlushing() => internal_IsMessageQueueFlushing();
|
||||
|
||||
public static bool GodotIs32Bits() => internal_GodotIs32Bits();
|
||||
|
||||
public static bool GodotIsRealTDouble() => internal_GodotIsRealTDouble();
|
||||
@ -53,6 +48,9 @@ namespace GodotTools.Internals
|
||||
|
||||
// Internal Calls
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern string internal_UpdateApiAssembliesFromPrebuilt();
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern string internal_FullTemplatesDir();
|
||||
|
||||
@ -62,15 +60,6 @@ namespace GodotTools.Internals
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern bool internal_IsOsxAppBundleInstalled(string bundleId);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern bool internal_MetadataIsApiAssemblyInvalidated(ApiAssemblyType apiType);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern void internal_MetadataSetApiAssemblyInvalidated(ApiAssemblyType apiType, bool invalidated);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern bool internal_IsMessageQueueFlushing();
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
private static extern bool internal_GodotIs32Bits();
|
||||
|
||||
|
@ -30,7 +30,6 @@
|
||||
|
||||
#include "editor_internal_calls.h"
|
||||
|
||||
#include "core/message_queue.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/version.h"
|
||||
#include "editor/editor_node.h"
|
||||
@ -245,7 +244,7 @@ MonoObject *godot_icall_Globals_GlobalDef(MonoString *p_setting, MonoObject *p_d
|
||||
MonoObject *godot_icall_Globals_EditorDef(MonoString *p_setting, MonoObject *p_default_value, MonoBoolean p_restart_if_changed) {
|
||||
String setting = GDMonoMarshal::mono_string_to_godot(p_setting);
|
||||
Variant default_value = GDMonoMarshal::mono_object_to_variant(p_default_value);
|
||||
Variant result = _GLOBAL_DEF(setting, default_value, (bool)p_restart_if_changed);
|
||||
Variant result = _EDITOR_DEF(setting, default_value, (bool)p_restart_if_changed);
|
||||
return GDMonoMarshal::variant_to_mono_object(result);
|
||||
}
|
||||
|
||||
@ -254,6 +253,11 @@ MonoString *godot_icall_Globals_TTR(MonoString *p_text) {
|
||||
return GDMonoMarshal::mono_string_from_godot(TTR(text));
|
||||
}
|
||||
|
||||
MonoString *godot_icall_Internal_UpdateApiAssembliesFromPrebuilt() {
|
||||
String error_str = GDMono::get_singleton()->update_api_assemblies_from_prebuilt();
|
||||
return GDMonoMarshal::mono_string_from_godot(error_str);
|
||||
}
|
||||
|
||||
MonoString *godot_icall_Internal_FullTemplatesDir() {
|
||||
String full_templates_dir = EditorSettings::get_singleton()->get_templates_dir().plus_file(VERSION_FULL_CONFIG);
|
||||
return GDMonoMarshal::mono_string_from_godot(full_templates_dir);
|
||||
@ -274,18 +278,6 @@ MonoBoolean godot_icall_Internal_IsOsxAppBundleInstalled(MonoString *p_bundle_id
|
||||
#endif
|
||||
}
|
||||
|
||||
MonoBoolean godot_icall_Internal_MetadataIsApiAssemblyInvalidated(int32_t p_api_type) {
|
||||
return GDMono::get_singleton()->metadata_is_api_assembly_invalidated((APIAssembly::Type)p_api_type);
|
||||
}
|
||||
|
||||
void godot_icall_Internal_MetadataSetApiAssemblyInvalidated(int32_t p_api_type, MonoBoolean p_invalidated) {
|
||||
GDMono::get_singleton()->metadata_set_api_assembly_invalidated((APIAssembly::Type)p_api_type, (bool)p_invalidated);
|
||||
}
|
||||
|
||||
MonoBoolean godot_icall_Internal_IsMessageQueueFlushing() {
|
||||
return (MonoBoolean)MessageQueue::get_singleton()->is_flushing();
|
||||
}
|
||||
|
||||
MonoBoolean godot_icall_Internal_GodotIs32Bits() {
|
||||
return sizeof(void *) == 4;
|
||||
}
|
||||
@ -407,12 +399,10 @@ void register_editor_internal_calls() {
|
||||
mono_add_internal_call("GodotTools.GodotSharpExport::internal_GetExportedAssemblyDependencies", (void *)godot_icall_GodotSharpExport_GetExportedAssemblyDependencies);
|
||||
|
||||
// Internals
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_UpdateApiAssembliesFromPrebuilt", (void *)godot_icall_Internal_UpdateApiAssembliesFromPrebuilt);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_FullTemplatesDir", (void *)godot_icall_Internal_FullTemplatesDir);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_SimplifyGodotPath", (void *)godot_icall_Internal_SimplifyGodotPath);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_IsOsxAppBundleInstalled", (void *)godot_icall_Internal_IsOsxAppBundleInstalled);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_MetadataIsApiAssemblyInvalidated", (void *)godot_icall_Internal_MetadataIsApiAssemblyInvalidated);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_MetadataSetApiAssemblyInvalidated", (void *)godot_icall_Internal_MetadataSetApiAssemblyInvalidated);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_IsMessageQueueFlushing", (void *)godot_icall_Internal_IsMessageQueueFlushing);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_GodotIs32Bits", (void *)godot_icall_Internal_GodotIs32Bits);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_GodotIsRealTDouble", (void *)godot_icall_Internal_GodotIsRealTDouble);
|
||||
mono_add_internal_call("GodotTools.Internals.Internal::internal_GodotMainIteration", (void *)godot_icall_Internal_GodotMainIteration);
|
||||
|
@ -59,10 +59,6 @@
|
||||
#include "android_mono_config.gen.h"
|
||||
#endif
|
||||
|
||||
#define OUT_OF_SYNC_ERR_MESSAGE(m_assembly_name) "The assembly '" m_assembly_name "' is out of sync. " \
|
||||
"This error is expected if you just upgraded to a newer Godot version. " \
|
||||
"Building the project will update the assembly to the correct version."
|
||||
|
||||
GDMono *GDMono::singleton = NULL;
|
||||
|
||||
namespace {
|
||||
@ -304,26 +300,9 @@ void GDMono::initialize() {
|
||||
mono_install_unhandled_exception_hook(&unhandled_exception_hook, NULL);
|
||||
|
||||
#ifndef TOOLS_ENABLED
|
||||
if (!DirAccess::exists("res://.mono")) {
|
||||
// 'res://.mono/' is missing so there is nothing to load. We don't need to initialize mono, but
|
||||
// we still do so unless mscorlib is missing (which is the case for projects that don't use C#).
|
||||
|
||||
String mscorlib_fname("mscorlib.dll");
|
||||
|
||||
Vector<String> search_dirs;
|
||||
GDMonoAssembly::fill_search_dirs(search_dirs);
|
||||
|
||||
bool found = false;
|
||||
for (int i = 0; i < search_dirs.size(); i++) {
|
||||
if (FileAccess::exists(search_dirs[i].plus_file(mscorlib_fname))) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return; // mscorlib is missing, do not initialize mono
|
||||
}
|
||||
// Export templates only load the Mono runtime if the project uses it
|
||||
if (!DirAccess::exists("res://.mono"))
|
||||
return;
|
||||
#endif
|
||||
|
||||
root_domain = mono_jit_init_version("GodotEngine.RootDomain", "v4.0.30319");
|
||||
@ -354,63 +333,48 @@ void GDMono::initialize() {
|
||||
|
||||
_register_internal_calls();
|
||||
|
||||
// The following assemblies are not required at initialization
|
||||
#ifdef MONO_GLUE_ENABLED
|
||||
if (_load_api_assemblies()) {
|
||||
// Everything is fine with the api assemblies, load the tools and project assemblies
|
||||
|
||||
#if defined(TOOLS_ENABLED)
|
||||
ERR_EXPLAIN("Mono: Failed to load GodotTools assemblies");
|
||||
ERR_FAIL_COND(!_load_tools_assemblies());
|
||||
#endif
|
||||
|
||||
_load_project_assembly();
|
||||
|
||||
} else {
|
||||
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
|
||||
// metadata, so we invalidate the version in the metadata and unload the script domain.
|
||||
|
||||
if (core_api_assembly_out_of_sync) {
|
||||
ERR_PRINT(OUT_OF_SYNC_ERR_MESSAGE(CORE_API_ASSEMBLY_NAME));
|
||||
metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true);
|
||||
} else if (!GDMonoUtils::mono_cache.godot_api_cache_updated) {
|
||||
ERR_PRINT("The loaded assembly '" CORE_API_ASSEMBLY_NAME "' is in sync, but the cache update failed");
|
||||
metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true);
|
||||
}
|
||||
|
||||
if (editor_api_assembly_out_of_sync) {
|
||||
ERR_PRINT(OUT_OF_SYNC_ERR_MESSAGE(EDITOR_API_ASSEMBLY_NAME));
|
||||
metadata_set_api_assembly_invalidated(APIAssembly::API_EDITOR, true);
|
||||
}
|
||||
|
||||
print_line("Mono: Proceeding to unload scripts domain because of invalid API assemblies.");
|
||||
|
||||
Error err = _unload_scripts_domain();
|
||||
if (err != OK) {
|
||||
WARN_PRINT("Mono: Failed to unload scripts domain");
|
||||
}
|
||||
#else
|
||||
ERR_PRINT("The loaded API assembly is invalid");
|
||||
CRASH_NOW();
|
||||
#endif // TOOLS_ENABLED
|
||||
}
|
||||
}
|
||||
#else
|
||||
print_verbose("Mono: Glue disabled, ignoring script assemblies.");
|
||||
#endif // MONO_GLUE_ENABLED
|
||||
|
||||
print_verbose("Mono: INITIALIZED");
|
||||
}
|
||||
|
||||
#ifdef MONO_GLUE_ENABLED
|
||||
void GDMono::initialize_load_assemblies() {
|
||||
|
||||
#ifndef MONO_GLUE_ENABLED
|
||||
ERR_EXPLAIN("Mono: This binary was built with `mono_glue=no`; cannot load assemblies");
|
||||
CRASH_NOW();
|
||||
#endif
|
||||
|
||||
// Load assemblies. The API and tools assemblies are required,
|
||||
// the application is aborted if these assemblies cannot be loaded.
|
||||
|
||||
_load_api_assemblies();
|
||||
|
||||
#if defined(TOOLS_ENABLED)
|
||||
if (!_load_tools_assemblies()) {
|
||||
ERR_EXPLAIN("Mono: Failed to load GodotTools assemblies");
|
||||
CRASH_NOW();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Load the project's main assembly. This doesn't necessarily need to succeed.
|
||||
// The game may not be using .NET at all, or if the project does use .NET and
|
||||
// we're running in the editor, it may just happen to be it wasn't built yet.
|
||||
if (!_load_project_assembly()) {
|
||||
if (OS::get_singleton()->is_stdout_verbose())
|
||||
print_error("Mono: Failed to load project assembly");
|
||||
}
|
||||
}
|
||||
|
||||
bool GDMono::_are_api_assemblies_out_of_sync() {
|
||||
bool out_of_sync = core_api_assembly && (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated);
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (!out_of_sync)
|
||||
out_of_sync = editor_api_assembly && editor_api_assembly_out_of_sync;
|
||||
#endif
|
||||
return out_of_sync;
|
||||
}
|
||||
|
||||
namespace GodotSharpBindings {
|
||||
#ifdef MONO_GLUE_ENABLED
|
||||
|
||||
uint64_t get_core_api_hash();
|
||||
#ifdef TOOLS_ENABLED
|
||||
@ -419,13 +383,33 @@ uint64_t get_editor_api_hash();
|
||||
uint32_t get_bindings_version();
|
||||
|
||||
void register_generated_icalls();
|
||||
} // namespace GodotSharpBindings
|
||||
|
||||
#else
|
||||
|
||||
uint64_t get_core_api_hash() {
|
||||
CRASH_NOW();
|
||||
GD_UNREACHABLE();
|
||||
}
|
||||
#ifdef TOOLS_ENABLED
|
||||
uint64_t get_editor_api_hash() {
|
||||
CRASH_NOW();
|
||||
GD_UNREACHABLE();
|
||||
}
|
||||
#endif
|
||||
uint32_t get_bindings_version() {
|
||||
CRASH_NOW();
|
||||
GD_UNREACHABLE();
|
||||
}
|
||||
|
||||
void register_generated_icalls() {
|
||||
/* Fine, just do nothing */
|
||||
}
|
||||
|
||||
#endif // MONO_GLUE_ENABLED
|
||||
} // namespace GodotSharpBindings
|
||||
|
||||
void GDMono::_register_internal_calls() {
|
||||
#ifdef MONO_GLUE_ENABLED
|
||||
GodotSharpBindings::register_generated_icalls();
|
||||
#endif
|
||||
}
|
||||
|
||||
void GDMono::_initialize_and_check_api_hashes() {
|
||||
@ -565,12 +549,21 @@ bool GDMono::_load_corlib_assembly() {
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
static bool copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name, APIAssembly::Type p_api_type) {
|
||||
bool GDMono::copy_prebuilt_api_assembly(APIAssembly::Type p_api_type) {
|
||||
|
||||
bool &api_assembly_out_of_sync = (p_api_type == APIAssembly::API_CORE) ?
|
||||
GDMono::get_singleton()->core_api_assembly_out_of_sync :
|
||||
GDMono::get_singleton()->editor_api_assembly_out_of_sync;
|
||||
|
||||
String src_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file("Debug");
|
||||
String dst_dir = GodotSharpDirs::get_res_assemblies_dir();
|
||||
|
||||
String assembly_name = p_api_type == APIAssembly::API_CORE ? CORE_API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME;
|
||||
|
||||
// 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);
|
||||
if (!DirAccess::exists(dst_dir)) {
|
||||
DirAccess *da = DirAccess::create_for_path(dst_dir);
|
||||
Error err = da->make_dir_recursive(dst_dir);
|
||||
memdelete(da);
|
||||
|
||||
if (err != OK) {
|
||||
@ -579,21 +572,19 @@ static bool copy_api_assembly(const String &p_src_dir, const String &p_dst_dir,
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
String assembly_file = assembly_name + ".dll";
|
||||
String assembly_src = src_dir.plus_file(assembly_file);
|
||||
String assembly_dst = dst_dir.plus_file(assembly_file);
|
||||
|
||||
if (!FileAccess::exists(assembly_dst) ||
|
||||
FileAccess::get_modified_time(assembly_src) > FileAccess::get_modified_time(assembly_dst) ||
|
||||
GDMono::get_singleton()->metadata_is_api_assembly_invalidated(p_api_type)) {
|
||||
if (!FileAccess::exists(assembly_dst) || api_assembly_out_of_sync) {
|
||||
DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
||||
|
||||
String xml_file = p_assembly_name + ".xml";
|
||||
if (da->copy(p_src_dir.plus_file(xml_file), p_dst_dir.plus_file(xml_file)) != OK)
|
||||
String xml_file = assembly_name + ".xml";
|
||||
if (da->copy(src_dir.plus_file(xml_file), dst_dir.plus_file(xml_file)) != OK)
|
||||
WARN_PRINTS("Failed to copy " + xml_file);
|
||||
|
||||
String pdb_file = p_assembly_name + ".pdb";
|
||||
if (da->copy(p_src_dir.plus_file(pdb_file), p_dst_dir.plus_file(pdb_file)) != OK)
|
||||
String pdb_file = assembly_name + ".pdb";
|
||||
if (da->copy(src_dir.plus_file(pdb_file), dst_dir.plus_file(pdb_file)) != OK)
|
||||
WARN_PRINTS("Failed to copy " + pdb_file);
|
||||
|
||||
Error err = da->copy(assembly_src, assembly_dst);
|
||||
@ -603,11 +594,46 @@ static bool copy_api_assembly(const String &p_src_dir, const String &p_dst_dir,
|
||||
return false;
|
||||
}
|
||||
|
||||
GDMono::get_singleton()->metadata_set_api_assembly_invalidated(p_api_type, false);
|
||||
api_assembly_out_of_sync = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
String GDMono::update_api_assemblies_from_prebuilt() {
|
||||
|
||||
#define FAIL_REASON(m_out_of_sync, m_prebuilt_exist) \
|
||||
( \
|
||||
(m_out_of_sync ? \
|
||||
String("The assembly is invalidated") : \
|
||||
String("The assembly was not found")) + \
|
||||
(m_prebuilt_exist ? \
|
||||
String(" and the prebuilt assemblies are missing") : \
|
||||
String(" and we failed to copy the prebuilt assemblies")))
|
||||
|
||||
bool api_assembly_out_of_sync = core_api_assembly_out_of_sync || editor_api_assembly_out_of_sync;
|
||||
|
||||
String core_assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(CORE_API_ASSEMBLY_NAME ".dll");
|
||||
String editor_assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
|
||||
|
||||
if (!api_assembly_out_of_sync && FileAccess::exists(core_assembly_path) && FileAccess::exists(editor_assembly_path))
|
||||
return String(); // No update needed
|
||||
|
||||
String prebuilt_api_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file("Debug");
|
||||
String prebuilt_core_dll_path = prebuilt_api_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll");
|
||||
String prebuilt_editor_dll_path = prebuilt_api_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
|
||||
|
||||
if (!FileAccess::exists(prebuilt_core_dll_path) || !FileAccess::exists(prebuilt_editor_dll_path))
|
||||
return FAIL_REASON(api_assembly_out_of_sync, /* prebuilt_exist: */ false);
|
||||
|
||||
// Copy the prebuilt Api
|
||||
if (!copy_prebuilt_api_assembly(APIAssembly::API_CORE) || !copy_prebuilt_api_assembly(APIAssembly::API_EDITOR))
|
||||
return FAIL_REASON(api_assembly_out_of_sync, /* prebuilt_exist: */ true);
|
||||
|
||||
return String(); // Updated successfully
|
||||
|
||||
#undef FAIL_REASON
|
||||
}
|
||||
#endif
|
||||
|
||||
bool GDMono::_load_core_api_assembly() {
|
||||
@ -616,35 +642,15 @@ bool GDMono::_load_core_api_assembly() {
|
||||
return true;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (metadata_is_api_assembly_invalidated(APIAssembly::API_CORE)) {
|
||||
String prebuilt_api_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file("Debug");
|
||||
String prebuilt_dll_path = prebuilt_api_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll");
|
||||
String invalidated_dll_path = get_invalidated_api_assembly_path(APIAssembly::API_CORE);
|
||||
|
||||
if (!FileAccess::exists(prebuilt_dll_path) ||
|
||||
FileAccess::get_modified_time(invalidated_dll_path) == FileAccess::get_modified_time(prebuilt_dll_path)) {
|
||||
print_verbose("Mono: Skipping loading of Core API assembly because it was invalidated");
|
||||
return false;
|
||||
} else {
|
||||
// Copy the prebuilt Api
|
||||
String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir();
|
||||
if (!copy_api_assembly(prebuilt_api_dir, res_assemblies_dir, CORE_API_ASSEMBLY_NAME, APIAssembly::API_CORE) ||
|
||||
!copy_api_assembly(prebuilt_api_dir, res_assemblies_dir, EDITOR_API_ASSEMBLY_NAME, APIAssembly::API_EDITOR)) {
|
||||
print_verbose("Mono: Failed to copy prebuilt API. Skipping loading of Core API assembly because it was invalidated");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// For the editor and the editor player we want to load it from a specific path to make sure we can keep it up to date
|
||||
String assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(CORE_API_ASSEMBLY_NAME ".dll");
|
||||
bool success = FileAccess::exists(assembly_path) &&
|
||||
load_assembly_from(CORE_API_ASSEMBLY_NAME, assembly_path, &core_api_assembly);
|
||||
#else
|
||||
bool success = load_assembly(CORE_API_ASSEMBLY_NAME, &core_api_assembly);
|
||||
#endif
|
||||
|
||||
String assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(CORE_API_ASSEMBLY_NAME ".dll");
|
||||
|
||||
bool success = (FileAccess::exists(assembly_path) &&
|
||||
load_assembly_from(CORE_API_ASSEMBLY_NAME, assembly_path, &core_api_assembly)) ||
|
||||
load_assembly(CORE_API_ASSEMBLY_NAME, &core_api_assembly);
|
||||
|
||||
if (success) {
|
||||
#ifdef MONO_GLUE_ENABLED
|
||||
APIAssembly::Version api_assembly_ver = APIAssembly::Version::get_from_loaded_assembly(core_api_assembly, APIAssembly::API_CORE);
|
||||
core_api_assembly_out_of_sync = GodotSharpBindings::get_core_api_hash() != api_assembly_ver.godot_api_hash ||
|
||||
GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version ||
|
||||
@ -654,9 +660,8 @@ bool GDMono::_load_core_api_assembly() {
|
||||
|
||||
_install_trace_listener();
|
||||
}
|
||||
#else
|
||||
GDMonoUtils::update_godot_api_cache();
|
||||
#endif
|
||||
} else {
|
||||
core_api_assembly_out_of_sync = false;
|
||||
}
|
||||
|
||||
return success;
|
||||
@ -668,44 +673,100 @@ bool GDMono::_load_editor_api_assembly() {
|
||||
if (editor_api_assembly)
|
||||
return true;
|
||||
|
||||
if (metadata_is_api_assembly_invalidated(APIAssembly::API_EDITOR)) {
|
||||
String prebuilt_api_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file("Debug");
|
||||
String prebuilt_dll_path = prebuilt_api_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
|
||||
String invalidated_dll_path = get_invalidated_api_assembly_path(APIAssembly::API_EDITOR);
|
||||
|
||||
if (!FileAccess::exists(prebuilt_dll_path) ||
|
||||
FileAccess::get_modified_time(invalidated_dll_path) == FileAccess::get_modified_time(prebuilt_dll_path)) {
|
||||
print_verbose("Mono: Skipping loading of Editor API assembly because it was invalidated");
|
||||
return false;
|
||||
} else {
|
||||
// Copy the prebuilt editor Api (no need to copy the core api if we got to this point)
|
||||
String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir();
|
||||
if (!copy_api_assembly(prebuilt_api_dir, res_assemblies_dir, EDITOR_API_ASSEMBLY_NAME, APIAssembly::API_EDITOR)) {
|
||||
print_verbose("Mono: Failed to copy prebuilt API. Skipping loading of Editor API assembly because it was invalidated");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For the editor and the editor player we want to load it from a specific path to make sure we can keep it up to date
|
||||
String assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
|
||||
|
||||
bool success = (FileAccess::exists(assembly_path) &&
|
||||
load_assembly_from(EDITOR_API_ASSEMBLY_NAME, assembly_path, &editor_api_assembly)) ||
|
||||
load_assembly(EDITOR_API_ASSEMBLY_NAME, &editor_api_assembly);
|
||||
bool success = FileAccess::exists(assembly_path) &&
|
||||
load_assembly_from(EDITOR_API_ASSEMBLY_NAME, assembly_path, &editor_api_assembly);
|
||||
|
||||
if (success) {
|
||||
#ifdef MONO_GLUE_ENABLED
|
||||
APIAssembly::Version api_assembly_ver = APIAssembly::Version::get_from_loaded_assembly(editor_api_assembly, APIAssembly::API_EDITOR);
|
||||
editor_api_assembly_out_of_sync = GodotSharpBindings::get_editor_api_hash() != api_assembly_ver.godot_api_hash ||
|
||||
GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version ||
|
||||
CS_GLUE_VERSION != api_assembly_ver.cs_glue_version;
|
||||
#endif
|
||||
} else {
|
||||
editor_api_assembly_out_of_sync = false;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool GDMono::_try_load_api_assemblies() {
|
||||
|
||||
if (!_load_core_api_assembly()) {
|
||||
if (OS::get_singleton()->is_stdout_verbose())
|
||||
print_error("Mono: Failed to load Core API assembly");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated)
|
||||
return false;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (!_load_editor_api_assembly()) {
|
||||
if (OS::get_singleton()->is_stdout_verbose())
|
||||
print_error("Mono: Failed to load Editor API assembly");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (editor_api_assembly_out_of_sync)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GDMono::_load_api_assemblies() {
|
||||
|
||||
if (!_try_load_api_assemblies()) {
|
||||
// The API assemblies are out of sync. Fine, try one more time, but this time
|
||||
// update them from the prebuilt assemblies directory before trying to load them.
|
||||
|
||||
// 1. Unload the scripts domain
|
||||
if (_unload_scripts_domain() != OK) {
|
||||
ERR_EXPLAIN("Mono: Failed to unload scripts domain");
|
||||
CRASH_NOW();
|
||||
}
|
||||
|
||||
// 2. Update the API assemblies
|
||||
String update_error = update_api_assemblies_from_prebuilt();
|
||||
if (!update_error.empty()) {
|
||||
ERR_EXPLAIN(update_error);
|
||||
CRASH_NOW();
|
||||
}
|
||||
|
||||
// 3. Load the scripts domain again
|
||||
if (_load_scripts_domain() != OK) {
|
||||
ERR_EXPLAIN("Mono: Failed to load scripts domain");
|
||||
CRASH_NOW();
|
||||
}
|
||||
|
||||
// 4. Try loading the updated assemblies
|
||||
if (!_try_load_api_assemblies()) {
|
||||
// welp... too bad
|
||||
|
||||
if (_are_api_assemblies_out_of_sync()) {
|
||||
if (core_api_assembly_out_of_sync) {
|
||||
ERR_PRINT("The assembly '" CORE_API_ASSEMBLY_NAME "' is out of sync");
|
||||
} else if (!GDMonoUtils::mono_cache.godot_api_cache_updated) {
|
||||
ERR_PRINT("The loaded assembly '" CORE_API_ASSEMBLY_NAME "' is in sync, but the cache update failed");
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (editor_api_assembly_out_of_sync) {
|
||||
ERR_PRINT("The assembly '" EDITOR_API_ASSEMBLY_NAME "' is out of sync");
|
||||
}
|
||||
#endif
|
||||
|
||||
CRASH_NOW();
|
||||
} else {
|
||||
ERR_EXPLAIN("Failed to load one of the API assemblies");
|
||||
CRASH_NOW();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
bool GDMono::_load_tools_assemblies() {
|
||||
|
||||
@ -734,39 +795,11 @@ bool GDMono::_load_project_assembly() {
|
||||
|
||||
if (success) {
|
||||
mono_assembly_set_main(project_assembly->get_assembly());
|
||||
} else {
|
||||
if (OS::get_singleton()->is_stdout_verbose())
|
||||
print_error("Mono: Failed to load project assembly");
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool GDMono::_load_api_assemblies() {
|
||||
|
||||
if (!_load_core_api_assembly()) {
|
||||
if (OS::get_singleton()->is_stdout_verbose())
|
||||
print_error("Mono: Failed to load Core API assembly");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated)
|
||||
return false;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (!_load_editor_api_assembly()) {
|
||||
if (OS::get_singleton()->is_stdout_verbose())
|
||||
print_error("Mono: Failed to load Editor API assembly");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (editor_api_assembly_out_of_sync)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GDMono::_install_trace_listener() {
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
@ -784,78 +817,6 @@ void GDMono::_install_trace_listener() {
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
String GDMono::_get_api_assembly_metadata_path() {
|
||||
|
||||
return GodotSharpDirs::get_res_metadata_dir().plus_file("api_assemblies.cfg");
|
||||
}
|
||||
|
||||
void GDMono::metadata_set_api_assembly_invalidated(APIAssembly::Type p_api_type, bool p_invalidated) {
|
||||
|
||||
String section = APIAssembly::to_string(p_api_type);
|
||||
String path = _get_api_assembly_metadata_path();
|
||||
|
||||
Ref<ConfigFile> metadata;
|
||||
metadata.instance();
|
||||
metadata->load(path);
|
||||
|
||||
metadata->set_value(section, "invalidated", p_invalidated);
|
||||
|
||||
String assembly_path = GodotSharpDirs::get_res_assemblies_dir()
|
||||
.plus_file(p_api_type == APIAssembly::API_CORE ?
|
||||
CORE_API_ASSEMBLY_NAME ".dll" :
|
||||
EDITOR_API_ASSEMBLY_NAME ".dll");
|
||||
|
||||
ERR_FAIL_COND(!FileAccess::exists(assembly_path));
|
||||
|
||||
uint64_t modified_time = FileAccess::get_modified_time(assembly_path);
|
||||
|
||||
metadata->set_value(section, "invalidated_asm_modified_time", String::num_uint64(modified_time));
|
||||
|
||||
String dir = path.get_base_dir();
|
||||
if (!DirAccess::exists(dir)) {
|
||||
DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
||||
ERR_FAIL_COND(!da);
|
||||
Error err = da->make_dir_recursive(ProjectSettings::get_singleton()->globalize_path(dir));
|
||||
ERR_FAIL_COND(err != OK);
|
||||
}
|
||||
|
||||
Error save_err = metadata->save(path);
|
||||
ERR_FAIL_COND(save_err != OK);
|
||||
}
|
||||
|
||||
bool GDMono::metadata_is_api_assembly_invalidated(APIAssembly::Type p_api_type) {
|
||||
|
||||
String section = APIAssembly::to_string(p_api_type);
|
||||
|
||||
Ref<ConfigFile> metadata;
|
||||
metadata.instance();
|
||||
metadata->load(_get_api_assembly_metadata_path());
|
||||
|
||||
String assembly_path = GodotSharpDirs::get_res_assemblies_dir()
|
||||
.plus_file(p_api_type == APIAssembly::API_CORE ?
|
||||
CORE_API_ASSEMBLY_NAME ".dll" :
|
||||
EDITOR_API_ASSEMBLY_NAME ".dll");
|
||||
|
||||
if (!FileAccess::exists(assembly_path))
|
||||
return false;
|
||||
|
||||
uint64_t modified_time = FileAccess::get_modified_time(assembly_path);
|
||||
|
||||
uint64_t stored_modified_time = metadata->get_value(section, "invalidated_asm_modified_time", 0);
|
||||
|
||||
return metadata->get_value(section, "invalidated", false) && modified_time <= stored_modified_time;
|
||||
}
|
||||
|
||||
String GDMono::get_invalidated_api_assembly_path(APIAssembly::Type p_api_type) {
|
||||
|
||||
return GodotSharpDirs::get_res_assemblies_dir()
|
||||
.plus_file(p_api_type == APIAssembly::API_CORE ?
|
||||
CORE_API_ASSEMBLY_NAME ".dll" :
|
||||
EDITOR_API_ASSEMBLY_NAME ".dll");
|
||||
}
|
||||
#endif
|
||||
|
||||
Error GDMono::_load_scripts_domain() {
|
||||
|
||||
ERR_FAIL_COND_V(scripts_domain != NULL, ERR_BUG);
|
||||
@ -903,11 +864,6 @@ Error GDMono::_unload_scripts_domain() {
|
||||
tools_project_editor_assembly = NULL;
|
||||
#endif
|
||||
|
||||
core_api_assembly_out_of_sync = false;
|
||||
#ifdef TOOLS_ENABLED
|
||||
editor_api_assembly_out_of_sync = false;
|
||||
#endif
|
||||
|
||||
MonoDomain *domain = scripts_domain;
|
||||
scripts_domain = NULL;
|
||||
|
||||
@ -944,57 +900,25 @@ Error GDMono::reload_scripts_domain() {
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef MONO_GLUE_ENABLED
|
||||
if (!_load_api_assemblies()) {
|
||||
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
|
||||
// metadata, so we invalidate the version in the metadata and unload the script domain.
|
||||
// Load assemblies. The API and tools assemblies are required,
|
||||
// the application is aborted if these assemblies cannot be loaded.
|
||||
|
||||
if (core_api_assembly_out_of_sync) {
|
||||
ERR_PRINT(OUT_OF_SYNC_ERR_MESSAGE(CORE_API_ASSEMBLY_NAME));
|
||||
metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true);
|
||||
} else if (!GDMonoUtils::mono_cache.godot_api_cache_updated) {
|
||||
ERR_PRINT("The loaded Core API assembly is in sync, but the cache update failed");
|
||||
metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true);
|
||||
}
|
||||
_load_api_assemblies();
|
||||
|
||||
if (editor_api_assembly_out_of_sync) {
|
||||
ERR_PRINT(OUT_OF_SYNC_ERR_MESSAGE(EDITOR_API_ASSEMBLY_NAME));
|
||||
metadata_set_api_assembly_invalidated(APIAssembly::API_EDITOR, true);
|
||||
}
|
||||
|
||||
err = _unload_scripts_domain();
|
||||
if (err != OK) {
|
||||
WARN_PRINT("Mono: Failed to unload scripts domain");
|
||||
}
|
||||
|
||||
return ERR_CANT_RESOLVE;
|
||||
#else
|
||||
ERR_PRINT("The loaded API assembly is invalid");
|
||||
CRASH_NOW();
|
||||
#endif
|
||||
} else {
|
||||
return ERR_CANT_OPEN;
|
||||
}
|
||||
#if defined(TOOLS_ENABLED)
|
||||
if (!_load_tools_assemblies()) {
|
||||
ERR_EXPLAIN("Mono: Failed to load GodotTools assemblies");
|
||||
CRASH_NOW();
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
ERR_EXPLAIN("Mono: Failed to load GodotTools assemblies");
|
||||
ERR_FAIL_COND_V(!_load_tools_assemblies(), ERR_CANT_OPEN);
|
||||
#endif
|
||||
|
||||
// Load the project's main assembly. Here, during hot-reloading, we do
|
||||
// consider failing to load the project's main assembly to be an error.
|
||||
// However, unlike the API and tools assemblies, the application can continue working.
|
||||
if (!_load_project_assembly()) {
|
||||
print_error("Mono: Failed to load project assembly");
|
||||
return ERR_CANT_OPEN;
|
||||
}
|
||||
#else
|
||||
print_verbose("Mono: Glue disabled, ignoring script assemblies.");
|
||||
#endif // MONO_GLUE_ENABLED
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
@ -104,6 +104,8 @@ class GDMono {
|
||||
|
||||
void _domain_assemblies_cleanup(uint32_t p_domain_id);
|
||||
|
||||
bool _are_api_assemblies_out_of_sync();
|
||||
|
||||
bool _load_corlib_assembly();
|
||||
bool _load_core_api_assembly();
|
||||
#ifdef TOOLS_ENABLED
|
||||
@ -112,11 +114,8 @@ class GDMono {
|
||||
#endif
|
||||
bool _load_project_assembly();
|
||||
|
||||
bool _load_api_assemblies();
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
String _get_api_assembly_metadata_path();
|
||||
#endif
|
||||
bool _try_load_api_assemblies();
|
||||
void _load_api_assemblies();
|
||||
|
||||
void _install_trace_listener();
|
||||
|
||||
@ -157,9 +156,8 @@ public:
|
||||
#endif
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
void metadata_set_api_assembly_invalidated(APIAssembly::Type p_api_type, bool p_invalidated);
|
||||
bool metadata_is_api_assembly_invalidated(APIAssembly::Type p_api_type);
|
||||
String get_invalidated_api_assembly_path(APIAssembly::Type p_api_type);
|
||||
bool copy_prebuilt_api_assembly(APIAssembly::Type p_api_type);
|
||||
String update_api_assemblies_from_prebuilt();
|
||||
#endif
|
||||
|
||||
static GDMono *get_singleton() { return singleton; }
|
||||
@ -203,6 +201,7 @@ public:
|
||||
Error finalize_and_unload_domain(MonoDomain *p_domain);
|
||||
|
||||
void initialize();
|
||||
void initialize_load_assemblies();
|
||||
|
||||
GDMono();
|
||||
~GDMono();
|
||||
|
Loading…
Reference in New Issue
Block a user