diff --git a/binding_generator.py b/binding_generator.py index 1a401a3b8..6c60e3694 100644 --- a/binding_generator.py +++ b/binding_generator.py @@ -293,7 +293,14 @@ def generate_bindings( def _generate_bindings( - api, api_filepath, interface_filepath, use_template_get_node, bits="64", precision="single", output_dir="." + api, + api_filepath, + interface_filepath, + use_template_get_node, + bits="64", + precision="single", + output_dir=".", + hooks=None, ): if "precision" in api["header"] and precision != api["header"]["precision"]: raise Exception( @@ -318,12 +325,12 @@ def _generate_bindings( generate_gdextension_interface_loader(interface_filepath, target_dir) - generate_global_constants(api, target_dir) + generate_global_constants(api, target_dir, hooks) generate_version_header(api, target_dir) generate_global_constant_binds(api, target_dir) - generate_builtin_bindings(api, target_dir, real_t + "_" + bits) - generate_engine_classes_bindings(api, target_dir, use_template_get_node) - generate_utility_functions(api, target_dir) + generate_builtin_bindings(api, target_dir, real_t + "_" + bits, hooks) + generate_engine_classes_bindings(api, target_dir, use_template_get_node, hooks) + generate_utility_functions(api, target_dir, hooks) def generate_gdextension_interface_loader(interface_filepath, output_dir): @@ -502,7 +509,7 @@ def generate_gdextension_interface_loader_source(data): singletons = [] -def generate_builtin_bindings(api, output_dir, build_config): +def generate_builtin_bindings(api, output_dir, build_config, hooks=None): global builtin_classes core_gen_folder = Path(output_dir) / "include" / "godot_cpp" / "core" @@ -606,10 +613,10 @@ def generate_builtin_bindings(api, output_dir, build_config): fully_used_classes.sort() with header_filename.open("w+", encoding="utf-8") as header_file: - header_file.write(generate_builtin_class_header(builtin_api, size, used_classes, fully_used_classes)) + header_file.write(generate_builtin_class_header(builtin_api, size, used_classes, fully_used_classes, hooks)) with source_filename.open("w+", encoding="utf-8") as source_file: - source_file.write(generate_builtin_class_source(builtin_api, size, used_classes, fully_used_classes)) + source_file.write(generate_builtin_class_source(builtin_api, size, used_classes, fully_used_classes, hooks)) # Create a header with all builtin types for convenience. builtin_header_filename = include_gen_folder / "builtin_types.hpp" @@ -685,7 +692,7 @@ def generate_builtin_class_vararg_method_implements_header(builtin_classes): return "\n".join(result) -def generate_builtin_class_header(builtin_api, size, used_classes, fully_used_classes): +def generate_builtin_class_header(builtin_api, size, used_classes, fully_used_classes, hooks=None): result = [] class_name = builtin_api["name"] @@ -1175,10 +1182,13 @@ def generate_builtin_class_header(builtin_api, size, used_classes, fully_used_cl result.append("") + if hooks: + result = hooks.alter_builtin_class_header(builtin_api, result) + return "\n".join(result) -def generate_builtin_class_source(builtin_api, size, used_classes, fully_used_classes): +def generate_builtin_class_source(builtin_api, size, used_classes, fully_used_classes, hooks=None): result = [] class_name = builtin_api["name"] @@ -1490,10 +1500,13 @@ def generate_builtin_class_source(builtin_api, size, used_classes, fully_used_cl result.append("} //namespace godot") result.append("") + if hooks: + result = hooks.alter_builtin_class_source(builtin_api, result) + return "\n".join(result) -def generate_engine_classes_bindings(api, output_dir, use_template_get_node): +def generate_engine_classes_bindings(api, output_dir, use_template_get_node, hooks=None): global engine_classes global singletons global native_structures @@ -1685,12 +1698,12 @@ def generate_engine_classes_bindings(api, output_dir, use_template_get_node): with header_filename.open("w+", encoding="utf-8") as header_file: header_file.write( - generate_engine_class_header(class_api, used_classes, fully_used_classes, use_template_get_node) + generate_engine_class_header(class_api, used_classes, fully_used_classes, use_template_get_node, hooks) ) with source_filename.open("w+", encoding="utf-8") as source_file: source_file.write( - generate_engine_class_source(class_api, used_classes, fully_used_classes, use_template_get_node) + generate_engine_class_source(class_api, used_classes, fully_used_classes, use_template_get_node, hooks) ) for native_struct in api["native_structures"]: @@ -1749,7 +1762,7 @@ def generate_engine_classes_bindings(api, output_dir, use_template_get_node): header_file.write("\n".join(result)) -def generate_engine_class_header(class_api, used_classes, fully_used_classes, use_template_get_node): +def generate_engine_class_header(class_api, used_classes, fully_used_classes, use_template_get_node, hooks): global singletons result = [] @@ -2068,10 +2081,13 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us result.append("") + if hooks: + result = hooks.alter_engine_class_header(class_api, result) + return "\n".join(result) -def generate_engine_class_source(class_api, used_classes, fully_used_classes, use_template_get_node): +def generate_engine_class_source(class_api, used_classes, fully_used_classes, use_template_get_node, hooks=None): global singletons result = [] @@ -2246,10 +2262,13 @@ def generate_engine_class_source(class_api, used_classes, fully_used_classes, us result.append("} // namespace godot") result.append("") + if hooks: + result = hooks.alter_engine_class_source(class_api, result) + return "\n".join(result) -def generate_global_constants(api, output_dir): +def generate_global_constants(api, output_dir, hooks=None): include_gen_folder = Path(output_dir) / "include" / "godot_cpp" / "classes" source_gen_folder = Path(output_dir) / "src" / "classes" @@ -2310,6 +2329,9 @@ def generate_global_constants(api, output_dir): header.append("") + if hooks: + header = hooks.alter_global_constants(api, header) + with header_filename.open("w+", encoding="utf-8") as header_file: header_file.write("\n".join(header)) @@ -2376,7 +2398,7 @@ def generate_global_constant_binds(api, output_dir): header_file.write("\n".join(header)) -def generate_utility_functions(api, output_dir): +def generate_utility_functions(api, output_dir, hooks=None): include_gen_folder = Path(output_dir) / "include" / "godot_cpp" / "variant" source_gen_folder = Path(output_dir) / "src" / "variant" @@ -2430,6 +2452,9 @@ def generate_utility_functions(api, output_dir): header.append("} // namespace godot") header.append("") + if hooks: + header = hooks.alter_utility_functions_header(api, header) + with header_filename.open("w+", encoding="utf-8") as header_file: header_file.write("\n".join(header)) @@ -2509,6 +2534,10 @@ def generate_utility_functions(api, output_dir): source.append("") source.append("} // namespace godot") + source.append("") + + if hooks: + header = hooks.alter_utility_functions_source(api, source) with source_filename.open("w+", encoding="utf-8") as source_file: source_file.write("\n".join(source)) diff --git a/binding_generator_hooks.py b/binding_generator_hooks.py new file mode 100644 index 000000000..3483057cc --- /dev/null +++ b/binding_generator_hooks.py @@ -0,0 +1,23 @@ +class BindingGeneratorHooks: + def alter_engine_class_header(self, class_api, lines): + return lines + + def alter_engine_class_source(self, class_api, lines): + return lines + + def alter_global_constants(self, api, lines): + return lines + + def alter_utility_functions_header(self, api, lines): + return lines + + def alter_utility_functions_source(self, api, lines): + return lines + + def alter_builtin_class_header(self, builtin_api, lines): + return lines + + def alter_builtin_class_source(self, builtin_api, lines): + return lines + + # and more... diff --git a/custom_generator.py b/custom_generator.py new file mode 100644 index 000000000..e2e57ea83 --- /dev/null +++ b/custom_generator.py @@ -0,0 +1,19 @@ +from binding_generator_hooks import BindingGeneratorHooks + + +class BindingGeneratorHooksExtension(BindingGeneratorHooks): + def alter_engine_class_header(self, class_api, lines): + signals = [] + if "signals" in class_api: + for signal_api in class_api["signals"]: + name = signal_api["name"] + signal_constant = "\tstatic constexpr char SIGNAL_" + name.upper() + '[] = "' + name + '";' + signals.append(signal_constant) + try: + idx = lines.index("public:") + 1 + for signal_const in signals: + lines.insert(idx, signal_const) + idx += 1 + except ValueError: + print("no public keyword found, not adding signals") + return lines diff --git a/tools/godotcpp.py b/tools/godotcpp.py index 456c52d0c..f2e21df37 100644 --- a/tools/godotcpp.py +++ b/tools/godotcpp.py @@ -1,3 +1,4 @@ +import importlib.util import os import platform import sys @@ -13,6 +14,7 @@ from SCons.Variables.BoolVariable import _text2bool from binding_generator import _generate_bindings, _get_file_list, get_file_list +from binding_generator_hooks import BindingGeneratorHooks from build_profile import generate_trimmed_api from doc_source_generator import scons_generate_doc_source @@ -164,6 +166,17 @@ def scons_generate_bindings(target, source, env): api = generate_trimmed_api(str(source[0]), profile_filepath) + custom_hooks: BindingGeneratorHooks = None + if "binding_hook_file" in env: + binding_hook_file = env["binding_hook_file"] + # yes apparently this is how you import a file dynamically ._. + spec = importlib.util.spec_from_file_location("custom_binding_generator", binding_hook_file) + loaded_module = importlib.util.module_from_spec(spec) + sys.modules["custom_binding_generator"] = loaded_module + spec.loader.exec_module(loaded_module) + # assume the name 'BindingGeneratorHooksExtension' for the class + custom_hooks = loaded_module.BindingGeneratorHooksExtension() + _generate_bindings( api, str(source[0]), @@ -172,6 +185,7 @@ def scons_generate_bindings(target, source, env): "32" if "32" in env["arch"] else "64", env["precision"], env["godot_cpp_gen_dir"], + custom_hooks, ) return None @@ -230,6 +244,15 @@ def options(opts, env): else: raise ValueError("Could not detect platform automatically, please specify with platform=") + opts.Add( + PathVariable( + key="binding_hook_file", + help="Path to python file containing custom binding generator hooks", + default=env.get("binding_hook_file", None), + validator=validate_file, + ) + ) + opts.Add( PathVariable( key="custom_tools",