22import tempfile
33import shutil
44import os
5+ import sys
56import os .path
67import settings
8+ import shutil
9+ import shelve
10+ from cPickle import dump , load
711
12+ #==============================================================================
13+ # exceptions
14+ #==============================================================================
815class CppParserError (Exception ): pass
916
10-
17+ #==============================================================================
18+ # CppParser
19+ #==============================================================================
1120class CppParser :
1221 'Parses a header file and returns a list of declarations'
1322
14- def __init__ (self , includes = None , defines = None ):
23+ def __init__ (self , includes = None , defines = None , cache_dir = None ):
1524 'includes and defines ar the directives given to gcc'
1625 if includes is None :
1726 includes = []
1827 if defines is None :
1928 defines = []
2029 self .includes = includes
2130 self .defines = defines
31+ #if cache_dir is None:
32+ # cache_dir = tempfile.mktemp()
33+ # self.delete_cache = True
34+ #else:
35+ # self.delete_cache = False
36+ self .delete_cache = False
37+ self .cache_dir = cache_dir
38+ self .cache_files = []
39+ # create the cache dir
40+ if cache_dir :
41+ try :
42+ os .makedirs (cache_dir )
43+ except OSError : pass
44+
2245
46+ def __del__ (self ):
47+ self .Close ()
2348
24- def _includeparams (self , filename ):
49+
50+ def _IncludeParams (self , filename ):
2551 includes = self .includes [:]
2652 filedir = os .path .dirname (filename )
2753 if not filedir :
@@ -31,64 +57,153 @@ def _includeparams(self, filename):
3157 return ' ' .join (includes )
3258
3359
34- def _defineparams (self ):
60+ def _DefineParams (self ):
3561 defines = ['-D "%s"' % x for x in self .defines ]
3662 return ' ' .join (defines )
3763
3864
39- def FindFileName (self , include ):
40- if os .path .isfile (include ):
41- return include
65+ def FindHeader (self , header ):
66+ if os .path .isfile (header ):
67+ return header
4268 for path in self .includes :
43- filename = os .path .join (path , include )
69+ filename = os .path .join (path , header )
4470 if os .path .isfile (filename ):
4571 return filename
46- name = os .path .basename (include )
47- raise RuntimeError , 'Header file "%s" not found!' % name
72+ else :
73+ name = os .path .basename (header )
74+ raise RuntimeError , 'Header file "%s" not found!' % name
4875
4976
50- def parse (self , include , tail = None , decl_name = None ):
51- '''Parses the given filename, and returns (declaration, header). The
52- header returned is normally the same as the given to this method,
53- except if tail is not None: in this case, the header is copied to a temp
54- filename and the tail code is appended to it before being passed on to gcc.
55- This temp filename is then returned.
56- '''
57- filename = self .FindFileName (include )
58- # copy file to temp folder, if needed
77+ def AppendTail (self , filename , tail ):
78+ '''Creates a temporary file, appends the text tail to it, and returns
79+ the filename of the file.
80+ '''
81+ temp = tempfile .mktemp ('.h' )
82+ shutil .copyfile (filename , temp )
83+ f = file (temp , 'a' )
84+ f .write ('\n \n ' + tail )
85+ f .close ()
86+ return temp
87+
88+
89+ def ParseWithGCCXML (self , header , tail ):
90+ '''Parses the given header using gccxml and GCCXMLParser.
91+ '''
92+ header = self .FindHeader (header )
5993 if tail :
60- tempfilename = tempfile .mktemp ('.h' )
61- infilename = tempfilename
62- shutil .copyfile (filename , infilename )
63- f = file (infilename , 'a' )
64- f .write ('\n \n ' + tail )
65- f .close ()
94+ filename = self .AppendTail (header , tail )
6695 else :
67- infilename = filename
96+ filename = header
6897 xmlfile = tempfile .mktemp ('.xml' )
6998 try :
7099 # get the params
71- includes = self ._includeparams (filename )
72- defines = self ._defineparams ()
100+ includes = self ._IncludeParams (filename )
101+ defines = self ._DefineParams ()
73102 # call gccxml
74- cmd = 'gccxml %s %s %s -fxml=%s' \
75- % (includes , defines , infilename , xmlfile )
76- if decl_name is not None :
77- cmd += ' "-fxml-start=%s"' % decl_name
78- status = os .system (cmd )
103+ cmd = 'gccxml %s %s %s -fxml=%s'
104+ status = os .system (cmd % (includes , defines , filename , xmlfile ))
79105 if status != 0 or not os .path .isfile (xmlfile ):
80106 raise CppParserError , 'Error executing gccxml'
81107 # parse the resulting xml
82108 declarations = ParseDeclarations (xmlfile )
109+ # make the declarations' location to point to the original file
110+ if tail :
111+ for decl in declarations :
112+ decl_filename = os .path .normpath (os .path .normcase (decl .location [0 ]))
113+ filename = os .path .normpath (os .path .normcase (filename ))
114+ if decl_filename == filename :
115+ decl .location = header , decl .location [1 ]
83116 # return the declarations
84- return declarations , infilename
117+ return declarations
85118 finally :
86119 if settings .DEBUG and os .path .isfile (xmlfile ):
87- filename = os .path .basename (include )
88- shutil .copy (xmlfile , os .path .splitext (filename )[0 ] + '.xml' )
120+ filename = os .path .basename (header )
121+ filename = os .path .splitext (filename )[0 ] + '.xml'
122+ shutil .copy (xmlfile , filename )
89123 # delete the temporary files
90124 try :
91125 os .remove (xmlfile )
92126 if tail :
93- os .remove (tempfilename )
94- except OSError : pass
127+ os .remove (filename )
128+ except OSError : pass
129+
130+
131+ def Parse (self , header , interface , tail = None ):
132+ '''Parses the given filename related to the given interface and returns
133+ the (declarations, headerfile). The header returned is normally the
134+ same as the given to this method (except that it is the full path),
135+ except if tail is not None: in this case, the header is copied to a temp
136+ filename and the tail code is appended to it before being passed on to
137+ gccxml. This temp filename is then returned.
138+ '''
139+ if tail is None :
140+ tail = ''
141+ tail .strip ()
142+ declarations = self .GetCache (header , interface , tail )
143+ if declarations is None :
144+ declarations = self .ParseWithGCCXML (header , tail )
145+ if self .cache_dir is not None :
146+ self .CreateCache (header , interface , tail , declarations )
147+ return declarations , header
148+
149+
150+ def CacheFileName (self , interface ):
151+ interface_name = os .path .basename (interface )
152+ cache_file = os .path .splitext (interface_name )[0 ] + '.pystec'
153+ cache_file = os .path .join (self .cache_dir , cache_file )
154+ return cache_file
155+
156+
157+
158+ def GetCache (self , header , interface , tail ):
159+ if self .cache_dir is None :
160+ return None
161+ header = self .FindHeader (header )
162+ cache_file = self .CacheFileName (interface )
163+ if os .path .isfile (cache_file ):
164+ f = file (cache_file , 'rb' )
165+ try :
166+ cache = load (f )
167+ key = (header , interface , tail )
168+ if cache .has_key (key ):
169+ self .cache_files .append (cache_file )
170+ return cache [key ]
171+ else :
172+ return None
173+ finally :
174+ f .close ()
175+ else :
176+ return None
177+
178+
179+ def CreateCache (self , header , interface , tail , declarations ):
180+ header = self .FindHeader (header )
181+ cache_file = self .CacheFileName (interface )
182+ if os .path .isfile (cache_file ):
183+ f = file (cache_file , 'rb' )
184+ try :
185+ cache = load (f )
186+ finally :
187+ f .close ()
188+ else :
189+ cache = {}
190+ key = (header , interface , tail )
191+ cache [key ] = declarations
192+ self .cache_files .append (cache_file )
193+ f = file (cache_file , 'wb' )
194+ try :
195+ dump (cache , f , 1 )
196+ finally :
197+ f .close ()
198+ return cache_file
199+
200+
201+ def Close (self ):
202+ if self .delete_cache and self .cache_files :
203+ for filename in self .cache_files :
204+ try :
205+ os .remove (filename )
206+ except OSError :
207+ pass
208+ self .cache_files = []
209+ shutil .rmtree (self .cache_dir )
0 commit comments