C# bindings generator cleanup

- Normal log messages are no longer warnings.
- BindingsGenerator is no longer a singleton.
- Added a log function.
This commit is contained in:
Ignacio Etcheverry 2019-04-25 15:45:12 +02:00
parent bf1fe11143
commit c20a3823a2
7 changed files with 139 additions and 108 deletions

View file

@ -131,14 +131,6 @@ void CSharpLanguage::finish() {
finalizing = true;
#ifdef TOOLS_ENABLED
// Must be here, to avoid StringName leaks
if (BindingsGenerator::singleton) {
memdelete(BindingsGenerator::singleton);
BindingsGenerator::singleton = NULL;
}
#endif
// Make sure all script binding gchandles are released before finalizing GDMono
for (Map<Object *, CSharpScriptBinding>::Element *E = script_bindings.front(); E; E = E->next()) {
CSharpScriptBinding &script_binding = E->value();

View file

@ -101,10 +101,6 @@
const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN("\t%0 %1_in = %1;\n");
bool BindingsGenerator::verbose_output = false;
BindingsGenerator *BindingsGenerator::singleton = NULL;
static String fix_doc_description(const String &p_bbcode) {
// This seems to be the correct way to do this. It's the same EditorHelp does.
@ -816,9 +812,7 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
CRASH_COND(enum_class_name != "Variant"); // Hard-coded...
if (verbose_output) {
WARN_PRINTS("Declaring global enum `" + enum_proxy_name + "` inside static class `" + enum_class_name + "`");
}
_log("Declaring global enum `%s` inside static class `%s`\n", enum_proxy_name.utf8().get_data(), enum_class_name.utf8().get_data());
p_output.append("\n" INDENT1 "public static partial class ");
p_output.append(enum_class_name);
@ -867,9 +861,7 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
p_output.append("\n#pragma warning restore CS1591\n");
}
Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir, DotNetSolution &r_solution, bool p_verbose_output) {
verbose_output = p_verbose_output;
Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir, DotNetSolution &r_solution) {
String proj_dir = p_solution_dir.plus_file(CORE_API_ASSEMBLY_NAME);
@ -1001,15 +993,12 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir,
r_solution.add_new_project(CORE_API_ASSEMBLY_NAME, proj_info);
if (verbose_output)
OS::get_singleton()->print("The solution and C# project for the Core API was generated successfully\n");
_log("The solution and C# project for the Core API was generated successfully\n");
return OK;
}
Error BindingsGenerator::generate_cs_editor_project(const String &p_solution_dir, DotNetSolution &r_solution, bool p_verbose_output) {
verbose_output = p_verbose_output;
Error BindingsGenerator::generate_cs_editor_project(const String &p_solution_dir, DotNetSolution &r_solution) {
String proj_dir = p_solution_dir.plus_file(EDITOR_API_ASSEMBLY_NAME);
@ -1100,13 +1089,12 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_solution_dir
r_solution.add_new_project(EDITOR_API_ASSEMBLY_NAME, proj_info);
if (verbose_output)
OS::get_singleton()->print("The solution and C# project for the Editor API was generated successfully\n");
_log("The solution and C# project for the Editor API was generated successfully\n");
return OK;
}
Error BindingsGenerator::generate_cs_api(const String &p_output_dir, bool p_verbose_output) {
Error BindingsGenerator::generate_cs_api(const String &p_output_dir) {
DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
ERR_FAIL_COND_V(!da, ERR_CANT_CREATE);
@ -1123,13 +1111,13 @@ Error BindingsGenerator::generate_cs_api(const String &p_output_dir, bool p_verb
Error proj_err;
proj_err = generate_cs_core_project(p_output_dir, solution, p_verbose_output);
proj_err = generate_cs_core_project(p_output_dir, solution);
if (proj_err != OK) {
ERR_PRINT("Generation of the Core API C# project failed");
return proj_err;
}
proj_err = generate_cs_editor_project(p_output_dir, solution, p_verbose_output);
proj_err = generate_cs_editor_project(p_output_dir, solution);
if (proj_err != OK) {
ERR_PRINT("Generation of the Editor API C# project failed");
return proj_err;
@ -1168,8 +1156,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
List<InternalCall> &custom_icalls = itype.api_type == ClassDB::API_EDITOR ? editor_custom_icalls : core_custom_icalls;
if (verbose_output)
OS::get_singleton()->print("Generating %s.cs...\n", itype.proxy_name.utf8().get_data());
_log("Generating %s.cs...\n", itype.proxy_name.utf8().get_data());
String ctor_method(ICALL_PREFIX + itype.proxy_name + "_Ctor"); // Used only for derived types
@ -1710,8 +1697,6 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
Error BindingsGenerator::generate_glue(const String &p_output_dir) {
verbose_output = true;
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);
@ -2128,15 +2113,13 @@ void BindingsGenerator::_populate_object_type_interfaces() {
}
if (!ClassDB::is_class_exposed(type_cname)) {
if (verbose_output)
WARN_PRINTS("Ignoring type " + type_cname.operator String() + " because it's not exposed");
_log("Ignoring type `%s` because it's not exposed\n", String(type_cname).utf8().get_data());
class_list.pop_front();
continue;
}
if (!ClassDB::is_class_enabled(type_cname)) {
if (verbose_output)
WARN_PRINTS("Ignoring type " + type_cname.operator String() + " because it's not enabled");
_log("Ignoring type `%s` because it's not enabled\n", String(type_cname).utf8().get_data());
class_list.pop_front();
continue;
}
@ -2186,12 +2169,10 @@ void BindingsGenerator::_populate_object_type_interfaces() {
iprop.proxy_name = escape_csharp_keyword(snake_to_pascal_case(iprop.cname));
// Prevent property and enclosing type from sharing the same name
// Prevent the property and its enclosing type from sharing the same name
if (iprop.proxy_name == itype.proxy_name) {
if (verbose_output) {
WARN_PRINTS("Name of property `" + iprop.proxy_name + "` is ambiguous with the name of its class `" +
itype.proxy_name + "`. Renaming property to `" + iprop.proxy_name + "_`");
}
_log("Name of property `%s` is ambiguous with the name of its enclosing class `%s`. Renaming property to `%s_`\n",
iprop.proxy_name.utf8().get_data(), itype.proxy_name.utf8().get_data(), iprop.proxy_name.utf8().get_data());
iprop.proxy_name += "_";
}
@ -2258,14 +2239,13 @@ void BindingsGenerator::_populate_object_type_interfaces() {
// which could actually will return something different.
// Let's put this to notify us if that ever happens.
if (itype.cname != name_cache.type_Object || imethod.name != "free") {
if (verbose_output) {
WARN_PRINTS("Notification: New unexpected virtual non-overridable method found.\n"
ERR_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);
ERR_EXPLAIN("Missing MethodBind for non-virtual method: " + itype.name + "." + imethod.name);
ERR_FAIL();
}
} else if (return_info.type == Variant::INT && return_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
imethod.return_type.cname = return_info.class_name;
@ -2319,12 +2299,10 @@ void BindingsGenerator::_populate_object_type_interfaces() {
imethod.proxy_name = escape_csharp_keyword(snake_to_pascal_case(imethod.name));
// Prevent naming the property and its enclosing type from sharing the same name
// Prevent the method and its enclosing type from sharing the same name
if (imethod.proxy_name == itype.proxy_name) {
if (verbose_output) {
WARN_PRINTS("Name of method `" + imethod.proxy_name + "` is ambiguous with the name of its class `" +
itype.proxy_name + "`. Renaming method to `" + imethod.proxy_name + "_`");
}
_log("Name of method `%s` is ambiguous with the name of its enclosing class `%s`. Renaming method to `%s_`\n",
imethod.proxy_name.utf8().get_data(), itype.proxy_name.utf8().get_data(), imethod.proxy_name.utf8().get_data());
imethod.proxy_name += "_";
}
@ -2858,7 +2836,18 @@ void BindingsGenerator::_populate_global_constants() {
}
}
void BindingsGenerator::initialize() {
void BindingsGenerator::_log(const char *p_format, ...) {
if (log_print_enabled) {
va_list list;
va_start(list, p_format);
OS::get_singleton()->print("%s", str_format(p_format, list).utf8().get_data());
va_end(list);
}
}
void BindingsGenerator::_initialize() {
EditorHelp::generate_doc();
@ -2881,12 +2870,13 @@ void BindingsGenerator::initialize() {
void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args) {
const int NUM_OPTIONS = 2;
int options_left = NUM_OPTIONS;
String mono_glue_option = "--generate-mono-glue";
String cs_api_option = "--generate-cs-api";
verbose_output = true;
String mono_glue_path;
String cs_api_path;
int options_left = NUM_OPTIONS;
const List<String>::Element *elem = p_cmdline_args.front();
@ -2895,8 +2885,7 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args)
const List<String>::Element *path_elem = elem->next();
if (path_elem) {
if (get_singleton()->generate_glue(path_elem->get()) != OK)
ERR_PRINTS(mono_glue_option + ": Failed to generate mono glue");
mono_glue_path = path_elem->get();
elem = elem->next();
} else {
ERR_PRINTS(mono_glue_option + ": No output directory specified");
@ -2907,8 +2896,7 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args)
const List<String>::Element *path_elem = elem->next();
if (path_elem) {
if (get_singleton()->generate_cs_api(path_elem->get()) != OK)
ERR_PRINTS(cs_api_option + ": Failed to generate the C# API");
cs_api_path = path_elem->get();
elem = elem->next();
} else {
ERR_PRINTS(cs_api_option + ": No output directory specified");
@ -2920,10 +2908,23 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args)
elem = elem->next();
}
verbose_output = false;
if (mono_glue_path.length() || cs_api_path.length()) {
BindingsGenerator bindings_generator;
bindings_generator.set_log_print_enabled(true);
if (options_left != NUM_OPTIONS)
if (mono_glue_path.length()) {
if (bindings_generator.generate_glue(mono_glue_path) != OK)
ERR_PRINTS(mono_glue_option + ": Failed to generate mono glue");
}
if (cs_api_path.length()) {
if (bindings_generator.generate_cs_api(cs_api_path) != OK)
ERR_PRINTS(cs_api_option + ": Failed to generate the C# API");
}
// Exit once done
::exit(0);
}
}
#endif

View file

@ -164,7 +164,6 @@ class BindingsGenerator {
}
MethodInterface() {
return_type.cname = BindingsGenerator::get_singleton()->name_cache.type_void;
is_vararg = false;
is_virtual = false;
requires_object_call = false;
@ -469,7 +468,7 @@ class BindingsGenerator {
}
};
static bool verbose_output;
bool log_print_enabled;
OrderedHashMap<StringName, TypeInterface> obj_types;
@ -515,6 +514,7 @@ class BindingsGenerator {
enum_Error = StaticCString::create("Error");
}
private:
NameCache(const NameCache &);
NameCache &operator=(const NameCache &);
};
@ -578,33 +578,26 @@ class BindingsGenerator {
Error _save_file(const String &p_path, const StringBuilder &p_content);
BindingsGenerator() {}
void _log(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
BindingsGenerator(const BindingsGenerator &);
BindingsGenerator &operator=(const BindingsGenerator &);
friend class CSharpLanguage;
static BindingsGenerator *singleton;
void _initialize();
public:
Error generate_cs_core_project(const String &p_solution_dir, DotNetSolution &r_solution, bool p_verbose_output = true);
Error generate_cs_editor_project(const String &p_solution_dir, DotNetSolution &r_solution, bool p_verbose_output = true);
Error generate_cs_api(const String &p_output_dir, bool p_verbose_output = true);
Error generate_cs_core_project(const String &p_solution_dir, DotNetSolution &r_solution);
Error generate_cs_editor_project(const String &p_solution_dir, DotNetSolution &r_solution);
Error generate_cs_api(const String &p_output_dir);
Error generate_glue(const String &p_output_dir);
void set_log_print_enabled(bool p_enabled) { log_print_enabled = p_enabled; }
static uint32_t get_version();
void initialize();
_FORCE_INLINE_ static BindingsGenerator *get_singleton() {
if (!singleton) {
singleton = memnew(BindingsGenerator);
singleton->initialize();
}
return singleton;
}
static void handle_cmdline_args(const List<String> &p_cmdline_args);
BindingsGenerator() :
log_print_enabled(true) {
_initialize();
}
};
#endif

View file

@ -323,10 +323,13 @@ bool GodotSharpBuilds::make_api_assembly(APIAssembly::Type p_api_type) {
String api_sln_file = api_sln_dir.plus_file(API_SOLUTION_NAME ".sln");
if (!DirAccess::exists(api_sln_dir) || !FileAccess::exists(api_sln_file)) {
BindingsGenerator *gen = BindingsGenerator::get_singleton();
bool gen_verbose = OS::get_singleton()->is_stdout_verbose();
BindingsGenerator bindings_generator;
Error err = gen->generate_cs_api(api_sln_dir, gen_verbose);
if (!OS::get_singleton()->is_stdout_verbose()) {
bindings_generator.set_log_print_enabled(false);
}
Error err = bindings_generator.generate_cs_api(api_sln_dir);
if (err != OK) {
show_build_error_dialog("Failed to generate " API_SOLUTION_NAME " solution. Error: " + itos(err));
return false;

View file

@ -37,6 +37,7 @@
#include "core/os/os.h"
#include "../godotsharp_dirs.h"
#include "../utils/string_utils.h"
static int log_level_get_id(const char *p_log_level) {
@ -125,27 +126,6 @@ void GDMonoLog::_delete_old_log_files(const String &p_logs_dir) {
da->list_dir_end();
}
static String format(const char *p_fmt, ...) {
va_list args;
va_start(args, p_fmt);
int len = vsnprintf(NULL, 0, p_fmt, args);
va_end(args);
len += 1; // for the trailing '/0'
char *buffer(memnew_arr(char, len));
va_start(args, p_fmt);
vsnprintf(buffer, len, p_fmt, args);
va_end(args);
String res(buffer);
memdelete_arr(buffer);
return res;
}
void GDMonoLog::initialize() {
CharString log_level = OS::get_singleton()->get_environment("GODOT_MONO_LOG_LEVEL").utf8();
@ -172,7 +152,7 @@ void GDMonoLog::initialize() {
OS::Time time_now = OS::get_singleton()->get_time();
int pid = OS::get_singleton()->get_process_id();
String log_file_name = format("%d_%02d_%02d %02d.%02d.%02d (%d).txt",
String log_file_name = str_format("%d_%02d_%02d %02d.%02d.%02d (%d).txt",
date_now.year, date_now.month, date_now.day,
time_now.hour, time_now.min, time_now.sec, pid);

View file

@ -32,6 +32,8 @@
#include "core/os/file_access.h"
#include <stdio.h>
namespace {
int sfind(const String &p_text, int p_from) {
@ -184,3 +186,50 @@ Error read_all_file_utf8(const String &p_path, String &r_content) {
r_content = source;
return OK;
}
// TODO: Move to variadic templates once we upgrade to C++11
String str_format(const char *p_format, ...) {
va_list list;
va_start(list, p_format);
String res = str_format(p_format, list);
va_end(list);
return res;
}
// va_copy was defined in the C99, but not in C++ standards before C++11.
// When you compile C++ without --std=c++<XX> option, compilers still define
// va_copy, otherwise you have to use the internal version (__va_copy).
#if !defined(va_copy)
#if defined(__GNUC__)
#define va_copy(d, s) __va_copy((d), (s))
#else
#define va_copy(d, s) ((d) = (s))
#endif
#endif
#if defined(MINGW_ENABLED) || defined(_MSC_VER)
#define vsnprintf vsnprintf_s
#endif
String str_format(const char *p_format, va_list p_list) {
va_list list;
va_copy(list, p_list);
int len = vsnprintf(NULL, 0, p_format, list);
va_end(list);
len += 1; // for the trailing '/0'
char *buffer(memnew_arr(char, len));
va_copy(list, p_list);
vsnprintf(buffer, len, p_format, list);
va_end(list);
String res(buffer);
memdelete_arr(buffer);
return res;
}

View file

@ -34,6 +34,8 @@
#include "core/ustring.h"
#include "core/variant.h"
#include <stdarg.h>
String sformat(const String &p_text, const Variant &p1 = Variant(), const Variant &p2 = Variant(), const Variant &p3 = Variant(), const Variant &p4 = Variant(), const Variant &p5 = Variant());
#ifdef TOOLS_ENABLED
@ -44,4 +46,15 @@ String escape_csharp_keyword(const String &p_name);
Error read_all_file_utf8(const String &p_path, String &r_content);
#if defined(__GNUC__)
#define _PRINTF_FORMAT_ATTRIBUTE_1_0 __attribute__((format(printf, 1, 0)))
#define _PRINTF_FORMAT_ATTRIBUTE_1_2 __attribute__((format(printf, 1, 2)))
#else
#define _PRINTF_FORMAT_ATTRIBUTE_1_0
#define _PRINTF_FORMAT_ATTRIBUTE_1_2
#endif
String str_format(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_1_2;
String str_format(const char *p_format, va_list p_list) _PRINTF_FORMAT_ATTRIBUTE_1_0;
#endif // STRING_FORMAT_H