#!/usr/bin/env python
#---------------------------------------------------------------------------
"""
-Usage: build_renamers.py filename.i
+Usage: build_renamers.py destdir modulename filename.xml
-Run SWIG on file.i using the XML language module and then scan the XML
-file produced and generate the %rename directives needed to implement
-the new wx namespace. The rename directives are output in a file
-named _filename_rename.i in the same dir as filename.i.
+Scans the XML file produced by SWIG (see setup.py) and generate the
+%rename directives needed to implement the new wx namespace. The
+rename directives are output in a file named _modulename_rename.i in
+the destdir given.
Also output a reverse 'renamer' Python module located in
wxPython/filename.py (relative the the current dir) to make a
"""
import sys, os, tempfile, pprint
-import xml.sax
+import xml.sax
from distutils.spawn import spawn
-
-
-#---------------------------------------------------------------------------
-
-DO_UNLINK = True
+try:
+ import libxml2
+ USE_LIBXML2 = True
+except ImportError:
+ USE_LIBXML2 = False
wxPythonDir = "wxPython"
-swig_cmd = "/opt/swig/bin/swig"
-if os.name == 'nt':
- swig_cmd = 'e:/projects/SWIG-cvs/swig.exe'
-swig_args = ['-c++',
- '-Wall',
- '-nodefault',
-
- '-xml',
- '-xmllite',
-
- '-I./src',
- '-noruntime'
- ]
+#---------------------------------------------------------------------------
renamerTemplateStart = """\
// A bunch of %%rename directives generated by %s
// in order to remove the wx prefix from all global scope names.
-#ifndef SWIGXML
+#ifndef BUILDING_RENAMERS
"""
def main(args):
# check args
- if len(args) < 1:
+ if len(args) < 3:
print __doc__
sys.exit(1)
sys.exit(1)
- source = args[0]
- sourcePath, sourceBase = os.path.split(source)
- sourceBase = os.path.splitext(sourceBase)[0]
-
- tempfile.tempdir = sourcePath
- xmlDest = tempfile.mktemp('.xml')
- swigDest = os.path.join(sourcePath, "_"+sourceBase+"_rename.i")
- pyDest = os.path.join(wxPythonDir, sourceBase + '.py')
-
- #print "source: ", source
- #print "xmlDest: ", xmlDest
- #print "swigDest: ", swigDest
- #print "pyDest: ", pyDest
-
- cmd = [ swig_cmd ] + swig_args + args[1:] + ['-I'+sourcePath, '-o', xmlDest, source]
- print ' '.join(cmd)
- spawn(cmd)
+ destdir = args[0]
+ modname = args[1]
+ xmlfile = args[2]
+
+ swigDest = os.path.join(destdir, "_"+modname+"_rename.i")
+ pyDest = os.path.join(wxPythonDir, modname + '.py')
swigDestTemp = tempfile.mktemp('.tmp')
swigFile = open(swigDestTemp, "w")
swigFile.write(renamerTemplateStart % sys.argv[0])
- pyFile = open(pyDest, "w")
- pyFile.write(wxPythonTemplateStart % (sys.argv[0], sourceBase))
-
- print "Parsing and building renamers",
+ pyDestTemp = tempfile.mktemp('.tmp')
+ pyFile = open(pyDestTemp, "w")
+ pyFile.write(wxPythonTemplateStart % (sys.argv[0], modname))
+
try:
-## try:
-## import libxml2
-## print "using libxml2..."
-## ctxt = libxml2.createPushParser(ContentHandler(source, sourceBase, swigFile, pyFile),
-## '', 0, xmlDest)
-## for line in file(xmlDest):
-## if not line:
-## ctxt.parseChunck('', 0, 1)
-## break
-## ctxt.parseChunk(line, len(line), 0)
-
-## except ImportError:
- print "using xml.sax..."
- xml.sax.parse(xmlDest, ContentHandler(source, sourceBase, swigFile, pyFile))
+ print "Parsing and building renamers",
+ if USE_LIBXML2:
+ processXML(xmlfile, modname, swigFile, pyFile)
+ else:
+ print "using xml.sax..."
+ xml.sax.parse(xmlfile, ContentHandler(modname, swigFile, pyFile))
finally:
- checkOtherNames(pyFile, sourceBase,
- os.path.join(sourcePath, '_'+sourceBase+'_reverse.txt'))
+
+ checkOtherNames(pyFile, modname,
+ os.path.join(destdir, '_'+modname+'_reverse.txt'))
pyFile.write(wxPythonTemplateEnd)
pyFile.close()
swigFile.write(renamerTemplateEnd)
swigFile.close()
- # Compare the file just created with the existing one and
+ # Compare the files just created with the existing one and
# blow away the old one if they are different.
- if open(swigDest).read() != open(swigDestTemp).read():
- os.unlink(swigDest)
- os.rename(swigDestTemp, swigDest)
- else:
- print swigDest + " not changed."
- os.unlink(swigDestTemp)
+ for dest, temp in [(swigDest, swigDestTemp),
+ (pyDest, pyDestTemp)]:
+ if not os.path.exists(dest):
+ os.rename(temp, dest)
+ elif open(dest).read() != open(temp).read():
+ os.unlink(dest)
+ os.rename(temp, dest)
+ else:
+ print dest + " not changed."
+ os.unlink(temp)
+
+#---------------------------------------------------------------------------
+
+
+def GetAttr(node, name):
+ path = "./attributelist/attribute[@name='%s']/@value" % name
+ n = node.xpathEval2(path)
+ if len(n):
+ return n[0].content
+ else:
+ return None
+
+
+def processXML(xmlfile, modname, swigFile, pyFile):
+ import libxml2
+ print "using libxml2..."
+
+ topnode = libxml2.parseFile(xmlfile).children
+
+ # remove any import nodes as we don't need to do renamers for symbols found therein
+ imports = topnode.xpathEval2("*/import")
+ for n in imports:
+ n.unlinkNode()
+ n.freeNode()
+
+ # do a depth first iteration over what's left
+ for node in topnode:
+ doRename = False
+ doPtr = False
+ addWX = False
+ revOnly = False
- if DO_UNLINK:
- os.unlink(xmlDest)
+ if node.name == "class":
+ lastClassName = name = GetAttr(node, "name")
+ lastClassSymName = sym_name = GetAttr(node, "sym_name")
+ doRename = True
+ doPtr = True
+ if sym_name != name:
+ name = sym_name
+ addWX = True
+
+ # renamed constructors
+ elif node.name == "constructor":
+ name = GetAttr(node, "name")
+ sym_name = GetAttr(node, "sym_name")
+ if sym_name != name:
+ name = sym_name
+ addWX = True
+ doRename = True
+
+ # only enumitems at the top level
+ elif node.name == "enumitem" and node.parent.parent.name == "include":
+ name = GetAttr(node, "name")
+ sym_name = GetAttr(node, "sym_name")
+ doRename = True
+
+
+ elif node.name in ["cdecl", "constant"]:
+ name = GetAttr(node, "name")
+ sym_name = GetAttr(node, "sym_name")
+ toplevel = node.parent.name == "include"
+
+ # top-level functions
+ if toplevel and GetAttr(node, "view") == "globalfunctionHandler":
+ doRename = True
+
+ # top-level global vars
+ elif toplevel and GetAttr(node, "feature_immutable") == "1":
+ doRename = True
+
+ # static methods
+ elif GetAttr(node, "view") == "staticmemberfunctionHandler":
+ name = lastClassName + '_' + name
+ sym_name = lastClassSymName + '_' + sym_name
+ # only output the reverse renamer in this case
+ doRename = revOnly = True
+
+ if doRename and name != sym_name:
+ name = sym_name
+ addWX = True
+
+
+ if doRename and name:
+ old = new = name
+ if old.startswith('wx') and not old.startswith('wxEVT_'):
+ # remove all wx prefixes except wxEVT_ and write a %rename directive for it
+ new = old[2:]
+ if not revOnly:
+ swigFile.write("%%rename(%s) %35s;\n" % (new, old))
+
+ # Write assignments to import into the old wxPython namespace
+ if addWX and not old.startswith('wx'):
+ old = 'wx'+old
+ pyFile.write("%s = wx.%s.%s\n" % (old, modname, new))
+ if doPtr:
+ pyFile.write("%sPtr = wx.%s.%sPtr\n" % (old, modname, new))
+
#---------------------------------------------------------------------------
#---------------------------------------------------------------------------
class ContentHandler(xml.sax.ContentHandler):
- def __init__(self, source, sourceBase, swigFile, pyFile):
+ def __init__(self, modname, swigFile, pyFile):
xml.sax.ContentHandler.__init__(self)
- self.source = source
- self.sourceBase = sourceBase
+ self.modname = modname
self.swigFile = swigFile
self.pyFile = pyFile
self.elements = []
if self.imports == 0:
# only write for items that are in this file, not imported
- ce.write(self.sourceBase, self.swigFile, self.pyFile)
+ ce.write(self.modname, self.swigFile, self.pyFile)
if name == 'import':
self.imports -= 1