Update .gitignore
[gnome.gobject-introspection] / giscanner / sourcescanner.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 import os
23 import subprocess
24 import tempfile
25
26 from .libtoolimporter import LibtoolImporter
27
28
29 (CSYMBOL_TYPE_INVALID,
30  CSYMBOL_TYPE_ELLIPSIS,
31  CSYMBOL_TYPE_CONST,
32  CSYMBOL_TYPE_OBJECT,
33  CSYMBOL_TYPE_FUNCTION,
34  CSYMBOL_TYPE_STRUCT,
35  CSYMBOL_TYPE_UNION,
36  CSYMBOL_TYPE_ENUM,
37  CSYMBOL_TYPE_TYPEDEF,
38  CSYMBOL_TYPE_MEMBER) = range(10)
39
40 (CTYPE_INVALID,
41  CTYPE_VOID,
42  CTYPE_BASIC_TYPE,
43  CTYPE_TYPEDEF,
44  CTYPE_STRUCT,
45  CTYPE_UNION,
46  CTYPE_ENUM,
47  CTYPE_POINTER,
48  CTYPE_ARRAY,
49  CTYPE_FUNCTION) = range(10)
50
51 STORAGE_CLASS_NONE = 0
52 STORAGE_CLASS_TYPEDEF = 1 << 1
53 STORAGE_CLASS_EXTERN = 1 << 2
54 STORAGE_CLASS_STATIC = 1 << 3
55 STORAGE_CLASS_AUTO = 1 << 4
56 STORAGE_CLASS_REGISTER = 1 << 5
57
58 TYPE_QUALIFIER_NONE = 0
59 TYPE_QUALIFIER_CONST = 1 << 1
60 TYPE_QUALIFIER_RESTRICT = 1 << 2
61 TYPE_QUALIFIER_VOLATILE = 1 << 3
62 TYPE_QUALIFIER_EXTENSION = 1 << 4
63
64 FUNCTION_NONE = 0
65 FUNCTION_INLINE = 1 << 1
66
67 (UNARY_ADDRESS_OF,
68  UNARY_POINTER_INDIRECTION,
69  UNARY_PLUS,
70  UNARY_MINUS,
71  UNARY_BITWISE_COMPLEMENT,
72  UNARY_LOGICAL_NEGATION) = range(6)
73
74
75 def symbol_type_name(symbol_type):
76     return {
77         CSYMBOL_TYPE_INVALID: 'invalid',
78         CSYMBOL_TYPE_ELLIPSIS: 'ellipsis',
79         CSYMBOL_TYPE_CONST: 'const',
80         CSYMBOL_TYPE_OBJECT: 'object',
81         CSYMBOL_TYPE_FUNCTION: 'function',
82         CSYMBOL_TYPE_STRUCT: 'struct',
83         CSYMBOL_TYPE_UNION: 'union',
84         CSYMBOL_TYPE_ENUM: 'enum',
85         CSYMBOL_TYPE_TYPEDEF: 'typedef',
86         CSYMBOL_TYPE_MEMBER: 'member',
87         }.get(symbol_type)
88
89
90 def ctype_name(ctype):
91     return {
92         CTYPE_INVALID: 'invalid',
93         CTYPE_VOID: 'void',
94         CTYPE_BASIC_TYPE: 'basic',
95         CTYPE_TYPEDEF: 'typedef',
96         CTYPE_STRUCT: 'struct',
97         CTYPE_UNION: 'union',
98         CTYPE_ENUM: 'enum',
99         CTYPE_POINTER: 'pointer',
100         CTYPE_ARRAY: 'array',
101         CTYPE_FUNCTION: 'function',
102         }.get(ctype)
103
104
105 class SourceType(object):
106     __members__ = ['type', 'base_type', 'name', 'type_qualifier',
107                    'child_list', 'is_bitfield']
108
109     def __init__(self, scanner, stype):
110         self._scanner = scanner
111         self._stype = stype
112
113     def __repr__(self):
114         return '<%s type=%r name=%r>' % (
115             self.__class__.__name__,
116             ctype_name(self.type),
117             self.name)
118
119     @property
120     def type(self):
121         return self._stype.type
122
123     @property
124     def base_type(self):
125         if self._stype.base_type is not None:
126             return SourceType(self._scanner, self._stype.base_type)
127
128     @property
129     def name(self):
130         return self._stype.name
131
132     @property
133     def type_qualifier(self):
134         return self._stype.type_qualifier
135
136     @property
137     def child_list(self):
138         for symbol in self._stype.child_list:
139             if symbol is None:
140                 continue
141             yield SourceSymbol(self._scanner, symbol)
142
143     @property
144     def is_bitfield(self):
145         return self._stype.is_bitfield
146
147
148 class SourceSymbol(object):
149     __members__ = ['const_int', 'const_double', 'const_string', 'ident',
150                    'type', 'base_type']
151
152     def __init__(self, scanner, symbol):
153         self._scanner = scanner
154         self._symbol = symbol
155
156     def __repr__(self):
157         return '<%s type=%r ident=%r>' % (
158             self.__class__.__name__,
159             symbol_type_name(self.type),
160             self.ident)
161
162     @property
163     def const_int(self):
164         return self._symbol.const_int
165
166     @property
167     def const_double(self):
168         return self._symbol.const_double
169
170     @property
171     def const_string(self):
172         return self._symbol.const_string
173
174     @property
175     def ident(self):
176         return self._symbol.ident
177
178     @property
179     def type(self):
180         return self._symbol.type
181
182     @property
183     def base_type(self):
184         if self._symbol.base_type is not None:
185             return SourceType(self._scanner, self._symbol.base_type)
186
187     @property
188     def source_filename(self):
189         return self._symbol.source_filename
190
191
192 class SourceScanner(object):
193
194     def __init__(self):
195         with LibtoolImporter:
196             from giscanner._giscanner import SourceScanner
197         self._scanner = SourceScanner()
198         self._filenames = []
199         self._cpp_options = []
200
201     # Public API
202
203     def set_cpp_options(self, includes, defines, undefines):
204         for prefix, args in [('-I', includes),
205                              ('-D', defines),
206                              ('-U', undefines)]:
207             for arg in (args or []):
208                 opt = prefix + arg
209                 if not opt in self._cpp_options:
210                     self._cpp_options.append(opt)
211
212     def parse_files(self, filenames):
213         for filename in filenames:
214             filename = os.path.abspath(filename)
215             self._scanner.append_filename(filename)
216
217         headers = []
218         for filename in filenames:
219             if filename.endswith('.c'):
220                 filename = os.path.abspath(filename)
221                 self._scanner.lex_filename(filename)
222             else:
223                 headers.append(filename)
224
225         self._parse(headers)
226         self._filenames.extend(headers)
227
228     def parse_macros(self, filenames):
229         self._scanner.set_macro_scan(True)
230         self._scanner.parse_macros(filenames)
231         self._scanner.set_macro_scan(False)
232
233     def get_symbols(self):
234         for symbol in self._scanner.get_symbols():
235             yield SourceSymbol(self._scanner, symbol)
236
237     def get_comments(self):
238         return self._scanner.get_comments()
239
240     def dump(self):
241         print '-'*30
242         for symbol in self._scanner.get_symbols():
243             print symbol.ident, symbol.base_type.name, symbol.type
244
245     # Private
246
247     def _parse(self, filenames):
248         if not filenames:
249             return
250
251         defines = ['__GI_SCANNER__']
252         undefs = []
253         cpp_args = ['cc', '-E', '-C', '-I.', '-']
254
255         cpp_args += self._cpp_options
256         proc = subprocess.Popen(cpp_args,
257                                 stdin=subprocess.PIPE,
258                                 stdout=subprocess.PIPE)
259
260         for define in defines:
261             proc.stdin.write('#ifndef %s\n' % (define, ))
262             proc.stdin.write('# define %s\n' % (define, ))
263             proc.stdin.write('#endif\n')
264         for undef in undefs:
265             proc.stdin.write('#undef %s\n' % (undef, ))
266         for filename in filenames:
267             filename = os.path.abspath(filename)
268             proc.stdin.write('#include <%s>\n' % (filename, ))
269         proc.stdin.close()
270
271         tmp = tempfile.mktemp()
272         fp = open(tmp, 'w+')
273         while True:
274             data = proc.stdout.read(4096)
275             if data is None:
276                 break
277             fp.write(data)
278             if len(data) < 4096:
279                 break
280         fp.seek(0, 0)
281         assert proc, 'Proc was none'
282         self._scanner.parse_file(fp.fileno())
283         fp.close()
284         os.unlink(tmp)