diff options
Diffstat (limited to 'libs/python/pyste/src/Pyste/pyste.py')
-rw-r--r-- | libs/python/pyste/src/Pyste/pyste.py | 424 |
1 files changed, 424 insertions, 0 deletions
diff --git a/libs/python/pyste/src/Pyste/pyste.py b/libs/python/pyste/src/Pyste/pyste.py new file mode 100644 index 000000000..cedffff55 --- /dev/null +++ b/libs/python/pyste/src/Pyste/pyste.py @@ -0,0 +1,424 @@ +# 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=<name> The name of the module that will be generated; + defaults to the first interface filename, without + the extension. + -I <path> Add an include path + -D <symbol> Define symbol + --multiple Create various cpps, instead of only one + (useful during development) + --out=<name> Specify output filename (default: <module>.cpp) + in --multiple mode, this will be a directory + --no-using Do not declare "using namespace boost"; + use explicit declarations instead + --pyste-ns=<name> 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=<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> 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() |