0f5c7002d02b7b6e8b11529c38105903bada67b8
[platform/upstream/gobject-introspection.git] / giscanner / girwriter.py
1 # -*- Mode: Python -*-
2 # GObject-Introspection - a framework for introspecting GObject libraries
3 # Copyright (C) 2008  Johan Dahlin
4 # Copyright (C) 2008, 2009 Red Hat, Inc.
5 #
6 # This library is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU Lesser General Public
8 # License as published by the Free Software Foundation; either
9 # version 2 of the License, or (at your option) any later version.
10 #
11 # This library is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 # Lesser General Public License for more details.
15 #
16 # You should have received a copy of the GNU Lesser General Public
17 # License along with this library; if not, write to the
18 # Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 # Boston, MA 02111-1307, USA.
20 #
21
22 from __future__ import with_statement
23
24 from . import ast
25 from .xmlwriter import XMLWriter
26
27 # Bump this for *incompatible* changes to the .gir.
28 # Compatible changes we just make inline
29 COMPATIBLE_GIR_VERSION = '1.2'
30
31 class GIRWriter(XMLWriter):
32
33     def __init__(self, namespace, shlibs, includes, pkgs, c_includes):
34         super(GIRWriter, self).__init__()
35         self.write_comment(
36 '''This file was automatically generated from C sources - DO NOT EDIT!
37 To affect the contents of this file, edit the original C definitions,
38 and/or use gtk-doc annotations. ''')
39         self._write_repository(namespace, shlibs, includes, pkgs,
40                                c_includes)
41
42     def _write_repository(self, namespace, shlibs, includes=None,
43                           packages=None, c_includes=None):
44         if includes is None:
45             includes = frozenset()
46         if packages is None:
47             packages = frozenset()
48         if c_includes is None:
49             c_includes = frozenset()
50         attrs = [
51             ('version', COMPATIBLE_GIR_VERSION),
52             ('xmlns', 'http://www.gtk.org/introspection/core/1.0'),
53             ('xmlns:c', 'http://www.gtk.org/introspection/c/1.0'),
54             ('xmlns:glib', 'http://www.gtk.org/introspection/glib/1.0'),
55             ]
56         with self.tagcontext('repository', attrs):
57             for include in sorted(includes):
58                 self._write_include(include)
59             for pkg in sorted(set(packages)):
60                 self._write_pkgconfig_pkg(pkg)
61             for c_include in sorted(set(c_includes)):
62                 self._write_c_include(c_include)
63             self._namespace = namespace
64             self._write_namespace(namespace, shlibs)
65             self._namespace = None
66
67     def _write_include(self, include):
68         attrs = [('name', include.name), ('version', include.version)]
69         self.write_tag('include', attrs)
70
71     def _write_pkgconfig_pkg(self, package):
72         attrs = [('name', package)]
73         self.write_tag('package', attrs)
74
75     def _write_c_include(self, c_include):
76         attrs = [('name', c_include)]
77         self.write_tag('c:include', attrs)
78
79     def _write_namespace(self, namespace, shlibs):
80         attrs = [('name', namespace.name),
81                  ('version', namespace.version),
82                  ('shared-library', ','.join(shlibs)),
83                  ('c:identifier-prefixes', ','.join(namespace.identifier_prefixes)),
84                  ('c:symbol-prefixes', ','.join(namespace.symbol_prefixes))]
85         with self.tagcontext('namespace', attrs):
86             # We define a custom sorting function here because
87             # we want aliases to be first.  They're a bit
88             # special because the typelib compiler expands them.
89             def nscmp(a, b):
90                 if isinstance(a, ast.Alias):
91                     if isinstance(b, ast.Alias):
92                         return cmp(a.name, b.name)
93                     else:
94                         return -1
95                 elif isinstance(b, ast.Alias):
96                     return 1
97                 else:
98                     return cmp(a, b)
99             for node in sorted(namespace.itervalues(), cmp=nscmp):
100                 self._write_node(node)
101
102     def _write_node(self, node):
103         if isinstance(node, ast.Function):
104             self._write_function(node)
105         elif isinstance(node, ast.Enum):
106             self._write_enum(node)
107         elif isinstance(node, ast.Bitfield):
108             self._write_bitfield(node)
109         elif isinstance(node, (ast.Class, ast.Interface)):
110             self._write_class(node)
111         elif isinstance(node, ast.Callback):
112             self._write_callback(node)
113         elif isinstance(node, ast.Record):
114             self._write_record(node)
115         elif isinstance(node, ast.Union):
116             self._write_union(node)
117         elif isinstance(node, ast.Boxed):
118             self._write_boxed(node)
119         elif isinstance(node, ast.Member):
120             # FIXME: atk_misc_instance singleton
121             pass
122         elif isinstance(node, ast.Alias):
123             self._write_alias(node)
124         elif isinstance(node, ast.Constant):
125             self._write_constant(node)
126         else:
127             print 'WRITER: Unhandled node', node
128
129     def _append_version(self, node, attrs):
130         if node.version:
131             attrs.append(('version', node.version))
132
133     def _write_generic(self, node):
134         for key, value in node.attributes:
135             self.write_tag('attribute', [('name', key), ('value', value)])
136         if hasattr(node, 'doc') and node.doc:
137             self.write_tag('doc', [('xml:whitespace', 'preserve')],
138                            node.doc)
139
140     def _append_node_generic(self, node, attrs):
141         if node.skip or not node.introspectable:
142             attrs.append(('introspectable', '0'))
143         if node.deprecated:
144             attrs.append(('deprecated', node.deprecated))
145             if node.deprecated_version:
146                 attrs.append(('deprecated-version',
147                               node.deprecated_version))
148         if node.stability:
149             attrs.append(('stability', node.stability))
150
151     def _append_throws(self, func, attrs):
152         if func.throws:
153             attrs.append(('throws', '1'))
154
155     def _write_alias(self, alias):
156         attrs = [('name', alias.name)]
157         if alias.ctype is not None:
158             attrs.append(('c:type', alias.ctype))
159         self._append_node_generic(alias, attrs)
160         with self.tagcontext('alias', attrs):
161             self._write_generic(alias)
162             self._write_type_ref(alias.target)
163
164     def _write_callable(self, callable, tag_name, extra_attrs):
165         attrs = [('name', callable.name)]
166         attrs.extend(extra_attrs)
167         self._append_version(callable, attrs)
168         self._append_node_generic(callable, attrs)
169         self._append_throws(callable, attrs)
170         with self.tagcontext(tag_name, attrs):
171             self._write_generic(callable)
172             self._write_return_type(callable.retval, parent=callable)
173             self._write_parameters(callable, callable.parameters)
174
175     def _write_function(self, func, tag_name='function'):
176         attrs = []
177         if hasattr(func, 'symbol'):
178             attrs.append(('c:identifier', func.symbol))
179         if func.shadowed_by:
180             attrs.append(('shadowed-by', func.shadowed_by))
181         elif func.shadows:
182             attrs.append(('shadows', func.shadows))
183         if func.moved_to is not None:
184             attrs.append(('moved-to', func.moved_to))
185         self._write_callable(func, tag_name, attrs)
186
187     def _write_method(self, method):
188         self._write_function(method, tag_name='method')
189
190     def _write_static_method(self, method):
191         self._write_function(method, tag_name='function')
192
193     def _write_constructor(self, method):
194         self._write_function(method, tag_name='constructor')
195
196     def _write_return_type(self, return_, parent=None):
197         if not return_:
198             return
199
200         attrs = []
201         if return_.transfer:
202             attrs.append(('transfer-ownership', return_.transfer))
203         if return_.skip:
204             attrs.append(('skip', '1'))
205         with self.tagcontext('return-value', attrs):
206             self._write_generic(return_)
207             self._write_type(return_.type, function=parent)
208
209     def _write_parameters(self, parent, parameters):
210         if not parameters:
211             return
212         with self.tagcontext('parameters'):
213             for parameter in parameters:
214                 self._write_parameter(parent, parameter)
215
216     def _write_parameter(self, parent, parameter):
217         attrs = []
218         if parameter.argname is not None:
219             attrs.append(('name', parameter.argname))
220         if (parameter.direction is not None) and (parameter.direction != 'in'):
221             attrs.append(('direction', parameter.direction))
222             attrs.append(('caller-allocates',
223                           '1' if parameter.caller_allocates else '0'))
224         if parameter.transfer:
225             attrs.append(('transfer-ownership',
226                           parameter.transfer))
227         if parameter.allow_none:
228             attrs.append(('allow-none', '1'))
229         if parameter.scope:
230             attrs.append(('scope', parameter.scope))
231         if parameter.closure_name is not None:
232             idx = parent.get_parameter_index(parameter.closure_name)
233             attrs.append(('closure', '%d' % (idx, )))
234         if parameter.destroy_name is not None:
235             idx = parent.get_parameter_index(parameter.destroy_name)
236             attrs.append(('destroy', '%d' % (idx, )))
237         if parameter.skip:
238             attrs.append(('skip', '1'))
239         with self.tagcontext('parameter', attrs):
240             self._write_generic(parameter)
241             self._write_type(parameter.type, function=parent)
242
243     def _type_to_name(self, typeval):
244         if not typeval.resolved:
245             raise AssertionError("Caught unresolved type %r (ctype=%r)" % (typeval, typeval.ctype))
246         assert typeval.target_giname is not None
247         prefix = self._namespace.name + '.'
248         if typeval.target_giname.startswith(prefix):
249             return typeval.target_giname[len(prefix):]
250         return typeval.target_giname
251
252     def _write_type_ref(self, ntype):
253         """ Like _write_type, but only writes the type name rather than the full details """
254         assert isinstance(ntype, ast.Type), ntype
255         attrs = []
256         if ntype.ctype:
257             attrs.append(('c:type', ntype.ctype))
258         if isinstance(ntype, ast.Array):
259             if ntype.array_type != ast.Array.C:
260                 attrs.insert(0, ('name', ntype.array_type))
261         elif isinstance(ntype, ast.List):
262             if ntype.name:
263                 attrs.insert(0, ('name', ntype.name))
264         elif isinstance(ntype, ast.Map):
265             attrs.insert(0, ('name', 'GLib.HashTable'))
266         else:
267             if ntype.target_giname:
268                 attrs.insert(0, ('name', self._type_to_name(ntype)))
269             elif ntype.target_fundamental:
270                 attrs.insert(0, ('name', ntype.target_fundamental))
271
272         self.write_tag('type', attrs)
273
274     def _write_type(self, ntype, relation=None, function=None):
275         assert isinstance(ntype, ast.Type), ntype
276         attrs = []
277         if ntype.complete_ctype:
278             attrs.append(('c:type', ntype.complete_ctype))
279         elif ntype.ctype:
280             attrs.append(('c:type', ntype.ctype))
281         if isinstance(ntype, ast.Varargs):
282             with self.tagcontext('varargs', []):
283                 pass
284         elif isinstance(ntype, ast.Array):
285             if ntype.array_type != ast.Array.C:
286                 attrs.insert(0, ('name', ntype.array_type))
287             # we insert an explicit 'zero-terminated' attribute
288             # when it is false, or when it would not be implied
289             # by the absence of length and fixed-size
290             if not ntype.zeroterminated:
291                 attrs.insert(0, ('zero-terminated', '0'))
292             elif (ntype.zeroterminated
293                   and (ntype.size is not None or ntype.length_param_name is not None)):
294                 attrs.insert(0, ('zero-terminated', '1'))
295             if ntype.size is not None:
296                 attrs.append(('fixed-size', '%d' % (ntype.size, )))
297             if ntype.length_param_name is not None:
298                 assert function
299                 attrs.insert(0, ('length', '%d'
300                             % (function.get_parameter_index(ntype.length_param_name, ))))
301
302             with self.tagcontext('array', attrs):
303                 self._write_type(ntype.element_type)
304         elif isinstance(ntype, ast.List):
305             if ntype.name:
306                 attrs.insert(0, ('name', ntype.name))
307             with self.tagcontext('type', attrs):
308                 self._write_type(ntype.element_type)
309         elif isinstance(ntype, ast.Map):
310             attrs.insert(0, ('name', 'GLib.HashTable'))
311             with self.tagcontext('type', attrs):
312                 self._write_type(ntype.key_type)
313                 self._write_type(ntype.value_type)
314         else:
315             # REWRITEFIXME - enable this for 1.2
316             if ntype.target_giname:
317                 attrs.insert(0, ('name', self._type_to_name(ntype)))
318             elif ntype.target_fundamental:
319                 # attrs = [('fundamental', ntype.target_fundamental)]
320                 attrs.insert(0, ('name', ntype.target_fundamental))
321             elif ntype.target_foreign:
322                 attrs.insert(0, ('foreign', '1'))
323             self.write_tag('type', attrs)
324
325     def _append_registered(self, node, attrs):
326         assert isinstance(node, ast.Registered)
327         if node.get_type:
328             attrs.extend([('glib:type-name', node.gtype_name),
329                           ('glib:get-type', node.get_type)])
330
331     def _write_enum(self, enum):
332         attrs = [('name', enum.name)]
333         self._append_version(enum, attrs)
334         self._append_node_generic(enum, attrs)
335         self._append_registered(enum, attrs)
336         attrs.append(('c:type', enum.ctype))
337         if enum.error_domain:
338             attrs.append(('glib:error-domain', enum.error_domain))
339
340         with self.tagcontext('enumeration', attrs):
341             self._write_generic(enum)
342             for member in enum.members:
343                 self._write_member(member)
344             for method in sorted(enum.static_methods):
345                 self._write_static_method(method)
346
347     def _write_bitfield(self, bitfield):
348         attrs = [('name', bitfield.name)]
349         self._append_version(bitfield, attrs)
350         self._append_node_generic(bitfield, attrs)
351         self._append_registered(bitfield, attrs)
352         attrs.append(('c:type', bitfield.ctype))
353         with self.tagcontext('bitfield', attrs):
354             self._write_generic(bitfield)
355             for member in bitfield.members:
356                 self._write_member(member)
357             for method in sorted(bitfield.static_methods):
358                 self._write_static_method(method)
359
360     def _write_member(self, member):
361         attrs = [('name', member.name),
362                  ('value', str(member.value)),
363                  ('c:identifier', member.symbol)]
364         if member.nick is not None:
365             attrs.append(('glib:nick', member.nick))
366         self.write_tag('member', attrs)
367
368     def _write_constant(self, constant):
369         attrs = [('name', constant.name),
370                  ('value', constant.value),
371                  ('c:type', constant.ctype)]
372         with self.tagcontext('constant', attrs):
373             self._write_type(constant.value_type)
374
375     def _write_class(self, node):
376         attrs = [('name', node.name),
377                  ('c:symbol-prefix', node.c_symbol_prefix),
378                  ('c:type', node.ctype)]
379         self._append_version(node, attrs)
380         self._append_node_generic(node, attrs)
381         if isinstance(node, ast.Class):
382             tag_name = 'class'
383             if node.parent is not None:
384                 attrs.append(('parent',
385                               self._type_to_name(node.parent)))
386             if node.is_abstract:
387                 attrs.append(('abstract', '1'))
388         else:
389             assert isinstance(node, ast.Interface)
390             tag_name = 'interface'
391         attrs.append(('glib:type-name', node.gtype_name))
392         if node.get_type is not None:
393             attrs.append(('glib:get-type', node.get_type))
394         if node.glib_type_struct is not None:
395             attrs.append(('glib:type-struct',
396                           self._type_to_name(node.glib_type_struct)))
397         if isinstance(node, ast.Class):
398             if node.fundamental:
399                 attrs.append(('glib:fundamental', '1'))
400             if node.ref_func:
401                 attrs.append(('glib:ref-func', node.ref_func))
402             if node.unref_func:
403                 attrs.append(('glib:unref-func', node.unref_func))
404             if node.set_value_func:
405                 attrs.append(('glib:set-value-func', node.set_value_func))
406             if node.get_value_func:
407                 attrs.append(('glib:get-value-func', node.get_value_func))
408         with self.tagcontext(tag_name, attrs):
409             self._write_generic(node)
410             if isinstance(node, ast.Class):
411                 for iface in sorted(node.interfaces):
412                     self.write_tag('implements',
413                                    [('name', self._type_to_name(iface))])
414             if isinstance(node, ast.Interface):
415                 for iface in sorted(node.prerequisites):
416                     self.write_tag('prerequisite',
417                                    [('name', self._type_to_name(iface))])
418             if isinstance(node, ast.Class):
419                 for method in sorted(node.constructors):
420                     self._write_constructor(method)
421             if isinstance(node, (ast.Class, ast.Interface)):
422                 for method in sorted(node.static_methods):
423                     self._write_static_method(method)
424             for vfunc in sorted(node.virtual_methods):
425                 self._write_vfunc(vfunc)
426             for method in sorted(node.methods):
427                 self._write_method(method)
428             for prop in sorted(node.properties):
429                 self._write_property(prop)
430             for field in node.fields:
431                 self._write_field(field)
432             for signal in sorted(node.signals):
433                 self._write_signal(signal)
434
435     def _write_boxed(self, boxed):
436         attrs = [('glib:name', boxed.name)]
437         if boxed.c_symbol_prefix is not None:
438             attrs.append(('c:symbol-prefix', boxed.c_symbol_prefix))
439         self._append_registered(boxed, attrs)
440         with self.tagcontext('glib:boxed', attrs):
441             self._write_generic(boxed)
442             for method in sorted(boxed.constructors):
443                 self._write_constructor(method)
444             for method in sorted(boxed.methods):
445                 self._write_method(method)
446             for method in sorted(boxed.static_methods):
447                 self._write_static_method(method)
448
449     def _write_property(self, prop):
450         attrs = [('name', prop.name)]
451         self._append_version(prop, attrs)
452         self._append_node_generic(prop, attrs)
453         # Properties are assumed to be readable (see also generate.c)
454         if not prop.readable:
455             attrs.append(('readable', '0'))
456         if prop.writable:
457             attrs.append(('writable', '1'))
458         if prop.construct:
459             attrs.append(('construct', '1'))
460         if prop.construct_only:
461             attrs.append(('construct-only', '1'))
462         if prop.transfer:
463             attrs.append(('transfer-ownership', prop.transfer))
464         with self.tagcontext('property', attrs):
465             self._write_generic(prop)
466             self._write_type(prop.type)
467
468     def _write_vfunc(self, vf):
469         attrs = []
470         if vf.invoker:
471             attrs.append(('invoker', vf.invoker))
472         self._write_callable(vf, 'virtual-method', attrs)
473
474     def _write_callback(self, callback):
475         attrs = []
476         if callback.namespace:
477             attrs.append(('c:type', callback.ctype or callback.c_name))
478         self._write_callable(callback, 'callback', attrs)
479
480     def _write_record(self, record, extra_attrs=[]):
481         is_gtype_struct = False
482         attrs = list(extra_attrs)
483         if record.name is not None:
484             attrs.append(('name', record.name))
485         if record.ctype is not None: # the record might be anonymous
486             attrs.append(('c:type', record.ctype))
487         if record.disguised:
488             attrs.append(('disguised', '1'))
489         if record.foreign:
490             attrs.append(('foreign', '1'))
491         if record.is_gtype_struct_for is not None:
492             is_gtype_struct = True
493             attrs.append(('glib:is-gtype-struct-for',
494                           self._type_to_name(record.is_gtype_struct_for)))
495         self._append_version(record, attrs)
496         self._append_node_generic(record, attrs)
497         self._append_registered(record, attrs)
498         if record.c_symbol_prefix:
499             attrs.append(('c:symbol-prefix', record.c_symbol_prefix))
500         with self.tagcontext('record', attrs):
501             self._write_generic(record)
502             if record.fields:
503                 for field in record.fields:
504                     self._write_field(field, is_gtype_struct)
505             for method in sorted(record.constructors):
506                 self._write_constructor(method)
507             for method in sorted(record.methods):
508                 self._write_method(method)
509             for method in sorted(record.static_methods):
510                 self._write_static_method(method)
511
512     def _write_union(self, union):
513         attrs = []
514         if union.name is not None:
515             attrs.append(('name', union.name))
516         if union.ctype is not None: # the union might be anonymous
517             attrs.append(('c:type', union.ctype))
518         self._append_version(union, attrs)
519         self._append_node_generic(union, attrs)
520         self._append_registered(union, attrs)
521         if union.c_symbol_prefix:
522             attrs.append(('c:symbol-prefix', union.c_symbol_prefix))
523         with self.tagcontext('union', attrs):
524             self._write_generic(union)
525             if union.fields:
526                 for field in union.fields:
527                     self._write_field(field)
528             for method in sorted(union.constructors):
529                 self._write_constructor(method)
530             for method in sorted(union.methods):
531                 self._write_method(method)
532             for method in sorted(union.static_methods):
533                 self._write_static_method(method)
534
535     def _write_field(self, field, is_gtype_struct=False):
536         if field.anonymous_node:
537             if isinstance(field.anonymous_node, ast.Callback):
538                 attrs = [('name', field.name)]
539                 self._append_node_generic(field, attrs)
540                 with self.tagcontext('field', attrs):
541                     self._write_callback(field.anonymous_node)
542             elif isinstance(field.anonymous_node, ast.Record):
543                 self._write_record(field.anonymous_node)
544             elif isinstance(field.anonymous_node, ast.Union):
545                 self._write_union(field.anonymous_node)
546             else:
547                 raise AssertionError("Unknown field anonymous: %r" \
548                                          % (field.anonymous_node, ))
549         else:
550             attrs = [('name', field.name)]
551             self._append_node_generic(field, attrs)
552             # Fields are assumed to be read-only
553             # (see also girparser.c and generate.c)
554             if not field.readable:
555                 attrs.append(('readable', '0'))
556             if field.writable:
557                 attrs.append(('writable', '1'))
558             if field.bits:
559                 attrs.append(('bits', str(field.bits)))
560             if field.private:
561                 attrs.append(('private', '1'))
562             with self.tagcontext('field', attrs):
563                 self._write_generic(field)
564                 self._write_type(field.type)
565
566     def _write_signal(self, signal):
567         attrs = [('name', signal.name)]
568         if signal.when:
569             attrs.append(('when', signal.when))
570         if signal.no_recurse:
571             attrs.append(('no-recurse', '1'))
572         if signal.detailed:
573             attrs.append(('detailed', '1'))
574         if signal.action:
575             attrs.append(('action', '1'))
576         if signal.no_hooks:
577             attrs.append(('no-hooks', '1'))
578
579         self._append_version(signal, attrs)
580         self._append_node_generic(signal, attrs)
581         with self.tagcontext('glib:signal', attrs):
582             self._write_generic(signal)
583             self._write_return_type(signal.retval)
584             self._write_parameters(signal, signal.parameters)