xref: /petsc/config/install.py (revision ef7bb5aa64da98453fa5fb4571451d73f643854f)
1#!/usr/bin/env python
2import re, os, sys, shutil
3
4configDir = os.path.abspath('config')
5sys.path.insert(0, configDir)
6bsDir     = os.path.abspath(os.path.join(configDir, 'BuildSystem'))
7sys.path.insert(0, bsDir)
8
9import script
10
11class Installer(script.Script):
12  def __init__(self, clArgs = None):
13    import RDict
14    script.Script.__init__(self, clArgs, RDict.RDict())
15    self.copies = []
16    return
17
18  def setupHelp(self, help):
19    import nargs
20
21    script.Script.setupHelp(self, help)
22    help.addArgument('Installer', '-rootDir=<path>', nargs.Arg(None, None, 'Install Root Directory'))
23    help.addArgument('Installer', '-installDir=<path>', nargs.Arg(None, None, 'Install Target Directory'))
24    help.addArgument('Installer', '-arch=<type>', nargs.Arg(None, None, 'Architecture type'))
25    help.addArgument('Installer', '-ranlib=<prog>', nargs.Arg(None, 'ranlib', 'Ranlib program'))
26    help.addArgument('Installer', '-make=<prog>', nargs.Arg(None, 'make', 'Make program'))
27    help.addArgument('Installer', '-libSuffix=<ext>', nargs.Arg(None, 'make', 'The static library suffix'))
28    return
29
30  def setupDirectories(self):
31    self.rootDir    = os.path.abspath(self.argDB['rootDir'])
32    self.installDir = os.path.abspath(self.argDB['installDir'])
33    self.arch       = self.argDB['arch']
34    self.rootIncludeDir    = os.path.join(self.rootDir, 'include')
35    self.archIncludeDir    = os.path.join(self.rootDir, self.arch, 'include')
36    self.rootConfDir       = os.path.join(self.rootDir, 'conf')
37    self.archConfDir       = os.path.join(self.rootDir, self.arch, 'conf')
38    self.rootBinDir        = os.path.join(self.rootDir, 'bin')
39    self.archBinDir        = os.path.join(self.rootDir, self.arch, 'bin')
40    self.archLibDir        = os.path.join(self.rootDir, self.arch, 'lib')
41    self.installIncludeDir = os.path.join(self.installDir, 'include')
42    self.installConfDir    = os.path.join(self.installDir, 'conf')
43    self.installLibDir     = os.path.join(self.installDir, 'lib')
44    self.installBinDir     = os.path.join(self.installDir, 'bin')
45    #Check for options db
46    dbpath = os.path.join(self.rootDir, self.arch, 'RDict.db')
47    if not os.path.isfile(dbpath):
48      self.ranlib     = os.path.abspath(self.argDB['ranlib'])
49      self.make       = os.path.abspath(self.argDB['make'])
50      self.libSuffix  = self.argDB['libSuffix']
51    else:
52      import cPickle
53      framework = cPickle.loads(dbpath)
54      m = framework.require('PETSc.utilities.Make', None)
55      self.make = m.make+' '+m.flags
56      m = framework.require('config.compilers', None)
57      self.ranlib    = m.RANLIB
58      self.libSuffix = m.AR_LIB_SUFFIX
59    return
60
61  def copytree(self, src, dst, symlinks = False, copyFunc = shutil.copy2):
62    """Recursively copy a directory tree using copyFunc, which defaults to shutil.copy2().
63
64    The destination directory must not already exist.
65    If exception(s) occur, an shutil.Error is raised with a list of reasons.
66
67    If the optional symlinks flag is true, symbolic links in the
68    source tree result in symbolic links in the destination tree; if
69    it is false, the contents of the files pointed to by symbolic
70    links are copied.
71    """
72    copies = []
73    names  = os.listdir(src)
74    if not os.path.exists(dst):
75      os.makedirs(dst)
76    elif not os.path.isdir(dst):
77      raise shutil.Error, 'Destination is not a directory'
78    errors = []
79    for name in names:
80      srcname = os.path.join(src, name)
81      dstname = os.path.join(dst, name)
82      try:
83        if symlinks and os.path.islink(srcname):
84          linkto = os.readlink(srcname)
85          os.symlink(linkto, dstname)
86        elif os.path.isdir(srcname):
87          copies.extend(self.copytree(srcname, dstname, symlinks))
88        else:
89          copyFunc(srcname, dstname)
90          copies.append((srcname, dstname))
91        # XXX What about devices, sockets etc.?
92      except (IOError, os.error), why:
93        errors.append((srcname, dstname, str(why)))
94      # catch the Error from the recursive copytree so that we can
95      # continue with other files
96      except shutil.Error, err:
97        errors.extend(err.args[0])
98    try:
99      shutil.copystat(src, dst)
100    except WindowsError:
101      # can't copy file access times on Windows
102      pass
103    except OSError, why:
104      errors.extend((src, dst, str(why)))
105    if errors:
106      raise shutil.Error, errors
107    return copies
108
109  def installIncludes(self):
110    self.copies.extend(self.copytree(self.rootIncludeDir, self.installIncludeDir))
111    self.copies.extend(self.copytree(self.archIncludeDir, self.installIncludeDir))
112    return
113
114  def copyConf(self, src, dst):
115    if os.path.isdir(dst):
116      dst = os.path.join(dst, os.path.basename(src))
117    lines   = []
118    oldFile = open(src, 'r')
119    for line in oldFile.readlines():
120      # paths generated by configure could be different link-path than whats used by user, so fix both
121      line = re.sub(re.escape(os.path.join(self.rootDir, self.arch)), self.installDir, line)
122      line = re.sub(re.escape(os.path.realpath(os.path.join(self.rootDir, self.arch))), self.installDir, line)
123      line = re.sub(re.escape(os.path.join(self.rootDir, 'bin')), self.installDir, line)
124      line = re.sub(re.escape(os.path.realpath(os.path.join(self.rootDir, 'bin'))), self.installDir, line)
125      # remove PETSC_DIR/PETSC_ARCH variables from conf-makefiles. They are no longer necessary
126      line = re.sub('\$\{PETSC_DIR\}/\$\{PETSC_ARCH\}', self.installDir, line)
127      line = re.sub('PETSC_ARCH=\$\{PETSC_ARCH\}', '', line)
128      line = re.sub('\$\{PETSC_DIR\}', self.installDir, line)
129      lines.append(line)
130    oldFile.close()
131    newFile = open(dst, 'w')
132    newFile.write(''.join(lines))
133    newFile.close()
134    shutil.copystat(src, dst)
135    return
136
137  def installConf(self):
138    # rootConfDir can have a duplicate petscvariables - so processing it first removes the appropriate duplicate file.
139    self.copies.extend(self.copytree(self.rootConfDir, self.installConfDir, copyFunc = self.copyConf))
140    self.copies.extend(self.copytree(self.archConfDir, self.installConfDir))
141    # Just copyConf() a couple of files manually [as the rest of the files should not be modified]
142    for file in ['petscrules', 'petscvariables']:
143      self.copyConf(os.path.join(self.archConfDir,file),os.path.join(self.installConfDir,file))
144    return
145
146  def installBin(self):
147    self.copies.extend(self.copytree(self.rootBinDir, self.installBinDir))
148    self.copies.extend(self.copytree(self.archBinDir, self.installBinDir))
149    return
150
151  def copyLib(self, src, dst):
152    '''Run ranlib on the destination library if it is an archive'''
153    shutil.copy2(src, dst)
154    if os.path.splitext(dst)[1] == '.'+self.libSuffix:
155      self.executeShellCommand(self.ranlib+' '+dst)
156    return
157
158  def installLib(self):
159    self.copies.extend(self.copytree(self.archLibDir, self.installLibDir, copyFunc = self.copyLib))
160    return
161
162  def createUninstaller(self):
163    uninstallscript = os.path.join(self.installConfDir, 'uninstall.py')
164    f = open(uninstallscript, 'w')
165    # Could use the Python AST to do this
166    f.write('#!'+sys.executable+'\n')
167    f.write('import os\n')
168
169    f.write('copies = '+repr(self.copies))
170    f.write('''
171for src, dst in copies:
172  if os.path.exists(dst):
173    os.remove(dst)
174''')
175    f.close()
176    os.chmod(uninstallscript,0744)
177    return
178
179  def outputHelp(self):
180    print '''
181====================================
182If using sh/bash, do the following:
183  PETSC_DIR=%s; export PETSC_DIR
184  unset PETSC_ARCH
185If using csh/tcsh, do the following:
186  setenv PETSC_DIR %s
187  unsetenv PETSC_ARCH
188Run the following to verify the install (remain in current directory for the tests):
189  make test
190====================================
191''' % (self.installDir, self.installDir)
192    return
193
194  def run(self):
195    self.setup()
196    self.setupDirectories()
197    if os.path.exists(self.installDir) and os.path.samefile(self.installDir, os.path.join(self.rootDir,self.arch)):
198      print '********************************************************************'
199      print 'Install directory is current directory; nothing needs to be done'
200      print '********************************************************************'
201      return
202    print '*** Installing PETSc at',self.installDir, ' ***'
203    if not os.path.exists(self.installDir):
204      try:
205        os.makedirs(self.installDir)
206      except:
207        print '********************************************************************'
208        print 'Unable to create', self.installDir, 'Perhaps you need to do "sudo make install"'
209        print '********************************************************************'
210        return
211    if not os.path.isdir(os.path.realpath(self.installDir)):
212      print '********************************************************************'
213      print 'Specified prefix', self.installDir, 'is not a directory. Cannot proceed!'
214      print '********************************************************************'
215      return
216    if not os.access(self.installDir, os.W_OK):
217      print '********************************************************************'
218      print 'Unable to write to ', self.installDir, 'Perhaps you need to do "sudo make install"'
219      print '********************************************************************'
220      return
221    self.installIncludes()
222    self.installConf()
223    self.installBin()
224    self.installLib()
225    output = self.executeShellCommand(self.make+' PETSC_ARCH=""'+' PETSC_DIR='+self.installDir+' shared mpi4py petsc4py')[0]
226    print output
227    self.createUninstaller()
228    self.outputHelp()
229    return
230
231if __name__ == '__main__':
232  Installer(sys.argv[1:]).run()
233  # temporary hack - delete log files created by BuildSystem - when 'sudo make install' is invoked
234  delfiles=['RDict.db','RDict.log','build.log','default.log','build.log.bkp','default.log.bkp']
235  for delfile in delfiles:
236    if os.path.exists(delfile) and (os.stat(delfile).st_uid==0):
237      os.remove(delfile)
238