Skip to content

Commit b79f1f0

Browse files
committed
Add support for build profiles.
Allow enabling or disabling specific classes (which will not be built).
1 parent 8cdd56e commit b79f1f0

File tree

3 files changed

+129
-5
lines changed

3 files changed

+129
-5
lines changed

binding_generator.py

+114-5
Original file line numberDiff line numberDiff line change
@@ -197,12 +197,14 @@ def generate_virtuals(target):
197197
f.write(txt)
198198

199199

200-
def get_file_list(api_filepath, output_dir, headers=False, sources=False):
200+
def get_file_list(api_filepath, output_dir, headers=False, sources=False, profile_filepath=""):
201201
api = {}
202202
files = []
203203
with open(api_filepath, encoding="utf-8") as api_file:
204204
api = json.load(api_file)
205205

206+
build_profile = parse_build_profile(profile_filepath, api)
207+
206208
core_gen_folder = Path(output_dir) / "gen" / "include" / "godot_cpp" / "core"
207209
include_gen_folder = Path(output_dir) / "gen" / "include" / "godot_cpp"
208210
source_gen_folder = Path(output_dir) / "gen" / "src"
@@ -233,7 +235,7 @@ def get_file_list(api_filepath, output_dir, headers=False, sources=False):
233235
source_filename = source_gen_folder / "classes" / (camel_to_snake(engine_class["name"]) + ".cpp")
234236
if headers:
235237
files.append(str(header_filename.as_posix()))
236-
if sources:
238+
if sources and is_class_included(engine_class["name"], build_profile):
237239
files.append(str(source_filename.as_posix()))
238240

239241
for native_struct in api["native_structures"]:
@@ -265,12 +267,105 @@ def get_file_list(api_filepath, output_dir, headers=False, sources=False):
265267
return files
266268

267269

268-
def print_file_list(api_filepath, output_dir, headers=False, sources=False):
269-
print(*get_file_list(api_filepath, output_dir, headers, sources), sep=";", end=None)
270+
def print_file_list(api_filepath, output_dir, headers=False, sources=False, profile_filepath=""):
271+
print(*get_file_list(api_filepath, output_dir, headers, sources, profile_filepath), sep=";", end=None)
272+
273+
274+
def parse_build_profile(profile_filepath, api):
275+
if profile_filepath == "":
276+
return {}
277+
print("Using feature build profile: " + profile_filepath)
278+
279+
with open(profile_filepath, encoding="utf-8") as profile_file:
280+
profile = json.load(profile_file)
281+
282+
api_dict = {}
283+
parents = {}
284+
children = {}
285+
for engine_class in api["classes"]:
286+
api_dict[engine_class["name"]] = engine_class
287+
parent = engine_class.get("inherits", "")
288+
child = engine_class["name"]
289+
parents[child] = parent
290+
if parent == "":
291+
continue
292+
children[parent] = children.get(parent, [])
293+
children[parent].append(child)
294+
295+
# Parse methods dependencies
296+
deps = {}
297+
reverse_deps = {}
298+
for name, engine_class in api_dict.items():
299+
ref_cls = set()
300+
for method in engine_class.get("methods", []):
301+
rtype = method.get("return_value", {}).get("type", "")
302+
args = [a["type"] for a in method.get("arguments", [])]
303+
if rtype in api_dict:
304+
ref_cls.add(rtype)
305+
elif is_enum(rtype) and get_enum_class(rtype) in api_dict:
306+
ref_cls.add(get_enum_class(rtype))
307+
for arg in args:
308+
if arg in api_dict:
309+
ref_cls.add(arg)
310+
elif is_enum(arg) and get_enum_class(arg) in api_dict:
311+
ref_cls.add(get_enum_class(arg))
312+
deps[engine_class["name"]] = set(filter(lambda x: x != name, ref_cls))
313+
for acls in ref_cls:
314+
if acls == name:
315+
continue
316+
reverse_deps[acls] = reverse_deps.get(acls, set())
317+
reverse_deps[acls].add(name)
318+
319+
included = []
320+
front = list(profile.get("enabled_classes", []))
321+
if front:
322+
# These must always be included
323+
front.append("WorkerThreadPool")
324+
front.append("ClassDB")
325+
front.append("ClassDBSingleton")
326+
while front:
327+
cls = front.pop()
328+
if cls in included:
329+
continue
330+
included.append(cls)
331+
parent = parents.get(cls, "")
332+
if parent:
333+
front.append(parent)
334+
for rcls in deps.get(cls, set()):
335+
if rcls in included or rcls in front:
336+
continue
337+
front.append(rcls)
338+
339+
excluded = []
340+
front = list(profile.get("disabled_classes", []))
341+
while front:
342+
cls = front.pop()
343+
if cls in excluded:
344+
continue
345+
excluded.append(cls)
346+
front += children.get(cls, [])
347+
for rcls in reverse_deps.get(cls, set()):
348+
if rcls in excluded or rcls in front:
349+
continue
350+
front.append(rcls)
351+
352+
if included and excluded:
353+
print(
354+
"WARNING: Cannot specify both 'enabled_classes' and 'disabled_classes' in build profile. 'disabled_classes' will be ignored."
355+
)
356+
357+
return {
358+
"enabled_classes": included,
359+
"disabled_classes": excluded,
360+
}
270361

271362

272363
def scons_emit_files(target, source, env):
273-
files = [env.File(f) for f in get_file_list(str(source[0]), target[0].abspath, True, True)]
364+
profile_filepath = env.get("build_profile", "")
365+
if profile_filepath and not Path(profile_filepath).is_absolute():
366+
profile_filepath = str((Path(env.Dir("#").abspath) / profile_filepath).as_posix())
367+
368+
files = [env.File(f) for f in get_file_list(str(source[0]), target[0].abspath, True, True, profile_filepath)]
274369
env.Clean(target, files)
275370
env["godot_cpp_gen_dir"] = target[0].abspath
276371
return files, source
@@ -2547,6 +2642,20 @@ def is_refcounted(type_name):
25472642
return type_name in engine_classes and engine_classes[type_name]
25482643

25492644

2645+
def is_class_included(class_name, build_profile):
2646+
"""
2647+
Check if an engine class should be included.
2648+
This removes classes according to a build profile of enabled or disabled classes.
2649+
"""
2650+
included = build_profile.get("enabled_classes", [])
2651+
excluded = build_profile.get("disabled_classes", [])
2652+
if included:
2653+
return class_name in included
2654+
if excluded:
2655+
return class_name not in excluded
2656+
return True
2657+
2658+
25502659
def is_included(type_name, current_type):
25512660
"""
25522661
Check if a builtin type should be included.

test/build_profile.json

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"enabled_classes": [
3+
"Control",
4+
"Label"
5+
]
6+
}

tools/godotcpp.py

+9
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,15 @@ def options(opts, env):
285285
)
286286
)
287287

288+
opts.Add(
289+
PathVariable(
290+
"build_profile",
291+
"Path to a file containing a feature build profile",
292+
default=env.get("build_profile", None),
293+
validator=validate_file,
294+
)
295+
)
296+
288297
opts.Add(
289298
BoolVariable(
290299
key="use_hot_reload",

0 commit comments

Comments
 (0)