core_builders.py 10 KB

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