Bug 562622 – Errordomains missing
[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 #
5 # This library is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU Lesser General Public
7 # License as published by the Free Software Foundation; either
8 # version 2 of the License, or (at your option) any later version.
9 #
10 # This library is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 # Lesser General Public License for more details.
14 #
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this library; if not, write to the
17 # Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 # Boston, MA 02111-1307, USA.
19 #
20
21 from __future__ import with_statement
22
23 import os
24 from ctypes.util import find_library
25
26 from .ast import (Alias, Array, Bitfield, Callback, Class, Constant, Enum,
27                   Function, Interface, List, Map, Member, Struct, Union,
28                   Varargs)
29 from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember,
30                       GLibFlags, GLibObject, GLibInterface)
31 from .xmlwriter import XMLWriter
32
33
34 class GIRWriter(XMLWriter):
35
36     def __init__(self, namespace, shlibs, includes):
37         super(GIRWriter, self).__init__()
38         self.write_comment(
39 '''This file was automatically generated from C sources - DO NOT EDIT!
40 To affect the contents of this file, edit the original C definitions,
41 and/or use gtk-doc annotations. ''')
42         self._write_repository(namespace, shlibs, includes)
43
44     def _write_repository(self, namespace, shlibs, includes=set()):
45         attrs = [
46             ('version', '1.0'),
47             ('xmlns', 'http://www.gtk.org/introspection/core/1.0'),
48             ('xmlns:c', 'http://www.gtk.org/introspection/c/1.0'),
49             ('xmlns:glib', 'http://www.gtk.org/introspection/glib/1.0'),
50             ]
51         with self.tagcontext('repository', attrs):
52             for include in sorted(includes):
53                 self._write_include(include)
54             self._write_namespace(namespace, shlibs)
55
56     def _write_include(self, include):
57         attrs = [('name', include.name), ('version', include.version)]
58         self.write_tag('include', attrs)
59
60     def _write_namespace(self, namespace, shlibs):
61         libraries = []
62         for l in shlibs:
63             found_libname = find_library(l)
64             if not found_libname:
65                 found_libname = l
66             libraries.append(os.path.basename(found_libname))
67
68         attrs = [('name', namespace.name),
69                  ('version', namespace.version),
70                  ('shared-library', ','.join(libraries))]
71         with self.tagcontext('namespace', attrs):
72             for node in namespace.nodes:
73                 self._write_node(node)
74
75     def _write_node(self, node):
76         if isinstance(node, Function):
77             self._write_function(node)
78         elif isinstance(node, Enum):
79             self._write_enum(node)
80         elif isinstance(node, Bitfield):
81             self._write_bitfield(node)
82         elif isinstance(node, (Class, Interface)):
83             self._write_class(node)
84         elif isinstance(node, Callback):
85             self._write_callback(node)
86         elif isinstance(node, Struct):
87             self._write_record(node)
88         elif isinstance(node, Union):
89             self._write_union(node)
90         elif isinstance(node, GLibBoxed):
91             self._write_boxed(node)
92         elif isinstance(node, Member):
93             # FIXME: atk_misc_instance singleton
94             pass
95         elif isinstance(node, Alias):
96             self._write_alias(node)
97         elif isinstance(node, Constant):
98             self._write_constant(node)
99         else:
100             print 'WRITER: Unhandled node', node
101
102     def _append_version(self, node, attrs):
103         if node.version:
104             attrs.append(('version', node.version))
105
106     def _append_deprecated(self, node, attrs):
107         if node.deprecated:
108             attrs.append(('deprecated', node.deprecated))
109             if node.deprecated_version:
110                 attrs.append(('deprecated-version',
111                               node.deprecated_version))
112
113     def _append_throws(self, func, attrs):
114         if func.throws:
115             attrs.append(('throws', '1'))
116
117     def _write_alias(self, alias):
118         attrs = [('name', alias.name), ('target', alias.target)]
119         if alias.ctype is not None:
120             attrs.append(('c:type', alias.ctype))
121         self.write_tag('alias', attrs)
122
123     def _write_function(self, func, tag_name='function'):
124         attrs = [('name', func.name),
125                  ('c:identifier', func.symbol)]
126         if func.doc:
127             attrs.append(('doc', func.doc))
128         self._append_version(func, attrs)
129         self._append_deprecated(func, attrs)
130         self._append_throws(func, attrs)
131         with self.tagcontext(tag_name, attrs):
132             self._write_return_type(func.retval)
133             self._write_parameters(func.parameters)
134
135     def _write_method(self, method):
136         self._write_function(method, tag_name='method')
137
138     def _write_static_method(self, method):
139         self._write_function(method, tag_name='function')
140
141     def _write_constructor(self, method):
142         self._write_function(method, tag_name='constructor')
143
144     def _write_return_type(self, return_):
145         if not return_:
146             return
147
148         assert return_.transfer is not None, return_
149
150         attrs = []
151         attrs.append(('transfer-ownership', return_.transfer))
152         if return_.doc:
153             attrs.append(('doc', return_.doc))
154         with self.tagcontext('return-value', attrs):
155             self._write_type(return_.type)
156
157     def _write_parameters(self, parameters):
158         if not parameters:
159             return
160         with self.tagcontext('parameters'):
161             for parameter in parameters:
162                 self._write_parameter(parameter)
163
164     def _write_parameter(self, parameter):
165         assert parameter.transfer is not None, parameter
166
167         attrs = []
168         if parameter.name is not None:
169             attrs.append(('name', parameter.name))
170         if parameter.direction != 'in':
171             attrs.append(('direction', parameter.direction))
172         attrs.append(('transfer-ownership',
173                      parameter.transfer))
174         if parameter.allow_none:
175             attrs.append(('allow-none', '1'))
176         if parameter.scope:
177             attrs.append(('scope', parameter.scope))
178         if parameter.closure_index >= 0:
179             attrs.append(('closure', '%d' % parameter.closure_index))
180         if parameter.destroy_index >= 0:
181             attrs.append(('destroy', '%d' % parameter.destroy_index))
182         if parameter.doc:
183             attrs.append(('doc', parameter.doc))
184         with self.tagcontext('parameter', attrs):
185             self._write_type(parameter.type)
186
187     def _type_to_string(self, ntype):
188         if isinstance(ntype, basestring):
189             return ntype
190         return ntype.name
191
192     def _write_type(self, ntype, relation=None):
193         if isinstance(ntype, basestring):
194             typename = ntype
195             type_cname = None
196         else:
197             typename = ntype.name
198             type_cname = ntype.ctype
199         if isinstance(ntype, Varargs):
200             with self.tagcontext('varargs', []):
201                 pass
202             return
203         if isinstance(ntype, Array):
204             attrs = []
205             if not ntype.zeroterminated:
206                 attrs.append(('zero-terminated', '0'))
207             if ntype.length_param_index >= 0:
208                 attrs.append(('length', '%d' % (ntype.length_param_index, )))
209             attrs.append(('c:type', ntype.ctype))
210             if ntype.size is not None:
211                 attrs.append(('fixed-size', ntype.size))
212             with self.tagcontext('array', attrs):
213                 self._write_type(ntype.element_type)
214             return
215         attrs = [('name', self._type_to_string(ntype))]
216         # FIXME: figure out if type references a basic type
217         #        or a boxed/class/interface etc. and skip
218         #        writing the ctype if the latter.
219         if type_cname is not None:
220             attrs.append(('c:type', type_cname))
221         if isinstance(ntype, List) and ntype.element_type:
222             with self.tagcontext('type', attrs):
223                 self._write_type(ntype.element_type)
224             return
225         if isinstance(ntype, Map) and ntype.key_type:
226             with self.tagcontext('type', attrs):
227                 self._write_type(ntype.key_type)
228                 self._write_type(ntype.value_type)
229             return
230         # Not a special type, just write it out
231         self.write_tag('type', attrs)
232
233     def _write_enum(self, enum):
234         attrs = [('name', enum.name)]
235         if enum.doc:
236             attrs.append(('doc', enum.doc))
237         self._append_version(enum, attrs)
238         self._append_deprecated(enum, attrs)
239         if isinstance(enum, GLibEnum):
240             attrs.extend([('glib:type-name', enum.type_name),
241                           ('glib:get-type', enum.get_type),
242                           ('c:type', enum.ctype)])
243             if enum.error_quark:
244                 attrs.append(('glib:error-quark', enum.error_quark))
245         else:
246             attrs.append(('c:type', enum.symbol))
247
248         with self.tagcontext('enumeration', attrs):
249             for member in enum.members:
250                 self._write_member(member)
251
252     def _write_bitfield(self, bitfield):
253         attrs = [('name', bitfield.name)]
254         if bitfield.doc:
255             attrs.append(('doc', bitfield.doc))
256         self._append_version(bitfield, attrs)
257         self._append_deprecated(bitfield, attrs)
258         if isinstance(bitfield, GLibFlags):
259             attrs.extend([('glib:type-name', bitfield.type_name),
260                           ('glib:get-type', bitfield.get_type),
261                           ('c:type', bitfield.ctype)])
262         else:
263             attrs.append(('c:type', bitfield.symbol))
264         with self.tagcontext('bitfield', attrs):
265             for member in bitfield.members:
266                 self._write_member(member)
267
268     def _write_member(self, member):
269         attrs = [('name', member.name),
270                  ('value', str(member.value)),
271                  ('c:identifier', member.symbol)]
272         if isinstance(member, GLibEnumMember):
273             attrs.append(('glib:nick', member.nick))
274         self.write_tag('member', attrs)
275
276     def _write_constant(self, constant):
277         attrs = [('name', constant.name),
278                  ('value', str(constant.value))]
279         with self.tagcontext('constant', attrs):
280             self._write_type(constant.type)
281
282     def _write_class(self, node):
283         attrs = [('name', node.name),
284                  ('c:type', node.ctype)]
285         if node.doc:
286             attrs.append(('doc', node.doc))
287         self._append_version(node, attrs)
288         self._append_deprecated(node, attrs)
289         if isinstance(node, Class):
290             tag_name = 'class'
291             if node.parent is not None:
292                 attrs.append(('parent', node.parent))
293             if node.is_abstract:
294                 attrs.append(('abstract', '1'))
295         else:
296             tag_name = 'interface'
297         if isinstance(node, (GLibObject, GLibInterface)):
298             attrs.append(('glib:type-name', node.type_name))
299             if node.get_type:
300                 attrs.append(('glib:get-type', node.get_type))
301         with self.tagcontext(tag_name, attrs):
302             if isinstance(node, GLibObject):
303                 for iface in node.interfaces:
304                     self.write_tag('implements', [('name', iface)])
305             if isinstance(node, Interface):
306                 for iface in node.prerequisites:
307                     self.write_tag('prerequisite', [('name', iface)])
308             if isinstance(node, Class):
309                 for method in node.constructors:
310                     self._write_constructor(method)
311                 for method in node.static_methods:
312                     self._write_static_method(method)
313             for method in node.methods:
314                 self._write_method(method)
315             for prop in node.properties:
316                 self._write_property(prop)
317             for field in node.fields:
318                 self._write_field(field)
319             for signal in node.signals:
320                 self._write_signal(signal)
321
322     def _write_boxed(self, boxed):
323         attrs = [('c:type', boxed.ctype),
324                  ('glib:name', boxed.name)]
325         if boxed.doc:
326             attrs.append(('doc', boxed.doc))
327         attrs.extend(self._boxed_attrs(boxed))
328         with self.tagcontext('glib:boxed', attrs):
329             for method in boxed.constructors:
330                 self._write_constructor(method)
331             for method in boxed.methods:
332                 self._write_method(method)
333
334     def _write_property(self, prop):
335         attrs = [('name', prop.name)]
336         self._append_version(prop, attrs)
337         self._append_deprecated(prop, attrs)
338         # Properties are assumed to be readable (see also generate.c)
339         if not prop.readable:
340             attrs.append(('readable', '0'))
341         if prop.writable:
342             attrs.append(('writable', '1'))
343         if prop.construct:
344             attrs.append(('construct', '1'))
345         if prop.construct_only:
346             attrs.append(('construct-only', '1'))
347         if prop.doc:
348             attrs.append(('doc', prop.doc))
349         with self.tagcontext('property', attrs):
350             self._write_type(prop.type)
351
352     def _write_callback(self, callback):
353         # FIXME: reuse _write_function
354         attrs = [('name', callback.name), ('c:type', callback.ctype)]
355         if callback.doc:
356             attrs.append(('doc', callback.doc))
357         self._append_version(callback, attrs)
358         self._append_deprecated(callback, attrs)
359         self._append_throws(callback, attrs)
360         with self.tagcontext('callback', attrs):
361             self._write_return_type(callback.retval)
362             self._write_parameters(callback.parameters)
363
364     def _boxed_attrs(self, boxed):
365         return [('glib:type-name', boxed.type_name),
366                 ('glib:get-type', boxed.get_type)]
367
368     def _write_record(self, record):
369         attrs = [('name', record.name),
370                  ('c:type', record.symbol)]
371         if record.disguised:
372             attrs.append(('disguised', '1'))
373         if record.doc:
374             attrs.append(('doc', record.doc))
375         self._append_version(record, attrs)
376         self._append_deprecated(record, attrs)
377         if isinstance(record, GLibBoxed):
378             attrs.extend(self._boxed_attrs(record))
379         with self.tagcontext('record', attrs):
380             if record.fields:
381                 for field in record.fields:
382                     self._write_field(field)
383             for method in record.constructors:
384                 self._write_constructor(method)
385             for method in record.methods:
386                 self._write_method(method)
387
388     def _write_union(self, union):
389         attrs = [('name', union.name),
390                  ('c:type', union.symbol)]
391         if union.doc:
392             attrs.append(('doc', union.doc))
393         self._append_version(union, attrs)
394         self._append_deprecated(union, attrs)
395         if isinstance(union, GLibBoxed):
396             attrs.extend(self._boxed_attrs(union))
397         with self.tagcontext('union', attrs):
398             if union.fields:
399                 for field in union.fields:
400                     self._write_field(field)
401             for method in union.constructors:
402                 self._write_constructor(method)
403             for method in union.methods:
404                 self._write_method(method)
405
406     def _write_field(self, field):
407         if isinstance(field, Function):
408             self._write_method(field)
409             return
410
411         if isinstance(field, Callback):
412             self._write_callback(field)
413             return
414
415         attrs = [('name', field.name)]
416         # Fields are assumed to be read-only
417         # (see also girparser.c and generate.c)
418         if not field.readable:
419             attrs.append(('readable', '0'))
420         if field.writable:
421             attrs.append(('writable', '1'))
422         if field.bits:
423             attrs.append(('bits', str(field.bits)))
424         with self.tagcontext('field', attrs):
425             self._write_type(field.type)
426
427     def _write_signal(self, signal):
428         attrs = [('name', signal.name)]
429         if signal.doc:
430             attrs.append(('doc', signal.doc))
431         self._append_version(signal, attrs)
432         self._append_deprecated(signal, attrs)
433         with self.tagcontext('glib:signal', attrs):
434             self._write_return_type(signal.retval)
435             self._write_parameters(signal.parameters)