-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use build_profile to improve build times
- Loading branch information
Showing
4 changed files
with
361 additions
and
6 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
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,63 @@ | ||
{ | ||
"enabled_classes": [ | ||
"ArrayMesh", | ||
"BaseMaterial3D", | ||
"BoxMesh", | ||
"BoxShape3D", | ||
"CollisionShape3D", | ||
"ConcavePolygonShape3D", | ||
"Curve", | ||
"EditorExportPlatform", | ||
"EditorExportPlatformAndroid", | ||
"EditorExportPlugin", | ||
"EditorPlugin", | ||
"Engine", | ||
"FileAccess", | ||
"GLTFDocument", | ||
"GLTFState", | ||
"GeometryInstance3D", | ||
"Gradient", | ||
"Image", | ||
"MainLoop", | ||
"Material", | ||
"Mesh", | ||
"MeshConvexDecompositionSettings", | ||
"MeshInstance3D", | ||
"Node", | ||
"Node3D", | ||
"OS", | ||
"Object", | ||
"OpenXRAPIExtension", | ||
"OpenXRExtensionWrapperExtension", | ||
"OpenXRInterface", | ||
"PackedScene", | ||
"PlaneMesh", | ||
"PrimitiveMesh", | ||
"ProjectSettings", | ||
"RefCounted", | ||
"Resource", | ||
"SceneTree", | ||
"Shader", | ||
"ShaderMaterial", | ||
"Shape3D", | ||
"Shortcut", | ||
"Skeleton3D", | ||
"SkeletonModifier3D", | ||
"StandardMaterial3D", | ||
"SurfaceTool", | ||
"Viewport", | ||
"VisualInstance3D", | ||
"XRAnchor3D", | ||
"XRBodyTracker", | ||
"XRFaceTracker", | ||
"XRHandModifier3D", | ||
"XRHandTracker", | ||
"XRInterface", | ||
"XRNode3D", | ||
"XROrigin3D", | ||
"XRPose", | ||
"XRPositionalTracker", | ||
"XRServer", | ||
"XRTracker" | ||
] | ||
} |
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,194 @@ | ||
diff --git a/binding_generator.py b/binding_generator.py | ||
index 67a7d5f06..ed8b18d58 100644 | ||
--- a/binding_generator.py | ||
+++ b/binding_generator.py | ||
@@ -197,12 +197,14 @@ def generate_virtuals(target): | ||
f.write(txt) | ||
|
||
|
||
-def get_file_list(api_filepath, output_dir, headers=False, sources=False): | ||
+def get_file_list(api_filepath, output_dir, headers=False, sources=False, profile_filepath=""): | ||
api = {} | ||
files = [] | ||
with open(api_filepath, encoding="utf-8") as api_file: | ||
api = json.load(api_file) | ||
|
||
+ build_profile = parse_build_profile(profile_filepath, api) | ||
+ | ||
core_gen_folder = Path(output_dir) / "gen" / "include" / "godot_cpp" / "core" | ||
include_gen_folder = Path(output_dir) / "gen" / "include" / "godot_cpp" | ||
source_gen_folder = Path(output_dir) / "gen" / "src" | ||
@@ -233,7 +235,7 @@ def get_file_list(api_filepath, output_dir, headers=False, sources=False): | ||
source_filename = source_gen_folder / "classes" / (camel_to_snake(engine_class["name"]) + ".cpp") | ||
if headers: | ||
files.append(str(header_filename.as_posix())) | ||
- if sources: | ||
+ if sources and is_class_included(engine_class["name"], build_profile): | ||
files.append(str(source_filename.as_posix())) | ||
|
||
for native_struct in api["native_structures"]: | ||
@@ -265,12 +267,105 @@ def get_file_list(api_filepath, output_dir, headers=False, sources=False): | ||
return files | ||
|
||
|
||
-def print_file_list(api_filepath, output_dir, headers=False, sources=False): | ||
- print(*get_file_list(api_filepath, output_dir, headers, sources), sep=";", end=None) | ||
+def print_file_list(api_filepath, output_dir, headers=False, sources=False, profile_filepath=""): | ||
+ print(*get_file_list(api_filepath, output_dir, headers, sources, profile_filepath), sep=";", end=None) | ||
+ | ||
+ | ||
+def parse_build_profile(profile_filepath, api): | ||
+ if profile_filepath == "": | ||
+ return {} | ||
+ print("Using feature build profile: " + profile_filepath) | ||
+ | ||
+ with open(profile_filepath, encoding="utf-8") as profile_file: | ||
+ profile = json.load(profile_file) | ||
+ | ||
+ api_dict = {} | ||
+ parents = {} | ||
+ children = {} | ||
+ for engine_class in api["classes"]: | ||
+ api_dict[engine_class["name"]] = engine_class | ||
+ parent = engine_class.get("inherits", "") | ||
+ child = engine_class["name"] | ||
+ parents[child] = parent | ||
+ if parent == "": | ||
+ continue | ||
+ children[parent] = children.get(parent, []) | ||
+ children[parent].append(child) | ||
+ | ||
+ # Parse methods dependencies | ||
+ deps = {} | ||
+ reverse_deps = {} | ||
+ for name, engine_class in api_dict.items(): | ||
+ ref_cls = set() | ||
+ for method in engine_class.get("methods", []): | ||
+ rtype = method.get("return_value", {}).get("type", "") | ||
+ args = [a["type"] for a in method.get("arguments", [])] | ||
+ if rtype in api_dict: | ||
+ ref_cls.add(rtype) | ||
+ elif is_enum(rtype) and get_enum_class(rtype) in api_dict: | ||
+ ref_cls.add(get_enum_class(rtype)) | ||
+ for arg in args: | ||
+ if arg in api_dict: | ||
+ ref_cls.add(arg) | ||
+ elif is_enum(arg) and get_enum_class(arg) in api_dict: | ||
+ ref_cls.add(get_enum_class(arg)) | ||
+ deps[engine_class["name"]] = set(filter(lambda x: x != name, ref_cls)) | ||
+ for acls in ref_cls: | ||
+ if acls == name: | ||
+ continue | ||
+ reverse_deps[acls] = reverse_deps.get(acls, set()) | ||
+ reverse_deps[acls].add(name) | ||
+ | ||
+ included = [] | ||
+ front = list(profile.get("enabled_classes", [])) | ||
+ if front: | ||
+ # These must always be included | ||
+ front.append("WorkerThreadPool") | ||
+ front.append("ClassDB") | ||
+ front.append("ClassDBSingleton") | ||
+ while front: | ||
+ cls = front.pop() | ||
+ if cls in included: | ||
+ continue | ||
+ included.append(cls) | ||
+ parent = parents.get(cls, "") | ||
+ if parent: | ||
+ front.append(parent) | ||
+ for rcls in deps.get(cls, set()): | ||
+ if rcls in included or rcls in front: | ||
+ continue | ||
+ front.append(rcls) | ||
+ | ||
+ excluded = [] | ||
+ front = list(profile.get("disabled_classes", [])) | ||
+ while front: | ||
+ cls = front.pop() | ||
+ if cls in excluded: | ||
+ continue | ||
+ excluded.append(cls) | ||
+ front += children.get(cls, []) | ||
+ for rcls in reverse_deps.get(cls, set()): | ||
+ if rcls in excluded or rcls in front: | ||
+ continue | ||
+ front.append(rcls) | ||
+ | ||
+ if included and excluded: | ||
+ print( | ||
+ "WARNING: Cannot specify both 'enabled_classes' and 'disabled_classes' in build profile. 'disabled_classes' will be ignored." | ||
+ ) | ||
+ | ||
+ return { | ||
+ "enabled_classes": included, | ||
+ "disabled_classes": excluded, | ||
+ } | ||
|
||
|
||
def scons_emit_files(target, source, env): | ||
- files = [env.File(f) for f in get_file_list(str(source[0]), target[0].abspath, True, True)] | ||
+ 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()) | ||
+ | ||
+ files = [env.File(f) for f in get_file_list(str(source[0]), target[0].abspath, True, True, profile_filepath)] | ||
env.Clean(target, files) | ||
env["godot_cpp_gen_dir"] = target[0].abspath | ||
return files, source | ||
@@ -2547,6 +2642,20 @@ def is_refcounted(type_name): | ||
return type_name in engine_classes and engine_classes[type_name] | ||
|
||
|
||
+def is_class_included(class_name, build_profile): | ||
+ """ | ||
+ Check if an engine class should be included. | ||
+ This removes classes according to a build profile of enabled or disabled classes. | ||
+ """ | ||
+ included = build_profile.get("enabled_classes", []) | ||
+ excluded = build_profile.get("disabled_classes", []) | ||
+ if included: | ||
+ return class_name in included | ||
+ if excluded: | ||
+ return class_name not in excluded | ||
+ return True | ||
+ | ||
+ | ||
def is_included(type_name, current_type): | ||
""" | ||
Check if a builtin type should be included. | ||
diff --git a/test/build_profile.json b/test/build_profile.json | ||
new file mode 100644 | ||
index 000000000..3587651c1 | ||
--- /dev/null | ||
+++ b/test/build_profile.json | ||
@@ -0,0 +1,9 @@ | ||
+{ | ||
+ "enabled_classes": [ | ||
+ "Control", | ||
+ "Label", | ||
+ "OS", | ||
+ "TileMap", | ||
+ "InputEventKey" | ||
+ ] | ||
+} | ||
diff --git a/tools/godotcpp.py b/tools/godotcpp.py | ||
index d5285dc1d..c4165c960 100644 | ||
--- a/tools/godotcpp.py | ||
+++ b/tools/godotcpp.py | ||
@@ -285,6 +285,15 @@ def options(opts, env): | ||
) | ||
) | ||
|
||
+ opts.Add( | ||
+ PathVariable( | ||
+ "build_profile", | ||
+ "Path to a file containing a feature build profile", | ||
+ default=env.get("build_profile", None), | ||
+ validator=validate_file, | ||
+ ) | ||
+ ) | ||
+ | ||
opts.Add( | ||
BoolVariable( | ||
key="use_hot_reload", |
93 changes: 93 additions & 0 deletions
93
thirdparty/godot_cpp_build_profile/create_build_profile.py
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,93 @@ | ||
#!/usr/bin/env python | ||
|
||
import os | ||
import re | ||
import json | ||
|
||
SOURCE_DIRS = [ | ||
'../../common', | ||
'../../godotopenxrkhronos', | ||
'../../godotopenxrlynx', | ||
'../../godotopenxrmeta', | ||
'../../godotopenxrpico', | ||
] | ||
|
||
GODOT_CPP_HEADERS = "../godot-cpp/gen/include/godot_cpp/classes" | ||
#GODOT_CPP_SOURCES = "../godot-cpp/gen/src/classes" | ||
|
||
INCLUDE_PATTERN = re.compile(r'#include <godot_cpp/classes/(\w+)\.hpp>') | ||
CLASS_PATTERN = re.compile(r'class\s+(\w+)\s*:\s*public') | ||
|
||
processed_files = {} | ||
class_list = [] | ||
|
||
def find_includes(file_path): | ||
""" | ||
Finds all the includes in a given file and returns the list of CLASS names. | ||
""" | ||
includes = [] | ||
try: | ||
with open(file_path, 'r') as file: | ||
content = file.read() | ||
includes = INCLUDE_PATTERN.findall(content) | ||
except FileNotFoundError: | ||
print(f"File not found: {file_path}") | ||
return includes | ||
|
||
def find_primary_class(file_path): | ||
""" | ||
Finds the primary class name in a given .hpp file by looking for 'class ClassName : public'. | ||
""" | ||
try: | ||
with open(file_path, 'r') as file: | ||
for line in file: | ||
match = CLASS_PATTERN.search(line) | ||
if match: | ||
return match.group(1) | ||
except FileNotFoundError: | ||
print(f"File not found: {file_path}") | ||
return None | ||
|
||
def process_file(file_path, top_level=False): | ||
""" | ||
Processes a given file for includes and processes the included files recursively. | ||
""" | ||
if file_path in processed_files: | ||
return | ||
|
||
processed_files[file_path] = True | ||
|
||
if not top_level and file_path.endswith('.hpp'): | ||
primary_class = find_primary_class(file_path) | ||
if primary_class and primary_class not in class_list: | ||
class_list.append(primary_class) | ||
|
||
includes = find_includes(file_path) | ||
for include_name in includes: | ||
hpp_file = f"{GODOT_CPP_HEADERS}/{include_name}.hpp" | ||
#cpp_file = f"{GODOT_CPP_SOURCES}/{include_name}.cpp" | ||
|
||
if os.path.exists(hpp_file): | ||
process_file(hpp_file) | ||
#if os.path.exists(cpp_file): | ||
# process_file(cpp_file) | ||
|
||
def main(): | ||
for dir in SOURCE_DIRS: | ||
for root, _, files in os.walk(dir): | ||
for file in files: | ||
if file.endswith('.cpp') or file.endswith('.h'): | ||
file_path = os.path.join(root, file) | ||
process_file(file_path, True) | ||
|
||
class_list.sort() | ||
|
||
build_profile = { | ||
"enabled_classes": class_list | ||
} | ||
|
||
with open("build_profile.json", "wt") as file: | ||
json.dump(build_profile, file, indent=4) | ||
|
||
if __name__ == "__main__": | ||
main() |