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