rename_api.py 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. #!/usr/bin/env python3
  2. #
  3. # This script renames symbols in the API, updating SDL_oldnames.h and
  4. # adding documentation for the change.
  5. import argparse
  6. import os
  7. import pathlib
  8. import pprint
  9. import re
  10. import sys
  11. from rename_symbols import create_regex_from_replacements, replace_symbols_in_path
  12. SDL_ROOT = pathlib.Path(__file__).resolve().parents[1]
  13. SDL_INCLUDE_DIR = SDL_ROOT / "include/SDL3"
  14. def main():
  15. if len(args.args) == 0 or (len(args.args) % 2) != 0:
  16. print("Usage: %s [-h] [--skip-header-check] header {enum,function,hint,macro,structure,symbol} [old new ...]" % sys.argv[0])
  17. exit(1)
  18. # Check whether we can still modify the ABI
  19. version_header = pathlib.Path( SDL_INCLUDE_DIR / "SDL_version.h" ).read_text()
  20. if not re.search("SDL_MINOR_VERSION\s+[01]\s", version_header):
  21. raise Exception("ABI is frozen, symbols cannot be renamed")
  22. # Find the symbol in the headers
  23. if pathlib.Path(args.header).is_file():
  24. header = pathlib.Path(args.header)
  25. else:
  26. header = pathlib.Path(SDL_INCLUDE_DIR / args.header)
  27. if not header.exists():
  28. raise Exception("Couldn't find header %s" % header)
  29. header_text = header.read_text()
  30. # Replace the symbols in source code
  31. replacements = {}
  32. i = 0
  33. while i < len(args.args):
  34. oldname = args.args[i + 0]
  35. newname = args.args[i + 1]
  36. if not args.skip_header_check and not re.search((r"\b%s\b" % oldname), header_text):
  37. raise Exception("Couldn't find %s in %s" % (oldname, header))
  38. replacements[ oldname ] = newname
  39. replacements[ oldname + "_REAL" ] = newname + "_REAL"
  40. i += 2
  41. regex = create_regex_from_replacements(replacements)
  42. for dir in ["src", "test", "include", "docs", "cmake/test", "Xcode-iOS/Demos"]:
  43. replace_symbols_in_path(SDL_ROOT / dir, regex, replacements)
  44. # Replace the symbols in documentation
  45. i = 0
  46. while i < len(args.args):
  47. oldname = args.args[i + 0]
  48. newname = args.args[i + 1]
  49. add_symbol_to_oldnames(header.name, oldname, newname)
  50. add_symbol_to_migration(header.name, args.type, oldname, newname)
  51. add_symbol_to_whatsnew(args.type, oldname, newname)
  52. i += 2
  53. def add_line(lines, i, section):
  54. lines.insert(i, section)
  55. i += 1
  56. return i
  57. def add_content(lines, i, content, add_trailing_line):
  58. if lines[i - 1] == "":
  59. lines[i - 1] = content
  60. else:
  61. i = add_line(lines, i, content)
  62. if add_trailing_line:
  63. i = add_line(lines, i, "")
  64. return i
  65. def add_symbol_to_oldnames(header, oldname, newname):
  66. file = (SDL_INCLUDE_DIR / "SDL_oldnames.h")
  67. lines = file.read_text().splitlines()
  68. mode = 0
  69. i = 0
  70. while i < len(lines):
  71. line = lines[i]
  72. if line == "#ifdef SDL_ENABLE_OLD_NAMES":
  73. if mode == 0:
  74. mode = 1
  75. section = ("/* ##%s */" % header)
  76. section_added = False
  77. content = ("#define %s %s" % (oldname, newname))
  78. content_added = False
  79. else:
  80. raise Exception("add_symbol_to_oldnames(): expected mode 0")
  81. elif line == "#else /* !SDL_ENABLE_OLD_NAMES */":
  82. if mode == 1:
  83. if not section_added:
  84. i = add_line(lines, i, section)
  85. if not content_added:
  86. i = add_content(lines, i, content, True)
  87. mode = 2
  88. section = ("/* ##%s */" % header)
  89. section_added = False
  90. content = ("#define %s %s_renamed_%s" % (oldname, oldname, newname))
  91. content_added = False
  92. else:
  93. raise Exception("add_symbol_to_oldnames(): expected mode 1")
  94. elif line == "#endif /* SDL_ENABLE_OLD_NAMES */":
  95. if mode == 2:
  96. if not section_added:
  97. i = add_line(lines, i, section)
  98. if not content_added:
  99. i = add_content(lines, i, content, True)
  100. mode = 3
  101. else:
  102. raise Exception("add_symbol_to_oldnames(): expected mode 2")
  103. elif line != "" and (mode == 1 or mode == 2):
  104. if line.startswith("/* ##"):
  105. if section_added:
  106. if not content_added:
  107. i = add_content(lines, i, content, True)
  108. content_added = True
  109. elif line == section:
  110. section_added = True
  111. elif section < line:
  112. i = add_line(lines, i, section)
  113. section_added = True
  114. i = add_content(lines, i, content, True)
  115. content_added = True
  116. elif line != "" and section_added and not content_added:
  117. if content == line:
  118. content_added = True
  119. elif content < line:
  120. i = add_content(lines, i, content, False)
  121. content_added = True
  122. i += 1
  123. file.write_text("\n".join(lines) + "\n")
  124. def add_symbol_to_migration(header, symbol_type, oldname, newname):
  125. file = (SDL_ROOT / "docs/README-migration.md")
  126. lines = file.read_text().splitlines()
  127. section = ("## %s" % header)
  128. section_added = False
  129. note = ("The following %ss have been renamed:" % symbol_type)
  130. note_added = False
  131. content = ("* %s => %s" % (oldname, newname))
  132. content_added = False
  133. mode = 0
  134. i = 0
  135. while i < len(lines):
  136. line = lines[i]
  137. if line.startswith("##") and line.endswith(".h"):
  138. if line == section:
  139. section_added = True
  140. elif section < line:
  141. break
  142. elif section_added and not note_added:
  143. if note == line:
  144. note_added = True
  145. elif note_added and not content_added:
  146. if content == line:
  147. content_added = True
  148. elif line == "" or content < line:
  149. i = add_line(lines, i, content)
  150. content_added = True
  151. i += 1
  152. if not section_added:
  153. i = add_line(lines, i, section)
  154. i = add_line(lines, i, "")
  155. if not note_added:
  156. i = add_line(lines, i, note)
  157. if not content_added:
  158. i = add_content(lines, i, content, True)
  159. file.write_text("\n".join(lines) + "\n")
  160. def add_symbol_to_whatsnew(symbol_type, oldname, newname):
  161. file = (SDL_ROOT / "WhatsNew.txt")
  162. lines = file.read_text().splitlines()
  163. note = ("* The following %ss have been renamed:" % symbol_type)
  164. note_added = False
  165. content = (" * %s => %s" % (oldname, newname))
  166. content_added = False
  167. mode = 0
  168. i = 0
  169. while i < len(lines):
  170. line = lines[i]
  171. if not note_added:
  172. if note == line:
  173. note_added = True
  174. elif not content_added:
  175. if content == line:
  176. content_added = True
  177. elif not line.startswith(" *") or content < line:
  178. i = add_line(lines, i, content)
  179. content_added = True
  180. i += 1
  181. if not note_added:
  182. i = add_line(lines, i, note)
  183. if not content_added:
  184. i = add_line(lines, i, content)
  185. file.write_text("\r\n".join(lines) + "\r\n")
  186. if __name__ == "__main__":
  187. parser = argparse.ArgumentParser(fromfile_prefix_chars='@')
  188. parser.add_argument("--skip-header-check", action="store_true")
  189. parser.add_argument("header");
  190. parser.add_argument("type", choices=["enum", "function", "hint", "macro", "structure", "symbol"]);
  191. parser.add_argument("args", nargs="*")
  192. args = parser.parse_args()
  193. try:
  194. main()
  195. except Exception as e:
  196. print(e)
  197. exit(-1)
  198. exit(0)