From 168476382a1ade8af40e106595cefb130efaf6b3 Mon Sep 17 00:00:00 2001 From: Bruno da Silva de Oliveira Date: Sat, 9 Aug 2003 21:18:12 +0000 Subject: [PATCH] - incremental code and some fixes [SVN r19499] --- pyste/src/Pyste/ClassExporter.py | 58 +++---- pyste/src/Pyste/CppParser.py | 191 +++++++++++++++++----- pyste/src/Pyste/EnumExporter.py | 13 +- pyste/src/Pyste/Exporter.py | 16 +- pyste/src/Pyste/FunctionExporter.py | 9 +- pyste/src/Pyste/HeaderExporter.py | 12 +- pyste/src/Pyste/IncludeExporter.py | 7 +- pyste/src/Pyste/MultipleCodeUnit.py | 23 +-- pyste/src/Pyste/SingleCodeUnit.py | 7 +- pyste/src/Pyste/VarExporter.py | 8 +- pyste/src/Pyste/infos.py | 27 ++-- pyste/src/Pyste/pyste-profile.py | 5 +- pyste/src/Pyste/pyste.py | 243 ++++++++++++++++++++-------- 13 files changed, 418 insertions(+), 201 deletions(-) diff --git a/pyste/src/Pyste/ClassExporter.py b/pyste/src/Pyste/ClassExporter.py index 7384245a..fdcf579d 100644 --- a/pyste/src/Pyste/ClassExporter.py +++ b/pyste/src/Pyste/ClassExporter.py @@ -48,25 +48,23 @@ class ClassExporter(Exporter): return makeid(self.class_.FullName()) + '_scope' - def Unit(self): - return makeid(self.class_.name) - - def Name(self): - return self.class_.FullName() + return self.info.name def SetDeclarations(self, declarations): Exporter.SetDeclarations(self, declarations) - decl = self.GetDeclaration(self.info.name) - if isinstance(decl, Typedef): - self.class_ = self.GetDeclaration(decl.type.name) - if not self.info.rename: - self.info.rename = decl.name + if self.declarations: + decl = self.GetDeclaration(self.info.name) + if isinstance(decl, Typedef): + self.class_ = self.GetDeclaration(decl.type.name) + if not self.info.rename: + self.info.rename = decl.name + else: + self.class_ = decl + self.class_ = copy.deepcopy(self.class_) else: - self.class_ = decl - self.class_ = copy.deepcopy(self.class_) - + self.class_ = None def ClassBases(self): @@ -82,7 +80,8 @@ class ClassExporter(Exporter): bases' bases. Do this because base classes must be instantialized before the derived classes in the module definition. ''' - return '%s_%s' % (len(self.ClassBases()), self.class_.FullName()) + num_bases = len(self.ClassBases()) + return num_bases, self.class_.FullName() def Export(self, codeunit, exported_names): @@ -101,7 +100,7 @@ class ClassExporter(Exporter): self.ExportSmartPointer() self.ExportOpaquePointerPolicies() self.Write(codeunit) - exported_names[self.class_.FullName()] = 1 + exported_names[self.Name()] = 1 def InheritMethods(self, exported_names): @@ -131,7 +130,7 @@ class ClassExporter(Exporter): break def IsValid(member): return isinstance(member, valid_members) and member.visibility == Scope.public - self.public_members = [x for x in self.class_ if IsValid(x)] + self.public_members = [x for x in self.class_ if IsValid(x)] def Write(self, codeunit): @@ -196,14 +195,9 @@ class ClassExporter(Exporter): def ExportBasics(self): - '''Export the name of the class and its class_ statement. - Also export the held_type if specified.''' + '''Export the name of the class and its class_ statement.''' class_name = self.class_.FullName() self.Add('template', class_name) - held_type = self.info.held_type - if held_type: - held_type = held_type % class_name - self.Add('template', held_type) name = self.info.rename or self.class_.name self.Add('constructor', '"%s"' % name) @@ -211,14 +205,14 @@ class ClassExporter(Exporter): def ExportBases(self, exported_names): 'Expose the bases of the class into the template section' hierarchy = self.class_.hierarchy + exported = [] for level in hierarchy: - exported = [] for base in level: if base.visibility == Scope.public and base.name in exported_names: exported.append(base.name) - if exported: - code = namespaces.python + 'bases< %s > ' % (', '.join(exported)) - self.Add('template', code) + if exported: + code = namespaces.python + 'bases< %s > ' % (', '.join(exported)) + self.Add('template', code) def ExportConstructors(self): @@ -408,13 +402,19 @@ class ClassExporter(Exporter): has_virtual_methods = True break + holder = self.info.holder if has_virtual_methods: generator = _VirtualWrapperGenerator(self.class_, self.ClassBases(), self.info) - self.Add('template', generator.FullName()) + if holder: + self.Add('template', holder(generator.FullName())) + else: + self.Add('template', generator.FullName()) for definition in generator.GenerateDefinitions(): self.Add('inside', definition) self.Add('declaration', generator.GenerateVirtualWrapper(self.INDENT)) - + else: + if holder: + self.Add('template', holder(self.class_.FullName())) # operators natively supported by boost BOOST_SUPPORTED_OPERATORS = '+ - * / % ^ & ! ~ | < > == != <= >= << >> && || += -='\ @@ -610,7 +610,7 @@ class ClassExporter(Exporter): if smart_ptr: class_name = self.class_.FullName() smart_ptr = smart_ptr % class_name - self.Add('scope', '%s::register_ptr_to_python< %s >();' % (namespaces.python, smart_ptr)) + self.Add('scope', '%sregister_ptr_to_python< %s >();' % (namespaces.python, smart_ptr)) def ExportOpaquePointerPolicies(self): diff --git a/pyste/src/Pyste/CppParser.py b/pyste/src/Pyste/CppParser.py index 716779ec..04f1ed2c 100644 --- a/pyste/src/Pyste/CppParser.py +++ b/pyste/src/Pyste/CppParser.py @@ -2,16 +2,25 @@ from GCCXMLParser import ParseDeclarations import tempfile import shutil import os +import sys import os.path import settings +import shutil +import shelve +from cPickle import dump, load +#============================================================================== +# exceptions +#============================================================================== class CppParserError(Exception): pass - +#============================================================================== +# CppParser +#============================================================================== class CppParser: 'Parses a header file and returns a list of declarations' - def __init__(self, includes=None, defines=None): + def __init__(self, includes=None, defines=None, cache_dir=None): 'includes and defines ar the directives given to gcc' if includes is None: includes = [] @@ -19,9 +28,26 @@ class CppParser: defines = [] self.includes = includes self.defines = defines + #if cache_dir is None: + # cache_dir = tempfile.mktemp() + # self.delete_cache = True + #else: + # self.delete_cache = False + self.delete_cache = False + self.cache_dir = cache_dir + self.cache_files = [] + # create the cache dir + if cache_dir: + try: + os.makedirs(cache_dir) + except OSError: pass - def _includeparams(self, filename): + def __del__(self): + self.Close() + + + def _IncludeParams(self, filename): includes = self.includes[:] filedir = os.path.dirname(filename) if not filedir: @@ -31,64 +57,153 @@ class CppParser: return ' '.join(includes) - def _defineparams(self): + def _DefineParams(self): defines = ['-D "%s"' % x for x in self.defines] return ' '.join(defines) - def FindFileName(self, include): - if os.path.isfile(include): - return include + def FindHeader(self, header): + if os.path.isfile(header): + return header for path in self.includes: - filename = os.path.join(path, include) + filename = os.path.join(path, header) if os.path.isfile(filename): return filename - name = os.path.basename(include) - raise RuntimeError, 'Header file "%s" not found!' % name + else: + name = os.path.basename(header) + raise RuntimeError, 'Header file "%s" not found!' % name - def parse(self, include, tail=None, decl_name=None): - '''Parses the given filename, and returns (declaration, header). The - header returned is normally the same as the given to this method, - except if tail is not None: in this case, the header is copied to a temp - filename and the tail code is appended to it before being passed on to gcc. - This temp filename is then returned. - ''' - filename = self.FindFileName(include) - # copy file to temp folder, if needed + def AppendTail(self, filename, tail): + '''Creates a temporary file, appends the text tail to it, and returns + the filename of the file. + ''' + temp = tempfile.mktemp('.h') + shutil.copyfile(filename, temp) + f = file(temp, 'a') + f.write('\n\n'+tail) + f.close() + return temp + + + def ParseWithGCCXML(self, header, tail): + '''Parses the given header using gccxml and GCCXMLParser. + ''' + header = self.FindHeader(header) if tail: - tempfilename = tempfile.mktemp('.h') - infilename = tempfilename - shutil.copyfile(filename, infilename) - f = file(infilename, 'a') - f.write('\n\n'+tail) - f.close() + filename = self.AppendTail(header, tail) else: - infilename = filename + filename = header xmlfile = tempfile.mktemp('.xml') try: # get the params - includes = self._includeparams(filename) - defines = self._defineparams() + includes = self._IncludeParams(filename) + defines = self._DefineParams() # call gccxml - cmd = 'gccxml %s %s %s -fxml=%s' \ - % (includes, defines, infilename, xmlfile) - if decl_name is not None: - cmd += ' "-fxml-start=%s"' % decl_name - status = os.system(cmd) + cmd = 'gccxml %s %s %s -fxml=%s' + status = os.system(cmd % (includes, defines, filename, xmlfile)) if status != 0 or not os.path.isfile(xmlfile): raise CppParserError, 'Error executing gccxml' # parse the resulting xml declarations = ParseDeclarations(xmlfile) + # make the declarations' location to point to the original file + if tail: + for decl in declarations: + decl_filename = os.path.normpath(os.path.normcase(decl.location[0])) + filename = os.path.normpath(os.path.normcase(filename)) + if decl_filename == filename: + decl.location = header, decl.location[1] # return the declarations - return declarations, infilename + return declarations finally: if settings.DEBUG and os.path.isfile(xmlfile): - filename = os.path.basename(include) - shutil.copy(xmlfile, os.path.splitext(filename)[0] + '.xml') + filename = os.path.basename(header) + filename = os.path.splitext(filename)[0] + '.xml' + shutil.copy(xmlfile, filename) # delete the temporary files try: os.remove(xmlfile) if tail: - os.remove(tempfilename) - except OSError: pass + os.remove(filename) + except OSError: pass + + + def Parse(self, header, interface, tail=None): + '''Parses the given filename related to the given interface and returns + the (declarations, headerfile). The header returned is normally the + same as the given to this method (except that it is the full path), + except if tail is not None: in this case, the header is copied to a temp + filename and the tail code is appended to it before being passed on to + gccxml. This temp filename is then returned. + ''' + if tail is None: + tail = '' + tail.strip() + declarations = self.GetCache(header, interface, tail) + if declarations is None: + declarations = self.ParseWithGCCXML(header, tail) + if self.cache_dir is not None: + self.CreateCache(header, interface, tail, declarations) + return declarations, header + + + def CacheFileName(self, interface): + interface_name = os.path.basename(interface) + cache_file = os.path.splitext(interface_name)[0] + '.pystec' + cache_file = os.path.join(self.cache_dir, cache_file) + return cache_file + + + + def GetCache(self, header, interface, tail): + if self.cache_dir is None: + return None + header = self.FindHeader(header) + cache_file = self.CacheFileName(interface) + if os.path.isfile(cache_file): + f = file(cache_file, 'rb') + try: + cache = load(f) + key = (header, interface, tail) + if cache.has_key(key): + self.cache_files.append(cache_file) + return cache[key] + else: + return None + finally: + f.close() + else: + return None + + + def CreateCache(self, header, interface, tail, declarations): + header = self.FindHeader(header) + cache_file = self.CacheFileName(interface) + if os.path.isfile(cache_file): + f = file(cache_file, 'rb') + try: + cache = load(f) + finally: + f.close() + else: + cache = {} + key = (header, interface, tail) + cache[key] = declarations + self.cache_files.append(cache_file) + f = file(cache_file, 'wb') + try: + dump(cache, f, 1) + finally: + f.close() + return cache_file + + + def Close(self): + if self.delete_cache and self.cache_files: + for filename in self.cache_files: + try: + os.remove(filename) + except OSError: + pass + self.cache_files = [] + shutil.rmtree(self.cache_dir) diff --git a/pyste/src/Pyste/EnumExporter.py b/pyste/src/Pyste/EnumExporter.py index 4e469db1..d4aabaf4 100644 --- a/pyste/src/Pyste/EnumExporter.py +++ b/pyste/src/Pyste/EnumExporter.py @@ -14,7 +14,10 @@ class EnumExporter(Exporter): def SetDeclarations(self, declarations): Exporter.SetDeclarations(self, declarations) - self.enum = self.GetDeclaration(self.info.name) + if self.declarations: + self.enum = self.GetDeclaration(self.info.name) + else: + self.enum = None def Export(self, codeunit, exported_names): @@ -34,12 +37,8 @@ class EnumExporter(Exporter): code += in_indent + '.value("%s", %s)\n' % (rename, value_fullname) code += indent + ';\n\n' codeunit.Write('module', code) - exported_names[self.enum.FullName()] = 1 + exported_names[self.Name()] = 1 - def Unit(self): - return utils.makeid(self.info.include) - - - def Order(self): + def Name(self): return self.info.name diff --git a/pyste/src/Pyste/Exporter.py b/pyste/src/Pyste/Exporter.py index c7de9072..d4a702ad 100644 --- a/pyste/src/Pyste/Exporter.py +++ b/pyste/src/Pyste/Exporter.py @@ -12,10 +12,11 @@ class Exporter(object): self.info = info self.parser_tail = parser_tail self.interface_file = None + self.declarations = [] def Name(self): - return self.info.name + raise NotImplementedError(self.__class__.__name__) def Tail(self): @@ -73,12 +74,15 @@ class Exporter(object): '''Returns a string that uniquely identifies this instance. All exporters will be sorted by Order before being exported. ''' - raise NotImplementedError - - - def Unit(self): - return self.info.include + return 0, self.info.name def Header(self): return self.info.include + + + def __eq__(self, other): + return self.Name() == other.Name() + + def __ne__(self, other): + return self.Name() != other.Name() diff --git a/pyste/src/Pyste/FunctionExporter.py b/pyste/src/Pyste/FunctionExporter.py index 53dea144..4d5854d7 100644 --- a/pyste/src/Pyste/FunctionExporter.py +++ b/pyste/src/Pyste/FunctionExporter.py @@ -24,7 +24,7 @@ class FunctionExporter(Exporter): self.ExportDeclaration(decl, len(decls) == 1, codeunit) self.ExportOpaquePointer(decl, codeunit) self.GenerateOverloads(decls, codeunit) - exported_names[decl.FullName()] = 1 + exported_names[self.Name()] = 1 def ExportDeclaration(self, decl, unique, codeunit): @@ -82,9 +82,6 @@ class FunctionExporter(Exporter): if macro: codeunit.Write('declaration-outside', macro) - def Order(self): + + def Name(self): return self.info.name - - - def Unit(self): - return utils.makeid(self.info.include) diff --git a/pyste/src/Pyste/HeaderExporter.py b/pyste/src/Pyste/HeaderExporter.py index 3ba1ecd8..9b3a3ea4 100644 --- a/pyste/src/Pyste/HeaderExporter.py +++ b/pyste/src/Pyste/HeaderExporter.py @@ -66,17 +66,11 @@ class HeaderExporter(Exporter): exporter.SetDeclarations(self.declarations) exporter.SetParsedHeader(self.parser_header) if isinstance(codeunit, MultipleCodeUnit.MultipleCodeUnit): - codeunit.SetCurrent(self.interface_file, exporter.Unit()) + codeunit.SetCurrent(self.interface_file, exporter.Name()) else: - codeunit.SetCurrent(exporter.Unit()) + codeunit.SetCurrent(exporter.Name()) exporter.GenerateCode(codeunit, exported_names) - - def Unit(self): - return None # doesn't write anything by itself - - def Order(self): + def Name(self): return self.info.include - - diff --git a/pyste/src/Pyste/IncludeExporter.py b/pyste/src/Pyste/IncludeExporter.py index c60a2848..b8d41f63 100644 --- a/pyste/src/Pyste/IncludeExporter.py +++ b/pyste/src/Pyste/IncludeExporter.py @@ -17,11 +17,8 @@ class IncludeExporter(Exporter): def Parse(self, parser): pass - def Order(self): - return self.info.include - - def Unit(self): - return '__all__' # include it in all generated cpps (multiple mode) + def Name(self): + return '__all__' def Header(self): return None # means "don't try to parse me!" diff --git a/pyste/src/Pyste/MultipleCodeUnit.py b/pyste/src/Pyste/MultipleCodeUnit.py index 5e8d9429..f7eaacba 100644 --- a/pyste/src/Pyste/MultipleCodeUnit.py +++ b/pyste/src/Pyste/MultipleCodeUnit.py @@ -22,8 +22,9 @@ class MultipleCodeUnit(object): self.all = SingleCodeUnit(None, None) - def _FunctionName(self, export_name): - return 'Export_%s' % utils.makeid(export_name) + def _FunctionName(self, interface_file): + name = os.path.splitext(interface_file)[0] + return 'Export_%s' % utils.makeid(name) def _FileName(self, interface_file): @@ -40,13 +41,13 @@ class MultipleCodeUnit(object): self._current = self.all else: filename = self._FileName(interface_file) - function = self._FunctionName(export_name) + function = self._FunctionName(interface_file) try: - codeunit = self.codeunits[(filename, function)] + codeunit = self.codeunits[filename] except KeyError: codeunit = SingleCodeUnit(None, filename) codeunit.module_definition = 'void %s()' % function - self.codeunits[(filename, function)] = codeunit + self.codeunits[filename] = codeunit if function not in self.functions: self.functions.append(function) self._current = codeunit @@ -84,7 +85,7 @@ class MultipleCodeUnit(object): # unit in the list of code units is used as the main unit # which dumps all the include, declaration and # declaration-outside sections at the top of the file. - for (filename, _), codeunit in self.codeunits.items(): + for filename, codeunit in self.codeunits.items(): if filename not in codeunits: # this codeunit is the main codeunit. codeunits[filename] = [codeunit] @@ -103,20 +104,24 @@ class MultipleCodeUnit(object): codeunit.Save(append) if not append: append = True + + + def GenerateMain(self, interfaces): # generate the main cpp filename = os.path.join(self.outdir, '_main.cpp') fout = SmartFile(filename, 'w') fout.write(utils.left_equals('Include')) - fout.write('#include \n\n') + fout.write('#include \n\n') fout.write(utils.left_equals('Exports')) - for function in self.functions: + functions = [self._FunctionName(x) for x in interfaces] + for function in functions: fout.write('void %s();\n' % function) fout.write('\n') fout.write(utils.left_equals('Module')) fout.write('BOOST_PYTHON_MODULE(%s)\n' % self.modulename) fout.write('{\n') indent = ' ' * 4 - for function in self.functions: + for function in functions: fout.write(indent) fout.write('%s();\n' % function) fout.write('}\n') diff --git a/pyste/src/Pyste/SingleCodeUnit.py b/pyste/src/Pyste/SingleCodeUnit.py index f86cdc09..3a8c73ea 100644 --- a/pyste/src/Pyste/SingleCodeUnit.py +++ b/pyste/src/Pyste/SingleCodeUnit.py @@ -85,13 +85,14 @@ class SingleCodeUnit: fout.write(declaration_outside + '\n\n') if declaration: pyste_namespace = namespaces.pyste[:-2] - fout.write('namespace %s {\n\n\n' % pyste_namespace) + fout.write('namespace %s {\n\n' % pyste_namespace) fout.write(declaration) - fout.write('\n\n}// namespace %s\n' % pyste_namespace) + fout.write('\n}// namespace %s\n' % pyste_namespace) fout.write(space) # module fout.write(left_equals('Module')) fout.write(self.module_definition + '\n') fout.write('{\n') fout.write(self.code['module']) - fout.write('}\n') + fout.write('}\n\n') + fout.close() diff --git a/pyste/src/Pyste/VarExporter.py b/pyste/src/Pyste/VarExporter.py index 461551a2..7fd830a3 100644 --- a/pyste/src/Pyste/VarExporter.py +++ b/pyste/src/Pyste/VarExporter.py @@ -28,8 +28,8 @@ class VarExporter(Exporter): def Order(self): + return 0, self.info.name + + + def Name(self): return self.info.name - - - def Unit(self): - return utils.makeid(self.info.include) diff --git a/pyste/src/Pyste/infos.py b/pyste/src/Pyste/infos.py index 43b0581a..c457f208 100644 --- a/pyste/src/Pyste/infos.py +++ b/pyste/src/Pyste/infos.py @@ -58,7 +58,8 @@ class FunctionInfo(DeclarationInfo): self._Attribute('exclude', False) # create a FunctionExporter exporter = FunctionExporter(InfoWrapper(self), tail) - exporters.exporters.append(exporter) + if exporter not in exporters.exporters: + exporters.exporters.append(exporter) exporter.interface_file = exporters.current_interface @@ -74,7 +75,8 @@ class ClassInfo(DeclarationInfo): self._Attribute('exclude', False) # create a ClassExporter exporter = ClassExporter(InfoWrapper(self), tail) - exporters.exporters.append(exporter) + if exporter not in exporters.exporters: + exporters.exporters.append(exporter) exporter.interface_file = exporters.current_interface @@ -87,7 +89,8 @@ class IncludeInfo(DeclarationInfo): DeclarationInfo.__init__(self) self._Attribute('include', include) exporter = IncludeExporter(InfoWrapper(self)) - exporters.exporters.append(exporter) + if exporter not in exporters.exporters: + exporters.exporters.append(exporter) exporter.interface_file = exporters.current_interface @@ -137,7 +140,8 @@ class EnumInfo(DeclarationInfo): self._Attribute('include', include) self._Attribute('exclude', False) exporter = EnumExporter(InfoWrapper(self)) - exporters.exporters.append(exporter) + if exporter not in exporters.exporters: + exporters.exporters.append(exporter) exporter.interface_file = exporters.current_interface @@ -150,7 +154,8 @@ class HeaderInfo(DeclarationInfo): DeclarationInfo.__init__(self) self._Attribute('include', include) exporter = HeaderExporter(InfoWrapper(self)) - exporters.exporters.append(exporter) + if exporter not in exporters.exporters: + exporters.exporters.append(exporter) exporter.interface_file = exporters.current_interface @@ -164,7 +169,8 @@ class VarInfo(DeclarationInfo): self._Attribute('name', name) self._Attribute('include', include) exporter = VarExporter(InfoWrapper(self)) - exporters.exporters.append(exporter) + if exporter not in exporters.exporters: + exporters.exporters.append(exporter) exporter.interface_file = exporters.current_interface @@ -215,11 +221,10 @@ def use_shared_ptr(info): def use_auto_ptr(info): info._Attribute('smart_ptr', 'std::auto_ptr< %s >') -def hold_with_shared_ptr(info): - info._Attribute('held_type', 'boost::shared_ptr< %s >') - -def hold_with_auto_ptr(info): - info._Attribute('held_type', 'std::auto_ptr< %s >') +def holder(info, function): + msg = "Expected a callable that accepts one string argument." + assert callable(function), msg + info._Attribute('holder', function) def add_method(info, name, rename=None): added = info._Attribute('__added__') diff --git a/pyste/src/Pyste/pyste-profile.py b/pyste/src/Pyste/pyste-profile.py index d7afff45..c6135ed4 100644 --- a/pyste/src/Pyste/pyste-profile.py +++ b/pyste/src/Pyste/pyste-profile.py @@ -2,7 +2,6 @@ import profile import pstats import pyste -import psyco import elementtree.XMLTreeBuilder as XMLTreeBuilder import GCCXMLParser @@ -12,6 +11,6 @@ if __name__ == '__main__': #psyco.bind(XMLTreeBuilder.fixname) #psyco.bind(XMLTreeBuilder.TreeBuilder) #psyco.bind(GCCXMLParser.GCCXMLParser) - profile.run('pyste.Main()', 'profile') + profile.run('pyste.main()', 'profile') p = pstats.Stats('profile') - p.strip_dirs().sort_stats(-1).print_stats() + p.strip_dirs().sort_stats('cumulative').print_stats() diff --git a/pyste/src/Pyste/pyste.py b/pyste/src/Pyste/pyste.py index 394f17fc..a9db4954 100644 --- a/pyste/src/Pyste/pyste.py +++ b/pyste/src/Pyste/pyste.py @@ -1,28 +1,31 @@ -''' +""" 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 - -h, --help print this help and exit - -v, --version print version information -''' + --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) + -h, --help Print this help and exit + -v, --version Print version information +""" import sys import os @@ -70,16 +73,22 @@ def ParseArguments(): options, files = getopt.getopt( sys.argv[1:], 'R:I:D:vh', - ['module=', 'multiple', 'out=', 'no-using', 'pyste-ns=', 'debug', 'version', 'help']) + ['module=', 'multiple', 'out=', 'no-using', 'pyste-ns=', 'debug', 'cache-dir=', + 'only-create-cache', 'version', 'generate-main', 'help']) except getopt.GetoptError, e: print print 'ERROR:', e Usage() + includes = GetDefaultIncludes() defines = [] module = None out = None multiple = False + cache_dir = None + create_cache = False + generate_main = False + for opt, value in options: if opt == '-I': includes.append(value) @@ -100,11 +109,17 @@ def ParseArguments(): 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 in ['-h', '--help']: Usage() elif opt in ['-v', '--version']: print 'Pyste version %s' % __VERSION__ sys.exit(2) + elif opt == '--generate-main': + generate_main = True else: print 'Unknown option:', opt Usage() @@ -122,12 +137,23 @@ def ParseArguments(): if d not in sys.path: sys.path.append(d) - return includes, defines, module, out, files, multiple + 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) + + return includes, defines, module, out, files, multiple, cache_dir, create_cache, generate_main def CreateContext(): 'create the context where a interface file will be executed' context = {} + context['Import'] = ExecuteInterface # infos context['Function'] = infos.FunctionInfo context['Class'] = infos.ClassInfo @@ -143,8 +169,7 @@ def CreateContext(): context['set_wrapper'] = infos.set_wrapper context['use_shared_ptr'] = infos.use_shared_ptr context['use_auto_ptr'] = infos.use_auto_ptr - context['hold_with_shared_ptr'] = infos.hold_with_shared_ptr - context['hold_with_auto_ptr'] = infos.hold_with_auto_ptr + context['holder'] = infos.holder context['add_method'] = infos.add_method context['final'] = infos.final # policies @@ -162,70 +187,146 @@ def CreateContext(): def Begin(): - includes, defines, module, out, interfaces, multiple = ParseArguments() - # execute the interface files + # parse arguments + includes, defines, module, out, interfaces, multiple, cache_dir, create_cache, generate_main = ParseArguments() + # run pyste scripts for interface in interfaces: - exporters.current_interface = interface - context = CreateContext() - execfile(interface, context) + ExecuteInterface(interface) # create the parser - parser = CppParser(includes, defines) + parser = CppParser(includes, defines, cache_dir) + 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() + execfile(interface, context) + exporters.current_interface = old_interface + + +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) - # group exporters by header files - groups = {} - for export in exporters.exporters: - header = export.Header() - if header in groups: - groups[header].append(export) - else: - groups[header] = [export] # stop referencing the exporters here + exports = exporters.exporters exporters.exporters = None - # export all the exporters in each group, releasing memory as doing so - while len(groups) > 0: - # get the first group - header = groups.keys()[0] - exports = groups[header] - del groups[header] - # gather all tails into one - all_tails = [] - for export in exports: - if export.Tail(): - all_tails.append(export.Tail()) - all_tails = '\n'.join(all_tails) - # parse header (if there's one) + exported_names = dict([(x.Name(), None) for x in exports]) + + # order the exports + interfaces_order = OrderInterfaces(interfaces) + order = {} + for export in exports: + if export.interface_file in order: + order[export.interface_file].append(export) + else: + order[export.interface_file] = [export] + exports = [] + 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: - try: - declarations, parsed_header = parser.parse(header, all_tails) - except CppParserError, e: - print>>sys.stderr, '\n\n***', e, ': exitting' - return 2 + tail = tails[(interface, header)] + declarations, parsed_header = parser.Parse(header, interface, tail) else: declarations = [] parsed_header = None - # first set the declarations and parsed_header for all the exporters - for export in exports: - export.SetDeclarations(declarations) - export.SetParsedHeader(parsed_header) - # sort the exporters by their order - exports = [(x.Order(), x) for x in exports] - exports.sort() - exports = [x for _, x in exports] - # maintain a dict of exported_names for this group - exported_names = {} - for export in exports: - if multiple: - codeunit.SetCurrent(export.interface_file, export.Unit()) - export.GenerateCode(codeunit, 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() - print 'Module %s generated' % module + codeunit.Save() + if not multiple: + print 'Module %s generated' % module return 0