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