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