Merge pull request #16920 from neikeq/mono-project-export
Mono project export
This commit is contained in:
commit
0712acec1c
13 changed files with 377 additions and 58 deletions
|
@ -274,8 +274,6 @@ void EditorExportPlatform::gen_debug_flags(Vector<String> &r_flags, int p_flags)
|
|||
}
|
||||
|
||||
Error EditorExportPlatform::_save_pack_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total) {
|
||||
if (p_path.ends_with(".so") || p_path.ends_with(".dylib") || p_path.ends_with(".dll"))
|
||||
return OK;
|
||||
|
||||
PackData *pd = (PackData *)p_userdata;
|
||||
|
||||
|
|
|
@ -118,6 +118,8 @@ void CSharpLanguage::init() {
|
|||
|
||||
#ifdef TOOLS_ENABLED
|
||||
EditorNode::add_init_callback(&gdsharp_editor_init_callback);
|
||||
|
||||
GLOBAL_DEF("mono/export/include_scripts_content", true);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -200,7 +200,7 @@ bool GodotSharpBuilds::copy_api_assembly(const String &p_src_dir, const String &
|
|||
memdelete(da);
|
||||
|
||||
if (err != OK) {
|
||||
show_build_error_dialog("Failed to copy " API_ASSEMBLY_NAME ".dll");
|
||||
show_build_error_dialog("Failed to copy " + assembly_file);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -283,7 +283,7 @@ bool GodotSharpBuilds::make_api_sln(GodotSharpBuilds::APIType p_api_type) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GodotSharpBuilds::build_project_blocking() {
|
||||
bool GodotSharpBuilds::build_project_blocking(const String &p_config) {
|
||||
|
||||
if (!FileAccess::exists(GodotSharpDirs::get_project_sln_path()))
|
||||
return true; // No solution to build
|
||||
|
@ -298,7 +298,7 @@ bool GodotSharpBuilds::build_project_blocking() {
|
|||
|
||||
pr.step("Building project solution");
|
||||
|
||||
MonoBuildInfo build_info(GodotSharpDirs::get_project_sln_path(), "Tools");
|
||||
MonoBuildInfo build_info(GodotSharpDirs::get_project_sln_path(), p_config);
|
||||
if (!GodotSharpBuilds::get_singleton()->build(build_info)) {
|
||||
GodotSharpBuilds::show_build_error_dialog("Failed to build project solution");
|
||||
return false;
|
||||
|
@ -309,6 +309,11 @@ bool GodotSharpBuilds::build_project_blocking() {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GodotSharpBuilds::editor_build_callback() {
|
||||
|
||||
return build_project_blocking("Tools");
|
||||
}
|
||||
|
||||
GodotSharpBuilds *GodotSharpBuilds::singleton = NULL;
|
||||
|
||||
void GodotSharpBuilds::build_exit_callback(const MonoBuildInfo &p_build_info, int p_exit_code) {
|
||||
|
@ -362,7 +367,7 @@ GodotSharpBuilds::GodotSharpBuilds() {
|
|||
|
||||
singleton = this;
|
||||
|
||||
EditorNode::get_singleton()->add_build_callback(&GodotSharpBuilds::build_project_blocking);
|
||||
EditorNode::get_singleton()->add_build_callback(&GodotSharpBuilds::editor_build_callback);
|
||||
|
||||
// Build tool settings
|
||||
EditorSettings *ed_settings = EditorSettings::get_singleton();
|
||||
|
@ -462,6 +467,7 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) {
|
|||
|
||||
if (ex) {
|
||||
exited = true;
|
||||
GDMonoUtils::print_unhandled_exception(ex);
|
||||
String message = "The build constructor threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(ex);
|
||||
build_tab->on_build_exec_failed(message);
|
||||
ERR_EXPLAIN(message);
|
||||
|
@ -482,6 +488,7 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) {
|
|||
|
||||
if (ex) {
|
||||
exited = true;
|
||||
GDMonoUtils::print_unhandled_exception(ex);
|
||||
String message = "The build method threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(ex);
|
||||
build_tab->on_build_exec_failed(message);
|
||||
ERR_EXPLAIN(message);
|
||||
|
|
|
@ -93,7 +93,9 @@ public:
|
|||
|
||||
static bool make_api_sln(APIType p_api_type);
|
||||
|
||||
static bool build_project_blocking();
|
||||
static bool build_project_blocking(const String &p_config);
|
||||
|
||||
static bool editor_build_callback();
|
||||
|
||||
GodotSharpBuilds();
|
||||
~GodotSharpBuilds();
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include "../utils/path_utils.h"
|
||||
#include "bindings_generator.h"
|
||||
#include "csharp_project.h"
|
||||
#include "godotsharp_export.h"
|
||||
#include "net_solution.h"
|
||||
|
||||
#ifdef WINDOWS_ENABLED
|
||||
|
@ -316,6 +317,11 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) {
|
|||
EditorSettings *ed_settings = EditorSettings::get_singleton();
|
||||
EDITOR_DEF("mono/editor/external_editor", EDITOR_NONE);
|
||||
ed_settings->add_property_hint(PropertyInfo(Variant::INT, "mono/editor/external_editor", PROPERTY_HINT_ENUM, "None,MonoDevelop,Visual Studio Code"));
|
||||
|
||||
// Export plugin
|
||||
Ref<GodotSharpExport> godotsharp_export;
|
||||
godotsharp_export.instance();
|
||||
EditorExport::get_singleton()->add_export_plugin(godotsharp_export);
|
||||
}
|
||||
|
||||
GodotSharpEditor::~GodotSharpEditor() {
|
||||
|
|
|
@ -32,7 +32,6 @@
|
|||
#define GODOTSHARP_EDITOR_H
|
||||
|
||||
#include "godotsharp_builds.h"
|
||||
|
||||
#include "monodevelop_instance.h"
|
||||
|
||||
class GodotSharpEditor : public Node {
|
||||
|
|
163
modules/mono/editor/godotsharp_export.cpp
Normal file
163
modules/mono/editor/godotsharp_export.cpp
Normal file
|
@ -0,0 +1,163 @@
|
|||
/*************************************************************************/
|
||||
/* godotsharp_export.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "godotsharp_export.h"
|
||||
|
||||
#include "../csharp_script.h"
|
||||
#include "../godotsharp_defs.h"
|
||||
#include "../godotsharp_dirs.h"
|
||||
#include "godotsharp_builds.h"
|
||||
|
||||
void GodotSharpExport::_export_file(const String &p_path, const String &p_type, const Set<String> &p_features) {
|
||||
|
||||
if (p_type != CSharpLanguage::get_singleton()->get_type())
|
||||
return;
|
||||
|
||||
ERR_FAIL_COND(p_path.get_extension() != CSharpLanguage::get_singleton()->get_extension());
|
||||
|
||||
// TODO what if the source file is not part of the game's C# project
|
||||
|
||||
if (!GLOBAL_GET("mono/export/include_scripts_content")) {
|
||||
// We don't want to include the source code on exported games
|
||||
add_file(p_path, Vector<uint8_t>(), false);
|
||||
skip();
|
||||
}
|
||||
}
|
||||
|
||||
void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug, const String &p_path, int p_flags) {
|
||||
|
||||
// TODO right now there is no way to stop the export process with an error
|
||||
|
||||
String build_config = p_debug ? "Debug" : "Release";
|
||||
|
||||
ERR_FAIL_COND(!GodotSharpBuilds::build_project_blocking(build_config));
|
||||
|
||||
// Add API assemblies
|
||||
|
||||
String core_api_dll_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(API_ASSEMBLY_NAME ".dll");
|
||||
ERR_FAIL_COND(!_add_assembly(core_api_dll_path, core_api_dll_path));
|
||||
|
||||
String editor_api_dll_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
|
||||
ERR_FAIL_COND(!_add_assembly(editor_api_dll_path, editor_api_dll_path));
|
||||
|
||||
// Add project assembly
|
||||
|
||||
String project_dll_name = ProjectSettings::get_singleton()->get("application/config/name");
|
||||
if (project_dll_name.empty()) {
|
||||
project_dll_name = "UnnamedProject";
|
||||
}
|
||||
|
||||
String project_dll_src_path = GodotSharpDirs::get_res_temp_assemblies_base_dir().plus_file(build_config).plus_file(project_dll_name + ".dll");
|
||||
String project_dll_dst_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(project_dll_name + ".dll");
|
||||
ERR_FAIL_COND(!_add_assembly(project_dll_src_path, project_dll_dst_path));
|
||||
|
||||
// Add dependencies
|
||||
|
||||
MonoDomain *prev_domain = mono_domain_get();
|
||||
MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.ProjectExportDomain");
|
||||
|
||||
ERR_FAIL_COND(!export_domain);
|
||||
ERR_FAIL_COND(!mono_domain_set(export_domain, false));
|
||||
|
||||
Map<String, String> dependencies;
|
||||
dependencies.insert("mscorlib", GDMono::get_singleton()->get_corlib_assembly()->get_path());
|
||||
|
||||
GDMonoAssembly *scripts_assembly = GDMonoAssembly::load_from(project_dll_name, project_dll_src_path, /* refonly: */ true);
|
||||
|
||||
ERR_EXPLAIN("Cannot load refonly assembly: " + project_dll_name);
|
||||
ERR_FAIL_COND(!scripts_assembly);
|
||||
|
||||
Error depend_error = _get_assembly_dependencies(scripts_assembly, dependencies);
|
||||
|
||||
GDMono::get_singleton()->finalize_and_unload_domain(export_domain);
|
||||
mono_domain_set(prev_domain, false);
|
||||
|
||||
ERR_FAIL_COND(depend_error != OK);
|
||||
|
||||
for (Map<String, String>::Element *E = dependencies.front(); E; E = E->next()) {
|
||||
String depend_src_path = E->value();
|
||||
String depend_dst_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(depend_src_path.get_file());
|
||||
ERR_FAIL_COND(!_add_assembly(depend_src_path, depend_dst_path));
|
||||
}
|
||||
}
|
||||
|
||||
bool GodotSharpExport::_add_assembly(const String &p_src_path, const String &p_dst_path) {
|
||||
|
||||
FileAccessRef f = FileAccess::open(p_src_path, FileAccess::READ);
|
||||
ERR_FAIL_COND_V(!f, false);
|
||||
|
||||
Vector<uint8_t> data;
|
||||
data.resize(f->get_len());
|
||||
f->get_buffer(data.ptrw(), data.size());
|
||||
|
||||
add_file(p_dst_path, data, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Error GodotSharpExport::_get_assembly_dependencies(GDMonoAssembly *p_assembly, Map<String, String> &r_dependencies) {
|
||||
|
||||
MonoImage *image = p_assembly->get_image();
|
||||
|
||||
for (int i = 0; i < mono_image_get_table_rows(image, MONO_TABLE_ASSEMBLYREF); i++) {
|
||||
MonoAssemblyName *ref_aname = aname_prealloc;
|
||||
mono_assembly_get_assemblyref(image, i, ref_aname);
|
||||
String ref_name = mono_assembly_name_get_name(ref_aname);
|
||||
|
||||
if (ref_name == "mscorlib" || r_dependencies.find(ref_name))
|
||||
continue;
|
||||
|
||||
GDMonoAssembly *ref_assembly = NULL;
|
||||
if (!GDMono::get_singleton()->load_assembly(ref_name, ref_aname, &ref_assembly, /* refonly: */ true)) {
|
||||
ERR_EXPLAIN("Cannot load refonly assembly: " + ref_name);
|
||||
ERR_FAIL_V(ERR_CANT_RESOLVE);
|
||||
}
|
||||
|
||||
r_dependencies.insert(ref_name, ref_assembly->get_path());
|
||||
|
||||
Error err = _get_assembly_dependencies(ref_assembly, r_dependencies);
|
||||
if (err != OK)
|
||||
return err;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
GodotSharpExport::GodotSharpExport() {
|
||||
// MonoAssemblyName is an incomplete type (internal to mono), so we can't allocate it ourselves.
|
||||
// There isn't any api to allocate an empty one either, so we need to do it this way.
|
||||
aname_prealloc = mono_assembly_name_new("whatever");
|
||||
mono_assembly_name_free(aname_prealloc); // "it does not frees the object itself, only the name members" (typo included)
|
||||
}
|
||||
|
||||
GodotSharpExport::~GodotSharpExport() {
|
||||
if (aname_prealloc)
|
||||
mono_free(aname_prealloc);
|
||||
}
|
57
modules/mono/editor/godotsharp_export.h
Normal file
57
modules/mono/editor/godotsharp_export.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*************************************************************************/
|
||||
/* godotsharp_export.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef GODOTSHARP_EXPORT_H
|
||||
#define GODOTSHARP_EXPORT_H
|
||||
|
||||
#include <mono/metadata/image.h>
|
||||
|
||||
#include "editor/editor_export.h"
|
||||
|
||||
#include "../mono_gd/gd_mono_header.h"
|
||||
|
||||
class GodotSharpExport : public EditorExportPlugin {
|
||||
|
||||
MonoAssemblyName *aname_prealloc;
|
||||
|
||||
bool _add_assembly(const String &p_src_path, const String &p_dst_path);
|
||||
|
||||
Error _get_assembly_dependencies(GDMonoAssembly *p_assembly, Map<String, String> &r_dependencies);
|
||||
|
||||
protected:
|
||||
virtual void _export_file(const String &p_path, const String &p_type, const Set<String> &p_features);
|
||||
virtual void _export_begin(const Set<String> &p_features, bool p_debug, const String &p_path, int p_flags);
|
||||
|
||||
public:
|
||||
GodotSharpExport();
|
||||
~GodotSharpExport();
|
||||
};
|
||||
|
||||
#endif // GODOTSHARP_EXPORT_H
|
|
@ -142,7 +142,7 @@ void MonoBottomPanel::_errors_toggled(bool p_pressed) {
|
|||
|
||||
void MonoBottomPanel::_build_project_pressed() {
|
||||
|
||||
GodotSharpBuilds::get_singleton()->build_project_blocking();
|
||||
GodotSharpBuilds::get_singleton()->build_project_blocking("Tools");
|
||||
|
||||
MonoReloadNode::get_singleton()->restart_reload_timer();
|
||||
CSharpLanguage::get_singleton()->reload_assemblies_if_needed(true);
|
||||
|
|
|
@ -271,33 +271,44 @@ GDMonoAssembly **GDMono::get_loaded_assembly(const String &p_name) {
|
|||
return assemblies[domain_id].getptr(p_name);
|
||||
}
|
||||
|
||||
bool GDMono::_load_assembly(const String &p_name, GDMonoAssembly **r_assembly) {
|
||||
bool GDMono::load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly) {
|
||||
|
||||
CRASH_COND(!r_assembly);
|
||||
|
||||
MonoAssemblyName *aname = mono_assembly_name_new(p_name.utf8());
|
||||
bool result = load_assembly(p_name, aname, r_assembly, p_refonly);
|
||||
mono_assembly_name_free(aname);
|
||||
mono_free(aname);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly) {
|
||||
|
||||
CRASH_COND(!r_assembly);
|
||||
|
||||
if (OS::get_singleton()->is_stdout_verbose())
|
||||
OS::get_singleton()->print((String() + "Mono: Loading assembly " + p_name + "...\n").utf8());
|
||||
OS::get_singleton()->print((String() + "Mono: Loading assembly " + p_name + (p_refonly ? " (refonly)" : "") + "...\n").utf8());
|
||||
|
||||
MonoImageOpenStatus status = MONO_IMAGE_OK;
|
||||
MonoAssemblyName *aname = mono_assembly_name_new(p_name.utf8());
|
||||
MonoAssembly *assembly = mono_assembly_load_full(aname, NULL, &status, false);
|
||||
mono_assembly_name_free(aname);
|
||||
MonoAssembly *assembly = mono_assembly_load_full(p_aname, NULL, &status, p_refonly);
|
||||
|
||||
if (!assembly)
|
||||
return false;
|
||||
|
||||
ERR_FAIL_COND_V(status != MONO_IMAGE_OK, false);
|
||||
|
||||
uint32_t domain_id = mono_domain_get_id(mono_domain_get());
|
||||
|
||||
GDMonoAssembly **stored_assembly = assemblies[domain_id].getptr(p_name);
|
||||
|
||||
ERR_FAIL_COND_V(status != MONO_IMAGE_OK, false);
|
||||
ERR_FAIL_COND_V(stored_assembly == NULL, false);
|
||||
|
||||
ERR_FAIL_COND_V((*stored_assembly)->get_assembly() != assembly, false);
|
||||
|
||||
*r_assembly = *stored_assembly;
|
||||
|
||||
if (OS::get_singleton()->is_stdout_verbose())
|
||||
OS::get_singleton()->print(String("Mono: Assembly " + p_name + " loaded from path: " + (*r_assembly)->get_path() + "\n").utf8());
|
||||
OS::get_singleton()->print(String("Mono: Assembly " + p_name + (p_refonly ? " (refonly)" : "") + " loaded from path: " + (*r_assembly)->get_path() + "\n").utf8());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -307,7 +318,7 @@ bool GDMono::_load_corlib_assembly() {
|
|||
if (corlib_assembly)
|
||||
return true;
|
||||
|
||||
bool success = _load_assembly("mscorlib", &corlib_assembly);
|
||||
bool success = load_assembly("mscorlib", &corlib_assembly);
|
||||
|
||||
if (success)
|
||||
GDMonoUtils::update_corlib_cache();
|
||||
|
@ -320,7 +331,7 @@ bool GDMono::_load_core_api_assembly() {
|
|||
if (api_assembly)
|
||||
return true;
|
||||
|
||||
bool success = _load_assembly(API_ASSEMBLY_NAME, &api_assembly);
|
||||
bool success = load_assembly(API_ASSEMBLY_NAME, &api_assembly);
|
||||
|
||||
if (success)
|
||||
GDMonoUtils::update_godot_api_cache();
|
||||
|
@ -334,7 +345,7 @@ bool GDMono::_load_editor_api_assembly() {
|
|||
if (editor_api_assembly)
|
||||
return true;
|
||||
|
||||
return _load_assembly(EDITOR_API_ASSEMBLY_NAME, &editor_api_assembly);
|
||||
return load_assembly(EDITOR_API_ASSEMBLY_NAME, &editor_api_assembly);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -346,7 +357,7 @@ bool GDMono::_load_editor_tools_assembly() {
|
|||
|
||||
_GDMONO_SCOPE_DOMAIN_(tools_domain)
|
||||
|
||||
return _load_assembly(EDITOR_TOOLS_ASSEMBLY_NAME, &editor_tools_assembly);
|
||||
return load_assembly(EDITOR_TOOLS_ASSEMBLY_NAME, &editor_tools_assembly);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -360,7 +371,7 @@ bool GDMono::_load_project_assembly() {
|
|||
name = "UnnamedProject";
|
||||
}
|
||||
|
||||
bool success = _load_assembly(name, &project_assembly);
|
||||
bool success = load_assembly(name, &project_assembly);
|
||||
|
||||
if (success)
|
||||
mono_assembly_set_main(project_assembly->get_assembly());
|
||||
|
@ -511,6 +522,37 @@ Error GDMono::reload_scripts_domain() {
|
|||
}
|
||||
#endif
|
||||
|
||||
Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) {
|
||||
|
||||
CRASH_COND(p_domain == NULL);
|
||||
|
||||
String domain_name = mono_domain_get_friendly_name(p_domain);
|
||||
|
||||
if (OS::get_singleton()->is_stdout_verbose()) {
|
||||
OS::get_singleton()->print(String("Mono: Unloading domain `" + domain_name + "`...\n").utf8());
|
||||
}
|
||||
|
||||
if (mono_domain_get() != root_domain)
|
||||
mono_domain_set(root_domain, true);
|
||||
|
||||
mono_gc_collect(mono_gc_max_generation());
|
||||
mono_domain_finalize(p_domain, 2000);
|
||||
mono_gc_collect(mono_gc_max_generation());
|
||||
|
||||
_domain_assemblies_cleanup(mono_domain_get_id(p_domain));
|
||||
|
||||
MonoObject *ex = NULL;
|
||||
mono_domain_try_unload(p_domain, &ex);
|
||||
|
||||
if (ex) {
|
||||
ERR_PRINTS("Exception thrown when unloading domain `" + domain_name + "`:");
|
||||
mono_print_unhandled_exception(ex);
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
GDMonoClass *GDMono::get_class(MonoClass *p_raw_class) {
|
||||
|
||||
MonoImage *image = mono_class_get_image(p_raw_class);
|
||||
|
|
|
@ -94,8 +94,6 @@ class GDMono {
|
|||
void _initialize_and_check_api_hashes();
|
||||
#endif
|
||||
|
||||
bool _load_assembly(const String &p_name, GDMonoAssembly **r_assembly);
|
||||
|
||||
GDMonoLog *gdmono_log;
|
||||
|
||||
#ifdef WINDOWS_ENABLED
|
||||
|
@ -145,6 +143,10 @@ public:
|
|||
Error reload_scripts_domain();
|
||||
#endif
|
||||
|
||||
bool load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly = false);
|
||||
bool load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly = false);
|
||||
Error finalize_and_unload_domain(MonoDomain *p_domain);
|
||||
|
||||
void initialize();
|
||||
|
||||
GDMono();
|
||||
|
|
|
@ -43,7 +43,23 @@
|
|||
bool GDMonoAssembly::no_search = false;
|
||||
Vector<String> GDMonoAssembly::search_dirs;
|
||||
|
||||
MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_data) {
|
||||
MonoAssembly *GDMonoAssembly::assembly_search_hook(MonoAssemblyName *aname, void *user_data) {
|
||||
return GDMonoAssembly::_search_hook(aname, user_data, false);
|
||||
}
|
||||
|
||||
MonoAssembly *GDMonoAssembly::assembly_refonly_search_hook(MonoAssemblyName *aname, void *user_data) {
|
||||
return GDMonoAssembly::_search_hook(aname, user_data, true);
|
||||
}
|
||||
|
||||
MonoAssembly *GDMonoAssembly::assembly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data) {
|
||||
return GDMonoAssembly::_preload_hook(aname, assemblies_path, user_data, false);
|
||||
}
|
||||
|
||||
MonoAssembly *GDMonoAssembly::assembly_refonly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data) {
|
||||
return GDMonoAssembly::_preload_hook(aname, assemblies_path, user_data, true);
|
||||
}
|
||||
|
||||
MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_data, bool refonly) {
|
||||
|
||||
(void)user_data; // UNUSED
|
||||
|
||||
|
@ -60,7 +76,7 @@ MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_d
|
|||
no_search = true; // Avoid the recursion madness
|
||||
|
||||
String path;
|
||||
MonoAssembly *res = NULL;
|
||||
GDMonoAssembly *res = NULL;
|
||||
|
||||
for (int i = 0; i < search_dirs.size(); i++) {
|
||||
const String &search_dir = search_dirs[i];
|
||||
|
@ -68,19 +84,19 @@ MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_d
|
|||
if (has_extension) {
|
||||
path = search_dir.plus_file(name);
|
||||
if (FileAccess::exists(path)) {
|
||||
res = _load_assembly_from(name.get_basename(), path);
|
||||
res = _load_assembly_from(name.get_basename(), path, refonly);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
path = search_dir.plus_file(name + ".dll");
|
||||
if (FileAccess::exists(path)) {
|
||||
res = _load_assembly_from(name, path);
|
||||
res = _load_assembly_from(name, path, refonly);
|
||||
break;
|
||||
}
|
||||
|
||||
path = search_dir.plus_file(name + ".exe");
|
||||
if (FileAccess::exists(path)) {
|
||||
res = _load_assembly_from(name, path);
|
||||
res = _load_assembly_from(name, path, refonly);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -88,17 +104,15 @@ MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_d
|
|||
|
||||
no_search = false;
|
||||
|
||||
return res;
|
||||
return res ? res->get_assembly() : NULL;
|
||||
}
|
||||
|
||||
MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data) {
|
||||
MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data, bool refonly) {
|
||||
|
||||
(void)user_data; // UNUSED
|
||||
|
||||
if (search_dirs.empty()) {
|
||||
#ifdef TOOLS_DOMAIN
|
||||
search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_dir());
|
||||
#endif
|
||||
search_dirs.push_back(GodotSharpDirs::get_res_assemblies_dir());
|
||||
search_dirs.push_back(OS::get_singleton()->get_resource_dir());
|
||||
search_dirs.push_back(OS::get_singleton()->get_executable_path().get_base_dir());
|
||||
|
@ -121,10 +135,11 @@ MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **asse
|
|||
|
||||
if (has_extension ? name == "mscorlib.dll" : name == "mscorlib") {
|
||||
GDMonoAssembly **stored_assembly = GDMono::get_singleton()->get_loaded_assembly(has_extension ? name.get_basename() : name);
|
||||
if (stored_assembly) return (*stored_assembly)->get_assembly();
|
||||
if (stored_assembly)
|
||||
return (*stored_assembly)->get_assembly();
|
||||
|
||||
String path;
|
||||
MonoAssembly *res = NULL;
|
||||
GDMonoAssembly *res = NULL;
|
||||
|
||||
for (int i = 0; i < search_dirs.size(); i++) {
|
||||
const String &search_dir = search_dirs[i];
|
||||
|
@ -132,53 +147,61 @@ MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **asse
|
|||
if (has_extension) {
|
||||
path = search_dir.plus_file(name);
|
||||
if (FileAccess::exists(path)) {
|
||||
res = _load_assembly_from(name.get_basename(), path);
|
||||
res = _load_assembly_from(name.get_basename(), path, refonly);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
path = search_dir.plus_file(name + ".dll");
|
||||
if (FileAccess::exists(path)) {
|
||||
res = _load_assembly_from(name, path);
|
||||
res = _load_assembly_from(name, path, refonly);
|
||||
break;
|
||||
}
|
||||
|
||||
path = search_dir.plus_file(name + ".exe");
|
||||
if (FileAccess::exists(path)) {
|
||||
res = _load_assembly_from(name, path, refonly);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (res) return res;
|
||||
return res ? res->get_assembly() : NULL;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MonoAssembly *GDMonoAssembly::_load_assembly_from(const String &p_name, const String &p_path) {
|
||||
GDMonoAssembly *GDMonoAssembly::_load_assembly_from(const String &p_name, const String &p_path, bool p_refonly) {
|
||||
|
||||
GDMonoAssembly *assembly = memnew(GDMonoAssembly(p_name, p_path));
|
||||
|
||||
MonoDomain *domain = mono_domain_get();
|
||||
|
||||
Error err = assembly->load(domain);
|
||||
Error err = assembly->load(p_refonly);
|
||||
|
||||
if (err != OK) {
|
||||
memdelete(assembly);
|
||||
ERR_FAIL_V(NULL);
|
||||
}
|
||||
|
||||
MonoDomain *domain = mono_domain_get();
|
||||
GDMono::get_singleton()->add_assembly(domain ? mono_domain_get_id(domain) : 0, assembly);
|
||||
|
||||
return assembly->get_assembly();
|
||||
return assembly;
|
||||
}
|
||||
|
||||
void GDMonoAssembly::initialize() {
|
||||
|
||||
// TODO refonly as well?
|
||||
mono_install_assembly_preload_hook(&GDMonoAssembly::_preload_hook, NULL);
|
||||
mono_install_assembly_search_hook(&GDMonoAssembly::_search_hook, NULL);
|
||||
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);
|
||||
mono_install_assembly_refonly_preload_hook(&assembly_refonly_preload_hook, NULL);
|
||||
}
|
||||
|
||||
Error GDMonoAssembly::load(MonoDomain *p_domain) {
|
||||
Error GDMonoAssembly::load(bool p_refonly) {
|
||||
|
||||
ERR_FAIL_COND_V(loaded, ERR_FILE_ALREADY_IN_USE);
|
||||
|
||||
refonly = p_refonly;
|
||||
|
||||
uint64_t last_modified_time = FileAccess::get_modified_time(path);
|
||||
|
||||
Vector<uint8_t> data = FileAccess::get_file_as_array(path);
|
||||
|
@ -190,7 +213,7 @@ Error GDMonoAssembly::load(MonoDomain *p_domain) {
|
|||
|
||||
image = mono_image_open_from_data_with_name(
|
||||
(char *)&data[0], data.size(),
|
||||
true, &status, false,
|
||||
true, &status, refonly,
|
||||
image_filename.utf8().get_data());
|
||||
|
||||
ERR_FAIL_COND_V(status != MONO_IMAGE_OK || image == NULL, ERR_FILE_CANT_OPEN);
|
||||
|
@ -213,15 +236,10 @@ no_pdb:
|
|||
|
||||
#endif
|
||||
|
||||
assembly = mono_assembly_load_from_full(image, image_filename.utf8().get_data(), &status, false);
|
||||
assembly = mono_assembly_load_from_full(image, image_filename.utf8().get_data(), &status, refonly);
|
||||
|
||||
ERR_FAIL_COND_V(status != MONO_IMAGE_OK || assembly == NULL, ERR_FILE_CANT_OPEN);
|
||||
|
||||
if (p_domain && mono_image_get_entry_point(image)) {
|
||||
// TODO should this be removed? do we want to call main? what other effects does this have?
|
||||
mono_jit_exec(p_domain, assembly, 0, NULL);
|
||||
}
|
||||
|
||||
loaded = true;
|
||||
modified_time = last_modified_time;
|
||||
|
||||
|
@ -373,12 +391,26 @@ GDMonoClass *GDMonoAssembly::get_object_derived_class(const StringName &p_class)
|
|||
return match;
|
||||
}
|
||||
|
||||
GDMonoAssembly *GDMonoAssembly::load_from(const String &p_name, const String &p_path, bool p_refonly) {
|
||||
|
||||
GDMonoAssembly **loaded_asm = GDMono::get_singleton()->get_loaded_assembly(p_name);
|
||||
if (loaded_asm)
|
||||
return *loaded_asm;
|
||||
|
||||
no_search = true;
|
||||
GDMonoAssembly *res = _load_assembly_from(p_name, p_path, p_refonly);
|
||||
no_search = false;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
GDMonoAssembly::GDMonoAssembly(const String &p_name, const String &p_path) {
|
||||
|
||||
loaded = false;
|
||||
gdobject_class_cache_updated = false;
|
||||
name = p_name;
|
||||
path = p_path;
|
||||
refonly = false;
|
||||
modified_time = 0;
|
||||
assembly = NULL;
|
||||
image = NULL;
|
||||
|
|
|
@ -71,6 +71,7 @@ class GDMonoAssembly {
|
|||
MonoAssembly *assembly;
|
||||
MonoImage *image;
|
||||
|
||||
bool refonly;
|
||||
bool loaded;
|
||||
|
||||
String name;
|
||||
|
@ -90,19 +91,25 @@ class GDMonoAssembly {
|
|||
static bool no_search;
|
||||
static Vector<String> search_dirs;
|
||||
|
||||
static MonoAssembly *_search_hook(MonoAssemblyName *aname, void *user_data);
|
||||
static MonoAssembly *_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data);
|
||||
static MonoAssembly *assembly_search_hook(MonoAssemblyName *aname, void *user_data);
|
||||
static MonoAssembly *assembly_refonly_search_hook(MonoAssemblyName *aname, void *user_data);
|
||||
static MonoAssembly *assembly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data);
|
||||
static MonoAssembly *assembly_refonly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data);
|
||||
|
||||
static MonoAssembly *_load_assembly_from(const String &p_name, const String &p_path);
|
||||
static MonoAssembly *_search_hook(MonoAssemblyName *aname, void *user_data, bool refonly);
|
||||
static MonoAssembly *_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data, bool refonly);
|
||||
|
||||
static GDMonoAssembly *_load_assembly_from(const String &p_name, const String &p_path, bool p_refonly);
|
||||
|
||||
friend class GDMono;
|
||||
static void initialize();
|
||||
|
||||
public:
|
||||
Error load(MonoDomain *p_domain);
|
||||
Error load(bool p_refonly);
|
||||
Error wrapper_for_image(MonoImage *p_image);
|
||||
void unload();
|
||||
|
||||
_FORCE_INLINE_ bool is_refonly() const { return refonly; }
|
||||
_FORCE_INLINE_ bool is_loaded() const { return loaded; }
|
||||
_FORCE_INLINE_ MonoImage *get_image() const { return image; }
|
||||
_FORCE_INLINE_ MonoAssembly *get_assembly() const { return assembly; }
|
||||
|
@ -115,6 +122,8 @@ public:
|
|||
|
||||
GDMonoClass *get_object_derived_class(const StringName &p_class);
|
||||
|
||||
static GDMonoAssembly *load_from(const String &p_name, const String &p_path, bool p_refonly);
|
||||
|
||||
GDMonoAssembly(const String &p_name, const String &p_path = String());
|
||||
~GDMonoAssembly();
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue