123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231 |
- #!/usr/bin/env python3
- """ SoLoud Python wrapper generator """
- import soloud_codegen
- fo = open("../glue/soloud.py", "w")
- #
- # from ctypes import *
- #
- # soloud_dll = CDLL("soloud_x86")
- #
- # myfunc = soloud_dll.myfunc
- # myfunc.argtypes = [c_char_p, c_int, c_double]
- # myfunc.restype = c_int
- #
- C_TO_PY_TYPES = {
- "int":"ctypes.c_int",
- "void":"None",
- "const char *":"ctypes.c_char_p",
- "char *":"ctypes.c_char_p",
- "unsigned int":"ctypes.c_uint",
- "float":"ctypes.c_float",
- "double":"ctypes.c_double",
- "float *":"ctypes.POINTER(ctypes.c_float * 256)",
- "File *":"ctypes.c_void_p",
- "unsigned char *":"ctypes.POINTER(ctypes.c_ubyte)",
- "const unsigned char *":"ctypes.POINTER(ctypes.c_ubyte)",
- "unsigned char":"ctypes.c_ubyte",
- "short *":"ctypes.POINTER(ctypes.c_short)"
- }
- for soloud_type in soloud_codegen.soloud_type:
- C_TO_PY_TYPES[soloud_type + " *"] = "ctypes.c_void_p"
- def fudge_types(origtype):
- """ Map ctypes to parameter types """
- return C_TO_PY_TYPES[origtype]
- def pythonize_camelcase(origstr):
- """ Turns camelCase into underscore_style """
- ret = ""
- for letter in origstr:
- if letter.isupper():
- ret += '_' + letter.lower()
- else:
- ret += letter
- # kludge, because calc_f_f_t is silly.
- ret = ret.replace("_f_f_t", "_fft")
- # kludge for 3d calls
- ret = ret.replace("3d", "_3d")
- return ret
- def has_ex_variant(funcname):
- """ Checks if this function has an "Ex" variant """
- if funcname[-2::] == "Ex":
- # Already an Ex..
- return False
- for func in soloud_codegen.soloud_func:
- if func[1] == (funcname + "Ex"):
- return True
- return False
- fo.write("# SoLoud wrapper for Python\n")
- fo.write("# This file is autogenerated; any changes will be overwritten\n")
- fo.write("\n")
- fo.write('import ctypes\n')
- fo.write('import sys\n')
- fo.write('\n')
- fo.write('try:\n')
- fo.write('\tsoloud_dll = ctypes.CDLL("soloud_x86")\n')
- fo.write('except:\n')
- fo.write('\ttry:\n')
- fo.write('\t\tsoloud_dll = ctypes.CDLL("soloud_x64")\n')
- fo.write('\texcept:\n')
- fo.write('\t\tprint("SoLoud dynamic link library (soloud_x86.dll / soloud_x64.dll on Windows, .so on Linux / OSX) not found. Terminating.")\n')
- fo.write('\t\tsys.exit()')
- fo.write("\n")
- # Since there's no reason to use the "raw" data anymore,
- # skip generating the enum dictionary
- #
- #fo.write("# Enumerations\n")
- #fo.write("soloud_enum = {\n")
- #first = True
- #for x in soloud_codegen.soloud_enum:
- # if first:
- # first = False
- # else:
- # fo.write(",\n")
- # fo.write('"' + x + '": ' + str(soloud_codegen.soloud_enum[x]))
- #fo.write("\n}\n")
- fo.write("\n")
- fo.write("# Raw DLL functions\n")
- for x in soloud_codegen.soloud_func:
- fo.write(x[1] + ' = soloud_dll.' + x[1] + '\n')
- fo.write(x[1] + '.restype = ' + fudge_types(x[0]) + '\n')
- fo.write(x[1] + '.argtypes = [')
- first = True
- for y in x[2]:
- if len(y) > 0:
- if first:
- first = False
- else:
- fo.write(", ")
- fo.write(fudge_types(y[0]))
- fo.write(']\n')
- fo.write('\n')
- #################################################################
- #
- # oop
- #
- # class cname(object):
- # def __init__(self):
- # self.objhandle = cname_create()
- # def __enter__(self):
- # return self
- # def __exit__(self, eType, eValue, eTrace):
- # cname_destroy(self.objhandle)
- # return False
- # def close(self)
- # cname_destroy(self.objhandle)
- #
- fo.write('# OOP wrappers\n')
- def fix_default_param(defparam, classname):
- """ 'fixes' default parameters from C to what python expectes """
- if defparam == 'false':
- defparam = 'False'
- if defparam == 'true':
- defparam = 'True'
- if (classname + '::') == defparam[0:len(classname)+2:]:
- return defparam[len(classname)+2::]
- if defparam[len(defparam)-1] == "f":
- return defparam[0:len(defparam)-1]
- return defparam
- for x in soloud_codegen.soloud_type:
- first = True
- for y in soloud_codegen.soloud_func:
- if (x + "_") == y[1][0:len(x)+1:]:
- if first:
- fo.write('\n')
- fo.write('class %s(object):\n'%(x))
- for z in soloud_codegen.soloud_enum:
- if z[0:len(x)+1] == x.upper()+'_':
- s = str(soloud_codegen.soloud_enum[z])
- fo.write('\t%s = %s\n'%(z[len(x)+1::], s))
- fo.write('\tdef __init__(self):\n')
- fo.write('\t\tself.objhandle = %s_create()\n'%(x))
- fo.write('\tdef __enter__(self):\n')
- fo.write('\t\treturn self\n')
- fo.write('\tdef __exit__(self, eType, eValue, eTrace):\n')
- fo.write('\t\t%s_destroy(self.objhandle)\n'%(x))
- fo.write('\t\tself.objhandle = ctypes.c_void_p(0)\n')
- fo.write('\t\treturn False\n')
- # Not sure which of the "destroy" funcs would be most suitable,
- # so let's generate three equally suitable options
- fo.write('\tdef close(self):\n')
- fo.write('\t\t%s_destroy(self.objhandle)\n'%(x))
- fo.write('\t\tself.objhandle = ctypes.c_void_p(0)\n')
- fo.write('\tdef destroy(self):\n')
- fo.write('\t\t%s_destroy(self.objhandle)\n'%(x))
- fo.write('\t\tself.objhandle = ctypes.c_void_p(0)\n')
- fo.write('\tdef quit(self):\n')
- fo.write('\t\t%s_destroy(self.objhandle)\n'%(x))
- fo.write('\t\tself.objhandle = ctypes.c_void_p(0)\n')
- first = False
- funcname = y[1][len(x)+1::]
- # If the function has the name "Ex", remove the subfix
- if funcname[-2::] == "Ex":
- funcname = funcname[:len(funcname)-2]
- # Skip generating functions that have an Ex variant
- if funcname == "create" or funcname == "destroy" or has_ex_variant(y[1]):
- pass # omit create/destroy, handled by __exit__ / close
- else:
- fo.write('\tdef %s(self'%(pythonize_camelcase(funcname)))
- for z in y[2]:
- if len(z) > 1:
- if z[1] == 'a'+x:
- pass # skip the 'self' pointer
- else:
- fo.write(', ' + z[1])
- if len(z) > 2:
- fo.write(' = ' + fix_default_param(z[2], x))
- fo.write('):\n')
- fo.write('\t\t')
- floatbufreturn = False
- if y[0] == 'void':
- pass
- elif y[0] == 'float *':
- floatbufreturn = True
- fo.write('floatbuf = ')
- else:
- fo.write('return ')
- fo.write(y[1] + '(self.objhandle')
- for z in y[2]:
- if len(z) > 1:
- if z[1] == 'a'+x:
- pass # skip the 'self' pointer
- else:
- fo.write(', ')
- fudged_type = fudge_types(z[0])
- if fudged_type == 'ctypes.c_void_p':
- fo.write(z[1] + '.objhandle')
- else:
- if fudged_type == 'ctypes.c_char_p':
- fo.write(fudged_type + '(' + z[1] + ".encode('utf-8'))")
- else:
- fo.write(fudged_type + '(' + z[1] + ')')
- fo.write(')\n')
- if floatbufreturn:
- fo.write('\t\treturn [f for f in floatbuf.contents]\n')
- print("soloud.py generated")
- fo.close()
|