123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292 |
- """Functions used to generate source files during build time"""
- from collections import OrderedDict
- from io import TextIOWrapper
- import methods
- # Generate disabled classes
- def disabled_class_builder(target, source, env):
- with methods.generated_wrapper(str(target[0])) as file:
- for c in source[0].read():
- if cs := c.strip():
- file.write(f"#define ClassDB_Disable_{cs} 1\n")
- # Generate version info
- def version_info_builder(target, source, env):
- with methods.generated_wrapper(str(target[0])) as file:
- file.write(
- """\
- #define GODOT_VERSION_SHORT_NAME "{short_name}"
- #define GODOT_VERSION_NAME "{name}"
- #define GODOT_VERSION_MAJOR {major}
- #define GODOT_VERSION_MINOR {minor}
- #define GODOT_VERSION_PATCH {patch}
- #define GODOT_VERSION_STATUS "{status}"
- #define GODOT_VERSION_BUILD "{build}"
- #define GODOT_VERSION_MODULE_CONFIG "{module_config}"
- #define GODOT_VERSION_WEBSITE "{website}"
- #define GODOT_VERSION_DOCS_BRANCH "{docs_branch}"
- #define GODOT_VERSION_DOCS_URL "https://docs.godotengine.org/en/" GODOT_VERSION_DOCS_BRANCH
- """.format(**source[0].read())
- )
- def version_hash_builder(target, source, env):
- with methods.generated_wrapper(str(target[0])) as file:
- file.write(
- """\
- #include "core/version.h"
- const char *const GODOT_VERSION_HASH = "{git_hash}";
- const uint64_t GODOT_VERSION_TIMESTAMP = {git_timestamp};
- """.format(**source[0].read())
- )
- def encryption_key_builder(target, source, env):
- src = source[0].read() or "0" * 64
- try:
- buffer = bytes.fromhex(src)
- if len(buffer) != 32:
- raise ValueError
- except ValueError:
- methods.print_error(
- f'Invalid AES256 encryption key, not 64 hexadecimal characters: "{src}".\n'
- "Unset `SCRIPT_AES256_ENCRYPTION_KEY` in your environment "
- "or make sure that it contains exactly 64 hexadecimal characters."
- )
- raise
- with methods.generated_wrapper(str(target[0])) as file:
- file.write(
- f"""\
- #include "core/config/project_settings.h"
- uint8_t script_encryption_key[32] = {{
- {methods.format_buffer(buffer, 1)}
- }};"""
- )
- def make_certs_header(target, source, env):
- buffer = methods.get_buffer(str(source[0]))
- decomp_size = len(buffer)
- buffer = methods.compress_buffer(buffer)
- with methods.generated_wrapper(str(target[0])) as file:
- # System certs path. Editor will use them if defined. (for package maintainers)
- file.write(f'#define _SYSTEM_CERTS_PATH "{source[2]}"\n')
- if source[1].read():
- # Defined here and not in env so changing it does not trigger a full rebuild.
- file.write(f"""\
- #define BUILTIN_CERTS_ENABLED
- inline constexpr int _certs_compressed_size = {len(buffer)};
- inline constexpr int _certs_uncompressed_size = {decomp_size};
- inline constexpr unsigned char _certs_compressed[] = {{
- {methods.format_buffer(buffer, 1)}
- }};
- """)
- def make_authors_header(target, source, env):
- SECTIONS = {
- "Project Founders": "AUTHORS_FOUNDERS",
- "Lead Developer": "AUTHORS_LEAD_DEVELOPERS",
- "Project Manager": "AUTHORS_PROJECT_MANAGERS",
- "Developers": "AUTHORS_DEVELOPERS",
- }
- buffer = methods.get_buffer(str(source[0]))
- reading = False
- with methods.generated_wrapper(str(target[0])) as file:
- def close_section():
- file.write("\tnullptr,\n};\n\n")
- for line in buffer.decode().splitlines():
- if line.startswith(" ") and reading:
- file.write(f'\t"{methods.to_escaped_cstring(line).strip()}",\n')
- elif line.startswith("## "):
- if reading:
- close_section()
- reading = False
- section = SECTIONS[line[3:].strip()]
- if section:
- file.write(f"inline constexpr const char *{section}[] = {{\n")
- reading = True
- if reading:
- close_section()
- def make_donors_header(target, source, env):
- SECTIONS = {
- "Patrons": "DONORS_PATRONS",
- "Platinum sponsors": "DONORS_SPONSORS_PLATINUM",
- "Gold sponsors": "DONORS_SPONSORS_GOLD",
- "Silver sponsors": "DONORS_SPONSORS_SILVER",
- "Diamond members": "DONORS_MEMBERS_DIAMOND",
- "Titanium members": "DONORS_MEMBERS_TITANIUM",
- "Platinum members": "DONORS_MEMBERS_PLATINUM",
- "Gold members": "DONORS_MEMBERS_GOLD",
- }
- buffer = methods.get_buffer(str(source[0]))
- reading = False
- with methods.generated_wrapper(str(target[0])) as file:
- def close_section():
- file.write("\tnullptr,\n};\n\n")
- for line in buffer.decode().splitlines():
- if line.startswith(" ") and reading:
- file.write(f'\t"{methods.to_escaped_cstring(line).strip()}",\n')
- elif line.startswith("## "):
- if reading:
- close_section()
- reading = False
- section = SECTIONS.get(line[3:].strip())
- if section:
- file.write(f"inline constexpr const char *{section}[] = {{\n")
- reading = True
- if reading:
- close_section()
- def make_license_header(target, source, env):
- src_copyright = str(source[0])
- src_license = str(source[1])
- class LicenseReader:
- def __init__(self, license_file: TextIOWrapper):
- self._license_file = license_file
- self.line_num = 0
- self.current = self.next_line()
- def next_line(self):
- line = self._license_file.readline()
- self.line_num += 1
- while line.startswith("#"):
- line = self._license_file.readline()
- self.line_num += 1
- self.current = line
- return line
- def next_tag(self):
- if ":" not in self.current:
- return ("", [])
- tag, line = self.current.split(":", 1)
- lines = [line.strip()]
- while self.next_line() and self.current.startswith(" "):
- lines.append(self.current.strip())
- return (tag, lines)
- projects = OrderedDict()
- license_list = []
- with open(src_copyright, "r", encoding="utf-8") as copyright_file:
- reader = LicenseReader(copyright_file)
- part = {}
- while reader.current:
- tag, content = reader.next_tag()
- if tag in ("Files", "Copyright", "License"):
- part[tag] = content[:]
- elif tag == "Comment" and part:
- # attach non-empty part to named project
- projects[content[0]] = projects.get(content[0], []) + [part]
- if not tag or not reader.current:
- # end of a paragraph start a new part
- if "License" in part and "Files" not in part:
- # no Files tag in this one, so assume standalone license
- license_list.append(part["License"])
- part = {}
- reader.next_line()
- data_list = []
- for project in iter(projects.values()):
- for part in project:
- part["file_index"] = len(data_list)
- data_list += part["Files"]
- part["copyright_index"] = len(data_list)
- data_list += part["Copyright"]
- with open(src_license, "r", encoding="utf-8") as file:
- license_text = file.read()
- with methods.generated_wrapper(str(target[0])) as file:
- file.write(f"""\
- inline constexpr const char *GODOT_LICENSE_TEXT = {{
- {methods.to_raw_cstring(license_text)}
- }};
- struct ComponentCopyrightPart {{
- const char *license;
- const char *const *files;
- const char *const *copyright_statements;
- int file_count;
- int copyright_count;
- }};
- struct ComponentCopyright {{
- const char *name;
- const ComponentCopyrightPart *parts;
- int part_count;
- }};
- """)
- file.write("inline constexpr const char *COPYRIGHT_INFO_DATA[] = {\n")
- for line in data_list:
- file.write(f'\t"{methods.to_escaped_cstring(line)}",\n')
- file.write("};\n\n")
- file.write("inline constexpr ComponentCopyrightPart COPYRIGHT_PROJECT_PARTS[] = {\n")
- part_index = 0
- part_indexes = {}
- for project_name, project in iter(projects.items()):
- part_indexes[project_name] = part_index
- for part in project:
- file.write(
- f'\t{{ "{methods.to_escaped_cstring(part["License"][0])}", '
- + f"©RIGHT_INFO_DATA[{part['file_index']}], "
- + f"©RIGHT_INFO_DATA[{part['copyright_index']}], "
- + f"{len(part['Files'])}, {len(part['Copyright'])} }},\n"
- )
- part_index += 1
- file.write("};\n\n")
- file.write(f"inline constexpr int COPYRIGHT_INFO_COUNT = {len(projects)};\n")
- file.write("inline constexpr ComponentCopyright COPYRIGHT_INFO[] = {\n")
- for project_name, project in iter(projects.items()):
- file.write(
- f'\t{{ "{methods.to_escaped_cstring(project_name)}", '
- + f"©RIGHT_PROJECT_PARTS[{part_indexes[project_name]}], "
- + f"{len(project)} }},\n"
- )
- file.write("};\n\n")
- file.write(f"inline constexpr int LICENSE_COUNT = {len(license_list)};\n")
- file.write("inline constexpr const char *LICENSE_NAMES[] = {\n")
- for license in license_list:
- file.write(f'\t"{methods.to_escaped_cstring(license[0])}",\n')
- file.write("};\n\n")
- file.write("inline constexpr const char *LICENSE_BODIES[] = {\n\n")
- for license in license_list:
- to_raw = []
- for line in license[1:]:
- if line == ".":
- to_raw += [""]
- else:
- to_raw += [line]
- file.write(f"{methods.to_raw_cstring(to_raw)},\n\n")
- file.write("};\n\n")
|