Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/scu_builders.py
9819 views
1
"""Functions used to generate scu build source files during build time"""
2
3
import glob
4
import math
5
import os
6
from pathlib import Path
7
8
from methods import print_error
9
10
base_folder_path = str(Path(__file__).parent) + "/"
11
base_folder_only = os.path.basename(os.path.normpath(base_folder_path))
12
_verbose = False # Set manually for debug prints
13
_scu_folders = set()
14
_max_includes_per_scu = 1024
15
16
17
def clear_out_stale_files(output_folder, extension, fresh_files):
18
output_folder = os.path.abspath(output_folder)
19
# print("clear_out_stale_files from folder: " + output_folder)
20
21
if not os.path.isdir(output_folder):
22
# folder does not exist or has not been created yet,
23
# no files to clearout. (this is not an error)
24
return
25
26
for file in glob.glob(output_folder + "/*." + extension):
27
file = Path(file)
28
if file not in fresh_files:
29
# print("removed stale file: " + str(file))
30
os.remove(file)
31
32
33
def folder_not_found(folder):
34
abs_folder = base_folder_path + folder + "/"
35
return not os.path.isdir(abs_folder)
36
37
38
def find_files_in_folder(folder, sub_folder, include_list, extension, sought_exceptions, found_exceptions):
39
abs_folder = base_folder_path + folder + "/" + sub_folder
40
41
if not os.path.isdir(abs_folder):
42
print_error(f'SCU: "{abs_folder}" not found.')
43
return include_list, found_exceptions
44
45
os.chdir(abs_folder)
46
47
sub_folder_slashed = ""
48
if sub_folder != "":
49
sub_folder_slashed = sub_folder + "/"
50
51
for file in glob.glob("*." + extension):
52
simple_name = Path(file).stem
53
54
if file.endswith(".gen.cpp"):
55
continue
56
57
li = '#include "' + folder + "/" + sub_folder_slashed + file + '"'
58
59
if simple_name not in sought_exceptions:
60
include_list.append(li)
61
else:
62
found_exceptions.append(li)
63
64
return include_list, found_exceptions
65
66
67
def write_output_file(file_count, include_list, start_line, end_line, output_folder, output_filename_prefix, extension):
68
output_folder = os.path.abspath(output_folder)
69
70
if not os.path.isdir(output_folder):
71
# create
72
os.mkdir(output_folder)
73
if not os.path.isdir(output_folder):
74
print_error(f'SCU: "{output_folder}" could not be created.')
75
return
76
if _verbose:
77
print("SCU: Creating folder: %s" % output_folder)
78
79
file_text = ""
80
81
for i in range(start_line, end_line):
82
if i < len(include_list):
83
line = include_list[i]
84
li = line + "\n"
85
file_text += li
86
87
num_string = ""
88
if file_count > 0:
89
num_string = "_" + str(file_count)
90
91
short_filename = output_filename_prefix + num_string + ".gen." + extension
92
output_filename = output_folder + "/" + short_filename
93
output_path = Path(output_filename)
94
95
if not output_path.exists() or output_path.read_text() != file_text:
96
if _verbose:
97
print("SCU: Generating: %s" % short_filename)
98
output_path.write_text(file_text, encoding="utf8")
99
elif _verbose:
100
print("SCU: Generation not needed for: " + short_filename)
101
102
return output_path
103
104
105
def write_exception_output_file(file_count, exception_string, output_folder, output_filename_prefix, extension):
106
output_folder = os.path.abspath(output_folder)
107
if not os.path.isdir(output_folder):
108
print_error(f"SCU: {output_folder} does not exist.")
109
return
110
111
file_text = exception_string + "\n"
112
113
num_string = ""
114
if file_count > 0:
115
num_string = "_" + str(file_count)
116
117
short_filename = output_filename_prefix + "_exception" + num_string + ".gen." + extension
118
output_filename = output_folder + "/" + short_filename
119
120
output_path = Path(output_filename)
121
122
if not output_path.exists() or output_path.read_text() != file_text:
123
if _verbose:
124
print("SCU: Generating: " + short_filename)
125
output_path.write_text(file_text, encoding="utf8")
126
elif _verbose:
127
print("SCU: Generation not needed for: " + short_filename)
128
129
return output_path
130
131
132
def find_section_name(sub_folder):
133
# Construct a useful name for the section from the path for debug logging
134
section_path = os.path.abspath(base_folder_path + sub_folder) + "/"
135
136
folders = []
137
folder = ""
138
139
for i in range(8):
140
folder = os.path.dirname(section_path)
141
folder = os.path.basename(folder)
142
if folder == base_folder_only:
143
break
144
folders.append(folder)
145
section_path += "../"
146
section_path = os.path.abspath(section_path) + "/"
147
148
section_name = ""
149
for n in range(len(folders)):
150
section_name += folders[len(folders) - n - 1]
151
if n != (len(folders) - 1):
152
section_name += "_"
153
154
return section_name
155
156
157
# "folders" is a list of folders to add all the files from to add to the SCU
158
# "section (like a module)". The name of the scu file will be derived from the first folder
159
# (thus e.g. scene/3d becomes scu_scene_3d.gen.cpp)
160
161
# "includes_per_scu" limits the number of includes in a single scu file.
162
# This allows the module to be built in several translation units instead of just 1.
163
# This will usually be slower to compile but will use less memory per compiler instance, which
164
# is most relevant in release builds.
165
166
# "sought_exceptions" are a list of files (without extension) that contain
167
# e.g. naming conflicts, and are therefore not suitable for the scu build.
168
# These will automatically be placed in their own separate scu file,
169
# which is slow like a normal build, but prevents the naming conflicts.
170
# Ideally in these situations, the source code should be changed to prevent naming conflicts.
171
172
173
# "extension" will usually be cpp, but can also be set to c (for e.g. third party libraries that use c)
174
def process_folder(folders, sought_exceptions=[], includes_per_scu=0, extension="cpp"):
175
if len(folders) == 0:
176
return
177
178
# Construct the filename prefix from the FIRST folder name
179
# e.g. "scene_3d"
180
out_filename = find_section_name(folders[0])
181
182
found_includes = []
183
found_exceptions = []
184
185
main_folder = folders[0]
186
abs_main_folder = base_folder_path + main_folder
187
188
# Keep a record of all folders that have been processed for SCU,
189
# this enables deciding what to do when we call "add_source_files()"
190
global _scu_folders
191
_scu_folders.add(main_folder)
192
193
# main folder (first)
194
found_includes, found_exceptions = find_files_in_folder(
195
main_folder, "", found_includes, extension, sought_exceptions, found_exceptions
196
)
197
198
# sub folders
199
for d in range(1, len(folders)):
200
found_includes, found_exceptions = find_files_in_folder(
201
main_folder, folders[d], found_includes, extension, sought_exceptions, found_exceptions
202
)
203
204
found_includes = sorted(found_includes)
205
206
# calculate how many lines to write in each file
207
total_lines = len(found_includes)
208
209
# adjust number of output files according to whether DEV or release
210
num_output_files = 1
211
212
if includes_per_scu == 0:
213
includes_per_scu = _max_includes_per_scu
214
else:
215
if includes_per_scu > _max_includes_per_scu:
216
includes_per_scu = _max_includes_per_scu
217
218
num_output_files = max(math.ceil(total_lines / float(includes_per_scu)), 1)
219
220
lines_per_file = math.ceil(total_lines / float(num_output_files))
221
lines_per_file = max(lines_per_file, 1)
222
223
start_line = 0
224
225
# These do not vary throughout the loop
226
output_folder = abs_main_folder + "/.scu/"
227
output_filename_prefix = "scu_" + out_filename
228
229
fresh_files = set()
230
231
for file_count in range(0, num_output_files):
232
end_line = start_line + lines_per_file
233
234
# special case to cover rounding error in final file
235
if file_count == (num_output_files - 1):
236
end_line = len(found_includes)
237
238
fresh_file = write_output_file(
239
file_count, found_includes, start_line, end_line, output_folder, output_filename_prefix, extension
240
)
241
242
fresh_files.add(fresh_file)
243
244
start_line = end_line
245
246
# Write the exceptions each in their own scu gen file,
247
# so they can effectively compile in "old style / normal build".
248
for exception_count in range(len(found_exceptions)):
249
fresh_file = write_exception_output_file(
250
exception_count, found_exceptions[exception_count], output_folder, output_filename_prefix, extension
251
)
252
253
fresh_files.add(fresh_file)
254
255
# Clear out any stale file (usually we will be overwriting if necessary,
256
# but we want to remove any that are pre-existing that will not be
257
# overwritten, so as to not compile anything stale).
258
clear_out_stale_files(output_folder, extension, fresh_files)
259
260
261
def generate_scu_files(max_includes_per_scu):
262
global _max_includes_per_scu
263
_max_includes_per_scu = max_includes_per_scu
264
265
print("SCU: Generating build files... (max includes per SCU: %d)" % _max_includes_per_scu)
266
267
curr_folder = os.path.abspath("./")
268
269
# check we are running from the correct folder
270
if folder_not_found("core") or folder_not_found("platform") or folder_not_found("scene"):
271
raise RuntimeError("scu_builders.py must be run from the godot folder.")
272
return
273
274
process_folder(["core"])
275
process_folder(["core/crypto"])
276
process_folder(["core/debugger"])
277
process_folder(["core/extension"])
278
process_folder(["core/input"])
279
process_folder(["core/io"])
280
process_folder(["core/math"])
281
process_folder(["core/object"])
282
process_folder(["core/os"])
283
process_folder(["core/string"])
284
process_folder(["core/variant"], ["variant_utility"])
285
286
process_folder(["drivers/unix"])
287
process_folder(["drivers/png"])
288
289
process_folder(["drivers/gles3/effects"])
290
process_folder(["drivers/gles3/storage"])
291
292
process_folder(["editor"], [], 32)
293
process_folder(["editor/animation"])
294
process_folder(["editor/asset_library"])
295
process_folder(["editor/audio"])
296
process_folder(["editor/debugger"])
297
process_folder(["editor/debugger/debug_adapter"])
298
process_folder(["editor/doc"])
299
process_folder(["editor/docks"], ["file_system_dock"])
300
process_folder(["editor/export"])
301
process_folder(["editor/file_system"])
302
process_folder(["editor/gui"])
303
process_folder(["editor/inspector"], ["editor_resource_preview"])
304
process_folder(["editor/themes"])
305
process_folder(["editor/project_manager"])
306
process_folder(["editor/project_upgrade"])
307
process_folder(["editor/import"])
308
process_folder(["editor/import/3d"])
309
process_folder(["editor/plugins"])
310
process_folder(["editor/run"])
311
process_folder(["editor/scene"])
312
process_folder(["editor/scene/2d"])
313
process_folder(["editor/scene/2d/physics"])
314
process_folder(["editor/scene/2d/tiles"])
315
process_folder(["editor/scene/3d"])
316
process_folder(["editor/scene/3d/gizmos"])
317
process_folder(["editor/scene/gui"])
318
process_folder(["editor/scene/texture"])
319
process_folder(["editor/script"])
320
process_folder(["editor/settings"])
321
process_folder(["editor/shader"])
322
process_folder(["editor/translations"])
323
process_folder(["editor/version_control"])
324
325
process_folder(["platform/android/export"])
326
process_folder(["platform/ios/export"])
327
process_folder(["platform/linuxbsd/export"])
328
process_folder(["platform/macos/export"])
329
process_folder(["platform/web/export"])
330
process_folder(["platform/windows/export"])
331
332
process_folder(["modules/lightmapper_rd"])
333
process_folder(["modules/gltf"])
334
process_folder(["modules/gltf/structures"])
335
process_folder(["modules/gltf/editor"])
336
process_folder(["modules/gltf/extensions"])
337
process_folder(["modules/gltf/extensions/physics"])
338
process_folder(["modules/navigation_3d"])
339
process_folder(["modules/navigation_3d/3d"])
340
process_folder(["modules/navigation_2d"])
341
process_folder(["modules/navigation_2d/2d"])
342
process_folder(["modules/webrtc"])
343
process_folder(["modules/websocket"])
344
process_folder(["modules/gridmap"])
345
process_folder(["modules/multiplayer"])
346
process_folder(["modules/multiplayer/editor"])
347
process_folder(["modules/openxr"], ["register_types"])
348
process_folder(["modules/openxr/action_map"])
349
process_folder(["modules/openxr/editor"])
350
# process_folder(["modules/openxr/extensions"]) # Sensitive include order for platform code.
351
process_folder(["modules/openxr/scene"])
352
process_folder(["modules/godot_physics_2d"])
353
process_folder(["modules/godot_physics_3d"])
354
process_folder(["modules/godot_physics_3d/joints"])
355
356
process_folder(["modules/csg"])
357
process_folder(["modules/gdscript"])
358
process_folder(["modules/gdscript/editor"])
359
process_folder(["modules/gdscript/language_server"])
360
361
process_folder(["scene/2d"])
362
process_folder(["scene/2d/physics"])
363
process_folder(["scene/2d/physics/joints"])
364
process_folder(["scene/3d"])
365
process_folder(["scene/3d/physics"])
366
process_folder(["scene/3d/physics/joints"])
367
process_folder(["scene/3d/xr"])
368
process_folder(["scene/animation"])
369
process_folder(["scene/gui"])
370
process_folder(["scene/main"])
371
process_folder(["scene/theme"])
372
process_folder(["scene/resources"])
373
process_folder(["scene/resources/2d"])
374
process_folder(["scene/resources/2d/skeleton"])
375
process_folder(["scene/resources/3d"])
376
377
process_folder(["servers"])
378
process_folder(["servers/rendering"])
379
process_folder(["servers/rendering/dummy/storage"])
380
process_folder(["servers/rendering/storage"])
381
process_folder(["servers/rendering/renderer_rd"])
382
process_folder(["servers/rendering/renderer_rd/effects"])
383
process_folder(["servers/rendering/renderer_rd/environment"])
384
process_folder(["servers/rendering/renderer_rd/storage_rd"])
385
process_folder(["servers/rendering/renderer_rd/forward_clustered"])
386
process_folder(["servers/rendering/renderer_rd/forward_mobile"])
387
process_folder(["servers/audio"])
388
process_folder(["servers/audio/effects"])
389
process_folder(["servers/navigation"])
390
process_folder(["servers/xr"])
391
392
# Finally change back the path to the calling folder
393
os.chdir(curr_folder)
394
395
if _verbose:
396
print("SCU: Processed folders: %s" % sorted(_scu_folders))
397
398
return _scu_folders
399
400