core_builders.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. """Functions used to generate source files during build time"""
  2. from collections import OrderedDict
  3. from io import TextIOWrapper
  4. import methods
  5. def make_certs_header(target, source, env):
  6. buffer = methods.get_buffer(str(source[0]))
  7. decomp_size = len(buffer)
  8. buffer = methods.compress_buffer(buffer)
  9. with methods.generated_wrapper(str(target[0])) as file:
  10. # System certs path. Editor will use them if defined. (for package maintainers)
  11. file.write('#define _SYSTEM_CERTS_PATH "{}"\n'.format(env["system_certs_path"]))
  12. if env["builtin_certs"]:
  13. # Defined here and not in env so changing it does not trigger a full rebuild.
  14. file.write(f"""\
  15. #define BUILTIN_CERTS_ENABLED
  16. inline constexpr int _certs_compressed_size = {len(buffer)};
  17. inline constexpr int _certs_uncompressed_size = {decomp_size};
  18. inline constexpr unsigned char _certs_compressed[] = {{
  19. {methods.format_buffer(buffer, 1)}
  20. }};
  21. """)
  22. def make_authors_header(target, source, env):
  23. SECTIONS = {
  24. "Project Founders": "AUTHORS_FOUNDERS",
  25. "Lead Developer": "AUTHORS_LEAD_DEVELOPERS",
  26. "Project Manager": "AUTHORS_PROJECT_MANAGERS",
  27. "Developers": "AUTHORS_DEVELOPERS",
  28. }
  29. buffer = methods.get_buffer(str(source[0]))
  30. reading = False
  31. with methods.generated_wrapper(str(target[0])) as file:
  32. def close_section():
  33. file.write("\tnullptr,\n};\n\n")
  34. for line in buffer.decode().splitlines():
  35. if line.startswith(" ") and reading:
  36. file.write(f'\t"{methods.to_escaped_cstring(line).strip()}",\n')
  37. elif line.startswith("## "):
  38. if reading:
  39. close_section()
  40. reading = False
  41. section = SECTIONS[line[3:].strip()]
  42. if section:
  43. file.write(f"inline constexpr const char *{section}[] = {{\n")
  44. reading = True
  45. if reading:
  46. close_section()
  47. def make_donors_header(target, source, env):
  48. SECTIONS = {
  49. "Patrons": "DONORS_PATRONS",
  50. "Platinum sponsors": "DONORS_SPONSORS_PLATINUM",
  51. "Gold sponsors": "DONORS_SPONSORS_GOLD",
  52. "Silver sponsors": "DONORS_SPONSORS_SILVER",
  53. "Diamond members": "DONORS_MEMBERS_DIAMOND",
  54. "Titanium members": "DONORS_MEMBERS_TITANIUM",
  55. "Platinum members": "DONORS_MEMBERS_PLATINUM",
  56. "Gold members": "DONORS_MEMBERS_GOLD",
  57. }
  58. buffer = methods.get_buffer(str(source[0]))
  59. reading = False
  60. with methods.generated_wrapper(str(target[0])) as file:
  61. def close_section():
  62. file.write("\tnullptr,\n};\n\n")
  63. for line in buffer.decode().splitlines():
  64. if line.startswith(" ") and reading:
  65. file.write(f'\t"{methods.to_escaped_cstring(line).strip()}",\n')
  66. elif line.startswith("## "):
  67. if reading:
  68. close_section()
  69. reading = False
  70. section = SECTIONS.get(line[3:].strip())
  71. if section:
  72. file.write(f"inline constexpr const char *{section}[] = {{\n")
  73. reading = True
  74. if reading:
  75. close_section()
  76. def make_license_header(target, source, env):
  77. src_copyright = str(source[0])
  78. src_license = str(source[1])
  79. class LicenseReader:
  80. def __init__(self, license_file: TextIOWrapper):
  81. self._license_file = license_file
  82. self.line_num = 0
  83. self.current = self.next_line()
  84. def next_line(self):
  85. line = self._license_file.readline()
  86. self.line_num += 1
  87. while line.startswith("#"):
  88. line = self._license_file.readline()
  89. self.line_num += 1
  90. self.current = line
  91. return line
  92. def next_tag(self):
  93. if ":" not in self.current:
  94. return ("", [])
  95. tag, line = self.current.split(":", 1)
  96. lines = [line.strip()]
  97. while self.next_line() and self.current.startswith(" "):
  98. lines.append(self.current.strip())
  99. return (tag, lines)
  100. projects = OrderedDict()
  101. license_list = []
  102. with open(src_copyright, "r", encoding="utf-8") as copyright_file:
  103. reader = LicenseReader(copyright_file)
  104. part = {}
  105. while reader.current:
  106. tag, content = reader.next_tag()
  107. if tag in ("Files", "Copyright", "License"):
  108. part[tag] = content[:]
  109. elif tag == "Comment" and part:
  110. # attach non-empty part to named project
  111. projects[content[0]] = projects.get(content[0], []) + [part]
  112. if not tag or not reader.current:
  113. # end of a paragraph start a new part
  114. if "License" in part and "Files" not in part:
  115. # no Files tag in this one, so assume standalone license
  116. license_list.append(part["License"])
  117. part = {}
  118. reader.next_line()
  119. data_list = []
  120. for project in iter(projects.values()):
  121. for part in project:
  122. part["file_index"] = len(data_list)
  123. data_list += part["Files"]
  124. part["copyright_index"] = len(data_list)
  125. data_list += part["Copyright"]
  126. with open(src_license, "r", encoding="utf-8") as file:
  127. license_text = file.read()
  128. with methods.generated_wrapper(str(target[0])) as file:
  129. file.write(f"""\
  130. inline constexpr const char *GODOT_LICENSE_TEXT = {{
  131. {methods.to_raw_cstring(license_text)}
  132. }};
  133. struct ComponentCopyrightPart {{
  134. const char *license;
  135. const char *const *files;
  136. const char *const *copyright_statements;
  137. int file_count;
  138. int copyright_count;
  139. }};
  140. struct ComponentCopyright {{
  141. const char *name;
  142. const ComponentCopyrightPart *parts;
  143. int part_count;
  144. }};
  145. """)
  146. file.write("inline constexpr const char *COPYRIGHT_INFO_DATA[] = {\n")
  147. for line in data_list:
  148. file.write(f'\t"{methods.to_escaped_cstring(line)}",\n')
  149. file.write("};\n\n")
  150. file.write("inline constexpr ComponentCopyrightPart COPYRIGHT_PROJECT_PARTS[] = {\n")
  151. part_index = 0
  152. part_indexes = {}
  153. for project_name, project in iter(projects.items()):
  154. part_indexes[project_name] = part_index
  155. for part in project:
  156. file.write(
  157. f'\t{{ "{methods.to_escaped_cstring(part["License"][0])}", '
  158. + f"&COPYRIGHT_INFO_DATA[{part['file_index']}], "
  159. + f"&COPYRIGHT_INFO_DATA[{part['copyright_index']}], "
  160. + f"{len(part['Files'])}, {len(part['Copyright'])} }},\n"
  161. )
  162. part_index += 1
  163. file.write("};\n\n")
  164. file.write(f"inline constexpr int COPYRIGHT_INFO_COUNT = {len(projects)};\n")
  165. file.write("inline constexpr ComponentCopyright COPYRIGHT_INFO[] = {\n")
  166. for project_name, project in iter(projects.items()):
  167. file.write(
  168. f'\t{{ "{methods.to_escaped_cstring(project_name)}", '
  169. + f"&COPYRIGHT_PROJECT_PARTS[{part_indexes[project_name]}], "
  170. + f"{len(project)} }},\n"
  171. )
  172. file.write("};\n\n")
  173. file.write(f"inline constexpr int LICENSE_COUNT = {len(license_list)};\n")
  174. file.write("inline constexpr const char *LICENSE_NAMES[] = {\n")
  175. for license in license_list:
  176. file.write(f'\t"{methods.to_escaped_cstring(license[0])}",\n')
  177. file.write("};\n\n")
  178. file.write("inline constexpr const char *LICENSE_BODIES[] = {\n\n")
  179. for license in license_list:
  180. to_raw = []
  181. for line in license[1:]:
  182. if line == ".":
  183. to_raw += [""]
  184. else:
  185. to_raw += [line]
  186. file.write(f"{methods.to_raw_cstring(to_raw)},\n\n")
  187. file.write("};\n\n")