library.py 7.3 KB

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