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