"""Functions used to generate source files during build time All such functions are invoked in a subprocess on Windows to prevent build flakiness. """ import zlib from platform_methods import subprocess_main def escape_string(s): def charcode_to_c_escapes(c): rev_result = [] while c >= 256: c, low = (c // 256, c % 256) rev_result.append("\\%03o" % low) rev_result.append("\\%03o" % c) return "".join(reversed(rev_result)) result = "" if isinstance(s, str): s = s.encode("utf-8") for c in s: if not (32 <= c < 127) or c in (ord("\\"), ord('"')): result += charcode_to_c_escapes(c) else: result += chr(c) return result def make_certs_header(target, source, env): src = source[0] dst = target[0] f = open(src, "rb") g = open(dst, "w", encoding="utf-8", newline="\n") buf = f.read() decomp_size = len(buf) # Use maximum zlib compression level to further reduce file size # (at the cost of initial build times). buf = zlib.compress(buf, zlib.Z_BEST_COMPRESSION) g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") g.write("#ifndef CERTS_COMPRESSED_GEN_H\n") g.write("#define CERTS_COMPRESSED_GEN_H\n") # System certs path. Editor will use them if defined. (for package maintainers) path = env["system_certs_path"] g.write('#define _SYSTEM_CERTS_PATH "%s"\n' % str(path)) if env["builtin_certs"]: # Defined here and not in env so changing it does not trigger a full rebuild. g.write("#define BUILTIN_CERTS_ENABLED\n") g.write("static const int _certs_compressed_size = " + str(len(buf)) + ";\n") g.write("static const int _certs_uncompressed_size = " + str(decomp_size) + ";\n") g.write("static const unsigned char _certs_compressed[] = {\n") for i in range(len(buf)): g.write("\t" + str(buf[i]) + ",\n") g.write("};\n") g.write("#endif // CERTS_COMPRESSED_GEN_H") g.close() f.close() def make_authors_header(target, source, env): sections = [ "Project Founders", "Lead Developer", "Project Manager", "Developers", ] sections_id = [ "AUTHORS_FOUNDERS", "AUTHORS_LEAD_DEVELOPERS", "AUTHORS_PROJECT_MANAGERS", "AUTHORS_DEVELOPERS", ] src = source[0] dst = target[0] f = open(src, "r", encoding="utf-8") g = open(dst, "w", encoding="utf-8", newline="\n") g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") g.write("#ifndef AUTHORS_GEN_H\n") g.write("#define AUTHORS_GEN_H\n") reading = False def close_section(): g.write("\t0\n") g.write("};\n") for line in f: if reading: if line.startswith(" "): g.write('\t"' + escape_string(line.strip()) + '",\n') continue if line.startswith("## "): if reading: close_section() reading = False for section, section_id in zip(sections, sections_id): if line.strip().endswith(section): current_section = escape_string(section_id) reading = True g.write("const char *const " + current_section + "[] = {\n") break if reading: close_section() g.write("#endif // AUTHORS_GEN_H\n") g.close() f.close() def make_donors_header(target, source, env): sections = [ "Patrons", "Platinum sponsors", "Gold sponsors", "Silver sponsors", "Diamond members", "Titanium members", "Platinum members", "Gold members", ] sections_id = [ "DONORS_PATRONS", "DONORS_SPONSORS_PLATINUM", "DONORS_SPONSORS_GOLD", "DONORS_SPONSORS_SILVER", "DONORS_MEMBERS_DIAMOND", "DONORS_MEMBERS_TITANIUM", "DONORS_MEMBERS_PLATINUM", "DONORS_MEMBERS_GOLD", ] src = source[0] dst = target[0] f = open(src, "r", encoding="utf-8") g = open(dst, "w", encoding="utf-8", newline="\n") g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") g.write("#ifndef DONORS_GEN_H\n") g.write("#define DONORS_GEN_H\n") reading = False def close_section(): g.write("\t0\n") g.write("};\n") for line in f: if reading >= 0: if line.startswith(" "): g.write('\t"' + escape_string(line.strip()) + '",\n') continue if line.startswith("## "): if reading: close_section() reading = False for section, section_id in zip(sections, sections_id): if line.strip().endswith(section): current_section = escape_string(section_id) reading = True g.write("const char *const " + current_section + "[] = {\n") break if reading: close_section() g.write("#endif // DONORS_GEN_H\n") g.close() f.close() def make_license_header(target, source, env): src_copyright = source[0] src_license = source[1] dst = target[0] class LicenseReader: def __init__(self, license_file): 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) from collections import OrderedDict projects: dict = 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": # attach 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 not "Files" in part: # no Files tag in this one, so assume standalone license license_list.append(part["License"]) part = {} reader.next_line() data_list: 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(dst, "w", encoding="utf-8", newline="\n") as f: f.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") f.write("#ifndef LICENSE_GEN_H\n") f.write("#define LICENSE_GEN_H\n") f.write("const char *const GODOT_LICENSE_TEXT =") with open(src_license, "r", encoding="utf-8") as license_file: for line in license_file: escaped_string = escape_string(line.strip()) f.write('\n\t\t"' + escaped_string + '\\n"') f.write(";\n\n") f.write( "struct ComponentCopyrightPart {\n" "\tconst char *license;\n" "\tconst char *const *files;\n" "\tconst char *const *copyright_statements;\n" "\tint file_count;\n" "\tint copyright_count;\n" "};\n\n" ) f.write( "struct ComponentCopyright {\n" "\tconst char *name;\n" "\tconst ComponentCopyrightPart *parts;\n" "\tint part_count;\n" "};\n\n" ) f.write("const char *const COPYRIGHT_INFO_DATA[] = {\n") for line in data_list: f.write('\t"' + escape_string(line) + '",\n') f.write("};\n\n") f.write("const 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: f.write( '\t{ "' + escape_string(part["License"][0]) + '", ' + "©RIGHT_INFO_DATA[" + str(part["file_index"]) + "], " + "©RIGHT_INFO_DATA[" + str(part["copyright_index"]) + "], " + str(len(part["Files"])) + ", " + str(len(part["Copyright"])) + " },\n" ) part_index += 1 f.write("};\n\n") f.write("const int COPYRIGHT_INFO_COUNT = " + str(len(projects)) + ";\n") f.write("const ComponentCopyright COPYRIGHT_INFO[] = {\n") for project_name, project in iter(projects.items()): f.write( '\t{ "' + escape_string(project_name) + '", ' + "©RIGHT_PROJECT_PARTS[" + str(part_indexes[project_name]) + "], " + str(len(project)) + " },\n" ) f.write("};\n\n") f.write("const int LICENSE_COUNT = " + str(len(license_list)) + ";\n") f.write("const char *const LICENSE_NAMES[] = {\n") for l in license_list: f.write('\t"' + escape_string(l[0]) + '",\n') f.write("};\n\n") f.write("const char *const LICENSE_BODIES[] = {\n\n") for l in license_list: for line in l[1:]: if line == ".": f.write('\t"\\n"\n') else: f.write('\t"' + escape_string(line) + '\\n"\n') f.write('\t"",\n\n') f.write("};\n\n") f.write("#endif // LICENSE_GEN_H\n") if __name__ == "__main__": subprocess_main(globals())