From 081c693fbb2f453fdb545d6e0c3865b0dd8fc1c4 Mon Sep 17 00:00:00 2001 From: Fabio Alessandrelli Date: Fri, 7 Jul 2023 21:49:56 +0200 Subject: [PATCH] Add support for build profiles. Allow enabling or disabling specific classes (which will not be built). --- binding_generator.py | 84 +++++++++++++++++++++++++++++++++++++---- test/build_profile.json | 20 ++++++++++ tools/godotcpp.py | 9 +++++ 3 files changed, 106 insertions(+), 7 deletions(-) create mode 100644 test/build_profile.json diff --git a/binding_generator.py b/binding_generator.py index 27721352..b363178e 100644 --- a/binding_generator.py +++ b/binding_generator.py @@ -1,6 +1,7 @@ #!/usr/bin/env python import json +import os import re import shutil from pathlib import Path @@ -70,7 +71,15 @@ def generate_wrappers(target): f.write(txt) -def get_file_list(api_filepath, output_dir, headers=False, sources=False): +def is_class_included(class_name, build_profile): + if "enabled_classes" in build_profile: + return class_name in build_profile["enabled_classes"] + if "disabled_classes" in build_profile: + return class_name not in build_profile["disabled_classes"] + return True + + +def get_file_list(api_filepath, output_dir, headers=False, sources=False, build_profile={}): api = {} files = [] with open(api_filepath, encoding="utf-8") as api_file: @@ -97,6 +106,8 @@ def get_file_list(api_filepath, output_dir, headers=False, sources=False): files.append(str(source_filename.as_posix())) for engine_class in api["classes"]: + if not is_class_included(engine_class["name"], build_profile): + continue # Generate code for the ClassDB singleton under a different name. if engine_class["name"] == "ClassDB": engine_class["name"] = "ClassDBSingleton" @@ -137,12 +148,64 @@ 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, build_profile={}): + print(*get_file_list(api_filepath, output_dir, headers, sources, build_profile), sep=";", end=None) + + +def scons_parse_build_profile(env, api_filepath): + if not "build_profile" in env: + return {} + profile_filename = env["build_profile"] + if not os.path.isabs(profile_filename): + profile_filename = os.path.join(env.Dir("#").abspath, profile_filename) + print("Using feature build profile: " + profile_filename) + + with open(profile_filename, encoding="utf-8") as profile_file: + profile = json.load(profile_file) + + with open(api_filepath, encoding="utf-8") as api_file: + api = json.load(api_file) + + parents = {} + childs = {} + for engine_class in api["classes"]: + parent = engine_class.get("inherits", "") + child = engine_class["name"] + parents[child] = parent + if parent == "": + continue + if parent not in childs: + childs[parent] = childs.get(parent, []) + childs[parent].append(child) + + included = [] + front = list(profile.get("enabled_classes", [])) + while len(front): + cls = front.pop() + if cls in included: + continue + included.append(cls) + parent = parents.get(cls, "") + if parent: + front.append(parent) + + excluded = [] + front = list(profile.get("disabled_classes", [])) + while len(front): + cls = front.pop() + if cls in excluded: + continue + excluded.append(cls) + front.concat(childs.get(cls, [])) + 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)] + build_profile = scons_parse_build_profile(env, str(source[0])) + files = [env.File(f) for f in get_file_list(str(source[0]), target[0].abspath, True, True, build_profile)] env.Clean(target, files) env["godot_cpp_gen_dir"] = target[0].abspath return files, source @@ -159,7 +222,9 @@ def scons_generate_bindings(target, source, env): return None -def generate_bindings(api_filepath, use_template_get_node, bits="64", precision="single", output_dir="."): +def generate_bindings( + api_filepath, use_template_get_node, bits="64", precision="single", output_dir=".", build_profile={} +): api = None target_dir = Path(output_dir) / "gen" @@ -177,7 +242,7 @@ def generate_bindings(api_filepath, use_template_get_node, bits="64", precision= 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_engine_classes_bindings(api, target_dir, use_template_get_node, build_profile) generate_utility_functions(api, target_dir) @@ -1070,7 +1135,7 @@ def generate_builtin_class_source(builtin_api, size, used_classes, fully_used_cl 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, build_profile={}): global engine_classes global singletons global native_structures @@ -1083,6 +1148,8 @@ def generate_engine_classes_bindings(api, output_dir, use_template_get_node): # First create map of classes and singletons. for class_api in api["classes"]: + if not is_class_included(class_api["name"], build_profile): + continue # Generate code for the ClassDB singleton under a different name. if class_api["name"] == "ClassDB": class_api["name"] = "ClassDBSingleton" @@ -1102,6 +1169,9 @@ def generate_engine_classes_bindings(api, output_dir, use_template_get_node): singletons.append(singleton["name"]) for class_api in api["classes"]: + if not is_class_included(class_api["name"], build_profile): + continue + # Check used classes for header include. used_classes = set() fully_used_classes = set() diff --git a/test/build_profile.json b/test/build_profile.json new file mode 100644 index 00000000..ed883669 --- /dev/null +++ b/test/build_profile.json @@ -0,0 +1,20 @@ +{ + "enabled_classes": [ + "Control", + "Viewport", + "InputEventKey", + "TileMap", + "Label", + "ClassDB", + "Texture2D", + "Material", + "StyleBox", + "SceneTree", + "Mesh", + "Window", + "FileAccess", + "DirAccess", + "Shader", + "Multimesh" + ] +} diff --git a/tools/godotcpp.py b/tools/godotcpp.py index 0b02eea2..74f709a2 100644 --- a/tools/godotcpp.py +++ b/tools/godotcpp.py @@ -198,6 +198,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, + ) + ) + # Add platform options for pl in platforms: tool = Tool(pl, toolpath=["tools"])