Bug 592055 - Build/test fixes
[gnome.gobject-introspection] / 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 .ast import (Alias, Array, Bitfield, Callback, Class, Constant, Enum,
25                   Function, Interface, List, Map, Member, Struct, Union,
26                   Varargs)
27 from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember,
28                       GLibFlags, GLibObject, GLibInterface,
29                       GLibRecord)
30 from .xmlwriter import XMLWriter
31
32
33 class GIRWriter(XMLWriter):
34
35     def __init__(self, namespace, shlibs, includes, pkgs, c_includes, cprefix):
36         super(GIRWriter, self).__init__()
37         self.write_comment(
38 '''This file was automatically generated from C sources - DO NOT EDIT!
39 To affect the contents of this file, edit the original C definitions,
40 and/or use gtk-doc annotations. ''')
41         self._write_repository(namespace, shlibs, includes, pkgs,
42                                c_includes, cprefix)
43
44     def _write_repository(self, namespace, shlibs, includes=None,
45                           packages=None, c_includes=None, cprefix=None):
46         if includes is None:
47             includes = frozenset()
48         if packages is None:
49             packages = frozenset()
50         if c_includes is None:
51             c_includes = frozenset()
52         attrs = [
53             ('version', '1.0'),
54             ('xmlns', 'http://www.gtk.org/introspection/core/1.0'),
55             ('xmlns:c', 'http://www.gtk.org/introspection/c/1.0'),
56             ('xmlns:glib', 'http://www.gtk.org/introspection/glib/1.0'),
57             ]
58         with self.tagcontext('repository', attrs):
59             for include in sorted(includes):
60                 self._write_include(include)
61             for pkg in sorted(set(packages)):
62                 self._write_pkgconfig_pkg(pkg)
63             for c_include in sorted(set(c_includes)):
64                 self._write_c_include(c_include)
65             self._write_namespace(namespace, shlibs, cprefix)
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, cprefix):
80         attrs = [('name', namespace.name),
81                  ('version', namespace.version),
82                  ('shared-library', ','.join(shlibs)),
83                  ('c:prefix', cprefix)]
84         with self.tagcontext('namespace', attrs):
85             # We define a custom sorting function here because
86             # we want aliases to be first.  They're a bit
87             # special because the typelib compiler expands them.
88             def nscmp(a, b):
89                 if isinstance(a, Alias):
90                     if isinstance(b, Alias):
91                         return cmp(a.name, b.name)
92                     else:
93                         return -1
94                 elif isinstance(b, Alias):
95                     return 1
96                 else:
97                     return cmp(a, b)
98             for node in sorted(namespace.nodes, cmp=nscmp):
99                 self._write_node(node)
100
101     def _write_node(self, node):
102         if isinstance(node, Function):
103             self._write_function(node)
104         elif isinstance(node, Enum):
105             self._write_enum(node)
106         elif isinstance(node, Bitfield):
107             self._write_bitfield(node)
108         elif isinstance(node, (Class, Interface)):
109             self._write_class(node)
110         elif isinstance(node, Callback):
111             self._write_callback(node)
112         elif isinstance(node, Struct):
113             self._write_record(node)
114         elif isinstance(node, Union):
115             self._write_union(node)
116         elif isinstance(node, GLibBoxed):
117             self._write_boxed(node)
118         elif isinstance(node, Member):
119             # FIXME: atk_misc_instance singleton
120             pass
121         elif isinstance(node, Alias):
122             self._write_alias(node)
123         elif isinstance(node, Constant):
124             self._write_constant(node)
125         else:
126             print 'WRITER: Unhandled node', node
127
128     def _append_version(self, node, attrs):
129         if node.version:
130             attrs.append(('version', node.version))
131
132     def _write_attributes(self, node):
133         for key, value in node.attributes:
134             self.write_tag('attribute', [('name', key), ('value', value)])
135
136     def _append_deprecated(self, node, attrs):
137         if node.deprecated:
138             attrs.append(('deprecated', node.deprecated))
139             if node.deprecated_version:
140                 attrs.append(('deprecated-version',
141                               node.deprecated_version))
142
143     def _append_throws(self, func, attrs):
144         if func.throws:
145             attrs.append(('throws', '1'))
146
147     def _write_alias(self, alias):
148         attrs = [('name', alias.name), ('target', alias.target)]
149         if alias.ctype is not None:
150             attrs.append(('c:type', alias.ctype))
151         self.write_tag('alias', attrs)
152
153     def _write_callable(self, callable, tag_name, extra_attrs):
154         attrs = [('name', callable.name)]
155         attrs.extend(extra_attrs)
156         if callable.doc:
157             attrs.append(('doc', callable.doc))
158         self._append_version(callable, attrs)
159         self._append_deprecated(callable, attrs)
160         self._append_throws(callable, attrs)
161         with self.tagcontext(tag_name, attrs):
162             self._write_attributes(callable)
163             self._write_return_type(callable.retval)
164             self._write_parameters(callable.parameters)
165
166     def _write_function(self, func, tag_name='function'):
167         attrs = [('c:identifier', func.symbol)]
168         self._write_callable(func, tag_name, attrs)
169
170     def _write_method(self, method):
171         self._write_function(method, tag_name='method')
172
173     def _write_static_method(self, method):
174         self._write_function(method, tag_name='function')
175
176     def _write_constructor(self, method):
177         self._write_function(method, tag_name='constructor')
178
179     def _write_return_type(self, return_):
180         if not return_:
181             return
182
183         assert return_.transfer is not None, return_
184
185         attrs = []
186         attrs.append(('transfer-ownership', return_.transfer))
187         if return_.doc:
188             attrs.append(('doc', return_.doc))
189         with self.tagcontext('return-value', attrs):
190             self._write_type(return_.type)
191
192     def _write_parameters(self, parameters):
193         if not parameters:
194             return
195         with self.tagcontext('parameters'):
196             for parameter in parameters:
197                 self._write_parameter(parameter)
198
199     def _write_parameter(self, parameter):
200         assert parameter.transfer is not None, parameter
201
202         attrs = []
203         if parameter.name is not None:
204             attrs.append(('name', parameter.name))
205         if parameter.direction != 'in':
206             attrs.append(('direction', parameter.direction))
207         attrs.append(('transfer-ownership',
208                      parameter.transfer))
209         if parameter.allow_none:
210             attrs.append(('allow-none', '1'))
211         if parameter.scope:
212             attrs.append(('scope', parameter.scope))
213         if parameter.closure_index >= 0:
214             attrs.append(('closure', '%d' % parameter.closure_index))
215         if parameter.destroy_index >= 0:
216             attrs.append(('destroy', '%d' % parameter.destroy_index))
217         if parameter.doc:
218             attrs.append(('doc', parameter.doc))
219         with self.tagcontext('parameter', attrs):
220             self._write_type(parameter.type)
221
222     def _type_to_string(self, ntype):
223         if isinstance(ntype, basestring):
224             return ntype
225         return ntype.name
226
227     def _write_type(self, ntype, relation=None):
228         if isinstance(ntype, basestring):
229             typename = ntype
230             type_cname = None
231         else:
232             typename = ntype.name
233             type_cname = ntype.ctype
234         if isinstance(ntype, Varargs):
235             with self.tagcontext('varargs', []):
236                 pass
237             return
238         if isinstance(ntype, Array):
239             attrs = []
240             if not ntype.zeroterminated:
241                 attrs.append(('zero-terminated', '0'))
242             if ntype.length_param_index >= 0:
243                 attrs.append(('length', '%d' % (ntype.length_param_index, )))
244             attrs.append(('c:type', ntype.ctype))
245             if ntype.size is not None:
246                 attrs.append(('fixed-size', ntype.size))
247             with self.tagcontext('array', attrs):
248                 self._write_type(ntype.element_type)
249             return
250         attrs = [('name', self._type_to_string(ntype))]
251         # FIXME: figure out if type references a basic type
252         #        or a boxed/class/interface etc. and skip
253         #        writing the ctype if the latter.
254         if type_cname is not None:
255             attrs.append(('c:type', type_cname))
256         if isinstance(ntype, List) and ntype.element_type:
257             with self.tagcontext('type', attrs):
258                 self._write_type(ntype.element_type)
259             return
260         if isinstance(ntype, Map) and ntype.key_type:
261             with self.tagcontext('type', attrs):
262                 self._write_type(ntype.key_type)
263                 self._write_type(ntype.value_type)
264             return
265         # Not a special type, just write it out
266         self.write_tag('type', attrs)
267
268     def _write_enum(self, enum):
269         attrs = [('name', enum.name)]
270         if enum.doc:
271             attrs.append(('doc', enum.doc))
272         self._append_version(enum, attrs)
273         self._append_deprecated(enum, attrs)
274         if isinstance(enum, GLibEnum):
275             attrs.extend([('glib:type-name', enum.type_name),
276                           ('glib:get-type', enum.get_type),
277                           ('c:type', enum.ctype)])
278             if enum.error_quark:
279                 attrs.append(('glib:error-quark', enum.error_quark))
280         else:
281             attrs.append(('c:type', enum.symbol))
282
283         with self.tagcontext('enumeration', attrs):
284             self._write_attributes(enum)
285             for member in enum.members:
286                 self._write_member(member)
287
288     def _write_bitfield(self, bitfield):
289         attrs = [('name', bitfield.name)]
290         if bitfield.doc:
291             attrs.append(('doc', bitfield.doc))
292         self._append_version(bitfield, attrs)
293         self._append_deprecated(bitfield, attrs)
294         if isinstance(bitfield, GLibFlags):
295             attrs.extend([('glib:type-name', bitfield.type_name),
296                           ('glib:get-type', bitfield.get_type),
297                           ('c:type', bitfield.ctype)])
298         else:
299             attrs.append(('c:type', bitfield.symbol))
300         with self.tagcontext('bitfield', attrs):
301             self._write_attributes(bitfield)
302             for member in bitfield.members:
303                 self._write_member(member)
304
305     def _write_member(self, member):
306         attrs = [('name', member.name),
307                  ('value', str(member.value)),
308                  ('c:identifier', member.symbol)]
309         if isinstance(member, GLibEnumMember):
310             attrs.append(('glib:nick', member.nick))
311         self.write_tag('member', attrs)
312
313     def _write_constant(self, constant):
314         attrs = [('name', constant.name),
315                  ('value', str(constant.value))]
316         with self.tagcontext('constant', attrs):
317             self._write_type(constant.type)
318
319     def _write_class(self, node):
320         attrs = [('name', node.name),
321                  ('c:type', node.ctype)]
322         if node.doc:
323             attrs.append(('doc', node.doc))
324         self._append_version(node, attrs)
325         self._append_deprecated(node, attrs)
326         if isinstance(node, Class):
327             tag_name = 'class'
328             if node.parent is not None:
329                 attrs.append(('parent', node.parent))
330             if node.is_abstract:
331                 attrs.append(('abstract', '1'))
332         else:
333             tag_name = 'interface'
334         if isinstance(node, (GLibObject, GLibInterface)):
335             attrs.append(('glib:type-name', node.type_name))
336             if node.get_type:
337                 attrs.append(('glib:get-type', node.get_type))
338             if node.glib_type_struct:
339                 attrs.append(('glib:type-struct', node.glib_type_struct.name))
340         with self.tagcontext(tag_name, attrs):
341             self._write_attributes(node)
342             if isinstance(node, GLibObject):
343                 for iface in node.interfaces:
344                     self.write_tag('implements', [('name', iface)])
345             if isinstance(node, Interface):
346                 for iface in node.prerequisites:
347                     self.write_tag('prerequisite', [('name', iface)])
348             if isinstance(node, Class):
349                 for method in node.constructors:
350                     self._write_constructor(method)
351                 for method in node.static_methods:
352                     self._write_static_method(method)
353             for vfunc in node.virtual_methods:
354                 self._write_vfunc(vfunc)
355             for method in node.methods:
356                 self._write_method(method)
357             for prop in node.properties:
358                 self._write_property(prop)
359             for field in node.fields:
360                 self._write_field(field)
361             for signal in node.signals:
362                 self._write_signal(signal)
363
364     def _write_boxed(self, boxed):
365         attrs = [('c:type', boxed.ctype),
366                  ('glib:name', boxed.name)]
367         if boxed.doc:
368             attrs.append(('doc', boxed.doc))
369         attrs.extend(self._boxed_attrs(boxed))
370         with self.tagcontext('glib:boxed', attrs):
371             self._write_attributes(boxed)
372             for method in boxed.constructors:
373                 self._write_constructor(method)
374             for method in boxed.methods:
375                 self._write_method(method)
376
377     def _write_property(self, prop):
378         attrs = [('name', prop.name)]
379         self._append_version(prop, attrs)
380         self._append_deprecated(prop, attrs)
381         # Properties are assumed to be readable (see also generate.c)
382         if not prop.readable:
383             attrs.append(('readable', '0'))
384         if prop.writable:
385             attrs.append(('writable', '1'))
386         if prop.construct:
387             attrs.append(('construct', '1'))
388         if prop.construct_only:
389             attrs.append(('construct-only', '1'))
390         if prop.doc:
391             attrs.append(('doc', prop.doc))
392         with self.tagcontext('property', attrs):
393             self._write_attributes(prop)
394             self._write_type(prop.type)
395
396     def _write_vfunc(self, vf):
397         attrs = []
398         if vf.invoker:
399             attrs.append(('invoker', vf.invoker))
400         self._write_callable(vf, 'virtual-method', attrs)
401
402     def _write_callback(self, callback):
403         attrs = [('c:type', callback.ctype)]
404         self._write_callable(callback, 'callback', attrs)
405
406     def _boxed_attrs(self, boxed):
407         return [('glib:type-name', boxed.type_name),
408                 ('glib:get-type', boxed.get_type)]
409
410     def _write_record(self, record, extra_attrs=[]):
411         attrs = list(extra_attrs)
412         if record.name is not None:
413             attrs.append(('name', record.name))
414         if record.symbol is not None: # the record might be anonymous
415             attrs.append(('c:type', record.symbol))
416         if record.disguised:
417             attrs.append(('disguised', '1'))
418         if isinstance(record, GLibRecord):
419             if record.is_gtype_struct_for:
420                 attrs.append(('glib:is-gtype-struct-for',
421                               record.is_gtype_struct_for))
422         if record.doc:
423             attrs.append(('doc', record.doc))
424         self._append_version(record, attrs)
425         self._append_deprecated(record, attrs)
426         if isinstance(record, GLibBoxed):
427             attrs.extend(self._boxed_attrs(record))
428         with self.tagcontext('record', attrs):
429             self._write_attributes(record)
430             if record.fields:
431                 for field in record.fields:
432                     self._write_field(field)
433             for method in record.constructors:
434                 self._write_constructor(method)
435             for method in record.methods:
436                 self._write_method(method)
437
438     def _write_union(self, union):
439         attrs = []
440         if union.name is not None:
441             attrs.append(('name', union.name))
442         if union.symbol is not None: # the union might be anonymous
443             attrs.append(('c:type', union.symbol))
444         if union.doc:
445             attrs.append(('doc', union.doc))
446         self._append_version(union, attrs)
447         self._append_deprecated(union, attrs)
448         if isinstance(union, GLibBoxed):
449             attrs.extend(self._boxed_attrs(union))
450         with self.tagcontext('union', attrs):
451             self._write_attributes(union)
452             if union.fields:
453                 for field in union.fields:
454                     self._write_field(field)
455             for method in union.constructors:
456                 self._write_constructor(method)
457             for method in union.methods:
458                 self._write_method(method)
459
460     def _write_field(self, field):
461         if isinstance(field, Function):
462             self._write_method(field)
463             return
464
465         if isinstance(field, Callback):
466             self._write_callback(field)
467         elif isinstance(field, Struct):
468             self._write_record(field)
469         elif isinstance(field, Union):
470             self._write_union(field)
471         else:
472             attrs = [('name', field.name)]
473             # Fields are assumed to be read-only
474             # (see also girparser.c and generate.c)
475             if not field.readable:
476                 attrs.append(('readable', '0'))
477             if field.writable:
478                 attrs.append(('writable', '1'))
479             if field.bits:
480                 attrs.append(('bits', str(field.bits)))
481             with self.tagcontext('field', attrs):
482                 self._write_attributes(field)
483                 self._write_type(field.type)
484
485     def _write_signal(self, signal):
486         attrs = [('name', signal.name)]
487         if signal.doc:
488             attrs.append(('doc', signal.doc))
489         self._append_version(signal, attrs)
490         self._append_deprecated(signal, attrs)
491         with self.tagcontext('glib:signal', attrs):
492             self._write_attributes(signal)
493             self._write_return_type(signal.retval)
494             self._write_parameters(signal.parameters)