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, dry_run=False): 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 if dry_run: 161 return 162 use_config_py = False 163 if use_config_py: 164 import configure 165 configure.petsc_configure(options) 166 import logger 167 logger.Logger.defaultLog = None 168 else: 169 python = sys.executable 170 command = [python, './configure'] + options 171 status = os.system(" ".join(command)) 172 if status != 0: 173 raise RuntimeError(status) 174 175 # Fix PETSc configuration 176 using_build_backend = any( 177 os.environ.get(prefix + '_BUILD_BACKEND') 178 for prefix in ('_PYPROJECT_HOOKS', 'PEP517') 179 ) 180 if using_build_backend: 181 pdir = os.environ['PETSC_DIR'] 182 parch = os.environ['PETSC_ARCH'] 183 include = os.path.join(pdir, parch, 'include') 184 for filename in ( 185 'petscconf.h', 186 'petscconfiginfo.h', 187 'petscmachineinfo.h', 188 ): 189 filename = os.path.join(include, filename) 190 with open(filename, 'r') as old_fh: 191 contents = old_fh.read() 192 contents = contents.replace(prefix, '${PETSC_DIR}') 193 contents = re.sub( 194 r'^(#define PETSC_PYTHON_EXE) "(.*)"$', 195 r'\1 "python%d"' % sys.version_info[0], 196 contents, flags=re.MULTILINE, 197 ) 198 with open(filename, 'w') as new_fh: 199 new_fh.write(contents) 200 201 202def build(dry_run=False): 203 log.info('PETSc: build') 204 # Run PETSc build 205 if dry_run: 206 return 207 use_builder_py = False 208 if use_builder_py: 209 import builder 210 builder.PETScMaker().run() 211 import logger 212 logger.Logger.defaultLog = None 213 else: 214 make = shutil.which('make') 215 command = [make, 'all'] 216 status = os.system(" ".join(command)) 217 if status != 0: 218 raise RuntimeError(status) 219 220 221def install(dry_run=False): 222 log.info('PETSc: install') 223 # Run PETSc installer 224 if dry_run: 225 return 226 use_install_py = False 227 if use_install_py: 228 import install 229 install.Installer().run() 230 import logger 231 logger.Logger.defaultLog = None 232 else: 233 make = shutil.which('make') 234 command = [make, 'install'] 235 status = os.system(" ".join(command)) 236 if status != 0: 237 raise RuntimeError(status) 238 239 240class context(object): 241 def __init__(self): 242 self.sys_argv = sys.argv[:] 243 self.wdir = os.getcwd() 244 def enter(self): 245 del sys.argv[1:] 246 pdir = os.environ['PETSC_DIR'] 247 os.chdir(pdir) 248 return self 249 def exit(self): 250 sys.argv[:] = self.sys_argv 251 os.chdir(self.wdir) 252 253 254class cmd_install(_install): 255 256 def initialize_options(self): 257 _install.initialize_options(self) 258 259 def finalize_options(self): 260 _install.finalize_options(self) 261 self.install_lib = self.install_platlib 262 self.install_libbase = self.install_lib 263 self.old_and_unmanageable = True 264 265 def run(self): 266 root_dir = os.path.abspath(self.install_lib) 267 prefix = os.path.join(root_dir, 'petsc') 268 # 269 ctx = context().enter() 270 try: 271 config(prefix, self.dry_run) 272 build(self.dry_run) 273 install(self.dry_run) 274 finally: 275 ctx.exit() 276 # 277 self.outputs = [] 278 for dirpath, _, filenames in os.walk(prefix): 279 for fn in filenames: 280 self.outputs.append(os.path.join(dirpath, fn)) 281 # 282 _install.run(self) 283 284 def get_outputs(self): 285 outputs = getattr(self, 'outputs', []) 286 outputs += _install.get_outputs(self) 287 return outputs 288 289 290class cmd_bdist_wheel(_bdist_wheel): 291 292 def finalize_options(self): 293 super().finalize_options() 294 self.root_is_pure = False 295 self.build_number = None 296 # self.keep_temp = True 297 298 def get_tag(self): 299 plat_tag = super().get_tag()[-1] 300 return (self.python_tag, "none", plat_tag) 301 302 303def version(): 304 version_re = { 305 'major' : re.compile(r"#define\s+PETSC_VERSION_MAJOR\s+(\d+)"), 306 'minor' : re.compile(r"#define\s+PETSC_VERSION_MINOR\s+(\d+)"), 307 'micro' : re.compile(r"#define\s+PETSC_VERSION_SUBMINOR\s+(\d+)"), 308 'release': re.compile(r"#define\s+PETSC_VERSION_RELEASE\s+([-]*\d+)"), 309 } 310 petscversion_h = os.path.join('include','petscversion.h') 311 data = open(petscversion_h, 'r').read() 312 major = int(version_re['major'].search(data).groups()[0]) 313 minor = int(version_re['minor'].search(data).groups()[0]) 314 micro = int(version_re['micro'].search(data).groups()[0]) 315 release = int(version_re['release'].search(data).groups()[0]) 316 if release: 317 v = "%d.%d.%d" % (major, minor, micro) 318 else: 319 v = "%d.%d.0.dev%d" % (major, minor+1, 0) 320 return v 321 322 323def tarball(): 324 VERSION = version() 325 if '.dev' in VERSION: 326 return None 327 return ('https://web.cels.anl.gov/projects/petsc/download/release-snapshots/' 328 'petsc-%s.tar.gz#egg=petsc-%s' % (VERSION, VERSION)) 329 330 331description = __doc__.split('\n')[1:-1] 332del description[1:3] 333 334classifiers = """ 335Development Status :: 5 - Production/Stable 336Intended Audience :: Developers 337Intended Audience :: Science/Research 338Operating System :: POSIX 339Programming Language :: C 340Programming Language :: C++ 341Programming Language :: Fortran 342Programming Language :: Python 343Programming Language :: Python :: 3 344Topic :: Scientific/Engineering 345Topic :: Software Development :: Libraries 346""" 347 348bootstrap() 349setup( 350 name='petsc', 351 version=version(), 352 description=description.pop(0), 353 long_description='\n'.join(description), 354 long_description_content_type='text/x-rst', 355 classifiers=classifiers.split('\n')[1:-1], 356 keywords = ['PETSc', 'MPI'], 357 platforms=['POSIX'], 358 license='BSD-2-Clause', 359 360 url='https://petsc.org/', 361 download_url=tarball(), 362 363 author='PETSc Team', 364 author_email='petsc-maint@mcs.anl.gov', 365 maintainer='Lisandro Dalcin', 366 maintainer_email='dalcinl@gmail.com', 367 368 packages=['petsc'], 369 package_dir= {'petsc': 'config/pypi'}, 370 cmdclass={ 371 'install': cmd_install, 372 'bdist_wheel': cmd_bdist_wheel, 373 }, 374 **metadata 375) 376