]> git.saurik.com Git - wxWidgets.git/blob - wxPython/distrib/build_renamers.py
reSWIGged
[wxWidgets.git] / wxPython / distrib / build_renamers.py
1 #!/usr/bin/env python
2 #---------------------------------------------------------------------------
3 """
4 Usage: build_renamers.py destdir modulename filename.xml
5
6 Scans the XML file produced by SWIG (see setup.py) and generate the
7 %rename directives needed to implement the new wx namespace. The
8 rename directives are output in a file named _modulename_rename.i in
9 the destdir given.
10
11 Also output a reverse 'renamer' Python module located in
12 wxPython/filename.py (relative the the current dir) to make a
13 backwards compatibility interface for the old wxPython packages.
14 """
15
16 import sys, os, tempfile, pprint
17 import xml.sax
18 from distutils.spawn import spawn
19
20 try:
21 import libxml2
22 USE_LIBXML2 = True
23 except ImportError:
24 USE_LIBXML2 = False
25
26 wxPythonDir = "wxPython"
27
28 #---------------------------------------------------------------------------
29
30
31 renamerTemplateStart = """\
32 // A bunch of %%rename directives generated by %s
33 // in order to remove the wx prefix from all global scope names.
34
35 #ifndef BUILDING_RENAMERS
36
37 """
38
39 renamerTemplateEnd = """
40 #endif
41 """
42
43 wxPythonTemplateStart = """\
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.
52 import sys
53 _wx = None
54 if sys.modules.has_key('wxPython.wx'):
55 _wx = sys.modules['wxPython.wx']
56 del sys.modules['wxPython.wx']
57
58 import wx.%s
59
60 sys.modules['wxPython.wx'] = _wx
61 del sys, _wx
62
63
64 # Now assign all the reverse-renamed names:
65 """
66
67 wxPythonTemplateEnd = """
68
69 """
70
71
72
73 #---------------------------------------------------------------------------
74
75 def main(args):
76 # check args
77 if len(args) < 3:
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
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')
94
95 swigDestTemp = tempfile.mktemp('.tmp')
96 swigFile = open(swigDestTemp, "w")
97 swigFile.write(renamerTemplateStart % sys.argv[0])
98
99 pyDestTemp = tempfile.mktemp('.tmp')
100 pyFile = open(pyDestTemp, "w")
101 pyFile.write(wxPythonTemplateStart % (sys.argv[0], modname))
102
103 try:
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))
110
111 finally:
112
113 checkOtherNames(pyFile, modname,
114 os.path.join(destdir, '_'+modname+'_reverse.txt'))
115 pyFile.write(wxPythonTemplateEnd)
116 pyFile.close()
117
118 swigFile.write(renamerTemplateEnd)
119 swigFile.close()
120
121 # Compare the files just created with the existing one and
122 # blow away the old one if they are different.
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
135 def 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
144 def 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
162
163
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
229
230 #---------------------------------------------------------------------------
231
232 def 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
266 interestingTypes = [ 'class', 'cdecl', 'enumitem', 'constructor', 'constant' ]
267 interestingAttrs = [ 'name', 'sym_name', 'decl', 'feature_immutable', 'module',
268 'storage', 'type' ]
269
270
271 class 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
288 doPtr = False
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
327 doPtr = True
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))
358 if doPtr:
359 pyFile.write("%sPtr = wx.%s.%sPtr\n" % (old, moduleName, new))
360
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
372 class ContentHandler(xml.sax.ContentHandler):
373 def __init__(self, modname, swigFile, pyFile):
374 xml.sax.ContentHandler.__init__(self)
375 self.modname = modname
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
466 ce.write(self.modname, self.swigFile, self.pyFile)
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
478 if __name__ == "__main__":
479 main(sys.argv[1:])
480