diff options
author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2013-06-25 22:59:01 +0000 |
---|---|---|
committer | <> | 2013-09-27 11:49:28 +0000 |
commit | 8c4528713d907ee2cfd3bfcbbad272c749867f84 (patch) | |
tree | c09e2ce80f47b90c85cc720f5139089ad9c8cfff /libs/python/pyste/src/Pyste/CppParser.py | |
download | boost-tarball-baserock/morph.tar.gz |
Imported from /home/lorry/working-area/delta_boost-tarball/boost_1_54_0.tar.bz2.boost_1_54_0baserock/morph
Diffstat (limited to 'libs/python/pyste/src/Pyste/CppParser.py')
-rw-r--r-- | libs/python/pyste/src/Pyste/CppParser.py | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/libs/python/pyste/src/Pyste/CppParser.py b/libs/python/pyste/src/Pyste/CppParser.py new file mode 100644 index 000000000..be68a448a --- /dev/null +++ b/libs/python/pyste/src/Pyste/CppParser.py @@ -0,0 +1,247 @@ +# Copyright Bruno da Silva de Oliveira 2003. Use, modification and +# distribution is subject to the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +from GCCXMLParser import ParseDeclarations +import tempfile +import shutil +import os +import sys +import os.path +import settings +import shutil +import shelve +from cPickle import dump, load + +#============================================================================== +# exceptions +#============================================================================== +class CppParserError(Exception): pass + +#============================================================================== +# CppParser +#============================================================================== +class CppParser: + 'Parses a header file and returns a list of declarations' + + def __init__(self, includes=None, defines=None, cache_dir=None, version=None, gccxml_path = 'gccxml'): + 'includes and defines ar the directives given to gcc' + if includes is None: + includes = [] + if defines is None: + defines = [] + self.includes = includes + self.gccxml_path = gccxml_path + self.defines = defines + self.version = version + #if cache_dir is None: + # cache_dir = tempfile.mktemp() + # self.delete_cache = True + #else: + # self.delete_cache = False + self.delete_cache = False + self.cache_dir = cache_dir + self.cache_files = [] + self.mem_cache = {} + # create the cache dir + if cache_dir: + try: + os.makedirs(cache_dir) + except OSError: pass + + + def __del__(self): + self.Close() + + + def _IncludeParams(self, filename): + includes = self.includes[:] + filedir = os.path.dirname(filename) + if not filedir: + filedir = '.' + includes.insert(0, filedir) + includes = ['-I "%s"' % self.Unixfy(x) for x in includes] + return ' '.join(includes) + + + def _DefineParams(self): + defines = ['-D "%s"' % x for x in self.defines] + return ' '.join(defines) + + + def FindHeader(self, header): + if os.path.isfile(header): + return header + for path in self.includes: + filename = os.path.join(path, header) + if os.path.isfile(filename): + return filename + else: + name = os.path.basename(header) + raise RuntimeError, 'Header file "%s" not found!' % name + + + def AppendTail(self, filename, tail): + '''Creates a temporary file, appends the text tail to it, and returns + the filename of the file. + ''' + if hasattr(tempfile, 'mkstemp'): + f_no, temp = tempfile.mkstemp('.h') + f = file(temp, 'a') + os.close(f_no) + else: + temp = tempfile.mktemp('.h') + f = file(temp, 'a') + f.write('#include "%s"\n\n' % os.path.abspath(filename)) + f.write(tail) + f.write('\n') + f.close() + return temp + + + def Unixfy(self, path): + return path.replace('\\', '/') + + + def ParseWithGCCXML(self, header, tail): + '''Parses the given header using gccxml and GCCXMLParser. + ''' + header = self.FindHeader(header) + if tail: + filename = self.AppendTail(header, tail) + else: + filename = header + xmlfile = tempfile.mktemp('.xml') + try: + # get the params + includes = self._IncludeParams(filename) + defines = self._DefineParams() + # call gccxml + cmd = '%s %s %s "%s" -fxml=%s' + filename = self.Unixfy(filename) + xmlfile = self.Unixfy(xmlfile) + status = os.system(cmd % (self.gccxml_path, includes, defines, filename, xmlfile)) + if status != 0 or not os.path.isfile(xmlfile): + raise CppParserError, 'Error executing gccxml' + # parse the resulting xml + declarations = ParseDeclarations(xmlfile) + # make the declarations' location to point to the original file + if tail: + for decl in declarations: + decl_filename = os.path.normpath(os.path.normcase(decl.location[0])) + filename = os.path.normpath(os.path.normcase(filename)) + if decl_filename == filename: + decl.location = header, decl.location[1] + # return the declarations + return declarations + finally: + if settings.DEBUG and os.path.isfile(xmlfile): + debugname = os.path.basename(header) + debugname = os.path.splitext(debugname)[0] + '.xml' + print 'DEBUG:', debugname + shutil.copy(xmlfile, debugname) + # delete the temporary files + try: + os.remove(xmlfile) + if tail: + os.remove(filename) + except OSError: pass + + + def Parse(self, header, interface, tail=None): + '''Parses the given filename related to the given interface and returns + the (declarations, headerfile). The header returned is normally the + same as the given to this method (except that it is the full path), + except if tail is not None: in this case, the header is copied to a temp + filename and the tail code is appended to it before being passed on to + gccxml. This temp filename is then returned. + ''' + if tail is None: + tail = '' + tail = tail.strip() + declarations = self.GetCache(header, interface, tail) + if declarations is None: + declarations = self.ParseWithGCCXML(header, tail) + self.CreateCache(header, interface, tail, declarations) + header_fullpath = os.path.abspath(self.FindHeader(header)) + return declarations, header_fullpath + + + def CacheFileName(self, interface): + interface_name = os.path.basename(interface) + cache_file = os.path.splitext(interface_name)[0] + '.pystec' + cache_file = os.path.join(self.cache_dir, cache_file) + return cache_file + + + def GetCache(self, header, interface, tail): + key = (header, interface, tail) + # try memory cache first + if key in self.mem_cache: + return self.mem_cache[key] + + # get the cache from the disk + if self.cache_dir is None: + return None + header = self.FindHeader(header) + cache_file = self.CacheFileName(interface) + if os.path.isfile(cache_file): + f = file(cache_file, 'rb') + try: + version = load(f) + if version != self.version: + return None + cache = load(f) + if cache.has_key(key): + self.cache_files.append(cache_file) + return cache[key] + else: + return None + finally: + f.close() + else: + return None + + + def CreateCache(self, header, interface, tail, declarations): + key = (header, interface, tail) + + # our memory cache only holds one item + self.mem_cache.clear() + self.mem_cache[key] = declarations + + # save the cache in the disk + if self.cache_dir is None: + return + header = self.FindHeader(header) + cache_file = self.CacheFileName(interface) + if os.path.isfile(cache_file): + f = file(cache_file, 'rb') + try: + version = load(f) + cache = load(f) + finally: + f.close() + else: + cache = {} + cache[key] = declarations + self.cache_files.append(cache_file) + f = file(cache_file, 'wb') + try: + dump(self.version, f, 1) + dump(cache, f, 1) + finally: + f.close() + return cache_file + + + def Close(self): + if self.delete_cache and self.cache_files: + for filename in self.cache_files: + try: + os.remove(filename) + except OSError: + pass + self.cache_files = [] + shutil.rmtree(self.cache_dir) |