xref: /petsc/src/binding/petsc4py/setup.py (revision 3594a89dd4e17cdeb3c4d4dd4e0166b2e32eea57)
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        suffix = os.path.join('src', 'binding', 'petsc4py')
118        in_petsc = topdir.endswith(os.path.sep + suffix)
119        if not has_src or has_git or has_hg or in_petsc:
120            setup_args['setup_requires'] = ['Cython>='+CYTHON]
121    #
122    setup(packages     = ['petsc4py',
123                          'petsc4py.lib',],
124          package_dir  = {'petsc4py'     : 'src',
125                          'petsc4py.lib' : 'src/lib'},
126          package_data = {'petsc4py'     : ['include/petsc4py/*.h',
127                                            'include/petsc4py/*.i',
128                                            'include/petsc4py/*.pxd',
129                                            'include/petsc4py/*.pxi',
130                                            'include/petsc4py/*.pyx',
131                                            'PETSc.pxd',],
132                          'petsc4py.lib' : ['petsc.cfg'],},
133          ext_modules  = get_ext_modules(Extension),
134          cmdclass     = {'config'     : config,
135                          'build'      : build,
136                          'build_src'  : build_src,
137                          'build_ext'  : build_ext,
138                          'install'    : install,
139                          'clean'      : clean,
140                          'test'       : test,
141                          'sdist'      : sdist,
142                          },
143          **setup_args)
144
145def chk_cython(VERSION):
146    from distutils import log
147    from distutils.version import LooseVersion
148    from distutils.version import StrictVersion
149    warn = lambda msg='': sys.stderr.write(msg+'\n')
150    #
151    try:
152        import Cython
153    except ImportError:
154        warn("*"*80)
155        warn()
156        warn(" You need to generate C source files with Cython!!")
157        warn(" Download and install Cython <http://www.cython.org>")
158        warn()
159        warn("*"*80)
160        return False
161    #
162    try:
163        CYTHON_VERSION = Cython.__version__
164    except AttributeError:
165        from Cython.Compiler.Version import version as CYTHON_VERSION
166    REQUIRED = VERSION
167    m = re.match(r"(\d+\.\d+(?:\.\d+)?).*", CYTHON_VERSION)
168    if m:
169        Version = StrictVersion
170        AVAILABLE = m.groups()[0]
171    else:
172        Version = LooseVersion
173        AVAILABLE = CYTHON_VERSION
174    if (REQUIRED is not None and
175        Version(AVAILABLE) < Version(REQUIRED)):
176        warn("*"*80)
177        warn()
178        warn(" You need to install Cython %s (you have version %s)"
179             % (REQUIRED, CYTHON_VERSION))
180        warn(" Download and install Cython <http://www.cython.org>")
181        warn()
182        warn("*"*80)
183        return False
184    #
185    return True
186
187def run_cython(source, depends=(), includes=(),
188               destdir_c=None, destdir_h=None,
189               wdir=None, force=False, VERSION=None):
190    from glob import glob
191    from distutils import log
192    from distutils import dep_util
193    from distutils.errors import DistutilsError
194    target = os.path.splitext(source)[0]+'.c'
195    cwd = os.getcwd()
196    try:
197        if wdir: os.chdir(wdir)
198        alldeps = [source]
199        for dep in depends:
200            alldeps += glob(dep)
201        if not (force or dep_util.newer_group(alldeps, target)):
202            log.debug("skipping '%s' -> '%s' (up-to-date)",
203                      source, target)
204            return
205    finally:
206        os.chdir(cwd)
207    if not chk_cython(VERSION):
208        raise DistutilsError("requires Cython>=%s" % VERSION)
209    log.info("cythonizing '%s' -> '%s'", source, target)
210    from conf.cythonize import cythonize
211    err = cythonize(source,
212                    includes=includes,
213                    destdir_c=destdir_c,
214                    destdir_h=destdir_h,
215                    wdir=wdir)
216    if err:
217        raise DistutilsError(
218            "Cython failure: '%s' -> '%s'" % (source, target))
219
220def build_sources(cmd):
221    from os.path import exists, isdir, join
222    if (exists(join('src', 'petsc4py.PETSc.c')) and
223        not (isdir('.hg') or isdir('.git')) and
224        not cmd.force): return
225    # petsc4py.PETSc
226    source = 'petsc4py.PETSc.pyx'
227    depends = ('include/*/*.pxd',
228               'PETSc/*.pyx',
229               'PETSc/*.pxi',)
230    includes = ['include']
231    destdir_h = os.path.join('include', 'petsc4py')
232    run_cython(source, depends, includes,
233               destdir_c=None, destdir_h=destdir_h, wdir='src',
234               force=cmd.force, VERSION=CYTHON)
235    # libpetsc4py
236    source = os.path.join('libpetsc4py', 'libpetsc4py.pyx')
237    depends = ['include/petsc4py/*.pxd',
238               'libpetsc4py/*.pyx',
239               'libpetsc4py/*.pxi']
240    includes = ['include']
241    run_cython(source, depends, includes,
242               destdir_c=None, destdir_h=None, wdir='src',
243               force=cmd.force, VERSION=CYTHON)
244
245build_src.run = build_sources
246
247def run_testsuite(cmd):
248    from distutils.errors import DistutilsError
249    sys.path.insert(0, 'test')
250    try:
251        from runtests import main
252    finally:
253        del sys.path[0]
254    if cmd.dry_run:
255        return
256    args = cmd.args[:] or []
257    if cmd.verbose < 1:
258        args.insert(0,'-q')
259    if cmd.verbose > 1:
260        args.insert(0,'-v')
261    err = main(args)
262    if err:
263        raise DistutilsError("test")
264
265test.run = run_testsuite
266
267# --------------------------------------------------------------------
268
269def main():
270    run_setup()
271
272if __name__ == '__main__':
273    main()
274
275# --------------------------------------------------------------------
276