library.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. from .schema import *
  2. from .writer import Writer
  3. from .enum import gen_enum
  4. from .struct import gen_struct
  5. from .function import gen_function
  6. from .converters import is_vmath_type
  7. from typing import TYPE_CHECKING
  8. if TYPE_CHECKING:
  9. from .meta import Header
  10. class Library:
  11. def __init__(self, name: str) -> None:
  12. self.name = name
  13. # ['defines', 'structs', 'aliases', 'enums', 'callbacks', 'functions']
  14. self.structs = [] # type: list[Struct]
  15. self.aliases = [] # type: list[Alias]
  16. self.enums = [] # type: list[Enum]
  17. self.functions = [] # type: list[Function]
  18. self.callbacks = set() # type: set[str]
  19. def build(self, *, glue_dir='.', stub_dir='.', includes: list[str] | None = None):
  20. self.remove_unsupported()
  21. w, pyi_w = Writer(), Writer()
  22. pyi_w.write('from vmath import vec2, vec3, vec2i, vec3i, mat3x3, color32')
  23. pyi_w.write('from typing import overload')
  24. pyi_w.write('intptr = int')
  25. pyi_w.write('')
  26. w.write('#include "pocketpy.h"')
  27. w.write(f'#include "string.h"')
  28. if includes:
  29. for include in includes:
  30. w.write(f'#include "{include}"')
  31. w.write('')
  32. w.write('#define ADD_ENUM(name) py_newint(py_emplacedict(mod, py_name(#name)), name)')
  33. w.write('')
  34. w.write('static bool struct__address__(int argc, py_Ref argv) {')
  35. w.indent()
  36. w.write('PY_CHECK_ARGC(1);')
  37. w.write('void* ud = py_touserdata(argv);')
  38. w.write('py_newint(py_retval(), (py_i64)ud);')
  39. w.write('return true;')
  40. w.dedent()
  41. w.write('}')
  42. w.write('')
  43. for alias in self.aliases:
  44. w.write(f'#define tp_user_{alias.name} tp_user_{alias.type}')
  45. w.write('')
  46. reg_exprs = [
  47. gen_struct(w, pyi_w, struct)
  48. for struct in self.structs
  49. ]
  50. w.write('/* functions */')
  51. for function in self.functions:
  52. gen_function(w, pyi_w, function)
  53. w.write(f'void py__add_module_{self.name}() {{')
  54. w.indent()
  55. w.write(f'py_GlobalRef mod = py_newmodule("{self.name}");')
  56. w.write('/* structs */')
  57. for reg_expr in reg_exprs:
  58. w.write(reg_expr)
  59. w.write('/* aliases */')
  60. pyi_w.write('# aliases')
  61. for alias in self.aliases:
  62. w.write(f'py_setdict(mod, py_name("{alias.name}"), py_getdict(mod, py_name("{alias.type}")));')
  63. pyi_w.write(f'{alias.name} = {alias.type}')
  64. w.write('/* functions */')
  65. for function in self.functions:
  66. w.write(f'py_bindfunc(mod, "{function.name}", &cfunc__{function.name});')
  67. w.write('/* enums */')
  68. pyi_w.write('# enums')
  69. for enum in self.enums:
  70. gen_enum(w, pyi_w, enum)
  71. w.dedent()
  72. w.write('}')
  73. with open(f'{glue_dir}/{self.name}.c', 'w') as f:
  74. f.write(str(w))
  75. with open(f'{stub_dir}/{self.name}.pyi', 'w') as f:
  76. f.write(str(pyi_w))
  77. def remove_unsupported(self):
  78. functions = []
  79. for f in self.functions:
  80. if f.params and f.params[-1].type == '...':
  81. print('[WARN]', f.signature(), 'is variadic')
  82. continue
  83. for p in f.params:
  84. if p.type in self.callbacks:
  85. print('[WARN]', f.signature(), 'has callback param')
  86. break
  87. else:
  88. functions.append(f)
  89. self.functions.clear()
  90. self.functions.extend(functions)
  91. @staticmethod
  92. def from_raylib(data: dict):
  93. self = Library('raylib')
  94. for struct in data['structs']:
  95. name = struct['name']
  96. if is_vmath_type(name):
  97. print(f'[INFO] {name} is a vmath type, skipping')
  98. continue
  99. self.structs.append(Struct(
  100. name=struct['name'],
  101. desc=struct['description'],
  102. fields=[StructField(
  103. type=field['type'],
  104. name=field['name'],
  105. desc=field['description']
  106. ) for field in struct['fields']]
  107. ))
  108. for alias in data['aliases']:
  109. self.aliases.append(Alias(
  110. type=alias['type'],
  111. name=alias['name'],
  112. desc=alias['description']
  113. ))
  114. for enum in data['enums']:
  115. self.enums.append(Enum(
  116. name=enum['name'],
  117. desc=enum['description'],
  118. values=[EnumValue(
  119. name=value['name'],
  120. value=value['value'],
  121. desc=value['description']
  122. ) for value in enum['values']]
  123. ))
  124. for function in data['functions']:
  125. self.functions.append(Function(
  126. name=function['name'],
  127. desc=function['description'],
  128. params=[FunctionParam(
  129. type=param['type'],
  130. name=param['name']
  131. ) for param in function['params']
  132. ] if 'params' in function else [],
  133. ret_type=function['returnType']
  134. ))
  135. for callback in data['callbacks']:
  136. self.callbacks.add(callback['name'])
  137. return self
  138. @staticmethod
  139. def from_header(name: str, header: 'Header'):
  140. from ffigen.meta import schema
  141. self = Library(name)
  142. for type in header.types:
  143. if isinstance(type, schema.NamedFields):
  144. if type.is_opaque():
  145. continue
  146. else:
  147. fields = type.fields
  148. assert fields is not None
  149. self.structs.append(Struct(
  150. name=type.name,
  151. fields=[StructField(
  152. type=field_type,
  153. name=field_name
  154. ) for field_name, field_type in fields.items()]
  155. ))
  156. elif isinstance(type, schema.Enum):
  157. self.enums.append(Enum(
  158. name=type.name,
  159. values=[EnumValue(
  160. name=value,
  161. value=None
  162. ) for value in type.values]
  163. ))
  164. for k, v in header.type_aliases.items():
  165. self.aliases.append(Alias(
  166. name=k,
  167. type=v
  168. ))
  169. for function in header.functions:
  170. self.functions.append(Function(
  171. name=function.name,
  172. params=[FunctionParam(
  173. type=param[0],
  174. name=param[1] or f'_{i}'
  175. ) for i, param in enumerate(function.args)],
  176. ret_type=function.ret
  177. ))
  178. return self