1#!/usr/bin/env python 2# -*- coding: utf-8 -*- 3 4import hashlib 5import io 6import os 7import re 8import shutil 9import subprocess 10import sys 11import warnings 12from setuptools import setup, Extension 13 14 15pkg_name = 'symcxx' 16 17SYMCXX_RELEASE_VERSION = os.environ.get('SYMCXX_RELEASE_VERSION', '') # v* 18 19# Cythonize .pyx file if it exists (not in source distribution) 20ext_modules = [] 21 22 23def _read(path, macro='SYMCXX_TYPE', inc_dir='./'): 24 for l in io.open(inc_dir + path, 'rt', encoding='utf-8'): 25 if l.startswith('#include'): 26 inner_path = l.split('#include')[1] 27 inner_path = inner_path.strip().strip('<').strip('>').strip('"') 28 for inner_l in _read(inner_path, macro, inc_dir): 29 yield inner_l 30 if not l.startswith(macro + '('): 31 continue 32 l = l.split('//')[0] # strip comments marked with // (misses /* */) 33 l = ')'.join(l.split(macro + '(')[1].split(')')[:-1]) 34 yield tuple([_.strip() for _ in l.split(',')]) 35 36USE_CYTHON = None 37 38if len(sys.argv) > 1 and '--help' not in sys.argv[1:] and sys.argv[1] not in ( 39 '--help-commands', 'egg_info', 'clean', '--version'): 40 try: 41 from mako.template import Template 42 from mako.exceptions import text_error_template 43 from Cython.Build import cythonize 44 except ImportError: 45 USE_CYTHON = False 46 else: 47 pyx_path = 'symcxx/_symcxx.pyx' 48 template_path = pyx_path + '.mako_template' 49 USE_CYTHON = os.path.exists(template_path) 50 51 ext = '.pyx' if USE_CYTHON else '.cpp' 52 ext_modules = [Extension( 53 'symcxx._symcxx', 54 ['symcxx/_symcxx'+ext] 55 )] 56 if USE_CYTHON: 57 stub = 'types_nonatomic_' 58 path_stub = 'symcxx/' + stub 59 subsd = {} 60 subsd['types'] = list(_read('symcxx/types_atomic.inc', inc_dir='./include/')) 61 for k in ('unary', 'binary', 'args_stack'): 62 subsd[stub+k] = list(_read(path_stub+k+'.inc', inc_dir='./include/')) 63 subsd['types'] += subsd[stub+k] 64 65 subsd['_message_for_rendered'] = 'THIS IS A GENERATED FILE DO NOT EDIT' 66 try: 67 rendered_pyx = Template(io.open(template_path, 'rt', encoding='utf-8').read()).render(**subsd) 68 except: 69 print(text_error_template().render_unicode()) 70 raise 71 else: 72 sha256hex = hashlib.sha256(rendered_pyx.encode('utf-8')).hexdigest() 73 hash_path = os.path.join('build', pyx_path.replace('/', '__')+'.sha256hex') 74 if os.path.exists(hash_path) and open(hash_path, 'rt').read(256//4) == sha256hex: 75 pass 76 else: 77 open(pyx_path, 'wt').write(rendered_pyx) 78 if not os.path.exists('build'): 79 os.makedirs('build') 80 open(hash_path, 'wt').write(sha256hex) 81 ext_modules = cythonize(ext_modules, 82 include_path=['./include'], 83 gdb_debug=True) 84 else: 85 ext_modules[0].sources = [ 86 'src/basic.cpp', 'src/namespace.cpp' 87 ] + ext_modules[0].sources 88 ext_modules[0].include_dirs += ['./include'] 89 ext_modules[0].language = 'c++' 90 ext_modules[0].extra_compile_args = ['-std=c++11'] 91 92 93# http://conda.pydata.org/docs/build.html#environment-variables-set-during-the-build-process 94if os.environ.get('CONDA_BUILD', '0') == '1': 95 try: 96 SYMCXX_RELEASE_VERSION = 'v' + open( 97 '__conda_version__.txt', 'rt').readline().rstrip() 98 except IOError: 99 pass 100 101release_py_path = os.path.join(pkg_name, '_release.py') 102 103if len(SYMCXX_RELEASE_VERSION) > 0: 104 if SYMCXX_RELEASE_VERSION[0] == 'v': 105 TAGGED_RELEASE = True 106 __version__ = SYMCXX_RELEASE_VERSION[1:] 107 else: 108 raise ValueError("Ill formated version") 109else: 110 TAGGED_RELEASE = False 111 # read __version__ attribute from _release.py: 112 exec(open(release_py_path).read()) 113 if __version__.endswith('git'): 114 try: 115 _git_version = subprocess.check_output( 116 ['git', 'describe', '--dirty']).rstrip().decode('utf-8').replace('-dirty', '.dirty') 117 except subprocess.CalledProcessError: 118 warnings.warn("A git-archive is being installed - version information incomplete.") 119 else: 120 if 'develop' not in sys.argv: 121 warnings.warn("Using git to derive version: dev-branches may compete.") 122 __version__ = re.sub('v([0-9.]+)-(\d+)-(\w+)', r'\1.post\2+\3', _git_version) # .dev < '' < .post 123 124 125classifiers = [ 126 "Development Status :: 3 - Alpha", 127 'License :: OSI Approved :: BSD License', 128 'Operating System :: OS Independent', 129 'Topic :: Scientific/Engineering', 130 'Topic :: Scientific/Engineering :: Mathematics', 131] 132 133tests = [ 134 'symcxx.tests', 135] 136 137long_description = io.open('README.rst', encoding='utf-8').read() 138with io.open(os.path.join(pkg_name, '__init__.py'), 'rt', encoding='utf-8') as f: 139 short_description = f.read().split('"""')[1] 140 141setup_kwargs = dict( 142 name=pkg_name, 143 version=__version__, 144 description=short_description, 145 long_description=long_description, 146 classifiers=classifiers, 147 author='Björn Dahlgren', 148 author_email='bjodah@DELETEMEgmail.com', 149 url='https://github.com/bjodah/' + pkg_name, 150 license='BSD', 151 packages=[pkg_name] + tests, 152 setup_requires=['mako'] if USE_CYTHON else [], 153 install_requires=['numpy'], 154 ext_modules=ext_modules, 155) 156 157if __name__ == '__main__': 158 try: 159 if TAGGED_RELEASE: 160 # Same commit should generate different sdist 161 # depending on tagged version (set $SYMCXX_RELEASE_VERSION) 162 # e.g.: $ SYMCXX_RELEASE_VERSION=v1.2.3 python setup.py sdist 163 # this will ensure source distributions contain the correct version 164 shutil.move(release_py_path, release_py_path+'__temp__') 165 open(release_py_path, 'wt').write( 166 "__version__ = '{}'\n".format(__version__)) 167 setup(**setup_kwargs) 168 finally: 169 if TAGGED_RELEASE: 170 shutil.move(release_py_path+'__temp__', release_py_path) 171