xref: /petsc/setup.py (revision 7ebd793db8367283ef35166e4eadc6d0210bd705)
1#!/usr/bin/env python
2# Author:  Lisandro Dalcin
3# Contact: dalcinl@gmail.com
4
5"""
6PETSc for Python
7"""
8
9import sys
10import os
11import re
12
13try:
14    import setuptools
15except ImportError:
16    setuptools = None
17
18pyver = sys.version_info[:2]
19if pyver < (2, 6) or (3, 0) <= pyver < (3, 2):
20    raise RuntimeError("Python version 2.6, 2.7 or >= 3.2 required")
21
22# --------------------------------------------------------------------
23# Metadata
24# --------------------------------------------------------------------
25
26topdir = os.path.abspath(os.path.dirname(__file__))
27
28from conf.metadata import metadata
29
30def name():
31    return 'petsc4py'
32
33def version():
34    with open(os.path.join(topdir, 'src', '__init__.py')) as f:
35        m = re.search(r"__version__\s*=\s*'(.*)'", f.read())
36        return m.groups()[0]
37
38def description():
39    with open(os.path.join(topdir, 'DESCRIPTION.rst')) as f:
40        return f.read()
41
42name     = name()
43version  = version()
44
45url      = 'https://bitbucket.org/petsc/%(name)s/' % vars()
46download = url + 'downloads/%(name)s-%(version)s.tar.gz' % vars()
47
48devstat  = ['Development Status :: 5 - Production/Stable']
49keywords = ['PETSc', 'MPI']
50
51metadata['name'] = name
52metadata['version'] = version
53metadata['description'] = __doc__.strip()
54metadata['long_description'] = description()
55metadata['keywords'] += keywords
56metadata['classifiers'] += devstat
57metadata['url'] = url
58metadata['download_url'] = download
59
60metadata['provides'] = ['petsc4py']
61metadata['requires'] = ['numpy']
62
63# --------------------------------------------------------------------
64# Extension modules
65# --------------------------------------------------------------------
66
67def get_ext_modules(Extension):
68    from os   import walk, path
69    from glob import glob
70    depends = []
71    for pth, dirs, files in walk('src'):
72        depends += glob(path.join(pth, '*.h'))
73        depends += glob(path.join(pth, '*.c'))
74    try:
75        import numpy
76        numpy_includes = [numpy.get_include()]
77    except ImportError:
78        numpy_includes = []
79    return [Extension('petsc4py.lib.PETSc',
80                      sources=['src/PETSc.c',
81                               'src/libpetsc4py.c',
82                               ],
83                      include_dirs=['src/include',
84                                    ] + numpy_includes,
85                      depends=depends)]
86
87# --------------------------------------------------------------------
88# Setup
89# --------------------------------------------------------------------
90
91from conf.petscconf import setup, Extension
92from conf.petscconf import config, build, build_src, build_ext, install
93from conf.petscconf import clean, test, sdist
94
95CYTHON = '0.22'
96
97def run_setup():
98    setup_args = metadata.copy()
99    if setuptools:
100        setup_args['zip_safe'] = False
101        setup_args['install_requires'] = ['numpy']
102        PETSC_DIR = os.environ.get('PETSC_DIR')
103        if not (PETSC_DIR and os.path.isdir(PETSC_DIR)):
104            vstr = setup_args['version'].split('.')[:2]
105            x, y = int(vstr[0]), int(vstr[1])
106            PETSC = ">=%s.%s,<%s.%s" % (x, y, x, y+1)
107            setup_args['install_requires'] += ['petsc'+PETSC]
108    if setuptools:
109        src = os.path.join('src', 'petsc4py.PETSc.c')
110        has_src = os.path.exists(os.path.join(topdir, src))
111        has_git = os.path.isdir(os.path.join(topdir, '.git'))
112        has_hg  = os.path.isdir(os.path.join(topdir, '.hg'))
113        if not has_src or has_git or has_hg:
114            setup_args['setup_requires'] = ['Cython>='+CYTHON]
115    #
116    setup(packages     = ['petsc4py',
117                          'petsc4py.lib',],
118          package_dir  = {'petsc4py'     : 'src',
119                          'petsc4py.lib' : 'src/lib'},
120          package_data = {'petsc4py'     : ['include/petsc4py/*.h',
121                                            'include/petsc4py/*.i',
122                                            'include/petsc4py/*.pxd',
123                                            'include/petsc4py/*.pxi',
124                                            'include/petsc4py/*.pyx',],
125                          'petsc4py.lib' : ['petsc.cfg'],},
126          ext_modules  = get_ext_modules(Extension),
127          cmdclass     = {'config'     : config,
128                          'build'      : build,
129                          'build_src'  : build_src,
130                          'build_ext'  : build_ext,
131                          'install'    : install,
132                          'clean'      : clean,
133                          'test'       : test,
134                          'sdist'      : sdist,
135                          },
136          **setup_args)
137
138def chk_cython(VERSION):
139    from distutils import log
140    from distutils.version import LooseVersion
141    from distutils.version import StrictVersion
142    warn = lambda msg='': sys.stderr.write(msg+'\n')
143    #
144    try:
145        import Cython
146    except ImportError:
147        warn("*"*80)
148        warn()
149        warn(" You need to generate C source files with Cython!!")
150        warn(" Download and install Cython <http://www.cython.org>")
151        warn()
152        warn("*"*80)
153        return False
154    #
155    try:
156        CYTHON_VERSION = Cython.__version__
157    except AttributeError:
158        from Cython.Compiler.Version import version as CYTHON_VERSION
159    REQUIRED = VERSION
160    m = re.match(r"(\d+\.\d+(?:\.\d+)?).*", CYTHON_VERSION)
161    if m:
162        Version = StrictVersion
163        AVAILABLE = m.groups()[0]
164    else:
165        Version = LooseVersion
166        AVAILABLE = CYTHON_VERSION
167    if (REQUIRED is not None and
168        Version(AVAILABLE) < Version(REQUIRED)):
169        warn("*"*80)
170        warn()
171        warn(" You need to install Cython %s (you have version %s)"
172             % (REQUIRED, CYTHON_VERSION))
173        warn(" Download and install Cython <http://www.cython.org>")
174        warn()
175        warn("*"*80)
176        return False
177    #
178    return True
179
180def run_cython(source, depends=(), includes=(),
181               destdir_c=None, destdir_h=None,
182               wdir=None, force=False, VERSION=None):
183    from glob import glob
184    from distutils import log
185    from distutils import dep_util
186    from distutils.errors import DistutilsError
187    target = os.path.splitext(source)[0]+'.c'
188    cwd = os.getcwd()
189    try:
190        if wdir: os.chdir(wdir)
191        alldeps = [source]
192        for dep in depends:
193            alldeps += glob(dep)
194        if not (force or dep_util.newer_group(alldeps, target)):
195            log.debug("skipping '%s' -> '%s' (up-to-date)",
196                      source, target)
197            return
198    finally:
199        os.chdir(cwd)
200    if not chk_cython(VERSION):
201        raise DistutilsError("requires Cython>=%s" % VERSION)
202    log.info("cythonizing '%s' -> '%s'", source, target)
203    from conf.cythonize import cythonize
204    err = cythonize(source,
205                    includes=includes,
206                    destdir_c=destdir_c,
207                    destdir_h=destdir_h,
208                    wdir=wdir)
209    if err:
210        raise DistutilsError(
211            "Cython failure: '%s' -> '%s'" % (source, target))
212
213def build_sources(cmd):
214    from os.path import exists, isdir, join
215    if (exists(join('src', 'petsc4py.PETSc.c')) and
216        not (isdir('.hg') or isdir('.git')) and
217        not cmd.force): return
218    # petsc4py.PETSc
219    source = 'petsc4py.PETSc.pyx'
220    depends = ('include/*/*.pxd',
221               'PETSc/*.pyx',
222               'PETSc/*.pxi',)
223    includes = ['include']
224    destdir_h = os.path.join('include', 'petsc4py')
225    run_cython(source, depends, includes,
226               destdir_c=None, destdir_h=destdir_h, wdir='src',
227               force=cmd.force, VERSION=CYTHON)
228    # libpetsc4py
229    source = os.path.join('libpetsc4py', 'libpetsc4py.pyx')
230    depends = ['include/petsc4py/*.pxd',
231               'libpetsc4py/*.pyx',
232               'libpetsc4py/*.pxi']
233    includes = ['include']
234    run_cython(source, depends, includes,
235               destdir_c=None, destdir_h=None, wdir='src',
236               force=cmd.force, VERSION=CYTHON)
237
238build_src.run = build_sources
239
240def run_testsuite(cmd):
241    from distutils.errors import DistutilsError
242    sys.path.insert(0, 'test')
243    try:
244        from runtests import main
245    finally:
246        del sys.path[0]
247    if cmd.dry_run:
248        return
249    args = cmd.args[:] or []
250    if cmd.verbose < 1:
251        args.insert(0,'-q')
252    if cmd.verbose > 1:
253        args.insert(0,'-v')
254    err = main(args)
255    if err:
256        raise DistutilsError("test")
257
258test.run = run_testsuite
259
260# --------------------------------------------------------------------
261
262def main():
263    run_setup()
264
265if __name__ == '__main__':
266    main()
267
268# --------------------------------------------------------------------
269