rename_api.py 7.9 KB

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