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