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