| # Gaudi Framework, version v25r0 |

Home |  | Generated: Mon Feb 17 2014

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
install.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 """
3 Script used to install files keeping track of the files that have
4 been installed, so that at the next installation the file removed
5 from the source directory will also be removed from the destination
6 directory.
7 The script provide also the "uninstall" functionality to remove all
8 and only the files that it installed for the package.
9
10 Command line:
11
12 install.py [-x exclusion1 [-x exclusion2 ...]] [-l logfile] source1 [source2 ...] dest
13 install.py -u [-l logfile] [dest1 ...]
14
15 @author: Marco Clemencic <marco.clemencic@cern.ch>
16 """
17 _version = "$Id: install.py,v 1.15 2008/10/28 17:24:39 marcocle Exp $"
18
19 import os, sys
20 from os import (makedirs, listdir, rmdir, walk, sep)
21 from os.path import (exists, isdir, getmtime, split, join, realpath, dirname,
22 normpath, splitext, splitdrive)
23 from pickle import (dump, load)
24 from fnmatch import fnmatch
25 import itertools, shutil
26
27 def main():
28 from optparse import OptionParser
29 parser = OptionParser()
30 parser.add_option("-x","--exclude",action="append",
31 metavar="PATTERN", default = [],
32 dest="exclusions", help="which files/directories to avoid to install")
33 parser.add_option("-l","--log",action="store",
34 dest="logfile", default="install.log",
35 help="where to store the informations about installed files [default: %default]")
36 parser.add_option("-d","--destname",action="store",
37 dest="destname", default=None,
38 help="name to use when installing the source into the destination directory [default: source name]")
39 parser.add_option("-u","--uninstall",action="store_true",
40 dest="uninstall", default=False,
41 help="do uninstall")
42 parser.add_option("-s","--symlink",action="store_true",
43 dest="symlink", default=False,
44 help="create symlinks instead of copy")
45 #parser.add_option("-p","--permission",action="store",
46 # metavar="PERM",
47 # dest="permission",
48 # help="modify the permission of the destination file (see 'man chown'). Unix only.")
49 (opts,args) = parser.parse_args()
50
51 # options consistency check
52 if opts.uninstall:
53 if opts.exclusions:
54 parser.error("Exclusion list does not make sense for uninstall")
55 opts.destination = args
56 try:
57 log = load(open(opts.logfile,"rb"))
58 except:
59 log = LogFile()
60 uninstall(log,opts.destination,realpath(dirname(opts.logfile)))
61 if log:
62 dump(log,open(opts.logfile,"wb"))
63 else:
64 from os import remove
65 try:
66 remove(opts.logfile)
67 except OSError, x:
68 if x.errno != 2 : raise
69 else : # install mode
70 if len(args) < 2:
71 parser.error("Specify at least one source and (only) one destination")
72 opts.destination = args[-1]
73 opts.sources = args[:-1]
74 try:
75 log = load(open(opts.logfile,"rb"))
76 except:
77 log = LogFile()
78 if opts.symlink :
79 if len(opts.sources) != 1:
80 parser.error("no more that 2 args with --symlink")
81 opts.destination, opts.destname = split(opts.destination)
82 install(opts.sources,opts.destination,
83 log,opts.exclusions,opts.destname,
84 opts.symlink, realpath(dirname(opts.logfile)))
85 dump(log,open(opts.logfile,"wb"))
86
87 class LogFile:
88 """
89 Class to incapsulate the logfile functionalities.
90 """
91 def __init__(self):
92 self._installed_files = {}
93
94 def get_dest(self,source):
95 try:
96 return self._installed_files[source]
97 except KeyError:
98 return None
99
100 def set_dest(self,source,dest):
101 self._installed_files[source] = dest
102
103 def get_sources(self):
104 return self._installed_files.keys()
105
106 def remove(self,source):
107 try:
108 del self._installed_files[source]
109 except KeyError:
110 pass
111
112 def __len__(self):
113 return self._installed_files.__len__()
114
115 def filename_match(name,patterns,default=False):
116 """
117 Check if the name is matched by any of the patterns in exclusions.
118 """
119 for x in patterns:
120 if fnmatch(name,x):
121 return True
122 return default
123
124 def expand_source_dir(source, destination, exclusions = [],
125 destname = None, logdir = realpath(".")):
126 """
127 Generate the list of copies.
128 """
129 expansion = {}
130 src_path,src_name = split(source)
131 if destname:
132 to_replace = source
133 replacement = join(destination,destname)
134 else:
135 to_replace = src_path
136 replacement = destination
137
138 for dirname, dirs, files in walk(source):
139 if to_replace:
140 dest_path=dirname.replace(to_replace,replacement)
141 else:
142 dest_path=join(destination,dirname)
143 # remove excluded dirs from the list
144 dirs[:] = [ d for d in dirs if not filename_match(d,exclusions) ]
145 # loop over files
146 for f in files:
147 if filename_match(f,exclusions): continue
148 key = getRelativePath(dest_path, join(dirname,f))
149 value = getRelativePath(logdir, join(dest_path,f))
150 expansion[key] = value
151 return expansion
152
153 def remove(file, logdir):
154 file = normpath(join(logdir, file))
155 try:
156 print "Remove '%s'"%file
157 os.remove(file)
158 # For python files, remove the compiled versions too
159 if splitext(file)[-1] == ".py":
160 for c in ['c', 'o']:
161 if exists(file + c):
162 print "Remove '%s'" % (file+c)
163 os.remove(file+c)
164 file_path = split(file)[0]
165 while file_path and (len(listdir(file_path)) == 0):
166 print "Remove empty dir '%s'"%file_path
167 rmdir(file_path)
168 file_path = split(file_path)[0]
169 except OSError, x: # ignore file-not-found errors
170 if x.errno in [2, 13] :
171 print "Previous removal ignored"
172 else:
173 raise
174
175
176 def getCommonPath(dirname, filename):
177 # if the 2 components are on different drives (windows)
178 if splitdrive(dirname)[0] != splitdrive(filename)[0]:
179 return None
180 dirl = dirname.split(sep)
181 filel = filename.split(sep)
182 commpth = []
183 for d, f in itertools.izip(dirl, filel):
184 if d == f :
185 commpth.append(d)
186 else :
187 break
188 commpth = sep.join(commpth)
189 if not commpth:
190 commpth = sep
191 elif commpth[-1] != sep:
192 commpth += sep
193 return commpth
194
195 def getRelativePath(dirname, filename):
196 """ calculate the relative path of filename with regards to dirname """
197 # Translate the filename to the realpath of the parent directory + basename
198 filepath,basename = os.path.split(filename)
199 filename = os.path.join(os.path.realpath(filepath),basename)
200 # Get the absolute pathof the destination directory
201 dirname = os.path.realpath(dirname)
202 commonpath = getCommonPath(dirname, filename)
203 # for windows if the 2 components are on different drives
204 if not commonpath:
205 return filename
206 relname = filename[len(commonpath):]
207 reldir = dirname[len(commonpath):]
208 if reldir:
209 relname = (os.path.pardir+os.path.sep)*len(reldir.split(os.path.sep)) \
210 + relname
211 return relname
212
213 def update(src,dest,old_dest = None, syml = False, logdir = realpath(".")):
214 realdest = normpath(join(logdir, dest))
215 dest_path = split(realdest)[0]
216 realsrc = normpath(join(dest_path,src))
217 # The modification time is compared only with the precision of the second
218 # to avoid a bug in Python 2.5 + Win32 (Fixed in Python 2.5.1).
219 # See:
220 # http://bugs.python.org/issue1671965
221 # http://bugs.python.org/issue1565150
222 if (not exists(realdest)) or (int(getmtime(realsrc)) > int(getmtime(realdest))):
223 if not isdir(dest_path):
224 print "Create dir '%s'"%(dest_path)
225 makedirs(dest_path)
226 # the destination file is missing or older than the source
227 if syml and sys.platform != "win32" :
228 if exists(realdest):
229 remove(realdest,logdir)
230 print "Create Link to '%s' in '%s'"%(src,dest_path)
231 os.symlink(src,realdest)
232 else:
233 print "Copy '%s' -> '%s'"%(src, realdest)
234 if exists(realdest):
235 # If the destination path exists it is better to remove it before
236 # doing the copy (shutil.copystat fails if the destination file
237 # is not owned by the current user).
238 os.remove(realdest)
239 if sys.platform != "darwin":
240 shutil.copy2(realsrc, realdest) # do the copy (cp -p src dest)
241 else:
242 shutil.copy(realsrc, realdest) # do the copy (cp src dest)
243
244 #if old_dest != dest: # the file was installed somewhere else
245 # # remove the old destination
246 # if old_dest is not None:
247 # remove(old_dest,logdir)
248
249 def install(sources, destination, logfile, exclusions = [],
250 destname = None, syml = False, logdir = realpath(".")):
251 """
252 Copy sources to destination keeping track of what has been done in logfile.
253 The destination must be a directory and sources are copied into it.
254 If exclusions is not empty, the files matching one of its elements are not
255 copied.
256 """
257 for s in sources:
258 src_path, src_name = split(s)
259 if not exists(s):
260 continue # silently ignore missing sources
261 elif not isdir(s): # copy the file, without logging (?)
262 if destname is None:
263 dest = join(destination,src_name)
264 else:
265 dest = join(destination,destname)
266 src = getRelativePath(destination,s)
267 dest = getRelativePath(logdir,dest)
268 old_dest = logfile.get_dest(src)
269 update(src,dest,old_dest,syml,logdir)
270 logfile.set_dest(src,dest) # update log
271 else: # for directories
272 # expand the content of the directory as a dictionary
273 # mapping sources to destinations
274 to_do = expand_source_dir(s,destination,exclusions,destname, logdir)
275 src = getRelativePath(destination,s)
276 last_done = logfile.get_dest(src)
277 if last_done is None: last_done = {}
278 for k in to_do:
279 try:
280 old_dest = last_done[k]
281 del last_done[k]
282 except KeyError:
283 old_dest = None
284 update(k,to_do[k],old_dest,syml,logdir)
285 # remove files that were copied but are not anymore in the list
286 for old_dest in last_done.values():
287 remove(old_dest,logdir)
288 logfile.set_dest(src,to_do) # update log
289
290 def uninstall(logfile, destinations = [], logdir=realpath(".")):
291 """
292 Remove copied files using logfile to know what to remove.
293 If destinations is not empty, only the files/directories specified are
294 removed.
295 """
296 for s in logfile.get_sources():
297 dest = logfile.get_dest(s)
298 if type(dest) is str:
299 if filename_match(dest,destinations,default=True):
300 remove(dest, logdir)
301 logfile.remove(s)
302 else:
303 for subs in dest.keys():
304 subdest = dest[subs]
305 if filename_match(subdest,destinations,default=True):
306 remove(subdest,logdir)
307 del dest[subs]
308 if not dest:
309 logfile.remove(s)
310
311 if __name__ == "__main__":
312 main()