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