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