summaryrefslogtreecommitdiff
path: root/modules/mono/editor
diff options
context:
space:
mode:
Diffstat (limited to 'modules/mono/editor')
-rw-r--r--modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs66
-rw-r--r--modules/mono/editor/GodotSharpTools/GodotSharpTools.sln34
-rw-r--r--modules/mono/editor/bindings_generator.cpp1247
-rw-r--r--modules/mono/editor/bindings_generator.h7
-rw-r--r--modules/mono/editor/godotsharp_builds.cpp103
-rw-r--r--modules/mono/editor/godotsharp_builds.h9
-rw-r--r--modules/mono/editor/godotsharp_editor.cpp76
-rw-r--r--modules/mono/editor/godotsharp_editor.h24
-rw-r--r--modules/mono/editor/mono_bottom_panel.cpp37
-rw-r--r--modules/mono/editor/mono_bottom_panel.h4
10 files changed, 876 insertions, 731 deletions
diff --git a/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs b/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs
index 5544233eb7..04da0600cc 100644
--- a/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs
+++ b/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs
@@ -4,6 +4,7 @@ using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using System.Security;
using Microsoft.Build.Framework;
@@ -12,22 +13,27 @@ namespace GodotSharpTools.Build
public class BuildInstance : IDisposable
{
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_BuildInstance_ExitCallback(string solution, string config, int exitCode);
+ private extern static void godot_icall_BuildInstance_ExitCallback(string solution, string config, int exitCode);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static string godot_icall_BuildInstance_get_MSBuildPath();
+ private extern static void godot_icall_BuildInstance_get_MSBuildInfo(ref string msbuildPath, ref string frameworkPath);
- private static string MSBuildPath
+ private struct MSBuildInfo
{
- get
- {
- string ret = godot_icall_BuildInstance_get_MSBuildPath();
+ public string path;
+ public string frameworkPathOverride;
+ }
- if (ret == null)
- throw new FileNotFoundException("Cannot find the MSBuild executable.");
+ private static MSBuildInfo GetMSBuildInfo()
+ {
+ MSBuildInfo msbuildInfo = new MSBuildInfo();
- return ret;
- }
+ godot_icall_BuildInstance_get_MSBuildInfo(ref msbuildInfo.path, ref msbuildInfo.frameworkPathOverride);
+
+ if (msbuildInfo.path == null)
+ throw new FileNotFoundException("Cannot find the MSBuild executable.");
+
+ return msbuildInfo;
}
private string solution;
@@ -48,9 +54,19 @@ namespace GodotSharpTools.Build
public bool Build(string loggerAssemblyPath, string loggerOutputDir, string[] customProperties = null)
{
- string compilerArgs = BuildArguments(loggerAssemblyPath, loggerOutputDir, customProperties);
+ MSBuildInfo msbuildInfo = GetMSBuildInfo();
+
+ List<string> customPropertiesList = new List<string>();
+
+ if (customProperties != null)
+ customPropertiesList.AddRange(customProperties);
+
+ if (msbuildInfo.frameworkPathOverride != null)
+ customPropertiesList.Add("FrameworkPathOverride=" + msbuildInfo.frameworkPathOverride);
- ProcessStartInfo startInfo = new ProcessStartInfo(MSBuildPath, compilerArgs);
+ string compilerArgs = BuildArguments(loggerAssemblyPath, loggerOutputDir, customPropertiesList);
+
+ ProcessStartInfo startInfo = new ProcessStartInfo(msbuildInfo.path, compilerArgs);
// No console output, thanks
startInfo.RedirectStandardOutput = true;
@@ -82,9 +98,19 @@ namespace GodotSharpTools.Build
if (process != null)
throw new InvalidOperationException("Already in use");
- string compilerArgs = BuildArguments(loggerAssemblyPath, loggerOutputDir, customProperties);
+ MSBuildInfo msbuildInfo = GetMSBuildInfo();
+
+ List<string> customPropertiesList = new List<string>();
+
+ if (customProperties != null)
+ customPropertiesList.AddRange(customProperties);
+
+ if (msbuildInfo.frameworkPathOverride.Length > 0)
+ customPropertiesList.Add("FrameworkPathOverride=" + msbuildInfo.frameworkPathOverride);
- ProcessStartInfo startInfo = new ProcessStartInfo("msbuild", compilerArgs);
+ string compilerArgs = BuildArguments(loggerAssemblyPath, loggerOutputDir, customPropertiesList);
+
+ ProcessStartInfo startInfo = new ProcessStartInfo(msbuildInfo.path, compilerArgs);
// No console output, thanks
startInfo.RedirectStandardOutput = true;
@@ -101,10 +127,13 @@ namespace GodotSharpTools.Build
process.Start();
+ process.BeginOutputReadLine();
+ process.BeginErrorReadLine();
+
return true;
}
- private string BuildArguments(string loggerAssemblyPath, string loggerOutputDir, string[] customProperties)
+ private string BuildArguments(string loggerAssemblyPath, string loggerOutputDir, List<string> customProperties)
{
string arguments = string.Format(@"""{0}"" /v:normal /t:Build ""/p:{1}"" ""/l:{2},{3};{4}""",
solution,
@@ -114,12 +143,9 @@ namespace GodotSharpTools.Build
loggerOutputDir
);
- if (customProperties != null)
+ foreach (string customProperty in customProperties)
{
- foreach (string customProperty in customProperties)
- {
- arguments += " /p:" + customProperty;
- }
+ arguments += " \"/p:" + customProperty + "\"";
}
return arguments;
diff --git a/modules/mono/editor/GodotSharpTools/GodotSharpTools.sln b/modules/mono/editor/GodotSharpTools/GodotSharpTools.sln
index 7eabcdff5d..5f7d0e8a39 100644
--- a/modules/mono/editor/GodotSharpTools/GodotSharpTools.sln
+++ b/modules/mono/editor/GodotSharpTools/GodotSharpTools.sln
@@ -1,17 +1,17 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 2012
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotSharpTools", "GodotSharpTools.csproj", "{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
-EndGlobal
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2012
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotSharpTools", "GodotSharpTools.csproj", "{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 123f00ea10..59a2b73dbc 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -31,12 +31,12 @@
#ifdef DEBUG_METHODS_ENABLED
+#include "engine.h"
#include "global_constants.h"
#include "io/compression.h"
#include "os/dir_access.h"
#include "os/file_access.h"
#include "os/os.h"
-#include "project_settings.h"
#include "ucaps.h"
#include "../glue/cs_compressed.gen.h"
@@ -108,42 +108,6 @@ const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN = "\t%0 %1_in
bool BindingsGenerator::verbose_output = false;
-static bool is_csharp_keyword(const String &p_name) {
-
- // Reserved keywords
-
- return p_name == "abstract" || p_name == "as" || p_name == "base" || p_name == "bool" ||
- p_name == "break" || p_name == "byte" || p_name == "case" || p_name == "catch" ||
- p_name == "char" || p_name == "checked" || p_name == "class" || p_name == "const" ||
- p_name == "continue" || p_name == "decimal" || p_name == "default" || p_name == "delegate" ||
- p_name == "do" || p_name == "double" || p_name == "else" || p_name == "enum" ||
- p_name == "event" || p_name == "explicit" || p_name == "extern" || p_name == "false" ||
- p_name == "finally" || p_name == "fixed" || p_name == "float" || p_name == "for" ||
- p_name == "forech" || p_name == "goto" || p_name == "if" || p_name == "implicit" ||
- p_name == "in" || p_name == "int" || p_name == "interface" || p_name == "internal" ||
- p_name == "is" || p_name == "lock" || p_name == "long" || p_name == "namespace" ||
- p_name == "new" || p_name == "null" || p_name == "object" || p_name == "operator" ||
- p_name == "out" || p_name == "override" || p_name == "params" || p_name == "private" ||
- p_name == "protected" || p_name == "public" || p_name == "readonly" || p_name == "ref" ||
- p_name == "return" || p_name == "sbyte" || p_name == "sealed" || p_name == "short" ||
- p_name == "sizeof" || p_name == "stackalloc" || p_name == "static" || p_name == "string" ||
- p_name == "struct" || p_name == "switch" || p_name == "this" || p_name == "throw" ||
- p_name == "true" || p_name == "try" || p_name == "typeof" || p_name == "uint" || p_name == "ulong" ||
- p_name == "unchecked" || p_name == "unsafe" || p_name == "ushort" || p_name == "using" ||
- p_name == "virtual" || p_name == "volatile" || p_name == "void" || p_name == "while";
-}
-
-static bool is_singleton_black_listed(const String &p_type) {
-
- return p_type == "IP_Unix" || p_type == "InputDefault" || p_type == "AudioServerSW" || p_type == "PhysicsServerSW" ||
- p_type == "Physics2DServerSW" || p_type == "SpatialSoundServerSW" || p_type == "SpatialSound2DServerSW";
-}
-
-inline static String escape_csharp_keyword(const String &p_name) {
-
- return is_csharp_keyword(p_name) ? "@" + p_name : p_name;
-}
-
static String snake_to_pascal_case(const String &p_identifier) {
String ret;
@@ -247,9 +211,6 @@ void BindingsGenerator::_generate_header_icalls() {
void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) {
- if (p_itype.base_name.length() && obj_types[p_itype.base_name].is_singleton && is_singleton_black_listed(p_itype.name))
- return;
-
for (const List<MethodInterface>::Element *E = p_itype.methods.front(); E; E = E->next()) {
const MethodInterface &imethod = E->get();
@@ -359,9 +320,9 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_output_dir, bo
int global_constants_count = GlobalConstants::get_global_constant_count();
if (global_constants_count > 0) {
- Map<String, DocData::ClassDoc>::Element *match = EditorHelp::get_doc_data()->class_list.find("@Global Scope");
+ Map<String, DocData::ClassDoc>::Element *match = EditorHelp::get_doc_data()->class_list.find("@GlobalScope");
- ERR_EXPLAIN("Could not find `@Global Scope` in DocData");
+ ERR_EXPLAIN("Could not find `@GlobalScope` in DocData");
ERR_FAIL_COND_V(!match, ERR_BUG);
const DocData::ClassDoc &global_scope_doc = match->value();
@@ -477,6 +438,9 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_output_dir, bo
return sln_error;
}
+ if (verbose_output)
+ OS::get_singleton()->print("The solution and C# project for the Core API was generated successfully\n");
+
return OK;
}
@@ -569,6 +533,9 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_output_dir,
return sln_error;
}
+ if (verbose_output)
+ OS::get_singleton()->print("The solution and C# project for the Editor API was generated successfully\n");
+
return OK;
}
@@ -576,13 +543,8 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_output_dir,
// e.g.: warning CS0108: 'SpriteBase3D.FLAG_MAX' hides inherited member 'GeometryInstance.FLAG_MAX'. Use the new keyword if hiding was intended.
Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const String &p_output_file) {
- int method_bind_count = 0;
-
bool is_derived_type = itype.base_name.length();
- if (is_derived_type && obj_types[itype.base_name].is_singleton && is_singleton_black_listed(itype.name))
- return ERR_SKIP;
-
List<InternalCall> &custom_icalls = itype.api_type == ClassDB::API_EDITOR ? editor_custom_icalls : core_custom_icalls;
if (verbose_output)
@@ -590,51 +552,51 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
String ctor_method(ICALL_PREFIX + itype.proxy_name + "_Ctor");
- List<String> cs_file;
+ List<String> output;
- cs_file.push_back("using System;\n"); // IntPtr
+ output.push_back("using System;\n"); // IntPtr
if (itype.requires_collections)
- cs_file.push_back("using System.Collections.Generic;\n"); // Dictionary
+ output.push_back("using System.Collections.Generic;\n"); // Dictionary
- cs_file.push_back("\nnamespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
+ output.push_back("\nnamespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
const DocData::ClassDoc *class_doc = itype.class_doc;
if (class_doc && class_doc->description.size()) {
- cs_file.push_back(INDENT1 "/// <summary>\n");
+ output.push_back(INDENT1 "/// <summary>\n");
Vector<String> description_lines = class_doc->description.split("\n");
for (int i = 0; i < description_lines.size(); i++) {
if (description_lines[i].size()) {
- cs_file.push_back(INDENT1 "/// ");
- cs_file.push_back(description_lines[i].strip_edges().xml_escape());
- cs_file.push_back("\n");
+ output.push_back(INDENT1 "/// ");
+ output.push_back(description_lines[i].strip_edges().xml_escape());
+ output.push_back("\n");
}
}
- cs_file.push_back(INDENT1 "/// </summary>\n");
+ output.push_back(INDENT1 "/// </summary>\n");
}
- cs_file.push_back(INDENT1 "public ");
- cs_file.push_back(itype.is_singleton ? "static class " : "class ");
- cs_file.push_back(itype.proxy_name);
+ output.push_back(INDENT1 "public ");
+ output.push_back(itype.is_singleton ? "static class " : "class ");
+ output.push_back(itype.proxy_name);
if (itype.is_singleton || !itype.is_object_type) {
- cs_file.push_back("\n");
+ output.push_back("\n");
} else if (!is_derived_type) {
- cs_file.push_back(" : IDisposable\n");
+ output.push_back(" : IDisposable\n");
} else if (obj_types.has(itype.base_name)) {
- cs_file.push_back(" : ");
- cs_file.push_back(obj_types[itype.base_name].proxy_name);
- cs_file.push_back("\n");
+ output.push_back(" : ");
+ output.push_back(obj_types[itype.base_name].proxy_name);
+ output.push_back("\n");
} else {
- ERR_PRINTS("Base type ' " + itype.base_name + "' does not exist");
+ ERR_PRINTS("Base type '" + itype.base_name + "' does not exist, for class " + itype.name);
return ERR_INVALID_DATA;
}
- cs_file.push_back(INDENT1 "{");
+ output.push_back(INDENT1 "{");
if (class_doc) {
@@ -644,270 +606,165 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
const DocData::ConstantDoc &const_doc = class_doc->constants[i];
if (const_doc.description.size()) {
- cs_file.push_back(MEMBER_BEGIN "/// <summary>\n");
+ output.push_back(MEMBER_BEGIN "/// <summary>\n");
Vector<String> description_lines = const_doc.description.split("\n");
for (int i = 0; i < description_lines.size(); i++) {
if (description_lines[i].size()) {
- cs_file.push_back(INDENT2 "/// ");
- cs_file.push_back(description_lines[i].strip_edges().xml_escape());
- cs_file.push_back("\n");
+ output.push_back(INDENT2 "/// ");
+ output.push_back(description_lines[i].strip_edges().xml_escape());
+ output.push_back("\n");
}
}
- cs_file.push_back(INDENT2 "/// </summary>");
+ output.push_back(INDENT2 "/// </summary>");
}
- cs_file.push_back(MEMBER_BEGIN "public const int ");
- cs_file.push_back(const_doc.name);
- cs_file.push_back(" = ");
- cs_file.push_back(const_doc.value);
- cs_file.push_back(";");
+ output.push_back(MEMBER_BEGIN "public const int ");
+ output.push_back(const_doc.name);
+ output.push_back(" = ");
+ output.push_back(const_doc.value);
+ output.push_back(";");
}
if (class_doc->constants.size())
- cs_file.push_back("\n");
+ output.push_back("\n");
// Add properties
- const Vector<DocData::PropertyDoc> &properties = itype.class_doc->properties;
+ const Vector<DocData::PropertyDoc> &properties = class_doc->properties;
for (int i = 0; i < properties.size(); i++) {
const DocData::PropertyDoc &prop_doc = properties[i];
-
- const MethodInterface *setter = itype.find_method_by_name(prop_doc.setter);
-
- // Search it in base types too
- const TypeInterface *current_type = &itype;
- while (!setter && current_type->base_name.length()) {
- Map<String, TypeInterface>::Element *base_match = obj_types.find(current_type->base_name);
- ERR_FAIL_NULL_V(base_match, ERR_BUG);
- current_type = &base_match->get();
- setter = current_type->find_method_by_name(prop_doc.setter);
- }
-
- const MethodInterface *getter = itype.find_method_by_name(prop_doc.getter);
-
- // Search it in base types too
- current_type = &itype;
- while (!getter && current_type->base_name.length()) {
- Map<String, TypeInterface>::Element *base_match = obj_types.find(current_type->base_name);
- ERR_FAIL_NULL_V(base_match, ERR_BUG);
- current_type = &base_match->get();
- getter = current_type->find_method_by_name(prop_doc.getter);
- }
-
- ERR_FAIL_COND_V(!setter && !getter, ERR_BUG);
-
- bool is_valid = false;
- int prop_index = ClassDB::get_property_index(itype.name, prop_doc.name, &is_valid);
- ERR_FAIL_COND_V(!is_valid, ERR_BUG);
-
- if (setter) {
- int setter_argc = prop_index != -1 ? 2 : 1;
- ERR_FAIL_COND_V(setter->arguments.size() != setter_argc, ERR_BUG);
- }
-
- if (getter) {
- int getter_argc = prop_index != -1 ? 1 : 0;
- ERR_FAIL_COND_V(getter->arguments.size() != getter_argc, ERR_BUG);
- }
-
- if (getter && setter) {
- ERR_FAIL_COND_V(getter->return_type != setter->arguments.back()->get().type, ERR_BUG);
- }
-
- // Let's not trust PropertyDoc::type
- String proptype_name = getter ? getter->return_type : setter->arguments.back()->get().type;
-
- const TypeInterface *prop_itype = _get_type_by_name_or_null(proptype_name);
- if (!prop_itype) {
- // Try with underscore prefix
- prop_itype = _get_type_by_name_or_null("_" + proptype_name);
+ Error prop_err = _generate_cs_property(itype, prop_doc, output);
+ if (prop_err != OK) {
+ ERR_EXPLAIN("Failed to generate property '" + prop_doc.name + "' for class '" + itype.name + "'");
+ ERR_FAIL_V(prop_err);
}
-
- ERR_FAIL_NULL_V(prop_itype, ERR_BUG);
-
- String prop_proxy_name = escape_csharp_keyword(snake_to_pascal_case(prop_doc.name));
-
- // Prevent property and enclosing type from sharing the same name
- if (prop_proxy_name == itype.proxy_name) {
- if (verbose_output) {
- WARN_PRINTS("Name of property `" + prop_proxy_name + "` is ambiguous with the name of its class `" +
- itype.proxy_name + "`. Renaming property to `" + prop_proxy_name + "_`");
- }
-
- prop_proxy_name += "_";
- }
-
- if (prop_doc.description.size()) {
- cs_file.push_back(MEMBER_BEGIN "/// <summary>\n");
-
- Vector<String> description_lines = prop_doc.description.split("\n");
-
- for (int i = 0; i < description_lines.size(); i++) {
- if (description_lines[i].size()) {
- cs_file.push_back(INDENT2 "/// ");
- cs_file.push_back(description_lines[i].strip_edges().xml_escape());
- cs_file.push_back("\n");
- }
- }
-
- cs_file.push_back(INDENT2 "/// </summary>");
- }
-
- cs_file.push_back(MEMBER_BEGIN "public ");
-
- if (itype.is_singleton)
- cs_file.push_back("static ");
-
- cs_file.push_back(prop_itype->cs_type);
- cs_file.push_back(" ");
- cs_file.push_back(prop_proxy_name.replace("/", "__"));
- cs_file.push_back("\n" INDENT2 OPEN_BLOCK);
-
- if (getter) {
- cs_file.push_back(INDENT3 "get\n" OPEN_BLOCK_L3);
- cs_file.push_back("return ");
- cs_file.push_back(getter->proxy_name + "(");
- if (prop_index != -1)
- cs_file.push_back(itos(prop_index));
- cs_file.push_back(");\n" CLOSE_BLOCK_L3);
- }
-
- if (setter) {
- cs_file.push_back(INDENT3 "set\n" OPEN_BLOCK_L3);
- cs_file.push_back(setter->proxy_name + "(");
- if (prop_index != -1)
- cs_file.push_back(itos(prop_index) + ", ");
- cs_file.push_back("value);\n" CLOSE_BLOCK_L3);
- }
-
- cs_file.push_back(CLOSE_BLOCK_L2);
}
if (class_doc->properties.size())
- cs_file.push_back("\n");
+ output.push_back("\n");
}
if (!itype.is_object_type) {
- cs_file.push_back(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \"" + itype.name + "\";\n");
- cs_file.push_back(MEMBER_BEGIN "private bool disposed = false;\n");
- cs_file.push_back(MEMBER_BEGIN "internal IntPtr " BINDINGS_PTR_FIELD ";\n");
+ output.push_back(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \"" + itype.name + "\";\n");
+ output.push_back(MEMBER_BEGIN "private bool disposed = false;\n");
+ output.push_back(MEMBER_BEGIN "internal IntPtr " BINDINGS_PTR_FIELD ";\n");
- cs_file.push_back(MEMBER_BEGIN "internal static IntPtr " CS_SMETHOD_GETINSTANCE "(");
- cs_file.push_back(itype.proxy_name);
- cs_file.push_back(" instance)\n" OPEN_BLOCK_L2 "return instance == null ? IntPtr.Zero : instance." BINDINGS_PTR_FIELD ";\n" CLOSE_BLOCK_L2);
+ output.push_back(MEMBER_BEGIN "internal static IntPtr " CS_SMETHOD_GETINSTANCE "(");
+ output.push_back(itype.proxy_name);
+ output.push_back(" instance)\n" OPEN_BLOCK_L2 "return instance == null ? IntPtr.Zero : instance." BINDINGS_PTR_FIELD ";\n" CLOSE_BLOCK_L2);
// Add Destructor
- cs_file.push_back(MEMBER_BEGIN "~");
- cs_file.push_back(itype.proxy_name);
- cs_file.push_back("()\n" OPEN_BLOCK_L2 "Dispose(false);\n" CLOSE_BLOCK_L2);
+ output.push_back(MEMBER_BEGIN "~");
+ output.push_back(itype.proxy_name);
+ output.push_back("()\n" OPEN_BLOCK_L2 "Dispose(false);\n" CLOSE_BLOCK_L2);
// Add the Dispose from IDisposable
- cs_file.push_back(MEMBER_BEGIN "public void Dispose()\n" OPEN_BLOCK_L2 "Dispose(true);\n" INDENT3 "GC.SuppressFinalize(this);\n" CLOSE_BLOCK_L2);
+ output.push_back(MEMBER_BEGIN "public void Dispose()\n" OPEN_BLOCK_L2 "Dispose(true);\n" INDENT3 "GC.SuppressFinalize(this);\n" CLOSE_BLOCK_L2);
// Add the virtual Dispose
- cs_file.push_back(MEMBER_BEGIN "public virtual void Dispose(bool disposing)\n" OPEN_BLOCK_L2
- "if (disposed) return;\n" INDENT3
- "if (" BINDINGS_PTR_FIELD " != IntPtr.Zero)\n" OPEN_BLOCK_L3 "NativeCalls.godot_icall_");
- cs_file.push_back(itype.proxy_name);
- cs_file.push_back("_Dtor(" BINDINGS_PTR_FIELD ");\n" INDENT5 BINDINGS_PTR_FIELD " = IntPtr.Zero;\n" CLOSE_BLOCK_L3 INDENT3
- "GC.SuppressFinalize(this);\n" INDENT3 "disposed = true;\n" CLOSE_BLOCK_L2);
-
- cs_file.push_back(MEMBER_BEGIN "internal ");
- cs_file.push_back(itype.proxy_name);
- cs_file.push_back("(IntPtr " BINDINGS_PTR_FIELD ")\n" OPEN_BLOCK_L2 "this." BINDINGS_PTR_FIELD " = " BINDINGS_PTR_FIELD ";\n" CLOSE_BLOCK_L2);
-
- cs_file.push_back(MEMBER_BEGIN "public bool HasValidHandle()\n" OPEN_BLOCK_L2
- "return " BINDINGS_PTR_FIELD " == IntPtr.Zero;\n" CLOSE_BLOCK_L2);
+ output.push_back(MEMBER_BEGIN "public virtual void Dispose(bool disposing)\n" OPEN_BLOCK_L2
+ "if (disposed) return;\n" INDENT3
+ "if (" BINDINGS_PTR_FIELD " != IntPtr.Zero)\n" OPEN_BLOCK_L3 "NativeCalls.godot_icall_");
+ output.push_back(itype.proxy_name);
+ output.push_back("_Dtor(" BINDINGS_PTR_FIELD ");\n" INDENT5 BINDINGS_PTR_FIELD " = IntPtr.Zero;\n" CLOSE_BLOCK_L3 INDENT3
+ "GC.SuppressFinalize(this);\n" INDENT3 "disposed = true;\n" CLOSE_BLOCK_L2);
+
+ output.push_back(MEMBER_BEGIN "internal ");
+ output.push_back(itype.proxy_name);
+ output.push_back("(IntPtr " BINDINGS_PTR_FIELD ")\n" OPEN_BLOCK_L2 "this." BINDINGS_PTR_FIELD " = " BINDINGS_PTR_FIELD ";\n" CLOSE_BLOCK_L2);
+
+ output.push_back(MEMBER_BEGIN "public IntPtr NativeInstance\n" OPEN_BLOCK_L2
+ "get { return " BINDINGS_PTR_FIELD "; }\n" CLOSE_BLOCK_L2);
} else if (itype.is_singleton) {
// Add the type name and the singleton pointer as static fields
- cs_file.push_back(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \"");
- cs_file.push_back(itype.name);
- cs_file.push_back("\";\n");
+ output.push_back(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \"");
+ output.push_back(itype.name);
+ output.push_back("\";\n");
- cs_file.push_back(INDENT2 "internal static IntPtr " BINDINGS_PTR_FIELD " = ");
- cs_file.push_back(itype.api_type == ClassDB::API_EDITOR ? CS_CLASS_NATIVECALLS_EDITOR : CS_CLASS_NATIVECALLS);
- cs_file.push_back("." ICALL_PREFIX);
- cs_file.push_back(itype.name);
- cs_file.push_back(SINGLETON_ICALL_SUFFIX "();\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("." ICALL_PREFIX);
+ output.push_back(itype.name);
+ output.push_back(SINGLETON_ICALL_SUFFIX "();\n");
} else {
// Add member fields
- cs_file.push_back(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \"");
- cs_file.push_back(itype.name);
- cs_file.push_back("\";\n");
+ output.push_back(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \"");
+ output.push_back(itype.name);
+ output.push_back("\";\n");
// Only the base class stores the pointer to the native object
// This pointer is expected to be and must be of type Object*
if (!is_derived_type) {
- cs_file.push_back(MEMBER_BEGIN "private bool disposed = false;\n");
- cs_file.push_back(INDENT2 "internal IntPtr " BINDINGS_PTR_FIELD ";\n");
- cs_file.push_back(INDENT2 "internal bool " CS_FIELD_MEMORYOWN ";\n");
+ output.push_back(MEMBER_BEGIN "private bool disposed = false;\n");
+ output.push_back(INDENT2 "internal IntPtr " BINDINGS_PTR_FIELD ";\n");
+ output.push_back(INDENT2 "internal bool " CS_FIELD_MEMORYOWN ";\n");
}
// Add default constructor
if (itype.is_instantiable) {
- cs_file.push_back(MEMBER_BEGIN "public ");
- cs_file.push_back(itype.proxy_name);
- cs_file.push_back("() : this(");
- cs_file.push_back(itype.memory_own ? "true" : "false");
+ output.push_back(MEMBER_BEGIN "public ");
+ output.push_back(itype.proxy_name);
+ output.push_back("() : this(");
+ output.push_back(itype.memory_own ? "true" : "false");
// The default constructor may also be called by the engine when instancing existing native objects
// 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
- cs_file.push_back(")\n" OPEN_BLOCK_L2 "if (" BINDINGS_PTR_FIELD " == IntPtr.Zero)\n" INDENT4 BINDINGS_PTR_FIELD " = ");
- cs_file.push_back(itype.api_type == ClassDB::API_EDITOR ? CS_CLASS_NATIVECALLS_EDITOR : CS_CLASS_NATIVECALLS);
- cs_file.push_back("." + ctor_method);
- cs_file.push_back("(this);\n" CLOSE_BLOCK_L2);
+ 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("." + ctor_method);
+ output.push_back("(this);\n" CLOSE_BLOCK_L2);
} else {
// Hide the constructor
- cs_file.push_back(MEMBER_BEGIN "internal ");
- cs_file.push_back(itype.proxy_name);
- cs_file.push_back("() {}\n");
+ output.push_back(MEMBER_BEGIN "internal ");
+ output.push_back(itype.proxy_name);
+ output.push_back("() {}\n");
}
// Add.. em.. trick constructor. Sort of.
- cs_file.push_back(MEMBER_BEGIN "internal ");
- cs_file.push_back(itype.proxy_name);
+ output.push_back(MEMBER_BEGIN "internal ");
+ output.push_back(itype.proxy_name);
if (is_derived_type) {
- cs_file.push_back("(bool " CS_FIELD_MEMORYOWN ") : base(" CS_FIELD_MEMORYOWN ") {}\n");
+ output.push_back("(bool " CS_FIELD_MEMORYOWN ") : base(" CS_FIELD_MEMORYOWN ") {}\n");
} else {
- cs_file.push_back("(bool " CS_FIELD_MEMORYOWN ")\n" OPEN_BLOCK_L2
- "this." CS_FIELD_MEMORYOWN " = " CS_FIELD_MEMORYOWN ";\n" CLOSE_BLOCK_L2);
+ output.push_back("(bool " CS_FIELD_MEMORYOWN ")\n" OPEN_BLOCK_L2
+ "this." CS_FIELD_MEMORYOWN " = " CS_FIELD_MEMORYOWN ";\n" CLOSE_BLOCK_L2);
}
// Add methods
if (!is_derived_type) {
- cs_file.push_back(MEMBER_BEGIN "public bool HasValidHandle()\n" OPEN_BLOCK_L2
- "return " BINDINGS_PTR_FIELD " == IntPtr.Zero;\n" CLOSE_BLOCK_L2);
+ output.push_back(MEMBER_BEGIN "public IntPtr NativeInstance\n" OPEN_BLOCK_L2
+ "get { return " BINDINGS_PTR_FIELD "; }\n" CLOSE_BLOCK_L2);
- cs_file.push_back(MEMBER_BEGIN "internal static IntPtr " CS_SMETHOD_GETINSTANCE "(Object instance)\n" OPEN_BLOCK_L2
- "return instance == null ? IntPtr.Zero : instance." BINDINGS_PTR_FIELD ";\n" CLOSE_BLOCK_L2);
+ output.push_back(MEMBER_BEGIN "internal static IntPtr " CS_SMETHOD_GETINSTANCE "(Object instance)\n" OPEN_BLOCK_L2
+ "return instance == null ? IntPtr.Zero : instance." BINDINGS_PTR_FIELD ";\n" CLOSE_BLOCK_L2);
}
if (!is_derived_type) {
// Add destructor
- cs_file.push_back(MEMBER_BEGIN "~");
- cs_file.push_back(itype.proxy_name);
- cs_file.push_back("()\n" OPEN_BLOCK_L2 "Dispose(false);\n" CLOSE_BLOCK_L2);
+ output.push_back(MEMBER_BEGIN "~");
+ output.push_back(itype.proxy_name);
+ output.push_back("()\n" OPEN_BLOCK_L2 "Dispose(false);\n" CLOSE_BLOCK_L2);
// Add the Dispose from IDisposable
- cs_file.push_back(MEMBER_BEGIN "public void Dispose()\n" OPEN_BLOCK_L2 "Dispose(true);\n" INDENT3 "GC.SuppressFinalize(this);\n" CLOSE_BLOCK_L2);
+ output.push_back(MEMBER_BEGIN "public void Dispose()\n" OPEN_BLOCK_L2 "Dispose(true);\n" INDENT3 "GC.SuppressFinalize(this);\n" CLOSE_BLOCK_L2);
// Add the virtual Dispose
- cs_file.push_back(MEMBER_BEGIN "public virtual void Dispose(bool disposing)\n" OPEN_BLOCK_L2
- "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
- "(" BINDINGS_PTR_FIELD ");\n" INDENT5 BINDINGS_PTR_FIELD
- " = IntPtr.Zero;\n" CLOSE_BLOCK_L4 CLOSE_BLOCK_L3 INDENT3
- "GC.SuppressFinalize(this);\n" INDENT3 "disposed = true;\n" CLOSE_BLOCK_L2);
+ output.push_back(MEMBER_BEGIN "public virtual void Dispose(bool disposing)\n" OPEN_BLOCK_L2
+ "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
+ "(" BINDINGS_PTR_FIELD ");\n" INDENT5 BINDINGS_PTR_FIELD
+ " = IntPtr.Zero;\n" CLOSE_BLOCK_L4 CLOSE_BLOCK_L3 INDENT3
+ "GC.SuppressFinalize(this);\n" INDENT3 "disposed = true;\n" CLOSE_BLOCK_L2);
Map<String, TypeInterface>::Element *array_itype = builtin_types.find("Array");
@@ -916,419 +773,390 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
return ERR_BUG;
}
- cs_file.push_back(MEMBER_BEGIN "private void _AwaitedSignalCallback(");
- cs_file.push_back(array_itype->get().cs_type);
- cs_file.push_back(" args, SignalAwaiter awaiter)\n" OPEN_BLOCK_L2 "awaiter.SignalCallback(args);\n" CLOSE_BLOCK_L2);
-
Map<String, TypeInterface>::Element *object_itype = obj_types.find("Object");
if (!object_itype) {
- ERR_PRINT("BUG: Array type interface not found!");
+ ERR_PRINT("BUG: Object type interface not found!");
return ERR_BUG;
}
- cs_file.push_back(MEMBER_BEGIN "public " CS_CLASS_SIGNALAWAITER " ToSignal(");
- cs_file.push_back(object_itype->get().cs_type);
- cs_file.push_back(" source, string signal)\n" OPEN_BLOCK_L2
- "return new " CS_CLASS_SIGNALAWAITER "(source, signal, this);\n" CLOSE_BLOCK_L2);
+ output.push_back(MEMBER_BEGIN "public " CS_CLASS_SIGNALAWAITER " ToSignal(");
+ output.push_back(object_itype->get().cs_type);
+ output.push_back(" source, string signal)\n" OPEN_BLOCK_L2
+ "return new " CS_CLASS_SIGNALAWAITER "(source, signal, this);\n" CLOSE_BLOCK_L2);
}
}
Map<String, String>::Element *extra_member = extra_members.find(itype.name);
if (extra_member)
- cs_file.push_back(extra_member->get());
+ output.push_back(extra_member->get());
+ int method_bind_count = 0;
for (const List<MethodInterface>::Element *E = itype.methods.front(); E; E = E->next()) {
const MethodInterface &imethod = E->get();
+ Error method_err = _generate_cs_method(itype, imethod, method_bind_count, output);
+ if (method_err != OK) {
+ ERR_EXPLAIN("Failed to generate method '" + imethod.name + "' for class '" + itype.name + "'");
+ ERR_FAIL_V(method_err);
+ }
+ }
- const TypeInterface *return_type = _get_type_by_name_or_placeholder(imethod.return_type);
+ if (itype.is_singleton) {
+ InternalCall singleton_icall = InternalCall(itype.api_type, ICALL_PREFIX + itype.name + SINGLETON_ICALL_SUFFIX, "IntPtr");
- String method_bind_field = "method_bind_" + itos(method_bind_count);
+ if (!find_icall_by_name(singleton_icall.name, custom_icalls))
+ custom_icalls.push_back(singleton_icall);
+ }
- String icall_params = method_bind_field + ", " + sformat(itype.cs_in, "this");
- String arguments_sig;
- String cs_in_statements;
+ if (itype.is_instantiable) {
+ InternalCall ctor_icall = InternalCall(itype.api_type, ctor_method, "IntPtr", itype.proxy_name + " obj");
- List<String> default_args_doc;
+ if (!find_icall_by_name(ctor_icall.name, custom_icalls))
+ custom_icalls.push_back(ctor_icall);
+ }
- // Retrieve information from the arguments
- for (const List<ArgumentInterface>::Element *F = imethod.arguments.front(); F; F = F->next()) {
- const ArgumentInterface &iarg = F->get();
- const TypeInterface *arg_type = _get_type_by_name_or_placeholder(iarg.type);
+ output.push_back(INDENT1 CLOSE_BLOCK CLOSE_BLOCK);
- // Add the current arguments to the signature
- // If the argument has a default value which is not a constant, we will make it Nullable
- {
- if (F != imethod.arguments.front())
- arguments_sig += ", ";
+ return _save_file(p_output_file, output);
+}
- if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL)
- arguments_sig += "Nullable<";
+Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInterface &p_itype, const DocData::PropertyDoc &p_prop_doc, List<String> &p_output) {
- arguments_sig += arg_type->cs_type;
+ const MethodInterface *setter = p_itype.find_method_by_name(p_prop_doc.setter);
- if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL)
- arguments_sig += "> ";
- else
- arguments_sig += " ";
+ // Search it in base types too
+ const TypeInterface *current_type = &p_itype;
+ while (!setter && current_type->base_name.length()) {
+ Map<String, TypeInterface>::Element *base_match = obj_types.find(current_type->base_name);
+ ERR_FAIL_NULL_V(base_match, ERR_BUG);
+ current_type = &base_match->get();
+ setter = current_type->find_method_by_name(p_prop_doc.setter);
+ }
- arguments_sig += iarg.name;
+ const MethodInterface *getter = p_itype.find_method_by_name(p_prop_doc.getter);
- if (iarg.default_argument.size()) {
- if (iarg.def_param_mode != ArgumentInterface::CONSTANT)
- arguments_sig += " = null";
- else
- arguments_sig += " = " + sformat(iarg.default_argument, arg_type->cs_type);
- }
- }
+ // Search it in base types too
+ current_type = &p_itype;
+ while (!getter && current_type->base_name.length()) {
+ Map<String, TypeInterface>::Element *base_match = obj_types.find(current_type->base_name);
+ ERR_FAIL_NULL_V(base_match, ERR_BUG);
+ current_type = &base_match->get();
+ getter = current_type->find_method_by_name(p_prop_doc.getter);
+ }
- icall_params += ", ";
+ ERR_FAIL_COND_V(!setter && !getter, ERR_BUG);
- if (iarg.default_argument.size() && iarg.def_param_mode != ArgumentInterface::CONSTANT) {
- // The default value of an argument must be constant. Otherwise we make it Nullable and do the following:
- // Type arg_in = arg.HasValue ? arg.Value : <non-const default value>;
- String arg_in = iarg.name;
- arg_in += "_in";
+ bool is_valid = false;
+ int prop_index = ClassDB::get_property_index(p_itype.name, p_prop_doc.name, &is_valid);
+ ERR_FAIL_COND_V(!is_valid, ERR_BUG);
- cs_in_statements += arg_type->cs_type;
- cs_in_statements += " ";
- cs_in_statements += arg_in;
- cs_in_statements += " = ";
- cs_in_statements += iarg.name;
+ if (setter) {
+ int setter_argc = prop_index != -1 ? 2 : 1;
+ ERR_FAIL_COND_V(setter->arguments.size() != setter_argc, ERR_BUG);
+ }
- if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL)
- cs_in_statements += ".HasValue ? ";
- else
- cs_in_statements += " != null ? ";
+ if (getter) {
+ int getter_argc = prop_index != -1 ? 1 : 0;
+ ERR_FAIL_COND_V(getter->arguments.size() != getter_argc, ERR_BUG);
+ }
- cs_in_statements += iarg.name;
+ if (getter && setter) {
+ ERR_FAIL_COND_V(getter->return_type != setter->arguments.back()->get().type, ERR_BUG);
+ }
- if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL)
- cs_in_statements += ".Value : ";
- else
- cs_in_statements += " : ";
+ // Let's not trust PropertyDoc::type
+ String proptype_name = getter ? getter->return_type : setter->arguments.back()->get().type;
- String def_arg = sformat(iarg.default_argument, arg_type->cs_type);
+ const TypeInterface *prop_itype = _get_type_by_name_or_null(proptype_name);
+ if (!prop_itype) {
+ // Try with underscore prefix
+ prop_itype = _get_type_by_name_or_null("_" + proptype_name);
+ }
- cs_in_statements += def_arg;
- cs_in_statements += ";\n" INDENT3;
+ ERR_FAIL_NULL_V(prop_itype, ERR_BUG);
- icall_params += arg_type->cs_in.empty() ? arg_in : sformat(arg_type->cs_in, arg_in);
+ String prop_proxy_name = escape_csharp_keyword(snake_to_pascal_case(p_prop_doc.name));
- default_args_doc.push_back(INDENT2 "/// <param name=\"" + iarg.name + "\">If the param is null, then the default value is " + def_arg + "</param>\n");
- } else {
- icall_params += arg_type->cs_in.empty() ? iarg.name : sformat(arg_type->cs_in, iarg.name);
- }
+ // Prevent property and enclosing type from sharing the same name
+ if (prop_proxy_name == p_itype.proxy_name) {
+ if (verbose_output) {
+ WARN_PRINTS("Name of property `" + prop_proxy_name + "` is ambiguous with the name of its class `" +
+ p_itype.proxy_name + "`. Renaming property to `" + prop_proxy_name + "_`");
}
- // Generate method
- {
- if (!imethod.is_virtual && !imethod.requires_object_call) {
- cs_file.push_back(MEMBER_BEGIN "private ");
- cs_file.push_back(itype.is_singleton ? "static IntPtr " : "IntPtr ");
- cs_file.push_back(method_bind_field + " = " CS_CLASS_NATIVECALLS "." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \"");
- cs_file.push_back(imethod.name);
- cs_file.push_back("\");\n");
- }
+ prop_proxy_name += "_";
+ }
- if (imethod.method_doc && imethod.method_doc->description.size()) {
- cs_file.push_back(MEMBER_BEGIN "/// <summary>\n");
+ if (p_prop_doc.description.size()) {
+ p_output.push_back(MEMBER_BEGIN "/// <summary>\n");
- Vector<String> description_lines = imethod.method_doc->description.split("\n");
+ Vector<String> description_lines = p_prop_doc.description.split("\n");
- for (int i = 0; i < description_lines.size(); i++) {
- if (description_lines[i].size()) {
- cs_file.push_back(INDENT2 "/// ");
- cs_file.push_back(description_lines[i].strip_edges().xml_escape());
- cs_file.push_back("\n");
- }
- }
+ for (int i = 0; i < description_lines.size(); i++) {
+ if (description_lines[i].size()) {
+ p_output.push_back(INDENT2 "/// ");
+ p_output.push_back(description_lines[i].strip_edges().xml_escape());
+ p_output.push_back("\n");
+ }
+ }
- for (List<String>::Element *E = default_args_doc.front(); E; E = E->next()) {
- cs_file.push_back(E->get().xml_escape());
- }
+ p_output.push_back(INDENT2 "/// </summary>");
+ }
- cs_file.push_back(INDENT2 "/// </summary>");
- }
+ p_output.push_back(MEMBER_BEGIN "public ");
- if (!imethod.is_internal) {
- cs_file.push_back(MEMBER_BEGIN "[GodotMethod(\"");
- cs_file.push_back(imethod.name);
- cs_file.push_back("\")]");
- }
+ if (p_itype.is_singleton)
+ p_output.push_back("static ");
- cs_file.push_back(MEMBER_BEGIN);
- cs_file.push_back(imethod.is_internal ? "internal " : "public ");
+ p_output.push_back(prop_itype->cs_type);
+ p_output.push_back(" ");
+ p_output.push_back(prop_proxy_name.replace("/", "__"));
+ p_output.push_back("\n" INDENT2 OPEN_BLOCK);
- if (itype.is_singleton) {
- cs_file.push_back("static ");
- } else if (imethod.is_virtual) {
- cs_file.push_back("virtual ");
- }
+ if (getter) {
+ p_output.push_back(INDENT3 "get\n" OPEN_BLOCK_L3);
+ p_output.push_back("return ");
+ p_output.push_back(getter->proxy_name + "(");
+ if (prop_index != -1)
+ p_output.push_back(itos(prop_index));
+ p_output.push_back(");\n" CLOSE_BLOCK_L3);
+ }
- cs_file.push_back(return_type->cs_type + " ");
- cs_file.push_back(imethod.proxy_name + "(");
- cs_file.push_back(arguments_sig + ")\n" OPEN_BLOCK_L2);
+ if (setter) {
+ p_output.push_back(INDENT3 "set\n" OPEN_BLOCK_L3);
+ p_output.push_back(setter->proxy_name + "(");
+ if (prop_index != -1)
+ p_output.push_back(itos(prop_index) + ", ");
+ p_output.push_back("value);\n" CLOSE_BLOCK_L3);
+ }
- if (imethod.is_virtual) {
- // Godot virtual method must be overridden, therefore we return a default value by default.
+ p_output.push_back(CLOSE_BLOCK_L2);
- if (return_type->name == "void") {
- cs_file.push_back("return;\n" CLOSE_BLOCK_L2);
- } else {
- cs_file.push_back("return default(");
- cs_file.push_back(return_type->cs_type);
- cs_file.push_back(");\n" CLOSE_BLOCK_L2);
- }
+ return OK;
+}
- continue;
- }
+Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, int &p_method_bind_count, List<String> &p_output) {
- if (imethod.requires_object_call) {
- // Fallback to Godot's object.Call(string, params)
+ const TypeInterface *return_type = _get_type_by_name_or_placeholder(p_imethod.return_type);
- cs_file.push_back(CS_METHOD_CALL "(\"");
- cs_file.push_back(imethod.name);
- cs_file.push_back("\"");
+ String method_bind_field = "method_bind_" + itos(p_method_bind_count);
- for (const List<ArgumentInterface>::Element *F = imethod.arguments.front(); F; F = F->next()) {
- cs_file.push_back(", ");
- cs_file.push_back(F->get().name);
- }
+ String icall_params = method_bind_field + ", " + sformat(p_itype.cs_in, "this");
+ String arguments_sig;
+ String cs_in_statements;
- cs_file.push_back(");\n" CLOSE_BLOCK_L2);
+ List<String> default_args_doc;
- continue;
- }
+ // Retrieve information from the arguments
+ for (const List<ArgumentInterface>::Element *F = p_imethod.arguments.front(); F; F = F->next()) {
+ const ArgumentInterface &iarg = F->get();
+ const TypeInterface *arg_type = _get_type_by_name_or_placeholder(iarg.type);
- const Map<const MethodInterface *, const InternalCall *>::Element *match = method_icalls_map.find(&E->get());
- ERR_FAIL_NULL_V(match, ERR_BUG);
+ // Add the current arguments to the signature
+ // If the argument has a default value which is not a constant, we will make it Nullable
+ {
+ if (F != p_imethod.arguments.front())
+ arguments_sig += ", ";
- const InternalCall *im_icall = match->value();
+ if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL)
+ arguments_sig += "Nullable<";
- String im_call = im_icall->editor_only ? CS_CLASS_NATIVECALLS_EDITOR : CS_CLASS_NATIVECALLS;
- im_call += "." + im_icall->name + "(" + icall_params + ");\n";
+ arguments_sig += arg_type->cs_type;
- if (imethod.arguments.size())
- cs_file.push_back(cs_in_statements);
+ if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL)
+ arguments_sig += "> ";
+ else
+ arguments_sig += " ";
- if (return_type->name == "void") {
- cs_file.push_back(im_call);
- } else if (return_type->cs_out.empty()) {
- cs_file.push_back("return " + im_call);
- } else {
- cs_file.push_back(return_type->im_type_out);
- cs_file.push_back(" " LOCAL_RET " = ");
- cs_file.push_back(im_call);
- cs_file.push_back(INDENT3);
- cs_file.push_back(sformat(return_type->cs_out, LOCAL_RET) + "\n");
- }
+ arguments_sig += iarg.name;
- cs_file.push_back(CLOSE_BLOCK_L2);
+ if (iarg.default_argument.size()) {
+ if (iarg.def_param_mode != ArgumentInterface::CONSTANT)
+ arguments_sig += " = null";
+ else
+ arguments_sig += " = " + sformat(iarg.default_argument, arg_type->cs_type);
+ }
}
- method_bind_count++;
- }
+ icall_params += ", ";
- if (itype.is_singleton) {
- InternalCall singleton_icall = InternalCall(itype.api_type, ICALL_PREFIX + itype.name + SINGLETON_ICALL_SUFFIX, "IntPtr");
+ if (iarg.default_argument.size() && iarg.def_param_mode != ArgumentInterface::CONSTANT) {
+ // The default value of an argument must be constant. Otherwise we make it Nullable and do the following:
+ // Type arg_in = arg.HasValue ? arg.Value : <non-const default value>;
+ String arg_in = iarg.name;
+ arg_in += "_in";
- if (!find_icall_by_name(singleton_icall.name, custom_icalls))
- custom_icalls.push_back(singleton_icall);
- }
+ cs_in_statements += arg_type->cs_type;
+ cs_in_statements += " ";
+ cs_in_statements += arg_in;
+ cs_in_statements += " = ";
+ cs_in_statements += iarg.name;
- if (itype.is_instantiable) {
- InternalCall ctor_icall = InternalCall(itype.api_type, ctor_method, "IntPtr", itype.proxy_name + " obj");
+ if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL)
+ cs_in_statements += ".HasValue ? ";
+ else
+ cs_in_statements += " != null ? ";
- if (!find_icall_by_name(ctor_icall.name, custom_icalls))
- custom_icalls.push_back(ctor_icall);
- }
+ cs_in_statements += iarg.name;
- cs_file.push_back(INDENT1 CLOSE_BLOCK CLOSE_BLOCK);
+ if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL)
+ cs_in_statements += ".Value : ";
+ else
+ cs_in_statements += " : ";
- return _save_file(p_output_file, cs_file);
-}
+ String def_arg = sformat(iarg.default_argument, arg_type->cs_type);
-Error BindingsGenerator::generate_glue(const String &p_output_dir) {
+ cs_in_statements += def_arg;
+ cs_in_statements += ";\n" INDENT3;
- verbose_output = true;
+ icall_params += arg_type->cs_in.empty() ? arg_in : sformat(arg_type->cs_in, arg_in);
- bool dir_exists = DirAccess::exists(p_output_dir);
- ERR_EXPLAIN("The output directory does not exist.");
- ERR_FAIL_COND_V(!dir_exists, ERR_FILE_BAD_PATH);
+ default_args_doc.push_back(INDENT2 "/// <param name=\"" + iarg.name + "\">If the param is null, then the default value is " + def_arg + "</param>\n");
+ } else {
+ icall_params += arg_type->cs_in.empty() ? iarg.name : sformat(arg_type->cs_in, iarg.name);
+ }
+ }
- List<String> cpp_file;
+ // Generate method
+ {
+ if (!p_imethod.is_virtual && !p_imethod.requires_object_call) {
+ p_output.push_back(MEMBER_BEGIN "private ");
+ p_output.push_back(p_itype.is_singleton ? "static IntPtr " : "IntPtr ");
+ p_output.push_back(method_bind_field + " = " CS_CLASS_NATIVECALLS "." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \"");
+ p_output.push_back(p_imethod.name);
+ p_output.push_back("\");\n");
+ }
- cpp_file.push_back("#include \"" GLUE_HEADER_FILE "\"\n"
- "\n");
+ if (p_imethod.method_doc && p_imethod.method_doc->description.size()) {
+ p_output.push_back(MEMBER_BEGIN "/// <summary>\n");
- List<const InternalCall *> generated_icall_funcs;
+ Vector<String> description_lines = p_imethod.method_doc->description.split("\n");
- for (Map<String, TypeInterface>::Element *type_elem = obj_types.front(); type_elem; type_elem = type_elem->next()) {
- const TypeInterface &itype = type_elem->get();
+ for (int i = 0; i < description_lines.size(); i++) {
+ if (description_lines[i].size()) {
+ p_output.push_back(INDENT2 "/// ");
+ p_output.push_back(description_lines[i].strip_edges().xml_escape());
+ p_output.push_back("\n");
+ }
+ }
- if (itype.base_name.length() && obj_types[itype.base_name].is_singleton && is_singleton_black_listed(itype.name))
- continue;
+ for (List<String>::Element *E = default_args_doc.front(); E; E = E->next()) {
+ p_output.push_back(E->get().xml_escape());
+ }
- List<InternalCall> &custom_icalls = itype.api_type == ClassDB::API_EDITOR ? editor_custom_icalls : core_custom_icalls;
+ p_output.push_back(INDENT2 "/// </summary>");
+ }
- OS::get_singleton()->print(String("Generating " + itype.name + "...\n").utf8());
+ if (!p_imethod.is_internal) {
+ p_output.push_back(MEMBER_BEGIN "[GodotMethod(\"");
+ p_output.push_back(p_imethod.name);
+ p_output.push_back("\")]");
+ }
- String ctor_method(ICALL_PREFIX + itype.proxy_name + "_Ctor");
+ p_output.push_back(MEMBER_BEGIN);
+ p_output.push_back(p_imethod.is_internal ? "internal " : "public ");
- for (const List<MethodInterface>::Element *E = itype.methods.front(); E; E = E->next()) {
- const MethodInterface &imethod = E->get();
+ if (p_itype.is_singleton) {
+ p_output.push_back("static ");
+ } else if (p_imethod.is_virtual) {
+ p_output.push_back("virtual ");
+ }
- if (imethod.is_virtual)
- continue;
+ p_output.push_back(return_type->cs_type + " ");
+ p_output.push_back(p_imethod.proxy_name + "(");
+ p_output.push_back(arguments_sig + ")\n" OPEN_BLOCK_L2);
- bool ret_void = imethod.return_type == "void";
+ if (p_imethod.is_virtual) {
+ // Godot virtual method must be overridden, therefore we return a default value by default.
- const TypeInterface *return_type = _get_type_by_name_or_placeholder(imethod.return_type);
+ if (return_type->name == "void") {
+ p_output.push_back("return;\n" CLOSE_BLOCK_L2);
+ } else {
+ p_output.push_back("return default(");
+ p_output.push_back(return_type->cs_type);
+ p_output.push_back(");\n" CLOSE_BLOCK_L2);
+ }
- String argc_str = itos(imethod.arguments.size());
+ return OK; // Won't increment method bind count
+ }
- String c_func_sig = "MethodBind* " CS_PARAM_METHODBIND ", " + itype.c_type_in + " " CS_PARAM_INSTANCE;
- String c_in_statements;
- String c_args_var_content;
+ if (p_imethod.requires_object_call) {
+ // Fallback to Godot's object.Call(string, params)
- // Get arguments information
- int i = 0;
- for (const List<ArgumentInterface>::Element *F = imethod.arguments.front(); F; F = F->next()) {
- const ArgumentInterface &iarg = F->get();
- const TypeInterface *arg_type = _get_type_by_name_or_placeholder(iarg.type);
+ p_output.push_back(CS_METHOD_CALL "(\"");
+ p_output.push_back(p_imethod.name);
+ p_output.push_back("\"");
- String c_param_name = "arg" + itos(i + 1);
+ for (const List<ArgumentInterface>::Element *F = p_imethod.arguments.front(); F; F = F->next()) {
+ p_output.push_back(", ");
+ p_output.push_back(F->get().name);
+ }
- if (imethod.is_vararg) {
- if (i < 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";
- }
- } else {
- if (i > 0)
- c_args_var_content += ", ";
- if (arg_type->c_in.size())
- c_in_statements += sformat(arg_type->c_in, arg_type->c_type, c_param_name);
- c_args_var_content += sformat(arg_type->c_arg_in, c_param_name);
- }
+ p_output.push_back(");\n" CLOSE_BLOCK_L2);
- c_func_sig += ", ";
- c_func_sig += arg_type->c_type_in;
- c_func_sig += " ";
- c_func_sig += c_param_name;
+ return OK; // Won't increment method bind count
+ }
- i++;
- }
+ const Map<const MethodInterface *, const InternalCall *>::Element *match = method_icalls_map.find(&p_imethod);
+ ERR_FAIL_NULL_V(match, ERR_BUG);
- const Map<const MethodInterface *, const InternalCall *>::Element *match = method_icalls_map.find(&E->get());
- ERR_FAIL_NULL_V(match, ERR_BUG);
+ const InternalCall *im_icall = match->value();
- const InternalCall *im_icall = match->value();
- String icall_method = im_icall->name;
+ String im_call = im_icall->editor_only ? CS_CLASS_NATIVECALLS_EDITOR : CS_CLASS_NATIVECALLS;
+ im_call += "." + im_icall->name + "(" + icall_params + ");\n";
- if (!generated_icall_funcs.find(im_icall)) {
- generated_icall_funcs.push_back(im_icall);
+ if (p_imethod.arguments.size())
+ p_output.push_back(cs_in_statements);
- if (im_icall->editor_only)
- cpp_file.push_back("#ifdef TOOLS_ENABLED\n");
+ if (return_type->name == "void") {
+ p_output.push_back(im_call);
+ } else if (return_type->cs_out.empty()) {
+ p_output.push_back("return " + im_call);
+ } else {
+ p_output.push_back(return_type->im_type_out);
+ p_output.push_back(" " LOCAL_RET " = ");
+ p_output.push_back(im_call);
+ p_output.push_back(INDENT3);
+ p_output.push_back(sformat(return_type->cs_out, LOCAL_RET) + "\n");
+ }
- // Generate icall function
+ p_output.push_back(CLOSE_BLOCK_L2);
+ }
- cpp_file.push_back(ret_void ? "void " : return_type->c_type_out + " ");
- cpp_file.push_back(icall_method);
- cpp_file.push_back("(");
- cpp_file.push_back(c_func_sig);
- cpp_file.push_back(") " OPEN_BLOCK);
+ p_method_bind_count++;
+ return OK;
+}
- String fail_ret = ret_void ? "" : ", " + (return_type->c_type_out.ends_with("*") ? "NULL" : return_type->c_type_out + "()");
+Error BindingsGenerator::generate_glue(const String &p_output_dir) {
- if (!ret_void) {
- String ptrcall_return_type;
- String initialization;
+ verbose_output = true;
- if (return_type->is_object_type) {
- ptrcall_return_type = return_type->is_reference ? "Ref<Reference>" : return_type->c_type;
- initialization = return_type->is_reference ? "" : " = NULL";
- } else {
- ptrcall_return_type = return_type->c_type;
- }
+ bool dir_exists = DirAccess::exists(p_output_dir);
+ ERR_EXPLAIN("The output directory does not exist.");
+ ERR_FAIL_COND_V(!dir_exists, ERR_FILE_BAD_PATH);
- cpp_file.push_back("\t" + ptrcall_return_type);
- cpp_file.push_back(" " LOCAL_RET);
- cpp_file.push_back(initialization + ";\n");
- cpp_file.push_back("\tERR_FAIL_NULL_V(" CS_PARAM_INSTANCE);
- cpp_file.push_back(fail_ret);
- cpp_file.push_back(");\n");
- } else {
- cpp_file.push_back("\tERR_FAIL_NULL(" CS_PARAM_INSTANCE ");\n");
- }
+ List<String> output;
- if (imethod.arguments.size()) {
- if (imethod.is_vararg) {
- String err_fail_macro = ret_void ? "ERR_FAIL_COND" : "ERR_FAIL_COND_V";
- String vararg_arg = "arg" + argc_str;
- String real_argc_str = itos(imethod.arguments.size() - 1); // Arguments count without vararg
-
- cpp_file.push_back("\tVector<Variant> varargs;\n"
- "\tint vararg_length = mono_array_length(");
- cpp_file.push_back(vararg_arg);
- cpp_file.push_back(");\n\tint total_length = ");
- cpp_file.push_back(real_argc_str);
- cpp_file.push_back(" + vararg_length;\n\t");
- cpp_file.push_back(err_fail_macro);
- cpp_file.push_back("(varargs.resize(vararg_length) != OK");
- cpp_file.push_back(fail_ret);
- cpp_file.push_back(");\n\tVector<Variant*> " C_LOCAL_PTRCALL_ARGS ";\n\t");
- cpp_file.push_back(err_fail_macro);
- cpp_file.push_back("(call_args.resize(total_length) != OK");
- cpp_file.push_back(fail_ret);
- cpp_file.push_back(");\n");
- cpp_file.push_back(c_in_statements);
- cpp_file.push_back("\tfor (int i = 0; i < vararg_length; i++) " OPEN_BLOCK
- "\t\tMonoObject* elem = mono_array_get(");
- cpp_file.push_back(vararg_arg);
- cpp_file.push_back(", MonoObject*, i);\n"
- "\t\tvarargs.set(i, GDMonoMarshal::mono_object_to_variant(elem));\n"
- "\t\t" C_LOCAL_PTRCALL_ARGS ".set(");
- cpp_file.push_back(real_argc_str);
- cpp_file.push_back(" + i, &varargs[i]);\n\t" CLOSE_BLOCK);
- } else {
- cpp_file.push_back(c_in_statements);
- cpp_file.push_back("\tconst void* " C_LOCAL_PTRCALL_ARGS "[");
- cpp_file.push_back(argc_str + "] = { ");
- cpp_file.push_back(c_args_var_content + " };\n");
- }
- }
+ output.push_back("#include \"" GLUE_HEADER_FILE "\"\n"
+ "\n");
- if (imethod.is_vararg) {
- cpp_file.push_back("\tVariant::CallError vcall_error;\n\t");
+ generated_icall_funcs.clear();
- if (!ret_void)
- cpp_file.push_back(LOCAL_RET " = ");
+ for (Map<String, TypeInterface>::Element *type_elem = obj_types.front(); type_elem; type_elem = type_elem->next()) {
+ const TypeInterface &itype = type_elem->get();
- cpp_file.push_back(CS_PARAM_METHODBIND "->call(" CS_PARAM_INSTANCE ", ");
- cpp_file.push_back(imethod.arguments.size() ? "(const Variant**)" C_LOCAL_PTRCALL_ARGS ".ptr()" : "NULL");
- cpp_file.push_back(", total_length, vcall_error);\n");
- } else {
- cpp_file.push_back("\t" CS_PARAM_METHODBIND "->ptrcall(" CS_PARAM_INSTANCE ", ");
- cpp_file.push_back(imethod.arguments.size() ? C_LOCAL_PTRCALL_ARGS ", " : "NULL, ");
- cpp_file.push_back(!ret_void ? "&" LOCAL_RET ");\n" : "NULL);\n");
- }
+ List<InternalCall> &custom_icalls = itype.api_type == ClassDB::API_EDITOR ? editor_custom_icalls : core_custom_icalls;
- if (!ret_void) {
- if (return_type->c_out.empty())
- cpp_file.push_back("\treturn " LOCAL_RET ";\n");
- else
- cpp_file.push_back(sformat(return_type->c_out, return_type->c_type_out, LOCAL_RET, return_type->name));
- }
+ OS::get_singleton()->print(String("Generating " + itype.name + "...\n").utf8());
- cpp_file.push_back(CLOSE_BLOCK "\n");
+ String ctor_method(ICALL_PREFIX + itype.proxy_name + "_Ctor");
- if (im_icall->editor_only)
- cpp_file.push_back("#endif // TOOLS_ENABLED\n");
+ for (const List<MethodInterface>::Element *E = itype.methods.front(); E; E = E->next()) {
+ const MethodInterface &imethod = E->get();
+ Error method_err = _generate_glue_method(itype, imethod, output);
+ if (method_err != OK) {
+ ERR_EXPLAIN("Failed to generate method '" + imethod.name + "' for class '" + itype.name + "'");
+ ERR_FAIL_V(method_err);
}
}
@@ -1339,11 +1167,11 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
if (!find_icall_by_name(singleton_icall.name, custom_icalls))
custom_icalls.push_back(singleton_icall);
- cpp_file.push_back("Object* ");
- cpp_file.push_back(singleton_icall_name);
- cpp_file.push_back("() " OPEN_BLOCK "\treturn ProjectSettings::get_singleton()->get_singleton_object(\"");
- cpp_file.push_back(itype.proxy_name);
- cpp_file.push_back("\");\n" CLOSE_BLOCK "\n");
+ output.push_back("Object* ");
+ output.push_back(singleton_icall_name);
+ output.push_back("() " OPEN_BLOCK "\treturn Engine::get_singleton()->get_singleton_object(\"");
+ output.push_back(itype.proxy_name);
+ output.push_back("\");\n" CLOSE_BLOCK "\n");
}
if (itype.is_instantiable) {
@@ -1352,36 +1180,36 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
if (!find_icall_by_name(ctor_icall.name, custom_icalls))
custom_icalls.push_back(ctor_icall);
- cpp_file.push_back("Object* ");
- cpp_file.push_back(ctor_method);
- cpp_file.push_back("(MonoObject* obj) " OPEN_BLOCK
- "\t" C_MACRO_OBJECT_CONSTRUCT "(instance, \"");
- cpp_file.push_back(itype.name);
- cpp_file.push_back("\");\n"
- "\t" C_METHOD_TIE_MANAGED_TO_UNMANAGED "(obj, instance);\n"
- "\treturn instance;\n" CLOSE_BLOCK "\n");
+ output.push_back("Object* ");
+ output.push_back(ctor_method);
+ output.push_back("(MonoObject* obj) " OPEN_BLOCK
+ "\t" C_MACRO_OBJECT_CONSTRUCT "(instance, \"");
+ output.push_back(itype.name);
+ output.push_back("\");\n"
+ "\t" C_METHOD_TIE_MANAGED_TO_UNMANAGED "(obj, instance);\n"
+ "\treturn instance;\n" CLOSE_BLOCK "\n");
}
}
- cpp_file.push_back("namespace GodotSharpBindings\n" OPEN_BLOCK);
- cpp_file.push_back("uint64_t get_core_api_hash() { return ");
- cpp_file.push_back(itos(GDMono::get_singleton()->get_api_core_hash()) + "; }\n");
- cpp_file.push_back("#ifdef TOOLS_ENABLED\n"
- "uint64_t get_editor_api_hash() { return ");
- cpp_file.push_back(itos(GDMono::get_singleton()->get_api_editor_hash()) +
- "; }\n#endif // TOOLS_ENABLED\n");
- cpp_file.push_back("void register_generated_icalls() " OPEN_BLOCK);
-
-#define ADD_INTERNAL_CALL_REGISTRATION(m_icall) \
- { \
- cpp_file.push_back("\tmono_add_internal_call("); \
- cpp_file.push_back("\"" BINDINGS_NAMESPACE "."); \
- cpp_file.push_back(m_icall.editor_only ? CS_CLASS_NATIVECALLS_EDITOR : CS_CLASS_NATIVECALLS); \
- cpp_file.push_back("::"); \
- cpp_file.push_back(m_icall.name); \
- cpp_file.push_back("\", (void*)"); \
- cpp_file.push_back(m_icall.name); \
- cpp_file.push_back(");\n"); \
+ 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("#ifdef TOOLS_ENABLED\n"
+ "uint64_t get_editor_api_hash() { return ");
+ output.push_back(itos(GDMono::get_singleton()->get_api_editor_hash()) +
+ "; }\n#endif // TOOLS_ENABLED\n");
+ output.push_back("void register_generated_icalls() " OPEN_BLOCK);
+
+#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"); \
}
bool tools_sequence = false;
@@ -1390,11 +1218,11 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
if (tools_sequence) {
if (!E->get().editor_only) {
tools_sequence = false;
- cpp_file.push_back("#endif\n");
+ output.push_back("#endif\n");
}
} else {
if (E->get().editor_only) {
- cpp_file.push_back("#ifdef TOOLS_ENABLED\n");
+ output.push_back("#ifdef TOOLS_ENABLED\n");
tools_sequence = true;
}
}
@@ -1404,24 +1232,23 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
if (tools_sequence) {
tools_sequence = false;
- cpp_file.push_back("#endif\n");
+ output.push_back("#endif\n");
}
- cpp_file.push_back("#ifdef TOOLS_ENABLED\n");
+ output.push_back("#ifdef TOOLS_ENABLED\n");
for (const List<InternalCall>::Element *E = editor_custom_icalls.front(); E; E = E->next())
ADD_INTERNAL_CALL_REGISTRATION(E->get());
- cpp_file.push_back("#endif // TOOLS_ENABLED\n");
+ output.push_back("#endif // TOOLS_ENABLED\n");
for (const List<InternalCall>::Element *E = method_icalls.front(); E; E = E->next()) {
-
if (tools_sequence) {
if (!E->get().editor_only) {
tools_sequence = false;
- cpp_file.push_back("#endif\n");
+ output.push_back("#endif\n");
}
} else {
if (E->get().editor_only) {
- cpp_file.push_back("#ifdef TOOLS_ENABLED\n");
+ output.push_back("#ifdef TOOLS_ENABLED\n");
tools_sequence = true;
}
}
@@ -1431,20 +1258,27 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
if (tools_sequence) {
tools_sequence = false;
- cpp_file.push_back("#endif\n");
+ output.push_back("#endif\n");
}
#undef ADD_INTERNAL_CALL_REGISTRATION
- cpp_file.push_back(CLOSE_BLOCK "}\n");
+ output.push_back(CLOSE_BLOCK "}\n");
+
+ Error save_err = _save_file(path_join(p_output_dir, "mono_glue.gen.cpp"), output);
+ if (save_err != OK)
+ return save_err;
- return _save_file(path_join(p_output_dir, "mono_glue.gen.cpp"), cpp_file);
+ OS::get_singleton()->print("Mono glue generated successfully\n");
+
+ return OK;
}
Error BindingsGenerator::_save_file(const String &p_path, const List<String> &p_content) {
FileAccessRef file = FileAccess::open(p_path, FileAccess::WRITE);
+ ERR_EXPLAIN("Cannot open file: " + p_path);
ERR_FAIL_COND_V(!file, ERR_FILE_CANT_WRITE);
for (const List<String>::Element *E = p_content.front(); E; E = E->next()) {
@@ -1456,6 +1290,163 @@ Error BindingsGenerator::_save_file(const String &p_path, const List<String> &p_
return OK;
}
+Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, List<String> &p_output) {
+
+ if (p_imethod.is_virtual)
+ return OK; // Ignore
+
+ bool ret_void = p_imethod.return_type == "void";
+
+ const TypeInterface *return_type = _get_type_by_name_or_placeholder(p_imethod.return_type);
+
+ String argc_str = itos(p_imethod.arguments.size());
+
+ String c_func_sig = "MethodBind* " CS_PARAM_METHODBIND ", " + p_itype.c_type_in + " " CS_PARAM_INSTANCE;
+ String c_in_statements;
+ String c_args_var_content;
+
+ // Get arguments information
+ int i = 0;
+ for (const List<ArgumentInterface>::Element *F = p_imethod.arguments.front(); F; F = F->next()) {
+ const ArgumentInterface &iarg = F->get();
+ const TypeInterface *arg_type = _get_type_by_name_or_placeholder(iarg.type);
+
+ String c_param_name = "arg" + itos(i + 1);
+
+ 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";
+ }
+ } else {
+ if (i > 0)
+ c_args_var_content += ", ";
+ if (arg_type->c_in.size())
+ c_in_statements += sformat(arg_type->c_in, arg_type->c_type, c_param_name);
+ c_args_var_content += sformat(arg_type->c_arg_in, c_param_name);
+ }
+
+ c_func_sig += ", ";
+ c_func_sig += arg_type->c_type_in;
+ c_func_sig += " ";
+ c_func_sig += c_param_name;
+
+ i++;
+ }
+
+ const Map<const MethodInterface *, const InternalCall *>::Element *match = method_icalls_map.find(&p_imethod);
+ ERR_FAIL_NULL_V(match, ERR_BUG);
+
+ const InternalCall *im_icall = match->value();
+ String icall_method = im_icall->name;
+
+ if (!generated_icall_funcs.find(im_icall)) {
+ generated_icall_funcs.push_back(im_icall);
+
+ if (im_icall->editor_only)
+ p_output.push_back("#ifdef TOOLS_ENABLED\n");
+
+ // Generate icall function
+
+ p_output.push_back(ret_void ? "void " : return_type->c_type_out + " ");
+ p_output.push_back(icall_method);
+ p_output.push_back("(");
+ p_output.push_back(c_func_sig);
+ p_output.push_back(") " OPEN_BLOCK);
+
+ String fail_ret = ret_void ? "" : ", " + (return_type->c_type_out.ends_with("*") ? "NULL" : return_type->c_type_out + "()");
+
+ if (!ret_void) {
+ String ptrcall_return_type;
+ String initialization;
+
+ if (return_type->is_object_type) {
+ ptrcall_return_type = return_type->is_reference ? "Ref<Reference>" : return_type->c_type;
+ initialization = return_type->is_reference ? "" : " = NULL";
+ } else {
+ ptrcall_return_type = return_type->c_type;
+ }
+
+ p_output.push_back("\t" + ptrcall_return_type);
+ p_output.push_back(" " LOCAL_RET);
+ p_output.push_back(initialization + ";\n");
+ p_output.push_back("\tERR_FAIL_NULL_V(" CS_PARAM_INSTANCE);
+ p_output.push_back(fail_ret);
+ p_output.push_back(");\n");
+ } else {
+ p_output.push_back("\tERR_FAIL_NULL(" CS_PARAM_INSTANCE ");\n");
+ }
+
+ if (p_imethod.arguments.size()) {
+ if (p_imethod.is_vararg) {
+ String err_fail_macro = ret_void ? "ERR_FAIL_COND" : "ERR_FAIL_COND_V";
+ String vararg_arg = "arg" + argc_str;
+ String real_argc_str = itos(p_imethod.arguments.size() - 1); // Arguments count without vararg
+
+ p_output.push_back("\tVector<Variant> varargs;\n"
+ "\tint vararg_length = mono_array_length(");
+ p_output.push_back(vararg_arg);
+ p_output.push_back(");\n\tint total_length = ");
+ p_output.push_back(real_argc_str);
+ p_output.push_back(" + vararg_length;\n\t");
+ p_output.push_back(err_fail_macro);
+ p_output.push_back("(varargs.resize(vararg_length) != OK");
+ p_output.push_back(fail_ret);
+ p_output.push_back(");\n\tVector<Variant*> " C_LOCAL_PTRCALL_ARGS ";\n\t");
+ p_output.push_back(err_fail_macro);
+ p_output.push_back("(call_args.resize(total_length) != OK");
+ p_output.push_back(fail_ret);
+ p_output.push_back(");\n");
+ p_output.push_back(c_in_statements);
+ p_output.push_back("\tfor (int i = 0; i < vararg_length; i++) " OPEN_BLOCK
+ "\t\tMonoObject* elem = mono_array_get(");
+ p_output.push_back(vararg_arg);
+ p_output.push_back(", MonoObject*, i);\n"
+ "\t\tvarargs.set(i, GDMonoMarshal::mono_object_to_variant(elem));\n"
+ "\t\t" C_LOCAL_PTRCALL_ARGS ".set(");
+ p_output.push_back(real_argc_str);
+ p_output.push_back(" + i, &varargs[i]);\n\t" CLOSE_BLOCK);
+ } else {
+ p_output.push_back(c_in_statements);
+ p_output.push_back("\tconst void* " C_LOCAL_PTRCALL_ARGS "[");
+ p_output.push_back(argc_str + "] = { ");
+ p_output.push_back(c_args_var_content + " };\n");
+ }
+ }
+
+ if (p_imethod.is_vararg) {
+ p_output.push_back("\tVariant::CallError vcall_error;\n\t");
+
+ if (!ret_void)
+ p_output.push_back(LOCAL_RET " = ");
+
+ p_output.push_back(CS_PARAM_METHODBIND "->call(" CS_PARAM_INSTANCE ", ");
+ p_output.push_back(p_imethod.arguments.size() ? "(const Variant**)" C_LOCAL_PTRCALL_ARGS ".ptr()" : "NULL");
+ p_output.push_back(", total_length, vcall_error);\n");
+ } else {
+ p_output.push_back("\t" CS_PARAM_METHODBIND "->ptrcall(" CS_PARAM_INSTANCE ", ");
+ p_output.push_back(p_imethod.arguments.size() ? C_LOCAL_PTRCALL_ARGS ", " : "NULL, ");
+ p_output.push_back(!ret_void ? "&" LOCAL_RET ");\n" : "NULL);\n");
+ }
+
+ if (!ret_void) {
+ if (return_type->c_out.empty())
+ p_output.push_back("\treturn " LOCAL_RET ";\n");
+ else
+ p_output.push_back(sformat(return_type->c_out, return_type->c_type_out, LOCAL_RET, return_type->name));
+ }
+
+ p_output.push_back(CLOSE_BLOCK "\n");
+
+ if (im_icall->editor_only)
+ p_output.push_back("#endif // TOOLS_ENABLED\n");
+ }
+
+ return OK;
+}
+
const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_by_name_or_null(const String &p_name) {
const Map<String, TypeInterface>::Element *match = builtin_types.find(p_name);
@@ -1514,11 +1505,18 @@ void BindingsGenerator::_populate_object_type_interfaces() {
TypeInterface itype = TypeInterface::create_object_type(type_cname, api_type);
itype.base_name = ClassDB::get_parent_class(type_cname);
- itype.is_singleton = ProjectSettings::get_singleton()->has_singleton(itype.proxy_name);
+ itype.is_singleton = Engine::get_singleton()->has_singleton(itype.proxy_name);
itype.is_instantiable = ClassDB::can_instance(type_cname) && !itype.is_singleton;
itype.is_reference = ClassDB::is_parent_class(type_cname, refclass_name);
itype.memory_own = itype.is_reference;
+ if (!ClassDB::is_class_exposed(type_cname)) {
+ if (verbose_output)
+ WARN_PRINTS("Ignoring type " + String(type_cname) + " because it's not exposed");
+ class_list.pop_front();
+ continue;
+ }
+
itype.c_out = "\treturn ";
itype.c_out += C_METHOD_UNMANAGED_GET_MANAGED;
itype.c_out += itype.is_reference ? "(%1.ptr());\n" : "(%1);\n";
@@ -1578,9 +1576,11 @@ void BindingsGenerator::_populate_object_type_interfaces() {
// which could actually will return something differnet.
// Let's put this to notify us if that ever happens.
if (itype.name != "Object" || imethod.name != "free") {
- WARN_PRINTS("Notification: New unexpected virtual non-overridable method found.\n"
- "We only expected Object.free, but found " +
- itype.name + "." + imethod.name);
+ if (verbose_output) {
+ WARN_PRINTS("Notification: New unexpected virtual non-overridable method found.\n"
+ "We only expected Object.free, but found " +
+ itype.name + "." + imethod.name);
+ }
}
} else {
ERR_PRINTS("Missing MethodBind for non-virtual method: " + itype.name + "." + imethod.name);
@@ -1721,7 +1721,7 @@ void BindingsGenerator::_default_argument_from_variant(const Variant &p_val, Arg
r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
break;
case Variant::PLANE:
- case Variant::RECT3:
+ case Variant::AABB:
case Variant::COLOR:
r_iarg.default_argument = "new Color(1, 1, 1, 1)";
r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
@@ -1793,7 +1793,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
INSERT_STRUCT_TYPE(Basis, "real_t*")
INSERT_STRUCT_TYPE(Quat, "real_t*")
INSERT_STRUCT_TYPE(Transform, "real_t*")
- INSERT_STRUCT_TYPE(Rect3, "real_t*")
+ INSERT_STRUCT_TYPE(AABB, "real_t*")
INSERT_STRUCT_TYPE(Color, "real_t*")
INSERT_STRUCT_TYPE(Plane, "real_t*")
@@ -2086,7 +2086,8 @@ BindingsGenerator::BindingsGenerator() {
void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args) {
- int options_count = 3;
+ const int NUM_OPTIONS = 3;
+ int options_left = NUM_OPTIONS;
String mono_glue_option = "--generate-mono-glue";
String cs_core_api_option = "--generate-cs-core-api";
@@ -2096,33 +2097,35 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args)
const List<String>::Element *elem = p_cmdline_args.front();
- while (elem && options_count) {
+ while (elem && options_left) {
if (elem->get() == mono_glue_option) {
const List<String>::Element *path_elem = elem->next();
if (path_elem) {
- get_singleton().generate_glue(path_elem->get());
+ if (get_singleton().generate_glue(path_elem->get()) != OK)
+ ERR_PRINT("Mono glue generation failed");
elem = elem->next();
} else {
ERR_PRINTS("--generate-mono-glue: No output directory specified");
}
- --options_count;
+ --options_left;
} else if (elem->get() == cs_core_api_option) {
const List<String>::Element *path_elem = elem->next();
if (path_elem) {
- get_singleton().generate_cs_core_project(path_elem->get());
+ if (get_singleton().generate_cs_core_project(path_elem->get()) != OK)
+ ERR_PRINT("Generation of solution and C# project for the Core API failed");
elem = elem->next();
} else {
ERR_PRINTS(cs_core_api_option + ": No output directory specified");
}
- --options_count;
+ --options_left;
} else if (elem->get() == cs_editor_api_option) {
@@ -2130,7 +2133,8 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args)
if (path_elem) {
if (path_elem->next()) {
- get_singleton().generate_cs_editor_project(path_elem->get(), path_elem->next()->get());
+ if (get_singleton().generate_cs_editor_project(path_elem->get(), path_elem->next()->get()) != OK)
+ ERR_PRINT("Generation of solution and C# project for the Editor API failed");
elem = path_elem->next();
} else {
ERR_PRINTS(cs_editor_api_option + ": No hint path for the Core API dll specified");
@@ -2139,13 +2143,16 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args)
ERR_PRINTS(cs_editor_api_option + ": No output directory specified");
}
- --options_count;
+ --options_left;
}
elem = elem->next();
}
verbose_output = false;
+
+ if (options_left != NUM_OPTIONS)
+ exit(0);
}
#endif
diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h
index 437a566556..dfa3aa9911 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -368,6 +368,8 @@ class BindingsGenerator {
List<InternalCall> method_icalls;
Map<const MethodInterface *, const InternalCall *> method_icalls_map;
+ List<const InternalCall *> generated_icall_funcs;
+
List<InternalCall> core_custom_icalls;
List<InternalCall> editor_custom_icalls;
@@ -404,6 +406,11 @@ class BindingsGenerator {
Error _generate_cs_type(const TypeInterface &itype, const String &p_output_file);
+ Error _generate_cs_property(const TypeInterface &p_itype, const DocData::PropertyDoc &p_prop_doc, List<String> &p_output);
+ Error _generate_cs_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, int &p_method_bind_count, List<String> &p_output);
+
+ Error _generate_glue_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, List<String> &p_output);
+
Error _save_file(const String &path, const List<String> &content);
BindingsGenerator();
diff --git a/modules/mono/editor/godotsharp_builds.cpp b/modules/mono/editor/godotsharp_builds.cpp
index 1bad8a3f85..b88d34fc33 100644
--- a/modules/mono/editor/godotsharp_builds.cpp
+++ b/modules/mono/editor/godotsharp_builds.cpp
@@ -32,6 +32,7 @@
#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"
@@ -52,6 +53,10 @@ String _find_build_engine_on_unix(const String &p_name) {
if (ret.length())
return ret;
+ String ret_fallback = path_which(p_name + ".exe");
+ if (ret_fallback.length())
+ return ret_fallback;
+
const char *locations[] = {
#ifdef OSX_ENABLED
"/Library/Frameworks/Mono.framework/Versions/Current/bin/",
@@ -60,10 +65,10 @@ String _find_build_engine_on_unix(const String &p_name) {
};
for (int i = 0; i < sizeof(locations) / sizeof(const char *); i++) {
- String location = locations[i];
+ String hint_path = locations[i] + p_name;
- if (FileAccess::exists(location + p_name)) {
- return location;
+ if (FileAccess::exists(hint_path)) {
+ return hint_path;
}
}
@@ -71,7 +76,7 @@ String _find_build_engine_on_unix(const String &p_name) {
}
#endif
-MonoString *godot_icall_BuildInstance_get_MSBuildPath() {
+void godot_icall_BuildInstance_get_MSBuildInfo(MonoString **r_msbuild_path, MonoString **r_framework_path) {
GodotSharpBuilds::BuildTool build_tool = GodotSharpBuilds::BuildTool(int(EditorSettings::get_singleton()->get("mono/builds/build_tool")));
@@ -84,11 +89,23 @@ MonoString *godot_icall_BuildInstance_get_MSBuildPath() {
if (!msbuild_tools_path.ends_with("\\"))
msbuild_tools_path += "\\";
- return GDMonoMarshal::mono_string_from_godot(msbuild_tools_path + "MSBuild.exe");
+ // FrameworkPathOverride
+ const MonoRegInfo &mono_reg_info = GDMono::get_singleton()->get_mono_reg_info();
+ if (mono_reg_info.assembly_dir.length()) {
+ *r_msbuild_path = GDMonoMarshal::mono_string_from_godot(msbuild_tools_path + "MSBuild.exe");
+
+ String framework_path = path_join(mono_reg_info.assembly_dir, "mono", "4.5");
+ *r_framework_path = GDMonoMarshal::mono_string_from_godot(framework_path);
+ } else {
+ ERR_PRINT("Cannot find Mono's assemblies directory in the registry");
+ }
+
+ return;
}
- OS::get_singleton()->print("Cannot find System's MSBuild. Trying with Mono's...\n");
- }
+ if (OS::get_singleton()->is_stdout_verbose())
+ OS::get_singleton()->print("Cannot find System's MSBuild. Trying with Mono's...\n");
+ } // fall through
case GodotSharpBuilds::MSBUILD_MONO: {
String msbuild_path = GDMono::get_singleton()->get_mono_reg_info().bin_dir.plus_file("msbuild.bat");
@@ -96,17 +113,10 @@ MonoString *godot_icall_BuildInstance_get_MSBuildPath() {
WARN_PRINTS("Cannot find msbuild ('mono/builds/build_tool'). Tried with path: " + msbuild_path);
}
- return GDMonoMarshal::mono_string_from_godot(msbuild_path);
- }
- case GodotSharpBuilds::XBUILD: {
- String xbuild_path = GDMono::get_singleton()->get_mono_reg_info().bin_dir.plus_file("xbuild.bat");
+ *r_msbuild_path = GDMonoMarshal::mono_string_from_godot(msbuild_path);
- if (!FileAccess::exists(xbuild_path)) {
- WARN_PRINTS("Cannot find xbuild ('mono/builds/build_tool'). Tried with path: " + xbuild_path);
- }
-
- return GDMonoMarshal::mono_string_from_godot(xbuild_path);
- }
+ return;
+ } break;
default:
ERR_EXPLAIN("You don't deserve to live");
CRASH_NOW();
@@ -118,25 +128,28 @@ MonoString *godot_icall_BuildInstance_get_MSBuildPath() {
if (build_tool != GodotSharpBuilds::XBUILD) {
if (msbuild_path.empty()) {
WARN_PRINT("Cannot find msbuild ('mono/builds/build_tool').");
- return NULL;
+ return;
}
} else {
if (xbuild_path.empty()) {
WARN_PRINT("Cannot find xbuild ('mono/builds/build_tool').");
- return NULL;
+ return;
}
}
- return GDMonoMarshal::mono_string_from_godot(build_tool != GodotSharpBuilds::XBUILD ? msbuild_path : xbuild_path);
+ *r_msbuild_path = GDMonoMarshal::mono_string_from_godot(build_tool != GodotSharpBuilds::XBUILD ? msbuild_path : xbuild_path);
+
+ return;
#else
- return NULL;
+ ERR_PRINT("Not implemented on this platform");
+ return;
#endif
}
void GodotSharpBuilds::_register_internal_calls() {
mono_add_internal_call("GodotSharpTools.Build.BuildSystem::godot_icall_BuildInstance_ExitCallback", (void *)godot_icall_BuildInstance_ExitCallback);
- mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_MSBuildPath", (void *)godot_icall_BuildInstance_get_MSBuildPath);
+ mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_MSBuildInfo", (void *)godot_icall_BuildInstance_get_MSBuildInfo);
}
void GodotSharpBuilds::show_build_error_dialog(const String &p_message) {
@@ -269,7 +282,7 @@ bool GodotSharpBuilds::make_api_sln(GodotSharpBuilds::APIType p_api_type) {
return true;
}
-bool godotsharp_build_callback() {
+bool GodotSharpBuilds::build_project_blocking() {
if (!FileAccess::exists(GodotSharpDirs::get_project_sln_path()))
return true; // No solution to build
@@ -348,14 +361,25 @@ GodotSharpBuilds::GodotSharpBuilds() {
singleton = this;
- EditorNode::get_singleton()->add_build_callback(&godotsharp_build_callback);
+ EditorNode::get_singleton()->add_build_callback(&GodotSharpBuilds::build_project_blocking);
// Build tool settings
EditorSettings *ed_settings = EditorSettings::get_singleton();
- if (!ed_settings->has_setting("mono/builds/build_tool")) {
- ed_settings->set_setting("mono/builds/build_tool", MSBUILD);
- }
- ed_settings->add_property_hint(PropertyInfo(Variant::INT, "mono/builds/build_tool", PROPERTY_HINT_ENUM, "MSBuild (System),MSBuild (Mono),xbuild"));
+
+#ifdef WINDOWS_ENABLED
+ // TODO: Default to MSBUILD_MONO if its csc.exe issue is fixed in the installed mono version
+ EDITOR_DEF("mono/builds/build_tool", MSBUILD);
+#else
+ EDITOR_DEF("mono/builds/build_tool", MSBUILD_MONO);
+#endif
+
+ ed_settings->add_property_hint(PropertyInfo(Variant::INT, "mono/builds/build_tool", PROPERTY_HINT_ENUM,
+#ifdef WINDOWS_ENABLED
+ "MSBuild (Mono),MSBuild (System)"
+#else
+ "MSBuild (Mono),xbuild (Deprecated)"
+#endif
+ ));
}
GodotSharpBuilds::~GodotSharpBuilds() {
@@ -395,10 +419,11 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) {
}
if (!exited) {
- ERR_PRINT("BuildProcess::start called, but process still running");
exited = true;
- build_tab->on_build_exec_failed("!exited");
- return;
+ String message = "Tried to start build process, but it is already running";
+ build_tab->on_build_exec_failed(message);
+ ERR_EXPLAIN(message);
+ ERR_FAIL();
}
exited = false;
@@ -410,10 +435,12 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) {
if (d->file_exists(issues_file)) {
Error err = d->remove(issues_file);
if (err != OK) {
- ERR_PRINTS("Cannot remove file: " + logs_dir.plus_file(issues_file));
exited = true;
- build_tab->on_build_exec_failed("Cannot remove file: " + issues_file);
- return;
+ String file_path = ProjectSettings::get_singleton()->localize_path(logs_dir).plus_file(issues_file);
+ String message = "Cannot remove issues file: " + file_path;
+ build_tab->on_build_exec_failed(message);
+ ERR_EXPLAIN(message);
+ ERR_FAIL();
}
}
@@ -434,7 +461,9 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) {
if (ex) {
exited = true;
- build_tab->on_build_exec_failed("The build constructor threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(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);
ERR_FAIL();
}
@@ -452,7 +481,9 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) {
if (ex) {
exited = true;
- build_tab->on_build_exec_failed("The build method threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(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);
ERR_FAIL();
}
diff --git a/modules/mono/editor/godotsharp_builds.h b/modules/mono/editor/godotsharp_builds.h
index 6d5fa3b44a..7d2f38a774 100644
--- a/modules/mono/editor/godotsharp_builds.h
+++ b/modules/mono/editor/godotsharp_builds.h
@@ -67,9 +67,12 @@ public:
};
enum BuildTool {
- MSBUILD,
MSBUILD_MONO,
- XBUILD
+#ifdef WINDOWS_ENABLED
+ MSBUILD
+#else
+ XBUILD // Deprecated
+#endif
};
_FORCE_INLINE_ static GodotSharpBuilds *get_singleton() { return singleton; }
@@ -89,6 +92,8 @@ public:
static bool make_api_sln(APIType p_api_type);
+ static bool build_project_blocking();
+
GodotSharpBuilds();
~GodotSharpBuilds();
};
diff --git a/modules/mono/editor/godotsharp_editor.cpp b/modules/mono/editor/godotsharp_editor.cpp
index 30e7653256..1bc1e8a515 100644
--- a/modules/mono/editor/godotsharp_editor.cpp
+++ b/modules/mono/editor/godotsharp_editor.cpp
@@ -46,21 +46,6 @@
#include "../utils/mono_reg_utils.h"
#endif
-class MonoReloadNode : public Node {
- GDCLASS(MonoReloadNode, Node)
-
-protected:
- void _notification(int p_what) {
- switch (p_what) {
- case MainLoop::NOTIFICATION_WM_FOCUS_IN: {
- CSharpLanguage::get_singleton()->reload_assemblies_if_needed(true);
- } break;
- default: {
- } break;
- };
- }
-};
-
GodotSharpEditor *GodotSharpEditor::singleton = NULL;
bool GodotSharpEditor::_create_project_solution() {
@@ -71,6 +56,10 @@ bool GodotSharpEditor::_create_project_solution() {
String path = OS::get_singleton()->get_resource_dir();
String name = ProjectSettings::get_singleton()->get("application/config/name");
+ if (name.empty()) {
+ name = "UnnamedProject";
+ }
+
String guid = CSharpProject::generate_game_project(path, name);
if (guid.length()) {
@@ -182,11 +171,6 @@ Error GodotSharpEditor::open_in_external_editor(const Ref<Script> &p_script, int
String script_path = ProjectSettings::get_singleton()->globalize_path(p_script->get_path());
monodevel_instance->execute(script_path);
} break;
- case EDITOR_VISUAL_STUDIO:
- // TODO
- // devenv <PathToSolutionFolder>
- // devenv /edit <PathToCsFile> /command "edit.goto <Line>"
- // HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\SxS\VS7
default:
return ERR_UNAVAILABLE;
}
@@ -237,10 +221,8 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) {
// External editor settings
EditorSettings *ed_settings = EditorSettings::get_singleton();
- if (!ed_settings->has_setting("mono/editor/external_editor")) {
- ed_settings->set_setting("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,Visual Studio Code"));
+ 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"));
}
GodotSharpEditor::~GodotSharpEditor() {
@@ -254,3 +236,49 @@ GodotSharpEditor::~GodotSharpEditor() {
monodevel_instance = NULL;
}
}
+
+MonoReloadNode *MonoReloadNode::singleton = NULL;
+
+void MonoReloadNode::_reload_timer_timeout() {
+
+ CSharpLanguage::get_singleton()->reload_assemblies_if_needed(false);
+}
+
+void MonoReloadNode::restart_reload_timer() {
+
+ reload_timer->stop();
+ reload_timer->start();
+}
+
+void MonoReloadNode::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_reload_timer_timeout"), &MonoReloadNode::_reload_timer_timeout);
+}
+
+void MonoReloadNode::_notification(int p_what) {
+ switch (p_what) {
+ case MainLoop::NOTIFICATION_WM_FOCUS_IN: {
+ restart_reload_timer();
+ CSharpLanguage::get_singleton()->reload_assemblies_if_needed(true);
+ } break;
+ default: {
+ } break;
+ };
+}
+
+MonoReloadNode::MonoReloadNode() {
+
+ singleton = this;
+
+ reload_timer = memnew(Timer);
+ add_child(reload_timer);
+ reload_timer->set_one_shot(false);
+ reload_timer->set_wait_time(EDITOR_DEF("mono/assembly_watch_interval_sec", 0.5));
+ reload_timer->connect("timeout", this, "_reload_timer_timeout");
+ reload_timer->start();
+}
+
+MonoReloadNode::~MonoReloadNode() {
+
+ singleton = NULL;
+}
diff --git a/modules/mono/editor/godotsharp_editor.h b/modules/mono/editor/godotsharp_editor.h
index 1ecb8c7a94..0f2c163582 100644
--- a/modules/mono/editor/godotsharp_editor.h
+++ b/modules/mono/editor/godotsharp_editor.h
@@ -69,7 +69,6 @@ public:
enum ExternalEditor {
EDITOR_NONE,
EDITOR_MONODEVELOP,
- EDITOR_VISUAL_STUDIO,
EDITOR_CODE,
};
@@ -84,4 +83,27 @@ public:
~GodotSharpEditor();
};
+class MonoReloadNode : public Node {
+ GDCLASS(MonoReloadNode, Node)
+
+ Timer *reload_timer;
+
+ void _reload_timer_timeout();
+
+ static MonoReloadNode *singleton;
+
+protected:
+ static void _bind_methods();
+
+ void _notification(int p_what);
+
+public:
+ _FORCE_INLINE_ static MonoReloadNode *get_singleton() { return singleton; }
+
+ void restart_reload_timer();
+
+ MonoReloadNode();
+ ~MonoReloadNode();
+};
+
#endif // GODOTSHARP_EDITOR_H
diff --git a/modules/mono/editor/mono_bottom_panel.cpp b/modules/mono/editor/mono_bottom_panel.cpp
index 07109eaac7..31dc09856a 100644
--- a/modules/mono/editor/mono_bottom_panel.cpp
+++ b/modules/mono/editor/mono_bottom_panel.cpp
@@ -139,6 +139,14 @@ void MonoBottomPanel::_errors_toggled(bool p_pressed) {
build_tab->_update_issues_list();
}
+void MonoBottomPanel::_build_project_pressed() {
+
+ GodotSharpBuilds::get_singleton()->build_project_blocking();
+
+ MonoReloadNode::get_singleton()->restart_reload_timer();
+ CSharpLanguage::get_singleton()->reload_assemblies_if_needed(true);
+}
+
void MonoBottomPanel::_notification(int p_what) {
switch (p_what) {
@@ -153,6 +161,7 @@ void MonoBottomPanel::_notification(int p_what) {
void MonoBottomPanel::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_build_project_pressed"), &MonoBottomPanel::_build_project_pressed);
ClassDB::bind_method(D_METHOD("_warnings_toggled", "pressed"), &MonoBottomPanel::_warnings_toggled);
ClassDB::bind_method(D_METHOD("_errors_toggled", "pressed"), &MonoBottomPanel::_errors_toggled);
ClassDB::bind_method(D_METHOD("_build_tab_item_selected", "idx"), &MonoBottomPanel::_build_tab_item_selected);
@@ -187,6 +196,12 @@ MonoBottomPanel::MonoBottomPanel(EditorNode *p_editor) {
toolbar_hbc->set_h_size_flags(SIZE_EXPAND_FILL);
panel_builds_tab->add_child(toolbar_hbc);
+ ToolButton *build_project_btn = memnew(ToolButton);
+ build_project_btn->set_text("Build Project");
+ build_project_btn->set_focus_mode(FOCUS_NONE);
+ build_project_btn->connect("pressed", this, "_build_project_pressed");
+ toolbar_hbc->add_child(build_project_btn);
+
toolbar_hbc->add_spacer();
warnings_btn = memnew(ToolButton);
@@ -280,7 +295,11 @@ void MonoBuildTab::_update_issues_list() {
String tooltip;
tooltip += String("Message: ") + issue.message;
- tooltip += String("\nCode: ") + issue.code;
+
+ if (issue.code.length()) {
+ tooltip += String("\nCode: ") + issue.code;
+ }
+
tooltip += String("\nType: ") + (issue.warning ? "warning" : "error");
String text;
@@ -356,23 +375,21 @@ void MonoBuildTab::on_build_exit(BuildResult result) {
MonoBottomPanel::get_singleton()->raise_build_tab(this);
}
-void MonoBuildTab::on_build_exec_failed(const String &p_cause, const String &p_detailed) {
+void MonoBuildTab::on_build_exec_failed(const String &p_cause) {
build_exited = true;
build_result = RESULT_ERROR;
issues_list->clear();
- String tooltip;
+ BuildIssue issue;
+ issue.message = p_cause;
+ issue.warning = false;
- tooltip += "Message: " + (p_detailed.length() ? p_detailed : p_cause);
- tooltip += "\nType: error";
+ error_count += 1;
+ issues.push_back(issue);
- int line_break_idx = p_cause.find("\n");
- issues_list->add_item(line_break_idx == -1 ? p_cause : p_cause.substr(0, line_break_idx),
- get_icon("Error", "EditorIcons"));
- int index = issues_list->get_item_count() - 1;
- issues_list->set_item_tooltip(index, tooltip);
+ _update_issues_list();
MonoBottomPanel::get_singleton()->raise_build_tab(this);
}
diff --git a/modules/mono/editor/mono_bottom_panel.h b/modules/mono/editor/mono_bottom_panel.h
index 909fa4b385..5cc4aa3240 100644
--- a/modules/mono/editor/mono_bottom_panel.h
+++ b/modules/mono/editor/mono_bottom_panel.h
@@ -61,6 +61,8 @@ class MonoBottomPanel : public VBoxContainer {
void _warnings_toggled(bool p_pressed);
void _errors_toggled(bool p_pressed);
+ void _build_project_pressed();
+
static MonoBottomPanel *singleton;
protected:
@@ -134,7 +136,7 @@ public:
void on_build_start();
void on_build_exit(BuildResult result);
- void on_build_exec_failed(const String &p_cause, const String &p_detailed = String());
+ void on_build_exec_failed(const String &p_cause);
void restart_build();
void stop_build();