]>
Commit | Line | Data |
---|---|---|
d14a1e28 RD |
1 | #!/usr/bin/env python |
2 | #--------------------------------------------------------------------------- | |
3 | """ | |
4 | Usage: build_renamers.py filename.i | |
5 | ||
6 | Run SWIG on file.i using the XML language module and then scan the XML | |
7 | file produced and generate the %rename directives needed to implement | |
8 | the new wx namespace. The rename directives are output in a file | |
9 | named _filename_rename.i in the same dir as filename.i. | |
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 | ||
21 | ||
22 | #--------------------------------------------------------------------------- | |
23 | ||
24 | DO_UNLINK = True | |
25 | ||
26 | wxPythonDir = "wxPython" | |
27 | swig_cmd = "/opt/swig/bin/swig" | |
28 | if os.name == 'nt': | |
29 | swig_cmd = 'e:/projects/SWIG-cvs/swig.exe' | |
30 | ||
31 | swig_args = ['-c++', | |
32 | '-Wall', | |
33 | '-nodefault', | |
34 | ||
35 | '-xml', | |
36 | '-xmllite', | |
37 | ||
38 | '-I./src', | |
39 | '-c' | |
40 | ] | |
41 | ||
42 | ||
43 | renamerTemplateStart = """\ | |
44 | // A bunch of %%rename directives generated by %s | |
45 | // in order to remove the wx prefix from all global scope names. | |
46 | ||
47 | #ifndef SWIGXML | |
48 | ||
49 | """ | |
50 | ||
51 | renamerTemplateEnd = """ | |
52 | #endif | |
53 | """ | |
54 | ||
55 | wxPythonTemplateStart = """\ | |
56 | ## This file reverse renames symbols in the wx package to give | |
57 | ## them their wx prefix again, for backwards compatibility. | |
58 | ## | |
59 | ## Generated by %s | |
60 | ||
61 | # This silly stuff here is so the wxPython.wx module doesn't conflict | |
62 | # with the wx package. We need to import modules from the wx package | |
63 | # here, then we'll put the wxPython.wx entry back in sys.modules. | |
64 | import sys | |
65 | _wx = None | |
66 | if sys.modules.has_key('wxPython.wx'): | |
67 | _wx = sys.modules['wxPython.wx'] | |
68 | del sys.modules['wxPython.wx'] | |
69 | ||
70 | import wx.%s | |
71 | ||
72 | sys.modules['wxPython.wx'] = _wx | |
73 | del sys, _wx | |
74 | ||
75 | ||
76 | # Now assign all the reverse-renamed names: | |
77 | """ | |
78 | ||
79 | wxPythonTemplateEnd = """ | |
80 | ||
81 | """ | |
82 | ||
83 | ||
84 | ||
85 | #--------------------------------------------------------------------------- | |
86 | ||
87 | def main(args): | |
88 | # check args | |
89 | if len(args) < 1: | |
90 | print __doc__ | |
91 | sys.exit(1) | |
92 | ||
93 | # check location (there should be a wxPython subdir) | |
94 | if not os.path.exists(wxPythonDir) or not os.path.isdir(wxPythonDir): | |
95 | print __doc__ | |
96 | print "You should only run this script from the main wxPython source dir.\n" | |
97 | sys.exit(1) | |
98 | ||
99 | ||
100 | source = args[0] | |
101 | sourcePath, sourceBase = os.path.split(source) | |
102 | sourceBase = os.path.splitext(sourceBase)[0] | |
103 | ||
104 | tempfile.tempdir = sourcePath | |
105 | xmlDest = tempfile.mktemp('.xml') | |
106 | swigDest = os.path.join(sourcePath, "_"+sourceBase+"_rename.i") | |
107 | pyDest = os.path.join(wxPythonDir, sourceBase + '.py') | |
108 | ||
109 | #print "source: ", source | |
110 | #print "xmlDest: ", xmlDest | |
111 | #print "swigDest: ", swigDest | |
112 | #print "pyDest: ", pyDest | |
113 | ||
114 | cmd = [ swig_cmd ] + swig_args + args[1:] + ['-I'+sourcePath, '-o', xmlDest, source] | |
115 | print ' '.join(cmd) | |
116 | spawn(cmd) | |
117 | ||
118 | swigDestTemp = tempfile.mktemp('.tmp') | |
119 | swigFile = open(swigDestTemp, "w") | |
120 | swigFile.write(renamerTemplateStart % sys.argv[0]) | |
121 | ||
122 | pyFile = open(pyDest, "w") | |
123 | pyFile.write(wxPythonTemplateStart % (sys.argv[0], sourceBase)) | |
124 | ||
125 | print "Parsing and building renamers", | |
126 | try: | |
127 | ## try: | |
128 | ## import libxml2 | |
129 | ## print "using libxml2..." | |
130 | ## ctxt = libxml2.createPushParser(ContentHandler(source, sourceBase, swigFile, pyFile), | |
131 | ## '', 0, xmlDest) | |
132 | ## for line in file(xmlDest): | |
133 | ## if not line: | |
134 | ## ctxt.parseChunck('', 0, 1) | |
135 | ## break | |
136 | ## ctxt.parseChunk(line, len(line), 0) | |
137 | ||
138 | ## except ImportError: | |
139 | print "using xml.sax..." | |
140 | xml.sax.parse(xmlDest, ContentHandler(source, sourceBase, swigFile, pyFile)) | |
141 | ||
142 | finally: | |
143 | checkOtherNames(pyFile, sourceBase, | |
144 | os.path.join(sourcePath, '_'+sourceBase+'_reverse.txt')) | |
145 | pyFile.write(wxPythonTemplateEnd) | |
146 | pyFile.close() | |
147 | ||
148 | swigFile.write(renamerTemplateEnd) | |
149 | swigFile.close() | |
150 | ||
151 | # Compare the file just created with the existing one and | |
152 | # blow away the old one if they are different. | |
153 | if open(swigDest).read() != open(swigDestTemp).read(): | |
154 | os.unlink(swigDest) | |
155 | os.rename(swigDestTemp, swigDest) | |
156 | else: | |
157 | print swigDest + " not changed." | |
158 | os.unlink(swigDestTemp) | |
159 | ||
160 | if DO_UNLINK: | |
161 | os.unlink(xmlDest) | |
162 | ||
163 | ||
164 | #--------------------------------------------------------------------------- | |
165 | ||
166 | def checkOtherNames(pyFile, moduleName, filename): | |
167 | if os.path.exists(filename): | |
168 | prefixes = [] | |
169 | for line in file(filename): | |
170 | if line.endswith('\n'): | |
171 | line = line[:-1] | |
172 | if line and not line.startswith('#'): | |
173 | if line.endswith('*'): | |
174 | prefixes.append(line[:-1]) | |
175 | elif line.find('=') != -1: | |
176 | pyFile.write("%s\n" % line) | |
177 | else: | |
178 | wxname = 'wx' + line | |
179 | if line.startswith('wx') or line.startswith('WX') or line.startswith('EVT'): | |
180 | wxname = line | |
181 | pyFile.write("%s = wx.%s.%s\n" % (wxname, moduleName, line)) | |
182 | ||
183 | if prefixes: | |
184 | pyFile.write( | |
185 | "\n\nd = globals()\nfor k, v in wx.%s.__dict__.iteritems():" | |
186 | % moduleName) | |
187 | first = True | |
188 | for p in prefixes: | |
189 | if first: | |
190 | pyFile.write("\n if ") | |
191 | first = False | |
192 | else: | |
193 | pyFile.write("\n elif ") | |
194 | pyFile.write("k.startswith('%s'):\n d[k] = v" % p) | |
195 | pyFile.write("\ndel d, k, v\n\n") | |
196 | ||
197 | ||
198 | #--------------------------------------------------------------------------- | |
199 | ||
200 | interestingTypes = [ 'class', 'cdecl', 'enumitem', 'constructor', 'constant' ] | |
201 | interestingAttrs = [ 'name', 'sym_name', 'decl', 'feature_immutable', 'module', | |
202 | 'storage', 'type' ] | |
203 | ||
204 | ||
205 | class Element: | |
206 | def __init__(self, tagtype): | |
207 | self.tagtype = tagtype | |
208 | self.level = -1 | |
209 | self.name = None | |
210 | self.sym_name = None | |
211 | self.decl = None | |
212 | self.immutable = None | |
213 | self.klass = None | |
214 | self.module = None | |
215 | self.storage = None | |
216 | self.type = None | |
217 | self.startLine = -1 | |
218 | ||
219 | ||
220 | def write(self, moduleName, swigFile, pyFile): | |
221 | doRename = False | |
222 | addWX = False | |
223 | revOnly = False | |
224 | ||
225 | #if self.name.find('DefaultPosition') != -1: | |
226 | # pprint.pprint(self.__dict__) | |
227 | ||
228 | if self.tagtype in ['cdecl', 'constant']: | |
229 | if self.storage == 'typedef': | |
230 | pass | |
231 | ||
232 | # top level functions | |
233 | elif self.level == 0 and self.decl != "": | |
234 | doRename = True | |
235 | ||
236 | # top level global vars | |
237 | elif self.level == 0 and self.immutable == '1': | |
238 | doRename = True | |
239 | ||
240 | # static methods | |
241 | elif self.storage == 'static': | |
242 | if not self.klass: | |
243 | pprint.pprint(self.__dict__) | |
244 | else: | |
245 | self.name = self.klass + '_' + self.name | |
246 | self.sym_name = self.sym_klass + '_' + self.sym_name | |
247 | # only output the reverse renamer in this case | |
248 | doRename = revOnly = True | |
249 | ||
250 | ||
251 | ||
252 | if doRename and self.name != self.sym_name: | |
253 | #print "%-25s %-25s" % (self.name, self.sym_name) | |
254 | self.name = self.sym_name | |
255 | addWX = True | |
256 | ||
257 | ||
258 | elif self.tagtype == 'class' and self.module == moduleName: | |
259 | doRename = True | |
260 | if self.sym_name != self.klass: | |
261 | #print self.sym_name | |
262 | self.name = self.sym_name | |
263 | addWX = True | |
264 | ||
265 | elif self.tagtype == 'constructor': | |
266 | #print "%-25s %-25s" % (self.name, self.sym_name) | |
267 | if self.sym_name != self.klass: | |
268 | #print self.sym_name | |
269 | self.name = self.sym_name | |
270 | addWX = True | |
271 | doRename = True | |
272 | ||
273 | elif self.tagtype == 'enumitem' and self.level == 0: | |
274 | doRename = True | |
275 | ||
276 | ||
277 | if doRename: | |
278 | #print "%-25s %-25s" % (self.name, self.sym_name) | |
279 | old = new = self.name | |
280 | if old.startswith('wx') and not old.startswith('wxEVT_'): | |
281 | # remove all wx prefixes except wxEVT_ and write a %rename directive for it | |
282 | new = old[2:] | |
283 | if not revOnly: | |
284 | swigFile.write("%%rename(%s) %35s;\n" % (new, old)) | |
285 | ||
286 | # Write assignments to import into the old wxPython namespace | |
287 | if addWX and not old.startswith('wx'): | |
288 | old = 'wx'+old | |
289 | pyFile.write("%s = wx.%s.%s\n" % (old, moduleName, new)) | |
290 | ||
291 | ||
292 | #else: | |
293 | # text = "%07d %d %10s %-35s %s\n" % ( | |
294 | # self.startLine, self.level, self.tagtype, self.name, self.decl) | |
295 | # #rejects.write(text) | |
296 | # print text, | |
297 | ||
298 | ||
299 | #--------------------------------------------------------------------------- | |
300 | ||
301 | class ContentHandler(xml.sax.ContentHandler): | |
302 | def __init__(self, source, sourceBase, swigFile, pyFile): | |
303 | xml.sax.ContentHandler.__init__(self) | |
304 | self.source = source | |
305 | self.sourceBase = sourceBase | |
306 | self.swigFile = swigFile | |
307 | self.pyFile = pyFile | |
308 | self.elements = [] | |
309 | self.imports = 0 | |
310 | self.klass = None | |
311 | self.sym_klass = None | |
312 | ||
313 | ||
314 | def setDocumentLocator(self, locator): | |
315 | self.locator = locator | |
316 | ||
317 | ||
318 | ||
319 | def startElement(self, name, attrs): | |
320 | if name in interestingTypes: | |
321 | # start of a new element that we are interested in | |
322 | ce = Element(name) | |
323 | ce.startLine = self.locator.getLineNumber() | |
324 | ce.level = len(self.elements) | |
325 | if name == 'constructor': | |
326 | ce.klass = self.elements[0].name | |
327 | else: | |
328 | ce.klass = self.klass | |
329 | ce.sym_klass = self.sym_klass | |
330 | self.elements.insert(0, ce) | |
331 | ||
332 | ||
333 | elif len(self.elements) and name == 'attribute' and attrs['name'] in interestingAttrs: | |
334 | attrName = attrs['name'] | |
335 | attrVal = attrs['value'] | |
336 | if attrName.startswith('feature_'): | |
337 | attrName = attrName.replace('feature_', '') | |
338 | ce = self.elements[0] | |
339 | if getattr(ce, attrName) is None: | |
340 | setattr(ce, attrName, attrVal) | |
341 | if ce.tagtype == 'class' and attrName == 'name' and self.klass is None: | |
342 | self.klass = attrVal | |
343 | if ce.tagtype == 'class' and attrName == 'sym_name' and self.sym_klass is None: | |
344 | self.sym_klass = attrVal | |
345 | ||
346 | ||
347 | ## elif len(self.elements) and name == 'attribute' and attrs['name'] == 'name': | |
348 | ## # save the elements name | |
349 | ## ce = self.elements[0] | |
350 | ## if ce.name is None: | |
351 | ## ce.name = attrs['value'] | |
352 | ## ce.nameLine = self.locator.getLineNumber() | |
353 | ||
354 | ## elif len(self.elements) and name == 'attribute' and attrs['name'] == 'sym_name': | |
355 | ## # save the elements name | |
356 | ## ce = self.elements[0] | |
357 | ## if ce.sym_name is None: | |
358 | ## ce.sym_name = attrs['value'] | |
359 | ||
360 | ## elif len(self.elements) and name == 'attribute' and attrs['name'] == 'decl': | |
361 | ## # save the elements decl | |
362 | ## ce = self.elements[0] | |
363 | ## ce.decl = attrs['value'] | |
364 | ||
365 | ## elif len(self.elements) and name == 'attribute' and attrs['name'] == 'feature_immutable': | |
366 | ## # save the elements decl | |
367 | ## ce = self.elements[0] | |
368 | ## ce.immutable = int(attrs['value']) | |
369 | ||
370 | ## elif len(self.elements) and name == 'attribute' and attrs['name'] == 'module': | |
371 | ## # save the elements decl | |
372 | ## ce = self.elements[0] | |
373 | ## ce.module = attrs['value'] | |
374 | ||
375 | elif name == 'import': | |
376 | self.imports += 1 | |
377 | ||
378 | ## elif len(self.elements) and name == 'attribute' and attrs['name'] == 'storage': | |
379 | ## # save the elements decl | |
380 | ## ce = self.elements[0] | |
381 | ## ce.storage = attrs['value'] | |
382 | ||
383 | ## elif len(self.elements) and name == 'attribute' and attrs['name'] == 'type': | |
384 | ## # save the elements decl | |
385 | ## ce = self.elements[0] | |
386 | ## ce.type = attrs['value'] | |
387 | ||
388 | ||
389 | def endElement(self, name): | |
390 | if name in interestingTypes: | |
391 | # end of an element that we are interested in | |
392 | ce = self.elements.pop(0) | |
393 | ||
394 | if self.imports == 0: | |
395 | # only write for items that are in this file, not imported | |
396 | ce.write(self.sourceBase, self.swigFile, self.pyFile) | |
397 | ||
398 | if name == 'import': | |
399 | self.imports -= 1 | |
400 | ||
401 | if name == 'class': | |
402 | self.klass = None | |
403 | self.sym_klass = None | |
404 | ||
405 | ||
406 | #--------------------------------------------------------------------------- | |
407 | ||
408 | if __name__ == "__main__": | |
409 | main(sys.argv[1:]) | |
410 |