# -*- Mode: Python -*- # GObject-Introspection - a framework for introspecting GObject libraries # Copyright (C) 2008 Johan Dahlin # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the # Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. # import os import tempfile from .message import Position from .ccompiler import CCompiler from .utils import have_debug_flag, dll_dirs dlldirs = dll_dirs() dlldirs.add_dll_dirs(['gio-2.0']) if 'UNINSTALLED_INTROSPECTION_SRCDIR' in os.environ: from _giscanner import SourceScanner as CSourceScanner else: from giscanner._giscanner import SourceScanner as CSourceScanner dlldirs.cleanup_dll_dirs() HEADER_EXTS = ['.h', '.hpp', '.hxx'] SOURCE_EXTS = ['.c', '.cpp', '.cc', '.cxx'] ALL_EXTS = SOURCE_EXTS + HEADER_EXTS (CSYMBOL_TYPE_INVALID, CSYMBOL_TYPE_ELLIPSIS, CSYMBOL_TYPE_CONST, CSYMBOL_TYPE_OBJECT, CSYMBOL_TYPE_FUNCTION, CSYMBOL_TYPE_FUNCTION_MACRO, CSYMBOL_TYPE_STRUCT, CSYMBOL_TYPE_UNION, CSYMBOL_TYPE_ENUM, CSYMBOL_TYPE_TYPEDEF, CSYMBOL_TYPE_MEMBER) = range(11) (CTYPE_INVALID, CTYPE_VOID, CTYPE_BASIC_TYPE, CTYPE_TYPEDEF, CTYPE_STRUCT, CTYPE_UNION, CTYPE_ENUM, CTYPE_POINTER, CTYPE_ARRAY, CTYPE_FUNCTION) = range(10) STORAGE_CLASS_NONE = 0 STORAGE_CLASS_TYPEDEF = 1 << 1 STORAGE_CLASS_EXTERN = 1 << 2 STORAGE_CLASS_STATIC = 1 << 3 STORAGE_CLASS_AUTO = 1 << 4 STORAGE_CLASS_REGISTER = 1 << 5 STORAGE_CLASS_THREAD_LOCAL = 1 << 6 TYPE_QUALIFIER_NONE = 0 TYPE_QUALIFIER_CONST = 1 << 1 TYPE_QUALIFIER_RESTRICT = 1 << 2 TYPE_QUALIFIER_VOLATILE = 1 << 3 TYPE_QUALIFIER_EXTENSION = 1 << 4 FUNCTION_NONE = 0 FUNCTION_INLINE = 1 << 1 (UNARY_ADDRESS_OF, UNARY_POINTER_INDIRECTION, UNARY_PLUS, UNARY_MINUS, UNARY_BITWISE_COMPLEMENT, UNARY_LOGICAL_NEGATION) = range(6) def symbol_type_name(symbol_type): return { CSYMBOL_TYPE_INVALID: 'invalid', CSYMBOL_TYPE_ELLIPSIS: 'ellipsis', CSYMBOL_TYPE_CONST: 'const', CSYMBOL_TYPE_OBJECT: 'object', CSYMBOL_TYPE_FUNCTION: 'function', CSYMBOL_TYPE_FUNCTION_MACRO: 'function_macro', CSYMBOL_TYPE_STRUCT: 'struct', CSYMBOL_TYPE_UNION: 'union', CSYMBOL_TYPE_ENUM: 'enum', CSYMBOL_TYPE_TYPEDEF: 'typedef', CSYMBOL_TYPE_MEMBER: 'member'}.get(symbol_type) def ctype_name(ctype): return { CTYPE_INVALID: 'invalid', CTYPE_VOID: 'void', CTYPE_BASIC_TYPE: 'basic', CTYPE_TYPEDEF: 'typedef', CTYPE_STRUCT: 'struct', CTYPE_UNION: 'union', CTYPE_ENUM: 'enum', CTYPE_POINTER: 'pointer', CTYPE_ARRAY: 'array', CTYPE_FUNCTION: 'function'}.get(ctype) class SourceType(object): __members__ = ['type', 'base_type', 'name', 'type_qualifier', 'child_list', 'is_bitfield'] def __init__(self, scanner, stype): self._scanner = scanner self._stype = stype def __repr__(self): return "<%s type='%s' name='%s'>" % ( self.__class__.__name__, ctype_name(self.type), self.name) @property def type(self): return self._stype.type @property def base_type(self): if self._stype.base_type is not None: return SourceType(self._scanner, self._stype.base_type) @property def name(self): return self._stype.name @property def type_qualifier(self): return self._stype.type_qualifier @property def child_list(self): for symbol in self._stype.child_list: if symbol is None: continue yield SourceSymbol(self._scanner, symbol) @property def is_bitfield(self): return self._stype.is_bitfield class SourceSymbol(object): __members__ = ['const_int', 'const_double', 'const_string', 'const_boolean', 'ident', 'type', 'base_type'] def __init__(self, scanner, symbol): self._scanner = scanner self._symbol = symbol def __repr__(self): src = self.source_filename if src: line = self.line if line: src += ":'%s'" % (line, ) return "<%s type='%s' ident='%s' src='%s'>" % ( self.__class__.__name__, symbol_type_name(self.type), self.ident, src) @property def const_int(self): return self._symbol.const_int @property def const_double(self): return self._symbol.const_double @property def const_string(self): return self._symbol.const_string @property def const_boolean(self): return self._symbol.const_boolean @property def ident(self): return self._symbol.ident @property def type(self): return self._symbol.type @property def base_type(self): if self._symbol.base_type is not None: return SourceType(self._scanner, self._symbol.base_type) @property def source_filename(self): return self._symbol.source_filename @property def line(self): return self._symbol.line @property def private(self): return self._symbol.private @property def position(self): return Position(self._symbol.source_filename, self._symbol.line) class SourceScanner(object): def __init__(self): self._scanner = CSourceScanner() self._filenames = [] self._cpp_options = [] # Public API def set_cpp_options(self, includes, defines, undefines, cflags=[]): self._cpp_options.extend(cflags) for prefix, args in [('-I', [os.path.realpath(f) for f in includes]), ('-D', defines), ('-U', undefines)]: for arg in (args or []): opt = prefix + arg if opt not in self._cpp_options: self._cpp_options.append(opt) def parse_files(self, filenames): for filename in filenames: # self._scanner expects file names to be canonicalized and symlinks to be resolved filename = os.path.realpath(filename) self._scanner.append_filename(filename) self._filenames.append(filename) headers = [] for filename in self._filenames: if os.path.splitext(filename)[1] in SOURCE_EXTS: self._scanner.lex_filename(filename) else: headers.append(filename) self._parse(headers) def parse_macros(self, filenames): self._scanner.set_macro_scan(True) # self._scanner expects file names to be canonicalized and symlinks to be resolved self._scanner.parse_macros([os.path.realpath(f) for f in filenames]) self._scanner.set_macro_scan(False) def get_symbols(self): for symbol in self._scanner.get_symbols(): yield SourceSymbol(self._scanner, symbol) def get_comments(self): return self._scanner.get_comments() def get_errors(self): return self._scanner.get_errors() def dump(self): print('-' * 30) for symbol in self._scanner.get_symbols(): print(symbol.ident, symbol.base_type.name, symbol.type) # Private def _parse(self, filenames): if not filenames: return defines = ['__GI_SCANNER__'] undefs = [] cc = CCompiler() tmp_fd_cpp, tmp_name_cpp = tempfile.mkstemp(prefix='g-ir-cpp-', suffix='.c', dir=os.getcwd()) with os.fdopen(tmp_fd_cpp, 'wb') as fp_cpp: self._write_preprocess_src(fp_cpp, defines, undefs, filenames) tmpfile_basename = os.path.basename(os.path.splitext(tmp_name_cpp)[0]) # Output file name of the preprocessor, only really used on non-MSVC, # so we want the name to match the output file name of the MSVC preprocessor tmpfile_output = tmpfile_basename + '.i' cc.preprocess(tmp_name_cpp, tmpfile_output, self._cpp_options) if not have_debug_flag('save-temps'): os.unlink(tmp_name_cpp) self._scanner.parse_file(tmpfile_output) if not have_debug_flag('save-temps'): os.unlink(tmpfile_output) def _write_preprocess_src(self, fp, defines, undefs, filenames): # Write to the temp file for feeding into the preprocessor for define in defines: fp.write(('#ifndef %s\n' % (define, )).encode()) fp.write(('# define %s\n' % (define, )).encode()) fp.write('#endif\n'.encode()) for undef in undefs: fp.write(('#undef %s\n' % (undef, )).encode()) for filename in filenames: fp.write(('#include <%s>\n' % (filename, )).encode())