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