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