]>
Commit | Line | Data |
---|---|---|
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 not os.path.exists(dest): | |
126 | os.rename(temp, dest) | |
127 | elif open(dest).read() != open(temp).read(): | |
128 | os.unlink(dest) | |
129 | os.rename(temp, dest) | |
130 | else: | |
131 | print dest + " not changed." | |
132 | os.unlink(temp) | |
133 | ||
134 | #--------------------------------------------------------------------------- | |
135 | ||
136 | ||
137 | def GetAttr(node, name): | |
138 | path = "./attributelist/attribute[@name='%s']/@value" % name | |
139 | n = node.xpathEval2(path) | |
140 | if len(n): | |
141 | return n[0].content | |
142 | else: | |
143 | return None | |
144 | ||
145 | ||
146 | def processXML(xmlfile, modname, swigFile, pyFile): | |
147 | import libxml2 | |
148 | print "using libxml2..." | |
149 | ||
150 | topnode = libxml2.parseFile(xmlfile).children | |
151 | ||
152 | # remove any import nodes as we don't need to do renamers for symbols found therein | |
153 | imports = topnode.xpathEval2("*/import") | |
154 | for n in imports: | |
155 | n.unlinkNode() | |
156 | n.freeNode() | |
157 | ||
158 | # do a depth first iteration over what's left | |
159 | for node in topnode: | |
160 | doRename = False | |
161 | doPtr = False | |
162 | addWX = False | |
163 | revOnly = False | |
164 | ||
165 | ||
166 | if node.name == "class": | |
167 | lastClassName = name = GetAttr(node, "name") | |
168 | lastClassSymName = sym_name = GetAttr(node, "sym_name") | |
169 | doRename = True | |
170 | doPtr = True | |
171 | if sym_name != name: | |
172 | name = sym_name | |
173 | addWX = True | |
174 | ||
175 | # renamed constructors | |
176 | elif node.name == "constructor": | |
177 | name = GetAttr(node, "name") | |
178 | sym_name = GetAttr(node, "sym_name") | |
179 | if sym_name != name: | |
180 | name = sym_name | |
181 | addWX = True | |
182 | doRename = True | |
183 | ||
184 | # only enumitems at the top level | |
185 | elif node.name == "enumitem" and node.parent.parent.name == "include": | |
186 | name = GetAttr(node, "name") | |
187 | sym_name = GetAttr(node, "sym_name") | |
188 | doRename = True | |
189 | ||
190 | ||
191 | elif node.name in ["cdecl", "constant"]: | |
192 | name = GetAttr(node, "name") | |
193 | sym_name = GetAttr(node, "sym_name") | |
194 | toplevel = node.parent.name == "include" | |
195 | ||
196 | # top-level functions | |
197 | if toplevel and GetAttr(node, "view") == "globalfunctionHandler": | |
198 | doRename = True | |
199 | ||
200 | # top-level global vars | |
201 | elif toplevel and GetAttr(node, "feature_immutable") == "1": | |
202 | doRename = True | |
203 | ||
204 | # static methods | |
205 | elif GetAttr(node, "view") == "staticmemberfunctionHandler": | |
206 | name = lastClassName + '_' + name | |
207 | sym_name = lastClassSymName + '_' + sym_name | |
208 | # only output the reverse renamer in this case | |
209 | doRename = revOnly = True | |
210 | ||
211 | if doRename and name != sym_name: | |
212 | name = sym_name | |
213 | addWX = True | |
214 | ||
215 | ||
216 | if doRename and name: | |
217 | old = new = name | |
218 | if old.startswith('wx') and not old.startswith('wxEVT_'): | |
219 | # remove all wx prefixes except wxEVT_ and write a %rename directive for it | |
220 | new = old[2:] | |
221 | if not revOnly: | |
222 | swigFile.write("%%rename(%s) %35s;\n" % (new, old)) | |
223 | ||
224 | # Write assignments to import into the old wxPython namespace | |
225 | if addWX and not old.startswith('wx'): | |
226 | old = 'wx'+old | |
227 | pyFile.write("%s = wx.%s.%s\n" % (old, modname, new)) | |
228 | if doPtr: | |
229 | pyFile.write("%sPtr = wx.%s.%sPtr\n" % (old, modname, new)) | |
230 | ||
231 | ||
232 | #--------------------------------------------------------------------------- | |
233 | ||
234 | def checkOtherNames(pyFile, moduleName, filename): | |
235 | if os.path.exists(filename): | |
236 | prefixes = [] | |
237 | for line in file(filename): | |
238 | if line.endswith('\n'): | |
239 | line = line[:-1] | |
240 | if line and not line.startswith('#'): | |
241 | if line.endswith('*'): | |
242 | prefixes.append(line[:-1]) | |
243 | elif line.find('=') != -1: | |
244 | pyFile.write("%s\n" % line) | |
245 | else: | |
246 | wxname = 'wx' + line | |
247 | if line.startswith('wx') or line.startswith('WX') or line.startswith('EVT'): | |
248 | wxname = line | |
249 | pyFile.write("%s = wx.%s.%s\n" % (wxname, moduleName, line)) | |
250 | ||
251 | if prefixes: | |
252 | pyFile.write( | |
253 | "\n\nd = globals()\nfor k, v in wx.%s.__dict__.iteritems():" | |
254 | % moduleName) | |
255 | first = True | |
256 | for p in prefixes: | |
257 | if first: | |
258 | pyFile.write("\n if ") | |
259 | first = False | |
260 | else: | |
261 | pyFile.write("\n elif ") | |
262 | pyFile.write("k.startswith('%s'):\n d[k] = v" % p) | |
263 | pyFile.write("\ndel d, k, v\n\n") | |
264 | ||
265 | ||
266 | #--------------------------------------------------------------------------- | |
267 | ||
268 | interestingTypes = [ 'class', 'cdecl', 'enumitem', 'constructor', 'constant' ] | |
269 | interestingAttrs = [ 'name', 'sym_name', 'decl', 'feature_immutable', 'module', | |
270 | 'storage', 'type' ] | |
271 | ||
272 | ||
273 | class Element: | |
274 | def __init__(self, tagtype): | |
275 | self.tagtype = tagtype | |
276 | self.level = -1 | |
277 | self.name = None | |
278 | self.sym_name = None | |
279 | self.decl = None | |
280 | self.immutable = None | |
281 | self.klass = None | |
282 | self.module = None | |
283 | self.storage = None | |
284 | self.type = None | |
285 | self.startLine = -1 | |
286 | ||
287 | ||
288 | def write(self, moduleName, swigFile, pyFile): | |
289 | doRename = False | |
290 | doPtr = False | |
291 | addWX = False | |
292 | revOnly = False | |
293 | ||
294 | #if self.name.find('DefaultPosition') != -1: | |
295 | # pprint.pprint(self.__dict__) | |
296 | ||
297 | if self.tagtype in ['cdecl', 'constant']: | |
298 | if self.storage == 'typedef': | |
299 | pass | |
300 | ||
301 | # top level functions | |
302 | elif self.level == 0 and self.decl != "": | |
303 | doRename = True | |
304 | ||
305 | # top level global vars | |
306 | elif self.level == 0 and self.immutable == '1': | |
307 | doRename = True | |
308 | ||
309 | # static methods | |
310 | elif self.storage == 'static': | |
311 | if not self.klass: | |
312 | pprint.pprint(self.__dict__) | |
313 | else: | |
314 | self.name = self.klass + '_' + self.name | |
315 | self.sym_name = self.sym_klass + '_' + self.sym_name | |
316 | # only output the reverse renamer in this case | |
317 | doRename = revOnly = True | |
318 | ||
319 | ||
320 | ||
321 | if doRename and self.name != self.sym_name: | |
322 | #print "%-25s %-25s" % (self.name, self.sym_name) | |
323 | self.name = self.sym_name | |
324 | addWX = True | |
325 | ||
326 | ||
327 | elif self.tagtype == 'class' and self.module == moduleName: | |
328 | doRename = True | |
329 | doPtr = True | |
330 | if self.sym_name != self.klass: | |
331 | #print self.sym_name | |
332 | self.name = self.sym_name | |
333 | addWX = True | |
334 | ||
335 | elif self.tagtype == 'constructor': | |
336 | #print "%-25s %-25s" % (self.name, self.sym_name) | |
337 | if self.sym_name != self.klass: | |
338 | #print self.sym_name | |
339 | self.name = self.sym_name | |
340 | addWX = True | |
341 | doRename = True | |
342 | ||
343 | elif self.tagtype == 'enumitem' and self.level == 0: | |
344 | doRename = True | |
345 | ||
346 | ||
347 | if doRename: | |
348 | #print "%-25s %-25s" % (self.name, self.sym_name) | |
349 | old = new = self.name | |
350 | if old.startswith('wx') and not old.startswith('wxEVT_'): | |
351 | # remove all wx prefixes except wxEVT_ and write a %rename directive for it | |
352 | new = old[2:] | |
353 | if not revOnly: | |
354 | swigFile.write("%%rename(%s) %35s;\n" % (new, old)) | |
355 | ||
356 | # Write assignments to import into the old wxPython namespace | |
357 | if addWX and not old.startswith('wx'): | |
358 | old = 'wx'+old | |
359 | pyFile.write("%s = wx.%s.%s\n" % (old, moduleName, new)) | |
360 | if doPtr: | |
361 | pyFile.write("%sPtr = wx.%s.%sPtr\n" % (old, moduleName, new)) | |
362 | ||
363 | ||
364 | ||
365 | #else: | |
366 | # text = "%07d %d %10s %-35s %s\n" % ( | |
367 | # self.startLine, self.level, self.tagtype, self.name, self.decl) | |
368 | # #rejects.write(text) | |
369 | # print text, | |
370 | ||
371 | ||
372 | #--------------------------------------------------------------------------- | |
373 | ||
374 | class ContentHandler(xml.sax.ContentHandler): | |
375 | def __init__(self, modname, swigFile, pyFile): | |
376 | xml.sax.ContentHandler.__init__(self) | |
377 | self.modname = modname | |
378 | self.swigFile = swigFile | |
379 | self.pyFile = pyFile | |
380 | self.elements = [] | |
381 | self.imports = 0 | |
382 | self.klass = None | |
383 | self.sym_klass = None | |
384 | ||
385 | ||
386 | def setDocumentLocator(self, locator): | |
387 | self.locator = locator | |
388 | ||
389 | ||
390 | ||
391 | def startElement(self, name, attrs): | |
392 | if name in interestingTypes: | |
393 | # start of a new element that we are interested in | |
394 | ce = Element(name) | |
395 | ce.startLine = self.locator.getLineNumber() | |
396 | ce.level = len(self.elements) | |
397 | if name == 'constructor': | |
398 | ce.klass = self.elements[0].name | |
399 | else: | |
400 | ce.klass = self.klass | |
401 | ce.sym_klass = self.sym_klass | |
402 | self.elements.insert(0, ce) | |
403 | ||
404 | ||
405 | elif len(self.elements) and name == 'attribute' and attrs['name'] in interestingAttrs: | |
406 | attrName = attrs['name'] | |
407 | attrVal = attrs['value'] | |
408 | if attrName.startswith('feature_'): | |
409 | attrName = attrName.replace('feature_', '') | |
410 | ce = self.elements[0] | |
411 | if getattr(ce, attrName) is None: | |
412 | setattr(ce, attrName, attrVal) | |
413 | if ce.tagtype == 'class' and attrName == 'name' and self.klass is None: | |
414 | self.klass = attrVal | |
415 | if ce.tagtype == 'class' and attrName == 'sym_name' and self.sym_klass is None: | |
416 | self.sym_klass = attrVal | |
417 | ||
418 | ||
419 | ## elif len(self.elements) and name == 'attribute' and attrs['name'] == 'name': | |
420 | ## # save the elements name | |
421 | ## ce = self.elements[0] | |
422 | ## if ce.name is None: | |
423 | ## ce.name = attrs['value'] | |
424 | ## ce.nameLine = self.locator.getLineNumber() | |
425 | ||
426 | ## elif len(self.elements) and name == 'attribute' and attrs['name'] == 'sym_name': | |
427 | ## # save the elements name | |
428 | ## ce = self.elements[0] | |
429 | ## if ce.sym_name is None: | |
430 | ## ce.sym_name = attrs['value'] | |
431 | ||
432 | ## elif len(self.elements) and name == 'attribute' and attrs['name'] == 'decl': | |
433 | ## # save the elements decl | |
434 | ## ce = self.elements[0] | |
435 | ## ce.decl = attrs['value'] | |
436 | ||
437 | ## elif len(self.elements) and name == 'attribute' and attrs['name'] == 'feature_immutable': | |
438 | ## # save the elements decl | |
439 | ## ce = self.elements[0] | |
440 | ## ce.immutable = int(attrs['value']) | |
441 | ||
442 | ## elif len(self.elements) and name == 'attribute' and attrs['name'] == 'module': | |
443 | ## # save the elements decl | |
444 | ## ce = self.elements[0] | |
445 | ## ce.module = attrs['value'] | |
446 | ||
447 | elif name == 'import': | |
448 | self.imports += 1 | |
449 | ||
450 | ## elif len(self.elements) and name == 'attribute' and attrs['name'] == 'storage': | |
451 | ## # save the elements decl | |
452 | ## ce = self.elements[0] | |
453 | ## ce.storage = attrs['value'] | |
454 | ||
455 | ## elif len(self.elements) and name == 'attribute' and attrs['name'] == 'type': | |
456 | ## # save the elements decl | |
457 | ## ce = self.elements[0] | |
458 | ## ce.type = attrs['value'] | |
459 | ||
460 | ||
461 | def endElement(self, name): | |
462 | if name in interestingTypes: | |
463 | # end of an element that we are interested in | |
464 | ce = self.elements.pop(0) | |
465 | ||
466 | if self.imports == 0: | |
467 | # only write for items that are in this file, not imported | |
468 | ce.write(self.modname, self.swigFile, self.pyFile) | |
469 | ||
470 | if name == 'import': | |
471 | self.imports -= 1 | |
472 | ||
473 | if name == 'class': | |
474 | self.klass = None | |
475 | self.sym_klass = None | |
476 | ||
477 | ||
478 | #--------------------------------------------------------------------------- | |
479 | ||
480 | if __name__ == "__main__": | |
481 | main(sys.argv[1:]) | |
482 |