summaryrefslogtreecommitdiff
path: root/modules/mono/editor
diff options
context:
space:
mode:
authorIgnacio Etcheverry <ignalfonsore@gmail.com>2018-02-22 13:13:51 +0100
committerIgnacio Etcheverry <ignalfonsore@gmail.com>2018-02-25 20:56:27 +0100
commitf37090ccf4f699800a43878273b8b94b5906f4bc (patch)
tree2ae483b9c89320bacd7e6ad1939448d9741ba0d9 /modules/mono/editor
parent125fc8cc4432d28e63da76c3a2aff655b1a7cc6b (diff)
Mono: Better versioning and gracefully unloading of Godot API assemblies
Diffstat (limited to 'modules/mono/editor')
-rw-r--r--modules/mono/editor/bindings_generator.cpp78
-rw-r--r--modules/mono/editor/bindings_generator.h3
-rw-r--r--modules/mono/editor/godotsharp_builds.cpp46
-rw-r--r--modules/mono/editor/godotsharp_builds.h12
-rw-r--r--modules/mono/editor/godotsharp_editor.cpp4
-rw-r--r--modules/mono/editor/godotsharp_export.cpp3
6 files changed, 98 insertions, 48 deletions
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 7e92296554..315c1e2f1c 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -70,8 +70,6 @@
#define LOCAL_RET "ret"
-#define CS_CLASS_NATIVECALLS "NativeCalls"
-#define CS_CLASS_NATIVECALLS_EDITOR "EditorNativeCalls"
#define CS_FIELD_MEMORYOWN "memoryOwn"
#define CS_PARAM_METHODBIND "method"
#define CS_PARAM_INSTANCE "ptr"
@@ -105,6 +103,8 @@
#define C_METHOD_MANAGED_TO_DICT C_NS_MONOMARSHAL "::mono_object_to_Dictionary"
#define C_METHOD_MANAGED_FROM_DICT C_NS_MONOMARSHAL "::Dictionary_to_mono_object"
+#define BINDINGS_GENERATOR_VERSION UINT32_C(1)
+
const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN = "\t%0 %1_in = %1;\n";
bool BindingsGenerator::verbose_output = false;
@@ -529,7 +529,15 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_output_dir, bo
"using System.Collections.Generic;\n"
"\n");
cs_icalls_content.push_back("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
- cs_icalls_content.push_back(INDENT1 "internal static class " CS_CLASS_NATIVECALLS "\n" INDENT1 OPEN_BLOCK);
+ cs_icalls_content.push_back(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS "\n" INDENT1 OPEN_BLOCK);
+
+ cs_icalls_content.push_back(INDENT2 "internal static ulong godot_api_hash = ");
+ cs_icalls_content.push_back(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + ";\n");
+ cs_icalls_content.push_back(INDENT2 "internal static uint bindings_version = ");
+ cs_icalls_content.push_back(String::num_uint64(BINDINGS_GENERATOR_VERSION) + ";\n");
+ cs_icalls_content.push_back(INDENT2 "internal static uint cs_glue_version = ");
+ cs_icalls_content.push_back(String::num_uint64(CS_GLUE_VERSION) + ";\n");
+ cs_icalls_content.push_back("\n");
#define ADD_INTERNAL_CALL(m_icall) \
if (!m_icall.editor_only) { \
@@ -551,7 +559,7 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_output_dir, bo
cs_icalls_content.push_back(INDENT1 CLOSE_BLOCK CLOSE_BLOCK);
- String internal_methods_file = path_join(core_dir, CS_CLASS_NATIVECALLS ".cs");
+ String internal_methods_file = path_join(core_dir, BINDINGS_CLASS_NATIVECALLS ".cs");
Error err = _save_file(internal_methods_file, cs_icalls_content);
if (err != OK)
@@ -626,7 +634,15 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_output_dir,
"using System.Collections.Generic;\n"
"\n");
cs_icalls_content.push_back("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
- cs_icalls_content.push_back(INDENT1 "internal static class " CS_CLASS_NATIVECALLS_EDITOR "\n" INDENT1 OPEN_BLOCK);
+ cs_icalls_content.push_back(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS_EDITOR "\n" INDENT1 OPEN_BLOCK);
+
+ cs_icalls_content.push_back(INDENT2 "internal static ulong godot_api_hash = ");
+ cs_icalls_content.push_back(String::num_uint64(GDMono::get_singleton()->get_api_editor_hash()) + ";\n");
+ cs_icalls_content.push_back(INDENT2 "internal static uint bindings_version = ");
+ cs_icalls_content.push_back(String::num_uint64(BINDINGS_GENERATOR_VERSION) + ";\n");
+ cs_icalls_content.push_back(INDENT2 "internal static uint cs_glue_version = ");
+ cs_icalls_content.push_back(String::num_uint64(CS_GLUE_VERSION) + ";\n");
+ cs_icalls_content.push_back("\n");
#define ADD_INTERNAL_CALL(m_icall) \
if (m_icall.editor_only) { \
@@ -648,7 +664,7 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_output_dir,
cs_icalls_content.push_back(INDENT1 CLOSE_BLOCK CLOSE_BLOCK);
- String internal_methods_file = path_join(core_dir, CS_CLASS_NATIVECALLS_EDITOR ".cs");
+ String internal_methods_file = path_join(core_dir, BINDINGS_CLASS_NATIVECALLS_EDITOR ".cs");
Error err = _save_file(internal_methods_file, cs_icalls_content);
if (err != OK)
@@ -882,7 +898,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
output.push_back("\";\n");
output.push_back(INDENT2 "internal static IntPtr " BINDINGS_PTR_FIELD " = ");
- output.push_back(itype.api_type == ClassDB::API_EDITOR ? CS_CLASS_NATIVECALLS_EDITOR : CS_CLASS_NATIVECALLS);
+ output.push_back(itype.api_type == ClassDB::API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS);
output.push_back("." ICALL_PREFIX);
output.push_back(itype.name);
output.push_back(SINGLETON_ICALL_SUFFIX "();\n");
@@ -912,7 +928,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
// The engine will initialize the pointer field of the managed side before calling the constructor
// This is why we only allocate a new native object from the constructor if the pointer field is not set
output.push_back(")\n" OPEN_BLOCK_L2 "if (" BINDINGS_PTR_FIELD " == IntPtr.Zero)\n" INDENT4 BINDINGS_PTR_FIELD " = ");
- output.push_back(itype.api_type == ClassDB::API_EDITOR ? CS_CLASS_NATIVECALLS_EDITOR : CS_CLASS_NATIVECALLS);
+ output.push_back(itype.api_type == ClassDB::API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS);
output.push_back("." + ctor_method);
output.push_back("(this);\n" CLOSE_BLOCK_L2);
} else {
@@ -956,7 +972,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
"if (disposed) return;\n" INDENT3
"if (" BINDINGS_PTR_FIELD " != IntPtr.Zero)\n" OPEN_BLOCK_L3
"if (" CS_FIELD_MEMORYOWN ")\n" OPEN_BLOCK_L4 CS_FIELD_MEMORYOWN
- " = false;\n" INDENT5 CS_CLASS_NATIVECALLS "." ICALL_OBJECT_DTOR
+ " = false;\n" INDENT5 BINDINGS_CLASS_NATIVECALLS "." ICALL_OBJECT_DTOR
"(this, " BINDINGS_PTR_FIELD ");\n" CLOSE_BLOCK_L4 CLOSE_BLOCK_L3 INDENT3
"this." BINDINGS_PTR_FIELD " = IntPtr.Zero;\n" INDENT3
"GC.SuppressFinalize(this);\n" INDENT3 "disposed = true;\n" CLOSE_BLOCK_L2);
@@ -1229,7 +1245,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
{
if (p_itype.is_object_type && !p_imethod.is_virtual && !p_imethod.requires_object_call) {
p_output.push_back(MEMBER_BEGIN "private static IntPtr ");
- p_output.push_back(method_bind_field + " = " CS_CLASS_NATIVECALLS "." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \"");
+ p_output.push_back(method_bind_field + " = " BINDINGS_CLASS_NATIVECALLS "." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \"");
p_output.push_back(p_imethod.name);
p_output.push_back("\");\n");
}
@@ -1310,7 +1326,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
const InternalCall *im_icall = match->value();
- String im_call = im_icall->editor_only ? CS_CLASS_NATIVECALLS_EDITOR : CS_CLASS_NATIVECALLS;
+ String im_call = im_icall->editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS;
im_call += "." + im_icall->name + "(" + icall_params + ");\n";
if (p_imethod.arguments.size())
@@ -1400,25 +1416,33 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
}
output.push_back("namespace GodotSharpBindings\n" OPEN_BLOCK);
+
output.push_back("uint64_t get_core_api_hash() { return ");
- output.push_back(itos(GDMono::get_singleton()->get_api_core_hash()) + "; }\n");
+ output.push_back(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + "; }\n");
+
output.push_back("#ifdef TOOLS_ENABLED\n"
"uint64_t get_editor_api_hash() { return ");
- output.push_back(itos(GDMono::get_singleton()->get_api_editor_hash()) +
+ output.push_back(String::num_uint64(GDMono::get_singleton()->get_api_editor_hash()) +
"; }\n#endif // TOOLS_ENABLED\n");
+
+ output.push_back("uint32_t get_bindings_version() { return ");
+ output.push_back(String::num_uint64(BINDINGS_GENERATOR_VERSION) + "; }\n");
+ output.push_back("uint32_t get_cs_glue_version() { return ");
+ output.push_back(String::num_uint64(CS_GLUE_VERSION) + "; }\n");
+
output.push_back("void register_generated_icalls() " OPEN_BLOCK);
output.push_back("\tgodot_register_header_icalls();");
-#define ADD_INTERNAL_CALL_REGISTRATION(m_icall) \
- { \
- output.push_back("\tmono_add_internal_call("); \
- output.push_back("\"" BINDINGS_NAMESPACE "."); \
- output.push_back(m_icall.editor_only ? CS_CLASS_NATIVECALLS_EDITOR : CS_CLASS_NATIVECALLS); \
- output.push_back("::"); \
- output.push_back(m_icall.name); \
- output.push_back("\", (void*)"); \
- output.push_back(m_icall.name); \
- output.push_back(");\n"); \
+#define ADD_INTERNAL_CALL_REGISTRATION(m_icall) \
+ { \
+ output.push_back("\tmono_add_internal_call("); \
+ output.push_back("\"" BINDINGS_NAMESPACE "."); \
+ output.push_back(m_icall.editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS); \
+ output.push_back("::"); \
+ output.push_back(m_icall.name); \
+ output.push_back("\", (void*)"); \
+ output.push_back(m_icall.name); \
+ output.push_back(");\n"); \
}
bool tools_sequence = false;
@@ -1486,6 +1510,14 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
return OK;
}
+uint32_t BindingsGenerator::get_version() {
+ return BINDINGS_GENERATOR_VERSION;
+}
+
+uint32_t BindingsGenerator::get_cs_glue_version() {
+ return CS_GLUE_VERSION;
+}
+
Error BindingsGenerator::_save_file(const String &p_path, const List<String> &p_content) {
FileAccessRef file = FileAccess::open(p_path, FileAccess::WRITE);
diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h
index 9b5a9cea88..f6194139af 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -536,6 +536,9 @@ public:
Error generate_cs_editor_project(const String &p_output_dir, const String &p_core_dll_path, bool p_verbose_output = true);
Error generate_glue(const String &p_output_dir);
+ static uint32_t get_version();
+ static uint32_t get_cs_glue_version();
+
void initialize();
_FORCE_INLINE_ static BindingsGenerator *get_singleton() {
diff --git a/modules/mono/editor/godotsharp_builds.cpp b/modules/mono/editor/godotsharp_builds.cpp
index 51e6d76ae6..ad07f043b2 100644
--- a/modules/mono/editor/godotsharp_builds.cpp
+++ b/modules/mono/editor/godotsharp_builds.cpp
@@ -33,7 +33,6 @@
#include "main/main.h"
#include "../godotsharp_dirs.h"
-#include "../mono_gd/gd_mono.h"
#include "../mono_gd/gd_mono_class.h"
#include "../mono_gd/gd_mono_marshal.h"
#include "../utils/path_utils.h"
@@ -178,13 +177,15 @@ bool GodotSharpBuilds::build_api_sln(const String &p_name, const String &p_api_s
return true;
}
-bool GodotSharpBuilds::copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name) {
+bool GodotSharpBuilds::copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name, APIAssembly::Type p_api_type) {
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);
- if (!FileAccess::exists(assembly_dst) || FileAccess::get_modified_time(assembly_src) > FileAccess::get_modified_time(assembly_dst)) {
+ 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)) {
DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
String xml_file = p_assembly_name + ".xml";
@@ -203,33 +204,46 @@ bool GodotSharpBuilds::copy_api_assembly(const String &p_src_dir, const String &
show_build_error_dialog("Failed to copy " + assembly_file);
return false;
}
+
+ GDMono::get_singleton()->metadata_set_api_assembly_invalidated(p_api_type, false);
}
return true;
}
-bool GodotSharpBuilds::make_api_sln(GodotSharpBuilds::APIType p_api_type) {
+String GodotSharpBuilds::_api_folder_name(APIAssembly::Type p_api_type) {
+
+ uint64_t api_hash = p_api_type == APIAssembly::API_CORE ?
+ GDMono::get_singleton()->get_api_core_hash() :
+ GDMono::get_singleton()->get_api_editor_hash();
+ return String::num_uint64(api_hash) +
+ "_" + String::num_uint64(BindingsGenerator::get_version()) +
+ "_" + String::num_uint64(BindingsGenerator::get_cs_glue_version());
+}
- String api_name = p_api_type == API_CORE ? API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME;
+bool GodotSharpBuilds::make_api_sln(APIAssembly::Type p_api_type) {
+
+ String api_name = p_api_type == APIAssembly::API_CORE ? API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME;
String api_build_config = "Release";
EditorProgress pr("mono_build_release_" + api_name, "Building " + api_name + " solution...", 4);
pr.step("Generating " + api_name + " solution");
- uint64_t core_hash = GDMono::get_singleton()->get_api_core_hash();
- uint64_t editor_hash = GDMono::get_singleton()->get_api_editor_hash();
-
- String core_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir().plus_file(API_ASSEMBLY_NAME "_" + itos(core_hash));
- String editor_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir().plus_file(EDITOR_API_ASSEMBLY_NAME "_" + itos(editor_hash));
+ String core_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir()
+ .plus_file(_api_folder_name(APIAssembly::API_CORE))
+ .plus_file(API_ASSEMBLY_NAME);
+ String editor_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir()
+ .plus_file(_api_folder_name(APIAssembly::API_EDITOR))
+ .plus_file(EDITOR_API_ASSEMBLY_NAME);
- String api_sln_dir = p_api_type == API_CORE ? core_api_sln_dir : editor_api_sln_dir;
+ String api_sln_dir = p_api_type == APIAssembly::API_CORE ? core_api_sln_dir : editor_api_sln_dir;
String api_sln_file = api_sln_dir.plus_file(api_name + ".sln");
if (!DirAccess::exists(api_sln_dir) || !FileAccess::exists(api_sln_file)) {
String core_api_assembly;
- if (p_api_type == API_EDITOR) {
+ if (p_api_type == APIAssembly::API_EDITOR) {
core_api_assembly = core_api_sln_dir.plus_file("bin")
.plus_file(api_build_config)
.plus_file(API_ASSEMBLY_NAME ".dll");
@@ -242,7 +256,7 @@ bool GodotSharpBuilds::make_api_sln(GodotSharpBuilds::APIType p_api_type) {
BindingsGenerator *gen = BindingsGenerator::get_singleton();
bool gen_verbose = OS::get_singleton()->is_stdout_verbose();
- Error err = p_api_type == API_CORE ?
+ Error err = p_api_type == APIAssembly::API_CORE ?
gen->generate_cs_core_project(api_sln_dir, gen_verbose) :
gen->generate_cs_editor_project(api_sln_dir, core_api_assembly, gen_verbose);
@@ -275,7 +289,7 @@ bool GodotSharpBuilds::make_api_sln(GodotSharpBuilds::APIType p_api_type) {
// Copy the built assembly to the assemblies directory
String api_assembly_dir = api_sln_dir.plus_file("bin").plus_file(api_build_config);
- if (!GodotSharpBuilds::copy_api_assembly(api_assembly_dir, res_assemblies_dir, api_name))
+ if (!GodotSharpBuilds::copy_api_assembly(api_assembly_dir, res_assemblies_dir, api_name, p_api_type))
return false;
pr.step("Done");
@@ -288,10 +302,10 @@ bool GodotSharpBuilds::build_project_blocking(const String &p_config) {
if (!FileAccess::exists(GodotSharpDirs::get_project_sln_path()))
return true; // No solution to build
- if (!GodotSharpBuilds::make_api_sln(GodotSharpBuilds::API_CORE))
+ if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_CORE))
return false;
- if (!GodotSharpBuilds::make_api_sln(GodotSharpBuilds::API_EDITOR))
+ if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_EDITOR))
return false;
EditorProgress pr("mono_project_debug_build", "Building project solution...", 2);
diff --git a/modules/mono/editor/godotsharp_builds.h b/modules/mono/editor/godotsharp_builds.h
index 0574fbfe8c..27b771e324 100644
--- a/modules/mono/editor/godotsharp_builds.h
+++ b/modules/mono/editor/godotsharp_builds.h
@@ -31,6 +31,7 @@
#ifndef GODOTSHARP_BUILDS_H
#define GODOTSHARP_BUILDS_H
+#include "../mono_gd/gd_mono.h"
#include "mono_bottom_panel.h"
#include "mono_build_info.h"
@@ -56,17 +57,14 @@ private:
HashMap<MonoBuildInfo, BuildProcess, MonoBuildInfo::Hasher> builds;
+ static String _api_folder_name(APIAssembly::Type p_api_type);
+
static GodotSharpBuilds *singleton;
friend class GDMono;
static void _register_internal_calls();
public:
- enum APIType {
- API_CORE,
- API_EDITOR
- };
-
enum BuildTool {
MSBUILD_MONO,
#ifdef WINDOWS_ENABLED
@@ -89,9 +87,9 @@ public:
bool build_async(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback = NULL);
static bool build_api_sln(const String &p_name, const String &p_api_sln_dir, const String &p_config);
- static bool copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name);
+ 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);
- static bool make_api_sln(APIType p_api_type);
+ static bool make_api_sln(APIAssembly::Type p_api_type);
static bool build_project_blocking(const String &p_config);
diff --git a/modules/mono/editor/godotsharp_editor.cpp b/modules/mono/editor/godotsharp_editor.cpp
index 0919a7355b..998da8bda3 100644
--- a/modules/mono/editor/godotsharp_editor.cpp
+++ b/modules/mono/editor/godotsharp_editor.cpp
@@ -85,10 +85,10 @@ bool GodotSharpEditor::_create_project_solution() {
return false;
}
- if (!GodotSharpBuilds::make_api_sln(GodotSharpBuilds::API_CORE))
+ if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_CORE))
return false;
- if (!GodotSharpBuilds::make_api_sln(GodotSharpBuilds::API_EDITOR))
+ if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_EDITOR))
return false;
pr.step(TTR("Done"));
diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp
index 387929a02e..cd09e6516a 100644
--- a/modules/mono/editor/godotsharp_export.cpp
+++ b/modules/mono/editor/godotsharp_export.cpp
@@ -55,6 +55,9 @@ void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug
// TODO right now there is no way to stop the export process with an error
+ ERR_FAIL_COND(!GDMono::get_singleton()->is_runtime_initialized());
+ ERR_FAIL_NULL(GDMono::get_singleton()->get_tools_domain());
+
String build_config = p_debug ? "Debug" : "Release";
ERR_FAIL_COND(!GodotSharpBuilds::build_project_blocking(build_config));