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