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