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