summaryrefslogtreecommitdiff
path: root/modules/mono/editor
diff options
context:
space:
mode:
Diffstat (limited to 'modules/mono/editor')
-rw-r--r--modules/mono/editor/bindings_generator.cpp92
-rw-r--r--modules/mono/editor/bindings_generator.h3
-rw-r--r--modules/mono/editor/godotsharp_builds.cpp74
-rw-r--r--modules/mono/editor/godotsharp_builds.h16
-rw-r--r--modules/mono/editor/godotsharp_editor.cpp17
-rw-r--r--modules/mono/editor/godotsharp_editor.h1
-rw-r--r--modules/mono/editor/godotsharp_export.cpp166
-rw-r--r--modules/mono/editor/godotsharp_export.h57
-rw-r--r--modules/mono/editor/mono_bottom_panel.cpp33
9 files changed, 369 insertions, 90 deletions
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 62c7a94755..a210b8e480 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(2)
+
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)
@@ -714,7 +730,8 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
}
output.push_back(INDENT1 "public ");
- output.push_back(itype.is_singleton ? "static class " : "class ");
+ bool is_abstract = !ClassDB::can_instance(itype.name) && ClassDB::is_class_enabled(itype.name); // can_instance returns true if there's a constructor and the class is not 'disabled'
+ output.push_back(itype.is_singleton ? "static class " : (is_abstract ? "abstract class " : "class "));
output.push_back(itype.proxy_name);
if (itype.is_singleton) {
@@ -882,7 +899,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 +929,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 +973,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 +1246,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 +1327,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 +1417,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 +1511,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);
@@ -1528,9 +1561,9 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
if (p_imethod.is_vararg) {
if (i < p_imethod.arguments.size() - 1) {
c_in_statements += sformat(arg_type->c_in.size() ? arg_type->c_in : TypeInterface::DEFAULT_VARARG_C_IN, "Variant", c_param_name);
- c_in_statements += "\t" C_LOCAL_PTRCALL_ARGS ".set(0, ";
- c_in_statements += sformat("&%s_in", c_param_name);
- c_in_statements += ");\n";
+ c_in_statements += "\t" C_LOCAL_PTRCALL_ARGS ".set(";
+ c_in_statements += itos(i);
+ c_in_statements += sformat(", &%s_in);\n", c_param_name);
}
} else {
if (i > 0)
@@ -2231,7 +2264,8 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
"this." BINDINGS_PTR_FIELD " = NativeCalls.godot_icall_NodePath_Ctor(path);\n" CLOSE_BLOCK_L2
MEMBER_BEGIN "public static implicit operator NodePath(string from)\n" OPEN_BLOCK_L2 "return new NodePath(from);\n" CLOSE_BLOCK_L2
MEMBER_BEGIN "public static implicit operator string(NodePath from)\n" OPEN_BLOCK_L2
- "return NativeCalls." ICALL_PREFIX "NodePath_operator_String(NodePath." CS_SMETHOD_GETINSTANCE "(from));\n" CLOSE_BLOCK_L2);
+ "return NativeCalls." ICALL_PREFIX "NodePath_operator_String(NodePath." CS_SMETHOD_GETINSTANCE "(from));\n" CLOSE_BLOCK_L2
+ MEMBER_BEGIN "public override string ToString()\n" OPEN_BLOCK_L2 "return (string)this;\n" CLOSE_BLOCK_L2);
builtin_types.insert(itype.cname, itype);
// RID
@@ -2365,7 +2399,7 @@ void BindingsGenerator::_populate_builtin_type(TypeInterface &r_itype, Variant::
imethod.name = mi.name;
imethod.cname = imethod.name;
- imethod.proxy_name = mi.name;
+ imethod.proxy_name = escape_csharp_keyword(snake_to_pascal_case(mi.name));
for (int i = 0; i < mi.arguments.size(); i++) {
ArgumentInterface iarg;
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 6b41b10981..2f2b5768db 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";
@@ -200,36 +201,49 @@ 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;
}
+
+ 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());
+}
+
+bool GodotSharpBuilds::make_api_sln(APIAssembly::Type p_api_type) {
- String api_name = p_api_type == API_CORE ? API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME;
+ 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");
@@ -283,22 +297,22 @@ 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
- 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);
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 +323,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 +381,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 +481,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 +502,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);
@@ -504,11 +525,10 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) {
}
}
-GodotSharpBuilds::BuildProcess::BuildProcess(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback) {
-
- build_info = p_build_info;
- build_tab = NULL;
- exit_callback = p_callback;
- exited = true;
- exit_code = -1;
+GodotSharpBuilds::BuildProcess::BuildProcess(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback) :
+ build_info(p_build_info),
+ build_tab(NULL),
+ exit_callback(p_callback),
+ exited(true),
+ exit_code(-1) {
}
diff --git a/modules/mono/editor/godotsharp_builds.h b/modules/mono/editor/godotsharp_builds.h
index 5d2390ecd9..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,11 +87,13 @@ 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(APIAssembly::Type p_api_type);
- static bool make_api_sln(APIType p_api_type);
+ static bool build_project_blocking(const String &p_config);
- static bool build_project_blocking();
+ static bool editor_build_callback();
GodotSharpBuilds();
~GodotSharpBuilds();
diff --git a/modules/mono/editor/godotsharp_editor.cpp b/modules/mono/editor/godotsharp_editor.cpp
index 0ef3adfdd0..998da8bda3 100644
--- a/modules/mono/editor/godotsharp_editor.cpp
+++ b/modules/mono/editor/godotsharp_editor.cpp
@@ -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
@@ -84,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"));
@@ -278,10 +279,11 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) {
String about_text =
String("C# support in Godot Engine is a brand new feature and a work in progress.\n") +
"It is at the alpha stage and thus not suitable for use in production.\n\n" +
- "As of Godot 3.0, C# support is not feature-complete and can crash in some situations. " +
- "Bugs and usability issues will be addressed gradually over 3.0.x and 3.x releases.\n" +
+ "As of Godot 3.0, C# support is not feature-complete and may crash in some situations. " +
+ "Bugs and usability issues will be addressed gradually over 3.0.x and 3.x releases, " +
+ "including compatibility breaking changes as new features are implemented for a better overall C# experience.\n\n" +
"The main missing feature is the ability to export games using C# assemblies - you will therefore be able to develop and run games in the editor, " +
- "but not to share them as standalone binaries. This feature is of course high on the priority list and should be available in 3.0.1.\n\n" +
+ "but not to share them as standalone binaries yet. This feature is of course high on the priority list and should be available as soon as possible.\n\n" +
"If you experience issues with this Mono build, please report them on Godot's issue tracker with details about your system, Mono version, IDE, etc.:\n\n" +
" https://github.com/godotengine/godot/issues\n\n" +
"Your critical feedback at this stage will play a great role in shaping the C# support in future releases, so thank you!";
@@ -315,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() {
diff --git a/modules/mono/editor/godotsharp_editor.h b/modules/mono/editor/godotsharp_editor.h
index 81c49aec30..66da814c8b 100644
--- a/modules/mono/editor/godotsharp_editor.h
+++ b/modules/mono/editor/godotsharp_editor.h
@@ -32,7 +32,6 @@
#define GODOTSHARP_EDITOR_H
#include "godotsharp_builds.h"
-
#include "monodevelop_instance.h"
class GodotSharpEditor : public Node {
diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp
new file mode 100644
index 0000000000..cd09e6516a
--- /dev/null
+++ b/modules/mono/editor/godotsharp_export.cpp
@@ -0,0 +1,166 @@
+/*************************************************************************/
+/* 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
+
+ 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));
+
+ // 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);
+}
diff --git a/modules/mono/editor/godotsharp_export.h b/modules/mono/editor/godotsharp_export.h
new file mode 100644
index 0000000000..b38db9660c
--- /dev/null
+++ b/modules/mono/editor/godotsharp_export.h
@@ -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
diff --git a/modules/mono/editor/mono_bottom_panel.cpp b/modules/mono/editor/mono_bottom_panel.cpp
index ab62c62616..f1cf0bcdf5 100644
--- a/modules/mono/editor/mono_bottom_panel.cpp
+++ b/modules/mono/editor/mono_bottom_panel.cpp
@@ -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);
@@ -335,16 +335,14 @@ void MonoBuildTab::_update_issues_list() {
Ref<Texture> MonoBuildTab::get_icon_texture() const {
- // FIXME these icons were removed... find something better
-
if (build_exited) {
if (build_result == RESULT_ERROR) {
- return get_icon("DependencyChangedHl", "EditorIcons");
+ return get_icon("StatusError", "EditorIcons");
} else {
- return get_icon("DependencyOkHl", "EditorIcons");
+ return get_icon("StatusSuccess", "EditorIcons");
}
} else {
- return get_icon("GraphTime", "EditorIcons");
+ return get_icon("Stop", "EditorIcons");
}
}
@@ -439,21 +437,16 @@ void MonoBuildTab::_bind_methods() {
ClassDB::bind_method("_issue_activated", &MonoBuildTab::_issue_activated);
}
-MonoBuildTab::MonoBuildTab(const MonoBuildInfo &p_build_info, const String &p_logs_dir) {
-
- build_info = p_build_info;
- logs_dir = p_logs_dir;
-
- build_exited = false;
-
- issues_list = memnew(ItemList);
+MonoBuildTab::MonoBuildTab(const MonoBuildInfo &p_build_info, const String &p_logs_dir) :
+ build_info(p_build_info),
+ logs_dir(p_logs_dir),
+ build_exited(false),
+ issues_list(memnew(ItemList)),
+ error_count(0),
+ warning_count(0),
+ errors_visible(true),
+ warnings_visible(true) {
issues_list->set_v_size_flags(SIZE_EXPAND_FILL);
issues_list->connect("item_activated", this, "_issue_activated");
add_child(issues_list);
-
- error_count = 0;
- warning_count = 0;
-
- errors_visible = true;
- warnings_visible = true;
}