# Copyright Bruno da Silva de Oliveira 2003. Use, modification and # distribution is subject to the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) """ Pyste version %s Usage: pyste [options] interface-files where options are: --module= The name of the module that will be generated; defaults to the first interface filename, without the extension. -I Add an include path -D Define symbol --multiple Create various cpps, instead of only one (useful during development) --out= Specify output filename (default: .cpp) in --multiple mode, this will be a directory --no-using Do not declare "using namespace boost"; use explicit declarations instead --pyste-ns= Set the namespace where new types will be declared; default is the empty namespace --debug Writes the xml for each file parsed in the current directory --cache-dir= Directory for cache files (speeds up future runs) --only-create-cache Recreates all caches (doesn't generate code). --generate-main Generates the _main.cpp file (in multiple mode) --file-list A file with one pyste file per line. Use as a substitute for passing the files in the command line. --gccxml-path= Path to gccxml executable (default: gccxml) --no-default-include Do not use INCLUDE environment variable for include files to pass along gccxml. -h, --help Print this help and exit -v, --version Print version information """ import sys import os import getopt import exporters import SingleCodeUnit import MultipleCodeUnit import infos import exporterutils import settings import gc import sys from policies import * from CppParser import CppParser, CppParserError import time import declarations __version__ = '0.9.30' def RecursiveIncludes(include): 'Return a list containg the include dir and all its subdirectories' dirs = [include] def visit(arg, dir, names): # ignore CVS dirs if os.path.split(dir)[1] != 'CVS': dirs.append(dir) os.path.walk(include, visit, None) return dirs def GetDefaultIncludes(): if 'INCLUDE' in os.environ: include = os.environ['INCLUDE'] return include.split(os.pathsep) else: return [] def ProcessIncludes(includes): if sys.platform == 'win32': index = 0 for include in includes: includes[index] = include.replace('\\', '/') index += 1 def ReadFileList(filename): f = file(filename) files = [] try: for line in f: line = line.strip() if line: files.append(line) finally: f.close() return files def ParseArguments(): def Usage(): print __doc__ % __version__ sys.exit(1) try: options, files = getopt.getopt( sys.argv[1:], 'R:I:D:vh', ['module=', 'multiple', 'out=', 'no-using', 'pyste-ns=', 'debug', 'cache-dir=', 'only-create-cache', 'version', 'generate-main', 'file-list=', 'help', 'gccxml-path=', 'no-default-include']) except getopt.GetoptError, e: print print 'ERROR:', e Usage() default_includes = GetDefaultIncludes() includes = [] defines = [] module = None out = None multiple = False cache_dir = None create_cache = False generate_main = False gccxml_path = 'gccxml' for opt, value in options: if opt == '-I': includes.append(value) elif opt == '-D': defines.append(value) elif opt == '-R': includes.extend(RecursiveIncludes(value)) elif opt == '--module': module = value elif opt == '--out': out = value elif opt == '--no-using': settings.namespaces.python = 'boost::python::' settings.USING_BOOST_NS = False elif opt == '--pyste-ns': settings.namespaces.pyste = value + '::' elif opt == '--debug': settings.DEBUG = True elif opt == '--multiple': multiple = True elif opt == '--cache-dir': cache_dir = value elif opt == '--only-create-cache': create_cache = True elif opt == '--file-list': files += ReadFileList(value) elif opt in ['-h', '--help']: Usage() elif opt in ['-v', '--version']: print 'Pyste version %s' % __version__ sys.exit(2) elif opt == '--generate-main': generate_main = True elif opt == '--gccxml-path': gccxml_path = value elif opt == '--no-default-include': default_includes = [] else: print 'Unknown option:', opt Usage() includes[0:0] = default_includes if not files: Usage() if not module: module = os.path.splitext(os.path.basename(files[0]))[0] if not out: out = module if not multiple: out += '.cpp' for file in files: d = os.path.dirname(os.path.abspath(file)) if d not in sys.path: sys.path.append(d) if create_cache and not cache_dir: print 'Error: Use --cache-dir to indicate where to create the cache files!' Usage() sys.exit(3) if generate_main and not multiple: print 'Error: --generate-main only valid in multiple mode.' Usage() sys.exit(3) ProcessIncludes(includes) return includes, defines, module, out, files, multiple, cache_dir, create_cache, \ generate_main, gccxml_path def PCHInclude(*headers): code = '\n'.join(['#include <%s>' % x for x in headers]) infos.CodeInfo(code, 'pchinclude') def CreateContext(): 'create the context where a interface file will be executed' context = {} context['Import'] = Import # infos context['Function'] = infos.FunctionInfo context['Class'] = infos.ClassInfo context['Include'] = lambda header: infos.CodeInfo('#include <%s>\n' % header, 'include') context['PCHInclude'] = PCHInclude context['Template'] = infos.ClassTemplateInfo context['Enum'] = infos.EnumInfo context['AllFromHeader'] = infos.HeaderInfo context['Var'] = infos.VarInfo # functions context['rename'] = infos.rename context['set_policy'] = infos.set_policy context['exclude'] = infos.exclude context['set_wrapper'] = infos.set_wrapper context['use_shared_ptr'] = infos.use_shared_ptr context['use_auto_ptr'] = infos.use_auto_ptr context['holder'] = infos.holder context['add_method'] = infos.add_method context['final'] = infos.final context['export_values'] = infos.export_values # policies context['return_internal_reference'] = return_internal_reference context['with_custodian_and_ward'] = with_custodian_and_ward context['return_value_policy'] = return_value_policy context['reference_existing_object'] = reference_existing_object context['copy_const_reference'] = copy_const_reference context['copy_non_const_reference'] = copy_non_const_reference context['return_opaque_pointer'] = return_opaque_pointer context['manage_new_object'] = manage_new_object context['return_by_value'] = return_by_value context['return_self'] = return_self # utils context['Wrapper'] = exporterutils.FunctionWrapper context['declaration_code'] = lambda code: infos.CodeInfo(code, 'declaration-outside') context['module_code'] = lambda code: infos.CodeInfo(code, 'module') context['class_code'] = infos.class_code return context def Begin(): # parse arguments includes, defines, module, out, interfaces, multiple, cache_dir, create_cache, generate_main, gccxml_path = ParseArguments() # run pyste scripts for interface in interfaces: ExecuteInterface(interface) # create the parser parser = CppParser(includes, defines, cache_dir, declarations.version, gccxml_path) try: if not create_cache: if not generate_main: return GenerateCode(parser, module, out, interfaces, multiple) else: return GenerateMain(module, out, OrderInterfaces(interfaces)) else: return CreateCaches(parser) finally: parser.Close() def CreateCaches(parser): # There is one cache file per interface so we organize the headers # by interfaces. For each interface collect the tails from the # exporters sharing the same header. tails = JoinTails(exporters.exporters) # now for each interface file take each header, and using the tail # get the declarations and cache them. for interface, header in tails: tail = tails[(interface, header)] declarations = parser.ParseWithGCCXML(header, tail) cachefile = parser.CreateCache(header, interface, tail, declarations) print 'Cached', cachefile return 0 _imported_count = {} # interface => count def ExecuteInterface(interface): old_interface = exporters.current_interface if not os.path.exists(interface): if old_interface and os.path.exists(old_interface): d = os.path.dirname(old_interface) interface = os.path.join(d, interface) if not os.path.exists(interface): raise IOError, "Cannot find interface file %s."%interface _imported_count[interface] = _imported_count.get(interface, 0) + 1 exporters.current_interface = interface context = CreateContext() context['INTERFACE_FILE'] = os.path.abspath(interface) execfile(interface, context) exporters.current_interface = old_interface def Import(interface): exporters.importing = True ExecuteInterface(interface) exporters.importing = False def JoinTails(exports): '''Returns a dict of {(interface, header): tail}, where tail is the joining of all tails of all exports for the header. ''' tails = {} for export in exports: interface = export.interface_file header = export.Header() tail = export.Tail() or '' if (interface, header) in tails: all_tails = tails[(interface,header)] all_tails += '\n' + tail tails[(interface, header)] = all_tails else: tails[(interface, header)] = tail return tails def OrderInterfaces(interfaces): interfaces_order = [(_imported_count[x], x) for x in interfaces] interfaces_order.sort() interfaces_order.reverse() return [x for _, x in interfaces_order] def GenerateMain(module, out, interfaces): codeunit = MultipleCodeUnit.MultipleCodeUnit(module, out) codeunit.GenerateMain(interfaces) return 0 def GenerateCode(parser, module, out, interfaces, multiple): # prepare to generate the wrapper code if multiple: codeunit = MultipleCodeUnit.MultipleCodeUnit(module, out) else: codeunit = SingleCodeUnit.SingleCodeUnit(module, out) # stop referencing the exporters here exports = exporters.exporters exporters.exporters = None exported_names = dict([(x.Name(), None) for x in exports]) # order the exports order = {} for export in exports: if export.interface_file in order: order[export.interface_file].append(export) else: order[export.interface_file] = [export] exports = [] interfaces_order = OrderInterfaces(interfaces) for interface in interfaces_order: exports.extend(order[interface]) del order del interfaces_order # now generate the code in the correct order #print exported_names tails = JoinTails(exports) for i in xrange(len(exports)): export = exports[i] interface = export.interface_file header = export.Header() if header: tail = tails[(interface, header)] declarations, parsed_header = parser.Parse(header, interface, tail) else: declarations = [] parsed_header = None ExpandTypedefs(declarations, exported_names) export.SetDeclarations(declarations) export.SetParsedHeader(parsed_header) if multiple: codeunit.SetCurrent(export.interface_file, export.Name()) export.GenerateCode(codeunit, exported_names) # force collect of cyclic references exports[i] = None del declarations del export gc.collect() # finally save the code unit codeunit.Save() if not multiple: print 'Module %s generated' % module return 0 def ExpandTypedefs(decls, exported_names): '''Check if the names in exported_names are a typedef, and add the real class name in the dict. ''' for name in exported_names.keys(): for decl in decls: if isinstance(decl, declarations.Typedef): exported_names[decl.type.FullName()] = None def UsePsyco(): 'Tries to use psyco if possible' try: import psyco psyco.profile() except: pass def main(): start = time.clock() UsePsyco() status = Begin() print '%0.2f seconds' % (time.clock()-start) sys.exit(status) if __name__ == '__main__': main()