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