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