| # Gaudi Framework, version v21r7 |

Home |  | Generated: 22 Jan 2010

# install.py

Go to the documentation of this file.

    00001 #!/usr/bin/env python
    00002 """
    00003 Script used to install files keeping track of the files that have
    00004 been installed, so that at the next installation the file removed
    00005 from the source directory will also be removed from the destination
    00006 directory.
    00007 The script provide also the "uninstall" functionality to remove all
    00008 and only the files that it installed for the package.
    00009
    00010 Command line:
    00011
    00012    install.py [-x exclusion1 [-x exclusion2 ...]] [-l logfile] source1 [source2 ...] dest
    00013    install.py -u [-l logfile] [dest1 ...]
    00014
    00015 @author: Marco Clemencic <marco.clemencic@cern.ch>
    00016 """
    00017 _version = "$Id: install.py,v 1.15 2008/10/28 17:24:39 marcocle Exp $"
    00018
    00019 import os, sys
    00020 from os import (makedirs, listdir, rmdir, walk, sep)
    00021 from os.path import (exists, isdir, getmtime, split, join, realpath, dirname,
    00022                      normpath, splitext, splitdrive)
    00023 from pickle import (dump, load)
    00024 from fnmatch import fnmatch
    00025 import itertools, shutil
    00026
    00027 def main():
    00028     from optparse import OptionParser
    00029     parser = OptionParser()
    00030     parser.add_option("-x","--exclude",action="append",
    00031                       metavar="PATTERN", default = [],
    00032                       dest="exclusions", help="which files/directories to avoid to install")
    00033     parser.add_option("-l","--log",action="store",
    00034                       dest="logfile", default="install.log",
    00035                       help="where to store the informations about installed files [default: %default]")
    00036     parser.add_option("-d","--destname",action="store",
    00037                       dest="destname", default=None,
    00038                       help="name to use when installing the source into the destination directory [default: source name]")
    00039     parser.add_option("-u","--uninstall",action="store_true",
    00040                       dest="uninstall", default=False,
    00041                       help="do uninstall")
    00042     parser.add_option("-s","--symlink",action="store_true",
    00043                       dest="symlink", default=False,
    00044                       help="create symlinks instead of copy")
    00045     #parser.add_option("-p","--permission",action="store",
    00046     #                  metavar="PERM",
    00047     #                  dest="permission",
    00048     #                  help="modify the permission of the destination file (see 'man chown'). Unix only.")
    00049     (opts,args) = parser.parse_args()
    00050
    00051     # options consistency check
    00052     if opts.uninstall:
    00053         if opts.exclusions:
    00054             parser.error("Exclusion list does not make sense for uninstall")
    00055         opts.destination = args
    00056         try:
    00057             log = load(open(opts.logfile,"rb"))
    00058         except:
    00059             log = LogFile()
    00060         uninstall(log,opts.destination,realpath(dirname(opts.logfile)))
    00061         if log:
    00062             dump(log,open(opts.logfile,"wb"))
    00063         else:
    00064             from os import remove
    00065             try:
    00066                 remove(opts.logfile)
    00067             except OSError, x:
    00068                 if x.errno != 2 : raise
    00069     else : # install mode
    00070         if len(args) < 2:
    00071             parser.error("Specify at least one source and (only) one destination")
    00072         opts.destination = args[-1]
    00073         opts.sources = args[:-1]
    00074         try:
    00075             log = load(open(opts.logfile,"rb"))
    00076         except:
    00077             log = LogFile()
    00078         if opts.symlink :
    00079             if len(opts.sources) != 1:
    00080                 parser.error("no more that 2 args with --symlink")
    00081             opts.destination, opts.destname = split(opts.destination)
    00082         install(opts.sources,opts.destination,
    00083                 log,opts.exclusions,opts.destname,
    00084                 opts.symlink, realpath(dirname(opts.logfile)))
    00085         dump(log,open(opts.logfile,"wb"))
    00086
    00087 class LogFile:
    00088     """
    00089     Class to incapsulate the logfile functionalities.
    00090     """
    00091     def __init__(self):
    00092         self._installed_files = {}
    00093
    00094     def get_dest(self,source):
    00095         try:
    00096             return self._installed_files[source]
    00097         except KeyError:
    00098             return None
    00099
    00100     def set_dest(self,source,dest):
    00101         self._installed_files[source] = dest
    00102
    00103     def get_sources(self):
    00104         return self._installed_files.keys()
    00105
    00106     def remove(self,source):
    00107         try:
    00108             del  self._installed_files[source]
    00109         except KeyError:
    00110             pass
    00111
    00112     def __len__(self):
    00113         return self._installed_files.__len__()
    00114
    00115 def filename_match(name,patterns,default=False):
    00116     """
    00117     Check if the name is matched by any of the patterns in exclusions.
    00118     """
    00119     for x in patterns:
    00120         if fnmatch(name,x):
    00121             return True
    00122     return default
    00123
    00124 def expand_source_dir(source, destination, exclusions = [],
    00125                       destname = None, logdir = realpath(".")):
    00126     """
    00127     Generate the list of copies.
    00128     """
    00129     expansion = {}
    00130     src_path,src_name = split(source)
    00131     if destname:
    00132         to_replace = source
    00133         replacement = join(destination,destname)
    00134     else:
    00135         to_replace = src_path
    00136         replacement = destination
    00137
    00138     for dirname, dirs, files in walk(source):
    00139         if to_replace:
    00140             dest_path=dirname.replace(to_replace,replacement)
    00141         else:
    00142             dest_path=join(destination,dirname)
    00143         # remove excluded dirs from the list
    00144         dirs[:] = [ d for d in dirs if not filename_match(d,exclusions) ]
    00145         # loop over files
    00146         for f in files:
    00147             if filename_match(f,exclusions): continue
    00148             key = getRelativePath(dest_path, join(dirname,f))
    00149             value = getRelativePath(logdir, join(dest_path,f))
    00150             expansion[key] = value
    00151     return expansion
    00152
    00153 def remove(file, logdir):
    00154     file = normpath(join(logdir, file))
    00155     try:
    00156         print "Remove '%s'"%file
    00157         os.remove(file)
    00158         # For python files, remove the compiled versions too
    00159         if splitext(file)[-1] == ".py":
    00160             for c in ['c', 'o']:
    00161                 if exists(file + c):
    00162                     print "Remove '%s'" % (file+c)
    00163                     os.remove(file+c)
    00164         file_path = split(file)[0]
    00165         while file_path and (len(listdir(file_path)) == 0):
    00166             print "Remove empty dir '%s'"%file_path
    00167             rmdir(file_path)
    00168             file_path = split(file_path)[0]
    00169     except OSError, x: # ignore file-not-found errors
    00170         if x.errno in [2, 13] :
    00171             print "Previous removal ignored"
    00172         else:
    00173             raise
    00174
    00175
    00176 def getCommonPath(dirname, filename):
    00177     # if the 2 components are on different drives (windows)
    00178     if splitdrive(dirname)[0] != splitdrive(filename)[0]:
    00179         return None
    00180     dirl = dirname.split(sep)
    00181     filel = filename.split(sep)
    00182     commpth = []
    00183     for d, f in itertools.izip(dirl, filel):
    00184         if d == f :
    00185             commpth.append(d)
    00186         else :
    00187             break
    00188     commpth = sep.join(commpth)
    00189     if not commpth:
    00190         commpth = sep
    00191     elif commpth[-1] != sep:
    00192         commpth += sep
    00193     return commpth
    00194
    00195 def getRelativePath(dirname, filename):
    00196     """ calculate the relative path of filename with regards to dirname """
    00197     # Translate the filename to the realpath of the parent directory + basename
    00198     filepath,basename = os.path.split(filename)
    00199     filename = os.path.join(os.path.realpath(filepath),basename)
    00200     # Get the absolute pathof the destination directory
    00201     dirname = os.path.realpath(dirname)
    00202     commonpath = getCommonPath(dirname, filename)
    00203     # for windows if the 2 components are on different drives
    00204     if not commonpath:
    00205         return filename
    00206     relname = filename[len(commonpath):]
    00207     reldir = dirname[len(commonpath):]
    00208     if reldir:
    00209         relname = (os.path.pardir+os.path.sep)*len(reldir.split(os.path.sep)) \
    00210              + relname
    00211     return relname
    00212
    00213 def update(src,dest,old_dest = None, syml = False, logdir = realpath(".")):
    00214     realdest = normpath(join(logdir, dest))
    00215     dest_path = split(realdest)[0]
    00216     realsrc = normpath(join(dest_path,src))
    00217     # The modification time is compared only with the precision of the second
    00218     # to avoid a bug in Python 2.5 + Win32 (Fixed in Python 2.5.1).
    00219     # See:
    00220     #   http://bugs.python.org/issue1671965
    00221     #   http://bugs.python.org/issue1565150
    00222     if (not exists(realdest)) or (int(getmtime(realsrc)) > int(getmtime(realdest))):
    00223         if not isdir(dest_path):
    00224             print "Create dir '%s'"%(dest_path)
    00225             makedirs(dest_path)
    00226         # the destination file is missing or older than the source
    00227         if syml and sys.platform != "win32" :
    00228             if exists(realdest):
    00229                 remove(realdest,logdir)
    00230             print "Create Link to '%s' in '%s'"%(src,dest_path)
    00231             os.symlink(src,realdest)
    00232         else:
    00233             print "Copy '%s' -> '%s'"%(src,realdest)
    00234             shutil.copy2(realsrc,realdest) # do the copy (cp -p src dest)
    00235     #if old_dest != dest: # the file was installed somewhere else
    00236     #    # remove the old destination
    00237     #    if old_dest is not None:
    00238     #        remove(old_dest,logdir)
    00239
    00240 def install(sources, destination, logfile, exclusions = [],
    00241             destname = None, syml = False, logdir = realpath(".")):
    00242     """
    00243     Copy sources to destination keeping track of what has been done in logfile.
    00244     The destination must be a directory and sources are copied into it.
    00245     If exclusions is not empty, the files matching one of its elements are not
    00246     copied.
    00247     """
    00248     for s in sources:
    00249         src_path, src_name = split(s)
    00250         if not exists(s):
    00251             continue # silently ignore missing sources
    00252         elif not isdir(s): # copy the file, without logging (?)
    00253             if destname is None:
    00254                 dest = join(destination,src_name)
    00255             else:
    00256                 dest = join(destination,destname)
    00257             src = getRelativePath(destination,s)
    00258             dest = getRelativePath(logdir,dest)
    00259             old_dest = logfile.get_dest(src)
    00260             update(src,dest,old_dest,syml,logdir)
    00261             logfile.set_dest(src,dest) # update log
    00262         else: # for directories
    00263             # expand the content of the directory as a dictionary
    00264             # mapping sources to destinations
    00265             to_do = expand_source_dir(s,destination,exclusions,destname, logdir)
    00266             src = getRelativePath(destination,s)
    00267             last_done = logfile.get_dest(src)
    00268             if last_done is None: last_done = {}
    00269             for k in to_do:
    00270                 try:
    00271                     old_dest = last_done[k]
    00272                     del last_done[k]
    00273                 except KeyError:
    00274                     old_dest = None
    00275                 update(k,to_do[k],old_dest,syml,logdir)
    00276             # remove files that were copied but are not anymore in the list
    00277             for old_dest in last_done.values():
    00278                 remove(old_dest,logdir)
    00279             logfile.set_dest(src,to_do) # update log
    00280
    00281 def uninstall(logfile, destinations = [], logdir=realpath(".")):
    00282     """
    00283     Remove copied files using logfile to know what to remove.
    00284     If destinations is not empty, only the files/directories specified are
    00285     removed.
    00286     """
    00287     for s in logfile.get_sources():
    00288         dest = logfile.get_dest(s)
    00289         if type(dest) is str:
    00290             if filename_match(dest,destinations,default=True):
    00291                 remove(dest, logdir)
    00292                 logfile.remove(s)
    00293         else:
    00294             for subs in dest.keys():
    00295                 subdest = dest[subs]
    00296                 if filename_match(subdest,destinations,default=True):
    00297                     remove(subdest,logdir)
    00298                     del dest[subs]
    00299             if not dest:
    00300                 logfile.remove(s)
    00301
    00302 if __name__ == "__main__":
    00303     main()