xref: /petsc/config/install.py (revision 7a4fe282d1b349e95b3be72d69d8dd3d3bcd7bc6)
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       = os.path.abspath(self.argDB['arch'])
34    self.ranlib     = os.path.abspath(self.argDB['ranlib'])
35    self.make       = os.path.abspath(self.argDB['make'])
36    self.libSuffix  = os.path.abspath(self.argDB['libSuffix'])
37    self.rootIncludeDir    = os.path.join(self.rootDir, 'include')
38    self.archIncludeDir    = os.path.join(self.rootDir, self.arch, 'include')
39    self.rootConfDir       = os.path.join(self.rootDir, 'conf')
40    self.archConfDir       = os.path.join(self.rootDir, self.arch, 'conf')
41    self.rootBinDir        = os.path.join(self.rootDir, 'bin')
42    self.archBinDir        = os.path.join(self.rootDir, self.arch, 'bin')
43    self.archLibDir        = os.path.join(self.rootDir, self.arch, 'lib')
44    self.installIncludeDir = os.path.join(self.installDir, 'include')
45    self.installConfDir    = os.path.join(self.installDir, 'conf')
46    self.installLibDir     = os.path.join(self.installDir, 'lib')
47    self.installBinDir     = os.path.join(self.installDir, 'bin')
48    return
49
50  def copytree(self, src, dst, symlinks = False, copyFunc = shutil.copy2):
51    """Recursively copy a directory tree using copyFunc, which defaults to shutil.copy2().
52
53    The destination directory must not already exist.
54    If exception(s) occur, an shutil.Error is raised with a list of reasons.
55
56    If the optional symlinks flag is true, symbolic links in the
57    source tree result in symbolic links in the destination tree; if
58    it is false, the contents of the files pointed to by symbolic
59    links are copied.
60    """
61    copies = []
62    names  = os.listdir(src)
63    if not os.path.exists(dst):
64      os.makedirs(dst)
65    elif not os.path.isdir(dst):
66      raise shutil.Error, 'Destination is not a directory'
67    errors = []
68    for name in names:
69      srcname = os.path.join(src, name)
70      dstname = os.path.join(dst, name)
71      try:
72        if symlinks and os.path.islink(srcname):
73          linkto = os.readlink(srcname)
74          os.symlink(linkto, dstname)
75        elif os.path.isdir(srcname):
76          copies.extend(self.copytree(srcname, dstname, symlinks))
77        else:
78          copyFunc(srcname, dstname)
79          copies.append((srcname, dstname))
80        # XXX What about devices, sockets etc.?
81      except (IOError, os.error), why:
82        errors.append((srcname, dstname, str(why)))
83      # catch the Error from the recursive copytree so that we can
84      # continue with other files
85      except shutil.Error, err:
86        errors.extend(err.args[0])
87    try:
88      shutil.copystat(src, dst)
89    except WindowsError:
90      # can't copy file access times on Windows
91      pass
92    except OSError, why:
93      errors.extend((src, dst, str(why)))
94    if errors:
95      raise shutil.Error, errors
96    return copies
97
98  def installIncludes(self):
99    self.copies.extend(self.copytree(self.rootIncludeDir, self.installIncludeDir))
100    self.copies.extend(self.copytree(self.archIncludeDir, self.installIncludeDir))
101    return
102
103  def copyConf(self, src, dst):
104    if os.path.isdir(dst):
105      dst = os.path.join(dst, os.path.basename(src))
106    lines   = []
107    oldFile = open(src, 'r')
108    for line in oldFile.readlines():
109      line = re.sub(os.path.join(self.rootDir, self.arch), self.installDir, line)
110      line = re.sub(self.rootDir, self.installDir, line)
111      # paths generated by configure could be different link-path than whats used by user
112      line = re.sub(os.path.realpath(os.path.join(self.rootDir, self.arch)), self.installDir, line)
113      line = re.sub(os.path.realpath(self.rootDir), self.installDir, line)
114      # remove PETSC_DIR/PETSC_ARCH variables from conf-makefiles. They are no longer necessary
115      line = re.sub('\$\{PETSC_DIR\}/\$\{PETSC_ARCH\}', self.installDir, line)
116      line = re.sub('PETSC_ARCH=\$\{PETSC_ARCH\}', '', line)
117      line = re.sub('\$\{PETSC_DIR\}', self.installDir, line)
118      lines.append(line)
119    oldFile.close()
120    newFile = open(dst, 'w')
121    newFile.write(''.join(lines))
122    newFile.close()
123    shutil.copystat(src, dst)
124    return
125
126  def installConf(self):
127    self.copies.extend(self.copytree(self.rootConfDir, self.installConfDir, copyFunc = self.copyConf))
128    self.copies.extend(self.copytree(self.archConfDir, self.installConfDir, copyFunc = self.copyConf))
129    return
130
131  def installBin(self):
132    self.copies.extend(self.copytree(self.rootBinDir, self.installBinDir))
133    self.copies.extend(self.copytree(self.archBinDir, self.installBinDir))
134    return
135
136  def copyLib(self, src, dst):
137    '''Run ranlib on the destination library if it is an archive'''
138    shutil.copy2(src, dst)
139    if os.path.splitext(dst)[1] == '.'+self.libSuffix:
140      self.executeShellCommand(self.ranlib+' '+dst)
141    return
142
143  def installLib(self):
144    self.copies.extend(self.copytree(self.archLibDir, self.installLibDir, copyFunc = self.copyLib))
145    return
146
147  def createUninstaller(self):
148    uninstallscript = os.path.join(self.installConfDir, 'uninstall.py')
149    f = open(uninstallscript, 'w')
150    # Could use the Python AST to do this
151    f.write('#!'+sys.executable+'\n')
152    f.write('import os\n')
153
154    f.write('copies = '+repr(self.copies))
155    f.write('''
156for src, dst in copies:
157  if os.path.exists(dst):
158    os.remove(dst)
159''')
160    f.close()
161    os.chmod(uninstallscript,0744)
162    return
163
164  def outputHelp(self):
165    print '''
166If using sh/bash, do the following:
167  PETSC_DIR=%s; export PETSC_DIR
168  unset PETSC_ARCH
169If using csh/tcsh, do the following:
170  setenv PETSC_DIR %s
171  unsetenv PETSC_ARCH
172Now run the testsuite to verify the install with the following:
173  make test
174''' % (self.installDir, self.installDir)
175    return
176
177  def run(self):
178    self.setup()
179    self.setupDirectories()
180    if os.path.exists(self.installDir) and os.path.samefile(self.installDir, os.path.join(self.rootDir,self.arch)):
181      print '********************************************************************'
182      print 'Install directory is current directory; nothing needs to be done'
183      print '********************************************************************'
184      return
185    print 'Installing PETSc at',self.installDir
186    if not os.path.exists(self.installDir):
187      try:
188        os.makedirs(self.installDir)
189      except:
190        print '********************************************************************'
191        print 'Unable to create', self.installDir, 'Perhaps you need to do "sudo make install"'
192        print '********************************************************************'
193        return
194    if not os.path.isdir(os.path.realpath(self.installDir)):
195      print '********************************************************************'
196      print 'Specified prefix', self.installDir, 'is not a directory. Cannot proceed!'
197      print '********************************************************************'
198      return
199    if not os.access(self.installDir, os.W_OK):
200      print '********************************************************************'
201      print 'Unable to write to ', self.installDir, 'Perhaps you need to do "sudo make install"'
202      print '********************************************************************'
203      return
204    self.installIncludes()
205    self.installConf()
206    self.installBin()
207    self.installLib()
208    self.executeShellCommand(self.make+' PETSC_ARCH=""'+' PETSC_DIR='+self.installDir+' shared')
209    self.createUninstaller()
210    self.outputHelp()
211    return
212
213if __name__ == '__main__':
214  Installer(sys.argv[1:]).run()
215  # temporary hack - delete log files created by BuildSystem - when 'sudo make install' is invoked
216  delfiles=['RDict.db','RDict.log','build.log','default.log','build.log.bkp','default.log.bkp']
217  for delfile in delfiles:
218    if os.path.exists(delfile) and (os.stat(delfile).st_uid==0):
219      os.remove(delfile)
220