Merge branch 'master' of ssh://walters@git.gnome.org/git/gobject-introspection
[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, pkgs):
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, pkgs)
43
44     def _write_repository(self, namespace, shlibs, includes=None,
45                           packages=None):
46         if includes is None:
47             includes = frozenset()
48         if packages is None:
49             packages = frozenset()
50         attrs = [
51             ('version', '1.0'),
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             self._write_namespace(namespace, shlibs)
62
63     def _write_include(self, include):
64         attrs = [('name', include.name), ('version', include.version)]
65         self.write_tag('include', attrs)
66
67     def _write_pkgconfig_pkg(self, package):
68         attrs = [('name', package)]
69         self.write_tag('package', attrs)
70
71     def _write_namespace(self, namespace, shlibs):
72         libraries = []
73         for l in shlibs:
74             found_libname = find_library(l)
75             if not found_libname:
76                 found_libname = l
77             libraries.append(os.path.basename(found_libname))
78
79         attrs = [('name', namespace.name),
80                  ('version', namespace.version),
81                  ('shared-library', ','.join(libraries))]
82         with self.tagcontext('namespace', attrs):
83             for node in namespace.nodes:
84                 self._write_node(node)
85
86     def _write_node(self, node):
87         if isinstance(node, Function):
88             self._write_function(node)
89         elif isinstance(node, Enum):
90             self._write_enum(node)
91         elif isinstance(node, Bitfield):
92             self._write_bitfield(node)
93         elif isinstance(node, (Class, Interface)):
94             self._write_class(node)
95         elif isinstance(node, Callback):
96             self._write_callback(node)
97         elif isinstance(node, Struct):
98             self._write_record(node)
99         elif isinstance(node, Union):
100             self._write_union(node)
101         elif isinstance(node, GLibBoxed):
102             self._write_boxed(node)
103         elif isinstance(node, Member):
104             # FIXME: atk_misc_instance singleton
105             pass
106         elif isinstance(node, Alias):
107             self._write_alias(node)
108         elif isinstance(node, Constant):
109             self._write_constant(node)
110         else:
111             print 'WRITER: Unhandled node', node
112
113     def _append_version(self, node, attrs):
114         if node.version:
115             attrs.append(('version', node.version))
116
117     def _append_deprecated(self, node, attrs):
118         if node.deprecated:
119             attrs.append(('deprecated', node.deprecated))
120             if node.deprecated_version:
121                 attrs.append(('deprecated-version',
122                               node.deprecated_version))
123
124     def _append_throws(self, func, attrs):
125         if func.throws:
126             attrs.append(('throws', '1'))
127
128     def _write_alias(self, alias):
129         attrs = [('name', alias.name), ('target', alias.target)]
130         if alias.ctype is not None:
131             attrs.append(('c:type', alias.ctype))
132         self.write_tag('alias', attrs)
133
134     def _write_function(self, func, tag_name='function'):
135         attrs = [('name', func.name),
136                  ('c:identifier', func.symbol)]
137         if func.doc:
138             attrs.append(('doc', func.doc))
139         self._append_version(func, attrs)
140         self._append_deprecated(func, attrs)
141         self._append_throws(func, attrs)
142         with self.tagcontext(tag_name, attrs):
143             self._write_return_type(func.retval)
144             self._write_parameters(func.parameters)
145
146     def _write_method(self, method):
147         self._write_function(method, tag_name='method')
148
149     def _write_static_method(self, method):
150         self._write_function(method, tag_name='function')
151
152     def _write_constructor(self, method):
153         self._write_function(method, tag_name='constructor')
154
155     def _write_return_type(self, return_):
156         if not return_:
157             return
158
159         assert return_.transfer is not None, return_
160
161         attrs = []
162         attrs.append(('transfer-ownership', return_.transfer))
163         if return_.doc:
164             attrs.append(('doc', return_.doc))
165         with self.tagcontext('return-value', attrs):
166             self._write_type(return_.type)
167
168     def _write_parameters(self, parameters):
169         if not parameters:
170             return
171         with self.tagcontext('parameters'):
172             for parameter in parameters:
173                 self._write_parameter(parameter)
174
175     def _write_parameter(self, parameter):
176         assert parameter.transfer is not None, parameter
177
178         attrs = []
179         if parameter.name is not None:
180             attrs.append(('name', parameter.name))
181         if parameter.direction != 'in':
182             attrs.append(('direction', parameter.direction))
183         attrs.append(('transfer-ownership',
184                      parameter.transfer))
185         if parameter.allow_none:
186             attrs.append(('allow-none', '1'))
187         if parameter.scope:
188             attrs.append(('scope', parameter.scope))
189         if parameter.closure_index >= 0:
190             attrs.append(('closure', '%d' % parameter.closure_index))
191         if parameter.destroy_index >= 0:
192             attrs.append(('destroy', '%d' % parameter.destroy_index))
193         if parameter.doc:
194             attrs.append(('doc', parameter.doc))
195         with self.tagcontext('parameter', attrs):
196             self._write_type(parameter.type)
197
198     def _type_to_string(self, ntype):
199         if isinstance(ntype, basestring):
200             return ntype
201         return ntype.name
202
203     def _write_type(self, ntype, relation=None):
204         if isinstance(ntype, basestring):
205             typename = ntype
206             type_cname = None
207         else:
208             typename = ntype.name
209             type_cname = ntype.ctype
210         if isinstance(ntype, Varargs):
211             with self.tagcontext('varargs', []):
212                 pass
213             return
214         if isinstance(ntype, Array):
215             attrs = []
216             if not ntype.zeroterminated:
217                 attrs.append(('zero-terminated', '0'))
218             if ntype.length_param_index >= 0:
219                 attrs.append(('length', '%d' % (ntype.length_param_index, )))
220             attrs.append(('c:type', ntype.ctype))
221             if ntype.size is not None:
222                 attrs.append(('fixed-size', ntype.size))
223             with self.tagcontext('array', attrs):
224                 self._write_type(ntype.element_type)
225             return
226         attrs = [('name', self._type_to_string(ntype))]
227         # FIXME: figure out if type references a basic type
228         #        or a boxed/class/interface etc. and skip
229         #        writing the ctype if the latter.
230         if type_cname is not None:
231             attrs.append(('c:type', type_cname))
232         if isinstance(ntype, List) and ntype.element_type:
233             with self.tagcontext('type', attrs):
234                 self._write_type(ntype.element_type)
235             return
236         if isinstance(ntype, Map) and ntype.key_type:
237             with self.tagcontext('type', attrs):
238                 self._write_type(ntype.key_type)
239                 self._write_type(ntype.value_type)
240             return
241         # Not a special type, just write it out
242         self.write_tag('type', attrs)
243
244     def _write_enum(self, enum):
245         attrs = [('name', enum.name)]
246         if enum.doc:
247             attrs.append(('doc', enum.doc))
248         self._append_version(enum, attrs)
249         self._append_deprecated(enum, attrs)
250         if isinstance(enum, GLibEnum):
251             attrs.extend([('glib:type-name', enum.type_name),
252                           ('glib:get-type', enum.get_type),
253                           ('c:type', enum.ctype)])
254             if enum.error_quark:
255                 attrs.append(('glib:error-quark', enum.error_quark))
256         else:
257             attrs.append(('c:type', enum.symbol))
258
259         with self.tagcontext('enumeration', attrs):
260             for member in enum.members:
261                 self._write_member(member)
262
263     def _write_bitfield(self, bitfield):
264         attrs = [('name', bitfield.name)]
265         if bitfield.doc:
266             attrs.append(('doc', bitfield.doc))
267         self._append_version(bitfield, attrs)
268         self._append_deprecated(bitfield, attrs)
269         if isinstance(bitfield, GLibFlags):
270             attrs.extend([('glib:type-name', bitfield.type_name),
271                           ('glib:get-type', bitfield.get_type),
272                           ('c:type', bitfield.ctype)])
273         else:
274             attrs.append(('c:type', bitfield.symbol))
275         with self.tagcontext('bitfield', attrs):
276             for member in bitfield.members:
277                 self._write_member(member)
278
279     def _write_member(self, member):
280         attrs = [('name', member.name),
281                  ('value', str(member.value)),
282                  ('c:identifier', member.symbol)]
283         if isinstance(member, GLibEnumMember):
284             attrs.append(('glib:nick', member.nick))
285         self.write_tag('member', attrs)
286
287     def _write_constant(self, constant):
288         attrs = [('name', constant.name),
289                  ('value', str(constant.value))]
290         with self.tagcontext('constant', attrs):
291             self._write_type(constant.type)
292
293     def _write_class(self, node):
294         attrs = [('name', node.name),
295                  ('c:type', node.ctype)]
296         if node.doc:
297             attrs.append(('doc', node.doc))
298         self._append_version(node, attrs)
299         self._append_deprecated(node, attrs)
300         if isinstance(node, Class):
301             tag_name = 'class'
302             if node.parent is not None:
303                 attrs.append(('parent', node.parent))
304             if node.is_abstract:
305                 attrs.append(('abstract', '1'))
306         else:
307             tag_name = 'interface'
308         if isinstance(node, (GLibObject, GLibInterface)):
309             attrs.append(('glib:type-name', node.type_name))
310             if node.get_type:
311                 attrs.append(('glib:get-type', node.get_type))
312         if isinstance(node, GLibObject):
313             if node.class_struct:
314                 attrs.append(('glib:class-struct', node.class_struct.name))
315         with self.tagcontext(tag_name, attrs):
316             if isinstance(node, GLibObject):
317                 for iface in node.interfaces:
318                     self.write_tag('implements', [('name', iface)])
319             if isinstance(node, Interface):
320                 for iface in node.prerequisites:
321                     self.write_tag('prerequisite', [('name', iface)])
322             if isinstance(node, Class):
323                 for method in node.constructors:
324                     self._write_constructor(method)
325                 for method in node.static_methods:
326                     self._write_static_method(method)
327             for method in node.methods:
328                 self._write_method(method)
329             for prop in node.properties:
330                 self._write_property(prop)
331             for field in node.fields:
332                 self._write_field(field)
333             for signal in node.signals:
334                 self._write_signal(signal)
335
336     def _write_boxed(self, boxed):
337         attrs = [('c:type', boxed.ctype),
338                  ('glib:name', boxed.name)]
339         if boxed.doc:
340             attrs.append(('doc', boxed.doc))
341         attrs.extend(self._boxed_attrs(boxed))
342         with self.tagcontext('glib:boxed', attrs):
343             for method in boxed.constructors:
344                 self._write_constructor(method)
345             for method in boxed.methods:
346                 self._write_method(method)
347
348     def _write_property(self, prop):
349         attrs = [('name', prop.name)]
350         self._append_version(prop, attrs)
351         self._append_deprecated(prop, attrs)
352         # Properties are assumed to be readable (see also generate.c)
353         if not prop.readable:
354             attrs.append(('readable', '0'))
355         if prop.writable:
356             attrs.append(('writable', '1'))
357         if prop.construct:
358             attrs.append(('construct', '1'))
359         if prop.construct_only:
360             attrs.append(('construct-only', '1'))
361         if prop.doc:
362             attrs.append(('doc', prop.doc))
363         with self.tagcontext('property', attrs):
364             self._write_type(prop.type)
365
366     def _write_callback(self, callback):
367         # FIXME: reuse _write_function
368         attrs = [('name', callback.name), ('c:type', callback.ctype)]
369         if callback.doc:
370             attrs.append(('doc', callback.doc))
371         self._append_version(callback, attrs)
372         self._append_deprecated(callback, attrs)
373         self._append_throws(callback, attrs)
374         with self.tagcontext('callback', attrs):
375             self._write_return_type(callback.retval)
376             self._write_parameters(callback.parameters)
377
378     def _boxed_attrs(self, boxed):
379         return [('glib:type-name', boxed.type_name),
380                 ('glib:get-type', boxed.get_type)]
381
382     def _write_record(self, record, extra_attrs=[]):
383         attrs = list(extra_attrs)
384         if record.name is not None:
385             attrs.append(('name', record.name))
386         if record.symbol is not None: # the record might be anonymous
387             attrs.append(('c:type', record.symbol))
388         if record.disguised:
389             attrs.append(('disguised', '1'))
390         if record.is_gobject_struct_for:
391             attrs.append(('glib:is-class-struct-for',
392                           record.is_gobject_struct_for))
393         if record.doc:
394             attrs.append(('doc', record.doc))
395         self._append_version(record, attrs)
396         self._append_deprecated(record, attrs)
397         if isinstance(record, GLibBoxed):
398             attrs.extend(self._boxed_attrs(record))
399         with self.tagcontext('record', attrs):
400             if record.fields:
401                 for field in record.fields:
402                     self._write_field(field)
403             for method in record.constructors:
404                 self._write_constructor(method)
405             for method in record.methods:
406                 self._write_method(method)
407
408     def _write_union(self, union):
409         attrs = []
410         if union.name is not None:
411             attrs.append(('name', union.name))
412         if union.symbol is not None: # the union might be anonymous
413             attrs.append(('c:type', union.symbol))
414         if union.doc:
415             attrs.append(('doc', union.doc))
416         self._append_version(union, attrs)
417         self._append_deprecated(union, attrs)
418         if isinstance(union, GLibBoxed):
419             attrs.extend(self._boxed_attrs(union))
420         with self.tagcontext('union', attrs):
421             if union.fields:
422                 for field in union.fields:
423                     self._write_field(field)
424             for method in union.constructors:
425                 self._write_constructor(method)
426             for method in union.methods:
427                 self._write_method(method)
428
429     def _write_field(self, field):
430         if isinstance(field, Function):
431             self._write_method(field)
432             return
433
434         if isinstance(field, Callback):
435             self._write_callback(field)
436         elif isinstance(field, Struct):
437             self._write_record(field)
438         elif isinstance(field, Union):
439             self._write_union(field)
440         else:
441             attrs = [('name', field.name)]
442             # Fields are assumed to be read-only
443             # (see also girparser.c and generate.c)
444             if not field.readable:
445                 attrs.append(('readable', '0'))
446             if field.writable:
447                 attrs.append(('writable', '1'))
448             if field.bits:
449                 attrs.append(('bits', str(field.bits)))
450             with self.tagcontext('field', attrs):
451                 self._write_type(field.type)
452
453     def _write_signal(self, signal):
454         attrs = [('name', signal.name)]
455         if signal.doc:
456             attrs.append(('doc', signal.doc))
457         self._append_version(signal, attrs)
458         self._append_deprecated(signal, attrs)
459         with self.tagcontext('glib:signal', attrs):
460             self._write_return_type(signal.retval)
461             self._write_parameters(signal.parameters)