1
2
3
4
5
6 import os
7 import sys
8 import linker
9 import config
10 import patcher
11 import pygccxml.utils
12
13 try:
14 from etree_scanner import etree_scanner_t as scanner_t
15 except:
16 from scanner import scanner_t
17
18 import declarations_cache
19 from pygccxml import utils
20 from pygccxml.declarations import *
21
25
27 """
28 This function binds between class and it's typedefs.
29
30 @param decls: list of all declarations
31 @type all_classes: list of L{declaration_t} items
32
33 @return: None
34 """
35 visited = set()
36 typedefs = filter( lambda decl: isinstance( decl, typedef_t ), decls )
37 for decl in typedefs:
38 type_ = remove_alias( decl.type )
39 if not isinstance( type_, declarated_t ):
40 continue
41 cls_inst = type_.declaration
42 if not isinstance( cls_inst, class_types ):
43 continue
44 if id( cls_inst ) not in visited:
45 visited.add( id( cls_inst ) )
46 del cls_inst.aliases[:]
47 cls_inst.aliases.append( decl )
48
50 """
51 This class reads C++ source code and returns declarations tree.
52
53 This class is the only class that have an intime knowledge about GCC-XML.
54 It has only one responsibility: it calls GCC-XML with a source file specified
55 by user and creates declarations tree. The implementation of this class is split
56 to 2 classes:
57
58 1. L{scanner_t} - this class scans the "XML" file, generated by GCC-XML and
59 creates `pygccxml`_ declarations and types classes. After the xml file has
60 been processed declarations and type class instances keeps references to
61 each other using GCC-XML generated id's.
62
63 2. L{linker_t} - this class contains logic for replacing GCC-XML generated
64 ids with references to declarations or type class instances.
65 """
66 - def __init__( self, config, cache=None, decl_factory=None ):
91
93 assert isinstance( self.__config, config.config_t )
94
95 cmd = []
96
97 if 'win32' in sys.platform:
98 cmd.append( '"%s"' % os.path.normpath( self.__config.gccxml_path ) )
99 else:
100 cmd.append( '%s' % os.path.normpath( self.__config.gccxml_path ) )
101
102
103 if self.__config.cflags != "":
104 cmd.append(" %s "%self.__config.cflags)
105
106 cmd.append( ''.join( [' -I"%s"' % search_dir for search_dir in self.__search_directories] ) )
107
108 cmd.append( ''.join( [' -D"%s"' % defined_symbol for defined_symbol in self.__config.define_symbols] ) )
109 cmd.append( ''.join( [' -U"%s"' % undefined_symbol for undefined_symbol in self.__config.undefine_symbols] ) )
110
111 cmd.append( '"%s"' % file )
112
113 cmd.append( '-fxml="%s"' % xmlfile )
114 if self.__config.start_with_declarations:
115 cmd.append( '-fxml-start="%s"' % ','.join( self.__config.start_with_declarations ) )
116
117 if self.__config.compiler:
118 cmd.append( " --gccxml-compiler %s" % self.__config.compiler )
119 cmd_line = ' '.join(cmd)
120 if 'win32' in sys.platform :
121 cmd_line = '"%s"' % cmd_line
122 self.logger.info( 'gccxml cmd: %s' % cmd_line )
123 return cmd_line
124
126 """
127 This function will return the file name of the file, created by GCC-XML
128 for "header" file. If destination_file_path is not None, then this file
129 path will be used and returned.
130
131 @param header: path to source file, that should be parsed
132 @type header: str
133
134 @param destination: if given, will be used as target file/path for
135 GCC-XML generated file.
136 @type destination: str
137
138 @return: path to GCC-XML generated file
139 """
140 gccxml_file = destination
141
142 if gccxml_file:
143 pygccxml.utils.remove_file_no_raise( gccxml_file )
144 else:
145 gccxml_file = pygccxml.utils.create_temp_file_name( suffix='.xml' )
146 try:
147 ffname = header
148 if not os.path.isabs( ffname ):
149 ffname = self.__file_full_name(header)
150 command_line = self.__create_command_line( ffname, gccxml_file )
151 input_, output = os.popen4( command_line )
152 input_.close()
153 gccxml_reports = []
154 while True:
155 data = output.readline()
156 gccxml_reports.append( data )
157 if not data:
158 break
159 exit_status = output.close()
160 gccxml_msg = ''.join(gccxml_reports)
161 if self.__config.ignore_gccxml_output:
162 if not os.path.isfile(gccxml_file):
163 raise gccxml_runtime_error_t( "Error occured while running GCC-XML: %s status:%s" % (gccxml_msg, exit_status) )
164 else:
165 if gccxml_msg or exit_status or not os.path.isfile(gccxml_file):
166 raise gccxml_runtime_error_t( "Error occured while running GCC-XML: %s" % gccxml_msg )
167 except Exception, error:
168 pygccxml.utils.remove_file_no_raise( gccxml_file )
169 raise error
170 return gccxml_file
171
173 """
174 Creates XML file from text.
175
176 @param content: C++ source code
177 @type content: str
178
179 @param destination: file name for GCC-XML generated file
180 @type destination: str
181
182 @return: returns file name of GCC-XML generated file
183 """
184 header_file = pygccxml.utils.create_temp_file_name( suffix='.h' )
185 gccxml_file = None
186 try:
187 header_file_obj = file(header_file, 'w+')
188 header_file_obj.write( content )
189 header_file_obj.close()
190 gccxml_file = self.create_xml_file( header_file, destination )
191 finally:
192 pygccxml.utils.remove_file_no_raise( header_file )
193 return gccxml_file
194
200
202 """
203 Reads C++ source file and returns declarations tree
204
205 @param source_file: path to C++ source file
206 @type source_file: str
207 """
208 declarations, types = None, None
209 gccxml_file = ''
210 try:
211 ffname = self.__file_full_name(source_file)
212 self.logger.debug( "Reading source file: [%s]." % ffname )
213 declarations = self.__dcache.cached_value( ffname, self.__config )
214 if not declarations:
215 self.logger.debug( "File has not been found in cache, parsing..." )
216 gccxml_file = self.create_xml_file( ffname )
217 declarations, files = self.__parse_gccxml_created_file( gccxml_file )
218 self.__dcache.update( ffname, self.__config, declarations, files )
219 else:
220 self.logger.debug( "File has not been changed, reading declarations from cache." )
221 except Exception, error:
222 if gccxml_file:
223 pygccxml.utils.remove_file_no_raise( gccxml_file )
224 raise error
225 if gccxml_file:
226 pygccxml.utils.remove_file_no_raise( gccxml_file )
227 return declarations
228
230 """
231 Reads GCC-XML generated XML file.
232
233 @param gccxml_created_file: path to GCC-XML generated file
234 @type gccxml_created_file: str
235
236 @return: declarations tree
237 """
238 assert(self.__config!=None)
239
240 ffname = self.__file_full_name(gccxml_created_file)
241 self.logger.debug( "Reading xml file: [%s]" % gccxml_created_file )
242 declarations = self.__dcache.cached_value( ffname, self.__config )
243 if not declarations:
244 self.logger.debug( "File has not been found in cache, parsing..." )
245 declarations, files = self.__parse_gccxml_created_file( ffname )
246 self.__dcache.update( ffname, self.__config, declarations, [] )
247 else:
248 self.logger.debug( "File has not been changed, reading declarations from cache." )
249
250 return declarations
251
269
271 if os.path.isfile( file ):
272 return file
273 for path in self.__search_directories:
274 file_path = os.path.join( path, file )
275 if os.path.isfile( file_path ):
276 return file_path
277 raise RuntimeError( "pygccxml error: file '%s' does not exist" % file )
278
280 if 'win' in sys.platform or 'linux' in sys.platform:
281 file_path = file_path.replace( r'\/', os.path.sep )
282 if os.path.isabs( file_path ):
283 return file_path
284 try:
285 abs_file_path = os.path.realpath( os.path.join( self.__config.working_directory, file_path ) )
286 if os.path.exists( abs_file_path ):
287 return os.path.normpath( abs_file_path )
288 return file_path
289 except Exception:
290 return file_path
291
293 scanner_ = scanner_t( gccxml_file, self.__decl_factory )
294 scanner_.read()
295 decls = scanner_.declarations()
296 types = scanner_.types()
297 files = {}
298 for file_id, file_path in scanner_.files().iteritems():
299 files[file_id] = self.__produce_full_file(file_path)
300 linker_ = linker.linker_t( decls=decls
301 , types=types
302 , access=scanner_.access()
303 , membership=scanner_.members()
304 , files=files )
305 for type_ in types.values():
306
307 linker_.instance = type_
308 apply_visitor( linker_, type_ )
309 for decl in decls.itervalues():
310 linker_.instance = decl
311 apply_visitor( linker_, decl )
312 bind_aliases( decls.itervalues() )
313
314
315
316
317
318 patcher.fix_calldef_decls( scanner_.calldefs(), scanner_.enums() )
319 decls = filter( lambda inst: isinstance( inst, namespace_t ) and not inst.parent
320 , decls.itervalues() )
321 return ( decls, files.values() )
322
324 import synopsis_scanner
325 from Synopsis import AST
326 from Synopsis.Parsers import Cxx
327
328 ffname = self.__file_full_name(source_file)
329
330 cppflags = []
331 map( lambda dpath: cppflags.append( '-I %s' % dpath )
332 , self.__config.include_paths )
333 map( lambda define: cppflags.append( '-D %s' % define )
334 , self.__config.define_symbols )
335 map( lambda define: cppflags.append( '-U %s' % define )
336 , self.__config.undefine_symbols )
337
338 cxx = Cxx.Parser( preprocess=True, cppflags=cppflags )
339 ast = AST.AST()
340 cxx.process( ast, input=[source_file] )
341 scanner = synopsis_scanner.scanner_t( ast, self.__decl_factory )
342 scanner.visitAST( ast )
343 declarations = [scanner.global_ns]
344 self.__dcache.update( ffname, self.__config, declarations, [] )
345 return declarations
346