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