| The Gaudi Framework v27r1

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

- File List
- File Members

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 # To avoid race conditions on makedirs(), use EAFP (see GAUDI-1105)
218 if (not exists(realdest)) or (getmtime(realsrc) > getmtime(realdest)):
219 try:
220 makedirs(dest_path)
221 print "Created dir '{0}'".format(dest_path)
222 except OSError as e:
223 # OSerror no. 17 is "file exists" - harmless as long as the directory is there
224 if not(e.errno == 17 and isdir(dest_path)):
225 raise
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
245 def install(sources, destination, logfile, exclusions = [],
246 destname = None, syml = False, logdir = realpath(".")):
247 """
248 Copy sources to destination keeping track of what has been done in logfile.
249 The destination must be a directory and sources are copied into it.
250 If exclusions is not empty, the files matching one of its elements are not
251 copied.
252 """
253 for s in sources:
254 src_path, src_name = split(s)
255 if not exists(s):
256 continue # silently ignore missing sources
257 elif not isdir(s): # copy the file, without logging (?)
258 if destname is None:
259 dest = join(destination,src_name)
260 else:
261 dest = join(destination,destname)
262 src = getRelativePath(destination,s)
263 dest = getRelativePath(logdir,dest)
264 old_dest = logfile.get_dest(src)
265 update(src,dest,old_dest,syml,logdir)
266 logfile.set_dest(src,dest) # update log
267 else: # for directories
268 # expand the content of the directory as a dictionary
269 # mapping sources to destinations
270 to_do = expand_source_dir(s,destination,exclusions,destname, logdir)
271 src = getRelativePath(destination,s)
272 last_done = logfile.get_dest(src)
273 if last_done is None: last_done = {}
274 for k in to_do:
275 try:
276 old_dest = last_done[k]
277 del last_done[k]
278 except KeyError:
279 old_dest = None
280 update(k,to_do[k],old_dest,syml,logdir)
281 # remove files that were copied but are not anymore in the list
282 for old_dest in last_done.values():
283 remove(old_dest,logdir)
284 logfile.set_dest(src,to_do) # update log
285
286 def uninstall(logfile, destinations = [], logdir=realpath(".")):
287 """
288 Remove copied files using logfile to know what to remove.
289 If destinations is not empty, only the files/directories specified are
290 removed.
291 """
292 for s in logfile.get_sources():
293 dest = logfile.get_dest(s)
294 if type(dest) is str:
295 if filename_match(dest,destinations,default=True):
296 remove(dest, logdir)
297 logfile.remove(s)
298 else:
299 for subs in dest.keys():
300 subdest = dest[subs]
301 if filename_match(subdest,destinations,default=True):
302 remove(subdest,logdir)
303 del dest[subs]
304 if not dest:
305 logfile.remove(s)
306
307 if __name__ == "__main__":
308 main()
install.expand_source_dir
def expand_source_dir
Definition: install.py:125
install.LogFile._installed_files
_installed_files
Definition: install.py:92
install.LogFile.get_sources
def get_sources(self)
Definition: install.py:103
format
GAUDI_API std::string format(const char *,...)
MsgStream format utility "a la sprintf(...)".
Definition: MsgStream.cpp:119
install.LogFile.__len__
def __len__(self)
Definition: install.py:112
install.LogFile.remove
def remove(self, source)
Definition: install.py:106
install.filename_match
def filename_match
Definition: install.py:115
install.getCommonPath
def getCommonPath(dirname, filename)
Definition: install.py:176
install.getRelativePath
def getRelativePath(dirname, filename)
Definition: install.py:195
gaudirun.type
string type
Definition: gaudirun.py:151
install.main
def main()
Definition: install.py:27
install.LogFile.__init__
def __init__(self)
Definition: install.py:91
install.LogFile
Definition: install.py:87
install.remove
def remove(file, logdir)
Definition: install.py:153
install.uninstall
def uninstall
Definition: install.py:286
install.update
def update
Definition: install.py:213
install.LogFile.set_dest
def set_dest(self, source, dest)
Definition: install.py:100
install.LogFile.get_dest
def get_dest(self, source)
Definition: install.py:94
finalize_tags.dirname
def dirname(url)
Definition: finalize_tags.py:50
install.install
def install
Definition: install.py:246
install
Definition: install.py:1