Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/core/core_builders.py
9887 views
1
"""Functions used to generate source files during build time"""
2
3
from collections import OrderedDict
4
from io import TextIOWrapper
5
6
import methods
7
8
9
# Generate disabled classes
10
def disabled_class_builder(target, source, env):
11
with methods.generated_wrapper(str(target[0])) as file:
12
for c in source[0].read():
13
if cs := c.strip():
14
file.write(f"#define ClassDB_Disable_{cs} 1\n")
15
16
17
# Generate version info
18
def version_info_builder(target, source, env):
19
with methods.generated_wrapper(str(target[0])) as file:
20
file.write(
21
"""\
22
#define GODOT_VERSION_SHORT_NAME "{short_name}"
23
#define GODOT_VERSION_NAME "{name}"
24
#define GODOT_VERSION_MAJOR {major}
25
#define GODOT_VERSION_MINOR {minor}
26
#define GODOT_VERSION_PATCH {patch}
27
#define GODOT_VERSION_STATUS "{status}"
28
#define GODOT_VERSION_BUILD "{build}"
29
#define GODOT_VERSION_MODULE_CONFIG "{module_config}"
30
#define GODOT_VERSION_WEBSITE "{website}"
31
#define GODOT_VERSION_DOCS_BRANCH "{docs_branch}"
32
#define GODOT_VERSION_DOCS_URL "https://docs.godotengine.org/en/" GODOT_VERSION_DOCS_BRANCH
33
""".format(**source[0].read())
34
)
35
36
37
def version_hash_builder(target, source, env):
38
with methods.generated_wrapper(str(target[0])) as file:
39
file.write(
40
"""\
41
#include "core/version.h"
42
43
const char *const GODOT_VERSION_HASH = "{git_hash}";
44
const uint64_t GODOT_VERSION_TIMESTAMP = {git_timestamp};
45
""".format(**source[0].read())
46
)
47
48
49
def encryption_key_builder(target, source, env):
50
src = source[0].read() or "0" * 64
51
try:
52
buffer = bytes.fromhex(src)
53
if len(buffer) != 32:
54
raise ValueError
55
except ValueError:
56
methods.print_error(
57
f'Invalid AES256 encryption key, not 64 hexadecimal characters: "{src}".\n'
58
"Unset `SCRIPT_AES256_ENCRYPTION_KEY` in your environment "
59
"or make sure that it contains exactly 64 hexadecimal characters."
60
)
61
raise
62
63
with methods.generated_wrapper(str(target[0])) as file:
64
file.write(
65
f"""\
66
#include "core/config/project_settings.h"
67
68
uint8_t script_encryption_key[32] = {{
69
{methods.format_buffer(buffer, 1)}
70
}};"""
71
)
72
73
74
def make_certs_header(target, source, env):
75
buffer = methods.get_buffer(str(source[0]))
76
decomp_size = len(buffer)
77
buffer = methods.compress_buffer(buffer)
78
79
with methods.generated_wrapper(str(target[0])) as file:
80
# System certs path. Editor will use them if defined. (for package maintainers)
81
file.write(f'#define _SYSTEM_CERTS_PATH "{source[2]}"\n')
82
if source[1].read():
83
# Defined here and not in env so changing it does not trigger a full rebuild.
84
file.write(f"""\
85
#define BUILTIN_CERTS_ENABLED
86
87
inline constexpr int _certs_compressed_size = {len(buffer)};
88
inline constexpr int _certs_uncompressed_size = {decomp_size};
89
inline constexpr unsigned char _certs_compressed[] = {{
90
{methods.format_buffer(buffer, 1)}
91
}};
92
""")
93
94
95
def make_authors_header(target, source, env):
96
SECTIONS = {
97
"Project Founders": "AUTHORS_FOUNDERS",
98
"Lead Developer": "AUTHORS_LEAD_DEVELOPERS",
99
"Project Manager": "AUTHORS_PROJECT_MANAGERS",
100
"Developers": "AUTHORS_DEVELOPERS",
101
}
102
buffer = methods.get_buffer(str(source[0]))
103
reading = False
104
105
with methods.generated_wrapper(str(target[0])) as file:
106
107
def close_section():
108
file.write("\tnullptr,\n};\n\n")
109
110
for line in buffer.decode().splitlines():
111
if line.startswith(" ") and reading:
112
file.write(f'\t"{methods.to_escaped_cstring(line).strip()}",\n')
113
elif line.startswith("## "):
114
if reading:
115
close_section()
116
reading = False
117
section = SECTIONS[line[3:].strip()]
118
if section:
119
file.write(f"inline constexpr const char *{section}[] = {{\n")
120
reading = True
121
122
if reading:
123
close_section()
124
125
126
def make_donors_header(target, source, env):
127
SECTIONS = {
128
"Patrons": "DONORS_PATRONS",
129
"Platinum sponsors": "DONORS_SPONSORS_PLATINUM",
130
"Gold sponsors": "DONORS_SPONSORS_GOLD",
131
"Silver sponsors": "DONORS_SPONSORS_SILVER",
132
"Diamond members": "DONORS_MEMBERS_DIAMOND",
133
"Titanium members": "DONORS_MEMBERS_TITANIUM",
134
"Platinum members": "DONORS_MEMBERS_PLATINUM",
135
"Gold members": "DONORS_MEMBERS_GOLD",
136
}
137
buffer = methods.get_buffer(str(source[0]))
138
reading = False
139
140
with methods.generated_wrapper(str(target[0])) as file:
141
142
def close_section():
143
file.write("\tnullptr,\n};\n\n")
144
145
for line in buffer.decode().splitlines():
146
if line.startswith(" ") and reading:
147
file.write(f'\t"{methods.to_escaped_cstring(line).strip()}",\n')
148
elif line.startswith("## "):
149
if reading:
150
close_section()
151
reading = False
152
section = SECTIONS.get(line[3:].strip())
153
if section:
154
file.write(f"inline constexpr const char *{section}[] = {{\n")
155
reading = True
156
157
if reading:
158
close_section()
159
160
161
def make_license_header(target, source, env):
162
src_copyright = str(source[0])
163
src_license = str(source[1])
164
165
class LicenseReader:
166
def __init__(self, license_file: TextIOWrapper):
167
self._license_file = license_file
168
self.line_num = 0
169
self.current = self.next_line()
170
171
def next_line(self):
172
line = self._license_file.readline()
173
self.line_num += 1
174
while line.startswith("#"):
175
line = self._license_file.readline()
176
self.line_num += 1
177
self.current = line
178
return line
179
180
def next_tag(self):
181
if ":" not in self.current:
182
return ("", [])
183
tag, line = self.current.split(":", 1)
184
lines = [line.strip()]
185
while self.next_line() and self.current.startswith(" "):
186
lines.append(self.current.strip())
187
return (tag, lines)
188
189
projects = OrderedDict()
190
license_list = []
191
192
with open(src_copyright, "r", encoding="utf-8") as copyright_file:
193
reader = LicenseReader(copyright_file)
194
part = {}
195
while reader.current:
196
tag, content = reader.next_tag()
197
if tag in ("Files", "Copyright", "License"):
198
part[tag] = content[:]
199
elif tag == "Comment" and part:
200
# attach non-empty part to named project
201
projects[content[0]] = projects.get(content[0], []) + [part]
202
203
if not tag or not reader.current:
204
# end of a paragraph start a new part
205
if "License" in part and "Files" not in part:
206
# no Files tag in this one, so assume standalone license
207
license_list.append(part["License"])
208
part = {}
209
reader.next_line()
210
211
data_list = []
212
for project in iter(projects.values()):
213
for part in project:
214
part["file_index"] = len(data_list)
215
data_list += part["Files"]
216
part["copyright_index"] = len(data_list)
217
data_list += part["Copyright"]
218
219
with open(src_license, "r", encoding="utf-8") as file:
220
license_text = file.read()
221
222
with methods.generated_wrapper(str(target[0])) as file:
223
file.write(f"""\
224
inline constexpr const char *GODOT_LICENSE_TEXT = {{
225
{methods.to_raw_cstring(license_text)}
226
}};
227
228
struct ComponentCopyrightPart {{
229
const char *license;
230
const char *const *files;
231
const char *const *copyright_statements;
232
int file_count;
233
int copyright_count;
234
}};
235
236
struct ComponentCopyright {{
237
const char *name;
238
const ComponentCopyrightPart *parts;
239
int part_count;
240
}};
241
242
""")
243
244
file.write("inline constexpr const char *COPYRIGHT_INFO_DATA[] = {\n")
245
for line in data_list:
246
file.write(f'\t"{methods.to_escaped_cstring(line)}",\n')
247
file.write("};\n\n")
248
249
file.write("inline constexpr ComponentCopyrightPart COPYRIGHT_PROJECT_PARTS[] = {\n")
250
part_index = 0
251
part_indexes = {}
252
for project_name, project in iter(projects.items()):
253
part_indexes[project_name] = part_index
254
for part in project:
255
file.write(
256
f'\t{{ "{methods.to_escaped_cstring(part["License"][0])}", '
257
+ f"&COPYRIGHT_INFO_DATA[{part['file_index']}], "
258
+ f"&COPYRIGHT_INFO_DATA[{part['copyright_index']}], "
259
+ f"{len(part['Files'])}, {len(part['Copyright'])} }},\n"
260
)
261
part_index += 1
262
file.write("};\n\n")
263
264
file.write(f"inline constexpr int COPYRIGHT_INFO_COUNT = {len(projects)};\n")
265
266
file.write("inline constexpr ComponentCopyright COPYRIGHT_INFO[] = {\n")
267
for project_name, project in iter(projects.items()):
268
file.write(
269
f'\t{{ "{methods.to_escaped_cstring(project_name)}", '
270
+ f"&COPYRIGHT_PROJECT_PARTS[{part_indexes[project_name]}], "
271
+ f"{len(project)} }},\n"
272
)
273
file.write("};\n\n")
274
275
file.write(f"inline constexpr int LICENSE_COUNT = {len(license_list)};\n")
276
277
file.write("inline constexpr const char *LICENSE_NAMES[] = {\n")
278
for license in license_list:
279
file.write(f'\t"{methods.to_escaped_cstring(license[0])}",\n')
280
file.write("};\n\n")
281
282
file.write("inline constexpr const char *LICENSE_BODIES[] = {\n\n")
283
for license in license_list:
284
to_raw = []
285
for line in license[1:]:
286
if line == ".":
287
to_raw += [""]
288
else:
289
to_raw += [line]
290
file.write(f"{methods.to_raw_cstring(to_raw)},\n\n")
291
file.write("};\n\n")
292
293