]>
Commit | Line | Data |
---|---|---|
d14a1e28 RD |
1 | #!/usr/bin/env python |
2 | #--------------------------------------------------------------------------- | |
3 | """ | |
8994c3c2 | 4 | Usage: build_renamers.py destdir modulename filename.xml |
d14a1e28 | 5 | |
8994c3c2 RD |
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. | |
d14a1e28 RD |
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 | |
8994c3c2 | 17 | import xml.sax |
d14a1e28 RD |
18 | from distutils.spawn import spawn |
19 | ||
8994c3c2 RD |
20 | try: |
21 | import libxml2 | |
22 | USE_LIBXML2 = True | |
23 | except ImportError: | |
24 | USE_LIBXML2 = False | |
d14a1e28 RD |
25 | |
26 | wxPythonDir = "wxPython" | |
d14a1e28 | 27 | |
8994c3c2 | 28 | #--------------------------------------------------------------------------- |
d14a1e28 RD |
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 | ||
8994c3c2 | 35 | #ifndef BUILDING_RENAMERS |
d14a1e28 RD |
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 | |
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 | ||
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 | |
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 | ||
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 | |
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 | ||
372 | class 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 | ||
478 | if __name__ == "__main__": | |
479 | main(sys.argv[1:]) | |
480 |