3 # PyOpenSG is (C) Copyright 2005-2009 by Allen Bierbaum
5 # This file is part of PyOpenSG.
7 # PyOpenSG is free software; you can redistribute it and/or modify it under
8 # the terms of the GNU Lesser General Public License as published by the Free
9 # Software Foundation; either version 2 of the License, or (at your option)
12 # PyOpenSG is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
17 # You should have received a copy of the GNU Lesser General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21 # This script is derived from gen_bindings.py, all class specific tasks were
22 # moved to per lib scripts for the integration with the OpenSG CMake system
31 import xml
.etree
.ElementTree
as et
33 import elementtree
.ElementTree
as et
35 import xml
.parsers
.expat
37 if ${OSG_HAS_PYOSG_PYPLUSPLUS_DIR}
:
38 sys
.path
.insert(0, "${PYOSG_PYPLUSPLUS_DIR}")
40 if ${OSG_HAS_PYOSG_PYPLUSPLUS_GOODIES_DIR}
:
41 sys
.path
.insert(0, "${PYOSG_PYPLUSPLUS_GOODIES_DIR}")
43 sys
.path
.insert(0, "../Helper")
44 sys
.path
.insert(0, "../../../Python/Helper")
50 from pyplusplus
import messages
51 import pygccxml
.declarations
as pd
53 import osgSetupBindings_${PROJECT_NAME}
as ${PROJECT_NAME}Gen
60 # Setup global settings
61 set_recursive_default(False)
62 # Need a little more room for bindings
63 sys
.setrecursionlimit( sys
.getrecursionlimit() * 10)
65 set_logger_level(logging
.INFO
)
66 #set_logger_level(logging.DEBUG)
67 messages
.disable(*messages
.all_warning_msgs
)
69 import pyplusplus
.code_creators
.calldef
70 pyplusplus
.code_creators
.calldef
.use_enum_workaround
= True
72 def findFcdFileInSubdirectories(fcdFilename
, subdirectories
=[]):
74 for subdir
in subdirectories
:
75 for root
, dirs
, names
in os
.walk(subdir
):
76 if fcdFilename
in names
:
77 return os
.path
.join(root
, fcdFilename
)
81 def parseFCDFiles(fcdFullFilename
, fcInfoDict
, headerList
, fc_ignore_list
, skip_fcds
):
83 if fcdFullFilename
== None:
86 fcdName
= os
.path
.basename(fcdFullFilename
).replace('.fcd', '')
87 clsName
= fcdName
.replace("OSG", "")
89 # print "xbar ", fcdFullFilename
90 # print "xfoo ", fcdName
91 # print "xfb ", fc_ignore_list
92 # print "xbb ", skip_fcds
94 if clsName
in fc_ignore_list
or fcdFullFilename
in skip_fcds
:
95 print "IGNORE", clsName
98 headerList
.append(fcdName
+".h")
101 tree
= et
.parse(fcdFullFilename
)
102 fc_elt
= tree
.getroot()
103 assert fc_elt
.tag
== "FieldContainer"
104 attribs
= fc_elt
.attrib
.copy()
105 parent
= attribs
["parent"]
106 name
= attribs
["name"]
107 doc
= fc_elt
.text
.strip()
108 except xml
.parsers
.expat
.ExpatError
, ex
:
109 #print "Error parsing file: %s"%f
111 #print "Trying re method instead. "
113 file_text
= file(f
).read()
114 name
= re
.search('name=\"(.*?)\"', file_text
).group(1)
115 parent
= re
.search('parent=\"(.*?)\"', file_text
).group(1)
116 library
= re
.search('library=\"(.*?)\"', file_text
).group(1)
119 # Build up the class info
120 #print " name: %s parent: %s" % (name, parent)
121 if not fcInfoDict
.has_key(name
):
122 fcInfoDict
[name
] = osggen
.FCInfo(name
, parent
, documentation
= doc
)
124 def findAndParseFCDFiles(fcInfoDict
, headerList
, fc_ignore_list
, skip_fcds
):
126 for fcdName
in ${PROJECT_NAME}Gen
.moduleFCs
:
128 # print "foo ", fcdName
129 fcdFilename
= "OSG"+fcdName
+".fcd"
130 # print "bar ", fcdFilename
131 fcdFullFilename
= findFcdFileInSubdirectories(fcdFilename
,
132 ${PROJECT_NAME}Gen
.moduleIncludes
)
133 # print "foobar ", fcdFullFilename
135 parseFCDFiles(fcdFullFilename
, fcInfoDict
, headerList
, fc_ignore_list
, skip_fcds
)
137 def parseFCDFileList(fcdInfoDict
, headerList
, fc_ignore_list
, skip_fcds
):
139 for fcdFilename
in ${PROJECT_NAME}Gen
.moduleFCDFiles
:
140 parseFCDFiles(fcdFilename
, fcdInfoDict
, headerList
, fc_ignore_list
, skip_fcds
)
157 non_fc_template_instances
= []
159 modules
= [${PROJECT_NAME}Gen
]
162 mod
.preScanSetup(gen_classes
, fc_infos
, core_mods
, free_funcs
, free_func_mods
, exc_types
)
164 opensg_src_dir
= "${CMAKE_SOURCE_DIR}/Source"
165 output_dir
= "${OSG_PYTHON_${PROJECT_NAME}_MODULE_DIR}"
169 main_header_filename
= os
.path
.join(output_dir
,
170 '${PROJECT_NAME}_mainheader.h')
171 main_header_filename_in
= os
.path
.join(output_dir
,
172 '${PROJECT_NAME}_mainheader.h.in')
173 exported_filename
= os
.path
.join(output_dir
,
174 'generated', 'osg_exported.txt')
176 all_headers
= ${PROJECT_NAME}Gen
.moduleHeaders
179 mod
.excludeFCDSetup(opensg_src_dir
, fc_ignore_list
, skip_fcds
)
181 if ${PROJECT_NAME}Gen
.moduleFCs
!= None:
182 findAndParseFCDFiles(fc_infos
, all_headers
, fc_ignore_list
, skip_fcds
)
184 if ${PROJECT_NAME}Gen
.moduleFCDFiles
!= None:
185 parseFCDFileList(fc_infos
, all_headers
, fc_ignore_list
, skip_fcds
)
187 template_builder
= osggen
.LocalTemplateBuilder()
188 tbuilder
= TemplateBuilder()
191 mod
.genTemplatesSetup(template_builder
, tbuilder
, non_fc_template_instances
)
193 # Member template instantiations
194 template_instance_text
= "\n\n// Member template instantiations\n"
195 template_instance_text
+= "#if defined(__GCCXML__)\n"
197 for c
in fc_infos
.itervalues():
198 for t
in c
.templateInstances
:
199 inst_text
= "%s;\n"%t
200 template_instance_text
= template_instance_text
+ inst_text
202 for t
in non_fc_template_instances
:
203 inst_text
= "%s;\n" % t
204 template_instance_text
= template_instance_text
+ inst_text
206 template_instance_text
+= "#endif\n"
208 tbuilder_tfc_txt
= template_builder
.buildTemplateFileContents();
209 tbuilder_bac_txt
= tbuilder
.buildAutogenContents();
211 header_extra_txt
= "";
213 if tbuilder_tfc_txt
!= None:
214 header_extra_txt
= header_extra_txt
+ tbuilder_tfc_txt
216 if tbuilder_bac_txt
!= None:
217 header_extra_txt
= header_extra_txt
+ tbuilder_bac_txt
219 main_header_filename_in
= osggen
.buildContainerHeader(
221 header_extra_txt
+ template_instance_text
,
222 filename
= main_header_filename_in
)
224 cp_command
= "${CMAKE_COMMAND} -E copy_if_different "
225 cp_command
+= main_header_filename_in
+ " "
226 cp_command
+= main_header_filename
228 os
.system(cp_command
)
230 cache_file
= "pypp.pyopensg.cache"
232 gcc_xml
= "${GCCXML}"
234 inc_path
= ${PROJECT_NAME}Gen
.moduleIncludes
236 if ${PROJECT_NAME}Gen
.moduleDepIncludes
!= None:
237 inc_path
.extend(${PROJECT_NAME}Gen
.moduleDepIncludes
)
241 boost_cflags
= "-DBOOST_PYTHON_MAX_ARITY=21"
243 cflags
= opensg_cflags
+ " " + boost_cflags
246 mb
= ModuleBuilder([main_header_filename
],
247 working_directory
= ".",
248 include_paths
= inc_path
,
249 cache
= cache_file
+ ".module",
250 define_symbols
= defines
,
251 ignore_gccxml_output
= True,
252 optimize_queries
= use_query_opt
,
253 #start_with_declarations = ["OSG"],
256 #dependent_headers = all_headers_full_paths)
258 tbuilder
.processTemplates(mb
)
259 mb
.BOOST_PYTHON_MAX_ARITY
= 21
260 mb
.global_ns
.exclude()
262 osg
= mb
.global_ns
.namespace("OSG", recursive
=False)
264 osggen
.OSG_MODULE
= osg
266 if ${PROJECT_NAME}Gen
.moduleDepencies
!= None:
267 std
= mb
.global_ns
.namespace("std", recursive
=False)
268 for moddep
in ${PROJECT_NAME}Gen
.moduleDepencies
:
269 f
= open(os
.path
.join(moddep
, 'osg_exported.txt'), "r")
272 sline
= line
.rstrip()
274 cls
= osg
.class_(sline
)
275 cls
.already_exposed
= True
277 except Exception, ex
:
279 cls
= std
.class_(sline
)
280 cls
.already_exposed
= True
282 except Exception, ex
:
283 #print "class [%s] unknown" % sline
287 # mb.register_module_dependency(moddep)
289 if ${OSG_DO_DUMP_PYTHON_DECLS}
:
290 decl_file
= file("afterscan_decl_out_final.txt",'w', 1024000)
291 print "Writing out decls to file... ",
292 mb
.print_declarations(osg
, writer
=decl_file
.write
)
293 #mb.print_declarations(my_class)
297 # take the pure virtual functions out immediately otherwise they show
298 # up in random places.
299 cls
= osg
["FieldContainer"]
301 for pvf
in ["createAspectCopy", "shallowCopy",
302 "shallowCopyDependent", "shallowCopyLocal",
304 cls
[pvf
].ignore
= True
305 cls
[pvf
].set_virtuality(pd
.VIRTUALITY_TYPES
.NOT_VIRTUAL
)
307 # Map from template alias name to real decl wrapper type
308 template_alias_db
= {}
311 global_typedefs
= mb
.global_ns
.typedefs()
313 for td
in global_typedefs
:
314 template_alias_db
[td
.name
] = td
.type
316 osg_typedefs
= osg
.typedefs()
319 mod
.postScanSetup(osg
, template_alias_db
)
321 if "OSGBase" == "${PROJECT_NAME}":
322 ${PROJECT_NAME}Gen
.tempLookatPostScanSetup(osg
)
326 for class_info
in gen_classes
:
327 # print "Class: %s"%class_info.name
329 c
= osg
.class_(class_info
.name
)
332 osggen
.handleClassDefaults(c
, class_info
.finalize
)
333 for x
in class_info
.excludes
:
335 for (n
,p
) in class_info
.policies
:
336 c
[n
].call_policies
= p
337 if class_info
.use_shared_ptr
:
338 c
.held_type
= "boost::shared_ptr<OSG::%s>"%class
_info
.name
339 for d
in class_info
.declarations
:
340 c
.add_declaration_code(d
)
341 for r
in class_info
.registrations
:
342 c
.add_registration_code(r
)
343 if class_info
.modCallable
: # Call any customizations needed
344 class_info
.modCallable(c
)
345 if class_info
.finalize
:
349 mod
.postGenClassesSetup(osg
, mb
)
358 f
.call_policies
= osggen
.FC_POLICY_RESOLVER(f
)
359 for mod_callable
in free_func_mods
.get(m
, []):
363 # --- Exception Translation --- #
366 class_hierarchy
= t
[0].split('::')
367 ex_type_name
= class_hierarchy
.pop()
369 for c
in class_hierarchy
:
371 ex_type
= ns
.class_(ex_type_name
)
372 ex_type
.translate_exception_to_string('PyExc_%s' % t
[1], 'exc.what()')
378 # For each field container class we have information about, do the following:
379 # - Generate code for class and classBase
380 for key
, cinfo
in fc_infos
.iteritems():
382 # print "Field container: %s" % cinfo.name
384 # If requested, expose the base
388 base_name
= cinfo
.name
+ "Base"
389 base_c
= osg
.class_(base_name
)
392 osggen
.handleClassDefaults(base_c
, True)
393 finalize(base_c
, True)
394 except Exception, ex
:
395 print "Exception when exposing base class [%s]. Skipping" % base_name
396 print " details: ", ex
398 #osggen.removePureVirtuals(base_c)
399 #for x in base_excludes:
400 # base_c[x].exclude()
401 #for (n,p) in base_policies:
402 # base_c[n].call_policies = p
403 # print "Done with %s" % base_name
405 # Expose the core class
406 c
= osg
.class_(cinfo
.name
)
408 c
.alias
= cinfo
.alias
410 osggen
.handleClassDefaults(c
, True)
412 #c.documentation = cinfo.documentation
413 #osggen.removePureVirtuals(c)
415 for x
in cinfo
.excludes
:
416 #print " exclude:", x
419 except Exception, ex
:
421 #print "Exception excluding: %s\n%s"%(x,str(ex))
422 for (n
,p
) in cinfo
.policies
:
423 #print " set policy for:", n
425 c
[n
].call_policies
= p
426 except Exception, ex
:
428 #print "Exception setting policies on: %s\n%s"%(n,str(ex))
430 for method_info
in cinfo
.addMethods
:
431 add_member_function(c
, method_info
[0],method_info
[1])
433 # Handle template naming.
434 # XXX: This is a hack for the one specific case where it is used right now.
435 # It should be refactored to use some hints from cinfo to choose naming method.
436 for mname
in cinfo
.templateMethods
:
437 # print " member:", mname
438 for f
in c
.member_functions(mname
):
439 ret_type
= f
.return_type
440 # print " ret type:", ret_type
441 # print " dict:", ret_type.__dict__
442 if not is_void(ret_type
):
443 if hasattr(ret_type
, "declaration"):
444 ret_alias
= ret_type
.declaration
.alias
446 ret_alias
= ret_type
.CPPNAME
.replace(" ", "_")
447 f
.alias
= "%s_%s" % (f
.alias
, ret_alias
)
448 # print " aliases: %s %s" % (f.alias, ret_alias)
451 f
.name
= f
.demangled_name
452 f
.create_with_signature
= True
454 # Register all the pointer conversions that we need and sets held type
455 osggen
.addFCPtrCode(c
, mb
, cinfo
.name
, cinfo
.parent
)
457 # Apply mods (modification methods)
458 if core_mods
.has_key(key
):
459 #print "Apply mods for: ", key
460 for class_mod
in core_mods
[key
][0]:
462 for base_mod
in core_mods
[key
][1]:
466 mod
.postFCDInfoSetup(osg
, mb
, adddition_exp
)
468 # ------ Standard ------------- #
470 # - Always expose with scope method
471 all_classes
= osg
.classes()
472 for c
in all_classes
:
473 c
.operators(symbol
="=", allow_empty
=True).exclude()
474 c
.always_expose_using_scope
= True
478 mb
.build_code_creator(module_name
='${PROJECT_NAME}Py')
479 mb
.code_creator
.add_namespace_usage("std")
481 extra_includes
= ["OsgPtrHelpers.h",
482 "boost/python/suite/indexing/map_indexing_suite.hpp",
483 "boost/python/suite/indexing/vector_indexing_suite.hpp"]
485 mb
.code_creator
.replace_included_headers(
486 [main_header_filename
] + extra_includes
)
488 mb
.code_creator
.license
= """
489 // PyOpenSG is (C) Copyright 2005-2009 by Allen Bierbaum
491 // This file is part of PyOpenSG.
493 // PyOpenSG is free software; you can redistribute it and/or modify it under
494 // the terms of the GNU Lesser General Public License as published by the Free
495 // Software Foundation; either version 2 of the License, or (at your option)
496 // any later version.
498 // PyOpenSG is distributed in the hope that it will be useful, but WITHOUT ANY
499 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
500 // FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
503 // You should have received a copy of the GNU Lesser General Public License
504 // along with this program. If not, see <http://www.gnu.org/licenses/>.
506 #if __GNUC__ >= 4 || __GNUC_MINOR__ >=3
507 #pragma GCC diagnostic warning "-Wold-style-cast"
508 #pragma GCC diagnostic warning "-Wunused-local-typedefs"
509 #pragma GCC diagnostic warning "-Wnon-virtual-dtor"
510 #pragma GCC diagnostic warning "-Wshadow"
513 #pragma warning(disable : 4267)
514 #pragma warning(disable : 4344)
518 if ${OSG_DO_DUMP_PYTHON_DECLS}
:
519 decl_file
= file("new_decl_out_final.txt",'w', 1024000)
520 print "Writing out decls to file... ",
521 mb
.print_declarations(osg
, writer
=decl_file
.write
)
522 #mb.print_declarations(my_class)
526 mb
.code_creator
.user_defined_directories
.append(output_dir
)
528 mb
.split_module(pj(output_dir
,'generated'))
531 f
= open(exported_filename
, "w")
533 for cls
in osg
.classes():
534 if cls
.ignore
== False:
535 f
.write(cls
.alias
+ "\n")
536 for exp
in adddition_exp
:
541 if ${PROJECT_NAME}Gen
.nativeWinDependends
!= None:
542 for nativeWinDep
in ${PROJECT_NAME}Gen
.nativeWinDependends
:
544 nativeWinDepIn
= pj(output_dir
, "generated", nativeWinDep
+ "Base.pypp.cpp")
545 nativeWinDepPatched
= pj(output_dir
, "generated", nativeWinDep
+ "Base.pypp.cpp.patched")
546 nativeWinDepOut
= pj(output_dir
, "generated", nativeWinDep
+ "Base.pypp.patched.cpp")
548 _inFileContent
= open(nativeWinDepIn
, "r").read();
549 _patchedFileContent
= ""
553 _patchedFileContent
= open(nativeWinDepPatched
, "r").read()
554 except Exception, ex
:
555 _patchedFileContent
= "p"
558 _outFileContent
= open(nativeWinDepOut
, "r").read()
559 except Exception, ex
:
560 _outFileContent
= "o"
562 _inFileContent
= _inFileContent
.replace(${PROJECT_NAME}Gen
.nativeWin
, "NativeWindow")
564 if _patchedFileContent
!= _outFileContent
:
565 print "out file outdated"
566 open(nativeWinDepPatched
, "w").write(_inFileContent
)
567 open(nativeWinDepOut
, "w").write(_inFileContent
)