]> git.sesse.net Git - vlc/blob - bindings/python-ctypes/generate.py
python-ctypes: fix Logger bindings.
[vlc] / bindings / python-ctypes / generate.py
1 #! /usr/bin/python
2 debug=False
3
4 #
5 # Code generator for python ctypes bindings for VLC
6 # Copyright (C) 2009 the VideoLAN team
7 # $Id: $
8 #
9 # Authors: Olivier Aubert <olivier.aubert at liris.cnrs.fr>
10 #
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 2 of the License, or
14 # (at your option) any later version.
15 #
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 # GNU General Public License for more details.
20 #
21 # You should have received a copy of the GNU General Public License
22 # along with this program; if not, write to the Free Software
23 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 #
25
26 """This module parses VLC public API include files and generates
27 corresponding python/ctypes code. Moreover, it generates class
28 wrappers for most methods.
29 """
30
31 import sys
32 import re
33 import time
34 import operator
35 import itertools
36 from optparse import OptionParser
37
38 # DefaultDict from ASPN python cookbook
39 import copy
40 class DefaultDict(dict):
41     """Dictionary with a default value for unknown keys."""
42     def __init__(self, default=None, **items):
43         dict.__init__(self, **items)
44         self.default = default
45
46     def __getitem__(self, key):
47         if key in self:
48             return self.get(key)
49         else:
50             ## Need copy in case self.default is something like []
51             return self.setdefault(key, copy.deepcopy(self.default))
52
53     def __copy__(self):
54         return DefaultDict(self.default, **self)
55
56 # Methods not decorated/not referenced
57 blacklist=[
58     "libvlc_exception_raise",
59     "libvlc_exception_raised",
60     "libvlc_exception_get_message",
61     "libvlc_get_vlc_instance",
62
63     "libvlc_media_list_view_index_of_item",
64     "libvlc_media_list_view_insert_at_index",
65     "libvlc_media_list_view_remove_at_index",
66     "libvlc_media_list_view_add_item",
67
68     # In svn but not in current 1.0.0.
69     #"libvlc_media_add_option_flag",
70     #'libvlc_video_set_deinterlace',
71     #'libvlc_video_get_marquee_option_as_int',
72     #'libvlc_video_get_marquee_option_as_string',
73     #'libvlc_video_set_marquee_option_as_int',
74     #'libvlc_video_set_marquee_option_as_string',
75     #'libvlc_vlm_get_event_manager',
76     #"libvlc_media_list_player_event_manager",
77     #'libvlc_media_player_next_frame',
78
79     'mediacontrol_PlaylistSeq__free',
80     ]
81
82 # Precompiled regexps
83 api_re=re.compile('VLC_PUBLIC_API\s+(\S+\s+.+?)\s*\(\s*(.+?)\s*\)')
84 param_re=re.compile('\s*(const\s*|unsigned\s*|struct\s*)?(\S+\s*\**)\s+(.+)')
85 paramlist_re=re.compile('\s*,\s*')
86 comment_re=re.compile('\\param\s+(\S+)')
87 python_param_re=re.compile('(@param\s+\S+)(.+)')
88 forward_re=re.compile('.+\(\s*(.+?)\s*\)(\s*\S+)')
89 enum_re=re.compile('typedef\s+(enum)\s*(\S+\s*)?\{\s*(.+)\s*\}\s*(\S+);')
90 special_enum_re=re.compile('^(enum)\s*(\S+\s*)?\{\s*(.+)\s*\};')
91 event_def_re=re.compile('^DEF\(\s*(\w+)\s*\)')
92
93 # Definition of parameter passing mode for types.  This should not be
94 # hardcoded this way, but works alright ATM.
95 parameter_passing=DefaultDict(default=1)
96 parameter_passing['libvlc_exception_t*']=3
97
98 class Parser(object):
99     def __init__(self, list_of_files):
100         self.methods=[]
101         self.enums=[]
102
103         for name in list_of_files:
104             self.enums.extend(self.parse_typedef(name))
105             self.methods.extend(self.parse_include(name))
106
107     def parse_param(self, s):
108         """Parse a C parameter expression.
109
110         It is used to parse both the type/name for methods, and type/name
111         for their parameters.
112
113         It returns a tuple (type, name).
114         """
115         s=s.strip()
116         s=s.replace('const', '')
117         if 'VLC_FORWARD' in s:
118             m=forward_re.match(s)
119             s=m.group(1)+m.group(2)
120         m=param_re.search(s)
121         if m:
122             const, typ, name=m.groups()
123             while name.startswith('*'):
124                 typ += '*'
125                 name=name[1:]
126             if name == 'const*':
127                 # K&R definition: const char * const*
128                 name=''
129             typ=typ.replace(' ', '')
130             return typ, name
131         else:
132             # K&R definition: only type
133             return s.replace(' ', ''), ''
134
135     def parse_typedef(self, name):
136         """Parse include file for typedef expressions.
137
138         This generates a tuple for each typedef:
139         (type, name, value_list, comment)
140         with type == 'enum' (for the moment) and value_list being a list of (name, value)
141         Note that values are string, since this is intended for code generation.
142         """
143         event_names=[]
144
145         f=open(name, 'r')
146         accumulator=''
147         for l in f:
148             # Note: lstrip() should not be necessary, but there is 1 badly
149             # formatted comment in vlc1.0.0 includes
150             if l.lstrip().startswith('/**'):
151                 comment=''
152                 continue
153             elif l.startswith(' * '):
154                 comment = comment + l[3:]
155                 continue
156
157             l=l.strip()
158             if l.startswith('/*') or l.endswith('*/'):
159                 continue
160
161             if (l.startswith('typedef enum') or l.startswith('enum')) and not l.endswith(';'):
162                 # Multiline definition. Accumulate until end of definition
163                 accumulator=l
164                 continue
165             elif accumulator:
166                 accumulator=" ".join( (accumulator, l) )
167                 if l.endswith(';'):
168                     # End of definition
169                     l=accumulator
170                     accumulator=''
171
172             m=enum_re.match(l)
173             if m:
174                 values=[]
175                 (typ, dummy, data, name)=m.groups()
176                 for i, l in enumerate(paramlist_re.split(data)):
177                     l=l.strip()
178                     if l.startswith('/*'):
179                         continue
180                     if '=' in l:
181                         # A value was specified. Use it.
182                         values.append(re.split('\s*=\s*', l))
183                     else:
184                         if l:
185                             values.append( (l, str(i)) )
186                 comment=comment.replace('@{', '').replace('@see', 'See').replace('\ingroup', '')
187                 yield (typ, name.strip(), values, comment)
188                 comment=''
189                 continue
190
191             # Special case, used only for libvlc_events.h
192             # (version after 96a96f60bb0d1f2506e68b356897ceca6f6b586d)
193             m=event_def_re.match(l)
194             if m:
195                 # Event definition.
196                 event_names.append('libvlc_'+m.group(1))
197                 continue
198
199             # Special case, used only for libvlc_events.h
200             m=special_enum_re.match(l)
201             if m:
202                 (typ, name, data)=m.groups()
203                 if event_names:
204                     # event_names were defined through DEF macro
205                     # (see 96a96f60bb0d1f2506e68b356897ceca6f6b586d)
206                     values=list( (n, str(i)) for i, n in enumerate(event_names))
207                 else:
208                     # Before 96a96f60bb0d1f2506e68b356897ceca6f6b586d
209                     values=[]
210                     for i, l in enumerate(paramlist_re.split(data)):
211                         l=l.strip()
212                         if l.startswith('/*') or l.startswith('#'):
213                             continue
214                         if '=' in l:
215                             # A value was specified. Use it.
216                             values.append(re.split('\s*=\s*', l))
217                         else:
218                             if l:
219                                 values.append( (l, str(i)) )
220                 comment=comment.replace('@{', '').replace('@see', 'See').replace('\ingroup', '')
221                 yield (typ, name.strip(), values, comment)
222                 comment=''
223                 continue
224
225     def parse_include(self, name):
226         """Parse include file.
227
228         This generates a tuple for each function:
229         (return_type, method_name, parameter_list, comment)
230         with parameter_list being a list of tuples (parameter_type, parameter_name).
231         """
232         f=open(name, 'r')
233         accumulator=''
234         comment=''
235         for l in f:
236             # Note: lstrip() should not be necessary, but there is 1 badly
237             # formatted comment in vlc1.0.0 includes
238             if l.lstrip().startswith('/**'):
239                 comment=''
240                 continue
241             elif l.startswith(' * '):
242                 comment = comment + l[3:]
243                 continue
244
245             l=l.strip()
246
247             if accumulator:
248                 accumulator=" ".join( (accumulator, l) )
249                 if l.endswith(');'):
250                     # End of definition
251                     l=accumulator
252                     accumulator=''
253             elif l.startswith('VLC_PUBLIC_API') and not l.endswith(');'):
254                 # Multiline definition. Accumulate until end of definition
255                 accumulator=l
256                 continue
257
258             m=api_re.match(l)
259             if m:
260                 (ret, param)=m.groups()
261
262                 rtype, method=self.parse_param(ret)
263
264                 params=[]
265                 for p in paramlist_re.split(param):
266                     params.append( self.parse_param(p) )
267
268                 if len(params) == 1 and params[0][0] == 'void':
269                     # Empty parameter list
270                     params=[]
271
272                 if list(p for p in params if not p[1]):
273                     # Empty parameter names. Have to poke into comment.
274                     names=comment_re.findall(comment)
275                     if len(names) < len(params):
276                         # Bad description: all parameters are not specified.
277                         # Generate default parameter names
278                         badnames=[ "param%d" % i for i in xrange(len(params)) ]
279                         # Put in the existing ones
280                         for (i, p) in enumerate(names):
281                             badnames[i]=names[i]
282                         names=badnames
283                         print "### Error ###"
284                         print "### Cannot get parameter names from comment for %s: %s" % (method, comment.replace("\n", ' '))
285                         # Note: this was previously
286                         # raise Exception("Cannot get parameter names from comment for %s: %s" % (method, comment))
287                         # but it prevented code generation for a minor detail (some bad descriptions).
288                     params=[ (p[0], names[i]) for (i, p) in enumerate(params) ]
289
290                 # Transform Doxygen syntax into epydoc syntax
291                 comment=comment.replace('\\param', '@param').replace('\\return', '@return')
292
293                 if debug:
294                     print '********************'
295                     print l
296                     print '-------->'
297                     print "%s (%s)" % (method, rtype)
298                     for typ, name in params:
299                         print "        %s (%s)" % (name, typ)
300                     print '********************'
301                 yield (rtype,
302                        method,
303                        params,
304                        comment)
305                 comment=''
306
307     def dump_methods(self):
308         print "** Defined functions **"
309         for (rtype, name, params, comment) in self.methods:
310             print "%(name)s (%(rtype)s):" % locals()
311             for t, n in params:
312                 print "    %(n)s (%(t)s)" % locals()
313
314     def dump_enums(self):
315         print "** Defined enums **"
316         for (typ, name, values, comment) in self.enums:
317             print "%(name)s (%(typ)s):" % locals()
318             for k, v in values:
319                 print "    %(k)s=%(v)s" % locals()
320
321 class PythonGenerator(object):
322     # C-type to ctypes/python type conversion.
323     # Note that enum types conversions are generated (cf convert_enum_names)
324     type2class={
325         'libvlc_exception_t*': 'ctypes.POINTER(VLCException)',
326
327         'libvlc_media_player_t*': 'MediaPlayer',
328         'libvlc_instance_t*': 'Instance',
329         'libvlc_media_t*': 'Media',
330         'libvlc_log_t*': 'Log',
331         'libvlc_log_iterator_t*': 'LogIterator',
332         'libvlc_log_message_t*': 'ctypes.POINTER(LogMessage)',
333         'libvlc_event_type_t': 'ctypes.c_uint',
334         'libvlc_event_manager_t*': 'EventManager',
335         'libvlc_media_discoverer_t*': 'MediaDiscoverer',
336         'libvlc_media_library_t*': 'MediaLibrary',
337         'libvlc_media_list_t*': 'MediaList',
338         'libvlc_media_list_player_t*': 'MediaListPlayer',
339         'libvlc_media_list_view_t*': 'MediaListView',
340         'libvlc_track_description_t*': 'TrackDescription',
341         'libvlc_audio_output_t*': 'AudioOutput',
342
343         'mediacontrol_Instance*': 'MediaControl',
344         'mediacontrol_Exception*': 'MediaControlException',
345         'mediacontrol_RGBPicture*': 'ctypes.POINTER(RGBPicture)',
346         'mediacontrol_PlaylistSeq*': 'MediaControlPlaylistSeq',
347         'mediacontrol_Position*': 'ctypes.POINTER(MediaControlPosition)',
348         'mediacontrol_StreamInformation*': 'ctypes.POINTER(MediaControlStreamInformation)',
349         'WINDOWHANDLE': 'ctypes.c_ulong',
350
351         'void': 'None',
352         'void*': 'ctypes.c_void_p',
353         'short': 'ctypes.c_short',
354         'char*': 'ctypes.c_char_p',
355         'char**': 'ListPOINTER(ctypes.c_char_p)',
356         'uint32_t': 'ctypes.c_uint',
357         'float': 'ctypes.c_float',
358         'unsigned': 'ctypes.c_uint',
359         'int': 'ctypes.c_int',
360         '...': 'FIXMEva_list',
361         'libvlc_callback_t': 'ctypes.c_void_p',
362         'libvlc_time_t': 'ctypes.c_longlong',
363         }
364
365     # Defined python classes, i.e. classes for which we want to generate
366     # class wrappers around libvlc functions
367     defined_classes=(
368         'MediaPlayer',
369         'Instance',
370         'Media',
371         'Log',
372         'LogIterator',
373         'EventManager',
374         'MediaDiscoverer',
375         'MediaLibrary',
376         'MediaList',
377         'MediaListPlayer',
378         'MediaListView',
379         'TrackDescription',
380         'AudioOutput',
381         'MediaControl',
382         )
383
384     def __init__(self, parser=None):
385         self.parser=parser
386
387         # Generate python names for enums
388         self.type2class.update(self.convert_enum_names(parser.enums))
389         self.check_types()
390
391         # Definition of prefixes that we can strip from method names when
392         # wrapping them into class methods
393         self.prefixes=dict( (v, k[:-2])
394                             for (k, v) in self.type2class.iteritems()
395                             if  v in self.defined_classes )
396         self.prefixes['MediaControl']='mediacontrol_'
397
398     def save(self, filename=None):
399         if filename is None or filename == '-':
400             self.fd=sys.stdout
401         else:
402             self.fd=open(filename, 'w')
403
404         self.insert_code('header.py')
405         self.generate_enums(self.parser.enums)
406         wrapped_methods=self.generate_wrappers(self.parser.methods)
407         for l in self.parser.methods:
408             self.output_ctypes(*l)
409         self.insert_code('footer.py')
410
411         all_methods=set( t[1] for t in self.parser.methods )
412         not_wrapped=all_methods.difference(wrapped_methods)
413         self.output("# Not wrapped methods:")
414         for m in not_wrapped:
415             self.output("#   ", m)
416
417         if self.fd != sys.stdout:
418             self.fd.close()
419
420     def output(self, *p):
421         self.fd.write(" ".join(p))
422         self.fd.write("\n")
423
424     def check_types(self):
425         """Make sure that all types are properly translated.
426
427         This method must be called *after* convert_enum_names, since
428         the latter populates type2class with converted enum names.
429         """
430         for (rt, met, params, c) in self.parser.methods:
431             for typ, name in params:
432                 if not typ in self.type2class:
433                     raise Exception("No conversion for %s (from %s:%s)" % (typ, met, name))
434
435     def insert_code(self, filename):
436         """Generate header/footer code.
437         """
438         f=open(filename, 'r')
439         for l in f:
440             if 'build_date' in l:
441                 self.output('build_date="%s"' % time.ctime())
442             else:
443                 self.output(l.rstrip())
444         f.close()
445
446     def convert_enum_names(self, enums):
447         res={}
448         for (typ, name, values, comment) in enums:
449             if typ != 'enum':
450                 raise Exception('This method only handles enums')
451             pyname=re.findall('(libvlc|mediacontrol)_(.+?)(_t)?$', name)[0][1]
452             if '_' in pyname:
453                 pyname=pyname.title().replace('_', '')
454             elif not pyname[0].isupper():
455                 pyname=pyname.capitalize()
456             res[name]=pyname
457         return res
458
459     def generate_enums(self, enums):
460         for (typ, name, values, comment) in enums:
461             if typ != 'enum':
462                 raise Exception('This method only handles enums')
463             pyname=self.type2class[name]
464
465             self.output("class %s(ctypes.c_uint):" % pyname)
466             self.output('    """%s\n    """' % comment)
467
468             conv={}
469             # Convert symbol names
470             for k, v in values:
471                 n=k.split('_')[-1]
472                 if len(n) == 1:
473                     # Single character. Some symbols use 1_1, 5_1, etc.
474                     n="_".join( k.split('_')[-2:] )
475                 if re.match('^[0-9]', n):
476                     # Cannot start an identifier with a number
477                     n='_'+n
478                 conv[k]=n
479
480             for k, v in values:
481                 self.output("    %s=ctypes.c_uint(%s)" % (conv[k], v))
482
483             self.output("    _names={")
484             for k, v in values:
485                 self.output("        %s: '%s'," % (v, conv[k]))
486             self.output("    }")
487
488             self.output("""
489     def __repr__(self):
490         return ".".join((self.__class__.__module__, self.__class__.__name__, self._names[self.value]))
491
492     def __eq__(self, other):
493         return (isinstance(other, ctypes.c_uint) and self.value == other.value)
494
495     def __ne__(self, other):
496         return not self.__eq__(other)
497
498     """)
499
500     def output_ctypes(self, rtype, method, params, comment):
501         """Output ctypes decorator for the given method.
502         """
503         if method in blacklist:
504             # FIXME
505             return
506
507         self.output("""if hasattr(dll, '%s'):""" % method)
508         if params:
509             self.output("    prototype=ctypes.CFUNCTYPE(%s, %s)" % (self.type2class.get(rtype, 'FIXME_%s' % rtype),
510                                                                 ",".join( self.type2class[p[0]] for p in params )))
511         else:
512             self.output("    prototype=ctypes.CFUNCTYPE(%s)" % self.type2class.get(rtype, 'FIXME_%s' % rtype))
513
514
515         if not params:
516             flags='    paramflags= tuple()'
517         elif len(params) == 1:
518             flags="    paramflags=( (%d, ), )" % parameter_passing[params[0][0]]
519         else:
520             flags="    paramflags=%s" % ",".join( '(%d,)' % parameter_passing[p[0]] for p in params )
521         self.output(flags)
522         self.output('    %s = prototype( ("%s", dll), paramflags )' % (method, method))
523         if '3' in flags:
524             # A VLCException is present. Process it.
525             self.output("    %s.errcheck = check_vlc_exception" % method)
526         self.output('    %s.__doc__ = """%s"""' % (method, comment))
527         self.output()
528
529     def parse_override(self, name):
530         """Parse override definitions file.
531
532         It is possible to override methods definitions in classes.
533
534         It returns a tuple
535         (code, overriden_methods, docstring)
536         """
537         code={}
538
539         data=[]
540         current=None
541         f=open(name, 'r')
542         for l in f:
543             m=re.match('class (\S+):', l)
544             if m:
545                 # Dump old data
546                 if current is not None:
547                     code[current]="".join(data)
548                 current=m.group(1)
549                 data=[]
550                 continue
551             data.append(l)
552         code[current]="".join(data)
553         f.close()
554
555         docstring={}
556         for k, v in code.iteritems():
557             if v.lstrip().startswith('"""'):
558                 # Starting comment. Use it as docstring.
559                 dummy, docstring[k], code[k]=v.split('"""', 2)
560
561         # Not robust wrt. internal methods, but this works for the moment.
562         overridden_methods=dict( (k, re.findall('^\s+def\s+(\w+)', v, re.MULTILINE)) for (k, v) in code.iteritems() )
563
564         return code, overridden_methods, docstring
565
566     def fix_python_comment(self, c):
567         """Fix comment by removing first and last parameters (self and exception)
568         """
569         data=c.replace('@{', '').replace('@see', 'See').splitlines()
570         body=itertools.takewhile(lambda l: not '@param' in l and not '@return' in l, data)
571         param=[ python_param_re.sub('\\1:\\2', l) for l in  itertools.ifilter(lambda l: '@param' in l, data) ]
572         ret=[ l.replace('@return', '@return:') for l in itertools.ifilter(lambda l: '@return' in l, data) ]
573
574         if len(param) >= 2:
575             param=param[1:-1]
576         elif len(param) == 1:
577             param=[]
578
579         return "\n".join(itertools.chain(body, param, ret))
580
581     def generate_wrappers(self, methods):
582         """Generate class wrappers for all appropriate methods.
583
584         @return: the set of wrapped method names
585         """
586         ret=set()
587         # Sort methods against the element they apply to.
588         elements=sorted( ( (self.type2class.get(params[0][0]), rt, met, params, c)
589                            for (rt, met, params, c) in methods
590                            if params and self.type2class.get(params[0][0], '_') in self.defined_classes
591                            ),
592                          key=operator.itemgetter(0))
593
594         overrides, overriden_methods, docstring=self.parse_override('override.py')
595
596         for classname, el in itertools.groupby(elements, key=operator.itemgetter(0)):
597             self.output("""class %(name)s(object):""" % {'name': classname})
598             if classname in docstring:
599                 self.output('    """%s\n    """' % docstring[classname])
600
601             self.output("""
602     def __new__(cls, pointer=None):
603         '''Internal method used for instanciating wrappers from ctypes.
604         '''
605         if pointer is None:
606             raise Exception("Internal method. Surely this class cannot be instanciated by itself.")
607         if pointer == 0:
608             return None
609         else:
610             o=object.__new__(cls)
611             o._as_parameter_=ctypes.c_void_p(pointer)
612             return o
613
614     @staticmethod
615     def from_param(arg):
616         '''(INTERNAL) ctypes parameter conversion method.
617         '''
618         return arg._as_parameter_
619     """ % {'name': classname})
620
621             if classname in overrides:
622                 self.output(overrides[classname])
623
624             prefix=self.prefixes.get(classname, '')
625
626             for cl, rtype, method, params, comment in el:
627                 if method in blacklist:
628                     continue
629                 # Strip prefix
630                 name=method.replace(prefix, '').replace('libvlc_', '')
631                 ret.add(method)
632                 if name in overriden_methods.get(cl, []):
633                     # Method already defined in override.py
634                     continue
635
636                 if params:
637                     params[0]=(params[0][0], 'self')
638                 if params and params[-1][0] in ('libvlc_exception_t*', 'mediacontrol_Exception*'):
639                     args=", ".join( p[1] for p in params[:-1] )
640                 else:
641                     args=", ".join( p[1] for p in params )
642
643                 self.output("    if hasattr(dll, '%s'):" % method)
644                 self.output("        def %s(%s):" % (name, args))
645                 self.output('            """%s\n        """' % self.fix_python_comment(comment))
646                 if params and params[-1][0] == 'libvlc_exception_t*':
647                     # Exception handling
648                     self.output("            e=VLCException()")
649                     self.output("            return %s(%s, e)" % (method, args))
650                 elif params and params[-1][0] == 'mediacontrol_Exception*':
651                     # Exception handling
652                     self.output("            e=MediaControlException()")
653                     self.output("            return %s(%s, e)" % (method, args))
654                 else:
655                     self.output("            return %s(%s)" % (method, args))
656                 self.output()
657
658                 # Check for standard methods
659                 if name == 'count':
660                     # There is a count method. Generate a __len__ one.
661                     if params and params[-1][0] == 'libvlc_exception_t*':
662                         self.output("""    def __len__(self):
663         e=VLCException()
664         return %s(self, e)
665 """ % method)
666                     else:
667                         # No exception
668                         self.output("""    def __len__(self):
669         return %s(self)
670 """ % method)
671                 elif name.endswith('item_at_index'):
672                     # Indexable (and thus iterable)"
673                     self.output("""    def __getitem__(self, i):
674         e=VLCException()
675         return %s(self, i, e)
676
677     def __iter__(self):
678         e=VLCException()
679         for i in xrange(len(self)):
680             yield self[i]
681 """ % method)
682         return ret
683
684 def process(output, list_of_includes):
685     p=Parser(list_of_includes)
686     g=PythonGenerator(p)
687     g.save(output)
688
689 if __name__ == '__main__':
690     opt=OptionParser(usage="""Parse VLC include files and generate bindings code.
691 %prog [options] include_file.h [...]""")
692
693     opt.add_option("-d", "--debug", dest="debug", action="store_true",
694                       default=False,
695                       help="Debug mode")
696
697     opt.add_option("-o", "--output", dest="output", action="store",
698                       type="str", default="-",
699                       help="Output filename")
700
701     (options, args) = opt.parse_args()
702
703     if not args:
704         opt.print_help()
705         sys.exit(1)
706
707     p=Parser(args)
708     if options.debug:
709         p.dump_methods()
710         p.dump_enums()
711         sys.exit(0)
712
713     g=PythonGenerator(p)
714
715     g.save(options.output)