@@ -70,12 +70,14 @@ def generate_wrappers(target):
7070 f .write (txt )
7171
7272
73- def get_file_list (api_filepath , output_dir , headers = False , sources = False ):
73+ def get_file_list (api_filepath , output_dir , headers = False , sources = False , profile_filepath = "" ):
7474 api = {}
7575 files = []
7676 with open (api_filepath , encoding = "utf-8" ) as api_file :
7777 api = json .load (api_file )
7878
79+ build_profile = parse_build_profile (profile_filepath , api )
80+
7981 core_gen_folder = Path (output_dir ) / "gen" / "include" / "godot_cpp" / "core"
8082 include_gen_folder = Path (output_dir ) / "gen" / "include" / "godot_cpp"
8183 source_gen_folder = Path (output_dir ) / "gen" / "src"
@@ -105,7 +107,7 @@ def get_file_list(api_filepath, output_dir, headers=False, sources=False):
105107 source_filename = source_gen_folder / "classes" / (camel_to_snake (engine_class ["name" ]) + ".cpp" )
106108 if headers :
107109 files .append (str (header_filename .as_posix ()))
108- if sources :
110+ if sources and is_class_included ( engine_class [ "name" ], build_profile ) :
109111 files .append (str (source_filename .as_posix ()))
110112
111113 for native_struct in api ["native_structures" ]:
@@ -137,12 +139,105 @@ def get_file_list(api_filepath, output_dir, headers=False, sources=False):
137139 return files
138140
139141
140- def print_file_list (api_filepath , output_dir , headers = False , sources = False ):
141- print (* get_file_list (api_filepath , output_dir , headers , sources ), sep = ";" , end = None )
142+ def print_file_list (api_filepath , output_dir , headers = False , sources = False , profile_filepath = "" ):
143+ print (* get_file_list (api_filepath , output_dir , headers , sources , profile_filepath ), sep = ";" , end = None )
144+
145+
146+ def parse_build_profile (profile_filepath , api ):
147+ if profile_filepath == "" :
148+ return {}
149+ print ("Using feature build profile: " + profile_filepath )
150+
151+ with open (profile_filepath , encoding = "utf-8" ) as profile_file :
152+ profile = json .load (profile_file )
153+
154+ api_dict = {}
155+ parents = {}
156+ children = {}
157+ for engine_class in api ["classes" ]:
158+ api_dict [engine_class ["name" ]] = engine_class
159+ parent = engine_class .get ("inherits" , "" )
160+ child = engine_class ["name" ]
161+ parents [child ] = parent
162+ if parent == "" :
163+ continue
164+ children [parent ] = children .get (parent , [])
165+ children [parent ].append (child )
166+
167+ # Parse methods dependencies
168+ deps = {}
169+ reverse_deps = {}
170+ for name , engine_class in api_dict .items ():
171+ ref_cls = set ()
172+ for method in engine_class .get ("methods" , []):
173+ rtype = method .get ("return_value" , {}).get ("type" , "" )
174+ args = [a ["type" ] for a in method .get ("arguments" , [])]
175+ if rtype in api_dict :
176+ ref_cls .add (rtype )
177+ elif is_enum (rtype ) and get_enum_class (rtype ) in api_dict :
178+ ref_cls .add (get_enum_class (rtype ))
179+ for arg in args :
180+ if arg in api_dict :
181+ ref_cls .add (arg )
182+ elif is_enum (arg ) and get_enum_class (arg ) in api_dict :
183+ ref_cls .add (get_enum_class (arg ))
184+ deps [engine_class ["name" ]] = set (filter (lambda x : x != name , ref_cls ))
185+ for acls in ref_cls :
186+ if acls == name :
187+ continue
188+ reverse_deps [acls ] = reverse_deps .get (acls , set ())
189+ reverse_deps [acls ].add (name )
190+
191+ included = []
192+ front = list (profile .get ("enabled_classes" , []))
193+ if front :
194+ # These must always be included
195+ front .append ("WorkerThreadPool" )
196+ front .append ("ClassDB" )
197+ front .append ("ClassDBSingleton" )
198+ while front :
199+ cls = front .pop ()
200+ if cls in included :
201+ continue
202+ included .append (cls )
203+ parent = parents .get (cls , "" )
204+ if parent :
205+ front .append (parent )
206+ for rcls in deps .get (cls , set ()):
207+ if rcls in included or rcls in front :
208+ continue
209+ front .append (rcls )
210+
211+ excluded = []
212+ front = list (profile .get ("disabled_classes" , []))
213+ while front :
214+ cls = front .pop ()
215+ if cls in excluded :
216+ continue
217+ excluded .append (cls )
218+ front += children .get (cls , [])
219+ for rcls in reverse_deps .get (cls , set ()):
220+ if rcls in excluded or rcls in front :
221+ continue
222+ front .append (rcls )
223+
224+ if included and excluded :
225+ print (
226+ "WARNING: Cannot specify both 'enabled_classes' and 'disabled_classes' in build profile. 'disabled_classes' will be ignored."
227+ )
228+
229+ return {
230+ "enabled_classes" : included ,
231+ "disabled_classes" : excluded ,
232+ }
142233
143234
144235def scons_emit_files (target , source , env ):
145- files = [env .File (f ) for f in get_file_list (str (source [0 ]), target [0 ].abspath , True , True )]
236+ profile_filepath = env .get ("build_profile" , "" )
237+ if profile_filepath and not Path (profile_filepath ).is_absolute ():
238+ profile_filepath = str ((Path (env .Dir ("#" ).abspath ) / profile_filepath ).as_posix ())
239+
240+ files = [env .File (f ) for f in get_file_list (str (source [0 ]), target [0 ].abspath , True , True , profile_filepath )]
146241 env .Clean (target , files )
147242 env ["godot_cpp_gen_dir" ] = target [0 ].abspath
148243 return files , source
@@ -2411,6 +2506,20 @@ def is_refcounted(type_name):
24112506 return type_name in engine_classes and engine_classes [type_name ]
24122507
24132508
2509+ def is_class_included (class_name , build_profile ):
2510+ """
2511+ Check if an engine class should be included.
2512+ This removes classes according to a build profile of enabled or disabled classes.
2513+ """
2514+ included = build_profile .get ("enabled_classes" , [])
2515+ excluded = build_profile .get ("disabled_classes" , [])
2516+ if included :
2517+ return class_name in included
2518+ if excluded :
2519+ return class_name not in excluded
2520+ return True
2521+
2522+
24142523def is_included (type_name , current_type ):
24152524 """
24162525 Check if a builtin type should be included.
0 commit comments