Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/boost_1_33_1/libs/python/pyste/src/Pyste/CppParser.py @ 12

Last change on this file since 12 was 12, checked in by landauf, 17 years ago

added boost

File size: 8.4 KB
Line 
1# Copyright Bruno da Silva de Oliveira 2003. Use, modification and
2# distribution is subject to the Boost Software License, Version 1.0.
3# (See accompanying file LICENSE_1_0.txt or copy at
4# http:#www.boost.org/LICENSE_1_0.txt)
5
6from GCCXMLParser import ParseDeclarations
7import tempfile
8import shutil
9import os
10import sys
11import os.path
12import settings
13import shutil
14import shelve
15from cPickle import dump, load
16
17#==============================================================================
18# exceptions
19#==============================================================================
20class CppParserError(Exception): pass
21
22#==============================================================================
23# CppParser
24#==============================================================================
25class CppParser:
26    'Parses a header file and returns a list of declarations'
27   
28    def __init__(self, includes=None, defines=None, cache_dir=None, version=None, gccxml_path = 'gccxml'): 
29        'includes and defines ar the directives given to gcc'
30        if includes is None:
31            includes = []
32        if defines is None:
33            defines = []
34        self.includes = includes
35        self.gccxml_path = gccxml_path
36        self.defines = defines
37        self.version = version
38        #if cache_dir is None:
39        #    cache_dir = tempfile.mktemp()
40        #    self.delete_cache = True
41        #else:
42        #    self.delete_cache = False
43        self.delete_cache = False
44        self.cache_dir = cache_dir
45        self.cache_files = []
46        self.mem_cache = {}
47        # create the cache dir
48        if cache_dir:
49            try:
50                os.makedirs(cache_dir)
51            except OSError: pass 
52
53
54    def __del__(self):
55        self.Close()
56
57       
58    def _IncludeParams(self, filename):
59        includes = self.includes[:]
60        filedir = os.path.dirname(filename)
61        if not filedir:
62            filedir = '.'
63        includes.insert(0, filedir)
64        includes = ['-I "%s"' % self.Unixfy(x) for x in includes]
65        return ' '.join(includes)
66
67
68    def _DefineParams(self):
69        defines = ['-D "%s"' % x for x in self.defines]
70        return ' '.join(defines)
71   
72   
73    def FindHeader(self, header):
74        if os.path.isfile(header):
75            return header
76        for path in self.includes:
77            filename = os.path.join(path, header) 
78            if os.path.isfile(filename):
79                return filename
80        else:
81            name = os.path.basename(header)
82            raise RuntimeError, 'Header file "%s" not found!' % name
83   
84           
85    def AppendTail(self, filename, tail):
86        '''Creates a temporary file, appends the text tail to it, and returns
87        the filename of the file.
88        '''
89        if hasattr(tempfile, 'mkstemp'):
90            f_no, temp = tempfile.mkstemp('.h')
91            f = file(temp, 'a')
92            os.close(f_no)
93        else:
94            temp = tempfile.mktemp('.h') 
95            f = file(temp, 'a')
96        f.write('#include "%s"\n\n' % os.path.abspath(filename))
97        f.write(tail)
98        f.write('\n')
99        f.close()   
100        return temp
101
102
103    def Unixfy(self, path):
104        return path.replace('\\', '/')
105
106
107    def ParseWithGCCXML(self, header, tail):
108        '''Parses the given header using gccxml and GCCXMLParser.
109        '''
110        header = self.FindHeader(header) 
111        if tail:
112            filename = self.AppendTail(header, tail)
113        else:
114            filename = header
115        xmlfile = tempfile.mktemp('.xml')
116        try:           
117            # get the params
118            includes = self._IncludeParams(filename)
119            defines = self._DefineParams()
120            # call gccxml
121            cmd = '%s %s %s "%s" -fxml=%s' 
122            filename = self.Unixfy(filename)
123            xmlfile = self.Unixfy(xmlfile)
124            status = os.system(cmd % (self.gccxml_path, includes, defines, filename, xmlfile))
125            if status != 0 or not os.path.isfile(xmlfile):
126                raise CppParserError, 'Error executing gccxml'
127            # parse the resulting xml
128            declarations = ParseDeclarations(xmlfile)
129            # make the declarations' location to point to the original file
130            if tail:
131                for decl in declarations:
132                    decl_filename = os.path.normpath(os.path.normcase(decl.location[0]))
133                    filename = os.path.normpath(os.path.normcase(filename))
134                    if decl_filename == filename:
135                        decl.location = header, decl.location[1]
136            # return the declarations                         
137            return declarations
138        finally:
139            if settings.DEBUG and os.path.isfile(xmlfile):
140                debugname = os.path.basename(header)
141                debugname = os.path.splitext(debugname)[0] + '.xml'
142                print 'DEBUG:', debugname
143                shutil.copy(xmlfile, debugname)
144            # delete the temporary files
145            try:
146                os.remove(xmlfile)
147                if tail:
148                    os.remove(filename)
149            except OSError: pass               
150
151           
152    def Parse(self, header, interface, tail=None):
153        '''Parses the given filename related to the given interface and returns
154        the (declarations, headerfile). The header returned is normally the
155        same as the given to this method (except that it is the full path),
156        except if tail is not None: in this case, the header is copied to a temp
157        filename and the tail code is appended to it before being passed on to
158        gccxml.  This temp filename is then returned.
159        '''       
160        if tail is None:
161            tail = ''
162        tail = tail.strip()
163        declarations = self.GetCache(header, interface, tail)
164        if declarations is None:
165            declarations = self.ParseWithGCCXML(header, tail)
166            self.CreateCache(header, interface, tail, declarations)
167        header_fullpath = os.path.abspath(self.FindHeader(header))
168        return declarations, header_fullpath
169
170
171    def CacheFileName(self, interface):
172        interface_name = os.path.basename(interface)
173        cache_file = os.path.splitext(interface_name)[0] + '.pystec'
174        cache_file = os.path.join(self.cache_dir, cache_file) 
175        return cache_file
176       
177
178    def GetCache(self, header, interface, tail):
179        key = (header, interface, tail)
180        # try memory cache first
181        if key in self.mem_cache:
182            return self.mem_cache[key]
183       
184        # get the cache from the disk
185        if self.cache_dir is None:
186            return None 
187        header = self.FindHeader(header) 
188        cache_file = self.CacheFileName(interface)   
189        if os.path.isfile(cache_file):
190            f = file(cache_file, 'rb')
191            try:
192                version = load(f)
193                if version != self.version:
194                    return None
195                cache = load(f)
196                if cache.has_key(key):
197                    self.cache_files.append(cache_file)
198                    return cache[key]
199                else:
200                    return None
201            finally:
202                f.close()
203        else:
204            return None
205
206
207    def CreateCache(self, header, interface, tail, declarations):
208        key = (header, interface, tail)
209       
210        # our memory cache only holds one item
211        self.mem_cache.clear() 
212        self.mem_cache[key] = declarations
213
214        # save the cache in the disk
215        if self.cache_dir is None:
216            return
217        header = self.FindHeader(header) 
218        cache_file = self.CacheFileName(interface)
219        if os.path.isfile(cache_file):
220            f = file(cache_file, 'rb')
221            try:
222                version = load(f)
223                cache = load(f)
224            finally:
225                f.close()
226        else:
227            cache = {}
228        cache[key] = declarations
229        self.cache_files.append(cache_file)
230        f = file(cache_file, 'wb')
231        try:
232            dump(self.version, f, 1)
233            dump(cache, f, 1)
234        finally:
235            f.close()
236        return cache_file
237
238
239    def Close(self):
240        if self.delete_cache and self.cache_files:
241            for filename in self.cache_files:
242                try:
243                    os.remove(filename)
244                except OSError: 
245                    pass
246            self.cache_files = []
247            shutil.rmtree(self.cache_dir)
Note: See TracBrowser for help on using the repository browser.