]> git.saurik.com Git - wxWidgets.git/blob - wxPython/distrib/mac/uninstall_wxPython.py
fixed wxVsnprintf() to write as much as it can if the output buffer is too short
[wxWidgets.git] / wxPython / distrib / mac / uninstall_wxPython.py
1 #!/usr/bin/env python
2 """
3 This script will search for installed versions of wxPython on OSX and
4 allow the user to choose one to uninstall. It then will use the
5 metadata stored about the installed package to remove all the files
6 associated with that install.
7
8 Only the files installed by the main Installer Package will be
9 removed. This includes the Python modules and the wxWidgets shared
10 libraries. If you also installed the demo or docs by dragging them out
11 of the disk image, then you will need to drag them to the Trash
12 yourself.
13 """
14
15 import sys, os, glob
16 from fnmatch import fnmatchcase
17 import cPickle, urllib
18
19 RCPTDIR = "/Library/Receipts"
20 RSRCDIR = "Contents/Resources"
21
22 # Only completly clean out dirs that have one of these as a prefix.
23 # We do this because the file list returned from lsbom will include /,
24 # /usr, /usr/local, etc.
25 PREFIXES = [ '/Library/Python/2.3/',
26 '/Library/Python/2.4/',
27 '/Library/Python/2.5/',
28 '/Library/Frameworks/Python.framework/Versions/2.3/lib/python2.3/site-packages/',
29 '/Library/Frameworks/Python.framework/Versions/2.4/lib/python2.4/site-packages/',
30 '/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/site-packages/',
31 '/usr/local/lib/',
32 ]
33
34 # The files that match one of the items in this list will only be
35 # removed if the last installation of wxPython on the system is being
36 # uninstalled.
37 COMMON_FILES = [ '/usr/local/bin/*',
38 'wx.pth',
39 'wxversion.py',
40 ]
41
42
43
44 class AccessError(Exception):
45 pass
46
47 class ReceiptError(Exception):
48 pass
49
50
51 class InstalledReceipt(object):
52 def __init__(self, rcptPath):
53 self.rcptPath = rcptPath
54 self.rsrcPath = os.path.join(rcptPath, RSRCDIR)
55 bf = glob.glob(os.path.join(self.rsrcPath, "*.bom"))
56 if bf:
57 self.bomFile = bf[0]
58 else:
59 print "WARNING: Unable to find %s/*.bom" % self.rsrcPath
60 raise ReceiptError
61 self.findMetaData()
62
63
64 def findMetaData(self):
65 # TODO: Make this be able to also look at Info.plist files
66 infoFiles = glob.glob(os.path.join(self.rsrcPath, "*.info"))
67 if infoFiles:
68 # there should be only one
69 infoFile = infoFiles[0]
70 self.mdata = {}
71 for line in open(infoFile, "r").readlines():
72 line = line.strip()
73 if line and line[0] != '#':
74 ls = line.split()
75 self.mdata[ls[0]] = line[len(ls[0])+1:]
76 else:
77 print "WARNING: Unable to find %s/*.info" % self.rsrcPath
78 raise ReceiptError
79
80
81 def getFileList(self):
82 p = os.popen("lsbom -s %s" % self.bomFile, "r")
83 data = p.read()
84 data.strip()
85 data = filter(lambda s: s!='' and s!='.', data.split('\n'))
86 loc = self.mdata['DefaultLocation']
87 return [loc+item for item in data]
88
89
90 def walkFiles(self, handleFile, handleDir):
91 dirs = []
92 names = self.getFileList()
93
94 # the plain files
95 for name in names:
96 name = os.path.abspath(name)
97 if os.path.isdir(name):
98 dirs.append(name)
99 else:
100 handleFile(name)
101
102 # the directories
103 dirs.reverse()
104 for dir in dirs:
105 for prefix in PREFIXES:
106 if dir.startswith(prefix):
107 handleDir(dir)
108 break
109
110 # Finally, remove the Receipts package, bottom-up
111 for dirpath, dirname, filenames in os.walk(self.rcptPath, False):
112 for name in filenames:
113 name = os.path.join(dirpath, name)
114 handleFile(name)
115 handleDir(dirpath)
116
117 # wxaddons should be always kept as the user may have installed
118 # third-party modules seperate from wxpython.
119 def testWxaddons(self, name):
120 for prefix in PREFIXES:
121 if name.startswith(prefix + "wxaddons"):
122 return True
123 return False
124
125 def testCommon(self, name):
126 for cmn in COMMON_FILES:
127 if fnmatchcase(name, cmn) or fnmatchcase(os.path.basename(name), cmn):
128 return True
129 return False
130
131
132 def showFiles(self):
133 def show(name):
134 if os.path.exists(name):
135 if not self.lastInstall and self.testCommon(name):
136 return
137 if self.testWxaddons(name):
138 return
139 print "Will remove:", name
140 self.walkFiles(show, show)
141
142
143 def testUninstallAccess(self):
144 def testFile(name):
145 if os.path.exists(name):
146 if not self.lastInstall and self.testCommon(name):
147 return
148 if not os.access(name, os.W_OK):
149 raise AccessError(name)
150 self.walkFiles(testFile, testFile)
151
152
153 def doUninstall(self):
154 def removeFile(name):
155 if os.path.exists(name):
156 if not self.lastInstall and self.testCommon(name):
157 return
158 if self.testWxaddons(name):
159 return
160 print "Removing:", name
161 os.unlink(name)
162 def removeDir(name):
163 print "Removing:", name
164 if os.path.exists(name) and not self.testWxaddons(name):
165 hasFiles = os.listdir(name)
166 if hasFiles: # perhaps some stale symlinks, or .pyc files
167 for file in hasFiles:
168 os.unlink(os.path.join(name, file))
169 os.rmdir(name)
170
171 try:
172 self.testUninstallAccess()
173 except AccessError, e:
174 print "UNABLE TO UNINSTALL!\nNo permission to remove: ", e.args[0]
175 sys.exit()
176
177 self.walkFiles(removeFile, removeDir)
178
179
180
181
182 def findInstalled():
183 installed = []
184 for name in glob.glob(os.path.join(RCPTDIR, "wxPython*")):
185 try:
186 ir = InstalledReceipt(name)
187 installed.append(ir)
188 except ReceiptError:
189 pass # just skip it...
190
191 return installed
192
193
194 # Just in case a Python < 2.3 is used to run this
195 try:
196 enumerate
197 except NameError:
198 def enumerate(sequence):
199 return zip(range(len(sequence)), sequence)
200
201
202 def main():
203 if len(sys.argv) > 1 and sys.argv[1] == "-doit":
204 inst = cPickle.loads(urllib.unquote(sys.argv[2]))
205 inst.doUninstall()
206 sys.exit()
207
208 print __doc__
209 installed = findInstalled()
210
211 if not installed:
212 print "*** No wxPython installations found! ***"
213 raw_input("Press RETURN...")
214 sys.exit()
215
216 for i, inst in enumerate(installed):
217 print " %2d. %-40s %s" % (i+1, inst.mdata["Title"], inst.mdata["Version"])
218
219 print
220 ans = raw_input("Enter the number of the install to examine or 'Q' to quit: ")
221 if ans in ['Q', 'q']:
222 sys.exit()
223 inst = installed[int(ans) - 1]
224 inst.lastInstall = len(installed) == 1
225
226 while True:
227 print
228 print """
229 Title: %(Title)s
230 Version: %(Version)s
231 Description: %(Description)s
232 """ % inst.mdata
233
234 ans = raw_input("(U)ninstall, (S)how what will be removed, or (Q)uit? [u,s,q] ")
235 if ans in ['Q', 'q']:
236 sys.exit()
237
238 elif ans in ['S', 's']:
239 inst.showFiles()
240
241 elif ans in ['U', 'u']:
242 print
243 print "Launching uninstaller with sudo, please enter your password if prompted:"
244 os.system("sudo %s -doit %s" %
245 (sys.argv[0],
246 urllib.quote(cPickle.dumps(inst))))
247 sys.exit()
248
249
250 if __name__ == '__main__':
251 main()