Fail with better error if we can't find library.
[gnome.gobject-introspection] / giscanner / glibtransformer.py
1 # -*- Mode: Python -*-
2 # GObject-Introspection - a framework for introspecting GObject libraries
3 # Copyright (C) 2008  Johan Dahlin
4 #
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
9 #
10 # This program 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
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 # 02110-1301, USA.
19 #
20
21 import ctypes
22 from ctypes.util import find_library
23
24 from . import cgobject
25 from .ast import (Callback, Enum, Function, Member, Namespace, Parameter,
26                   Sequence, Property, Return, Struct, Type, Alias,
27                   Union, type_name_from_ctype)
28 from .transformer import Names
29 from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember, GLibFlags,
30                       GLibInterface, GLibObject, GLibSignal, type_names)
31 from .utils import extract_libtool, to_underscores
32
33
34 class Unresolved(object):
35
36     def __init__(self, target):
37         self.target = target
38
39
40 class UnknownTypeError(Exception):
41     pass
42
43
44 class GLibTransformer(object):
45
46     def __init__(self, transformer, noclosure=False):
47         self._transformer = transformer
48         self._namespace_name = None
49         self._names = Names()
50         self._uscore_type_names = {}
51         self._libraries = []
52         self._failed_types = {}
53         self._noclosure = noclosure
54         self._validating = False
55
56     # Public API
57
58     def add_library(self, libname):
59         # For testing mainly.
60         if libname.endswith('.la'):
61             found_libname = extract_libtool(libname)
62         else:
63             found_libname = find_library(libname)
64         if not found_libname:
65             raise ValueError("Failed to find library: %r" % (libname, ))
66         self._libraries.append(ctypes.cdll.LoadLibrary(found_libname))
67
68     def parse(self):
69         namespace = self._transformer.parse()
70         self._namespace_name = namespace.name
71
72         # First pass, parsing
73         for node in namespace.nodes:
74             self._parse_node(node)
75
76         # Introspection is done from within parsing
77
78         # Second pass, delete class structures, resolve
79         # all types we now know about
80         nodes = list(self._names.names.itervalues())
81         for (ns, node) in nodes:
82             self._resolve_node(node)
83             # associate GtkButtonClass with GtkButton
84             if isinstance(node, Struct):
85                 self._pair_class_struct(node)
86         for (ns, alias) in self._names.aliases.itervalues():
87             self._resolve_alias(alias)
88
89         # Third pass; ensure all types are known
90         if not self._noclosure:
91             self._validate(nodes)
92
93         # Create a new namespace with what we found
94         namespace = Namespace(namespace.name)
95         namespace.nodes = map(lambda x: x[1], self._names.aliases.itervalues())
96         for (ns, x) in self._names.names.itervalues():
97             namespace.nodes.append(x)
98         return namespace
99
100     # Private
101
102     def _add_attribute(self, node, replace=False):
103         node_name = node.name
104         if (not replace) and node_name in self._names.names:
105             return
106         self._names.names[node_name] = (None, node)
107
108     def _remove_attribute(self, name):
109         del self._names.names[name]
110
111     def _get_attribute(self, name):
112         node = self._names.names.get(name)
113         if node:
114             return node[1]
115         return None
116
117     def _register_internal_type(self, type_name, node):
118         self._names.type_names[type_name] = (None, node)
119         self._uscore_type_names[to_underscores(type_name).lower()] = node
120         # Besides the straight underscore conversion, we also try
121         # removing the underscores from the namespace as a possible C
122         # mapping; e.g. it's webkit_web_view, not web_kit_web_view
123         suffix = self._transformer.strip_namespace_object(type_name)
124         prefix = type_name[:-len(suffix)]
125         no_uscore_prefixed = (prefix + '_' + to_underscores(suffix)).lower()
126         self._uscore_type_names[no_uscore_prefixed] = node
127
128     # Helper functions
129
130     def _create_type(self, type_id):
131         ctype = cgobject.type_name(type_id)
132         type_name = type_name_from_ctype(ctype)
133         type_name = type_name.replace('*', '')
134         type_name = self._resolve_type_name(type_name)
135         return Type(type_name, ctype)
136
137     def _resolve_gtypename(self, gtype_name):
138         try:
139             return self._transformer.gtypename_to_giname(gtype_name,
140                                                          self._names)
141         except KeyError, e:
142             return Unresolved(gtype_name)
143
144     def _create_gobject(self, node):
145         type_name = 'G' + node.name
146         if type_name == 'GObject':
147             parent_gitype = None
148             symbol = 'intern'
149         else:
150             type_id = cgobject.type_from_name(type_name)
151             parent_type_name = cgobject.type_name(
152                 cgobject.type_parent(type_id))
153             parent_gitype = self._resolve_gtypename(parent_type_name)
154             symbol = to_underscores(type_name).lower() + '_get_type'
155         node = GLibObject(node.name, parent_gitype, type_name, symbol)
156         type_id = cgobject.TYPE_OBJECT
157         self._introspect_properties(node, type_id)
158         self._introspect_signals(node, type_id)
159         self._add_attribute(node)
160         self._register_internal_type(type_name, node)
161
162     # Parser
163
164     def _parse_node(self, node):
165         if isinstance(node, Enum):
166             self._parse_enum(node)
167         elif isinstance(node, Function):
168             self._parse_function(node)
169         elif isinstance(node, Struct):
170             self._parse_struct(node)
171         elif isinstance(node, Callback):
172             self._parse_callback(node)
173         elif isinstance(node, Alias):
174             self._parse_alias(node)
175         elif isinstance(node, Member):
176             # FIXME: atk_misc_instance singletons
177             pass
178         elif isinstance(node, Union):
179             self._parse_union(node)
180         else:
181             print 'GLIB Transformer: Unhandled node:', node
182
183     def _parse_alias(self, alias):
184         self._names.aliases[alias.name] = (None, alias)
185
186     def _parse_enum(self, enum):
187         self._add_attribute(enum)
188
189     def _parse_function(self, func):
190         if self._parse_get_type_function(func):
191             return
192
193         self._add_attribute(func)
194
195     def _parse_get_type_function(self, func):
196         symbol = func.symbol
197         if not symbol.endswith('_get_type'):
198             return False
199         # GType *_get_type(void)
200         # This is a bit fishy, why do we need all these aliases?
201         if func.retval.type.name not in ['Type',
202                                          'GType',
203                                          'Object.Type',
204                                          'GObject.Type',
205                                          'GObject.GType']:
206             print ("Warning: *_get_type function returns '%r'"
207                    ", not GObject.Type") % (func.retval.type.name, )
208             return False
209         if func.parameters:
210             return False
211
212         if not self._libraries:
213             print "Warning: No libraries loaded, cannot call %s" % (symbol, )
214             return False
215
216         for library in self._libraries:
217             try:
218                 func = getattr(library, symbol)
219                 break
220             except AttributeError:
221                 continue
222         else:
223             print 'Warning: could not find symbol: %s' % symbol
224             name = symbol.replace('_get_type', '')
225             self._failed_types[name] = True
226             return False
227
228         func.restype = cgobject.GType
229         func.argtypes = []
230         type_id = func()
231         self._introspect_type(type_id, symbol)
232         return True
233
234     def _name_is_internal_gtype(self, giname):
235         try:
236             node = self._get_attribute(giname)
237             return isinstance(node, (GLibObject, GLibInterface, GLibBoxed,
238                                      GLibEnum, GLibFlags))
239         except KeyError, e:
240             return False
241
242     def _parse_method(self, func):
243         if not func.parameters:
244             return False
245         return self._parse_method_common(func, True)
246
247     def _parse_constructor(self, func):
248         return self._parse_method_common(func, False)
249
250     def _parse_method_common(self, func, is_method):
251         if not is_method:
252             target_arg = func.retval
253         else:
254             target_arg = func.parameters[0]
255         target_arg.type = self._resolve_param_type(target_arg.type)
256
257         if is_method:
258             # Methods require their first arg to be a known class
259             # Look at the original C type (before namespace stripping), without
260             # pointers: GtkButton -> gtk_button_, so we can figure out the
261             # method name
262             argtype = target_arg.type.ctype.replace('*', '')
263             name = self._transformer.strip_namespace_object(argtype)
264             name_uscore = to_underscores(name).lower()
265             name_offset = func.symbol.find(name_uscore)
266             if name_offset < 0:
267                 return None
268             prefix = func.symbol[:name_offset+len(name_uscore)]
269         else:
270             # Constructors must have _new
271             # Take everything before that as class name
272             new_idx = func.symbol.find('_new')
273             if new_idx < 0:
274                 return None
275             # Constructors don't return basic types
276             if target_arg.type.name in type_names:
277                 return None
278             prefix = func.symbol[:new_idx]
279
280         klass = None
281         for key in self._uscore_type_names:
282             klass = None
283             if key.startswith(prefix):
284                 klass = self._uscore_type_names.get(key)
285                 if (klass is not None and
286                     isinstance(klass, (GLibObject, GLibBoxed,
287                                        GLibInterface))):
288                     break
289
290         if klass is None:
291             return
292
293         if not is_method:
294             # Interfaces can't have constructors, punt to global scope
295             if isinstance(klass, GLibInterface):
296                 return None
297             # TODO - check that the return type is a subclass of the
298             # class from the prefix
299
300         self._remove_attribute(func.name)
301         # Strip namespace and object prefix: gtk_window_new -> new
302         func.name = func.symbol[len(prefix)+1:]
303         if is_method:
304             klass.methods.append(func)
305         else:
306             klass.constructors.append(func)
307         return func
308
309     def _parse_struct(self, struct):
310         # This is a hack, but GObject is a rather fundamental piece so.
311         internal_names = ["Object", 'InitiallyUnowned']
312         g_internal_names = ["G" + x for x in internal_names]
313         if (self._namespace_name == 'GObject' and
314             struct.name in internal_names):
315             self._create_gobject(struct)
316             return
317         elif struct.name in g_internal_names:
318             # Avoid duplicates
319             return
320         node = self._names.names.get(struct.name)
321         if node is None:
322             self._add_attribute(struct, replace=True)
323             return
324         (ns, node) = node
325         node.fields = struct.fields[:]
326
327     def _parse_union(self, union):
328         node = self._names.names.get(union.name)
329         if node is None:
330             self._add_attribute(union, replace=True)
331             return
332         (ns, node) = node
333         node.fields = union.fields[:]
334
335     def _parse_callback(self, callback):
336         self._add_attribute(callback)
337
338     def _strip_class_suffix(self, name):
339         if (name.endswith('Class') or
340             name.endswith('Iface')):
341             return name[:-5]
342         elif name.endswith('Interface'):
343             return name[:-9]
344         else:
345             return name
346
347     def _arg_is_failed(self, param):
348         ctype = self._transformer.ctype_of(param).replace('*', '')
349         uscored = to_underscores(self._strip_class_suffix(ctype)).lower()
350         if uscored in self._failed_types:
351             print "Warning: failed type: %r" % (param, )
352             return True
353         return False
354
355     def _pair_class_struct(self, maybe_class):
356         name = self._strip_class_suffix(maybe_class.name)
357         if name == maybe_class.name:
358             return
359
360         if self._arg_is_failed(maybe_class):
361             print "WARNING: deleting no-type %r" % (maybe_class.name, )
362             del self._names.names[maybe_class.name]
363             return
364
365         name = self._resolve_type_name(name)
366         resolved = self._transformer.strip_namespace_object(name)
367         pair_class = self._get_attribute(resolved)
368         if pair_class and isinstance(pair_class,
369                                      (GLibObject, GLibBoxed, GLibInterface)):
370             for field in maybe_class.fields[1:]:
371                 pair_class.fields.append(field)
372             return
373         name = self._transformer.strip_namespace_object(maybe_class.name)
374         pair_class = self._get_attribute(name)
375         if pair_class and isinstance(pair_class,
376                                      (GLibObject, GLibBoxed, GLibInterface)):
377
378             del self._names.names[maybe_class.name]
379
380     # Introspection
381
382     def _introspect_type(self, type_id, symbol):
383         fundamental_type_id = cgobject.type_fundamental(type_id)
384         if (fundamental_type_id == cgobject.TYPE_ENUM or
385             fundamental_type_id == cgobject.TYPE_FLAGS):
386             self._introspect_enum(fundamental_type_id, type_id, symbol)
387         elif fundamental_type_id == cgobject.TYPE_OBJECT:
388             self._introspect_object(type_id, symbol)
389         elif fundamental_type_id == cgobject.TYPE_INTERFACE:
390             self._introspect_interface(type_id, symbol)
391         elif fundamental_type_id == cgobject.TYPE_BOXED:
392             self._introspect_boxed(type_id, symbol)
393         elif fundamental_type_id == cgobject.TYPE_BOXED:
394             self._introspect_boxed(type_id, symbol)
395         elif fundamental_type_id == cgobject.TYPE_POINTER:
396             # FIXME: Should we do something about these?
397             #        GHashTable, GValue and a few other fundamentals are
398             #        covered here
399             return
400         else:
401             print 'unhandled GType: %s(%d)' % (cgobject.type_name(type_id),
402                                                type_id)
403
404     def _introspect_enum(self, ftype_id, type_id, symbol):
405         type_class = cgobject.type_class_ref(type_id)
406         if type_class is None:
407             return
408
409         members = []
410         for enum_value in type_class.get_values():
411             members.append(GLibEnumMember(enum_value.value_nick,
412                                           enum_value.value,
413                                           enum_value.value_name,
414                                           enum_value.value_nick))
415
416         klass = (GLibFlags if ftype_id == cgobject.TYPE_FLAGS else GLibEnum)
417         type_name = cgobject.type_name(type_id)
418         enum_name = self._transformer.strip_namespace_object(type_name)
419         node = klass(enum_name, type_name, members, symbol)
420         self._add_attribute(node, replace=True)
421         self._register_internal_type(type_name, node)
422
423     def _introspect_object(self, type_id, symbol):
424         type_name = cgobject.type_name(type_id)
425         # We handle this specially above; in 2.16 and below there
426         # was no g_object_get_type, for later versions we need
427         # to skip it
428         if type_name == 'GObject':
429             return
430         parent_type_name = cgobject.type_name(cgobject.type_parent(type_id))
431         parent_gitype = self._resolve_gtypename(parent_type_name)
432         node = GLibObject(
433             self._transformer.strip_namespace_object(type_name),
434             parent_gitype,
435             type_name, symbol)
436         self._introspect_properties(node, type_id)
437         self._introspect_signals(node, type_id)
438         self._add_attribute(node, replace=True)
439         self._register_internal_type(type_name, node)
440
441     def _introspect_interface(self, type_id, symbol):
442         type_name = cgobject.type_name(type_id)
443         parent_type_name = cgobject.type_name(cgobject.type_parent(type_id))
444         if parent_type_name == 'GInterface':
445             parent_gitype = None
446         else:
447             parent_gitype = self._resolve_gtypename(parent_type_name)
448         node = GLibInterface(
449             self._transformer.strip_namespace_object(type_name),
450             parent_gitype,
451             type_name, symbol)
452         self._introspect_properties(node, type_id)
453         self._introspect_signals(node, type_id)
454         self._add_attribute(node, replace=True)
455         self._register_internal_type(type_name, node)
456
457     def _introspect_boxed(self, type_id, symbol):
458         type_name = cgobject.type_name(type_id)
459         node = GLibBoxed(self._transformer.strip_namespace_object(type_name),
460                          type_name, symbol)
461         self._add_attribute(node, replace=True)
462         self._register_internal_type(type_name, node)
463
464     def _introspect_properties(self, node, type_id):
465         fundamental_type_id = cgobject.type_fundamental(type_id)
466         if fundamental_type_id == cgobject.TYPE_OBJECT:
467             pspecs = cgobject.object_class_list_properties(type_id)
468         elif fundamental_type_id == cgobject.TYPE_INTERFACE:
469             pspecs = cgobject.object_interface_list_properties(type_id)
470         else:
471             raise AssertionError
472
473         for pspec in pspecs:
474             if pspec.owner_type != type_id:
475                 continue
476             ctype = cgobject.type_name(pspec.value_type)
477             node.properties.append(Property(
478                 pspec.name,
479                 type_name_from_ctype(ctype),
480                 ctype,
481                 ))
482
483     def _introspect_signals(self, node, type_id):
484         for signal_info in cgobject.signal_list(type_id):
485             rtype = self._create_type(signal_info.return_type)
486             return_ = Return(rtype)
487             signal = GLibSignal(signal_info.signal_name, return_)
488             for i, parameter in enumerate(signal_info.get_params()):
489                 if i == 0:
490                     name = 'object'
491                 else:
492                     name = 'p%s' % (i-1, )
493                 ptype = self._create_type(parameter)
494                 param = Parameter(name, ptype)
495                 signal.parameters.append(param)
496             node.signals.append(signal)
497
498     # Resolver
499
500     def _resolve_type_name(self, type_name, ctype=None):
501         # Workaround glib bug #548689, to be included in 2.18.0
502         if type_name == "GParam":
503             type_name = "GObject.ParamSpec"
504
505         res = self._transformer.resolve_type_name_full
506         try:
507             return res(type_name, ctype, self._names)
508         except KeyError, e:
509             return self._transformer.resolve_type_name(type_name, ctype)
510
511     def _validate_type_name(self, name):
512         if name in type_names:
513             return True
514         if name.find('.') >= 0:
515             return True
516         if name in self._names.aliases:
517             return True
518         if name in self._names.names:
519             return True
520         return False
521
522     def _validate_type(self, ptype):
523         if isinstance(ptype, Sequence):
524             etype = ptype.element_type
525             if isinstance(etype, Sequence):
526                 return self._validate_type(etype)
527             return self._validate_type_name(etype)
528         return self._validate_type_name(ptype.name)
529
530     def _resolve_param_type_validate(self, ptype):
531         ptype = self._resolve_param_type(ptype)
532         if self._validating and not self._validate_type(ptype):
533             raise UnknownTypeError("Unknown type %r" % (ptype, ))
534         return ptype
535
536     def _resolve_param_type(self, ptype):
537         try:
538             return self._transformer.resolve_param_type_full(ptype,
539                                                              self._names)
540         except KeyError, e:
541             return self._transformer.resolve_param_type(ptype)
542         return ptype
543
544     def _resolve_node(self, node):
545         if isinstance(node, Function):
546             self._resolve_function_toplevel(node)
547
548         elif isinstance(node, Callback):
549             self._resolve_function(node)
550         elif isinstance(node, GLibObject):
551             self._resolve_glib_object(node)
552         elif isinstance(node, GLibInterface):
553             self._resolve_glib_interface(node)
554         elif isinstance(node, GLibBoxed):
555             self._resolve_glib_boxed(node)
556         elif isinstance(node, Struct):
557             self._resolve_struct(node)
558         elif isinstance(node, Union):
559             self._resolve_union(node)
560         elif isinstance(node, Alias):
561             self._resolve_alias(node)
562
563     def _resolve_function_toplevel(self, func):
564         newfunc = self._parse_constructor(func)
565         if not newfunc:
566             newfunc = self._parse_method(func)
567             if not newfunc:
568                 self._resolve_function(func)
569                 return
570         self._resolve_function(newfunc)
571
572     def _resolve_struct(self, node):
573         for field in node.fields:
574             self._resolve_field(field)
575
576     def _resolve_union(self, node):
577         for field in node.fields:
578             self._resolve_field(field)
579
580     def _resolve_parent(self, node):
581         if not isinstance(node, (GLibInterface, GLibObject)):
582             raise AssertionError
583         if isinstance(node.parent, Unresolved):
584             node.parent = \
585                 self._transformer.gtypename_to_giname(node.parent.target,
586                                                       self._names)
587
588     def _resolve_glib_interface(self, node):
589         self._resolve_parent(node)
590         self._resolve_methods(node.methods)
591         self._resolve_properties(node.properties)
592         self._resolve_signals(node.signals)
593
594     def _resolve_glib_object(self, node):
595         self._resolve_parent(node)
596         self._resolve_constructors(node.constructors)
597         self._resolve_methods(node.methods)
598         self._resolve_properties(node.properties)
599         self._resolve_signals(node.signals)
600
601     def _resolve_glib_boxed(self, node):
602         self._resolve_constructors(node.constructors)
603         self._resolve_methods(node.methods)
604
605     def _resolve_constructors(self, constructors):
606         for ctor in constructors:
607             self._resolve_function(ctor)
608
609     def _resolve_methods(self, methods):
610         for method in methods:
611             self._resolve_function(method)
612
613     def _resolve_signals(self, signals):
614         for signal in signals:
615             self._resolve_function(signal)
616
617     def _resolve_properties(self, properties):
618         for prop in properties:
619             self._resolve_property(prop)
620
621     def _resolve_property(self, prop):
622         prop.type = self._resolve_param_type(prop.type)
623
624     def _resolve_function(self, func):
625         self._resolve_parameters(func.parameters)
626         func.retval.type = self._resolve_param_type(func.retval.type)
627
628     def _resolve_parameters(self, parameters):
629         for parameter in parameters:
630             parameter.type = self._resolve_param_type(parameter.type)
631
632     def _resolve_field(self, field):
633         if isinstance(field, Callback):
634             self._resolve_function(field)
635             return
636         field.type = self._resolve_param_type(field.type)
637
638     def _resolve_alias(self, alias):
639         alias.target = self._resolve_type_name(alias.target, alias.target)
640
641     # Validation
642
643     def _validate(self, nodes):
644         nodes = list(self._names.names.itervalues())
645         i = 0
646         self._validating = True
647         while True:
648             initlen = len(nodes)
649             print "Type resolution; pass=%d (%d nodes)" % (i, initlen)
650             nodes = list(self._names.names.itervalues())
651             for node in nodes:
652                 try:
653                     self._resolve_node(node)
654                 except UnknownTypeError, e:
655                     print "WARNING: %s: Deleting %r" % (e, node)
656                     self._remove_attribute(node.name)
657             if len(nodes) == initlen:
658                 break
659             i += 1
660         self._validating = False