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