Package pyplusplus :: Package file_writers :: Module multiple_files

# Source Code for Module pyplusplus.file_writers.multiple_files

      1  # Copyright 2004-2008 Roman Yakovenko.
      2  # Distributed under the Boost Software License, Version 1.0. (See
      3  # accompanying file LICENSE_1_0.txt or copy at
      4  # http://www.boost.org/LICENSE_1_0.txt)
      5
      6  """defines a class that writes L{code_creators.module_t} to multiple files"""
      7
      8  import os
      9  import writer
     10  from pyplusplus import messages
     11  from pyplusplus import _logging_
     12  from pygccxml import declarations
     13  from pyplusplus import decl_wrappers
     14  from pyplusplus import code_creators
     15
     16  #TODO: to add namespace_alias_t classes
     17 -class multiple_files_t(writer.writer_t):
     18      """
     19      This class implements classic strategy of deviding classes to files
     20      one class in one header + source files.
     21      """
     22      HEADER_EXT = '.pypp.hpp'
     23      SOURCE_EXT = '.pypp.cpp'
     24
     25 -    def __init__(self, extmodule, directory_path, write_main=True, files_sum_repository=None, encoding='ascii'):
     26          """Constructor.
     27
     28          @param extmodule: The root of a code creator tree
     29          @type extmodule: module_t
     30          @param directory_path: The output directory where the source files are written
     31          @type directory_path: str
     32
     33          @param write_main:  if it is True, the class will write out a main file
     34              that calls all the registration methods.
     35          @type write_main: boolean
     36          """
     37          writer.writer_t.__init__( self, extmodule, files_sum_repository, encoding=encoding )
     38          self.__directory_path = directory_path
     39          self.create_dir( directory_path )
     40          self.include_creators = []  # List of include_t creators that contain the generated headers
     41          self.split_header_names = []  # List of include file names for split files
     42          self.split_method_names = []  # List of methods from the split files
     43          self.write_main = write_main
     44          self.written_files = []
     45          self.ref_count_creators = ( code_creators.opaque_type_registrator_t, )
     46          self.__predefined_include_creators \
     47              = filter( lambda creator: isinstance( creator, code_creators.include_t )
     48                        , self.extmodule.creators )
     49          self.__value_traits = filter( lambda x: isinstance(x, code_creators.value_traits_t)
     50                                        , self.extmodule.creators )
     51
     52
     53 -    def write_file( self, fpath, content ):
     54          if fpath in self.written_files:
     55              msg = ['Py++ is going to write different content to the same file(%s).' % fpath]
     56              msg.append('The following is a short list of possible explanations for this behaviour:' )
     57              msg.append('* Py++ bug, in this case, please report it' )
     58              msg.append('* module_builder_t contains two or more classes with the same alias')
     59              msg.append('* module_builder_t contains two or more classes with the same wrapper alias')
     60              msg.append('Please carefully review Py++ warning messages. It should contain an additional information.')
     61              raise RuntimeError( os.linesep.join(msg) )
     62
     63          self.written_files.append( fpath )
     64          writer.writer_t.write_file( fpath, content, self.files_sum_repository, self.encoding )
     65
     66 -    def create_dir( self, directory_path ):
     67          """Create the output directory if it doesn't already exist.
     68          """
     69          if os.path.exists( directory_path ) and not os.path.isdir(directory_path):
     70              raise RuntimeError( 'directory_path should contain path to directory.' )
     71          if not os.path.exists( directory_path ):
     72              os.makedirs( directory_path )
     73
     74 -    def _get_directory_path(self):
     75          return self.__directory_path
     76      directory_path = property( _get_directory_path,
     77                                 doc="""The name of the output directory.
     78                                 @type: str
     79                                 """ )
     80
     81 -    def get_unique_creators( self, creators ):
     82          unique_creators = []
     83          unique_creator_ids = set()
     84          for creator in creators:
     85              if not id( creator ) in unique_creator_ids:
     86                  unique_creator_ids.add( id( creator ) )
     87                  unique_creators.append( creator )
     88          return unique_creators
     89
     90 -    def associated_decl_creators( self, creator ):
     91          """ references to all class declaration code creators. """
     92          if not isinstance( creator, code_creators.registration_based_t ):
     93              return []
     94
     95          associated_creators = creator.associated_decl_creators[:]
     96
     97          internal_creators = []
     98          if isinstance( creator, code_creators.compound_t ):
     99              internal_creators.extend(
    100                  filter( lambda creator: isinstance( creator, code_creators.registration_based_t )
    101                          , code_creators.make_flatten( creator.creators ) ) )
    102
    103          map( lambda internal_creator: associated_creators.extend( internal_creator.associated_decl_creators )
    104               , internal_creators )
    105          #now associated_creators contains all code creators associated with the creator
    106          #We should leave only creators, defined in the global namespace
    107          associated_creators = filter( lambda associated_creator: associated_creator.parent is self.extmodule
    108                                        , associated_creators )
    109          return associated_creators
    110
    111 -    def create_function_code( self, function_name ):
    112          return "void %s();" % function_name
    113
    114 -    def create_header( self, file_name, code ):
    115          """Return the content of a header file.
    116
    117          @param file_name: A string that uniquely identifies the file name
    118          @type file_name: str
    119          @param function_name: The name of the register_xyz() function
    120          @type function_name: str
    121          @returns: The content for a header file
    122          @rtype: str
    123          """
    124          tmpl = os.linesep.join([
    125                      "#ifndef %(file_name)s_hpp__pyplusplus_wrapper"
    126                    , "#define %(file_name)s_hpp__pyplusplus_wrapper"
    127                    , ''
    128                    , "%(code)s"
    129                    , ''
    130                    , "#endif//%(file_name)s_hpp__pyplusplus_wrapper" ])
    131
    132          content = ''
    133          if self.extmodule.license:
    134              content = self.extmodule.license.create() + os.linesep
    135          content = content + tmpl % { 'file_name' : file_name, 'code' : code }
    136          return content
    137
    138 -    def find_out_value_traits_header( self, code_creator ):
    139          if not isinstance( code_creator, ( code_creators.class_t, code_creators.class_declaration_t ) ):
    140              return None
    141          if None is code_creator.declaration.indexing_suite:
    142              return None
    143          if not isinstance( code_creator.declaration.indexing_suite, decl_wrappers.indexing_suite2_t ):
    144              return None
    145
    146          #sometimes, for some reason I expose containers as regular classes ( hash_map )
    147          #and in this case I do generate include to
    148          classes = ( code_creators.indexing_suite1_t, code_creators.indexing_suite2_t )
    149          for cont_code_creator in code_creator.creators:
    150              if isinstance( cont_code_creator, classes ):
    151                  break
    152          else:
    153              return None
    154
    155          try:
    156              element_type = code_creator.declaration.indexing_suite.element_type
    157              class_traits = declarations.class_traits
    158              if not class_traits.is_my_case( element_type ):
    159                  return None
    160              value_class = class_traits.get_declaration( element_type )
    161              if value_class.less_than_comparable and value_class.equality_comparable:
    162                  return None #Py++ doesn't create value traits for class that has
    163                              # = and < operators available
    164              return self.create_value_traits_header_name( value_class )
    165          except RuntimeError, error:
    166              decls_logger = _logging_.loggers.declarations
    167              if not messages.filter_disabled_msgs([messages.W1042], code_creator.declaration.disabled_messages ):
    168                  return #user disabled property warning
    169              decls_logger.warn( "%s;%s" % ( code_creator.declaration, messages.W1042 ) )
    170
    171 -    def create_include_code( self, creators, head_headers=None, tail_headers=None ):
    172          answer = []
    173          normalize = code_creators.include_directories_t.normalize
    174          unique_headers = code_creators.code_creator_t.unique_headers
    175
    176          if head_headers:
    177              answer.extend( map( lambda header: '#include "%s"' % normalize( header )
    178                                  , head_headers ) )
    179
    180          dependend_on_headers = []
    181          for creator in creators:
    182              dependend_on_headers.extend( creator.get_system_headers( recursive=True ) )
    183
    184          dependend_on_headers = unique_headers( map( normalize, dependend_on_headers ) )
    185
    186          for include_cc in self.__predefined_include_creators:
    187              if include_cc.is_system:
    188                  if include_cc.header in dependend_on_headers:
    189                      answer.append( include_cc.create() )
    190              else:# user header file - always include
    191                  answer.append( include_cc.create() )
    192
    193          map( lambda user_header: answer.append( '#include "%s"' % user_header )
    194               , self.get_user_headers( creators ) )
    195
    196          for creator in creators:
    197              header = self.find_out_value_traits_header( creator )
    198              if header:
    199                  answer.append( '#include "%s"' % header )
    200
    201          if tail_headers:
    202              answer.extend( map( lambda header: '#include "%s"' % normalize( header )
    203                                  , tail_headers ) )
    204
    205          return os.linesep.join( answer )
    206
    207 -    def create_namespaces_code( self, creators ):
    208          # Write all 'global' namespace_alias_t and namespace_using_t creators first...
    209          ns_types = ( code_creators.namespace_alias_t, code_creators.namespace_using_t )
    210          ns_creators = filter( lambda x: isinstance( x, ns_types ), self.extmodule.creators )
    211
    212          ns_creators.extend( filter( lambda x: isinstance( x, ns_types ), self.extmodule.body.creators ) )
    213          if not ns_creators:
    214              return ''
    215          else:
    216              return os.linesep.join( map( lambda creator: creator.create(), ns_creators ) )
    217
    218 -    def create_source( self, file_name, function_name, registration_creators ):
    219          """Return the content of a cpp file.
    220
    221          @param file_name: The base name of the corresponding include file (without extension)
    222          @type file_name: str
    223          @param function_name: The name of the register_xyz() function
    224          @type function_name: str
    225          @param creators: The code creators that create the register_xyz() function
    226          @type creators: list of code_creator_t
    227          @returns: The content for a cpp file
    228          @rtype: str
    229          """
    230          declaration_creators = []
    231          for rc in registration_creators:
    232              declaration_creators.extend( self.associated_decl_creators( rc ) )
    233          declaration_creators = self.get_unique_creators( declaration_creators )
    234
    235          creators = registration_creators + declaration_creators
    236
    237          answer = []
    238          if self.extmodule.license:
    239              answer.append( self.extmodule.license.create() )
    240
    241          head_headers = [ file_name + self.HEADER_EXT ]
    242          answer.append( self.create_include_code( creators, tail_headers=head_headers ) )
    243
    244          answer.append( '' )
    245          answer.append( self.create_namespaces_code( creators ) )
    246
    247          # Write wrapper classes...
    248          for creator in declaration_creators:
    249              answer.append( '' )
    250              answer.append( creator.create() )
    251              if not isinstance( creator, self.ref_count_creators ):
    252                  creator.create = lambda: ''
    253
    254          # Write the register() function...
    255          answer.append( '' )
    256          answer.append( 'void %s(){' % function_name )
    257          answer.append( '' )
    258          for creator in registration_creators:
    259              answer.append( code_creators.code_creator_t.indent( creator.create() ) )
    260              answer.append( '' )
    261          answer.append( '}' )
    262          return os.linesep.join( answer )
    263
    264 -    def split_class_impl( self, class_creator):
    265          function_name = 'register_%s_class' % class_creator.alias
    266          file_path = os.path.join( self.directory_path, class_creator.alias )
    267          # Write the .h file...
    268          header_name = file_path + self.HEADER_EXT
    269          self.write_file( header_name
    270                           , self.create_header( class_creator.alias
    271                                                 , self.create_function_code( function_name ) ) )
    272
    273          # Write the .cpp file...
    274          cpp_code = self.create_source( class_creator.alias, function_name, [class_creator] )
    275
    276          self.write_file( file_path + self.SOURCE_EXT, cpp_code )
    277
    278          # Replace the create() method so that only the register() method is called
    279          # (this is called later for the main source file).
    280          class_creator.create = lambda: function_name +'();'
    281          self.include_creators.append( code_creators.include_t( header_name ) )
    282          self.split_header_names.append(header_name)
    283          self.split_method_names.append(function_name)
    284
    285 -    def split_class( self, class_creator):
    286          """Write the .h/.cpp file for one class.
    287
    288          Writes a .h/.cpp file for the given class. The files use the class name
    289          as base file name.
    290
    291          @param class_creator: The class creator for one particular class
    292          @type class_creator: class_t
    293          """
    294          try:
    295              if class_creator.declaration.already_exposed:
    296                  return
    297              self.split_class_impl( class_creator )
    298          except IOError, error:
    299              msg = [ 'Failed to write code for class "%s" into file.;' % class_creator.declaration.name ]
    300              msg.append( "May be the class name is too long?." )
    301              msg.append( "Error: %s'" % str(error) )
    302              self.logger.error( os.linesep.join( msg ) )
    303              raise
    304
    305 -    def split_classes( self ):
    306          # Obtain a list of all class creators...
    307          class_creators = filter( lambda x: isinstance(x, ( code_creators.class_t, code_creators.class_declaration_t ) )
    308                                   , self.extmodule.body.creators )
    309          # ...and write a .h/.cpp file for each class
    310          map( self.split_class, class_creators )
    311
    312 -    def create_value_traits_header_name( self, value_class ):
    313          return "_" + value_class.alias + "__value_traits" + self.HEADER_EXT
    314
    315 -    def split_value_traits( self, value_traits ):
    316          """
    317          Write the value_traits class to header file, that will be included
    318          from files, that uses indexing suite 2
    319          """
    320          if value_traits.declaration.already_exposed:
    321              return
    322
    323          header_name = self.create_value_traits_header_name( value_traits.declaration )
    324          file_path = os.path.join( self.directory_path, header_name )
    325          self.write_file( file_path
    326                          , self.create_header( header_name.replace( '.', '_' )
    327                                                , value_traits.create() ) )
    328          value_traits.create = lambda: ''
    329
    330 -    def split_values_traits( self ):
    331          map( self.split_value_traits, self.__value_traits )
    332
    333 -    def split_creators( self, creators, pattern, function_name, registrator_pos ):
    334          """Write non-class creators into a particular .h/.cpp file.
    335
    336          @param creators: The code creators that should be written
    337          @type creators: list of code_creator_t
    338          @param pattern: Name pattern that is used for constructing the final output file name
    339          @type pattern: str
    340          @param function_name: The name of the register_xyz() function
    341          @type function_name: str
    342          @param registrator_pos: The position of the code creator that creates the code to invoke the register_xyz() function.
    343          @type registrator_pos: int
    344          """
    345          if not creators:
    346              return
    347          file_pattern = self.extmodule.body.name + pattern
    348          file_path = os.path.join( self.directory_path, file_pattern )
    349          header_name = file_path + self.HEADER_EXT
    350          self.write_file( header_name
    351                           , self.create_header( file_pattern, self.create_function_code( function_name ) ) )
    352          self.write_file( file_path + self.SOURCE_EXT
    353                           , self.create_source( file_pattern, function_name, creators ))
    354
    355          for creator in creators:
    356              creator.create = lambda: ''
    357          self.extmodule.body.adopt_creator(
    358              code_creators.custom_text_t( function_name + '();' )
    359              , registrator_pos)
    360          self.include_creators.append( code_creators.include_t( header_name ) )
    361          self.split_header_names.append(header_name)
    362          self.split_method_names.append(function_name)
    363
    364 -    def split_enums( self ):
    365          """Write all enumerations into a separate .h/.cpp file.
    366          """
    367          enums_creators = filter( lambda x: isinstance(x, code_creators.enum_t )
    368                                   , self.extmodule.body.creators )
    369
    370          self.split_creators( enums_creators, '_enumerations', 'register_enumerations', 0 )
    371
    372 -    def split_global_variables( self ):
    373          """Write all global variables into a separate .h/.cpp file.
    374          """
    375          creators = filter( lambda x: isinstance(x, code_creators.global_variable_t )
    376                             , self.extmodule.body.creators )
    377          creators.extend( filter( lambda x: isinstance(x, code_creators.unnamed_enum_t )
    378                             , self.extmodule.body.creators ) )
    379          self.split_creators( creators, '_global_variables', 'register_global_variables', -1 )
    380
    381 -    def split_free_functions( self ):
    382          """Write all free functions into a separate .h/.cpp file.
    383          """
    384          free_functions = ( code_creators.free_function_t, code_creators.free_fun_overloads_t )
    385          creators = filter( lambda x: isinstance(x, free_functions ), self.extmodule.body.creators )
    386          self.split_creators( creators, '_free_functions', 'register_free_functions', -1 )
    387
    388      #TODO: move write_main to __init__
    389 -    def write(self):
    390          """ Write out the module.
    391              Creates a separate source/header combo for each class and for enums, globals,
    392              and free functions.
    393              If write_main is True it writes out a main file that calls all the registration methods.
    394              After this call split_header_names and split_method_names will contain
    395              all the header files and registration methods used.  This can be used by
    396              user code to create custom registration methods if main is not written.
    397          """
    398
    399          self.write_code_repository( self.__directory_path )
    400          self.save_exposed_decls_db( self.__directory_path )
    401
    402          self.extmodule.do_include_dirs_optimization()
    403
    404          self.split_values_traits()
    405          self.split_classes()
    406          self.split_enums()
    407          self.split_global_variables()
    408          self.split_free_functions()
    409
    410          if self.write_main:
    411              self.include_creators.sort( cmp=lambda ic1, ic2: cmp( ic1.header, ic2.header ) )
    412              map( lambda creator: self.extmodule.adopt_include( creator )
    413                   , self.include_creators )
    414              main_cpp = os.path.join( self.directory_path, self.extmodule.body.name + '.main.cpp' )
    415              self.write_file( main_cpp, self.extmodule.create() + os.linesep )
    416          self.files_sum_repository.save_values()
    417