]> git.saurik.com Git - wxWidgets.git/blame - wxPython/distrib/build_renamers.py
reSWIGged
[wxWidgets.git] / wxPython / distrib / build_renamers.py
CommitLineData
d14a1e28
RD
1#!/usr/bin/env python
2#---------------------------------------------------------------------------
3"""
8994c3c2 4Usage: build_renamers.py destdir modulename filename.xml
d14a1e28 5
8994c3c2
RD
6Scans the XML file produced by SWIG (see setup.py) and generate the
7%rename directives needed to implement the new wx namespace. The
8rename directives are output in a file named _modulename_rename.i in
9the destdir given.
d14a1e28
RD
10
11Also output a reverse 'renamer' Python module located in
12wxPython/filename.py (relative the the current dir) to make a
13backwards compatibility interface for the old wxPython packages.
14"""
15
16import sys, os, tempfile, pprint
8994c3c2 17import xml.sax
d14a1e28
RD
18from distutils.spawn import spawn
19
8994c3c2
RD
20try:
21 import libxml2
22 USE_LIBXML2 = True
23except ImportError:
24 USE_LIBXML2 = False
d14a1e28
RD
25
26wxPythonDir = "wxPython"
d14a1e28 27
8994c3c2 28#---------------------------------------------------------------------------
d14a1e28
RD
29
30
31renamerTemplateStart = """\
32// A bunch of %%rename directives generated by %s
33// in order to remove the wx prefix from all global scope names.
34
8994c3c2 35#ifndef BUILDING_RENAMERS
d14a1e28
RD
36
37"""
38
39renamerTemplateEnd = """
40#endif
41"""
42
43wxPythonTemplateStart = """\
44## This file reverse renames symbols in the wx package to give
45## them their wx prefix again, for backwards compatibility.
46##
47## Generated by %s
48
49# This silly stuff here is so the wxPython.wx module doesn't conflict
50# with the wx package. We need to import modules from the wx package
51# here, then we'll put the wxPython.wx entry back in sys.modules.
52import sys
53_wx = None
54if sys.modules.has_key('wxPython.wx'):
55 _wx = sys.modules['wxPython.wx']
56 del sys.modules['wxPython.wx']
57
58import wx.%s
59
60sys.modules['wxPython.wx'] = _wx
61del sys, _wx
62
63
64# Now assign all the reverse-renamed names:
65"""
66
67wxPythonTemplateEnd = """
68
69"""
70
71
72
73#---------------------------------------------------------------------------
74
75def main(args):
76 # check args
8994c3c2 77 if len(args) < 3:
d14a1e28
RD
78 print __doc__
79 sys.exit(1)
80
81 # check location (there should be a wxPython subdir)
82 if not os.path.exists(wxPythonDir) or not os.path.isdir(wxPythonDir):
83 print __doc__
84 print "You should only run this script from the main wxPython source dir.\n"
85 sys.exit(1)
86
87
8994c3c2
RD
88 destdir = args[0]
89 modname = args[1]
90 xmlfile = args[2]
91
92 swigDest = os.path.join(destdir, "_"+modname+"_rename.i")
93 pyDest = os.path.join(wxPythonDir, modname + '.py')
d14a1e28
RD
94
95 swigDestTemp = tempfile.mktemp('.tmp')
96 swigFile = open(swigDestTemp, "w")
97 swigFile.write(renamerTemplateStart % sys.argv[0])
98
8994c3c2
RD
99 pyDestTemp = tempfile.mktemp('.tmp')
100 pyFile = open(pyDestTemp, "w")
101 pyFile.write(wxPythonTemplateStart % (sys.argv[0], modname))
102
d14a1e28 103 try:
8994c3c2
RD
104 print "Parsing and building renamers",
105 if USE_LIBXML2:
106 processXML(xmlfile, modname, swigFile, pyFile)
107 else:
108 print "using xml.sax..."
109 xml.sax.parse(xmlfile, ContentHandler(modname, swigFile, pyFile))
d14a1e28
RD
110
111 finally:
8994c3c2
RD
112
113 checkOtherNames(pyFile, modname,
114 os.path.join(destdir, '_'+modname+'_reverse.txt'))
d14a1e28
RD
115 pyFile.write(wxPythonTemplateEnd)
116 pyFile.close()
117
118 swigFile.write(renamerTemplateEnd)
119 swigFile.close()
120
8994c3c2 121 # Compare the files just created with the existing one and
d14a1e28 122 # blow away the old one if they are different.
8994c3c2
RD
123 for dest, temp in [(swigDest, swigDestTemp),
124 (pyDest, pyDestTemp)]:
125 if open(dest).read() != open(temp).read():
126 os.unlink(dest)
127 os.rename(temp, dest)
128 else:
129 print dest + " not changed."
130 os.unlink(temp)
131
132#---------------------------------------------------------------------------
133
134
135def GetAttr(node, name):
136 path = "./attributelist/attribute[@name='%s']/@value" % name
137 n = node.xpathEval(path)
138 if len(n):
139 return n[0].content
140 else:
141 return None
142
143
144def processXML(xmlfile, modname, swigFile, pyFile):
145 import libxml2
146 print "using libxml2..."
147
148 topnode = libxml2.parseFile(xmlfile).children
149
150 # remove any import nodes as we don't need to do renamers for symbols found therein
151 imports = topnode.xpathEval("*/import")
152 for n in imports:
153 n.unlinkNode()
154 n.freeNode()
155
156 # do a depth first iteration over what's left
157 for node in topnode:
158 doRename = False
159 doPtr = False
160 addWX = False
161 revOnly = False
d14a1e28 162
d14a1e28 163
8994c3c2
RD
164 if node.name == "class":
165 lastClassName = name = GetAttr(node, "name")
166 lastClassSymName = sym_name = GetAttr(node, "sym_name")
167 doRename = True
168 doPtr = True
169 if sym_name != name:
170 name = sym_name
171 addWX = True
172
173 # renamed constructors
174 elif node.name == "constructor":
175 name = GetAttr(node, "name")
176 sym_name = GetAttr(node, "sym_name")
177 if sym_name != name:
178 name = sym_name
179 addWX = True
180 doRename = True
181
182 # only enumitems at the top level
183 elif node.name == "enumitem" and node.parent.parent.name == "include":
184 name = GetAttr(node, "name")
185 sym_name = GetAttr(node, "sym_name")
186 doRename = True
187
188
189 elif node.name in ["cdecl", "constant"]:
190 name = GetAttr(node, "name")
191 sym_name = GetAttr(node, "sym_name")
192 toplevel = node.parent.name == "include"
193
194 # top-level functions
195 if toplevel and GetAttr(node, "view") == "globalfunctionHandler":
196 doRename = True
197
198 # top-level global vars
199 elif toplevel and GetAttr(node, "feature_immutable") == "1":
200 doRename = True
201
202 # static methods
203 elif GetAttr(node, "view") == "staticmemberfunctionHandler":
204 name = lastClassName + '_' + name
205 sym_name = lastClassSymName + '_' + sym_name
206 # only output the reverse renamer in this case
207 doRename = revOnly = True
208
209 if doRename and name != sym_name:
210 name = sym_name
211 addWX = True
212
213
214 if doRename:
215 old = new = name
216 if old.startswith('wx') and not old.startswith('wxEVT_'):
217 # remove all wx prefixes except wxEVT_ and write a %rename directive for it
218 new = old[2:]
219 if not revOnly:
220 swigFile.write("%%rename(%s) %35s;\n" % (new, old))
221
222 # Write assignments to import into the old wxPython namespace
223 if addWX and not old.startswith('wx'):
224 old = 'wx'+old
225 pyFile.write("%s = wx.%s.%s\n" % (old, modname, new))
226 if doPtr:
227 pyFile.write("%sPtr = wx.%s.%sPtr\n" % (old, modname, new))
228
d14a1e28
RD
229
230#---------------------------------------------------------------------------
231
232def checkOtherNames(pyFile, moduleName, filename):
233 if os.path.exists(filename):
234 prefixes = []
235 for line in file(filename):
236 if line.endswith('\n'):
237 line = line[:-1]
238 if line and not line.startswith('#'):
239 if line.endswith('*'):
240 prefixes.append(line[:-1])
241 elif line.find('=') != -1:
242 pyFile.write("%s\n" % line)
243 else:
244 wxname = 'wx' + line
245 if line.startswith('wx') or line.startswith('WX') or line.startswith('EVT'):
246 wxname = line
247 pyFile.write("%s = wx.%s.%s\n" % (wxname, moduleName, line))
248
249 if prefixes:
250 pyFile.write(
251 "\n\nd = globals()\nfor k, v in wx.%s.__dict__.iteritems():"
252 % moduleName)
253 first = True
254 for p in prefixes:
255 if first:
256 pyFile.write("\n if ")
257 first = False
258 else:
259 pyFile.write("\n elif ")
260 pyFile.write("k.startswith('%s'):\n d[k] = v" % p)
261 pyFile.write("\ndel d, k, v\n\n")
262
263
264#---------------------------------------------------------------------------
265
266interestingTypes = [ 'class', 'cdecl', 'enumitem', 'constructor', 'constant' ]
267interestingAttrs = [ 'name', 'sym_name', 'decl', 'feature_immutable', 'module',
268 'storage', 'type' ]
269
270
271class Element:
272 def __init__(self, tagtype):
273 self.tagtype = tagtype
274 self.level = -1
275 self.name = None
276 self.sym_name = None
277 self.decl = None
278 self.immutable = None
279 self.klass = None
280 self.module = None
281 self.storage = None
282 self.type = None
283 self.startLine = -1
284
285
286 def write(self, moduleName, swigFile, pyFile):
287 doRename = False
423af76e 288 doPtr = False
d14a1e28
RD
289 addWX = False
290 revOnly = False
291
292 #if self.name.find('DefaultPosition') != -1:
293 # pprint.pprint(self.__dict__)
294
295 if self.tagtype in ['cdecl', 'constant']:
296 if self.storage == 'typedef':
297 pass
298
299 # top level functions
300 elif self.level == 0 and self.decl != "":
301 doRename = True
302
303 # top level global vars
304 elif self.level == 0 and self.immutable == '1':
305 doRename = True
306
307 # static methods
308 elif self.storage == 'static':
309 if not self.klass:
310 pprint.pprint(self.__dict__)
311 else:
312 self.name = self.klass + '_' + self.name
313 self.sym_name = self.sym_klass + '_' + self.sym_name
314 # only output the reverse renamer in this case
315 doRename = revOnly = True
316
317
318
319 if doRename and self.name != self.sym_name:
320 #print "%-25s %-25s" % (self.name, self.sym_name)
321 self.name = self.sym_name
322 addWX = True
323
324
325 elif self.tagtype == 'class' and self.module == moduleName:
326 doRename = True
423af76e 327 doPtr = True
d14a1e28
RD
328 if self.sym_name != self.klass:
329 #print self.sym_name
330 self.name = self.sym_name
331 addWX = True
332
333 elif self.tagtype == 'constructor':
334 #print "%-25s %-25s" % (self.name, self.sym_name)
335 if self.sym_name != self.klass:
336 #print self.sym_name
337 self.name = self.sym_name
338 addWX = True
339 doRename = True
340
341 elif self.tagtype == 'enumitem' and self.level == 0:
342 doRename = True
343
344
345 if doRename:
346 #print "%-25s %-25s" % (self.name, self.sym_name)
347 old = new = self.name
348 if old.startswith('wx') and not old.startswith('wxEVT_'):
349 # remove all wx prefixes except wxEVT_ and write a %rename directive for it
350 new = old[2:]
351 if not revOnly:
352 swigFile.write("%%rename(%s) %35s;\n" % (new, old))
353
354 # Write assignments to import into the old wxPython namespace
355 if addWX and not old.startswith('wx'):
356 old = 'wx'+old
357 pyFile.write("%s = wx.%s.%s\n" % (old, moduleName, new))
423af76e
RD
358 if doPtr:
359 pyFile.write("%sPtr = wx.%s.%sPtr\n" % (old, moduleName, new))
360
d14a1e28
RD
361
362
363 #else:
364 # text = "%07d %d %10s %-35s %s\n" % (
365 # self.startLine, self.level, self.tagtype, self.name, self.decl)
366 # #rejects.write(text)
367 # print text,
368
369
370#---------------------------------------------------------------------------
371
372class ContentHandler(xml.sax.ContentHandler):
8994c3c2 373 def __init__(self, modname, swigFile, pyFile):
d14a1e28 374 xml.sax.ContentHandler.__init__(self)
8994c3c2 375 self.modname = modname
d14a1e28
RD
376 self.swigFile = swigFile
377 self.pyFile = pyFile
378 self.elements = []
379 self.imports = 0
380 self.klass = None
381 self.sym_klass = None
382
383
384 def setDocumentLocator(self, locator):
385 self.locator = locator
386
387
388
389 def startElement(self, name, attrs):
390 if name in interestingTypes:
391 # start of a new element that we are interested in
392 ce = Element(name)
393 ce.startLine = self.locator.getLineNumber()
394 ce.level = len(self.elements)
395 if name == 'constructor':
396 ce.klass = self.elements[0].name
397 else:
398 ce.klass = self.klass
399 ce.sym_klass = self.sym_klass
400 self.elements.insert(0, ce)
401
402
403 elif len(self.elements) and name == 'attribute' and attrs['name'] in interestingAttrs:
404 attrName = attrs['name']
405 attrVal = attrs['value']
406 if attrName.startswith('feature_'):
407 attrName = attrName.replace('feature_', '')
408 ce = self.elements[0]
409 if getattr(ce, attrName) is None:
410 setattr(ce, attrName, attrVal)
411 if ce.tagtype == 'class' and attrName == 'name' and self.klass is None:
412 self.klass = attrVal
413 if ce.tagtype == 'class' and attrName == 'sym_name' and self.sym_klass is None:
414 self.sym_klass = attrVal
415
416
417## elif len(self.elements) and name == 'attribute' and attrs['name'] == 'name':
418## # save the elements name
419## ce = self.elements[0]
420## if ce.name is None:
421## ce.name = attrs['value']
422## ce.nameLine = self.locator.getLineNumber()
423
424## elif len(self.elements) and name == 'attribute' and attrs['name'] == 'sym_name':
425## # save the elements name
426## ce = self.elements[0]
427## if ce.sym_name is None:
428## ce.sym_name = attrs['value']
429
430## elif len(self.elements) and name == 'attribute' and attrs['name'] == 'decl':
431## # save the elements decl
432## ce = self.elements[0]
433## ce.decl = attrs['value']
434
435## elif len(self.elements) and name == 'attribute' and attrs['name'] == 'feature_immutable':
436## # save the elements decl
437## ce = self.elements[0]
438## ce.immutable = int(attrs['value'])
439
440## elif len(self.elements) and name == 'attribute' and attrs['name'] == 'module':
441## # save the elements decl
442## ce = self.elements[0]
443## ce.module = attrs['value']
444
445 elif name == 'import':
446 self.imports += 1
447
448## elif len(self.elements) and name == 'attribute' and attrs['name'] == 'storage':
449## # save the elements decl
450## ce = self.elements[0]
451## ce.storage = attrs['value']
452
453## elif len(self.elements) and name == 'attribute' and attrs['name'] == 'type':
454## # save the elements decl
455## ce = self.elements[0]
456## ce.type = attrs['value']
457
458
459 def endElement(self, name):
460 if name in interestingTypes:
461 # end of an element that we are interested in
462 ce = self.elements.pop(0)
463
464 if self.imports == 0:
465 # only write for items that are in this file, not imported
8994c3c2 466 ce.write(self.modname, self.swigFile, self.pyFile)
d14a1e28
RD
467
468 if name == 'import':
469 self.imports -= 1
470
471 if name == 'class':
472 self.klass = None
473 self.sym_klass = None
474
475
476#---------------------------------------------------------------------------
477
478if __name__ == "__main__":
479 main(sys.argv[1:])
480