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