| Trees | Indices | Help |
|
|---|
|
|
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
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
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
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
76 directory_path = property( _get_directory_path,
77 doc="""The name of the output directory.
78 @type: str
79 """ )
80
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
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
113
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
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
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
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
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
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
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
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
314
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
331 map( self.split_value_traits, self.__value_traits )
332
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
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
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
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__
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
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Mon Oct 20 08:51:50 2008 | http://epydoc.sourceforge.net |