1#!/usr/bin/env python3 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 the ``PETSc`` and ``petsc4py`` packages use:: 16 17 $ python -m pip install numpy 18 $ python -m pip install petsc petsc4py 19 20.. tip:: 21 22 You can also install the in-development versions with:: 23 24 $ python -m pip install cython numpy 25 $ python -m pip install --no-deps https://gitlab.com/petsc/petsc/-/archive/main/petsc-main.tar.gz 26 27 Provide any ``PETSc`` ``./configure`` options using the environmental variable ``PETSC_CONFIGURE_OPTIONS``. 28 29 Do not use the ``PETSc`` ``./configure`` options ``--with-cc``, ``--with-cxx``, ``--with-fc``, or ``--with-mpi-dir``. 30 Compilers are detected from (in order): 1) the environmental variables ``MPICC``, ``MPICXX``, ``MPIFORT``, 31 or 2) from running the ``which mpicc``, ``which mpicxx``, ``which mpifort`` commands. 32 33""" 34 35import re 36import os 37import sys 38import shlex 39import shutil 40from setuptools import setup 41from setuptools.command.install import install as _install 42try: 43 from setuptools.command.bdist_wheel import bdist_wheel as _bdist_wheel 44except ImportError: 45 from wheel.bdist_wheel import bdist_wheel as _bdist_wheel 46from distutils import log 47 48init_py = """\ 49# Author: PETSc Team 50# Contact: petsc-maint@mcs.anl.gov 51 52def get_petsc_dir(): 53 import os 54 return os.path.dirname(__file__) 55 56 57def get_config(): 58 conf = {} 59 conf['PETSC_DIR'] = get_petsc_dir() 60 return conf 61""" 62 63main_py = """\ 64# Author: PETSc Team 65# Contact: petsc-maint@mcs.anl.gov 66 67if __name__ == "__main__": 68 import sys 69 if "--prefix" in sys.argv: 70 from . import get_petsc_dir 71 print(get_petsc_dir()) 72 del get_petsc_dir 73 del sys 74""" 75 76metadata = { 77 'provides' : ['petsc'], 78 'zip_safe' : False, 79} 80 81CONFIGURE_OPTIONS = [] 82 83 84def bootstrap(): 85 # Set PETSC_DIR and PETSC_ARCH 86 PETSC_DIR = os.path.abspath(os.getcwd()) 87 PETSC_ARCH = 'arch-python' 88 os.environ['PETSC_DIR'] = PETSC_DIR 89 os.environ['PETSC_ARCH'] = PETSC_ARCH 90 sys.path.insert(0, os.path.join(PETSC_DIR, 'config')) 91 sys.path.insert(0, os.path.join(PETSC_DIR, 'lib','petsc','conf')) 92 93 # Generate package __init__.py and __main__.py files 94 pkgdir = os.path.join('config', 'pypi') 95 os.makedirs(pkgdir, exist_ok=True) 96 for pyfile, contents in ( 97 ('__init__.py', init_py), 98 ('__main__.py', main_py), 99 ): 100 with open(os.path.join(pkgdir, pyfile), 'w') as fh: 101 fh.write(contents) 102 103 # Configure options 104 options = os.environ.get('PETSC_CONFIGURE_OPTIONS', '') 105 CONFIGURE_OPTIONS.extend(shlex.split(options)) 106 for i in CONFIGURE_OPTIONS: 107 if i.startswith('--with-mpi-dir='): 108 raise RuntimeError("Do not use --with-mpi-dir, use the environmental variables MPICC, MPICXX, MPIFORT") 109 if i.startswith('--with-cc='): 110 raise RuntimeError("Do not use --with-cc, use the environmental variable MPICC") 111 if i.startswith('--with-cxx=') and i != "--with-cxx=0": 112 raise RuntimeError("Do not use --with-cxx, use the environmental variable MPICXX") 113 if i.startswith('--with-fc=') and i != "--with-fc=0": 114 raise RuntimeError("Do not use --with-fc, use the environmental variable MPIFORT") 115 116 117 118def config(prefix): 119 log.info('PETSc: configure') 120 options = [ 121 '--prefix=' + prefix, 122 'PETSC_ARCH='+os.environ['PETSC_ARCH'], 123 '--with-shared-libraries=1', 124 '--with-c2html=0', # not needed 125 ] 126 if '--with-fc=0' in CONFIGURE_OPTIONS: 127 options.append('--with-sowing=0') 128 if '--with-debugging=1' not in CONFIGURE_OPTIONS: 129 options.append('--with-debugging=0') 130 if '--with-mpi=0' not in CONFIGURE_OPTIONS: 131 mpicc = os.environ.get('MPICC') or shutil.which('mpicc') 132 mpicxx = os.environ.get('MPICXX') or shutil.which('mpicxx') 133 mpifort = ( 134 os.environ.get('MPIFORT') 135 or os.environ.get('MPIF90') 136 or shutil.which('mpifort') 137 or shutil.which('mpif90') 138 ) 139 if mpicc: 140 options.append('--with-cc='+mpicc) 141 if '--with-cxx=0' not in CONFIGURE_OPTIONS: 142 if mpicxx: 143 options.append('--with-cxx='+mpicxx) 144 else: 145 options.append('--with-cxx=0') 146 if '--with-fc=0' not in CONFIGURE_OPTIONS: 147 if mpifort: 148 options.append('--with-fc='+mpifort) 149 else: 150 options.append('--with-fc=0') 151 options.append('--with-sowing=0') 152 else: 153 options.append('--with-mpi=0') 154 options.extend(CONFIGURE_OPTIONS) 155 # 156 log.info('configure options:') 157 for opt in options: 158 log.info(' '*4 + opt) 159 # Run PETSc configure 160 use_config_py = False 161 if use_config_py: 162 import configure 163 configure.petsc_configure(options) 164 import logger 165 logger.Logger.defaultLog = None 166 else: 167 python = sys.executable 168 command = [python, './configure'] + options 169 status = os.system(" ".join(command)) 170 if status != 0: 171 raise RuntimeError(status) 172 173 # Fix PETSc configuration 174 using_build_backend = any( 175 os.environ.get(prefix + '_BUILD_BACKEND') 176 for prefix in ('_PYPROJECT_HOOKS', 'PEP517') 177 ) 178 if using_build_backend: 179 pdir = os.environ['PETSC_DIR'] 180 parch = os.environ['PETSC_ARCH'] 181 include = os.path.join(pdir, parch, 'include') 182 for filename in ( 183 'petscconf.h', 184 'petscconfiginfo.h', 185 'petscmachineinfo.h', 186 ): 187 filename = os.path.join(include, filename) 188 with open(filename, 'r') as old_fh: 189 contents = old_fh.read() 190 contents = contents.replace(prefix, '${PETSC_DIR}') 191 contents = re.sub( 192 r'^(#define PETSC_PYTHON_EXE) "(.*)"$', 193 r'\1 "python%d"' % sys.version_info[0], 194 contents, flags=re.MULTILINE, 195 ) 196 with open(filename, 'w') as new_fh: 197 new_fh.write(contents) 198 199 200def build(): 201 log.info('PETSc: build') 202 # Run PETSc build 203 use_builder_py = False 204 if use_builder_py: 205 import builder 206 builder.PETScMaker().run() 207 import logger 208 logger.Logger.defaultLog = None 209 else: 210 make = shutil.which('make') 211 command = [make, 'all'] 212 status = os.system(" ".join(command)) 213 if status != 0: 214 raise RuntimeError(status) 215 216 217def install(): 218 log.info('PETSc: install') 219 # Run PETSc installer 220 use_install_py = False 221 if use_install_py: 222 import install 223 install.Installer().run() 224 import logger 225 logger.Logger.defaultLog = None 226 else: 227 make = shutil.which('make') 228 command = [make, 'install'] 229 status = os.system(" ".join(command)) 230 if status != 0: 231 raise RuntimeError(status) 232 233 234class context(object): 235 def __init__(self): 236 self.sys_argv = sys.argv[:] 237 self.wdir = os.getcwd() 238 def enter(self): 239 del sys.argv[1:] 240 pdir = os.environ['PETSC_DIR'] 241 os.chdir(pdir) 242 return self 243 def exit(self): 244 sys.argv[:] = self.sys_argv 245 os.chdir(self.wdir) 246 247 248class cmd_install(_install): 249 250 def initialize_options(self): 251 _install.initialize_options(self) 252 253 def finalize_options(self): 254 _install.finalize_options(self) 255 self.install_lib = self.install_platlib 256 self.install_libbase = self.install_lib 257 self.old_and_unmanageable = True 258 259 def run(self): 260 root_dir = os.path.abspath(self.install_lib) 261 prefix = os.path.join(root_dir, 'petsc') 262 # 263 ctx = context().enter() 264 try: 265 config(prefix) 266 build() 267 install() 268 finally: 269 ctx.exit() 270 # 271 self.outputs = [] 272 for dirpath, _, filenames in os.walk(prefix): 273 for fn in filenames: 274 self.outputs.append(os.path.join(dirpath, fn)) 275 # 276 _install.run(self) 277 278 def get_outputs(self): 279 outputs = getattr(self, 'outputs', []) 280 outputs += _install.get_outputs(self) 281 return outputs 282 283 284class cmd_bdist_wheel(_bdist_wheel): 285 286 def finalize_options(self): 287 super().finalize_options() 288 self.root_is_pure = False 289 self.build_number = None 290 # self.keep_temp = True 291 292 def get_tag(self): 293 plat_tag = super().get_tag()[-1] 294 return (self.python_tag, "none", plat_tag) 295 296 297def version(): 298 version_re = { 299 'major' : re.compile(r"#define\s+PETSC_VERSION_MAJOR\s+(\d+)"), 300 'minor' : re.compile(r"#define\s+PETSC_VERSION_MINOR\s+(\d+)"), 301 'micro' : re.compile(r"#define\s+PETSC_VERSION_SUBMINOR\s+(\d+)"), 302 'release': re.compile(r"#define\s+PETSC_VERSION_RELEASE\s+([-]*\d+)"), 303 } 304 petscversion_h = os.path.join('include','petscversion.h') 305 data = open(petscversion_h, 'r').read() 306 major = int(version_re['major'].search(data).groups()[0]) 307 minor = int(version_re['minor'].search(data).groups()[0]) 308 micro = int(version_re['micro'].search(data).groups()[0]) 309 release = int(version_re['release'].search(data).groups()[0]) 310 if release: 311 v = "%d.%d.%d" % (major, minor, micro) 312 else: 313 v = "%d.%d.0.dev%d" % (major, minor+1, 0) 314 return v 315 316 317def tarball(): 318 VERSION = version() 319 if '.dev' in VERSION: 320 return None 321 return ('https://web.cels.anl.gov/projects/petsc/download/release-snapshots/' 322 'petsc-%s.tar.gz#egg=petsc-%s' % (VERSION, VERSION)) 323 324 325description = __doc__.split('\n')[1:-1] 326del description[1:3] 327 328classifiers = """ 329Development Status :: 5 - Production/Stable 330Intended Audience :: Developers 331Intended Audience :: Science/Research 332Operating System :: POSIX 333Programming Language :: C 334Programming Language :: C++ 335Programming Language :: Fortran 336Programming Language :: Python 337Programming Language :: Python :: 3 338Topic :: Scientific/Engineering 339Topic :: Software Development :: Libraries 340""" 341 342bootstrap() 343setup( 344 name='petsc', 345 version=version(), 346 description=description.pop(0), 347 long_description='\n'.join(description), 348 long_description_content_type='text/x-rst', 349 classifiers=classifiers.split('\n')[1:-1], 350 keywords = ['PETSc', 'MPI'], 351 platforms=['POSIX'], 352 license='BSD-2-Clause', 353 354 url='https://petsc.org/', 355 download_url=tarball(), 356 357 author='PETSc Team', 358 author_email='petsc-maint@mcs.anl.gov', 359 maintainer='Lisandro Dalcin', 360 maintainer_email='dalcinl@gmail.com', 361 362 packages=['petsc'], 363 package_dir= {'petsc': 'config/pypi'}, 364 cmdclass={ 365 'install': cmd_install, 366 'bdist_wheel': cmd_bdist_wheel, 367 }, 368 **metadata 369) 370