89af8fb977d02d5ec75fc4332a2616fb92c690e4
[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         else:
244             attrs.append(('c:type', enum.symbol))
245         with self.tagcontext('enumeration', attrs):
246             for member in enum.members:
247                 self._write_member(member)
248
249     def _write_bitfield(self, bitfield):
250         attrs = [('name', bitfield.name)]
251         if bitfield.doc:
252             attrs.append(('doc', bitfield.doc))
253         self._append_version(bitfield, attrs)
254         self._append_deprecated(bitfield, attrs)
255         if isinstance(bitfield, GLibFlags):
256             attrs.extend([('glib:type-name', bitfield.type_name),
257                           ('glib:get-type', bitfield.get_type),
258                           ('c:type', bitfield.ctype)])
259         else:
260             attrs.append(('c:type', bitfield.symbol))
261         with self.tagcontext('bitfield', attrs):
262             for member in bitfield.members:
263                 self._write_member(member)
264
265     def _write_member(self, member):
266         attrs = [('name', member.name),
267                  ('value', str(member.value)),
268                  ('c:identifier', member.symbol)]
269         if isinstance(member, GLibEnumMember):
270             attrs.append(('glib:nick', member.nick))
271         self.write_tag('member', attrs)
272
273     def _write_constant(self, constant):
274         attrs = [('name', constant.name),
275                  ('value', str(constant.value))]
276         with self.tagcontext('constant', attrs):
277             self._write_type(constant.type)
278
279     def _write_class(self, node):
280         attrs = [('name', node.name),
281                  ('c:type', node.ctype)]
282         if node.doc:
283             attrs.append(('doc', node.doc))
284         self._append_version(node, attrs)
285         self._append_deprecated(node, attrs)
286         if isinstance(node, Class):
287             tag_name = 'class'
288             if node.parent is not None:
289                 attrs.append(('parent', node.parent))
290             if node.is_abstract:
291                 attrs.append(('abstract', '1'))
292         else:
293             tag_name = 'interface'
294         if isinstance(node, (GLibObject, GLibInterface)):
295             attrs.append(('glib:type-name', node.type_name))
296             if node.get_type:
297                 attrs.append(('glib:get-type', node.get_type))
298         with self.tagcontext(tag_name, attrs):
299             if isinstance(node, GLibObject):
300                 for iface in node.interfaces:
301                     self.write_tag('implements', [('name', iface)])
302             if isinstance(node, Interface):
303                 for iface in node.prerequisites:
304                     self.write_tag('prerequisite', [('name', iface)])
305             if isinstance(node, Class):
306                 for method in node.constructors:
307                     self._write_constructor(method)
308                 for method in node.static_methods:
309                     self._write_static_method(method)
310             for method in node.methods:
311                 self._write_method(method)
312             for prop in node.properties:
313                 self._write_property(prop)
314             for field in node.fields:
315                 self._write_field(field)
316             for signal in node.signals:
317                 self._write_signal(signal)
318
319     def _write_boxed(self, boxed):
320         attrs = [('c:type', boxed.ctype),
321                  ('glib:name', boxed.name)]
322         if boxed.doc:
323             attrs.append(('doc', boxed.doc))
324         attrs.extend(self._boxed_attrs(boxed))
325         with self.tagcontext('glib:boxed', attrs):
326             for method in boxed.constructors:
327                 self._write_constructor(method)
328             for method in boxed.methods:
329                 self._write_method(method)
330
331     def _write_property(self, prop):
332         attrs = [('name', prop.name)]
333         self._append_version(prop, attrs)
334         self._append_deprecated(prop, attrs)
335         # Properties are assumed to be readable (see also generate.c)
336         if not prop.readable:
337             attrs.append(('readable', '0'))
338         if prop.writable:
339             attrs.append(('writable', '1'))
340         if prop.construct:
341             attrs.append(('construct', '1'))
342         if prop.construct_only:
343             attrs.append(('construct-only', '1'))
344         if prop.doc:
345             attrs.append(('doc', prop.doc))
346         with self.tagcontext('property', attrs):
347             self._write_type(prop.type)
348
349     def _write_callback(self, callback):
350         # FIXME: reuse _write_function
351         attrs = [('name', callback.name), ('c:type', callback.ctype)]
352         if callback.doc:
353             attrs.append(('doc', callback.doc))
354         self._append_version(callback, attrs)
355         self._append_deprecated(callback, attrs)
356         self._append_throws(callback, attrs)
357         with self.tagcontext('callback', attrs):
358             self._write_return_type(callback.retval)
359             self._write_parameters(callback.parameters)
360
361     def _boxed_attrs(self, boxed):
362         return [('glib:type-name', boxed.type_name),
363                 ('glib:get-type', boxed.get_type)]
364
365     def _write_record(self, record):
366         attrs = [('name', record.name),
367                  ('c:type', record.symbol)]
368         if record.disguised:
369             attrs.append(('disguised', '1'))
370         if record.doc:
371             attrs.append(('doc', record.doc))
372         self._append_version(record, attrs)
373         self._append_deprecated(record, attrs)
374         if isinstance(record, GLibBoxed):
375             attrs.extend(self._boxed_attrs(record))
376         with self.tagcontext('record', attrs):
377             if record.fields:
378                 for field in record.fields:
379                     self._write_field(field)
380             for method in record.constructors:
381                 self._write_constructor(method)
382             for method in record.methods:
383                 self._write_method(method)
384
385     def _write_union(self, union):
386         attrs = [('name', union.name),
387                  ('c:type', union.symbol)]
388         if union.doc:
389             attrs.append(('doc', union.doc))
390         self._append_version(union, attrs)
391         self._append_deprecated(union, attrs)
392         if isinstance(union, GLibBoxed):
393             attrs.extend(self._boxed_attrs(union))
394         with self.tagcontext('union', attrs):
395             if union.fields:
396                 for field in union.fields:
397                     self._write_field(field)
398             for method in union.constructors:
399                 self._write_constructor(method)
400             for method in union.methods:
401                 self._write_method(method)
402
403     def _write_field(self, field):
404         if isinstance(field, Function):
405             self._write_method(field)
406             return
407
408         if isinstance(field, Callback):
409             self._write_callback(field)
410             return
411
412         attrs = [('name', field.name)]
413         # Fields are assumed to be read-only
414         # (see also girparser.c and generate.c)
415         if not field.readable:
416             attrs.append(('readable', '0'))
417         if field.writable:
418             attrs.append(('writable', '1'))
419         if field.bits:
420             attrs.append(('bits', str(field.bits)))
421         with self.tagcontext('field', attrs):
422             self._write_type(field.type)
423
424     def _write_signal(self, signal):
425         attrs = [('name', signal.name)]
426         if signal.doc:
427             attrs.append(('doc', signal.doc))
428         self._append_version(signal, attrs)
429         self._append_deprecated(signal, attrs)
430         with self.tagcontext('glib:signal', attrs):
431             self._write_return_type(signal.retval)
432             self._write_parameters(signal.parameters)