forked from goatchurchprime/two-voip-godot-4
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Alex J Lennon <ajlennon@dynamicdevices.co.uk>
- Loading branch information
Showing
5 changed files
with
368 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) { |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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] |