]> git.saurik.com Git - wxWidgets.git/blob - wxPython/distrib/mac/uninstall_wxPython.py
Combine the common installer package into the main package
[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/Frameworks/Python.framework/Versions/2.3/lib/python2.3/site-pacakges/',
28 '/Library/Frameworks/Python.framework/Versions/2.4/lib/python2.4/site-pacakges/',
29 '/usr/local/lib/',
30 ]
31
32 # The files that match one of the items in this list will only be
33 # removed if the last installation of wxPython on the system is being
34 # uninstalled.
35 COMMON_FILES = [ '/usr/local/bin/*',
36 'wx.pth',
37 'wxversion.py',
38 ]
39
40
41
42 class AccessError(Exception):
43 pass
44
45
46 class InstalledReceipt(object):
47 def __init__(self, rcptPath):
48 self.rcptPath = rcptPath
49 self.rsrcPath = os.path.join(rcptPath, RSRCDIR)
50 self.bomFile = glob.glob(os.path.join(self.rsrcPath, "*.bom"))[0]
51 self.findMetaData()
52
53
54 def findMetaData(self):
55 # TODO: Make this be able to also look at Info.plist files
56 infoFile = glob.glob(os.path.join(self.rsrcPath, "*.info"))[0]
57 self.mdata = {}
58 for line in open(infoFile, "r").readlines():
59 line = line.strip()
60 if line and line[0] != '#':
61 ls = line.split()
62 self.mdata[ls[0]] = line[len(ls[0])+1:]
63
64
65 def getFileList(self):
66 p = os.popen("lsbom -s %s" % self.bomFile, "r")
67 data = p.read()
68 data.strip()
69 data = filter(lambda s: s!='' and s!='.', data.split('\n'))
70 loc = self.mdata['DefaultLocation']
71 return [loc+item for item in data]
72
73
74 def walkFiles(self, handleFile, handleDir):
75 dirs = []
76 names = self.getFileList()
77
78 # the plain files
79 for name in names:
80 name = os.path.abspath(name)
81 if os.path.isdir(name):
82 dirs.append(name)
83 else:
84 handleFile(name)
85
86 # the directories
87 dirs.reverse()
88 for dir in dirs:
89 for prefix in PREFIXES:
90 if dir.startswith(prefix):
91 handleDir(dir)
92 break
93
94 # Finally, remove the Receipts package, bottom-up
95 for dirpath, dirname, filenames in os.walk(self.rcptPath, False):
96 for name in filenames:
97 name = os.path.join(dirpath, name)
98 handleFile(name)
99 handleDir(dirpath)
100
101
102 def testCommon(self, name):
103 for cmn in COMMON_FILES:
104 if fnmatchcase(name, cmn) or fnmatchcase(os.path.basename(name), cmn):
105 return True
106 return False
107
108
109 def showFiles(self):
110 def show(name):
111 if os.path.exists(name):
112 if not self.lastInstall and self.testCommon(name):
113 return
114 print "Will remove:", name
115 self.walkFiles(show, show)
116
117
118 def testUninstallAccess(self):
119 def testFile(name):
120 if os.path.exists(name):
121 if not self.lastInstall and self.testCommon(name):
122 return
123 if not os.access(name, os.W_OK):
124 raise AccessError(name)
125 self.walkFiles(testFile, testFile)
126
127
128 def doUninstall(self):
129 def removeFile(name):
130 if os.path.exists(name):
131 if not self.lastInstall and self.testCommon(name):
132 return
133 print "Removing:", name
134 os.unlink(name)
135 def removeDir(name):
136 print "Removing:", name
137 if os.path.exists(name):
138 hasFiles = os.listdir(name)
139 if hasFiles: # perhaps some stale symlinks, or .pyc files
140 for file in hasFiles:
141 os.unlink(os.path.join(name, file))
142 os.rmdir(name)
143
144 try:
145 self.testUninstallAccess()
146 except AccessError, e:
147 print "UNABLE TO UNINSTALL!\nNo permission to remove: ", e.args[0]
148 sys.exit()
149
150 self.walkFiles(removeFile, removeDir)
151
152
153
154
155 def findInstalled():
156 installed = []
157 for name in glob.glob(os.path.join(RCPTDIR, "wxPython*")):
158 ir = InstalledReceipt(name)
159 installed.append(ir)
160
161 return installed
162
163
164 # Just in case a Python < 2.3 is used to run this
165 try:
166 enumerate
167 except NameError:
168 def enumerate(sequence):
169 return zip(range(len(sequence)), sequence)
170
171
172 def main():
173 if len(sys.argv) > 1 and sys.argv[1] == "-doit":
174 inst = cPickle.loads(urllib.unquote(sys.argv[2]))
175 inst.doUninstall()
176 sys.exit()
177
178 print __doc__
179 installed = findInstalled()
180
181 if not installed:
182 print "*** No wxPython installations found! ***"
183 raw_input("Press RETURN...")
184 sys.exit()
185
186 for i, inst in enumerate(installed):
187 print " %d. %s \t%s" % (i+1, inst.mdata["Title"], inst.mdata["Version"])
188
189 print
190 ans = raw_input("Enter the number of the install to examine or 'Q' to quit: ")
191 if ans in ['Q', 'q']:
192 sys.exit()
193 inst = installed[int(ans) - 1]
194 inst.lastInstall = len(installed) == 1
195
196 while True:
197 print
198 print """
199 Title: %(Title)s
200 Version: %(Version)s
201 Description: %(Description)s
202 """ % inst.mdata
203
204 ans = raw_input("(U)ninstall, (S)how what will be removed, or (Q)uit? [u,s,q] ")
205 if ans in ['Q', 'q']:
206 sys.exit()
207
208 elif ans in ['S', 's']:
209 inst.showFiles()
210
211 elif ans in ['U', 'u']:
212 print
213 print "Launching uninstaller with sudo, please enter your password if prompted:"
214 os.system("sudo %s -doit %s" %
215 (sys.argv[0],
216 urllib.quote(cPickle.dumps(inst))))
217 sys.exit()
218
219
220 if __name__ == '__main__':
221 main()