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 if sys.platform !=
"darwin":
240 shutil.copy2(realsrc, realdest)
242 shutil.copy(realsrc, realdest)
249 def install(sources, destination, logfile, exclusions = [],
250 destname =
None, syml =
False, logdir = realpath(
".")):
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
258 src_path, src_name = split(s)
263 dest = join(destination,src_name)
265 dest = join(destination,destname)
268 old_dest = logfile.get_dest(src)
269 update(src,dest,old_dest,syml,logdir)
270 logfile.set_dest(src,dest)
276 last_done = logfile.get_dest(src)
277 if last_done
is None: last_done = {}
280 old_dest = last_done[k]
284 update(k,to_do[k],old_dest,syml,logdir)
286 for old_dest
in last_done.values():
288 logfile.set_dest(src,to_do)
290 def uninstall(logfile, destinations = [], logdir=realpath(
".")):
292 Remove copied files using logfile to know what to remove.
293 If destinations is not empty, only the files/directories specified are
296 for s
in logfile.get_sources():
297 dest = logfile.get_dest(s)
298 if type(dest)
is str:
303 for subs
in dest.keys():
311 if __name__ ==
"__main__":