core_builders.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. """Functions used to generate source files during build time"""
  2. import zlib
  3. def escape_string(s):
  4. def charcode_to_c_escapes(c):
  5. rev_result = []
  6. while c >= 256:
  7. c, low = (c // 256, c % 256)
  8. rev_result.append("\\%03o" % low)
  9. rev_result.append("\\%03o" % c)
  10. return "".join(reversed(rev_result))
  11. result = ""
  12. if isinstance(s, str):
  13. s = s.encode("utf-8")
  14. for c in s:
  15. if not (32 <= c < 127) or c in (ord("\\"), ord('"')):
  16. result += charcode_to_c_escapes(c)
  17. else:
  18. result += chr(c)
  19. return result
  20. def make_certs_header(target, source, env):
  21. src = str(source[0])
  22. dst = str(target[0])
  23. with open(src, "rb") as f, open(dst, "w", encoding="utf-8", newline="\n") as g:
  24. buf = f.read()
  25. decomp_size = len(buf)
  26. # Use maximum zlib compression level to further reduce file size
  27. # (at the cost of initial build times).
  28. buf = zlib.compress(buf, zlib.Z_BEST_COMPRESSION)
  29. g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
  30. g.write("#ifndef CERTS_COMPRESSED_GEN_H\n")
  31. g.write("#define CERTS_COMPRESSED_GEN_H\n")
  32. # System certs path. Editor will use them if defined. (for package maintainers)
  33. path = env["system_certs_path"]
  34. g.write('#define _SYSTEM_CERTS_PATH "%s"\n' % str(path))
  35. if env["builtin_certs"]:
  36. # Defined here and not in env so changing it does not trigger a full rebuild.
  37. g.write("#define BUILTIN_CERTS_ENABLED\n")
  38. g.write("static const int _certs_compressed_size = " + str(len(buf)) + ";\n")
  39. g.write("static const int _certs_uncompressed_size = " + str(decomp_size) + ";\n")
  40. g.write("static const unsigned char _certs_compressed[] = {\n")
  41. for i in range(len(buf)):
  42. g.write("\t" + str(buf[i]) + ",\n")
  43. g.write("};\n")
  44. g.write("#endif // CERTS_COMPRESSED_GEN_H")
  45. def make_authors_header(target, source, env):
  46. sections = [
  47. "Project Founders",
  48. "Lead Developer",
  49. "Project Manager",
  50. "Developers",
  51. ]
  52. sections_id = [
  53. "AUTHORS_FOUNDERS",
  54. "AUTHORS_LEAD_DEVELOPERS",
  55. "AUTHORS_PROJECT_MANAGERS",
  56. "AUTHORS_DEVELOPERS",
  57. ]
  58. src = str(source[0])
  59. dst = str(target[0])
  60. with open(src, "r", encoding="utf-8") as f, open(dst, "w", encoding="utf-8", newline="\n") as g:
  61. g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
  62. g.write("#ifndef AUTHORS_GEN_H\n")
  63. g.write("#define AUTHORS_GEN_H\n")
  64. reading = False
  65. def close_section():
  66. g.write("\t0\n")
  67. g.write("};\n")
  68. for line in f:
  69. if reading:
  70. if line.startswith(" "):
  71. g.write('\t"' + escape_string(line.strip()) + '",\n')
  72. continue
  73. if line.startswith("## "):
  74. if reading:
  75. close_section()
  76. reading = False
  77. for section, section_id in zip(sections, sections_id):
  78. if line.strip().endswith(section):
  79. current_section = escape_string(section_id)
  80. reading = True
  81. g.write("const char *const " + current_section + "[] = {\n")
  82. break
  83. if reading:
  84. close_section()
  85. g.write("#endif // AUTHORS_GEN_H\n")
  86. def make_donors_header(target, source, env):
  87. sections = [
  88. "Patrons",
  89. "Platinum sponsors",
  90. "Gold sponsors",
  91. "Silver sponsors",
  92. "Diamond members",
  93. "Titanium members",
  94. "Platinum members",
  95. "Gold members",
  96. ]
  97. sections_id = [
  98. "DONORS_PATRONS",
  99. "DONORS_SPONSORS_PLATINUM",
  100. "DONORS_SPONSORS_GOLD",
  101. "DONORS_SPONSORS_SILVER",
  102. "DONORS_MEMBERS_DIAMOND",
  103. "DONORS_MEMBERS_TITANIUM",
  104. "DONORS_MEMBERS_PLATINUM",
  105. "DONORS_MEMBERS_GOLD",
  106. ]
  107. src = str(source[0])
  108. dst = str(target[0])
  109. with open(src, "r", encoding="utf-8") as f, open(dst, "w", encoding="utf-8", newline="\n") as g:
  110. g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
  111. g.write("#ifndef DONORS_GEN_H\n")
  112. g.write("#define DONORS_GEN_H\n")
  113. reading = False
  114. def close_section():
  115. g.write("\t0\n")
  116. g.write("};\n")
  117. for line in f:
  118. if reading >= 0:
  119. if line.startswith(" "):
  120. g.write('\t"' + escape_string(line.strip()) + '",\n')
  121. continue
  122. if line.startswith("## "):
  123. if reading:
  124. close_section()
  125. reading = False
  126. for section, section_id in zip(sections, sections_id):
  127. if line.strip().endswith(section):
  128. current_section = escape_string(section_id)
  129. reading = True
  130. g.write("const char *const " + current_section + "[] = {\n")
  131. break
  132. if reading:
  133. close_section()
  134. g.write("#endif // DONORS_GEN_H\n")
  135. def make_license_header(target, source, env):
  136. src_copyright = str(source[0])
  137. src_license = str(source[1])
  138. dst = str(target[0])
  139. class LicenseReader:
  140. def __init__(self, license_file):
  141. self._license_file = license_file
  142. self.line_num = 0
  143. self.current = self.next_line()
  144. def next_line(self):
  145. line = self._license_file.readline()
  146. self.line_num += 1
  147. while line.startswith("#"):
  148. line = self._license_file.readline()
  149. self.line_num += 1
  150. self.current = line
  151. return line
  152. def next_tag(self):
  153. if ":" not in self.current:
  154. return ("", [])
  155. tag, line = self.current.split(":", 1)
  156. lines = [line.strip()]
  157. while self.next_line() and self.current.startswith(" "):
  158. lines.append(self.current.strip())
  159. return (tag, lines)
  160. from collections import OrderedDict
  161. projects: dict = OrderedDict()
  162. license_list = []
  163. with open(src_copyright, "r", encoding="utf-8") as copyright_file:
  164. reader = LicenseReader(copyright_file)
  165. part = {}
  166. while reader.current:
  167. tag, content = reader.next_tag()
  168. if tag in ("Files", "Copyright", "License"):
  169. part[tag] = content[:]
  170. elif tag == "Comment":
  171. # attach part to named project
  172. projects[content[0]] = projects.get(content[0], []) + [part]
  173. if not tag or not reader.current:
  174. # end of a paragraph start a new part
  175. if "License" in part and "Files" not in part:
  176. # no Files tag in this one, so assume standalone license
  177. license_list.append(part["License"])
  178. part = {}
  179. reader.next_line()
  180. data_list: list = []
  181. for project in iter(projects.values()):
  182. for part in project:
  183. part["file_index"] = len(data_list)
  184. data_list += part["Files"]
  185. part["copyright_index"] = len(data_list)
  186. data_list += part["Copyright"]
  187. with open(dst, "w", encoding="utf-8", newline="\n") as f:
  188. f.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
  189. f.write("#ifndef LICENSE_GEN_H\n")
  190. f.write("#define LICENSE_GEN_H\n")
  191. f.write("const char *const GODOT_LICENSE_TEXT =")
  192. with open(src_license, "r", encoding="utf-8") as license_file:
  193. for line in license_file:
  194. escaped_string = escape_string(line.strip())
  195. f.write('\n\t\t"' + escaped_string + '\\n"')
  196. f.write(";\n\n")
  197. f.write(
  198. "struct ComponentCopyrightPart {\n"
  199. "\tconst char *license;\n"
  200. "\tconst char *const *files;\n"
  201. "\tconst char *const *copyright_statements;\n"
  202. "\tint file_count;\n"
  203. "\tint copyright_count;\n"
  204. "};\n\n"
  205. )
  206. f.write(
  207. "struct ComponentCopyright {\n"
  208. "\tconst char *name;\n"
  209. "\tconst ComponentCopyrightPart *parts;\n"
  210. "\tint part_count;\n"
  211. "};\n\n"
  212. )
  213. f.write("const char *const COPYRIGHT_INFO_DATA[] = {\n")
  214. for line in data_list:
  215. f.write('\t"' + escape_string(line) + '",\n')
  216. f.write("};\n\n")
  217. f.write("const ComponentCopyrightPart COPYRIGHT_PROJECT_PARTS[] = {\n")
  218. part_index = 0
  219. part_indexes = {}
  220. for project_name, project in iter(projects.items()):
  221. part_indexes[project_name] = part_index
  222. for part in project:
  223. f.write(
  224. '\t{ "'
  225. + escape_string(part["License"][0])
  226. + '", '
  227. + "&COPYRIGHT_INFO_DATA["
  228. + str(part["file_index"])
  229. + "], "
  230. + "&COPYRIGHT_INFO_DATA["
  231. + str(part["copyright_index"])
  232. + "], "
  233. + str(len(part["Files"]))
  234. + ", "
  235. + str(len(part["Copyright"]))
  236. + " },\n"
  237. )
  238. part_index += 1
  239. f.write("};\n\n")
  240. f.write("const int COPYRIGHT_INFO_COUNT = " + str(len(projects)) + ";\n")
  241. f.write("const ComponentCopyright COPYRIGHT_INFO[] = {\n")
  242. for project_name, project in iter(projects.items()):
  243. f.write(
  244. '\t{ "'
  245. + escape_string(project_name)
  246. + '", '
  247. + "&COPYRIGHT_PROJECT_PARTS["
  248. + str(part_indexes[project_name])
  249. + "], "
  250. + str(len(project))
  251. + " },\n"
  252. )
  253. f.write("};\n\n")
  254. f.write("const int LICENSE_COUNT = " + str(len(license_list)) + ";\n")
  255. f.write("const char *const LICENSE_NAMES[] = {\n")
  256. for license in license_list:
  257. f.write('\t"' + escape_string(license[0]) + '",\n')
  258. f.write("};\n\n")
  259. f.write("const char *const LICENSE_BODIES[] = {\n\n")
  260. for license in license_list:
  261. for line in license[1:]:
  262. if line == ".":
  263. f.write('\t"\\n"\n')
  264. else:
  265. f.write('\t"' + escape_string(line) + '\\n"\n')
  266. f.write('\t"",\n\n')
  267. f.write("};\n\n")
  268. f.write("#endif // LICENSE_GEN_H\n")