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 get_release(): 115 release = 1 116 if topdir.endswith(os.path.join(os.path.sep, 'src', 'binding', name)): 117 topname = name.replace('4py', '') 118 rootdir = os.path.abspath(os.path.join(topdir, *[os.path.pardir]*3)) 119 version_h = os.path.join(rootdir, 'include', '%sversion.h' % topname) 120 release_macro = '%s_VERSION_RELEASE' % topname.upper() 121 version_re = re.compile(r"#define\s+%s\s+([-]*\d+)" % release_macro) 122 if os.path.exists(version_h) and os.path.isfile(version_h): 123 with open(version_h, 'r') as f: 124 release = int(version_re.search(f.read()).groups()[0]) 125 return bool(release) 126 127def requires(pkgname, major, minor, release=True): 128 minor = minor + int(not release) 129 devel = '' if release else '.dev0' 130 vmin = "%s.%s%s" % (major, minor, devel) 131 vmax = "%s.%s" % (major, minor + 1) 132 return "%s>=%s,<%s" % (pkgname, vmin, vmax) 133 134def run_setup(): 135 setup_args = metadata.copy() 136 vstr = setup_args['version'].split('.')[:2] 137 x, y = tuple(map(int, vstr)) 138 release = get_release() 139 if not release: 140 setup_args['version'] = "%d.%d.0.dev0" %(x, y+1) 141 if setuptools: 142 setup_args['zip_safe'] = False 143 setup_args['install_requires'] = ['numpy'] 144 PETSC_DIR = os.environ.get('PETSC_DIR') 145 if not (PETSC_DIR and os.path.isdir(PETSC_DIR)): 146 petsc = requires('petsc', x, y, release) 147 setup_args['install_requires'] += [petsc] 148 if setuptools: 149 src = os.path.join('src', 'petsc4py.PETSc.c') 150 has_src = os.path.exists(os.path.join(topdir, src)) 151 has_git = os.path.isdir(os.path.join(topdir, '.git')) 152 has_hg = os.path.isdir(os.path.join(topdir, '.hg')) 153 suffix = os.path.join('src', 'binding', 'petsc4py') 154 in_petsc = topdir.endswith(os.path.sep + suffix) 155 if not has_src or has_git or has_hg or in_petsc: 156 setup_args['setup_requires'] = ['Cython>='+CYTHON] 157 # 158 setup(packages = ['petsc4py', 159 'petsc4py.lib',], 160 package_dir = {'petsc4py' : 'src', 161 'petsc4py.lib' : 'src/lib'}, 162 package_data = {'petsc4py' : ['include/petsc4py/*.h', 163 'include/petsc4py/*.i', 164 'include/petsc4py/*.pxd', 165 'include/petsc4py/*.pxi', 166 'include/petsc4py/*.pyx', 167 'PETSc.pxd',], 168 'petsc4py.lib' : ['petsc.cfg'],}, 169 ext_modules = get_ext_modules(Extension), 170 cmdclass = {'config' : config, 171 'build' : build, 172 'build_src' : build_src, 173 'build_ext' : build_ext, 174 'install' : install, 175 'clean' : clean, 176 'test' : test, 177 'sdist' : sdist, 178 }, 179 **setup_args) 180 181def chk_cython(VERSION): 182 from distutils import log 183 from distutils.version import LooseVersion 184 from distutils.version import StrictVersion 185 warn = lambda msg='': sys.stderr.write(msg+'\n') 186 # 187 try: 188 import Cython 189 except ImportError: 190 warn("*"*80) 191 warn() 192 warn(" You need to generate C source files with Cython!!") 193 warn(" Download and install Cython <http://www.cython.org>") 194 warn() 195 warn("*"*80) 196 return False 197 # 198 try: 199 CYTHON_VERSION = Cython.__version__ 200 except AttributeError: 201 from Cython.Compiler.Version import version as CYTHON_VERSION 202 REQUIRED = VERSION 203 m = re.match(r"(\d+\.\d+(?:\.\d+)?).*", CYTHON_VERSION) 204 if m: 205 Version = StrictVersion 206 AVAILABLE = m.groups()[0] 207 else: 208 Version = LooseVersion 209 AVAILABLE = CYTHON_VERSION 210 if (REQUIRED is not None and 211 Version(AVAILABLE) < Version(REQUIRED)): 212 warn("*"*80) 213 warn() 214 warn(" You need to install Cython %s (you have version %s)" 215 % (REQUIRED, CYTHON_VERSION)) 216 warn(" Download and install Cython <http://www.cython.org>") 217 warn() 218 warn("*"*80) 219 return False 220 # 221 return True 222 223def run_cython(source, target=None, 224 depends=(), includes=(), 225 destdir_c=None, destdir_h=None, 226 wdir=None, force=False, VERSION=None): 227 from glob import glob 228 from distutils import log 229 from distutils import dep_util 230 from distutils.errors import DistutilsError 231 if target is None: 232 target = os.path.splitext(source)[0]+'.c' 233 cwd = os.getcwd() 234 try: 235 if wdir: os.chdir(wdir) 236 alldeps = [source] 237 for dep in depends: 238 alldeps += glob(dep) 239 if not (force or dep_util.newer_group(alldeps, target)): 240 log.debug("skipping '%s' -> '%s' (up-to-date)", 241 source, target) 242 return 243 finally: 244 os.chdir(cwd) 245 if not chk_cython(VERSION): 246 raise DistutilsError("requires Cython>=%s" % VERSION) 247 log.info("cythonizing '%s' -> '%s'", source, target) 248 from conf.cythonize import cythonize 249 err = cythonize(source, target, 250 includes=includes, 251 destdir_c=destdir_c, 252 destdir_h=destdir_h, 253 wdir=wdir) 254 if err: 255 raise DistutilsError( 256 "Cython failure: '%s' -> '%s'" % (source, target)) 257 258def build_sources(cmd): 259 from os.path import exists, isdir, join 260 261 # petsc4py.PETSc 262 source = 'petsc4py.PETSc.pyx' 263 target = 'petsc4py.PETSc.c' 264 depends = ['include/*/*.pxd', 265 'PETSc/*.pyx', 266 'PETSc/*.pxi'] 267 includes = ['include'] 268 destdir_h = os.path.join('include', 'petsc4py') 269 run_cython(source, target, 270 depends=depends, includes=includes, 271 destdir_c=None, destdir_h=destdir_h, wdir='src', 272 force=cmd.force, VERSION=CYTHON) 273 # libpetsc4py 274 source = os.path.join('libpetsc4py', 'libpetsc4py.pyx') 275 depends = ['include/petsc4py/*.pxd', 276 'libpetsc4py/*.pyx', 277 'libpetsc4py/*.pxi'] 278 includes = ['include'] 279 run_cython(source, 280 depends=depends, includes=includes, 281 destdir_c=None, destdir_h=None, wdir='src', 282 force=cmd.force, VERSION=CYTHON) 283 284build_src.run = build_sources 285 286def run_testsuite(cmd): 287 from distutils.errors import DistutilsError 288 sys.path.insert(0, 'test') 289 try: 290 from runtests import main 291 finally: 292 del sys.path[0] 293 if cmd.dry_run: 294 return 295 args = cmd.args[:] or [] 296 if cmd.verbose < 1: 297 args.insert(0,'-q') 298 if cmd.verbose > 1: 299 args.insert(0,'-v') 300 err = main(args) 301 if err: 302 raise DistutilsError("test") 303 304test.run = run_testsuite 305 306# -------------------------------------------------------------------- 307 308def main(): 309 run_setup() 310 311if __name__ == '__main__': 312 main() 313 314# -------------------------------------------------------------------- 315