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   
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