Mono: Better versioning and gracefully unloading of Godot API assemblies
This commit is contained in:
parent
125fc8cc44
commit
f37090ccf4
|
@ -1135,6 +1135,36 @@ String String::num_int64(int64_t p_num, int base, bool capitalize_hex) {
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String String::num_uint64(uint64_t p_num, int base, bool capitalize_hex) {
|
||||||
|
|
||||||
|
uint64_t n = p_num;
|
||||||
|
|
||||||
|
int chars = 0;
|
||||||
|
do {
|
||||||
|
n /= base;
|
||||||
|
chars++;
|
||||||
|
} while (n);
|
||||||
|
|
||||||
|
String s;
|
||||||
|
s.resize(chars + 1);
|
||||||
|
CharType *c = s.ptrw();
|
||||||
|
c[chars] = 0;
|
||||||
|
n = p_num;
|
||||||
|
do {
|
||||||
|
int mod = ABS(n % base);
|
||||||
|
if (mod >= 10) {
|
||||||
|
char a = (capitalize_hex ? 'A' : 'a');
|
||||||
|
c[--chars] = a + (mod - 10);
|
||||||
|
} else {
|
||||||
|
c[--chars] = '0' + mod;
|
||||||
|
}
|
||||||
|
|
||||||
|
n /= base;
|
||||||
|
} while (n);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
String String::num_real(double p_num) {
|
String String::num_real(double p_num) {
|
||||||
|
|
||||||
String s;
|
String s;
|
||||||
|
|
|
@ -146,6 +146,7 @@ public:
|
||||||
static String num_scientific(double p_num);
|
static String num_scientific(double p_num);
|
||||||
static String num_real(double p_num);
|
static String num_real(double p_num);
|
||||||
static String num_int64(int64_t p_num, int base = 10, bool capitalize_hex = false);
|
static String num_int64(int64_t p_num, int base = 10, bool capitalize_hex = false);
|
||||||
|
static String num_uint64(uint64_t p_num, int base = 10, bool capitalize_hex = false);
|
||||||
static String chr(CharType p_char);
|
static String chr(CharType p_char);
|
||||||
static String md5(const uint8_t *p_md5);
|
static String md5(const uint8_t *p_md5);
|
||||||
static String hex_encode_buffer(const uint8_t *p_buffer, int p_len);
|
static String hex_encode_buffer(const uint8_t *p_buffer, int p_len);
|
||||||
|
|
|
@ -31,11 +31,18 @@ def make_cs_files_header(src, dst):
|
||||||
if i > 0:
|
if i > 0:
|
||||||
header.write(', ')
|
header.write(', ')
|
||||||
header.write(byte_to_str(buf[buf_idx]))
|
header.write(byte_to_str(buf[buf_idx]))
|
||||||
inserted_files += '\tr_files.insert(\"' + file + '\", ' \
|
inserted_files += '\tr_files.insert("' + file + '", ' \
|
||||||
'CompressedFile(_cs_' + name + '_compressed_size, ' \
|
'CompressedFile(_cs_' + name + '_compressed_size, ' \
|
||||||
'_cs_' + name + '_uncompressed_size, ' \
|
'_cs_' + name + '_uncompressed_size, ' \
|
||||||
'_cs_' + name + '_compressed));\n'
|
'_cs_' + name + '_compressed));\n'
|
||||||
header.write(' };\n')
|
header.write(' };\n')
|
||||||
|
version_file = os.path.join(src, 'VERSION.txt')
|
||||||
|
with open(version_file, 'r') as content_file:
|
||||||
|
try:
|
||||||
|
glue_version = int(content_file.read()) # make sure the format is valid
|
||||||
|
header.write('\n#define CS_GLUE_VERSION UINT32_C(' + str(glue_version) + ')\n')
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError('Invalid C# glue version in: ' + version_file)
|
||||||
header.write('\nstruct CompressedFile\n' '{\n'
|
header.write('\nstruct CompressedFile\n' '{\n'
|
||||||
'\tint compressed_size;\n' '\tint uncompressed_size;\n' '\tconst unsigned char* data;\n'
|
'\tint compressed_size;\n' '\tint uncompressed_size;\n' '\tconst unsigned char* data;\n'
|
||||||
'\n\tCompressedFile(int p_comp_size, int p_uncomp_size, const unsigned char* p_data)\n'
|
'\n\tCompressedFile(int p_comp_size, int p_uncomp_size, const unsigned char* p_data)\n'
|
||||||
|
@ -80,23 +87,23 @@ def find_msbuild_unix(filename):
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
hint_dirs = ['/opt/novell/mono/bin']
|
hint_dirs = ['/opt/novell/mono/bin']
|
||||||
if sys.platform == "darwin":
|
if sys.platform == 'darwin':
|
||||||
hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin'] + hint_dirs
|
hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin'] + hint_dirs
|
||||||
|
|
||||||
for hint_dir in hint_dirs:
|
for hint_dir in hint_dirs:
|
||||||
hint_path = os.path.join(hint_dir, filename)
|
hint_path = os.path.join(hint_dir, filename)
|
||||||
if os.path.isfile(hint_path):
|
if os.path.isfile(hint_path):
|
||||||
return hint_path
|
return hint_path
|
||||||
elif os.path.isfile(hint_path + ".exe"):
|
elif os.path.isfile(hint_path + '.exe'):
|
||||||
return hint_path + ".exe"
|
return hint_path + '.exe'
|
||||||
|
|
||||||
for hint_dir in os.environ["PATH"].split(os.pathsep):
|
for hint_dir in os.environ['PATH'].split(os.pathsep):
|
||||||
hint_dir = hint_dir.strip('"')
|
hint_dir = hint_dir.strip('"')
|
||||||
hint_path = os.path.join(hint_dir, filename)
|
hint_path = os.path.join(hint_dir, filename)
|
||||||
if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
|
if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
|
||||||
return hint_path
|
return hint_path
|
||||||
if os.path.isfile(hint_path + ".exe") and os.access(hint_path + ".exe", os.X_OK):
|
if os.path.isfile(hint_path + '.exe') and os.access(hint_path + '.exe', os.X_OK):
|
||||||
return hint_path + ".exe"
|
return hint_path + '.exe'
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -152,7 +159,7 @@ def mono_build_solution(source, target, env):
|
||||||
xbuild_fallback = env['xbuild_fallback']
|
xbuild_fallback = env['xbuild_fallback']
|
||||||
|
|
||||||
if xbuild_fallback and os.name == 'nt':
|
if xbuild_fallback and os.name == 'nt':
|
||||||
print("Option 'xbuild_fallback' not supported on Windows")
|
print('Option \'xbuild_fallback\' not supported on Windows')
|
||||||
xbuild_fallback = False
|
xbuild_fallback = False
|
||||||
|
|
||||||
if xbuild_fallback:
|
if xbuild_fallback:
|
||||||
|
|
|
@ -456,7 +456,7 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info()
|
||||||
#ifdef DEBUG_ENABLED
|
#ifdef DEBUG_ENABLED
|
||||||
// Printing an error here will result in endless recursion, so we must be careful
|
// Printing an error here will result in endless recursion, so we must be careful
|
||||||
|
|
||||||
if (!gdmono->is_runtime_initialized() || !GDMono::get_singleton()->get_api_assembly() || !GDMonoUtils::mono_cache.corlib_cache_updated)
|
if (!gdmono->is_runtime_initialized() || !GDMono::get_singleton()->get_core_api_assembly() || !GDMonoUtils::mono_cache.corlib_cache_updated)
|
||||||
return Vector<StackInfo>();
|
return Vector<StackInfo>();
|
||||||
|
|
||||||
MonoObject *stack_trace = mono_object_new(mono_domain_get(), CACHED_CLASS(System_Diagnostics_StackTrace)->get_mono_ptr());
|
MonoObject *stack_trace = mono_object_new(mono_domain_get(), CACHED_CLASS(System_Diagnostics_StackTrace)->get_mono_ptr());
|
||||||
|
|
|
@ -70,8 +70,6 @@
|
||||||
|
|
||||||
#define LOCAL_RET "ret"
|
#define LOCAL_RET "ret"
|
||||||
|
|
||||||
#define CS_CLASS_NATIVECALLS "NativeCalls"
|
|
||||||
#define CS_CLASS_NATIVECALLS_EDITOR "EditorNativeCalls"
|
|
||||||
#define CS_FIELD_MEMORYOWN "memoryOwn"
|
#define CS_FIELD_MEMORYOWN "memoryOwn"
|
||||||
#define CS_PARAM_METHODBIND "method"
|
#define CS_PARAM_METHODBIND "method"
|
||||||
#define CS_PARAM_INSTANCE "ptr"
|
#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_TO_DICT C_NS_MONOMARSHAL "::mono_object_to_Dictionary"
|
||||||
#define C_METHOD_MANAGED_FROM_DICT C_NS_MONOMARSHAL "::Dictionary_to_mono_object"
|
#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";
|
const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN = "\t%0 %1_in = %1;\n";
|
||||||
|
|
||||||
bool BindingsGenerator::verbose_output = false;
|
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"
|
"using System.Collections.Generic;\n"
|
||||||
"\n");
|
"\n");
|
||||||
cs_icalls_content.push_back("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
|
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) \
|
#define ADD_INTERNAL_CALL(m_icall) \
|
||||||
if (!m_icall.editor_only) { \
|
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);
|
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);
|
Error err = _save_file(internal_methods_file, cs_icalls_content);
|
||||||
if (err != OK)
|
if (err != OK)
|
||||||
|
@ -626,7 +634,15 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_output_dir,
|
||||||
"using System.Collections.Generic;\n"
|
"using System.Collections.Generic;\n"
|
||||||
"\n");
|
"\n");
|
||||||
cs_icalls_content.push_back("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
|
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) \
|
#define ADD_INTERNAL_CALL(m_icall) \
|
||||||
if (m_icall.editor_only) { \
|
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);
|
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);
|
Error err = _save_file(internal_methods_file, cs_icalls_content);
|
||||||
if (err != OK)
|
if (err != OK)
|
||||||
|
@ -882,7 +898,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
|
||||||
output.push_back("\";\n");
|
output.push_back("\";\n");
|
||||||
|
|
||||||
output.push_back(INDENT2 "internal static IntPtr " BINDINGS_PTR_FIELD " = ");
|
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("." ICALL_PREFIX);
|
||||||
output.push_back(itype.name);
|
output.push_back(itype.name);
|
||||||
output.push_back(SINGLETON_ICALL_SUFFIX "();\n");
|
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
|
// 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
|
// 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(")\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("." + ctor_method);
|
||||||
output.push_back("(this);\n" CLOSE_BLOCK_L2);
|
output.push_back("(this);\n" CLOSE_BLOCK_L2);
|
||||||
} else {
|
} else {
|
||||||
|
@ -956,7 +972,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
|
||||||
"if (disposed) return;\n" INDENT3
|
"if (disposed) return;\n" INDENT3
|
||||||
"if (" BINDINGS_PTR_FIELD " != IntPtr.Zero)\n" OPEN_BLOCK_L3
|
"if (" BINDINGS_PTR_FIELD " != IntPtr.Zero)\n" OPEN_BLOCK_L3
|
||||||
"if (" CS_FIELD_MEMORYOWN ")\n" OPEN_BLOCK_L4 CS_FIELD_MEMORYOWN
|
"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 ");\n" CLOSE_BLOCK_L4 CLOSE_BLOCK_L3 INDENT3
|
||||||
"this." BINDINGS_PTR_FIELD " = IntPtr.Zero;\n" INDENT3
|
"this." BINDINGS_PTR_FIELD " = IntPtr.Zero;\n" INDENT3
|
||||||
"GC.SuppressFinalize(this);\n" INDENT3 "disposed = true;\n" CLOSE_BLOCK_L2);
|
"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) {
|
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(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(p_imethod.name);
|
||||||
p_output.push_back("\");\n");
|
p_output.push_back("\");\n");
|
||||||
}
|
}
|
||||||
|
@ -1310,7 +1326,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
|
||||||
|
|
||||||
const InternalCall *im_icall = match->value();
|
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";
|
im_call += "." + im_icall->name + "(" + icall_params + ");\n";
|
||||||
|
|
||||||
if (p_imethod.arguments.size())
|
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("namespace GodotSharpBindings\n" OPEN_BLOCK);
|
||||||
|
|
||||||
output.push_back("uint64_t get_core_api_hash() { return ");
|
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"
|
output.push_back("#ifdef TOOLS_ENABLED\n"
|
||||||
"uint64_t get_editor_api_hash() { return ");
|
"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");
|
"; }\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("void register_generated_icalls() " OPEN_BLOCK);
|
||||||
output.push_back("\tgodot_register_header_icalls();");
|
output.push_back("\tgodot_register_header_icalls();");
|
||||||
|
|
||||||
#define ADD_INTERNAL_CALL_REGISTRATION(m_icall) \
|
#define ADD_INTERNAL_CALL_REGISTRATION(m_icall) \
|
||||||
{ \
|
{ \
|
||||||
output.push_back("\tmono_add_internal_call("); \
|
output.push_back("\tmono_add_internal_call("); \
|
||||||
output.push_back("\"" BINDINGS_NAMESPACE "."); \
|
output.push_back("\"" BINDINGS_NAMESPACE "."); \
|
||||||
output.push_back(m_icall.editor_only ? CS_CLASS_NATIVECALLS_EDITOR : CS_CLASS_NATIVECALLS); \
|
output.push_back(m_icall.editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS); \
|
||||||
output.push_back("::"); \
|
output.push_back("::"); \
|
||||||
output.push_back(m_icall.name); \
|
output.push_back(m_icall.name); \
|
||||||
output.push_back("\", (void*)"); \
|
output.push_back("\", (void*)"); \
|
||||||
output.push_back(m_icall.name); \
|
output.push_back(m_icall.name); \
|
||||||
output.push_back(");\n"); \
|
output.push_back(");\n"); \
|
||||||
}
|
}
|
||||||
|
|
||||||
bool tools_sequence = false;
|
bool tools_sequence = false;
|
||||||
|
@ -1486,6 +1510,14 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
|
||||||
return OK;
|
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) {
|
Error BindingsGenerator::_save_file(const String &p_path, const List<String> &p_content) {
|
||||||
|
|
||||||
FileAccessRef file = FileAccess::open(p_path, FileAccess::WRITE);
|
FileAccessRef file = FileAccess::open(p_path, FileAccess::WRITE);
|
||||||
|
|
|
@ -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_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);
|
Error generate_glue(const String &p_output_dir);
|
||||||
|
|
||||||
|
static uint32_t get_version();
|
||||||
|
static uint32_t get_cs_glue_version();
|
||||||
|
|
||||||
void initialize();
|
void initialize();
|
||||||
|
|
||||||
_FORCE_INLINE_ static BindingsGenerator *get_singleton() {
|
_FORCE_INLINE_ static BindingsGenerator *get_singleton() {
|
||||||
|
|
|
@ -33,7 +33,6 @@
|
||||||
#include "main/main.h"
|
#include "main/main.h"
|
||||||
|
|
||||||
#include "../godotsharp_dirs.h"
|
#include "../godotsharp_dirs.h"
|
||||||
#include "../mono_gd/gd_mono.h"
|
|
||||||
#include "../mono_gd/gd_mono_class.h"
|
#include "../mono_gd/gd_mono_class.h"
|
||||||
#include "../mono_gd/gd_mono_marshal.h"
|
#include "../mono_gd/gd_mono_marshal.h"
|
||||||
#include "../utils/path_utils.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;
|
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_file = p_assembly_name + ".dll";
|
||||||
String assembly_src = p_src_dir.plus_file(assembly_file);
|
String assembly_src = p_src_dir.plus_file(assembly_file);
|
||||||
String assembly_dst = p_dst_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);
|
DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
||||||
|
|
||||||
String xml_file = p_assembly_name + ".xml";
|
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);
|
show_build_error_dialog("Failed to copy " + assembly_file);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GDMono::get_singleton()->metadata_set_api_assembly_invalidated(p_api_type, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GodotSharpBuilds::make_api_sln(GodotSharpBuilds::APIType p_api_type) {
|
String GodotSharpBuilds::_api_folder_name(APIAssembly::Type p_api_type) {
|
||||||
|
|
||||||
String api_name = p_api_type == API_CORE ? API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME;
|
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 == APIAssembly::API_CORE ? API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME;
|
||||||
String api_build_config = "Release";
|
String api_build_config = "Release";
|
||||||
|
|
||||||
EditorProgress pr("mono_build_release_" + api_name, "Building " + api_name + " solution...", 4);
|
EditorProgress pr("mono_build_release_" + api_name, "Building " + api_name + " solution...", 4);
|
||||||
|
|
||||||
pr.step("Generating " + api_name + " solution");
|
pr.step("Generating " + api_name + " solution");
|
||||||
|
|
||||||
uint64_t core_hash = GDMono::get_singleton()->get_api_core_hash();
|
String core_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir()
|
||||||
uint64_t editor_hash = GDMono::get_singleton()->get_api_editor_hash();
|
.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 core_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir().plus_file(API_ASSEMBLY_NAME "_" + itos(core_hash));
|
String api_sln_dir = p_api_type == APIAssembly::API_CORE ? core_api_sln_dir : editor_api_sln_dir;
|
||||||
String editor_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir().plus_file(EDITOR_API_ASSEMBLY_NAME "_" + itos(editor_hash));
|
|
||||||
|
|
||||||
String api_sln_dir = p_api_type == API_CORE ? core_api_sln_dir : editor_api_sln_dir;
|
|
||||||
String api_sln_file = api_sln_dir.plus_file(api_name + ".sln");
|
String api_sln_file = api_sln_dir.plus_file(api_name + ".sln");
|
||||||
|
|
||||||
if (!DirAccess::exists(api_sln_dir) || !FileAccess::exists(api_sln_file)) {
|
if (!DirAccess::exists(api_sln_dir) || !FileAccess::exists(api_sln_file)) {
|
||||||
String core_api_assembly;
|
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")
|
core_api_assembly = core_api_sln_dir.plus_file("bin")
|
||||||
.plus_file(api_build_config)
|
.plus_file(api_build_config)
|
||||||
.plus_file(API_ASSEMBLY_NAME ".dll");
|
.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();
|
BindingsGenerator *gen = BindingsGenerator::get_singleton();
|
||||||
bool gen_verbose = OS::get_singleton()->is_stdout_verbose();
|
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_core_project(api_sln_dir, gen_verbose) :
|
||||||
gen->generate_cs_editor_project(api_sln_dir, core_api_assembly, 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
|
// Copy the built assembly to the assemblies directory
|
||||||
String api_assembly_dir = api_sln_dir.plus_file("bin").plus_file(api_build_config);
|
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;
|
return false;
|
||||||
|
|
||||||
pr.step("Done");
|
pr.step("Done");
|
||||||
|
@ -288,10 +302,10 @@ bool GodotSharpBuilds::build_project_blocking(const String &p_config) {
|
||||||
if (!FileAccess::exists(GodotSharpDirs::get_project_sln_path()))
|
if (!FileAccess::exists(GodotSharpDirs::get_project_sln_path()))
|
||||||
return true; // No solution to build
|
return true; // No solution to build
|
||||||
|
|
||||||
if (!GodotSharpBuilds::make_api_sln(GodotSharpBuilds::API_CORE))
|
if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_CORE))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!GodotSharpBuilds::make_api_sln(GodotSharpBuilds::API_EDITOR))
|
if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_EDITOR))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
EditorProgress pr("mono_project_debug_build", "Building project solution...", 2);
|
EditorProgress pr("mono_project_debug_build", "Building project solution...", 2);
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#ifndef GODOTSHARP_BUILDS_H
|
#ifndef GODOTSHARP_BUILDS_H
|
||||||
#define GODOTSHARP_BUILDS_H
|
#define GODOTSHARP_BUILDS_H
|
||||||
|
|
||||||
|
#include "../mono_gd/gd_mono.h"
|
||||||
#include "mono_bottom_panel.h"
|
#include "mono_bottom_panel.h"
|
||||||
#include "mono_build_info.h"
|
#include "mono_build_info.h"
|
||||||
|
|
||||||
|
@ -56,17 +57,14 @@ private:
|
||||||
|
|
||||||
HashMap<MonoBuildInfo, BuildProcess, MonoBuildInfo::Hasher> builds;
|
HashMap<MonoBuildInfo, BuildProcess, MonoBuildInfo::Hasher> builds;
|
||||||
|
|
||||||
|
static String _api_folder_name(APIAssembly::Type p_api_type);
|
||||||
|
|
||||||
static GodotSharpBuilds *singleton;
|
static GodotSharpBuilds *singleton;
|
||||||
|
|
||||||
friend class GDMono;
|
friend class GDMono;
|
||||||
static void _register_internal_calls();
|
static void _register_internal_calls();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum APIType {
|
|
||||||
API_CORE,
|
|
||||||
API_EDITOR
|
|
||||||
};
|
|
||||||
|
|
||||||
enum BuildTool {
|
enum BuildTool {
|
||||||
MSBUILD_MONO,
|
MSBUILD_MONO,
|
||||||
#ifdef WINDOWS_ENABLED
|
#ifdef WINDOWS_ENABLED
|
||||||
|
@ -89,9 +87,9 @@ public:
|
||||||
bool build_async(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback = NULL);
|
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 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);
|
static bool build_project_blocking(const String &p_config);
|
||||||
|
|
||||||
|
|
|
@ -85,10 +85,10 @@ bool GodotSharpEditor::_create_project_solution() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!GodotSharpBuilds::make_api_sln(GodotSharpBuilds::API_CORE))
|
if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_CORE))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!GodotSharpBuilds::make_api_sln(GodotSharpBuilds::API_EDITOR))
|
if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_EDITOR))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
pr.step(TTR("Done"));
|
pr.step(TTR("Done"));
|
||||||
|
|
|
@ -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
|
// 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";
|
String build_config = p_debug ? "Debug" : "Release";
|
||||||
|
|
||||||
ERR_FAIL_COND(!GodotSharpBuilds::build_project_blocking(build_config));
|
ERR_FAIL_COND(!GodotSharpBuilds::build_project_blocking(build_config));
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
|
@ -39,4 +39,7 @@
|
||||||
#define EDITOR_API_ASSEMBLY_NAME "GodotSharpEditor"
|
#define EDITOR_API_ASSEMBLY_NAME "GodotSharpEditor"
|
||||||
#define EDITOR_TOOLS_ASSEMBLY_NAME "GodotSharpTools"
|
#define EDITOR_TOOLS_ASSEMBLY_NAME "GodotSharpTools"
|
||||||
|
|
||||||
|
#define BINDINGS_CLASS_NATIVECALLS "NativeCalls"
|
||||||
|
#define BINDINGS_CLASS_NATIVECALLS_EDITOR "EditorNativeCalls"
|
||||||
|
|
||||||
#endif // GODOTSHARP_DEFS_H
|
#endif // GODOTSHARP_DEFS_H
|
||||||
|
|
|
@ -42,7 +42,10 @@
|
||||||
#include "project_settings.h"
|
#include "project_settings.h"
|
||||||
|
|
||||||
#include "../csharp_script.h"
|
#include "../csharp_script.h"
|
||||||
|
#include "../godotsharp_dirs.h"
|
||||||
#include "../utils/path_utils.h"
|
#include "../utils/path_utils.h"
|
||||||
|
#include "gd_mono_class.h"
|
||||||
|
#include "gd_mono_marshal.h"
|
||||||
#include "gd_mono_utils.h"
|
#include "gd_mono_utils.h"
|
||||||
|
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
|
@ -208,7 +211,46 @@ void GDMono::initialize() {
|
||||||
_register_internal_calls();
|
_register_internal_calls();
|
||||||
|
|
||||||
// The following assemblies are not required at initialization
|
// The following assemblies are not required at initialization
|
||||||
_load_all_script_assemblies();
|
#ifndef MONO_GLUE_DISABLED
|
||||||
|
if (_load_api_assemblies()) {
|
||||||
|
if (!core_api_assembly_out_of_sync && !editor_api_assembly_out_of_sync && GDMonoUtils::mono_cache.godot_api_cache_updated) {
|
||||||
|
// Everything is fine with the api assemblies, load the project assembly
|
||||||
|
_load_project_assembly();
|
||||||
|
} else {
|
||||||
|
#ifdef TOOLS_ENABLED
|
||||||
|
// The assembly was successfuly 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("The loaded Core API assembly is out of sync");
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (editor_api_assembly_out_of_sync) {
|
||||||
|
ERR_PRINT("The loaded Editor API assembly is out of sync");
|
||||||
|
metadata_set_api_assembly_invalidated(APIAssembly::API_EDITOR, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
OS::get_singleton()->print("Mono: Proceeding to unload scripts domain because of invalid API assemblies\n");
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (OS::get_singleton()->is_stdout_verbose())
|
||||||
|
OS::get_singleton()->print("Mono: Glue disabled, ignoring script assemblies\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
mono_install_unhandled_exception_hook(gdmono_unhandled_exception_hook, NULL);
|
mono_install_unhandled_exception_hook(gdmono_unhandled_exception_hook, NULL);
|
||||||
|
|
||||||
|
@ -219,7 +261,11 @@ void GDMono::initialize() {
|
||||||
namespace GodotSharpBindings {
|
namespace GodotSharpBindings {
|
||||||
|
|
||||||
uint64_t get_core_api_hash();
|
uint64_t get_core_api_hash();
|
||||||
|
#ifdef TOOLS_ENABLED
|
||||||
uint64_t get_editor_api_hash();
|
uint64_t get_editor_api_hash();
|
||||||
|
#endif // TOOLS_ENABLED
|
||||||
|
uint32_t get_bindings_version();
|
||||||
|
uint32_t get_cs_glue_version();
|
||||||
|
|
||||||
void register_generated_icalls();
|
void register_generated_icalls();
|
||||||
} // namespace GodotSharpBindings
|
} // namespace GodotSharpBindings
|
||||||
|
@ -313,6 +359,36 @@ bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMo
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
APIAssembly::Version APIAssembly::Version::get_from_loaded_assembly(GDMonoAssembly *p_api_assembly, APIAssembly::Type p_api_type) {
|
||||||
|
APIAssembly::Version api_assembly_version;
|
||||||
|
|
||||||
|
const char *nativecalls_name = p_api_type == APIAssembly::API_CORE ?
|
||||||
|
BINDINGS_CLASS_NATIVECALLS :
|
||||||
|
BINDINGS_CLASS_NATIVECALLS_EDITOR;
|
||||||
|
|
||||||
|
GDMonoClass *nativecalls_klass = p_api_assembly->get_class(BINDINGS_NAMESPACE, nativecalls_name);
|
||||||
|
|
||||||
|
if (nativecalls_klass) {
|
||||||
|
GDMonoField *api_hash_field = nativecalls_klass->get_field("godot_api_hash");
|
||||||
|
if (api_hash_field)
|
||||||
|
api_assembly_version.godot_api_hash = GDMonoMarshal::unbox<uint64_t>(api_hash_field->get_value(NULL));
|
||||||
|
|
||||||
|
GDMonoField *binds_ver_field = nativecalls_klass->get_field("bindings_version");
|
||||||
|
if (binds_ver_field)
|
||||||
|
api_assembly_version.bindings_version = GDMonoMarshal::unbox<uint32_t>(binds_ver_field->get_value(NULL));
|
||||||
|
|
||||||
|
GDMonoField *cs_glue_ver_field = nativecalls_klass->get_field("cs_glue_version");
|
||||||
|
if (cs_glue_ver_field)
|
||||||
|
api_assembly_version.cs_glue_version = GDMonoMarshal::unbox<uint32_t>(cs_glue_ver_field->get_value(NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
return api_assembly_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
String APIAssembly::to_string(APIAssembly::Type p_type) {
|
||||||
|
return p_type == APIAssembly::API_CORE ? "API_CORE" : "API_EDITOR";
|
||||||
|
}
|
||||||
|
|
||||||
bool GDMono::_load_corlib_assembly() {
|
bool GDMono::_load_corlib_assembly() {
|
||||||
|
|
||||||
if (corlib_assembly)
|
if (corlib_assembly)
|
||||||
|
@ -328,13 +404,25 @@ bool GDMono::_load_corlib_assembly() {
|
||||||
|
|
||||||
bool GDMono::_load_core_api_assembly() {
|
bool GDMono::_load_core_api_assembly() {
|
||||||
|
|
||||||
if (api_assembly)
|
if (core_api_assembly)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
bool success = load_assembly(API_ASSEMBLY_NAME, &api_assembly);
|
#ifdef TOOLS_ENABLED
|
||||||
|
if (metadata_is_api_assembly_invalidated(APIAssembly::API_CORE))
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (success)
|
bool success = load_assembly(API_ASSEMBLY_NAME, &core_api_assembly);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
#ifndef MONO_GLUE_DISABLED
|
||||||
|
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 ||
|
||||||
|
GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version;
|
||||||
|
#endif
|
||||||
GDMonoUtils::update_godot_api_cache();
|
GDMonoUtils::update_godot_api_cache();
|
||||||
|
}
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
@ -345,7 +433,23 @@ bool GDMono::_load_editor_api_assembly() {
|
||||||
if (editor_api_assembly)
|
if (editor_api_assembly)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return load_assembly(EDITOR_API_ASSEMBLY_NAME, &editor_api_assembly);
|
#ifdef TOOLS_ENABLED
|
||||||
|
if (metadata_is_api_assembly_invalidated(APIAssembly::API_EDITOR))
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool success = load_assembly(EDITOR_API_ASSEMBLY_NAME, &editor_api_assembly);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
#ifndef MONO_GLUE_DISABLED
|
||||||
|
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 ||
|
||||||
|
GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -373,15 +477,18 @@ bool GDMono::_load_project_assembly() {
|
||||||
|
|
||||||
bool success = load_assembly(name, &project_assembly);
|
bool success = load_assembly(name, &project_assembly);
|
||||||
|
|
||||||
if (success)
|
if (success) {
|
||||||
mono_assembly_set_main(project_assembly->get_assembly());
|
mono_assembly_set_main(project_assembly->get_assembly());
|
||||||
|
} else {
|
||||||
|
if (OS::get_singleton()->is_stdout_verbose())
|
||||||
|
OS::get_singleton()->printerr("Mono: Failed to load project assembly\n");
|
||||||
|
}
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GDMono::_load_all_script_assemblies() {
|
bool GDMono::_load_api_assemblies() {
|
||||||
|
|
||||||
#ifndef MONO_GLUE_DISABLED
|
|
||||||
if (!_load_core_api_assembly()) {
|
if (!_load_core_api_assembly()) {
|
||||||
if (OS::get_singleton()->is_stdout_verbose())
|
if (OS::get_singleton()->is_stdout_verbose())
|
||||||
OS::get_singleton()->printerr("Mono: Failed to load Core API assembly\n");
|
OS::get_singleton()->printerr("Mono: Failed to load Core API assembly\n");
|
||||||
|
@ -396,21 +503,73 @@ bool GDMono::_load_all_script_assemblies() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_load_project_assembly()) {
|
return true;
|
||||||
if (OS::get_singleton()->is_stdout_verbose())
|
}
|
||||||
OS::get_singleton()->printerr("Mono: Failed to load project assembly\n");
|
|
||||||
return false;
|
#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 ?
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
Error save_err = metadata->save(path);
|
||||||
#else
|
ERR_FAIL_COND(save_err != OK);
|
||||||
if (OS::get_singleton()->is_stdout_verbose())
|
|
||||||
OS::get_singleton()->print("Mono: Glue disbled, ignoring script assemblies\n");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 ?
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
Error GDMono::_load_scripts_domain() {
|
Error GDMono::_load_scripts_domain() {
|
||||||
|
|
||||||
ERR_FAIL_COND_V(scripts_domain != NULL, ERR_BUG);
|
ERR_FAIL_COND_V(scripts_domain != NULL, ERR_BUG);
|
||||||
|
@ -452,12 +611,15 @@ Error GDMono::_unload_scripts_domain() {
|
||||||
|
|
||||||
_domain_assemblies_cleanup(mono_domain_get_id(scripts_domain));
|
_domain_assemblies_cleanup(mono_domain_get_id(scripts_domain));
|
||||||
|
|
||||||
api_assembly = NULL;
|
core_api_assembly = NULL;
|
||||||
project_assembly = NULL;
|
project_assembly = NULL;
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
editor_api_assembly = NULL;
|
editor_api_assembly = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
core_api_assembly_out_of_sync = false;
|
||||||
|
editor_api_assembly_out_of_sync = false;
|
||||||
|
|
||||||
MonoDomain *domain = scripts_domain;
|
MonoDomain *domain = scripts_domain;
|
||||||
scripts_domain = NULL;
|
scripts_domain = NULL;
|
||||||
|
|
||||||
|
@ -512,12 +674,45 @@ Error GDMono::reload_scripts_domain() {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_load_all_script_assemblies()) {
|
#ifndef MONO_GLUE_DISABLED
|
||||||
if (OS::get_singleton()->is_stdout_verbose())
|
if (!_load_api_assemblies()) {
|
||||||
OS::get_singleton()->printerr("Mono: Failed to load script assemblies\n");
|
|
||||||
return ERR_CANT_OPEN;
|
return ERR_CANT_OPEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!core_api_assembly_out_of_sync && !editor_api_assembly_out_of_sync && GDMonoUtils::mono_cache.godot_api_cache_updated) {
|
||||||
|
// Everything is fine with the api assemblies, load the project assembly
|
||||||
|
_load_project_assembly();
|
||||||
|
} else {
|
||||||
|
// The assembly was successfuly 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) {
|
||||||
|
metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true);
|
||||||
|
} else if (!GDMonoUtils::mono_cache.godot_api_cache_updated) {
|
||||||
|
ERR_PRINT("Core API assembly is in sync, but the cache update failed");
|
||||||
|
metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (editor_api_assembly_out_of_sync) {
|
||||||
|
metadata_set_api_assembly_invalidated(APIAssembly::API_EDITOR, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Error err = _unload_scripts_domain();
|
||||||
|
if (err != OK) {
|
||||||
|
WARN_PRINT("Mono: Failed to unload scripts domain");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ERR_CANT_RESOLVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_load_project_assembly())
|
||||||
|
return ERR_CANT_OPEN;
|
||||||
|
#else
|
||||||
|
if (OS::get_singleton()->is_stdout_verbose())
|
||||||
|
OS::get_singleton()->print("Mono: Glue disabled, ignoring script assemblies\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -604,8 +799,11 @@ GDMono::GDMono() {
|
||||||
tools_domain = NULL;
|
tools_domain = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
core_api_assembly_out_of_sync = false;
|
||||||
|
editor_api_assembly_out_of_sync = false;
|
||||||
|
|
||||||
corlib_assembly = NULL;
|
corlib_assembly = NULL;
|
||||||
api_assembly = NULL;
|
core_api_assembly = NULL;
|
||||||
project_assembly = NULL;
|
project_assembly = NULL;
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
editor_api_assembly = NULL;
|
editor_api_assembly = NULL;
|
||||||
|
|
|
@ -31,6 +31,8 @@
|
||||||
#ifndef GD_MONO_H
|
#ifndef GD_MONO_H
|
||||||
#define GD_MONO_H
|
#define GD_MONO_H
|
||||||
|
|
||||||
|
#include "core/io/config_file.h"
|
||||||
|
|
||||||
#include "../godotsharp_defs.h"
|
#include "../godotsharp_defs.h"
|
||||||
#include "gd_mono_assembly.h"
|
#include "gd_mono_assembly.h"
|
||||||
#include "gd_mono_log.h"
|
#include "gd_mono_log.h"
|
||||||
|
@ -39,6 +41,43 @@
|
||||||
#include "../utils/mono_reg_utils.h"
|
#include "../utils/mono_reg_utils.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace APIAssembly {
|
||||||
|
enum Type {
|
||||||
|
API_CORE,
|
||||||
|
API_EDITOR
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Version {
|
||||||
|
uint64_t godot_api_hash;
|
||||||
|
uint32_t bindings_version;
|
||||||
|
uint32_t cs_glue_version;
|
||||||
|
|
||||||
|
bool operator==(const Version &p_other) const {
|
||||||
|
return godot_api_hash == p_other.godot_api_hash &&
|
||||||
|
bindings_version == p_other.bindings_version &&
|
||||||
|
cs_glue_version == p_other.cs_glue_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
Version() :
|
||||||
|
godot_api_hash(0),
|
||||||
|
bindings_version(0),
|
||||||
|
cs_glue_version(0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
Version(uint64_t p_godot_api_hash,
|
||||||
|
uint32_t p_bindings_version,
|
||||||
|
uint32_t p_cs_glue_version) :
|
||||||
|
godot_api_hash(p_godot_api_hash),
|
||||||
|
bindings_version(p_bindings_version),
|
||||||
|
cs_glue_version(p_cs_glue_version) {
|
||||||
|
}
|
||||||
|
|
||||||
|
static Version get_from_loaded_assembly(GDMonoAssembly *p_api_assembly, Type p_api_type);
|
||||||
|
};
|
||||||
|
|
||||||
|
String to_string(Type p_type);
|
||||||
|
} // namespace APIAssembly
|
||||||
|
|
||||||
#define SCRIPTS_DOMAIN GDMono::get_singleton()->get_scripts_domain()
|
#define SCRIPTS_DOMAIN GDMono::get_singleton()->get_scripts_domain()
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
#define TOOLS_DOMAIN GDMono::get_singleton()->get_tools_domain()
|
#define TOOLS_DOMAIN GDMono::get_singleton()->get_tools_domain()
|
||||||
|
@ -55,8 +94,11 @@ class GDMono {
|
||||||
MonoDomain *tools_domain;
|
MonoDomain *tools_domain;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
bool core_api_assembly_out_of_sync;
|
||||||
|
bool editor_api_assembly_out_of_sync;
|
||||||
|
|
||||||
GDMonoAssembly *corlib_assembly;
|
GDMonoAssembly *corlib_assembly;
|
||||||
GDMonoAssembly *api_assembly;
|
GDMonoAssembly *core_api_assembly;
|
||||||
GDMonoAssembly *project_assembly;
|
GDMonoAssembly *project_assembly;
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
GDMonoAssembly *editor_api_assembly;
|
GDMonoAssembly *editor_api_assembly;
|
||||||
|
@ -75,7 +117,11 @@ class GDMono {
|
||||||
#endif
|
#endif
|
||||||
bool _load_project_assembly();
|
bool _load_project_assembly();
|
||||||
|
|
||||||
bool _load_all_script_assemblies();
|
bool _load_api_assemblies();
|
||||||
|
|
||||||
|
#ifdef TOOLS_ENABLED
|
||||||
|
String _get_api_assembly_metadata_path();
|
||||||
|
#endif
|
||||||
|
|
||||||
void _register_internal_calls();
|
void _register_internal_calls();
|
||||||
|
|
||||||
|
@ -111,6 +157,11 @@ public:
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#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);
|
||||||
|
#endif
|
||||||
|
|
||||||
static GDMono *get_singleton() { return singleton; }
|
static GDMono *get_singleton() { return singleton; }
|
||||||
|
|
||||||
// Do not use these, unless you know what you're doing
|
// Do not use these, unless you know what you're doing
|
||||||
|
@ -126,7 +177,7 @@ public:
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
_FORCE_INLINE_ GDMonoAssembly *get_corlib_assembly() const { return corlib_assembly; }
|
_FORCE_INLINE_ GDMonoAssembly *get_corlib_assembly() const { return corlib_assembly; }
|
||||||
_FORCE_INLINE_ GDMonoAssembly *get_api_assembly() const { return api_assembly; }
|
_FORCE_INLINE_ GDMonoAssembly *get_core_api_assembly() const { return core_api_assembly; }
|
||||||
_FORCE_INLINE_ GDMonoAssembly *get_project_assembly() const { return project_assembly; }
|
_FORCE_INLINE_ GDMonoAssembly *get_project_assembly() const { return project_assembly; }
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
_FORCE_INLINE_ GDMonoAssembly *get_editor_api_assembly() const { return editor_api_assembly; }
|
_FORCE_INLINE_ GDMonoAssembly *get_editor_api_assembly() const { return editor_api_assembly; }
|
||||||
|
|
|
@ -143,7 +143,7 @@ void MonoCache::cleanup() {
|
||||||
godot_api_cache_updated = false;
|
godot_api_cache_updated = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class))
|
#define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class))
|
||||||
|
|
||||||
void update_corlib_cache() {
|
void update_corlib_cache() {
|
||||||
|
|
||||||
|
@ -245,7 +245,7 @@ void update_godot_api_cache() {
|
||||||
mono_runtime_object_init(task_scheduler);
|
mono_runtime_object_init(task_scheduler);
|
||||||
mono_cache.task_scheduler_handle = MonoGCHandle::create_strong(task_scheduler);
|
mono_cache.task_scheduler_handle = MonoGCHandle::create_strong(task_scheduler);
|
||||||
|
|
||||||
mono_cache.corlib_cache_updated = true;
|
mono_cache.godot_api_cache_updated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear_cache() {
|
void clear_cache() {
|
||||||
|
@ -305,7 +305,7 @@ GDMonoClass *type_get_proxy_class(const StringName &p_type) {
|
||||||
if (class_name[0] == '_')
|
if (class_name[0] == '_')
|
||||||
class_name = class_name.substr(1, class_name.length());
|
class_name = class_name.substr(1, class_name.length());
|
||||||
|
|
||||||
GDMonoClass *klass = GDMono::get_singleton()->get_api_assembly()->get_class(BINDINGS_NAMESPACE, class_name);
|
GDMonoClass *klass = GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, class_name);
|
||||||
|
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
if (!klass) {
|
if (!klass) {
|
||||||
|
@ -321,7 +321,7 @@ GDMonoClass *get_class_native_base(GDMonoClass *p_class) {
|
||||||
|
|
||||||
do {
|
do {
|
||||||
const GDMonoAssembly *assembly = klass->get_assembly();
|
const GDMonoAssembly *assembly = klass->get_assembly();
|
||||||
if (assembly == GDMono::get_singleton()->get_api_assembly())
|
if (assembly == GDMono::get_singleton()->get_core_api_assembly())
|
||||||
return klass;
|
return klass;
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
if (assembly == GDMono::get_singleton()->get_editor_api_assembly())
|
if (assembly == GDMono::get_singleton()->get_editor_api_assembly())
|
||||||
|
|
Loading…
Reference in New Issue