Gaudi Framework, version v22r2

Home   Generated: Tue May 10 2011

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             if exists(realdest):
00235                 # If the destination path exists it is better to remove it before
00236                 # doing the copy (shutil.copystat fails if the destination file
00237                 # is not owned by the current user).
00238                 os.remove(realdest)
00239             shutil.copy2(realsrc, realdest) # do the copy (cp -p src dest)
00240             
00241     #if old_dest != dest: # the file was installed somewhere else
00242     #    # remove the old destination
00243     #    if old_dest is not None:
00244     #        remove(old_dest,logdir)
00245 
00246 def install(sources, destination, logfile, exclusions = [],
00247             destname = None, syml = False, logdir = realpath(".")):
00248     """
00249     Copy sources to destination keeping track of what has been done in logfile.
00250     The destination must be a directory and sources are copied into it.
00251     If exclusions is not empty, the files matching one of its elements are not
00252     copied.
00253     """
00254     for s in sources:
00255         src_path, src_name = split(s)
00256         if not exists(s):
00257             continue # silently ignore missing sources
00258         elif not isdir(s): # copy the file, without logging (?)
00259             if destname is None:
00260                 dest = join(destination,src_name)
00261             else:
00262                 dest = join(destination,destname)
00263             src = getRelativePath(destination,s)
00264             dest = getRelativePath(logdir,dest)
00265             old_dest = logfile.get_dest(src)
00266             update(src,dest,old_dest,syml,logdir)
00267             logfile.set_dest(src,dest) # update log
00268         else: # for directories
00269             # expand the content of the directory as a dictionary
00270             # mapping sources to destinations
00271             to_do = expand_source_dir(s,destination,exclusions,destname, logdir)
00272             src = getRelativePath(destination,s)
00273             last_done = logfile.get_dest(src)
00274             if last_done is None: last_done = {}
00275             for k in to_do:
00276                 try:
00277                     old_dest = last_done[k]
00278                     del last_done[k]
00279                 except KeyError:
00280                     old_dest = None  
00281                 update(k,to_do[k],old_dest,syml,logdir)
00282             # remove files that were copied but are not anymore in the list 
00283             for old_dest in last_done.values():
00284                 remove(old_dest,logdir)
00285             logfile.set_dest(src,to_do) # update log
00286 
00287 def uninstall(logfile, destinations = [], logdir=realpath(".")):
00288     """
00289     Remove copied files using logfile to know what to remove.
00290     If destinations is not empty, only the files/directories specified are
00291     removed.
00292     """
00293     for s in logfile.get_sources():
00294         dest = logfile.get_dest(s)
00295         if type(dest) is str:
00296             if filename_match(dest,destinations,default=True):
00297                 remove(dest, logdir)
00298                 logfile.remove(s)
00299         else:
00300             for subs in dest.keys():
00301                 subdest = dest[subs]
00302                 if filename_match(subdest,destinations,default=True):
00303                     remove(subdest,logdir)
00304                     del dest[subs]
00305             if not dest:
00306                 logfile.remove(s)
00307             
00308 if __name__ == "__main__":
00309     main()
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines

Generated at Tue May 10 2011 18:53:47 for Gaudi Framework, version v22r2 by Doxygen version 1.7.2 written by Dimitri van Heesch, © 1997-2004