mirror of
https://github.com/boostorg/python.git
synced 2026-01-22 05:22:45 +00:00
240 lines
8.0 KiB
Python
240 lines
8.0 KiB
Python
# 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):
|
|
'includes and defines ar the directives given to gcc'
|
|
if includes is None:
|
|
includes = []
|
|
if defines is None:
|
|
defines = []
|
|
self.includes = includes
|
|
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.
|
|
'''
|
|
temp = tempfile.mktemp('.h')
|
|
shutil.copyfile(filename, temp)
|
|
f = file(temp, 'a')
|
|
f.write('\n\n'+tail)
|
|
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 = 'gccxml %s %s %s -fxml=%s'
|
|
filename = self.Unixfy(filename)
|
|
xmlfile = self.Unixfy(xmlfile)
|
|
status = os.system(cmd % (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):
|
|
filename = os.path.basename(header)
|
|
filename = os.path.splitext(filename)[0] + '.xml'
|
|
shutil.copy(xmlfile, filename)
|
|
# 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.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)
|