home *** CD-ROM | disk | FTP | other *** search
/ PC World 2002 May / PCWorld_2002-05_cd.bin / Software / TemaCD / activepython / ActivePython-2.1.1.msi / Python21_PPM_installer.py < prev    next >
Encoding:
Python Source  |  2001-07-26  |  19.1 KB  |  516 lines

  1. # Copyright 2001 (C) ActiveState Tool Corp., All Rights Reserved.
  2. #
  3. import sys, os, urllib, urlparse, string
  4. import soaptalk
  5. import ppmconfig
  6. import xml.dom.minidom
  7. import shutil
  8. import re
  9. import tempfile
  10.  
  11. from types import *
  12. from ppmerrors import *
  13. from distutils import dir_util
  14. from distutils.errors import DistutilsExecError
  15. from distutils.spawn import spawn
  16. from distutils.dir_util import mkpath
  17.  
  18. def filenameFromURL(url):
  19.      return os.path.split(urlparse.urlparse(url)[2])[1]
  20.  
  21. def find_loco_type(loco):
  22.     if string.find(loco.lower() , 'file:')>=0:
  23.         return 'file'
  24.     if string.find(loco.lower(), 'ftp:')>=0:
  25.         return 'ftp'
  26.     if string.find(loco.lower(), 'http:')>=0:
  27.         return 'http'
  28.     if os.path.isfile(loco) or\
  29.        os.path.isfile(loco+'.ppd') or\
  30.        os.path.isfile(loco+'.PPD'):
  31.         return 'file'
  32.     # error checking ????  - this is a hack to enable relative paths
  33.     # but a check is needed for bad files
  34.     return ''
  35.  
  36.  
  37. def isurl(url):
  38.    if find_loco_type(url) != 'file':
  39.        if string.find(url,'://')>=0:
  40.             return 1
  41.    return 0
  42.  
  43. def isfile(file):
  44.     if find_loco_type(file) == 'file' or\
  45.        re.match(r'[A-z]:(/|\\)\w+',file):
  46.             return 1
  47.     return 0
  48.  
  49. def isdir(location):
  50.      if os.path.isdir(location):
  51.           return 1
  52.      return 0
  53.  
  54. def CheckOs(ValidOsList):
  55.     for myos in ValidOsList:
  56.         if (myos.lower() == 'nt') and (os.name=='nt' or os.name =='dos'):
  57.             return 1
  58.         elif myos.lower() == 'linux' and string.find(sys.platform,'linux')>=0:
  59.             return 1
  60.         elif myos.lower() == 'sun' and string.find(sys.platform,'sun')>=0:
  61.             return 1
  62.         #XXX 
  63.         elif myos=='':
  64.             return 1
  65.     return 0
  66.  
  67. def get_repository_location(ImplementationObj):
  68.     try:
  69.          loco = ImplementationObj.codebase['href']
  70.          # if loco is None we get ride of the key
  71.          if loco is None:
  72.               del ImplementationObj.codebase['href']
  73.               raise KeyError('HREF attr not found')
  74.          return str(loco)
  75.     except KeyError: 
  76.          try:
  77.                loco = ImplementationObj.codebase['filename']
  78.                if loco is None:
  79.                     del ImplementationObj.codebase['filename']
  80.                     raise KeyError('FILENAME or HREF attr not found')
  81.                return str(loco)
  82.          except KeyError, e:
  83.               raise FailInstall('Codebase is not valid: %s' % e)
  84.  
  85. def switchdir(newdir):
  86.     olddir = os.getcwd()
  87.     os.chdir(newdir)
  88.     return olddir
  89.  
  90. def decompress_zipfile(build_dir,zip_filename,
  91.                    verbose=0,dry_run=0):
  92.     mkpath(build_dir, verbose=verbose)
  93.     curdir = switchdir(build_dir)
  94.     #XXX this is one disadvantage of using distutil. If
  95.     #you don't reset this hash, Distutils would be deceived
  96.     #and does not recreate a path which has been visited before,
  97.     #even if the path is not valid anymore.
  98.     dir_util._path_created = {}
  99.     errorreporter.Msg("Inflating '%s'..." % zip_filename)
  100.     try:
  101.          import zipfile
  102.     except ImportError:
  103.          try:
  104.               spawn(["unzip", zip_filename],
  105.                     verbose=verbose, dry_run=dry_run)
  106.               switchdir(curdir)
  107.               return zip_filename
  108.          except DistutilsExecError:
  109.               raise DistutilsExecError, \
  110.                ("unable to create zip file '%s': " + 
  111.                 "could neither find a standalone zip utility nor " +
  112.                 "import the 'zipfile' module") % zip_filename
  113.  
  114.     if not zipfile.is_zipfile(zip_filename):
  115.         raise FailInstall("Invalid ZIP file: format not recognized")
  116.  
  117.     def WriteFile(filename,bytes,verbose=0):
  118.         dirname , fname = os.path.split(filename)
  119.         mkpath(dirname , verbose=verbose)
  120.         file = open(filename, 'wb')
  121.         file.write(bytes)
  122.         file.close()
  123.  
  124.     z = zipfile.ZipFile(zip_filename,'r')
  125.     try:
  126.         for filename in z.namelist():
  127.             bytes = z.read(filename)
  128.             WriteFile(filename , bytes, verbose=verbose)
  129.     except zipfile.error, e:
  130.         raise FailInstall('An error occured while decompressing '+\
  131.         'the archive: ' % e)
  132.  
  133.     switchdir(curdir)
  134.     return zip_filename
  135.  
  136. def decompress_tarball(build_dir,archive_name,decompress="gunzip",
  137.                          verbose=0,dry_run=0):
  138.     decompress_ext = { 'gunzip': ".gz",
  139.                        'bunzip2': '.bz2',
  140.                        'decompress': ".Z" }
  141.     
  142.     if decompress is not None and decompress not in decompress_ext.keys():
  143.         raise ValueError, \
  144.               "bad value for 'decompress': %s: must be "+\
  145.               "None, 'gunzip', or 'decompress'" % decompress
  146.  
  147.     if decompress:
  148.         errorreporter.Msg(decompress+' '+ os.path.join(build_dir,archive_name))
  149.         if os.system(decompress +' '+ os.path.join(build_dir,archive_name))!=0:
  150.             raise FailInstall('Command %s returned a negative '+\
  151.                               'value' % decompress)
  152.         archive_name = archive_name[0:string.rfind(archive_name , \
  153.                                                    decompress_ext[decompress])]
  154.  
  155.     dnld_dir = os.path.dirname(build_dir)
  156.     mkpath(build_dir, verbose=verbose)
  157.     curdir = switchdir(build_dir)
  158.     if verbose:
  159.         cmd = "tar -xvf " + archive_name
  160.     else:
  161.         cmd = "tar -xf " + archive_name
  162.     errorreporter.Msg("Inflating '%s'..." % cmd)
  163.     if os.system(cmd)!=0:
  164.         raise FailInstall("Command %s returned a negative value" % cmd)
  165.     switchdir(build_dir)
  166.     return archive_name
  167.  
  168. # known archive formats
  169. ARCHIVE_FORMATS = {
  170.     'tar.gz': (decompress_tarball, [('decompress', 'gunzip')], \
  171.                "gzip'ed tar-file"),
  172.     'tar.bz2': (decompress_tarball, [('decompress', 'bunzip2')], \
  173.                 "bzip2'ed tar-file"),
  174.     'tar.Z':  (decompress_tarball, [('decompress', 'decompress')], \
  175.                "compressed tar file"),
  176.     'tar':   (decompress_tarball, [('decompress', None)], \
  177.               "uncompressed tar file"),
  178.     'zip':   (decompress_zipfile, [],"ZIP file")
  179.     }
  180.  
  181. def get_archive_type(fullfilename):
  182.     name , ext = (None, None)
  183.     (head , tail) = os.path.split(fullfilename)
  184.     if not tail:
  185.         raise ValueError
  186.     for formats in ARCHIVE_FORMATS.keys():
  187.         index = string.rfind(tail,formats)
  188.         if index>0:
  189.             ext = tail[index:]
  190.             name = tail[0:index-1]
  191.     if not name or not ext:
  192.         raise ValueError 
  193.     return (name , ext)
  194.  
  195.  
  196. def decompress_archive(fullfilename, Build_dir,verbose=0,dry_run=0):
  197.     #get the name and the type of the compressed file
  198.     errorreporter.Msg('Decompressing the archive %s...' % fullfilename)
  199.     try:
  200.          (filename , ext) = get_archive_type(fullfilename)
  201.     except ValueError, e:
  202.          errorreporter.LowError("Archive type not recognized: %s" % e)
  203.          return
  204.  
  205.     kwargs = { 'verbose': verbose,
  206.                'dry_run': dry_run }
  207.     try:
  208.         format_info = ARCHIVE_FORMATS[ext]
  209.     except KeyError:
  210.         raise ValueError, "unknown archive format '%s'" % ext
  211.  
  212.     errorreporter.Msg('Archive format recognized as: %s' % format_info[2])
  213.     func = format_info[0]
  214.     for (arg,val) in format_info[1]:
  215.         kwargs[arg] = val
  216.         
  217.     return apply(func,(Build_dir,fullfilename),kwargs)
  218.  
  219.  
  220. def CheckInstallation(pkg,instpkgs):
  221.      return pkg.name in [p.name for p in instpkgs]
  222.  
  223. FancyStatus = '[ %d, %d ] ************************************* [ %s ]'
  224. class Installer:
  225.     def __init__(self, verbose, printfunc): 
  226.                        #XXX 
  227.         self.verbose = verbose
  228.         self.TotalDownload = 0
  229.         self.printfunc = printfunc
  230.  
  231.     def init(self, package_name, ppd_loco = None, bin_loco = None):
  232.         self.pn = package_name
  233.         self.ppd_loco = ppd_loco
  234.         self.bin_loco = bin_loco
  235.  
  236.     def fetch_ppd(self, locos, pac_name):
  237.         if 1:  # XXX remove this silly thing later
  238.             #if it is a file or url just fetch it right here and do not
  239.             #use SOAP calls to PPM server
  240.             rc = find_loco_type(pac_name)
  241.             if rc == 'file':
  242.                 self.printfunc( "Opening PPD file on local file system...")
  243.                 if string.find(pac_name,'.ppd')<1:
  244.                      pac_name += '.ppd'
  245.                 try:
  246.                     fobj = open(pac_name, 'r')
  247.             self.ppd_path = os.path.abspath(pac_name)
  248.             ppd = fobj.read()
  249.             fobj.close()
  250.                 except IOError, e:
  251.                     errorreporter.Fatal('can not open ppd file: %s' % e)
  252.                     return None
  253.                     #XXX 
  254.                     #XXX 
  255.                 return ppd
  256.             if rc =='http':
  257.                 return self.fetch_ppd_fromURL(pac_name) 
  258.             self.init(pac_name, locos.uri)
  259.             if not self.ppd_loco:
  260.                 errorreporter.Fatal("Can not fetch the package info."+\
  261.                       "No location has been specified.")
  262.                 return
  263.             if not self.pn:
  264.                 errorreporter.Fatal("No package name has been specified")
  265.                 return
  266.             #if locos is a repository url then we have to deal with it using SOAP
  267.             rc = find_loco_type(locos.uri)
  268.             if rc != 'http':
  269.                 raise ProtocolError('Sorry, The SOAP server can not bind to this protocol: %s' \
  270.                                     % rc)
  271.                 return
  272.             try:
  273.                 soap_server = soaptalk.soaptalk(locos.uri,namespace=locos.namespace)
  274.             except Exception, e:
  275.                  errorreporter.Error("Can not access the specified server: %s" % e)
  276.                  return ''
  277.             return soap_server.fetch_ppd(self.pn)
  278.  
  279.     def status_hook(self,blocks,blockSize,FileSize):
  280.          size = blocks*blockSize
  281.          if self.DownloadStatus and \
  282.             int(size/self.DownloadStatus)>self.TotalDownload:
  283.               self.TotalDownload += 1
  284.               if FileSize:
  285.                    self.printfunc(FancyStatus % (int(size/1000),
  286.                         int(FileSize/1000), str(int((size*100)/FileSize))))
  287.               else:
  288.                    self.printfunc(FancyStatus % (str(size/1000), 'UNKNOWN'))
  289.                    
  290.     def fetch_binary(self, location, builddir=os.curdir):
  291.         self.bin_loco = location
  292.         self.build_dir = str(builddir)
  293.  
  294.         if self.build_dir[-1] != '/':
  295.             self.build_dir += '/'
  296.         mkpath(self.build_dir,verbose=self.verbose)
  297.         if isurl(self.bin_loco):
  298.             filename = filenameFromURL(self.bin_loco)
  299.         (dummy, _) = get_archive_type(filename)
  300.             build_dir = os.path.join(self.build_dir,dummy)
  301.             mkpath(build_dir)
  302.             fullfilename = os.path.join(build_dir,filename)
  303.             if os.path.exists(fullfilename):
  304.                 self.printfunc("(Path already exists -> Deleting %s)" % fullfilename)
  305.             try: 
  306.                     if(os.path.isdir(fullfilename)):
  307.                          shutil.rmtree(fullfilename,1)
  308.                     if(os.path.isfile(fullfilename)):
  309.                          os.unlink(fullfilename)
  310.             except Exception, e:
  311.             errorreporter.Error("can not download the package: %s" % e)
  312.             return ''
  313.  
  314.             self.printfunc("(fetching %s)"% filename)
  315.             try:
  316.                  urllib.urlretrieve(self.bin_loco, fullfilename,
  317.                                     self.status_hook)
  318.                  return filename
  319.             except IOError, e:
  320.                  errorreporter.Error("can not access the specified URL: %s" % e)
  321.                  raise ProtocolError('')
  322.  
  323.         else:
  324.         # check for paths relative to the PPD location
  325.         ppd_dir = os.path.dirname(self.ppd_path)
  326.         bin_loco_path = os.path.join(ppd_dir,self.bin_loco)
  327.         if isfile(bin_loco_path):
  328.         # url name machinery to make urllib happy
  329.             bin_loco_path = string.translate(bin_loco_path,string.maketrans(':','|'))
  330.         self.bin_loco = 'file:///' + bin_loco_path    
  331.         self.printfunc("self.bin_loco %s" % self.bin_loco)
  332.              filename = filenameFromURL(self.bin_loco)
  333.         (dummy, _) = get_archive_type(filename)
  334.             build_dir = os.path.join(self.build_dir,dummy)
  335.             mkpath(build_dir)
  336.             fullfilename = os.path.join(build_dir,filename)
  337.                   if os.path.exists(fullfilename):
  338.                     self.printfunc("(Path already exists -> Deleting %s)" % fullfilename)
  339.             try: 
  340.                     if(os.path.isdir(fullfilename)):
  341.                          shutil.rmtree(fullfilename,1)
  342.                     if(os.path.isfile(fullfilename)):
  343.                          os.unlink(fullfilename)
  344.             except Exception, e:
  345.             errorreporter.Error("can not fetch the package: %s" % e)
  346.             return ''
  347.  
  348.               self.printfunc("(fetching %s)"% filename)
  349.                try:
  350.                     urllib.urlretrieve(self.bin_loco, fullfilename,
  351.                                     self.status_hook)
  352.                     return filename
  353.                 except IOError, e:
  354.                  errorreporter.Error("can not access the specified URL: %s" % e)
  355.                     raise ProtocolError('')
  356.             else:
  357.              return ''
  358.         
  359.     def fetch_ppd_fromURL(self,url):
  360.         if not string.rfind(url,'.ppd')>0:
  361.             url += '.ppd'
  362.         if isurl(url):
  363.             try:
  364.                file = urllib.urlopen(url)
  365.                ppd = file.read()
  366.                ppd = str(ppd)
  367.                ppd.strip()
  368.                file.close()
  369.                return ppd
  370.             except IOError, e:
  371.                errorreporter.Error("can not read the socket: %s" % e)
  372.                return ''
  373.             else:
  374.                return ''
  375.        
  376.     def InstallPackage(self,ppd,cnfs,clean=0,upgrade=0):
  377.         if type(ppd) is TupleType:
  378.              raise BadPPD(str(ppd))
  379.         if not ppd:
  380.              raise PackageNotFound
  381.              return
  382.         elif string.find(ppd,'404')>=0 or \
  383.              string.find(ppd,'Not Found')>=0:
  384.              raise PackageNotFound
  385.         else:
  386.              ppd = str(ppd)
  387.  
  388.         self.TotalSize = 0
  389.         self.DownloadStatus = cnfs.options['downloadstatus']
  390.         try:
  391.              domdom = xml.dom.minidom.parseString(ppd)
  392.         except Exception, e:
  393.              raise BadPPD(e)
  394.         try:
  395.              self.softpkg = ppmconfig.Package(domdom.getElementsByTagName\
  396.                                               ("SOFTPKG")[0])             
  397.         except:
  398.              raise FailInstall("Defective PPD")
  399.  
  400.         # checks to see if this package has been installed before
  401.         if CheckInstallation(self.softpkg,cnfs.InstPackages) and \
  402.            not upgrade:
  403.              raise AlreadyInstalled
  404.         
  405.         for imp in self.softpkg.implementations:
  406.              #checks to see if our OS is among the OS tags listed in the PPD
  407.              if not imp.python_version:
  408.                 filename = tempfile.mktemp(".ppd")
  409.                 self.printfunc("tempfile %s" % filename)
  410.                 open(filename, "w").write(ppd)
  411.                 os.system("ppm install %s" % filename )
  412.                 #os.remove(tempfile)
  413.                 return 
  414.  
  415.              if CheckOs(imp.oss):
  416.                   location = get_repository_location(imp)
  417.                   InstallerApp = imp.installer
  418.                   try:
  419.                        self.binary_name = self.fetch_binary(location,cnfs.builddir)
  420.                   except ProtocolError:
  421.                        raise FailInstall('URL inaccessible')
  422.                   try:
  423.                        self.InstallBinary(cnfs.options['root'],
  424.                                           clean=clean,
  425.                                           verbose=self.verbose,
  426.                                           InstallerApp=InstallerApp)
  427.                        display = errorreporter.tracer.displayer.display
  428.                        display("Package %s was successfully installed" % \
  429.                            self.softpkg.name, 4)
  430.                        return self.softpkg
  431.                   except Exception, e:
  432.                        raise FailInstall(e)
  433.             
  434.         raise FailInstall('Package %s has not been implemented on your platform.' \
  435.                           % self.softpkg.name)
  436.  
  437.     def execute(self, arg,dirname,names):
  438.         if arg[1]:
  439.             if not arg[1] in names:
  440.                 raise FailInstall("Setup script '%s' not found!" % arg[1])
  441.             curdir = switchdir(dirname)
  442.             rc = os.system(arg[1])
  443.             switchdir(curdir)
  444.             if rc==0:
  445.                 return
  446.             else:
  447.                 raise FailInstall("Can not execute the setup script")
  448.         elif 'setup.py' in names:
  449.             curdir = switchdir(dirname)
  450.  
  451.             if sys.platform.startswith("win") and \
  452.                       ppmconfig.using_gui():
  453.                 nul = "> nul:"
  454.             else:
  455.                 nul=""
  456.  
  457.             if arg[0]:
  458.                 root = "--root"
  459.             else:
  460.                 root = ""
  461.  
  462.             SPACE=" "
  463.             command = SPACE.join(['"'+sys.executable+'"',
  464.                                 "setup.py install --skip-build", 
  465.                                 root, nul] )
  466.  
  467.             rc = os.system(command)
  468.             switchdir(curdir)
  469.             if rc==0:
  470.                 arg[2][0]=1 #XXX 
  471.                 return
  472.             else:
  473.                 raise FailInstall("Can not execute the setup script")
  474.               
  475.     def InstallBinary(self,root,clean=0,verbose=0,InstallerApp=''):
  476.                                     # Python!!!
  477.  
  478.          self.printfunc("entering install binary... %s" % (self.binary_name))
  479.      (dummy, _) = get_archive_type(self.binary_name)
  480.  
  481.          build_dir = os.path.join(self.build_dir, dummy)
  482.          errorreporter.Msg('Trying to inflate and install '+ \
  483.              'downloaded binary %s in directory %s' % \
  484.              (self.binary_name, build_dir))
  485.  
  486.          archive_name = decompress_archive(self.binary_name , build_dir,\
  487.                                            verbose=self.verbose)
  488.          
  489.          if not InstallerApp:
  490.               errorreporter.Msg('installing the package using ''setup.py'' script')
  491.          else:
  492.               errorreporter.Msg('installing the package using %s' % InstallerApp)
  493.          IsItInstalled = [0]
  494.          try:
  495.               os.path.walk(build_dir, self.execute,[root,InstallerApp,IsItInstalled])
  496.          except Exception, e:
  497.               raise FailInstall(e)
  498.          if not IsItInstalled[0]:
  499.               raise FailInstall('')
  500.              
  501.          def deletem(arg,dirname,names):
  502.               if arg:
  503.                    os.remove(os.path.join(dirname,arg))
  504.               elif 'setup.py' in names or\
  505.                    'Setup.py' in names:
  506.                    names = []
  507.                    shutil.rmtree(dirname,1)
  508.  
  509.          if clean:
  510.               errorreporter.Msg('Removing downloaded files...')
  511.               try:
  512.           shutil.rmtree(build_dir)
  513.           except Exception, e:
  514.                   errorreporter.Error("Error removing build dir. Some \
  515. files may not be removed: %s" % e)
  516.