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
7 The script provide also the "uninstall" functionality to remove all
8 and only the files that it installed for the package.
12 install.py [-x exclusion1 [-x exclusion2 ...]] [-l logfile] source1 [source2 ...] dest
13 install.py -u [-l logfile] [dest1 ...]
15 @author: Marco Clemencic <marco.clemencic@cern.ch>
17 _version =
"$Id: install.py,v 1.15 2008/10/28 17:24:39 marcocle Exp $"
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
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,
42 parser.add_option(
"-s",
"--symlink",action=
"store_true",
43 dest=
"symlink", default=
False,
44 help=
"create symlinks instead of copy")
49 (opts,args) = parser.parse_args()
54 parser.error(
"Exclusion list does not make sense for uninstall")
55 opts.destination = args
57 log = load(open(opts.logfile,
"rb"))
62 dump(log,open(opts.logfile,
"wb"))
68 if x.errno != 2 :
raise
71 parser.error(
"Specify at least one source and (only) one destination")
72 opts.destination = args[-1]
73 opts.sources = args[:-1]
75 log = load(open(opts.logfile,
"rb"))
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"))
89 Class to incapsulate the logfile functionalities.
104 return self._installed_files.keys()
113 return self._installed_files.__len__()
117 Check if the name is matched by any of the patterns in exclusions.
125 destname =
None, logdir = realpath(
".")):
127 Generate the list of copies.
130 src_path,src_name = split(source)
133 replacement = join(destination,destname)
135 to_replace = src_path
136 replacement = destination
138 for dirname, dirs, files
in walk(source):
140 dest_path=dirname.replace(to_replace,replacement)
142 dest_path=join(destination,dirname)
150 expansion[key] = value
154 file = normpath(join(logdir, file))
156 print "Remove '%s'"%file
159 if splitext(file)[-1] ==
".py":
162 print "Remove '%s'" % (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
168 file_path = split(file_path)[0]
170 if x.errno
in [2, 13] :
171 print "Previous removal ignored"
178 if splitdrive(dirname)[0] != splitdrive(filename)[0]:
180 dirl = dirname.split(sep)
181 filel = filename.split(sep)
183 for d, f
in itertools.izip(dirl, filel):
188 commpth = sep.join(commpth)
191 elif commpth[-1] != sep:
196 """ calculate the relative path of filename with regards to dirname """
198 filepath,basename = os.path.split(filename)
199 filename = os.path.join(os.path.realpath(filepath),basename)
201 dirname = os.path.realpath(dirname)
206 relname = filename[len(commonpath):]
207 reldir = dirname[len(commonpath):]
209 relname = (os.path.pardir+os.path.sep)*len(reldir.split(os.path.sep)) \
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))
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)
227 if syml
and sys.platform !=
"win32" :
230 print "Create Link to '%s' in '%s'"%(src,dest_path)
231 os.symlink(src,realdest)
233 print "Copy '%s' -> '%s'"%(src, realdest)
239 shutil.copy2(realsrc, realdest)
246 def install(sources, destination, logfile, exclusions = [],
247 destname =
None, syml =
False, logdir = realpath(
".")):
249 Copy sources to destination keeping track of what has been done in logfile.
250 The destination must be a directory and sources are copied into it.
251 If exclusions is not empty, the files matching one of its elements are not
255 src_path, src_name = split(s)
260 dest = join(destination,src_name)
262 dest = join(destination,destname)
265 old_dest = logfile.get_dest(src)
266 update(src,dest,old_dest,syml,logdir)
267 logfile.set_dest(src,dest)
273 last_done = logfile.get_dest(src)
274 if last_done
is None: last_done = {}
277 old_dest = last_done[k]
281 update(k,to_do[k],old_dest,syml,logdir)
283 for old_dest
in last_done.values():
285 logfile.set_dest(src,to_do)
287 def uninstall(logfile, destinations = [], logdir=realpath(
".")):
289 Remove copied files using logfile to know what to remove.
290 If destinations is not empty, only the files/directories specified are
293 for s
in logfile.get_sources():
294 dest = logfile.get_dest(s)
295 if type(dest)
is str:
300 for subs
in dest.keys():
308 if __name__ ==
"__main__":