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