PYTHON   16

package.py

Guest on 26th May 2021 07:42:45 PM

  1. #
  2. #  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  3. #  SLEPc - Scalable Library for Eigenvalue Problem Computations
  4. #  Copyright (c) 2002-2021, Universitat Politecnica de Valencia, Spain
  5. #
  6. #  This file is part of SLEPc.
  7. #  SLEPc is distributed under a 2-clause BSD license (see LICENSE).
  8. #  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  9. #
  10.  
  11. from __future__ import print_function
  12. import os, sys, tempfile, shutil, tarfile
  13. import log, argdb
  14. try:
  15.   from urllib import urlretrieve
  16. except ImportError:
  17.   from urllib.request import urlretrieve
  18. try:
  19.   import urlparse as urlparse_local
  20. except ImportError:
  21.   from urllib import parse as urlparse_local
  22. if sys.version_info < (3,):
  23.   import commands
  24. else:
  25.   import subprocess
  26. import socket
  27.  
  28. # Fix parsing for nonstandard schemes
  29. urlparse_local.uses_netloc.extend(['bk', 'ssh', 'svn'])
  30.  
  31. class Package:
  32.  
  33.   def __init__(self,argdb,log):
  34.     self.installable     = False  # an already installed package can be picked --with-xxx-dir
  35.     self.downloadable    = False  # package can be downloaded and installed with --download-xxx
  36.     self.downloadpackage = 0
  37.     self.packagedir      = ''
  38.     self.packagelibs     = []
  39.     self.packageincludes = []
  40.     self.packageurl      = ''
  41.     self.buildflags      = ''
  42.     self.log             = log
  43.     self.supportsscalar  = ['real', 'complex']
  44.     self.supportssingle  = False
  45.     self.supports64bint  = False
  46.     self.fortran         = False
  47.     self.hasheaders      = False
  48.     self.hasdloadflags   = False
  49.     self.requested       = False
  50.     self.havepackage     = False
  51.  
  52.   def RunCommand(self,instr):
  53.     try:
  54.       self.log.write('- '*35+'\nRunning command:\n'+instr+'\n'+'- '*35)
  55.     except AttributeError: pass
  56.     if sys.version_info < (3,):
  57.       (result,output) = commands.getstatusoutput(instr)
  58.     else:
  59.       try:
  60.         output = subprocess.check_output(instr,shell=True,stderr=subprocess.STDOUT)
  61.         result = 0
  62.       except subprocess.CalledProcessError as ex:
  63.         output = ex.output
  64.         result = ex.returncode
  65.       output = output.decode(encoding='UTF-8',errors='replace').rstrip()
  66.     try:
  67.       self.log.write('Output:\n'+output+'\n'+'- '*35)
  68.     except AttributeError: pass
  69.     return (result,output)
  70.  
  71.   def ProcessArgs(self,argdb,petscpackages=''):
  72.     if hasattr(self,'petscdepend') and self.petscdepend in petscpackages:
  73.       self.requested = True
  74.     if self.installable and not hasattr(self,'petscdepend'):
  75.       string,found = argdb.PopPath('with-'+self.packagename+'-dir',exist=True)
  76.       if found:
  77.         self.requested = True
  78.         self.packagedir = string
  79.       string,found = argdb.PopString('with-'+self.packagename+'-lib')
  80.       if found:
  81.         self.requested = True
  82.         self.packagelibs = string.split(',')
  83.       if self.hasheaders:
  84.         string,found = argdb.PopString('with-'+self.packagename+'-include')
  85.         if found:
  86.           self.requested = True
  87.           self.packageincludes = string.split(',')
  88.     if self.installable:
  89.       value,found = argdb.PopBool('with-'+self.packagename)
  90.       if found:
  91.         self.requested = value
  92.     if self.downloadable:
  93.       string,cflagsfound = argdb.PopString('download-'+self.packagename+'-cflags')
  94.       url,flag,found = argdb.PopUrl('download-'+self.packagename)
  95.       if found:
  96.         if self.requested:
  97.           self.log.Exit('Cannot request both download and install simultaneously')
  98.         self.requested = True
  99.         self.download = True
  100.         self.packageurl = url
  101.         self.downloadpackage = flag
  102.       if cflagsfound:
  103.         if not hasattr(self,'download') or not self.download:
  104.           self.log.Exit('--download-'+self.packagename+'-cflags must be used together with --download-'+self.packagename)
  105.         self.buildflags = string
  106.  
  107.   def Process(self,slepcconf,slepcvars,slepcrules,slepc,petsc,archdir=''):
  108.     self.make = petsc.make
  109.     if petsc.buildsharedlib:
  110.       self.slflag = petsc.slflag
  111.     if self.requested:
  112.       name = self.packagename.upper()
  113.       if self.downloadpackage:
  114.         if hasattr(self,'version'):
  115.           self.log.NewSection('Installing '+name+' version '+self.version+'...')
  116.         else:
  117.           self.log.NewSection('Installing '+name+'...')
  118.         self.Precondition(slepc,petsc)
  119.         self.DownloadAndInstall(slepcconf,slepcvars,slepc,petsc,archdir,slepc.prefixdir)
  120.       elif self.installable:
  121.         self.log.NewSection('Checking '+name+'...')
  122.         self.Precondition(slepc,petsc)
  123.         self.Check(slepcconf,slepcvars,petsc,archdir)
  124.         if not self.havepackage: self.log.setLastFailed()
  125.       try:
  126.         self.LoadVersion(slepcconf)
  127.         self.log.write('Version number for '+name+' is '+self.iversion)
  128.       except AttributeError:
  129.         pass
  130.     else: # not requested
  131.       if hasattr(self,'SkipInstall'):
  132.         self.SkipInstall(slepcrules)
  133.  
  134.   def Precondition(self,slepc,petsc):
  135.     package = self.packagename.upper()
  136.     if petsc.scalar == 'complex':
  137.       if 'complex' not in self.supportsscalar:
  138.         self.log.Exit(package+' does not support complex scalars')
  139.     elif petsc.scalar == 'real':
  140.       if 'real' not in self.supportsscalar:
  141.         self.log.Exit(package+' is supported only with complex scalars')
  142.     if petsc.precision == 'single':
  143.       if not self.supportssingle:
  144.         self.log.Exit(package+' is supported only in double precision')
  145.     elif petsc.precision != 'double':
  146.       self.log.Exit('Precision '+petsc.precision+' is not supported for external packages')
  147.     if petsc.ind64 and not self.supports64bint:
  148.       self.log.Exit(package+' cannot be used with 64-bit integers')
  149.     if self.downloadpackage and self.fortran and not hasattr(petsc,'fc'):
  150.       self.log.Exit('Option --download-'+self.packagename+' requires a Fortran compiler')
  151.  
  152.   def MissingTarball(self,downloaddir):
  153.     '''Check if tarball is missing in downloaddir'''
  154.     if self.downloadable and hasattr(self,'download') and self.download and hasattr(self,'archive'):
  155.       localFile = os.path.join(downloaddir,self.archive)
  156.       if not os.path.exists(localFile):
  157.         url = self.packageurl
  158.         if url=='':
  159.           url = self.url
  160.         return self.packagename+': '+url+' --> '+localFile
  161.  
  162.   def Download(self,externdir,downloaddir):
  163.     # Check if source is already available
  164.     if os.path.exists(os.path.join(externdir,self.dirname)):
  165.       self.log.write('Using '+os.path.join(externdir,self.dirname))
  166.       dirname = self.dirname
  167.     else:
  168.  
  169.       if downloaddir:
  170.         # Get tarball from download dir
  171.         if self.packageurl=='':
  172.           localFile = os.path.join(downloaddir,self.archive)
  173.         else:
  174.           localFile = os.path.join(downloaddir,self.packageurl)
  175.         if not os.path.exists(localFile):
  176.           self.log.Exit('Could not find file '+self.archive+' under '+downloaddir)
  177.         url = localFile
  178.         filename = os.path.basename(url)
  179.       else:
  180.         # Download tarball
  181.         url = self.packageurl
  182.         if url=='':
  183.           url = self.url
  184.         if os.path.exists(url):
  185.           url = 'file:'+url
  186.         filename = os.path.basename(urlparse_local.urlparse(url)[2])
  187.         localFile = os.path.join(externdir,self.archive)
  188.         self.log.write('Downloading '+url+' to '+localFile)
  189.  
  190.         if os.path.exists(localFile):
  191.           os.remove(localFile)
  192.         try:
  193.           sav_timeout = socket.getdefaulttimeout()
  194.           socket.setdefaulttimeout(30)
  195.           urlretrieve(url, localFile)
  196.           socket.setdefaulttimeout(sav_timeout)
  197.         except Exception as e:
  198.           socket.setdefaulttimeout(sav_timeout)
  199.           failureMessage = '''\
  200. Unable to download package %s from: %s
  201. * If URL specified manually - perhaps there is a typo?
  202. * If your network is disconnected - please reconnect and rerun ./configure
  203. * Or perhaps you have a firewall blocking the download
  204. * You can run with --with-packages-download-dir=/adirectory and ./configure will instruct you what packages to download manually
  205. * or you can download the above URL manually, to /yourselectedlocation/%s
  206.  and use the configure option:
  207.  --download-%s=/yourselectedlocation/%s
  208. ''' % (self.packagename.upper(), url, filename, self.packagename, filename)
  209.           self.log.Exit(failureMessage)
  210.  
  211.       # Uncompress tarball
  212.       extractdir = os.path.join(externdir,self.dirname)
  213.       self.log.write('Uncompressing '+localFile+' to directory '+extractdir)
  214.       if os.path.exists(extractdir):
  215.         for root, dirs, files in os.walk(extractdir, topdown=False):
  216.           for name in files:
  217.             os.remove(os.path.join(root,name))
  218.           for name in dirs:
  219.             os.rmdir(os.path.join(root,name))
  220.       failureMessage = '''\
  221. Downloaded package %s from: %s is not a tarball.
  222. [or installed python cannot process compressed files]
  223. * If you are behind a firewall - please fix your proxy and rerun ./configure
  224.  For example at LANL you may need to set the environmental variable http_proxy (or HTTP_PROXY?) to  http://proxyout.lanl.gov
  225. * You can run with --with-packages-download-dir=/adirectory and ./configure will instruct you what packages to download manually
  226. * or you can download the above URL manually, to /yourselectedlocation/%s
  227.  and use the configure option:
  228.  --download-%s=/yourselectedlocation/%s
  229. ''' % (self.packagename.upper(), url, filename, self.packagename, filename)
  230.       try:
  231.         tf = tarfile.open(localFile)
  232.       except tarfile.ReadError as e:
  233.         self.log.Exit(str(e)+'\n'+failureMessage)
  234.       if not tf: self.log.Exit(failureMessage)
  235.       #git puts 'pax_global_header' as the first entry and some tar utils process this as a file
  236.       firstname = tf.getnames()[0]
  237.       if firstname == 'pax_global_header':
  238.         firstmember = tf.getmembers()[1]
  239.       else:
  240.         firstmember = tf.getmembers()[0]
  241.       # some tarfiles list packagename/ but some list packagename/filename in the first entry
  242.       if firstmember.isdir():
  243.         dirname = firstmember.name
  244.       else:
  245.         dirname = os.path.dirname(firstmember.name)
  246.       tf.extractall(path=externdir)
  247.       tf.close()
  248.  
  249.       # fix file permissions for the untared tarballs
  250.       try:
  251.         # check if 'dirname' is set'
  252.         if dirname:
  253.           (result,output) = self.RunCommand('cd '+externdir+'; chmod -R a+r '+dirname+'; find '+dirname+' -type d -name "*" -exec chmod a+rx {} \;')
  254.         else:
  255.           self.log.Warn('Could not determine dirname extracted by '+localFile+' to fix file permissions')
  256.       except RuntimeError as e:
  257.         self.log.Exit('Error changing permissions for '+dirname+' obtained from '+localFile+ ' : '+str(e))
  258.       if not downloaddir:
  259.         os.remove(localFile)
  260.     return os.path.join(externdir,dirname)
  261.  
  262.   wd = 36
  263.  
  264.   def ShowHelp(self):
  265.     wd = Package.wd
  266.     if self.downloadable or self.installable:
  267.       print(self.packagename.upper()+':')
  268.     if self.downloadable:
  269.       print(('  --download-'+self.packagename+'[=<fname>]').ljust(wd)+': Download and install '+self.packagename.upper())
  270.       if self.hasdloadflags:
  271.         print(('  --download-'+self.packagename+'-cflags=<flags>').ljust(wd)+': Indicate extra flags to compile '+self.packagename.upper())
  272.     if self.installable:
  273.       print(('  --with-'+self.packagename+'=<bool>').ljust(wd)+': Test for '+self.packagename.upper()+(' (requires PETSc with %s)'%self.petscdepend.upper() if hasattr(self,'petscdepend') else ''))
  274.     if self.installable and not hasattr(self,'petscdepend'):
  275.       print(('  --with-'+self.packagename+'-dir=<dir>').ljust(wd)+': Indicate the root directory of the '+self.packagename.upper()+' installation')
  276.       print(('  --with-'+self.packagename+'-lib=<libraries>').ljust(wd)+': Indicate comma-separated libraries and link flags for '+self.packagename.upper())
  277.       if self.hasheaders:
  278.         print(('  --with-'+self.packagename+'-include=<dirs>').ljust(wd)+': Indicate the directory of the '+self.packagename.upper()+' include files')
  279.  
  280.   def ShowInfo(self):
  281.     if self.havepackage:
  282.       if hasattr(self,'version'):
  283.         packagename = self.packagename.upper()+' version '+self.version
  284.       else:
  285.         packagename = self.packagename.upper()
  286.       if hasattr(self,'petscdepend'):
  287.         self.log.Println(packagename+' from %s linked by PETSc' % self.petscdepend.upper())
  288.       elif hasattr(self,'packageflags'):
  289.         self.log.Println(packagename+' library flags:')
  290.         self.log.Println(' '+' '.join(self.packageflags))
  291.       else:
  292.         self.log.Println(packagename+' installed')
  293.  
  294.   def Link(self,functions,callbacks,flags,givencode='',cflags='',clanguage='c',logdump=True):
  295.  
  296.     # Create temporary directory and makefile
  297.     try:
  298.       tmpdir = tempfile.mkdtemp(prefix='slepc-')
  299.       if not os.path.isdir(tmpdir): os.mkdir(tmpdir)
  300.     except:
  301.       self.log.Exit('Cannot create temporary directory')
  302.     try:
  303.       makefile = open(os.path.join(tmpdir,'makefile'),'w')
  304.       if cflags!='':
  305.         if clanguage=='c++': makefile.write('CXXFLAGS='+cflags+'\n')
  306.         else: makefile.write('CFLAGS='+cflags+'\n')
  307.       makefile.write('checklink: checklink.o\n')
  308.       makefile.write('\t${CLINKER} -o checklink checklink.o ${LINKFLAGS} ${PETSC_SNES_LIB}\n')
  309.       makefile.write('\t@${RM} -f checklink checklink.o\n')
  310.       makefile.write('LOCDIR = ./\n')
  311.       makefile.write('include '+os.path.join('${PETSC_DIR}','lib','petsc','conf','variables')+'\n')
  312.       makefile.write('include '+os.path.join('${PETSC_DIR}','lib','petsc','conf','rules')+'\n')
  313.       makefile.close()
  314.     except:
  315.       self.log.Exit('Cannot create makefile in temporary directory')
  316.  
  317.     # Create source file
  318.     if givencode == '':
  319.       code = '#include "petscsnes.h"\n'
  320.       for f in functions:
  321.         code += 'PETSC_EXTERN int\n' + f + '();\n'
  322.  
  323.       for c in callbacks:
  324.         code += 'int '+ c + '() { return 0; } \n'
  325.  
  326.       code += 'int main() {\n'
  327.       code += 'Vec v; Mat m; KSP k;\n'
  328.       code += 'PetscInitializeNoArguments();\n'
  329.       code += 'VecCreate(PETSC_COMM_WORLD,&v);\n'
  330.       code += 'MatCreate(PETSC_COMM_WORLD,&m);\n'
  331.       code += 'KSPCreate(PETSC_COMM_WORLD,&k);\n'
  332.       for f in functions:
  333.         code += f + '();\n'
  334.       code += 'return 0;\n}\n'
  335.     else:
  336.       code = givencode
  337.  
  338.     cfile = open(os.path.join(tmpdir,'checklink.cxx' if clanguage=='c++' else 'checklink.c'),'w')
  339.     cfile.write(code)
  340.     cfile.close()
  341.     if logdump:
  342.       try:
  343.         self.log.write('- '*35+'\nChecking link with code:\n')
  344.         self.log.write(code)
  345.       except AttributeError: pass
  346.  
  347.     # Try to compile test program
  348.     (result, output) = self.RunCommand('cd ' + tmpdir + ';' + self.make + ' checklink LINKFLAGS="'+' '.join(flags)+'"')
  349.     shutil.rmtree(tmpdir)
  350.  
  351.     if result:
  352.       return (0,code + output)
  353.     else:
  354.       return (1,code + output)
  355.  
  356.   def FortranLink(self,functions,callbacks,flags):
  357.     output = '\n=== With linker flags: '+' '.join(flags)
  358.  
  359.     f = []
  360.     for i in functions:
  361.       f.append(i+'_')
  362.     c = []
  363.     for i in callbacks:
  364.       c.append(i+'_')
  365.     (result, output1) = self.Link(f,c,flags,logdump=False)
  366.     output1 = '\n====== With underscore Fortran names\n' + output1
  367.     if result: return ('UNDERSCORE',output1)
  368.  
  369.     f = []
  370.     for i in functions:
  371.       f.append(i.upper())
  372.     c = []
  373.     for i in callbacks:
  374.       c.append(i.upper())
  375.     (result, output2) = self.Link(f,c,flags,logdump=False)
  376.     output2 = '\n====== With capital Fortran names\n' + output2
  377.     if result: return ('CAPS',output2)
  378.  
  379.     return ('',output + output1 + output2)
  380.  
  381.   def GenerateGuesses(self,name,archdir,word='lib'):
  382.     installdirs = [os.path.join(os.path.sep,'usr','local'),os.path.join(os.path.sep,'opt')]
  383.     if 'HOME' in os.environ:
  384.       installdirs.insert(0,os.environ['HOME'])
  385.  
  386.     dirs = []
  387.     for i in installdirs:
  388.       dirs = dirs + [os.path.join(i,word)]
  389.       for d in [name,name.upper(),name.lower()]:
  390.         dirs = dirs + [os.path.join(i,d)]
  391.         dirs = dirs + [os.path.join(i,d,word)]
  392.         dirs = dirs + [os.path.join(i,word,d)]
  393.  
  394.     for d in dirs[:]:
  395.       if not os.path.exists(d):
  396.         dirs.remove(d)
  397.     dirs = [''] + dirs + [os.path.join(archdir,word)]
  398.     return dirs
  399.  
  400.   def FortranLib(self,slepcconf,slepcvars,dirs,libs,functions,callbacks = []):
  401.     name = self.packagename.upper()
  402.     error = ''
  403.     mangling = ''
  404.     flags = ['']
  405.     for d in dirs:
  406.       for l in libs:
  407.         if d:
  408.           if hasattr(self,'slflag'):
  409.             flags = [self.slflag + d] + ['-L' + d] + l
  410.           else:
  411.             flags = ['-L' + d] + l
  412.         else:
  413.           flags = l
  414.         (mangling, output) = self.FortranLink(functions,callbacks,flags)
  415.         error += output
  416.         if mangling: break
  417.       if mangling: break
  418.  
  419.     if mangling:
  420.       self.log.write(output)
  421.     else:
  422.       self.log.write(error)
  423.       self.log.Exit('Unable to link with '+name+' library in directories '+' '.join(dirs)+' with libraries and link flags '+' '.join(flags))
  424.  
  425.     slepcconf.write('#define SLEPC_HAVE_' + name + ' 1\n#define SLEPC_' + name + '_HAVE_'+mangling+' 1\n')
  426.     slepcvars.write(name + '_LIB = '+' '.join(flags)+'\n')
  427.     self.havepackage = True
  428.     self.packageflags = flags
  429.  
  430.   def WriteMakefile(self,fname,builddir,cont):
  431.     self.log.write('Using makefile:\n')
  432.     self.log.write(cont)
  433.     with open(os.path.join(builddir,fname),'w') as mfile:
  434.       mfile.write(cont)

Raw Paste


Login or Register to edit or fork this paste. It's free.