1#!/usr/bin/env python 2 3""" 4PETSc: Portable, Extensible Toolkit for Scientific Computation 5============================================================== 6 7The Portable, Extensible Toolkit for Scientific Computation (PETSc), 8is a suite of data structures and routines for the scalable (parallel) 9solution of scientific applications modeled by partial differential 10equations. It employs the Message Passing Interface (MPI) standard for 11all message-passing communication. 12 13.. note:: 14 15 To install ``PETSc`` and ``petsc4py`` (``mpi4py`` is optional 16 but highly recommended) use:: 17 18 $ pip install numpy mpi4py 19 $ pip install petsc petsc4py 20 21.. tip:: 22 23 You can also install the in-development versions with:: 24 25 $ pip install Cython numpy mpi4py 26 $ pip install --no-deps https://gitlab.com/petsc/petsc/-/archive/master/petsc-master.tar.gz 27 $ pip install --no-deps https://bitbucket.org/petsc/petsc4py/get/master.tar.gz 28 29 To set the MPI compilers use the environmental variables ``MPICC``, ``MPICXX``, ``MPIF90``. 30 31 Provide any ``PETSc`` ./configure options using the environmental variable ``PETSC_CONFIGURE_OPTIONS``. 32 33 Do not use the ``PETSc`` ``./configure`` options ``--with-cc``, ``--with-cxx``, ``--with-fc``, or ``--with-mpi-dir``. 34 35 If ``mpi4py`` is installed the compilers will obtained from that installation and ``MPICC``, ``MPICXX``, ``MPIF90`` will be ignored. 36 37""" 38 39import sys, os 40from setuptools import setup 41from setuptools.command.install import install as _install 42from distutils.util import get_platform, split_quoted 43from distutils.spawn import find_executable 44from distutils import log 45 46init_py = """\ 47# Author: PETSc Team 48# Contact: petsc-maint@mcs.anl.gov 49 50def get_petsc_dir(): 51 import os 52 return os.path.dirname(__file__) 53 54def get_config(): 55 conf = {} 56 conf['PETSC_DIR'] = get_petsc_dir() 57 return conf 58""" 59 60metadata = { 61 'provides' : ['petsc'], 62 'zip_safe' : False, 63} 64 65CONFIGURE_OPTIONS = [] 66 67def bootstrap(): 68 # Set PETSC_DIR and PETSC_ARCH 69 PETSC_DIR = os.path.abspath(os.getcwd()) 70 PETSC_ARCH = 'arch-python-' + get_platform() 71 os.environ['PETSC_DIR'] = PETSC_DIR 72 os.environ['PETSC_ARCH'] = PETSC_ARCH 73 sys.path.insert(0, os.path.join(PETSC_DIR, 'config')) 74 sys.path.insert(0, os.path.join(PETSC_DIR, 'lib','petsc','conf')) 75 # Generate package __init__.py file 76 from distutils.dir_util import mkpath 77 pkgdir = os.path.join('config', 'pypi') 78 if not os.path.exists(pkgdir): mkpath(pkgdir) 79 pkgfile = os.path.join(pkgdir, '__init__.py') 80 fh = open(pkgfile, 'w') 81 fh.write(init_py) 82 fh.close() 83 # Configure options 84 options = os.environ.get('PETSC_CONFIGURE_OPTIONS', '') 85 CONFIGURE_OPTIONS.extend(split_quoted(options)) 86 for i in CONFIGURE_OPTIONS: 87 if i.startswith('--with-mpi-dir='): 88 raise RuntimeError("Do not use --with-mpi-dir, use the environmental variables MPICC, MPICXX, MPIF90") 89 if i.startswith('--with-cc='): 90 raise RuntimeError("Do not use --with-cc, use the environmental variable MPICC") 91 if i.startswith('--with-cxx=') and i != "--with-cxx=0": 92 raise RuntimeError("Do not use --with-cxx, use the environmental variable MPICXX") 93 if i.startswith('--with-fc=') and i != "--with-fc=0": 94 raise RuntimeError("Do not use --with-fc, use the environmental variable MPIF90") 95 96 if '--with-mpi=0' not in CONFIGURE_OPTIONS: 97 # Simple-minded lookup for MPI and mpi4py 98 mpi4py = mpicc = None 99 try: 100 import mpi4py 101 conf = mpi4py.get_config() 102 mpicc = conf.get('mpicc') 103 except ImportError: # mpi4py is not installed 104 mpi4py = None 105 mpicc = (os.environ.get('MPICC') or 106 find_executable('mpicc')) 107 except AttributeError: # mpi4py is too old 108 pass 109 if not mpi4py and mpicc: 110 metadata['install_requires'] = ['mpi4py>=1.2.2'] 111 112def config(prefix, dry_run=False): 113 log.info('PETSc: configure') 114 options = [ 115 '--prefix=' + prefix, 116 'PETSC_ARCH='+os.environ['PETSC_ARCH'], 117 '--with-shared-libraries=1', 118 '--with-debugging=0', 119 '--with-c2html=0', # not needed 120 ] 121 if '--with-fc=0' in CONFIGURE_OPTIONS: 122 options.append('--with-sowing=0') 123 if '--with-mpi=0' not in CONFIGURE_OPTIONS: 124 try: 125 import mpi4py 126 conf = mpi4py.get_config() 127 mpicc = conf.get('mpicc') 128 mpicxx = conf.get('mpicxx') 129 mpif90 = conf.get('mpif90') 130 except (ImportError, AttributeError): 131 mpicc = os.environ.get('MPICC') or find_executable('mpicc') 132 mpicxx = os.environ.get('MPICXX') or find_executable('mpicxx') 133 mpif90 = os.environ.get('MPIF90') or find_executable('mpif90') 134 if mpicc: 135 options.append('--with-cc='+mpicc) 136 if '--with-cxx=0' not in CONFIGURE_OPTIONS: 137 if mpicxx: 138 options.append('--with-cxx='+mpicxx) 139 else: 140 options.append('--with-cxx=0') 141 if '--with-fc=0' not in CONFIGURE_OPTIONS: 142 if mpif90: 143 options.append('--with-fc='+mpif90) 144 else: 145 options.append('--with-fc=0') 146 options.append('--with-sowing=0') 147 else: 148 options.append('--with-mpi=0') 149 options.extend(CONFIGURE_OPTIONS) 150 # 151 log.info('configure options:') 152 for opt in options: 153 log.info(' '*4 + opt) 154 # Run PETSc configure 155 if dry_run: return 156 use_config_py = False 157 if use_config_py: 158 import configure 159 configure.petsc_configure(options) 160 import logger 161 logger.Logger.defaultLog = None 162 else: 163 python = find_executable('python2') or find_executable('python') 164 command = [python, './configure'] + options 165 status = os.system(" ".join(command)) 166 if status != 0: raise RuntimeError(status) 167 168def build(dry_run=False): 169 log.info('PETSc: build') 170 # Run PETSc build 171 if dry_run: return 172 use_builder_py = False 173 if use_builder_py: 174 import builder 175 builder.PETScMaker().run() 176 import logger 177 logger.Logger.defaultLog = None 178 else: 179 make = find_executable('make') 180 command = [make, 'all'] 181 status = os.system(" ".join(command)) 182 if status != 0: raise RuntimeError(status) 183 184def install(dry_run=False): 185 log.info('PETSc: install') 186 # Run PETSc installer 187 if dry_run: return 188 use_install_py = False 189 if use_install_py: 190 import install 191 install.Installer().run() 192 import logger 193 logger.Logger.defaultLog = None 194 else: 195 make = find_executable('make') 196 command = [make, 'install'] 197 status = os.system(" ".join(command)) 198 if status != 0: raise RuntimeError(status) 199 200class context(object): 201 def __init__(self): 202 self.sys_argv = sys.argv[:] 203 self.wdir = os.getcwd() 204 def enter(self): 205 del sys.argv[1:] 206 pdir = os.environ['PETSC_DIR'] 207 os.chdir(pdir) 208 return self 209 def exit(self): 210 sys.argv[:] = self.sys_argv 211 os.chdir(self.wdir) 212 213class cmd_install(_install): 214 215 def initialize_options(self): 216 _install.initialize_options(self) 217 self.optimize = 1 218 219 def finalize_options(self): 220 _install.finalize_options(self) 221 self.install_lib = self.install_platlib 222 self.install_libbase = self.install_lib 223 224 def run(self): 225 root_dir = os.path.abspath(self.install_lib) 226 prefix = os.path.join(root_dir, 'petsc') 227 # 228 ctx = context().enter() 229 try: 230 config(prefix, self.dry_run) 231 build(self.dry_run) 232 install(self.dry_run) 233 finally: 234 ctx.exit() 235 # 236 self.outputs = [] 237 for dirpath, _, filenames in os.walk(prefix): 238 for fn in filenames: 239 self.outputs.append(os.path.join(dirpath, fn)) 240 # 241 _install.run(self) 242 243 def get_outputs(self): 244 outputs = getattr(self, 'outputs', []) 245 outputs += _install.get_outputs(self) 246 return outputs 247 248def version(): 249 import re 250 version_re = { 251 'major' : re.compile(r"#define\s+PETSC_VERSION_MAJOR\s+(\d+)"), 252 'minor' : re.compile(r"#define\s+PETSC_VERSION_MINOR\s+(\d+)"), 253 'micro' : re.compile(r"#define\s+PETSC_VERSION_SUBMINOR\s+(\d+)"), 254 'release': re.compile(r"#define\s+PETSC_VERSION_RELEASE\s+([-]*\d+)"), 255 } 256 petscversion_h = os.path.join('include','petscversion.h') 257 data = open(petscversion_h, 'r').read() 258 major = int(version_re['major'].search(data).groups()[0]) 259 minor = int(version_re['minor'].search(data).groups()[0]) 260 micro = int(version_re['micro'].search(data).groups()[0]) 261 release = int(version_re['release'].search(data).groups()[0]) 262 if release > 0 : 263 v = "%d.%d.%d" % (major, minor, micro) 264 else: 265 v = "%d.%d.0.dev%d" % (major, minor+1, 0) 266 return v 267 268def tarball(): 269 VERSION = version() 270 if '.dev' in VERSION: return None 271 return ('http://ftp.mcs.anl.gov/pub/petsc/release-snapshots//' 272 'petsc-lite-%s.tar.gz#egg=petsc-%s' % (VERSION, VERSION)) 273 274description = __doc__.split('\n')[1:-1]; del description[1:3] 275classifiers = """ 276Development Status :: 5 - Production/Stable 277Intended Audience :: Developers 278Intended Audience :: Science/Research 279License :: OSI Approved :: BSD License 280Operating System :: POSIX 281Programming Language :: C 282Programming Language :: C++ 283Programming Language :: Fortran 284Programming Language :: Python 285Topic :: Scientific/Engineering 286Topic :: Software Development :: Libraries 287""" 288 289if 'bdist_wheel' in sys.argv: 290 sys.stderr.write("petsc: this package cannot be built as a wheel\n") 291 sys.exit(1) 292 293bootstrap() 294setup(name='petsc', 295 version=version(), 296 description=description.pop(0), 297 long_description='\n'.join(description), 298 classifiers= classifiers.split('\n')[1:-1], 299 keywords = ['PETSc', 'MPI'], 300 platforms=['POSIX'], 301 license='BSD', 302 303 url='https://www.mcs.anl.gov/petsc/', 304 download_url=tarball(), 305 306 author='PETSc Team', 307 author_email='petsc-maint@mcs.anl.gov', 308 maintainer='Lisandro Dalcin', 309 maintainer_email='dalcinl@gmail.com', 310 311 packages = ['petsc'], 312 package_dir = {'petsc': 'config/pypi'}, 313 cmdclass={'install': cmd_install}, 314 **metadata) 315