mirror of
				https://github.com/godotengine/godot.git
				synced 2025-10-31 21:51:22 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			291 lines
		
	
	
	
		
			9.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			291 lines
		
	
	
	
		
			9.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| """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")
 | 
