xref: /petsc/setup.py (revision af4fa82cc29c77689f3cd2af837601dbdc3602c2)
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