"""
import sys
+import os
import re
import time
import operator
forward_re=re.compile('.+\(\s*(.+?)\s*\)(\s*\S+)')
enum_re=re.compile('typedef\s+(enum)\s*(\S+\s*)?\{\s*(.+)\s*\}\s*(\S+);')
special_enum_re=re.compile('^(enum)\s*(\S+\s*)?\{\s*(.+)\s*\};')
+event_def_re=re.compile('^DEF\(\s*(\w+)\s*\)')
# Definition of parameter passing mode for types. This should not be
# hardcoded this way, but works alright ATM.
with type == 'enum' (for the moment) and value_list being a list of (name, value)
Note that values are string, since this is intended for code generation.
"""
+ event_names=[]
+
f=open(name, 'r')
accumulator=''
for l in f:
comment=''
continue
+ # Special case, used only for libvlc_events.h
+ # (version after 96a96f60bb0d1f2506e68b356897ceca6f6b586d)
+ m=event_def_re.match(l)
+ if m:
+ # Event definition.
+ event_names.append('libvlc_'+m.group(1))
+ continue
+
# Special case, used only for libvlc_events.h
m=special_enum_re.match(l)
if m:
- values=[]
(typ, name, data)=m.groups()
- for i, l in enumerate(paramlist_re.split(data)):
- l=l.strip()
- if l.startswith('/*') or l.startswith('#'):
- continue
- if '=' in l:
- # A value was specified. Use it.
- values.append(re.split('\s*=\s*', l))
- else:
- if l:
- values.append( (l, str(i)) )
+ if event_names:
+ # event_names were defined through DEF macro
+ # (see 96a96f60bb0d1f2506e68b356897ceca6f6b586d)
+ values=list( (n, str(i)) for i, n in enumerate(event_names))
+ else:
+ # Before 96a96f60bb0d1f2506e68b356897ceca6f6b586d
+ values=[]
+ for i, l in enumerate(paramlist_re.split(data)):
+ l=l.strip()
+ if l.startswith('/*') or l.startswith('#'):
+ continue
+ if '=' in l:
+ # A value was specified. Use it.
+ values.append(re.split('\s*=\s*', l))
+ else:
+ if l:
+ values.append( (l, str(i)) )
comment=comment.replace('@{', '').replace('@see', 'See').replace('\ingroup', '')
yield (typ, name.strip(), values, comment)
comment=''
'libvlc_media_t*': 'Media',
'libvlc_log_t*': 'Log',
'libvlc_log_iterator_t*': 'LogIterator',
- 'libvlc_log_message_t*': 'LogMessage',
+ 'libvlc_log_message_t*': 'ctypes.POINTER(LogMessage)',
'libvlc_event_type_t': 'ctypes.c_uint',
'libvlc_event_manager_t*': 'EventManager',
'libvlc_media_discoverer_t*': 'MediaDiscoverer',
'libvlc_media_list_view_t*': 'MediaListView',
'libvlc_track_description_t*': 'TrackDescription',
'libvlc_audio_output_t*': 'AudioOutput',
+ 'libvlc_media_stats_t*': 'ctypes.POINTER(MediaStats)',
'mediacontrol_Instance*': 'MediaControl',
'mediacontrol_Exception*': 'MediaControlException',
self.fd=open(filename, 'w')
self.insert_code('header.py')
- self.generate_enums(self.parser.enums)
wrapped_methods=self.generate_wrappers(self.parser.methods)
for l in self.parser.methods:
self.output_ctypes(*l)
self.output("# Not wrapped methods:")
for m in not_wrapped:
self.output("# ", m)
-
+
if self.fd != sys.stdout:
self.fd.close()
"""
f=open(filename, 'r')
for l in f:
- if 'build_date' in l:
+ if l.startswith('build_date'):
self.output('build_date="%s"' % time.ctime())
+ elif l.startswith('# GENERATED_ENUMS'):
+ self.generate_enums(self.parser.enums)
else:
self.output(l.rstrip())
+
f.close()
def convert_enum_names(self, enums):
raise Exception('This method only handles enums')
pyname=self.type2class[name]
- self.output("class %s(ctypes.c_uint):" % pyname)
+ self.output("class %s(ctypes.c_ulong):" % pyname)
self.output(' """%s\n """' % comment)
conv={}
n='_'+n
conv[k]=n
- for k, v in values:
- self.output(" %s=%s" % (conv[k], v))
-
self.output(" _names={")
for k, v in values:
self.output(" %s: '%s'," % (v, conv[k]))
self.output("""
def __repr__(self):
return ".".join((self.__class__.__module__, self.__class__.__name__, self._names[self.value]))
+
+ def __eq__(self, other):
+ return ( (isinstance(other, ctypes.c_ulong) and self.value == other.value)
+ or (isinstance(other, (int, long)) and self.value == other ) )
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
""")
+ for k, v in values:
+ self.output("%(class)s.%(attribute)s=%(class)s(%(value)s)" % {
+ 'class': pyname,
+ 'attribute': conv[k],
+ 'value': v
+ })
+ self.output("")
def output_ctypes(self, rtype, method, params, comment):
"""Output ctypes decorator for the given method.
self.output("""if hasattr(dll, '%s'):""" % method)
if params:
self.output(" prototype=ctypes.CFUNCTYPE(%s, %s)" % (self.type2class.get(rtype, 'FIXME_%s' % rtype),
- ",".join( self.type2class[p[0]] for p in params )))
+ ", ".join( self.type2class[p[0]] for p in params )))
else:
self.output(" prototype=ctypes.CFUNCTYPE(%s)" % self.type2class.get(rtype, 'FIXME_%s' % rtype))
elif len(params) == 1:
flags=" paramflags=( (%d, ), )" % parameter_passing[params[0][0]]
else:
- flags=" paramflags=%s" % ",".join( '(%d,)' % parameter_passing[p[0]] for p in params )
+ flags=" paramflags=%s" % ", ".join( '(%d,)' % parameter_passing[p[0]] for p in params )
self.output(flags)
self.output(' %s = prototype( ("%s", dll), paramflags )' % (method, method))
if '3' in flags:
if classname in docstring:
self.output(' """%s\n """' % docstring[classname])
- self.output("""
+ if not 'def __new__' in overrides.get(classname, ''):
+ self.output("""
def __new__(cls, pointer=None):
'''Internal method used for instanciating wrappers from ctypes.
'''
o=object.__new__(cls)
o._as_parameter_=ctypes.c_void_p(pointer)
return o
+""")
+ self.output("""
@staticmethod
def from_param(arg):
'''(INTERNAL) ctypes parameter conversion method.
'''
return arg._as_parameter_
- """ % {'name': classname})
+""")
if classname in overrides:
self.output(overrides[classname])
# Check for standard methods
if name == 'count':
# There is a count method. Generate a __len__ one.
- self.output(""" def __len__(self):
+ if params and params[-1][0] == 'libvlc_exception_t*':
+ self.output(""" def __len__(self):
e=VLCException()
return %s(self, e)
+""" % method)
+ else:
+ # No exception
+ self.output(""" def __len__(self):
+ return %s(self)
""" % method)
elif name.endswith('item_at_index'):
# Indexable (and thus iterable)"
""" % method)
return ret
+class JavaGenerator(object):
+ # C-type to java/jna type conversion.
+ # Note that enum types conversions are generated (cf convert_enum_names)
+ type2class={
+ 'libvlc_exception_t*': 'libvlc_exception_t',
+ 'libvlc_media_player_t*': 'LibVlcMediaPlayer',
+ 'libvlc_instance_t*': 'LibVlcInstance',
+ 'libvlc_media_t*': 'LibVlcMedia',
+ 'libvlc_log_t*': 'LibVlcLog',
+ 'libvlc_log_iterator_t*': 'LibVlcLogIterator',
+ 'libvlc_log_message_t*': 'libvlc_log_message_t',
+ 'libvlc_event_type_t': 'int',
+ 'libvlc_event_manager_t*': 'LibVlcEventManager',
+ 'libvlc_media_discoverer_t*': 'LibVlcMediaDiscoverer',
+ 'libvlc_media_library_t*': 'LibVlcMediaLibrary',
+ 'libvlc_media_list_t*': 'LibVlcMediaList',
+ 'libvlc_media_list_player_t*': 'LibVlcMediaListPlayer',
+ 'libvlc_media_list_view_t*': 'LibVlcMediaListView',
+ 'libvlc_media_stats_t*': 'LibVlcMediaStats',
+
+ 'libvlc_track_description_t*': 'LibVlcTrackDescription',
+ 'libvlc_audio_output_t*': 'LibVlcAudioOutput',
+
+ 'void': 'void',
+ 'void*': 'Pointer',
+ 'short': 'short',
+ 'char*': 'String',
+ 'char**': 'String[]',
+ 'uint32_t': 'uint32',
+ 'float': 'float',
+ 'unsigned': 'int',
+ 'int': 'int',
+ '...': 'FIXMEva_list',
+ 'libvlc_callback_t': 'LibVlcCallback',
+ 'libvlc_time_t': 'long',
+
+ 'mediacontrol_RGBPicture*': 'Pointer',
+ 'mediacontrol_PlaylistSeq*': 'Pointer',
+ 'mediacontrol_StreamInformation*': 'Pointer',
+ }
+
+ def __init__(self, parser=None):
+ self.parser=parser
+
+ # Blacklist all mediacontrol methods
+ for (rt, met, params, c) in self.parser.methods:
+ if met.startswith('mediacontrol'):
+ blacklist.append(met)
+ # Generate Java names for enums
+ self.type2class.update(self.convert_enum_names(parser.enums))
+ self.check_types()
+
+ def save(self, dirname=None):
+ if dirname is None or dirname == '-':
+ dirname='internal'
+ if not os.path.isdir(dirname):
+ os.mkdir(dirname)
+
+ print "Generating java code in %s/" % dirname
+
+ # Generate enum files
+ self.generate_enums(dirname, self.parser.enums)
+
+ # Generate LibVlc.java code
+ self.generate_libvlc(dirname)
+
+ def output(self, fd, *p):
+ fd.write(" ".join(p))
+ fd.write("\n")
+
+ def check_types(self):
+ """Make sure that all types are properly translated.
+
+ This method must be called *after* convert_enum_names, since
+ the latter populates type2class with converted enum names.
+ """
+ for (rt, met, params, c) in self.parser.methods:
+ if met in blacklist:
+ continue
+ for typ, name in params:
+ if not typ in self.type2class:
+ raise Exception("No conversion for %s (from %s:%s)" % (typ, met, name))
+
+ def convert_enum_names(self, enums):
+ """Convert enum names into Java names.
+ """
+ res={}
+ for (typ, name, values, comment) in enums:
+ if typ != 'enum':
+ raise Exception('This method only handles enums')
+ pyname=re.findall('(libvlc|mediacontrol)_(.+?)(_t)?$', name)[0][1]
+ if '_' in pyname:
+ pyname=pyname.title().replace('_', '')
+ elif not pyname[0].isupper():
+ pyname=pyname.capitalize()
+ res[name]=pyname
+ return res
+
+ def insert_code(self, fd, filename):
+ """Generate header/footer code.
+ """
+ f=open(filename, 'r')
+ for l in f:
+ if l.startswith('build_date'):
+ self.output(fd, 'build_date="%s";' % time.ctime())
+ else:
+ self.output(fd, l.rstrip())
+ f.close()
+
+ def generate_header(self, fd):
+ """Generate LibVlc header.
+ """
+ for (c_type, jna_type) in self.type2class.iteritems():
+ if c_type.endswith('*') and jna_type.startswith('LibVlc'):
+ self.output(fd, ''' public class %s extends PointerType
+ {
+ }
+''' % jna_type)
+
+ def generate_libvlc(self, dirname):
+ """Generate LibVlc.java JNA glue code.
+ """
+ filename=os.path.join(dirname, 'LibVlc.java')
+ fd=open(filename, 'w')
+
+ self.insert_code(fd, 'boilerplate.java')
+ self.insert_code(fd, 'LibVlc-header.java')
+ #wrapped_methods=self.generate_wrappers(self.parser.methods)
+ self.generate_header(fd)
+ for (rtype, method, params, comment) in self.parser.methods:
+ if method in blacklist:
+ # FIXME
+ continue
+ self.output(fd, "%s %s(%s);\n" % (self.type2class.get(rtype, 'FIXME_%s' % rtype),
+ method,
+ ", ".join( ("%s %s" % (self.type2class[p[0]],
+ p[1])) for p in params )))
+ self.insert_code(fd, 'LibVlc-footer.java')
+ fd.close()
+
+ def generate_enums(self, dirname, enums):
+ """Generate JNA glue code for enums
+ """
+ for (typ, name, values, comment) in enums:
+ if typ != 'enum':
+ raise Exception('This method only handles enums')
+ javaname=self.type2class[name]
+
+ filename=javaname+".java"
+
+ fd=open(os.path.join(dirname, filename), 'w')
+
+ self.insert_code(fd, 'boilerplate.java')
+ self.output(fd, """package org.videolan.jvlc.internal;
+
+
+public enum %s
+{
+""" % javaname)
+ # FIXME: write comment
+
+ for k, v in values:
+ self.output(fd, " %s, // %s," % (k, v))
+ self.output(fd, "}")
+ fd.close()
+
+ def fix_python_comment(self, c):
+ """Fix comment by removing first and last parameters (self and exception)
+ """
+ data=c.replace('@{', '').replace('@see', 'See').splitlines()
+ body=itertools.takewhile(lambda l: not '@param' in l and not '@return' in l, data)
+ param=[ python_param_re.sub('\\1:\\2', l) for l in itertools.ifilter(lambda l: '@param' in l, data) ]
+ ret=[ l.replace('@return', '@return:') for l in itertools.ifilter(lambda l: '@return' in l, data) ]
+
+ if len(param) >= 2:
+ param=param[1:-1]
+ elif len(param) == 1:
+ param=[]
+
+ return "\n".join(itertools.chain(body, param, ret))
+
def process(output, list_of_includes):
p=Parser(list_of_includes)
g=PythonGenerator(p)
default=False,
help="Debug mode")
+ opt.add_option("-c", "--check", dest="check", action="store_true",
+ default=False,
+ help="Check mode")
+
+ opt.add_option("-j", "--java", dest="java", action="store_true",
+ default=False,
+ help="Generate java bindings (default is python)")
+
opt.add_option("-o", "--output", dest="output", action="store",
type="str", default="-",
- help="Output filename")
+ help="Output filename(python)/dirname(java)")
(options, args) = opt.parse_args()
sys.exit(1)
p=Parser(args)
+ if options.check:
+ # Various consistency checks.
+ for (rt, name, params, comment) in p.methods:
+ if not comment.strip():
+ print "No comment for %s" % name
+ continue
+ names=comment_re.findall(comment)
+ if len(names) != len(params):
+ print "Docstring comment parameters mismatch for %s" % name
+
if options.debug:
p.dump_methods()
p.dump_enums()
+
+ if options.check or options.debug:
sys.exit(0)
- g=PythonGenerator(p)
+ if options.java:
+ g=JavaGenerator(p)
+ else:
+ g=PythonGenerator(p)
g.save(options.output)