xref: /petsc/config/install.py (revision adf35c6e2d4d8722a4aa63e7a71b5a3c576bf800)
1#!/usr/bin/env python
2import os, re, shutil, sys
3
4if os.environ.has_key('PETSC_DIR'):
5  PETSC_DIR = os.environ['PETSC_DIR']
6else:
7  fd = file(os.path.join('lib','petsc','conf','petscvariables'))
8  a = fd.readline()
9  a = fd.readline()
10  PETSC_DIR = a.split('=')[1][0:-1]
11  fd.close()
12
13if os.environ.has_key('PETSC_ARCH'):
14  PETSC_ARCH = os.environ['PETSC_ARCH']
15else:
16  fd = file(os.path.join('lib','petsc','conf','petscvariables'))
17  a = fd.readline()
18  PETSC_ARCH = a.split('=')[1][0:-1]
19  fd.close()
20
21print '*** Using PETSC_DIR='+PETSC_DIR+' PETSC_ARCH='+PETSC_ARCH+' ***'
22sys.path.insert(0, os.path.join(PETSC_DIR, 'config'))
23sys.path.insert(0, os.path.join(PETSC_DIR, 'config', 'BuildSystem'))
24
25import script
26
27try:
28  WindowsError
29except NameError:
30  WindowsError = None
31
32class Installer(script.Script):
33  def __init__(self, clArgs = None):
34    import RDict
35    argDB = RDict.RDict(None, None, 0, 0, readonly = True)
36    argDB.saveFilename = os.path.join(PETSC_DIR, PETSC_ARCH, 'lib','petsc','conf', 'RDict.db')
37    argDB.load()
38    script.Script.__init__(self, argDB = argDB)
39    if not clArgs is None: self.clArgs = clArgs
40    self.copies = []
41    return
42
43  def setupHelp(self, help):
44    import nargs
45    script.Script.setupHelp(self, help)
46    help.addArgument('Installer', '-destDir=<path>', nargs.Arg(None, None, 'Destination Directory for install'))
47    return
48
49
50  def setupModules(self):
51    self.setCompilers  = self.framework.require('config.setCompilers',         None)
52    self.arch          = self.framework.require('PETSc.options.arch',          None)
53    self.petscdir      = self.framework.require('PETSc.options.petscdir',      None)
54    self.compilers     = self.framework.require('config.compilers',            None)
55    self.mpi           = self.framework.require('config.packages.MPI',         None)
56    return
57
58  def setup(self):
59    script.Script.setup(self)
60    self.framework = self.loadConfigure()
61    self.setupModules()
62    return
63
64  def setupDirectories(self):
65    self.rootDir    = self.petscdir.dir
66    self.installDir = os.path.abspath(os.path.expanduser(self.framework.argDB['prefix']))
67    self.destDir    = os.path.abspath(self.argDB['destDir']+self.installDir)
68    self.arch       = self.arch.arch
69    self.archDir           = os.path.join(self.rootDir, self.arch)
70    self.rootIncludeDir    = os.path.join(self.rootDir, 'include')
71    self.archIncludeDir    = os.path.join(self.rootDir, self.arch, 'include')
72    self.rootConfDir       = os.path.join(self.rootDir, 'lib','petsc','conf')
73    self.archConfDir       = os.path.join(self.rootDir, self.arch, 'lib','petsc','conf')
74    self.rootBinDir        = os.path.join(self.rootDir, 'lib','petsc','bin')
75    self.archBinDir        = os.path.join(self.rootDir, self.arch, 'bin')
76    self.archLibDir        = os.path.join(self.rootDir, self.arch, 'lib')
77    self.destIncludeDir    = os.path.join(self.destDir, 'include')
78    self.destConfDir       = os.path.join(self.destDir, 'lib','petsc','conf')
79    self.destLibDir        = os.path.join(self.destDir, 'lib')
80    self.destBinDir        = os.path.join(self.destDir, 'lib','petsc','bin')
81    self.installIncludeDir = os.path.join(self.installDir, 'include')
82    self.installBinDir     = os.path.join(self.installDir, 'lib','petsc','bin')
83    self.rootShareDir      = os.path.join(self.rootDir, 'share')
84    self.destShareDir      = os.path.join(self.destDir, 'share')
85    self.rootSrcDir        = os.path.join(self.rootDir, 'src')
86
87    self.ranlib      = self.compilers.RANLIB
88    self.arLibSuffix = self.compilers.AR_LIB_SUFFIX
89    return
90
91  def checkPrefix(self):
92    if not self.installDir:
93      print '********************************************************************'
94      print 'PETSc is built without prefix option. So "make install" is not appropriate.'
95      print 'If you need a prefix install of PETSc - rerun configure with --prefix option.'
96      print '********************************************************************'
97      sys.exit(1)
98    return
99
100  def checkDestdir(self):
101    if os.path.exists(self.destDir):
102      if os.path.samefile(self.destDir, self.rootDir):
103        print '********************************************************************'
104        print 'Incorrect prefix usage. Specified destDir same as current PETSC_DIR'
105        print '********************************************************************'
106        sys.exit(1)
107      if os.path.samefile(self.destDir, os.path.join(self.rootDir,self.arch)):
108        print '********************************************************************'
109        print 'Incorrect prefix usage. Specified destDir same as current PETSC_DIR/PETSC_ARCH'
110        print '********************************************************************'
111        sys.exit(1)
112      if not os.path.isdir(os.path.realpath(self.destDir)):
113        print '********************************************************************'
114        print 'Specified destDir', self.destDir, 'is not a directory. Cannot proceed!'
115        print '********************************************************************'
116        sys.exit(1)
117      if not os.access(self.destDir, os.W_OK):
118        print '********************************************************************'
119        print 'Unable to write to ', self.destDir, 'Perhaps you need to do "sudo make install"'
120        print '********************************************************************'
121        sys.exit(1)
122    return
123
124  def copyfile(self, src, dst, symlinks = False, copyFunc = shutil.copy2):
125    """Copies a single file    """
126    copies = []
127    errors = []
128    if not os.path.exists(dst):
129      os.makedirs(dst)
130    elif not os.path.isdir(dst):
131      raise shutil.Error, 'Destination is not a directory'
132    srcname = src
133    dstname = os.path.join(dst, os.path.basename(src))
134    try:
135      if symlinks and os.path.islink(srcname):
136        linkto = os.readlink(srcname)
137        os.symlink(linkto, dstname)
138      else:
139        copyFunc(srcname, dstname)
140        copies.append((srcname, dstname))
141    except (IOError, os.error), why:
142      errors.append((srcname, dstname, str(why)))
143    except shutil.Error, err:
144      errors.extend((srcname,dstname,str(err.args[0])))
145    if errors:
146      raise shutil.Error, errors
147    return copies
148
149  def fixExamplesMakefile(self, src):
150    '''Change ././${PETSC_ARCH} in makefile in root petsc directory with ${PETSC_DIR}'''
151    lines   = []
152    oldFile = open(src, 'r')
153    alllines=oldFile.read()
154    oldFile.close()
155    newlines=alllines.split('\n')[0]+'\n'  # Firstline
156    # Hardcode PETSC_DIR and PETSC_ARCH to avoid users doing the worng thing
157    newlines+='PETSC_DIR='+self.installDir+'\n'
158    newlines+='PETSC_ARCH=\n'
159    for line in alllines.split('\n')[1:]:
160      if line.startswith('TESTLOGFILE'):
161        newlines+='TESTLOGFILE = $(TESTDIR)/examples-install.log\n'
162      elif line.startswith('CONFIGDIR'):
163        newlines+='CONFIGDIR:=$(PETSC_DIR)/$(PETSC_ARCH)/share/petsc/examples/config\n'
164      elif line.startswith('EXAMPLESDIR'):
165        newlines+='EXAMPLESDIR:=$(PETSC_DIR)/$(PETSC_ARCH)/share/petsc/examples\n'
166      elif line.startswith('$(generatedtest)') and 'petscvariables' in line:
167        newlines+='all: test\n\n'+line+'\n'
168      else:
169        newlines+=line+'\n'
170    newFile = open(src, 'w')
171    newFile.write(newlines)
172    newFile.close()
173    return
174
175  def copyConfig(self, src, dst):
176    """Recursively copy the examples directories
177    """
178    if not os.path.isdir(dst):
179      raise shutil.Error, 'Destination is not a directory'
180
181    self.copies.extend(self.copyfile('gmakefile.test',dst))
182    newConfigDir=os.path.join(dst,'config')  # Am not renaming at present
183    if not os.path.isdir(newConfigDir): os.mkdir(newConfigDir)
184    testConfFiles="gmakegentest.py gmakegen.py testparse.py example_template.py".split()
185    testConfFiles+="petsc_harness.sh report_tests.py watchtime.sh".split()
186    testConfFiles+=["cmakegen.py"]
187    for tf in testConfFiles:
188      self.copies.extend(self.copyfile(os.path.join('config',tf),newConfigDir))
189    return
190
191  def copyExamples(self, src, dst):
192    """copy the examples directories
193    """
194    top=os.path.relpath(src,os.path.abspath(os.curdir))
195    for root, dirs, files in os.walk(top, topdown=False):
196        if not os.path.basename(root) == "examples": continue
197        self.copies.extend(self.copytree(root, os.path.join(dst,root),exclude_ext=['DSYM']))
198    return
199
200  def copytree(self, src, dst, symlinks = False, copyFunc = shutil.copy2, exclude = [], exclude_ext= [], recurse = 1):
201    """Recursively copy a directory tree using copyFunc, which defaults to shutil.copy2().
202
203       The copyFunc() you provide is only used on the top level, lower levels always use shutil.copy2
204
205    The destination directory must not already exist.
206    If exception(s) occur, an shutil.Error is raised with a list of reasons.
207
208    If the optional symlinks flag is true, symbolic links in the
209    source tree result in symbolic links in the destination tree; if
210    it is false, the contents of the files pointed to by symbolic
211    links are copied.
212    """
213    copies = []
214    names  = os.listdir(src)
215    if not os.path.exists(dst):
216      os.makedirs(dst)
217    elif not os.path.isdir(dst):
218      raise shutil.Error, 'Destination is not a directory'
219    errors = []
220    for name in names:
221      srcname = os.path.join(src, name)
222      dstname = os.path.join(dst, name)
223      try:
224        if symlinks and os.path.islink(srcname):
225          linkto = os.readlink(srcname)
226          os.symlink(linkto, dstname)
227        elif os.path.isdir(srcname) and recurse and not os.path.basename(srcname) in exclude:
228          copies.extend(self.copytree(srcname, dstname, symlinks,exclude = exclude, exclude_ext = exclude_ext))
229        elif os.path.isfile(srcname) and not os.path.basename(srcname) in exclude and os.path.splitext(name)[1] not in exclude_ext :
230          copyFunc(srcname, dstname)
231          copies.append((srcname, dstname))
232        # XXX What about devices, sockets etc.?
233      except (IOError, os.error), why:
234        errors.append((srcname, dstname, str(why)))
235      # catch the Error from the recursive copytree so that we can
236      # continue with other files
237      except shutil.Error, err:
238        errors.extend((srcname,dstname,str(err.args[0])))
239    try:
240      shutil.copystat(src, dst)
241    except OSError, e:
242      if WindowsError is not None and isinstance(e, WindowsError):
243        # Copying file access times may fail on Windows
244        pass
245      else:
246        errors.extend((src, dst, str(e)))
247    if errors:
248      raise shutil.Error, errors
249    return copies
250
251
252  def fixConfFile(self, src):
253    lines   = []
254    oldFile = open(src, 'r')
255    for line in oldFile.readlines():
256      # paths generated by configure could be different link-path than whats used by user, so fix both
257      line = line.replace(os.path.join(self.rootDir, self.arch), self.installDir)
258      line = line.replace(os.path.realpath(os.path.join(self.rootDir, self.arch)), self.installDir)
259      line = line.replace(os.path.join(self.rootDir, 'bin'), self.installBinDir)
260      line = line.replace(os.path.realpath(os.path.join(self.rootDir, 'bin')), self.installBinDir)
261      line = line.replace(os.path.join(self.rootDir, 'include'), self.installIncludeDir)
262      line = line.replace(os.path.realpath(os.path.join(self.rootDir, 'include')), self.installIncludeDir)
263      # remove PETSC_DIR/PETSC_ARCH variables from conf-makefiles. They are no longer necessary
264      line = line.replace('${PETSC_DIR}/${PETSC_ARCH}', self.installDir)
265      line = line.replace('PETSC_ARCH=${PETSC_ARCH}', '')
266      line = line.replace('${PETSC_DIR}', self.installDir)
267      lines.append(line)
268    oldFile.close()
269    newFile = open(src, 'w')
270    newFile.write(''.join(lines))
271    newFile.close()
272    return
273
274  def fixConf(self):
275    import shutil
276    for file in ['rules', 'variables','petscrules', 'petscvariables']:
277      self.fixConfFile(os.path.join(self.destConfDir,file))
278    self.fixConfFile(os.path.join(self.destLibDir,'pkgconfig','PETSc.pc'))
279    return
280
281  def createUninstaller(self):
282    uninstallscript = os.path.join(self.destConfDir, 'uninstall.py')
283    f = open(uninstallscript, 'w')
284    # Could use the Python AST to do this
285    f.write('#!'+sys.executable+'\n')
286    f.write('import os\n')
287    f.write('prefixdir = "'+self.installDir+'"\n')
288    files = [dst.replace(self.destDir,self.installDir) for src, dst in self.copies]
289    files.append(uninstallscript.replace(self.destDir,self.installDir))
290    f.write('files = '+repr(files))
291    f.write('''
292for file in files:
293  if os.path.exists(file) or os.path.islink(file):
294    os.remove(file)
295    dir = os.path.dirname(file)
296    while dir not in [prefixdir,'/']:
297      if not os.listdir(dir):
298        os.rmdir(dir)
299      dir = os.path.dirname(dir)
300''')
301    f.close()
302    os.chmod(uninstallscript,0744)
303    return
304
305  def installIncludes(self):
306    exclude = ['makefile']
307    if not hasattr(self.compilers.setCompilers, 'FC'):
308      exclude.append('finclude')
309    if not self.mpi.usingMPIUni:
310      exclude.append('mpiuni')
311    self.copies.extend(self.copytree(self.rootIncludeDir, self.destIncludeDir,exclude = exclude))
312    self.copies.extend(self.copytree(self.archIncludeDir, self.destIncludeDir))
313    return
314
315  def installConf(self):
316    self.copies.extend(self.copytree(self.rootConfDir, self.destConfDir, exclude = ['uncrustify.cfg','bfort-base.txt','bfort-petsc.txt','bfort-mpi.txt','test.log']))
317    self.copies.extend(self.copytree(self.archConfDir, self.destConfDir, exclude = ['sowing', 'configure.log.bkp','configure.log','make.log','gmake.log','test.log','error.log']))
318    return
319
320  def installBin(self):
321    exclude = ['bfort','bib2html','doc2lt','doctext','mapnames', 'pstogif','pstoxbm','tohtml']
322    self.copies.extend(self.copytree(self.archBinDir, self.destBinDir, exclude = exclude ))
323    exclude = ['maint']
324    if not self.mpi.usingMPIUni:
325      exclude.append('petsc-mpiexec.uni')
326    self.setCompilers.pushLanguage('C')
327    if not self.setCompilers.isWindows(self.setCompilers.getCompiler(),self.log):
328      exclude.append('win32fe')
329    self.setCompilers.popLanguage()
330    self.copies.extend(self.copytree(self.rootBinDir, self.destBinDir, exclude = exclude ))
331    return
332
333  def installShare(self):
334    self.copies.extend(self.copytree(self.rootShareDir, self.destShareDir))
335    examplesdir=os.path.join(self.destShareDir,'petsc','examples')
336    if os.path.exists(examplesdir):
337      shutil.rmtree(examplesdir)
338    os.mkdir(examplesdir)
339    os.mkdir(os.path.join(examplesdir,'src'))
340    self.copyExamples(self.rootSrcDir,examplesdir)
341    self.copyConfig(self.rootDir,examplesdir)
342    self.fixExamplesMakefile(os.path.join(examplesdir,'gmakefile.test'))
343    return
344
345  def copyLib(self, src, dst):
346    '''Run ranlib on the destination library if it is an archive. Also run install_name_tool on dylib on Mac'''
347    # Symlinks (assumed local) are recreated at dst
348    if os.path.islink(src):
349      linkto = os.readlink(src)
350      try:
351        os.remove(dst)            # In case it already exists
352      except OSError:
353        pass
354      os.symlink(linkto, dst)
355      return
356    # Do not install object files
357    if not os.path.splitext(src)[1] == '.o':
358      shutil.copy2(src, dst)
359    if os.path.splitext(dst)[1] == '.'+self.arLibSuffix:
360      self.executeShellCommand(self.ranlib+' '+dst)
361    if os.path.splitext(dst)[1] == '.dylib' and os.path.isfile('/usr/bin/install_name_tool'):
362      [output,err,flg] = self.executeShellCommand("otool -D "+src)
363      oldname = output[output.find("\n")+1:]
364      installName = oldname.replace(self.archDir, self.installDir)
365      self.executeShellCommand('/usr/bin/install_name_tool -id ' + installName + ' ' + dst)
366    # preserve the original timestamps - so that the .a vs .so time order is preserved
367    shutil.copystat(src,dst)
368    return
369
370  def installLib(self):
371    self.copies.extend(self.copytree(self.archLibDir, self.destLibDir, copyFunc = self.copyLib, exclude = ['.DIR'],recurse = 0))
372    self.copies.extend(self.copytree(os.path.join(self.archLibDir,'pkgconfig'), os.path.join(self.destLibDir,'pkgconfig'), copyFunc = self.copyLib, exclude = ['.DIR'],recurse = 0))
373    return
374
375
376  def outputInstallDone(self):
377    print '''\
378====================================
379Install complete.
380Now to check if the libraries are working do (in current directory):
381make PETSC_DIR=%s PETSC_ARCH="" test
382====================================\
383''' % (self.installDir)
384    return
385
386  def outputDestDirDone(self):
387    print '''\
388====================================
389Copy to DESTDIR %s is now complete.
390Before use - please copy/install over to specified prefix: %s
391====================================\
392''' % (self.destDir,self.installDir)
393    return
394
395  def runsetup(self):
396    self.setup()
397    self.setupDirectories()
398    self.checkPrefix()
399    self.checkDestdir()
400    return
401
402  def runcopy(self):
403    if self.destDir == self.installDir:
404      print '*** Installing PETSc at prefix location:',self.destDir, ' ***'
405    else:
406      print '*** Copying PETSc to DESTDIR location:',self.destDir, ' ***'
407    if not os.path.exists(self.destDir):
408      try:
409        os.makedirs(self.destDir)
410      except:
411        print '********************************************************************'
412        print 'Unable to create', self.destDir, 'Perhaps you need to do "sudo make install"'
413        print '********************************************************************'
414        sys.exit(1)
415    self.installIncludes()
416    self.installConf()
417    self.installBin()
418    self.installLib()
419    self.installShare()
420    return
421
422  def runfix(self):
423    self.fixConf()
424    return
425
426  def rundone(self):
427    self.createUninstaller()
428    if self.destDir == self.installDir:
429      self.outputInstallDone()
430    else:
431      self.outputDestDirDone()
432    return
433
434  def run(self):
435    self.runsetup()
436    self.runcopy()
437    self.runfix()
438    self.rundone()
439    return
440
441if __name__ == '__main__':
442  Installer(sys.argv[1:]).run()
443  # temporary hack - delete log files created by BuildSystem - when 'sudo make install' is invoked
444  delfiles=['RDict.db','RDict.log','buildsystem.log','default.log','buildsystem.log.bkp','default.log.bkp']
445  for delfile in delfiles:
446    if os.path.exists(delfile) and (os.stat(delfile).st_uid==0):
447      os.remove(delfile)
448