timeline-object: Add TrackObject to the Track after the TimelineObject
[platform/upstream/gstreamer.git] / bindings / python / codegen / h2def.py
1 #!/usr/bin/env python
2 # -*- Mode: Python; py-indent-offset: 4 -*-
3 # GPL'ed
4 # Toby D. Reeves <toby@max.rl.plh.af.mil>
5 #
6 # Modified by James Henstridge <james@daa.com.au> to output stuff in
7 # Havoc's new defs format.  Info on this format can be seen at:
8 #   http://mail.gnome.org/archives/gtk-devel-list/2000-January/msg00070.html
9 # Updated to be PEP-8 compatible and refactored to use OOP
10 #
11 # Scan the given public .h files of a GTK module (or module using
12 # GTK object conventions) and generates a set of scheme defs.
13 #
14 # h2def searches through a header file looking for function prototypes and
15 # generates a scheme style defenition for each prototype.
16 # Basically the operation of h2def is:
17 #
18 # - read each .h file into a buffer which is scrubbed of extraneous data
19 # - find all object defenitions:
20 #   - find all structures that may represent a GtkObject
21 #   - find all structures that might represent a class
22 #   - find all structures that may represent a GtkObject subclass
23 #   - find all structures that might represent a class/Iface inherited from
24 #     GTypeInterface
25 # - find all enum defenitions
26 # - write out the defs
27 #
28 # The command line options are:
29 #
30 #   -s --separate    Create separate files for objects and function/method defs
31 #                    using the given name as the base name (optional). If this
32 #                    is not specified the combined object and function defs
33 #                    will be output to sys.stdout.
34 #   -f --defsfilter  Extract defs from the given file to filter the output defs
35 #                    that is don't output defs that are defined in the
36 #                    defsfile. More than one deffile may be specified.
37 #   -m --modulename  The prefix to be stripped from the front of function names
38 #                    for the given module
39 #   -n --namespace   The module or namespace name to be used, for example
40 #                    WebKit where h2def is unable to detect the module name
41 #                    automatically. it also sets the gtype-id prefix.
42 #   --onlyenums      Only produce defs for enums and flags
43 #   --onlyobjdefs    Only produce defs for objects
44 #   -v               Verbose output
45 #
46 # Examples:
47 #
48 # python h2def.py /usr/local/include/pango-1.0/pango/*.h >/tmp/pango.defs
49 #
50 # - Outputs all defs for the pango module.
51 #
52 # python h2def.py -m gdk -s /tmp/gdk-2.10 \
53 #            -f /usr/tmp/pygtk/gtk/gdk-base.defs \
54 #            /usr/local/include/gtk-2.0/gdk/*.h \
55 #            /usr/local/include/gtk-2.0/gdk-pixbuf/*.h
56 #
57 # - Outputs the gdk module defs that are not contained in the defs file
58 #   /usr/tmp/pygtk/gtk/gdk-base.defs. Two output files are created:
59 #   /tmp/gdk-2.10-types.defs and /tmp/gdk-2.10.defs.
60 #
61 # python h2def.py -n WebKit /usr/incude/webkit-1.0/webkit/*.h \
62 #            >/tmp/webkit.defs
63 #
64 # - Outputs all the defs for webkit module, setting the module name to WebKit
65 #   and the gtype-id prefix to WEBKIT_ which can't be detected automatically.
66 #
67
68 import getopt
69 import os
70 import re
71 import string
72 import sys
73
74 import defsparser
75
76 # ------------------ Create typecodes from typenames ---------
77
78 _upperstr_pat1 = re.compile(r'([^A-Z])([A-Z])')
79 _upperstr_pat2 = re.compile(r'([A-Z][A-Z])([A-Z][0-9a-z])')
80 _upperstr_pat3 = re.compile(r'^([A-Z])([A-Z])')
81
82 def to_upper_str(name):
83     """Converts a typename to the equivalent upercase and underscores
84     name.  This is used to form the type conversion macros and enum/flag
85     name variables"""
86     name = _upperstr_pat1.sub(r'\1_\2', name)
87     name = _upperstr_pat2.sub(r'\1_\2', name)
88     name = _upperstr_pat3.sub(r'\1_\2', name, count=1)
89     return string.upper(name)
90
91 def typecode(typename, namespace=None):
92     """create a typecode (eg. GTK_TYPE_WIDGET) from a typename"""
93     if namespace:
94       return string.replace(string.upper(namespace) + "_" + to_upper_str(typename[len(namespace):]), '_', '_TYPE_', 1)
95
96     return string.replace(to_upper_str(typename), '_', '_TYPE_', 1)
97
98
99 # ------------------ Find object definitions -----------------
100 # Strips the comments from buffer
101 def strip_comments(buf):
102     parts = []
103     lastpos = 0
104     while 1:
105         pos = string.find(buf, '/*', lastpos)
106         if pos >= 0:
107             parts.append(buf[lastpos:pos])
108             pos = string.find(buf, '*/', pos)
109             if pos >= 0:
110                 lastpos = pos + 2
111             else:
112                 break
113         else:
114             parts.append(buf[lastpos:])
115             break
116     return string.join(parts, '')
117
118 # Strips the dll API from buffer, for example WEBKIT_API
119 def strip_dll_api(buf):
120     pat = re.compile("[A-Z]*_API ")
121     buf = pat.sub("", buf)
122     return buf
123
124 obj_name_pat = "[A-Z][a-z]*[A-Z][A-Za-z0-9]*"
125
126 split_prefix_pat = re.compile('([A-Z]+[a-z]*)([A-Za-z0-9]+)')
127
128 def find_obj_defs(buf, objdefs=[]):
129     """
130     Try to find object definitions in header files.
131     """
132
133     # filter out comments from buffer.
134     buf = strip_comments(buf)
135
136     # filter out dll api
137     buf = strip_dll_api(buf)
138
139     maybeobjdefs = []  # contains all possible objects from file
140
141     # first find all structures that look like they may represent a GtkObject
142     pat = re.compile("struct\s+_(" + obj_name_pat + ")\s*{\s*" +
143                      "(" + obj_name_pat + ")\s+", re.MULTILINE)
144     pos = 0
145     while pos < len(buf):
146         m = pat.search(buf, pos)
147         if not m: break
148         maybeobjdefs.append((m.group(1), m.group(2)))
149         pos = m.end()
150
151     # handle typedef struct { ... } style struct defs.
152     pat = re.compile("typedef struct\s+[_\w]*\s*{\s*" +
153                      "(" + obj_name_pat + ")\s+[^}]*}\s*" +
154                      "(" + obj_name_pat + ")\s*;", re.MULTILINE)
155     pos = 0
156     while pos < len(buf):
157         m = pat.search(buf, pos)
158         if not m: break
159         maybeobjdefs.append((m.group(2), m.group(1)))
160         pos = m.end()
161
162     # now find all structures that look like they might represent a class:
163     pat = re.compile("struct\s+_(" + obj_name_pat + ")Class\s*{\s*" +
164                      "(" + obj_name_pat + ")Class\s+", re.MULTILINE)
165     pos = 0
166     while pos < len(buf):
167         m = pat.search(buf, pos)
168         if not m: break
169         t = (m.group(1), m.group(2))
170         # if we find an object structure together with a corresponding
171         # class structure, then we have probably found a GtkObject subclass.
172         if t in maybeobjdefs:
173             objdefs.append(t)
174         pos = m.end()
175
176     pat = re.compile("typedef struct\s+[_\w]*\s*{\s*" +
177                      "(" + obj_name_pat + ")Class\s+[^}]*}\s*" +
178                      "(" + obj_name_pat + ")Class\s*;", re.MULTILINE)
179     pos = 0
180     while pos < len(buf):
181         m = pat.search(buf, pos)
182         if not m: break
183         t = (m.group(2), m.group(1))
184         # if we find an object structure together with a corresponding
185         # class structure, then we have probably found a GtkObject subclass.
186         if t in maybeobjdefs:
187             objdefs.append(t)
188         pos = m.end()
189
190     # now find all structures that look like they might represent
191     # a class inherited from GTypeInterface:
192     pat = re.compile("struct\s+_(" + obj_name_pat + ")Class\s*{\s*" +
193                      "GTypeInterface\s+", re.MULTILINE)
194     pos = 0
195     while pos < len(buf):
196         m = pat.search(buf, pos)
197         if not m: break
198         t = (m.group(1), '')
199         t2 = (m.group(1)+'Class', 'GTypeInterface')
200         # if we find an object structure together with a corresponding
201         # class structure, then we have probably found a GtkObject subclass.
202         if t2 in maybeobjdefs:
203             objdefs.append(t)
204         pos = m.end()
205
206     # now find all structures that look like they might represent
207     # an Iface inherited from GTypeInterface:
208     pat = re.compile("struct\s+_(" + obj_name_pat + ")Iface\s*{\s*" +
209                      "GTypeInterface\s+", re.MULTILINE)
210     pos = 0
211     while pos < len(buf):
212         m = pat.search(buf, pos)
213         if not m: break
214         t = (m.group(1), '')
215         t2 = (m.group(1)+'Iface', 'GTypeInterface')
216         # if we find an object structure together with a corresponding
217         # class structure, then we have probably found a GtkObject subclass.
218         if t2 in maybeobjdefs:
219             objdefs.append(t)
220         pos = m.end()
221
222 def sort_obj_defs(objdefs):
223     objdefs.sort()  # not strictly needed, but looks nice
224     pos = 0
225     while pos < len(objdefs):
226         klass,parent = objdefs[pos]
227         for i in range(pos+1, len(objdefs)):
228             # parent below subclass ... reorder
229             if objdefs[i][0] == parent:
230                 objdefs.insert(i+1, objdefs[pos])
231                 del objdefs[pos]
232                 break
233         else:
234             pos = pos + 1
235     return objdefs
236
237 # ------------------ Find enum definitions -----------------
238
239 def find_enum_defs(buf, enums=[]):
240     # strip comments
241     # bulk comments
242     buf = strip_comments(buf)
243
244     # strip dll api macros
245     buf = strip_dll_api(buf)
246
247     # strip # directives
248     pat = re.compile(r"""^[#].*?$""", re.MULTILINE)
249     buf = pat.sub('', buf)
250
251     buf = re.sub('\n', ' ', buf)
252
253     enum_pat = re.compile(r'enum\s*{([^}]*)}\s*([A-Z][A-Za-z]*)(\s|;)')
254     splitter = re.compile(r'\s*,\s', re.MULTILINE)
255     pos = 0
256     while pos < len(buf):
257         m = enum_pat.search(buf, pos)
258         if not m: break
259
260         name = m.group(2)
261         vals = m.group(1)
262         isflags = string.find(vals, '<<') >= 0
263         entries = []
264         for val in splitter.split(vals):
265             if not string.strip(val): continue
266             entries.append(string.split(val)[0])
267         if name != 'GdkCursorType':
268             enums.append((name, isflags, entries))
269
270         pos = m.end()
271
272 # ------------------ Find function definitions -----------------
273
274 def clean_func(buf):
275     """
276     Ideally would make buf have a single prototype on each line.
277     Actually just cuts out a good deal of junk, but leaves lines
278     where a regex can figure prototypes out.
279     """
280     # bulk comments
281     buf = strip_comments(buf)
282
283     # dll api
284     buf = strip_dll_api(buf)
285
286     # compact continued lines
287     pat = re.compile(r"""\\\n""", re.MULTILINE)
288     buf = pat.sub('', buf)
289
290     # Preprocess directives
291     pat = re.compile(r"""^[#].*?$""", re.MULTILINE)
292     buf = pat.sub('', buf)
293
294     #typedefs, stucts, and enums
295     pat = re.compile(r"""^(typedef|struct|enum)(\s|.|\n)*?;\s*""",
296                      re.MULTILINE)
297     buf = pat.sub('', buf)
298
299     #strip DECLS macros
300     pat = re.compile(r"""G_BEGIN_DECLS|BEGIN_LIBGTOP_DECLS""", re.MULTILINE)
301     buf = pat.sub('', buf)
302
303     #extern "C"
304     pat = re.compile(r"""^\s*(extern)\s+\"C\"\s+{""", re.MULTILINE)
305     buf = pat.sub('', buf)
306
307     #multiple whitespace
308     pat = re.compile(r"""\s+""", re.MULTILINE)
309     buf = pat.sub(' ', buf)
310
311     #clean up line ends
312     pat = re.compile(r""";\s*""", re.MULTILINE)
313     buf = pat.sub('\n', buf)
314     buf = buf.lstrip()
315
316     #associate *, &, and [] with type instead of variable
317     #pat = re.compile(r'\s+([*|&]+)\s*(\w+)')
318     pat = re.compile(r' \s* ([*|&]+) \s* (\w+)', re.VERBOSE)
319     buf = pat.sub(r'\1 \2', buf)
320     pat = re.compile(r'\s+ (\w+) \[ \s* \]', re.VERBOSE)
321     buf = pat.sub(r'[] \1', buf)
322
323     # make return types that are const work.
324     buf = re.sub(r'\s*\*\s*G_CONST_RETURN\s*\*\s*', '** ', buf)
325     buf = string.replace(buf, 'G_CONST_RETURN ', 'const-')
326     buf = string.replace(buf, 'const ', 'const-')
327
328     #strip GSEAL macros from the middle of function declarations:
329     pat = re.compile(r"""GSEAL""", re.VERBOSE)
330     buf = pat.sub('', buf)
331
332     return buf
333
334 proto_pat=re.compile(r"""
335 (?P<ret>(-|\w|\&|\*)+\s*)  # return type
336 \s+                   # skip whitespace
337 (?P<func>\w+)\s*[(]   # match the function name until the opening (
338 \s*(?P<args>.*?)\s*[)]     # group the function arguments
339 """, re.IGNORECASE|re.VERBOSE)
340 #"""
341 arg_split_pat = re.compile("\s*,\s*")
342
343 get_type_pat = re.compile(r'(const-)?([A-Za-z0-9]+)\*?\s+')
344 pointer_pat = re.compile('.*\*$')
345 func_new_pat = re.compile('(\w+)_new$')
346
347 class DefsWriter:
348     def __init__(self, fp=None, prefix=None, ns=None, verbose=False,
349                  defsfilter=None):
350         if not fp:
351             fp = sys.stdout
352
353         self.fp = fp
354         self.prefix = prefix
355         self.namespace = ns
356         self.verbose = verbose
357
358         self._enums = {}
359         self._objects = {}
360         self._functions = {}
361         if defsfilter:
362             filter = defsparser.DefsParser(defsfilter)
363             filter.startParsing()
364             for func in filter.functions + filter.methods.values():
365                 self._functions[func.c_name] = func
366             for obj in filter.objects + filter.boxes + filter.interfaces:
367                 self._objects[obj.c_name] = obj
368             for obj in filter.enums:
369                 self._enums[obj.c_name] = obj
370
371     def write_def(self, deffile):
372         buf = open(deffile).read()
373
374         self.fp.write('\n;; From %s\n\n' % os.path.basename(deffile))
375         self._define_func(buf)
376         self.fp.write('\n')
377
378     def write_enum_defs(self, enums, fp=None):
379         if not fp:
380             fp = self.fp
381
382         fp.write(';; Enumerations and flags ...\n\n')
383         trans = string.maketrans(string.uppercase + '_',
384                                  string.lowercase + '-')
385         filter = self._enums
386         for cname, isflags, entries in enums:
387             if filter:
388                 if cname in filter:
389                     continue
390             name = cname
391             module = None
392             if self.namespace:
393                 module = self.namespace
394                 name = cname[len(self.namespace):]
395             else:
396                 m = split_prefix_pat.match(cname)
397                 if m:
398                     module = m.group(1)
399                     name = m.group(2)
400             if isflags:
401                 fp.write('(define-flags ' + name + '\n')
402             else:
403                 fp.write('(define-enum ' + name + '\n')
404             if module:
405                 fp.write('  (in-module "' + module + '")\n')
406             fp.write('  (c-name "' + cname + '")\n')
407             fp.write('  (gtype-id "' + typecode(cname, self.namespace) + '")\n')
408             prefix = entries[0]
409             for ent in entries:
410                 # shorten prefix til we get a match ...
411                 # and handle GDK_FONT_FONT, GDK_FONT_FONTSET case
412                 while ((len(prefix) and prefix[-1] != '_') or ent[:len(prefix)] != prefix
413                        or len(prefix) >= len(ent)):
414                     prefix = prefix[:-1]
415             prefix_len = len(prefix)
416             fp.write('  (values\n')
417             for ent in entries:
418                 fp.write('    \'("%s" "%s")\n' %
419                          (string.translate(ent[prefix_len:], trans), ent))
420             fp.write('  )\n')
421             fp.write(')\n\n')
422
423     def write_obj_defs(self, objdefs, fp=None):
424         if not fp:
425             fp = self.fp
426
427         fp.write(';; -*- scheme -*-\n')
428         fp.write('; object definitions ...\n')
429
430         filter = self._objects
431         for klass, parent in objdefs:
432             if filter:
433                 if klass in filter:
434                     continue
435             if self.namespace:
436                 cname = klass[len(self.namespace):]
437                 cmodule = self.namespace
438             else:
439                 m = split_prefix_pat.match(klass)
440                 cname = klass
441                 cmodule = None
442                 if m:
443                     cmodule = m.group(1)
444                     cname = m.group(2)
445             fp.write('(define-object ' + cname + '\n')
446             if cmodule:
447                 fp.write('  (in-module "' + cmodule + '")\n')
448             if parent:
449                 fp.write('  (parent "' + parent + '")\n')
450             fp.write('  (c-name "' + klass + '")\n')
451             fp.write('  (gtype-id "' + typecode(klass, self.namespace) + '")\n')
452             # should do something about accessible fields
453             fp.write(')\n\n')
454
455     def _define_func(self, buf):
456         buf = clean_func(buf)
457         buf = string.split(buf,'\n')
458         filter = self._functions
459         for p in buf:
460             if not p:
461                 continue
462             m = proto_pat.match(p)
463             if m == None:
464                 if self.verbose:
465                     sys.stderr.write('No match:|%s|\n' % p)
466                 continue
467             func = m.group('func')
468             if func[0] == '_':
469                 continue
470             if filter:
471                 if func in filter:
472                     continue
473             ret = m.group('ret')
474             args = m.group('args')
475             args = arg_split_pat.split(args)
476             for i in range(len(args)):
477                 spaces = string.count(args[i], ' ')
478                 if spaces > 1:
479                     args[i] = string.replace(args[i], ' ', '-', spaces - 1)
480
481             self._write_func(func, ret, args)
482
483     def _write_func(self, name, ret, args):
484         if len(args) >= 1:
485             # methods must have at least one argument
486             munged_name = name.replace('_', '')
487             m = get_type_pat.match(args[0])
488             if m:
489                 obj = m.group(2)
490                 if munged_name[:len(obj)] == obj.lower():
491                     self._write_method(obj, name, ret, args)
492                     return
493
494         if self.prefix:
495             l = len(self.prefix)
496             if name[:l] == self.prefix and name[l] == '_':
497                 fname = name[l+1:]
498             else:
499                 fname = name
500         else:
501             fname = name
502
503         # it is either a constructor or normal function
504         self.fp.write('(define-function ' + fname + '\n')
505         self.fp.write('  (c-name "' + name + '")\n')
506
507         # Hmmm... Let's asume that a constructor function name
508         # ends with '_new' and it returns a pointer.
509         m = func_new_pat.match(name)
510         if pointer_pat.match(ret) and m:
511             cname = ''
512             names = m.group(1).split('_')
513
514             if self.namespace:
515                 cname = self.namespace
516                 names = names[1:]
517
518             for s in names:
519                 cname += s.title()
520             if cname != '':
521                 self.fp.write('  (is-constructor-of "' + cname + '")\n')
522
523         self._write_return(ret)
524         self._write_arguments(args)
525
526     def _write_method(self, obj, name, ret, args):
527         regex = string.join(map(lambda x: x+'_?', string.lower(obj)),'')
528         mname = re.sub(regex, '', name, 1)
529         if self.prefix:
530             l = len(self.prefix) + 1
531             if mname[:l] == self.prefix and mname[l+1] == '_':
532                 mname = mname[l+1:]
533         self.fp.write('(define-method ' + mname + '\n')
534         self.fp.write('  (of-object "' + obj + '")\n')
535         self.fp.write('  (c-name "' + name + '")\n')
536         self._write_return(ret)
537         self._write_arguments(args[1:])
538
539     def _write_return(self, ret):
540         if ret != 'void':
541             self.fp.write('  (return-type "' + ret + '")\n')
542         else:
543             self.fp.write('  (return-type "none")\n')
544
545     def _write_arguments(self, args):
546         is_varargs = 0
547         has_args = len(args) > 0
548         for arg in args:
549             if arg == '...':
550                 is_varargs = 1
551             elif arg in ('void', 'void '):
552                 has_args = 0
553         if has_args:
554             self.fp.write('  (parameters\n')
555             for arg in args:
556                 if arg != '...':
557                     tupleArg = tuple(string.split(arg))
558                     if len(tupleArg) == 2:
559                         self.fp.write('    \'("%s" "%s")\n' % tupleArg)
560             self.fp.write('  )\n')
561         if is_varargs:
562             self.fp.write('  (varargs #t)\n')
563         self.fp.write(')\n\n')
564
565 # ------------------ Main function -----------------
566
567 def main(args):
568     verbose = False
569     onlyenums = False
570     onlyobjdefs = False
571     separate = False
572     modulename = None
573     namespace = None
574     defsfilter = None
575     opts, args = getopt.getopt(args[1:], 'vs:m:n:f:',
576                                ['onlyenums', 'onlyobjdefs',
577                                 'modulename=', 'namespace=',
578                                 'separate=', 'defsfilter='])
579     for o, v in opts:
580         if o == '-v':
581             verbose = True
582         if o == '--onlyenums':
583             onlyenums = True
584         if o == '--onlyobjdefs':
585             onlyobjdefs = True
586         if o in ('-s', '--separate'):
587             separate = v
588         if o in ('-m', '--modulename'):
589             modulename = v
590         if o in ('-n', '--namespace'):
591             namespace = v
592         if o in ('-f', '--defsfilter'):
593             defsfilter = v
594
595     if not args[0:1]:
596         print 'Must specify at least one input file name'
597         return -1
598
599     # read all the object definitions in
600     objdefs = []
601     enums = []
602     for filename in args:
603         buf = open(filename).read()
604         find_obj_defs(buf, objdefs)
605         find_enum_defs(buf, enums)
606     objdefs = sort_obj_defs(objdefs)
607
608     if separate:
609         methods = file(separate + '.defs', 'w')
610         types = file(separate + '-types.defs', 'w')
611
612         dw = DefsWriter(methods, prefix=modulename, ns=namespace,
613                         verbose=verbose, defsfilter=defsfilter)
614         dw.write_obj_defs(objdefs, types)
615         dw.write_enum_defs(enums, types)
616         print "Wrote %s-types.defs" % separate
617
618         for filename in args:
619             dw.write_def(filename)
620         print "Wrote %s.defs" % separate
621     else:
622         dw = DefsWriter(prefix=modulename, ns=namespace,
623                         verbose=verbose, defsfilter=defsfilter)
624
625         if onlyenums:
626             dw.write_enum_defs(enums)
627         elif onlyobjdefs:
628             dw.write_obj_defs(objdefs)
629         else:
630             dw.write_obj_defs(objdefs)
631             dw.write_enum_defs(enums)
632
633             for filename in args:
634                 dw.write_def(filename)
635
636 if __name__ == '__main__':
637     sys.exit(main(sys.argv))