-
-
Notifications
You must be signed in to change notification settings - Fork 35
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #147 from Faless/build/build_profile
[CI] Add patches to use build profile and speed up builds
- Loading branch information
Showing
5 changed files
with
631 additions
and
1 deletion.
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,10 @@ | ||
{ | ||
"enabled_classes": [ | ||
"WebRTCDataChannelExtension", | ||
"WebRTCPeerConnectionExtension", | ||
"WebRTCDataChannelGDNative", | ||
"WebRTCPeerConnectionGDNative", | ||
"NativeScript", | ||
"GDNativeLibrary" | ||
] | ||
} |
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,238 @@ | ||
diff --git a/godot-cpp/SConstruct b/godot-cpp/SConstruct | ||
index 42f8fc0..9d59d1f 100644 | ||
--- a/godot-cpp/SConstruct | ||
+++ b/godot-cpp/SConstruct | ||
@@ -121,6 +121,14 @@ opts.Add( | ||
BoolVariable("generate_bindings", "Force GDExtension API bindings generation. Auto-detected by default.", False) | ||
) | ||
opts.Add(BoolVariable("generate_template_get_node", "Generate a template version of the Node class's get_node.", True)) | ||
+opts.Add( | ||
+ PathVariable( | ||
+ "build_profile", | ||
+ "Path to a file containing a feature build profile", | ||
+ default=env.get("build_profile", None), | ||
+ validator=lambda key, val, env: os.path.isfile(normalize_path(val)), | ||
+ ) | ||
+) | ||
|
||
opts.Add(BoolVariable("build_library", "Build the godot-cpp library.", True)) | ||
opts.Add(EnumVariable("precision", "Set the floating-point precision level", "single", ("single", "double"))) | ||
diff --git a/godot-cpp/binding_generator.py b/godot-cpp/binding_generator.py | ||
index d04c698..41cf29b 100644 | ||
--- a/godot-cpp/binding_generator.py | ||
+++ b/godot-cpp/binding_generator.py | ||
@@ -70,12 +70,14 @@ def generate_wrappers(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" | ||
@@ -104,7 +106,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"]: | ||
@@ -134,14 +136,107 @@ 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): | ||
+def print_file_list(api_filepath, output_dir, headers=False, sources=False, profile_filepath=""): | ||
end = ";" | ||
- for f in get_file_list(api_filepath, output_dir, headers, sources): | ||
+ for f in get_file_list(api_filepath, output_dir, headers, sources, profile_filepath): | ||
print(f, end=end) | ||
|
||
|
||
+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 | ||
@@ -154,11 +249,12 @@ def scons_generate_bindings(target, source, env): | ||
"32" if "32" in env["arch"] else "64", | ||
env["precision"], | ||
env["godot_cpp_gen_dir"], | ||
+ env.get("build_profile", ""), | ||
) | ||
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=".", profile_filepath=""): | ||
api = None | ||
|
||
target_dir = Path(output_dir) / "gen" | ||
@@ -175,7 +271,7 @@ def generate_bindings(api_filepath, use_template_get_node, bits="64", precision= | ||
generate_global_constants(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, profile_filepath) | ||
generate_utility_functions(api, target_dir) | ||
|
||
|
||
@@ -1023,7 +1119,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, profile_filepath=""): | ||
global engine_classes | ||
global singletons | ||
global native_structures | ||
@@ -1161,7 +1257,7 @@ def generate_engine_classes_bindings(api, output_dir, use_template_get_node): | ||
|
||
register_engine_classes_filename = Path(output_dir) / "src" / "register_engine_classes.cpp" | ||
with register_engine_classes_filename.open("w+", encoding="utf-8") as source_file: | ||
- source_file.write(generate_register_engine_classes_source(api)) | ||
+ source_file.write(generate_register_engine_classes_source(api, profile_filepath)) | ||
|
||
for native_struct in api["native_structures"]: | ||
struct_name = native_struct["name"] | ||
@@ -1585,12 +1681,14 @@ def generate_engine_class_source(class_api, used_classes, fully_used_classes, us | ||
return "\n".join(result) | ||
|
||
|
||
-def generate_register_engine_classes_source(api): | ||
+def generate_register_engine_classes_source(api, profile_filepath=""): | ||
includes = [] | ||
registrations = [] | ||
|
||
+ build_profile = parse_build_profile(profile_filepath, api) | ||
+ | ||
for class_api in api["classes"]: | ||
- if class_api["name"] == "ClassDB": | ||
+ if class_api["name"] == "ClassDB" or not is_class_included(class_api["name"], build_profile): | ||
continue | ||
|
||
class_name = class_api["name"] | ||
@@ -2066,6 +2164,20 @@ def is_pod_type(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(type_name): | ||
# Types which we already have implemented. | ||
return is_included_struct_type(type_name) or type_name in ["ObjectID"] |
Oops, something went wrong.