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