gen_python.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. #!/usr/bin/env python3
  2. """ SoLoud Python wrapper generator """
  3. import soloud_codegen
  4. fo = open("../glue/soloud.py", "w")
  5. #
  6. # from ctypes import *
  7. #
  8. # soloud_dll = CDLL("soloud_x86")
  9. #
  10. # myfunc = soloud_dll.myfunc
  11. # myfunc.argtypes = [c_char_p, c_int, c_double]
  12. # myfunc.restype = c_int
  13. #
  14. C_TO_PY_TYPES = {
  15. "int":"ctypes.c_int",
  16. "void":"None",
  17. "const char *":"ctypes.c_char_p",
  18. "char *":"ctypes.c_char_p",
  19. "unsigned int":"ctypes.c_uint",
  20. "float":"ctypes.c_float",
  21. "double":"ctypes.c_double",
  22. "float *":"ctypes.POINTER(ctypes.c_float * 256)",
  23. "File *":"ctypes.c_void_p",
  24. "unsigned char *":"ctypes.POINTER(ctypes.c_ubyte)",
  25. "const unsigned char *":"ctypes.POINTER(ctypes.c_ubyte)",
  26. "unsigned char":"ctypes.c_ubyte",
  27. "short *":"ctypes.POINTER(ctypes.c_short)"
  28. }
  29. for soloud_type in soloud_codegen.soloud_type:
  30. C_TO_PY_TYPES[soloud_type + " *"] = "ctypes.c_void_p"
  31. def fudge_types(origtype):
  32. """ Map ctypes to parameter types """
  33. return C_TO_PY_TYPES[origtype]
  34. def pythonize_camelcase(origstr):
  35. """ Turns camelCase into underscore_style """
  36. ret = ""
  37. for letter in origstr:
  38. if letter.isupper():
  39. ret += '_' + letter.lower()
  40. else:
  41. ret += letter
  42. # kludge, because calc_f_f_t is silly.
  43. ret = ret.replace("_f_f_t", "_fft")
  44. # kludge for 3d calls
  45. ret = ret.replace("3d", "_3d")
  46. return ret
  47. def has_ex_variant(funcname):
  48. """ Checks if this function has an "Ex" variant """
  49. if funcname[-2::] == "Ex":
  50. # Already an Ex..
  51. return False
  52. for func in soloud_codegen.soloud_func:
  53. if func[1] == (funcname + "Ex"):
  54. return True
  55. return False
  56. fo.write("# SoLoud wrapper for Python\n")
  57. fo.write("# This file is autogenerated; any changes will be overwritten\n")
  58. fo.write("\n")
  59. fo.write('import ctypes\n')
  60. fo.write('import sys\n')
  61. fo.write('\n')
  62. fo.write('try:\n')
  63. fo.write('\tsoloud_dll = ctypes.CDLL("soloud_x86")\n')
  64. fo.write('except:\n')
  65. fo.write('\ttry:\n')
  66. fo.write('\t\tsoloud_dll = ctypes.CDLL("soloud_x64")\n')
  67. fo.write('\texcept:\n')
  68. fo.write('\t\tprint("SoLoud dynamic link library (soloud_x86.dll / soloud_x64.dll on Windows, .so on Linux / OSX) not found. Terminating.")\n')
  69. fo.write('\t\tsys.exit()')
  70. fo.write("\n")
  71. # Since there's no reason to use the "raw" data anymore,
  72. # skip generating the enum dictionary
  73. #
  74. #fo.write("# Enumerations\n")
  75. #fo.write("soloud_enum = {\n")
  76. #first = True
  77. #for x in soloud_codegen.soloud_enum:
  78. # if first:
  79. # first = False
  80. # else:
  81. # fo.write(",\n")
  82. # fo.write('"' + x + '": ' + str(soloud_codegen.soloud_enum[x]))
  83. #fo.write("\n}\n")
  84. fo.write("\n")
  85. fo.write("# Raw DLL functions\n")
  86. for x in soloud_codegen.soloud_func:
  87. fo.write(x[1] + ' = soloud_dll.' + x[1] + '\n')
  88. fo.write(x[1] + '.restype = ' + fudge_types(x[0]) + '\n')
  89. fo.write(x[1] + '.argtypes = [')
  90. first = True
  91. for y in x[2]:
  92. if len(y) > 0:
  93. if first:
  94. first = False
  95. else:
  96. fo.write(", ")
  97. fo.write(fudge_types(y[0]))
  98. fo.write(']\n')
  99. fo.write('\n')
  100. #################################################################
  101. #
  102. # oop
  103. #
  104. # class cname(object):
  105. # def __init__(self):
  106. # self.objhandle = cname_create()
  107. # def __enter__(self):
  108. # return self
  109. # def __exit__(self, eType, eValue, eTrace):
  110. # cname_destroy(self.objhandle)
  111. # return False
  112. # def close(self)
  113. # cname_destroy(self.objhandle)
  114. #
  115. fo.write('# OOP wrappers\n')
  116. def fix_default_param(defparam, classname):
  117. """ 'fixes' default parameters from C to what python expectes """
  118. if defparam == 'false':
  119. defparam = 'False'
  120. if defparam == 'true':
  121. defparam = 'True'
  122. if (classname + '::') == defparam[0:len(classname)+2:]:
  123. return defparam[len(classname)+2::]
  124. if defparam[len(defparam)-1] == "f":
  125. return defparam[0:len(defparam)-1]
  126. return defparam
  127. for x in soloud_codegen.soloud_type:
  128. first = True
  129. for y in soloud_codegen.soloud_func:
  130. if (x + "_") == y[1][0:len(x)+1:]:
  131. if first:
  132. fo.write('\n')
  133. fo.write('class %s(object):\n'%(x))
  134. for z in soloud_codegen.soloud_enum:
  135. if z[0:len(x)+1] == x.upper()+'_':
  136. s = str(soloud_codegen.soloud_enum[z])
  137. fo.write('\t%s = %s\n'%(z[len(x)+1::], s))
  138. fo.write('\tdef __init__(self):\n')
  139. fo.write('\t\tself.objhandle = %s_create()\n'%(x))
  140. fo.write('\tdef __enter__(self):\n')
  141. fo.write('\t\treturn self\n')
  142. fo.write('\tdef __exit__(self, eType, eValue, eTrace):\n')
  143. fo.write('\t\t%s_destroy(self.objhandle)\n'%(x))
  144. fo.write('\t\tself.objhandle = ctypes.c_void_p(0)\n')
  145. fo.write('\t\treturn False\n')
  146. # Not sure which of the "destroy" funcs would be most suitable,
  147. # so let's generate three equally suitable options
  148. fo.write('\tdef close(self):\n')
  149. fo.write('\t\t%s_destroy(self.objhandle)\n'%(x))
  150. fo.write('\t\tself.objhandle = ctypes.c_void_p(0)\n')
  151. fo.write('\tdef destroy(self):\n')
  152. fo.write('\t\t%s_destroy(self.objhandle)\n'%(x))
  153. fo.write('\t\tself.objhandle = ctypes.c_void_p(0)\n')
  154. fo.write('\tdef quit(self):\n')
  155. fo.write('\t\t%s_destroy(self.objhandle)\n'%(x))
  156. fo.write('\t\tself.objhandle = ctypes.c_void_p(0)\n')
  157. first = False
  158. funcname = y[1][len(x)+1::]
  159. # If the function has the name "Ex", remove the subfix
  160. if funcname[-2::] == "Ex":
  161. funcname = funcname[:len(funcname)-2]
  162. # Skip generating functions that have an Ex variant
  163. if funcname == "create" or funcname == "destroy" or has_ex_variant(y[1]):
  164. pass # omit create/destroy, handled by __exit__ / close
  165. else:
  166. fo.write('\tdef %s(self'%(pythonize_camelcase(funcname)))
  167. for z in y[2]:
  168. if len(z) > 1:
  169. if z[1] == 'a'+x:
  170. pass # skip the 'self' pointer
  171. else:
  172. fo.write(', ' + z[1])
  173. if len(z) > 2:
  174. fo.write(' = ' + fix_default_param(z[2], x))
  175. fo.write('):\n')
  176. fo.write('\t\t')
  177. floatbufreturn = False
  178. if y[0] == 'void':
  179. pass
  180. elif y[0] == 'float *':
  181. floatbufreturn = True
  182. fo.write('floatbuf = ')
  183. else:
  184. fo.write('return ')
  185. fo.write(y[1] + '(self.objhandle')
  186. for z in y[2]:
  187. if len(z) > 1:
  188. if z[1] == 'a'+x:
  189. pass # skip the 'self' pointer
  190. else:
  191. fo.write(', ')
  192. fudged_type = fudge_types(z[0])
  193. if fudged_type == 'ctypes.c_void_p':
  194. fo.write(z[1] + '.objhandle')
  195. else:
  196. if fudged_type == 'ctypes.c_char_p':
  197. fo.write(fudged_type + '(' + z[1] + ".encode('utf-8'))")
  198. else:
  199. fo.write(fudged_type + '(' + z[1] + ')')
  200. fo.write(')\n')
  201. if floatbufreturn:
  202. fo.write('\t\treturn [f for f in floatbuf.contents]\n')
  203. print("soloud.py generated")
  204. fo.close()