Skip to content

Commit

Permalink
Add missing patches
Browse files Browse the repository at this point in the history
Signed-off-by: Alex J Lennon <ajlennon@dynamicdevices.co.uk>
  • Loading branch information
ajlennon committed Jul 20, 2024
1 parent 081a7b0 commit bc369c3
Show file tree
Hide file tree
Showing 5 changed files with 368 additions and 0 deletions.
78 changes: 78 additions & 0 deletions patches/debug_string.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
diff --git a/binding_generator.py b/binding_generator.py
index 49664d1..d125d11 100644
--- a/binding_generator.py
+++ b/binding_generator.py
@@ -407,6 +407,10 @@ def generate_builtin_class_header(builtin_api, size, used_classes, fully_used_cl
result.append(f"class {class_name} {{")
result.append(f"\tstatic constexpr size_t {snake_class_name}_SIZE = {size};")
result.append(f"\tuint8_t opaque[{snake_class_name}_SIZE] = {{}};")
+ if class_name == "String" or class_name == "StringName":
+ result.append("#ifdef DEV_ENABLED")
+ result.append("\tCharString str;")
+ result.append("#endif")

result.append("")
result.append("\tfriend class Variant;")
@@ -885,6 +889,13 @@ def generate_builtin_class_source(builtin_api, size, used_classes, fully_used_cl
method_call += ");"

result.append(method_call)
+ if class_name == "String" or class_name == "StringName" :
+ result.append("#ifdef DEV_ENABLED")
+ if class_name == "StringName":
+ result.append("\tstr = String(*this).utf8();")
+ else:
+ result.append("\tstr = utf8();")
+ result.append("#endif")
result.append("}")
result.append("")

@@ -896,6 +907,13 @@ def generate_builtin_class_source(builtin_api, size, used_classes, fully_used_cl
)
else:
result.append("\tstd::swap(opaque, other.opaque);")
+ if class_name == "String" or class_name == "StringName":
+ result.append("#ifdef DEV_ENABLED")
+ if class_name == "StringName":
+ result.append("\tstr = String(*this).utf8();")
+ else:
+ result.append("\tstr = utf8();")
+ result.append("#endif")
result.append("}")
result.append("")

diff --git a/src/variant/char_string.cpp b/src/variant/char_string.cpp
index 856037c..c21b714 100644
--- a/src/variant/char_string.cpp
+++ b/src/variant/char_string.cpp
@@ -158,18 +158,30 @@ template class CharStringT<wchar_t>;

String::String(const char *from) {
internal::gdextension_interface_string_new_with_latin1_chars(_native_ptr(), from);
+#ifdef DEV_ENABLED
+ str = utf8();
+#endif
}

String::String(const wchar_t *from) {
internal::gdextension_interface_string_new_with_wide_chars(_native_ptr(), from);
+#ifdef DEV_ENABLED
+ str = utf8();
+#endif
}

String::String(const char16_t *from) {
internal::gdextension_interface_string_new_with_utf16_chars(_native_ptr(), from);
+#ifdef DEV_ENABLED
+ str = utf8();
+#endif
}

String::String(const char32_t *from) {
internal::gdextension_interface_string_new_with_utf32_chars(_native_ptr(), from);
+#ifdef DEV_ENABLED
+ str = utf8();
+#endif
}

String String::utf8(const char *from, int len) {
178 changes: 178 additions & 0 deletions patches/get_used_classes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
#!/usr/bin/env python

import os
import re
from pathlib import Path


# This patch does not affect the size of the library, but speeds up the build time.
# Use the argument `exclude_unused_classes=no` to generate all classes
# Use the `folder_to_include_classes=path/` argument to scan classes in a specific directory
is_need_to_exclude_classes = True
sources_folder = ""
found_dependencies = set()
temp_engine_class_names = set()


def setup(_is_exclude: bool, _sources_folder: str):
global is_need_to_exclude_classes
global sources_folder
is_need_to_exclude_classes = _is_exclude
sources_folder = _sources_folder


def extract_used_classes(folder_path: str):
folder_path = Path(folder_path).resolve().as_posix()
godot_cpp_src = Path("src").resolve().as_posix()
godot_cpp_include = Path("include").resolve().as_posix()

print()
print("Performing search for used classes inside these paths:")
print(folder_path)
print(godot_cpp_src)
print(godot_cpp_include)
print()

find_class = re.compile(r"(?:#include [<\"]godot_cpp\/classes\/)(.*)(?:.hpp[>\"])", re.MULTILINE)

found_classes = set()

def scan(scan_path):
skips = 0
for dir, subdirs, files in os.walk(scan_path):
for f in files:
if not f.endswith((".cpp", ".cxx", ".c++", ".c", ".cc", ".inc", ".hpp", ".hxx", ".h", ".hh")):
continue

def read_data(opened_file):
data = file.read()

matches = find_class.finditer(data)
for matchNum, match in enumerate(matches, start=1):
found_classes.add(match.group(1))

try:
with open(Path(dir) / f, "r") as file:
read_data(file)
except UnicodeDecodeError:
try:
with open(Path(dir) / f, "r", encoding="utf-8") as file:
read_data(file)
except UnicodeDecodeError as e:
print(
"Skipping file due to 'UnicodeDecodeError' exception: "
+ (Path(dir) / f).resolve().as_posix()
+ "\nException: "
+ str(e)
)
skips += 1
continue
return skips

skips = 0
skips += scan(godot_cpp_src)
skips += scan(godot_cpp_include)
skips += scan(folder_path)

if skips > 0:
print()

# generate array of class names
return ["".join([w.title() for w in c.split("_")]) for c in found_classes]


def scan_dependencies(api):
api = dict(api)
if not is_need_to_exclude_classes:
return

used_classes = extract_used_classes(sources_folder)

for class_api in api["classes"]:
# It will change the actual value inside the `api`!!!
#
# ClassDB Singleton is renamed in godot-cpp.
# This class cannot appear as an argument or return value, so no other renaming is required yet.
if class_api["name"] == "ClassDB":
class_api["name"] = "ClassDBSingleton"

temp_engine_class_names.add(class_api["name"])

for name in used_classes:
_get_dependencies(api, name)

print("Provided", len(used_classes), "explicit classes:", str(sorted(used_classes)))
print()
print("A total of", len(found_dependencies), "classes were found:", str(sorted(found_dependencies)))
print()

temp_engine_class_names.clear()


def _get_dependencies(api, name):
def _add_dependency_engine_class(_class, _to_scan):
for start in ["enum::", "typedarray::", "bitfield::"]:
if _class.startswith(start):
_class = _class[len(start) :].partition(".")[0]
break

if _class in temp_engine_class_names:
_to_scan.add(_class)

for class_api in api["classes"]:
if not (class_api["name"] == name or class_api["name"].lower() == name.lower()):
continue

if class_api["name"] == "Object":
break

need_to_scan = set()
need_to_scan.add(class_api["name"])
need_to_scan.add(class_api["inherits"])

for method in class_api.get("methods", []):
if "return_value" in method:
_add_dependency_engine_class(method["return_value"]["type"], need_to_scan)

for arg in method.get("arguments", []):
_add_dependency_engine_class(arg["type"], need_to_scan)

for c in need_to_scan:
if c not in found_dependencies:
found_dependencies.add(c)
_get_dependencies(api, c)
break


# from binding_generator.py
def camel_to_snake(name):
name = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", name)
name = re.sub("([a-z0-9])([A-Z])", r"\1_\2", name)
return name.replace("2_D", "2D").replace("3_D", "3D").lower()


def need_to_exclude(name):
return len(found_dependencies) > 0 and name not in found_dependencies


def delete_useless(files):
if len(found_dependencies) == 0:
return files

# convert found class names to file names
deps_file_names = [camel_to_snake(c) + ".cpp" for c in found_dependencies]

src_path = "gen/src/classes/"
print("These", len(deps_file_names), "files from the", src_path, "directory will be compiled:", deps_file_names)
print()

new_files_list = []
for f in files:
split = f.split(src_path)
if len(split) > 1:
if split[1] in deps_file_names:
new_files_list.append(f)
else:
new_files_list.append(f)

return new_files_list
55 changes: 55 additions & 0 deletions patches/godot_cpp_exclude_unused_classes.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
diff --git a/binding_generator.py b/binding_generator.py
index 7911a7e..58188f7 100644
--- a/binding_generator.py
+++ b/binding_generator.py
@@ -5,6 +5,11 @@ import re
import shutil
from pathlib import Path

+import sys
+
+sys.path.insert(0, "../patches")
+import get_used_classes
+

def generate_mod_version(argcount, const=False, returns=False):
s = """
@@ -76,6 +81,7 @@ def get_file_list(api_filepath, output_dir, headers=False, sources=False, profil
with open(api_filepath, encoding="utf-8") as api_file:
api = json.load(api_file)

+ get_used_classes.scan_dependencies(api)
build_profile = parse_build_profile(profile_filepath, api)

core_gen_folder = Path(output_dir) / "gen" / "include" / "godot_cpp" / "core"
@@ -133,6 +139,7 @@ def get_file_list(api_filepath, output_dir, headers=False, sources=False, profil
utility_functions_source_path = source_gen_folder / "variant" / "utility_functions.cpp"
files.append(str(utility_functions_source_path.as_posix()))

+ files = get_used_classes.delete_useless(files)
return files


@@ -230,6 +237,8 @@ def parse_build_profile(profile_filepath, api):


def scons_emit_files(target, source, env):
+ get_used_classes.setup(env.get("exclude_unused_classes", True), env.get("folder_to_include_classes", "../src"))
+
profile_filepath = env.get("build_profile", "")
if profile_filepath and not Path(profile_filepath).is_absolute():
profile_filepath = str((Path(env.Dir("#").abspath) / profile_filepath).as_posix())
diff --git a/tools/godotcpp.py b/tools/godotcpp.py
index cc2b02f..d138052 100644
--- a/tools/godotcpp.py
+++ b/tools/godotcpp.py
@@ -322,6 +322,9 @@ def options(opts, env):
opts.Add(BoolVariable("dev_build", "Developer build with dev-only debugging code (DEV_ENABLED)", False))
opts.Add(BoolVariable("verbose", "Enable verbose output for the compilation", False))

+ opts.Add(BoolVariable("exclude_unused_classes", "Disable generation of unused classes.", True))
+ opts.Add(PathVariable("folder_to_include_classes", "Path to the directory containing extension sources", "../src", PathVariable.PathIsDir))
+
# Add platform options (custom tools can override platforms)
for pl in sorted(set(platforms + custom_platforms)):
tool = Tool(pl, toolpath=get_platform_tools_paths(env))
24 changes: 24 additions & 0 deletions patches/unity_build.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
diff --git a/tools/godotcpp.py b/tools/godotcpp.py
index cc2b02f..b0d3017 100644
--- a/tools/godotcpp.py
+++ b/tools/godotcpp.py
@@ -12,6 +12,9 @@ from SCons.Variables.BoolVariable import _text2bool

from binding_generator import scons_emit_files, scons_generate_bindings

+sys.path.insert(0, "../patches")
+import unity_tools
+

def add_sources(sources, dir, extension):
for f in os.listdir(dir):
@@ -468,6 +471,9 @@ def _godot_cpp(env):
"binding_generator.py",
],
)
+
+ bindings = unity_tools.generate_unity_build(bindings, "godot-cpp_")
+
# Forces bindings regeneration.
if env["generate_bindings"]:
env.AlwaysBuild(bindings)
33 changes: 33 additions & 0 deletions patches/unity_tools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/usr/bin/env python

import os
import re
from pathlib import Path

unity_batch_size = 32


def generate_unity_build(src, prefix="", gen_folder="obj", batch_size=unity_batch_size):
if os.environ.get("FORCE_DISABLE_UNITY", "").lower() not in ["", "no", "false", "n"]:
return src

from pathlib import Path
import math

print("Generating source files for unity build:")
res = [Path(str(f)).absolute().as_posix() for f in src if str(f).endswith(".cpp")]
unity_dir = Path(gen_folder)
unity_dir.mkdir(parents=True, exist_ok=True)
unity_files = []
for i in range(math.ceil(len(res) / batch_size)):
u_path = unity_dir / (prefix + ("unity_%d.cpp" % i))
print(u_path)
unity_files.append(u_path.as_posix())
with u_path.open("w+") as unity_file:
unity_file.write(
"\n".join(["/* generated by Scons */\n"] + ['#include "%s"\n' % f for f in res[:batch_size]])
)
res = res[batch_size:]

print()
return [f for f in src if not str(f).endswith(".cpp")] + [Path(f).absolute().as_posix() for f in unity_files]

0 comments on commit bc369c3

Please sign in to comment.