Imported Upstream version 1.61.1
[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 import os
22 import tempfile
23
24 from .libtoolimporter import LibtoolImporter
25 from .message import Position
26 from .ccompiler import CCompiler
27 from .utils import have_debug_flag
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 HEADER_EXTS = ['.h', '.hpp', '.hxx']
36 SOURCE_EXTS = ['.c', '.cpp', '.cc', '.cxx']
37 ALL_EXTS = SOURCE_EXTS + HEADER_EXTS
38
39 (CSYMBOL_TYPE_INVALID,
40  CSYMBOL_TYPE_ELLIPSIS,
41  CSYMBOL_TYPE_CONST,
42  CSYMBOL_TYPE_OBJECT,
43  CSYMBOL_TYPE_FUNCTION,
44  CSYMBOL_TYPE_FUNCTION_MACRO,
45  CSYMBOL_TYPE_STRUCT,
46  CSYMBOL_TYPE_UNION,
47  CSYMBOL_TYPE_ENUM,
48  CSYMBOL_TYPE_TYPEDEF,
49  CSYMBOL_TYPE_MEMBER) = range(11)
50
51 (CTYPE_INVALID,
52  CTYPE_VOID,
53  CTYPE_BASIC_TYPE,
54  CTYPE_TYPEDEF,
55  CTYPE_STRUCT,
56  CTYPE_UNION,
57  CTYPE_ENUM,
58  CTYPE_POINTER,
59  CTYPE_ARRAY,
60  CTYPE_FUNCTION) = range(10)
61
62 STORAGE_CLASS_NONE = 0
63 STORAGE_CLASS_TYPEDEF = 1 << 1
64 STORAGE_CLASS_EXTERN = 1 << 2
65 STORAGE_CLASS_STATIC = 1 << 3
66 STORAGE_CLASS_AUTO = 1 << 4
67 STORAGE_CLASS_REGISTER = 1 << 5
68 STORAGE_CLASS_THREAD_LOCAL = 1 << 6
69
70 TYPE_QUALIFIER_NONE = 0
71 TYPE_QUALIFIER_CONST = 1 << 1
72 TYPE_QUALIFIER_RESTRICT = 1 << 2
73 TYPE_QUALIFIER_VOLATILE = 1 << 3
74 TYPE_QUALIFIER_EXTENSION = 1 << 4
75
76 FUNCTION_NONE = 0
77 FUNCTION_INLINE = 1 << 1
78
79 (UNARY_ADDRESS_OF,
80  UNARY_POINTER_INDIRECTION,
81  UNARY_PLUS,
82  UNARY_MINUS,
83  UNARY_BITWISE_COMPLEMENT,
84  UNARY_LOGICAL_NEGATION) = range(6)
85
86
87 def symbol_type_name(symbol_type):
88     return {
89         CSYMBOL_TYPE_INVALID: 'invalid',
90         CSYMBOL_TYPE_ELLIPSIS: 'ellipsis',
91         CSYMBOL_TYPE_CONST: 'const',
92         CSYMBOL_TYPE_OBJECT: 'object',
93         CSYMBOL_TYPE_FUNCTION: 'function',
94         CSYMBOL_TYPE_FUNCTION_MACRO: 'function_macro',
95         CSYMBOL_TYPE_STRUCT: 'struct',
96         CSYMBOL_TYPE_UNION: 'union',
97         CSYMBOL_TYPE_ENUM: 'enum',
98         CSYMBOL_TYPE_TYPEDEF: 'typedef',
99         CSYMBOL_TYPE_MEMBER: 'member'}.get(symbol_type)
100
101
102 def ctype_name(ctype):
103     return {
104         CTYPE_INVALID: 'invalid',
105         CTYPE_VOID: 'void',
106         CTYPE_BASIC_TYPE: 'basic',
107         CTYPE_TYPEDEF: 'typedef',
108         CTYPE_STRUCT: 'struct',
109         CTYPE_UNION: 'union',
110         CTYPE_ENUM: 'enum',
111         CTYPE_POINTER: 'pointer',
112         CTYPE_ARRAY: 'array',
113         CTYPE_FUNCTION: 'function'}.get(ctype)
114
115
116 class SourceType(object):
117     __members__ = ['type', 'base_type', 'name', 'type_qualifier',
118                    'child_list', 'is_bitfield']
119
120     def __init__(self, scanner, stype):
121         self._scanner = scanner
122         self._stype = stype
123
124     def __repr__(self):
125         return "<%s type='%s' name='%s'>" % (
126             self.__class__.__name__,
127             ctype_name(self.type),
128             self.name)
129
130     @property
131     def type(self):
132         return self._stype.type
133
134     @property
135     def base_type(self):
136         if self._stype.base_type is not None:
137             return SourceType(self._scanner, self._stype.base_type)
138
139     @property
140     def name(self):
141         return self._stype.name
142
143     @property
144     def type_qualifier(self):
145         return self._stype.type_qualifier
146
147     @property
148     def child_list(self):
149         for symbol in self._stype.child_list:
150             if symbol is None:
151                 continue
152             yield SourceSymbol(self._scanner, symbol)
153
154     @property
155     def is_bitfield(self):
156         return self._stype.is_bitfield
157
158
159 class SourceSymbol(object):
160     __members__ = ['const_int', 'const_double', 'const_string', 'const_boolean',
161                    'ident', 'type', 'base_type']
162
163     def __init__(self, scanner, symbol):
164         self._scanner = scanner
165         self._symbol = symbol
166
167     def __repr__(self):
168         src = self.source_filename
169         if src:
170             line = self.line
171             if line:
172                 src += ":'%s'" % (line, )
173         return "<%s type='%s' ident='%s' src='%s'>" % (
174             self.__class__.__name__,
175             symbol_type_name(self.type),
176             self.ident,
177             src)
178
179     @property
180     def const_int(self):
181         return self._symbol.const_int
182
183     @property
184     def const_double(self):
185         return self._symbol.const_double
186
187     @property
188     def const_string(self):
189         return self._symbol.const_string
190
191     @property
192     def const_boolean(self):
193         return self._symbol.const_boolean
194
195     @property
196     def ident(self):
197         return self._symbol.ident
198
199     @property
200     def type(self):
201         return self._symbol.type
202
203     @property
204     def base_type(self):
205         if self._symbol.base_type is not None:
206             return SourceType(self._scanner, self._symbol.base_type)
207
208     @property
209     def source_filename(self):
210         return self._symbol.source_filename
211
212     @property
213     def line(self):
214         return self._symbol.line
215
216     @property
217     def private(self):
218         return self._symbol.private
219
220     @property
221     def position(self):
222         return Position(self._symbol.source_filename,
223                         self._symbol.line)
224
225
226 class SourceScanner(object):
227
228     def __init__(self):
229         self._scanner = CSourceScanner()
230         self._filenames = []
231         self._cpp_options = []
232
233     # Public API
234
235     def set_cpp_options(self, includes, defines, undefines, cflags=[]):
236         self._cpp_options.extend(cflags)
237         for prefix, args in [('-I', [os.path.realpath(f) for f in includes]),
238                              ('-D', defines),
239                              ('-U', undefines)]:
240             for arg in (args or []):
241                 opt = prefix + arg
242                 if opt not in self._cpp_options:
243                     self._cpp_options.append(opt)
244
245     def parse_files(self, filenames):
246         for filename in filenames:
247             # self._scanner expects file names to be canonicalized and symlinks to be resolved
248             filename = os.path.realpath(filename)
249             self._scanner.append_filename(filename)
250             self._filenames.append(filename)
251
252         headers = []
253         for filename in self._filenames:
254             if os.path.splitext(filename)[1] in SOURCE_EXTS:
255                 self._scanner.lex_filename(filename)
256             else:
257                 headers.append(filename)
258
259         self._parse(headers)
260
261     def parse_macros(self, filenames):
262         self._scanner.set_macro_scan(True)
263         # self._scanner expects file names to be canonicalized and symlinks to be resolved
264         self._scanner.parse_macros([os.path.realpath(f) for f in filenames])
265         self._scanner.set_macro_scan(False)
266
267     def get_symbols(self):
268         for symbol in self._scanner.get_symbols():
269             yield SourceSymbol(self._scanner, symbol)
270
271     def get_comments(self):
272         return self._scanner.get_comments()
273
274     def get_errors(self):
275         return self._scanner.get_errors()
276
277     def dump(self):
278         print('-' * 30)
279         for symbol in self._scanner.get_symbols():
280             print(symbol.ident, symbol.base_type.name, symbol.type)
281
282     # Private
283
284     def _parse(self, filenames):
285         if not filenames:
286             return
287
288         defines = ['__GI_SCANNER__']
289         undefs = []
290
291         cc = CCompiler()
292
293         tmp_fd_cpp, tmp_name_cpp = tempfile.mkstemp(prefix='g-ir-cpp-',
294                                                     suffix='.c',
295                                                     dir=os.getcwd())
296         with os.fdopen(tmp_fd_cpp, 'wb') as fp_cpp:
297             self._write_preprocess_src(fp_cpp, defines, undefs, filenames)
298
299         tmpfile_basename = os.path.basename(os.path.splitext(tmp_name_cpp)[0])
300
301         # Output file name of the preprocessor, only really used on non-MSVC,
302         # so we want the name to match the output file name of the MSVC preprocessor
303         tmpfile_output = tmpfile_basename + '.i'
304
305         cc.preprocess(tmp_name_cpp,
306                       tmpfile_output,
307                       self._cpp_options)
308
309         if not have_debug_flag('save-temps'):
310             os.unlink(tmp_name_cpp)
311         self._scanner.parse_file(tmpfile_output)
312         if not have_debug_flag('save-temps'):
313             os.unlink(tmpfile_output)
314
315     def _write_preprocess_src(self, fp, defines, undefs, filenames):
316         # Write to the temp file for feeding into the preprocessor
317         for define in defines:
318             fp.write(('#ifndef %s\n' % (define, )).encode())
319             fp.write(('# define %s\n' % (define, )).encode())
320             fp.write('#endif\n'.encode())
321         for undef in undefs:
322             fp.write(('#undef %s\n' % (undef, )).encode())
323         for filename in filenames:
324             fp.write(('#include <%s>\n' % (filename, )).encode())