1#!/usr/bin/env python 2 3#----------------------------------------------------------------------------- 4# Copyright (c) 2013-2015, PyStan developers 5# 6# This file is licensed under Version 3.0 of the GNU General Public 7# License. See LICENSE for a text of the license. 8#----------------------------------------------------------------------------- 9 10#----------------------------------------------------------------------------- 11# This file is part of PyStan. 12# 13# PyStan is free software: you can redistribute it and/or modify it 14# under the terms of the GNU General Public License version 3 as 15# published by the Free Software Foundation. 16# 17# PyStan is distributed in the hope that it will be useful, but 18# WITHOUT ANY WARRANTY; without even the implied warranty of 19# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20# General Public License for more details. 21# 22# You should have received a copy of the GNU General Public License 23# along with PyStan. If not, see <http://www.gnu.org/licenses/>. 24#----------------------------------------------------------------------------- 25import ast 26import codecs 27import os 28import platform 29import sys 30 31LONG_DESCRIPTION = codecs.open('README.rst', encoding='utf-8').read() 32NAME = 'pystan' 33DESCRIPTION = 'Python interface to Stan, a package for Bayesian inference' 34AUTHOR = 'PyStan Developers' 35AUTHOR_EMAIL = 'stan-users@googlegroups.com' 36URL = 'https://github.com/stan-dev/pystan' 37LICENSE = 'GPLv3' 38CLASSIFIERS = [ 39 'Programming Language :: Python', 40 'Programming Language :: Python :: 2', 41 'Programming Language :: Python :: 3', 42 'Programming Language :: Cython', 43 'Development Status :: 4 - Beta', 44 'Environment :: Console', 45 'Operating System :: OS Independent', 46 'Intended Audience :: Developers', 47 'Intended Audience :: Science/Research', 48 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)', 49 'Topic :: Scientific/Engineering', 50 'Topic :: Scientific/Engineering :: Information Analysis' 51] 52 53 54# VersionFinder from from django-compressor 55class VersionFinder(ast.NodeVisitor): 56 def __init__(self): 57 self.version = None 58 59 def visit_Assign(self, node): 60 if node.targets[0].id == '__version__': 61 self.version = node.value.s 62 63 64def read(*parts): 65 filename = os.path.join(os.path.dirname(__file__), *parts) 66 with codecs.open(filename, encoding='utf-8') as fp: 67 return fp.read() 68 69 70def find_version(*parts): 71 finder = VersionFinder() 72 finder.visit(ast.parse(read(*parts))) 73 return finder.version 74 75 76############################################################################### 77# Optional setuptools features 78# We need to import setuptools early, if we want setuptools features, 79# as it monkey-patches the 'setup' function 80 81# For some commands, use setuptools 82if len(set(('develop', 'release', 'bdist_egg', 'bdist_rpm', 83 'bdist_wininst', 'install_egg_info', 'build_sphinx', 84 'egg_info', 'easy_install', 'upload', 'bdist_wheel', 85 '--single-version-externally-managed', 86 )).intersection(sys.argv)) > 0: 87 import setuptools 88 extra_setuptools_args = dict( 89 install_requires=['Cython>=0.22,!=0.25.1', 'numpy >= 1.7'], 90 zip_safe=False, # the package can run out of an .egg file 91 include_package_data=True, 92 ) 93else: 94 extra_setuptools_args = dict() 95 96############################################################################### 97 98from distutils.errors import CCompilerError, DistutilsError 99from distutils.extension import Extension 100 101stan_include_dirs = ['pystan/stan/src', 102 'pystan/stan/lib/stan_math/', 103 'pystan/stan/lib/stan_math/lib/eigen_3.3.3', 104 'pystan/stan/lib/stan_math/lib/boost_1.69.0', 105 'pystan/stan/lib/stan_math/lib/sundials_4.1.0/include'] 106stan_macros = [ 107 ('BOOST_DISABLE_ASSERTS', None), 108 ('BOOST_NO_DECLTYPE', None), 109 ('BOOST_PHOENIX_NO_VARIADIC_EXPRESSION', None), # needed for stanc 110 ('BOOST_RESULT_OF_USE_TR1', None), 111 ('FUSION_MAX_VECTOR_SIZE', 12), # for parser, stan-dev/pystan#222 112] 113extra_compile_args = [ 114 '-Os', 115 '-ftemplate-depth-256', 116 '-Wno-unused-function', 117 '-Wno-uninitialized', 118 '-std=c++1y', 119] 120 121if platform.platform().startswith('Win'): 122 from Cython.Build.Inline import _get_build_extension 123 if _get_build_extension().compiler in (None, 'msvc'): 124 print("Warning: MSVC is not supported") 125 extra_compile_args = [ 126 '/EHsc', 127 '-DBOOST_DATE_TIME_NO_LIB', 128 '/std:c++14', 129 ] 130 else: 131 # fix bug in MingW-W64 132 # use posix threads 133 extra_compile_args.extend([ 134 "-D_hypot=hypot", 135 "-pthread", 136 "-fexceptions", 137 ]) 138 139 140stanc_sources = [ 141 "pystan/stan/src/stan/lang/ast_def.cpp", 142 "pystan/stan/src/stan/lang/grammars/bare_type_grammar_inst.cpp", 143 "pystan/stan/src/stan/lang/grammars/block_var_decls_grammar_inst.cpp", 144 "pystan/stan/src/stan/lang/grammars/expression07_grammar_inst.cpp", 145 "pystan/stan/src/stan/lang/grammars/expression_grammar_inst.cpp", 146 "pystan/stan/src/stan/lang/grammars/functions_grammar_inst.cpp", 147 "pystan/stan/src/stan/lang/grammars/indexes_grammar_inst.cpp", 148 "pystan/stan/src/stan/lang/grammars/local_var_decls_grammar_inst.cpp", 149 "pystan/stan/src/stan/lang/grammars/program_grammar_inst.cpp", 150 "pystan/stan/src/stan/lang/grammars/semantic_actions_def.cpp", 151 "pystan/stan/src/stan/lang/grammars/statement_2_grammar_inst.cpp", 152 "pystan/stan/src/stan/lang/grammars/statement_grammar_inst.cpp", 153 "pystan/stan/src/stan/lang/grammars/term_grammar_inst.cpp", 154 "pystan/stan/src/stan/lang/grammars/whitespace_grammar_inst.cpp", 155] 156 157extensions = [ 158 Extension("pystan._api", 159 ["pystan/_api.pyx"] + stanc_sources, 160 language='c++', 161 define_macros=stan_macros, 162 include_dirs=stan_include_dirs, 163 extra_compile_args=extra_compile_args), 164 Extension("pystan._chains", 165 ["pystan/_chains.pyx"], 166 language='c++', 167 define_macros=stan_macros, 168 include_dirs=stan_include_dirs, 169 extra_compile_args=extra_compile_args), 170 # _misc.pyx does not use Stan libs 171 Extension("pystan._misc", 172 ["pystan/_misc.pyx"], 173 language='c++', 174 extra_compile_args=extra_compile_args) 175] 176 177 178## package data 179package_data_pats = ['*.hpp', '*.pxd', '*.pyx', 'tests/data/*.csv', 180 'tests/data/*.stan', 'lookuptable/*.txt'] 181 182# get every file under pystan/stan/src and pystan/stan/lib 183stan_files_all = sum( 184 [[os.path.join(path.replace('pystan/', ''), fn) for fn in files] 185 for path, dirs, files in os.walk('pystan/stan/src/')], []) 186 187lib_files_all = sum( 188 [[os.path.join(path.replace('pystan/', ''), fn) for fn in files] 189 for path, dirs, files in os.walk('pystan/stan/lib/')], []) 190 191package_data_pats += stan_files_all 192package_data_pats += lib_files_all 193 194 195def setup_package(): 196 metadata = dict(name=NAME, 197 version=find_version("pystan", "__init__.py"), 198 maintainer=AUTHOR, 199 maintainer_email=AUTHOR_EMAIL, 200 packages=['pystan', 201 'pystan.tests', 202 'pystan.experimental', 203 'pystan.external', 204 'pystan.external.pymc', 205 'pystan.external.enum', 206 'pystan.external.scipy'], 207 ext_modules=extensions, 208 package_data={'pystan': package_data_pats}, 209 platforms='any', 210 description=DESCRIPTION, 211 license=LICENSE, 212 url=URL, 213 long_description=LONG_DESCRIPTION, 214 classifiers=CLASSIFIERS, 215 **extra_setuptools_args) 216 if len(sys.argv) >= 2 and ('--help' in sys.argv[1:] or sys.argv[1] 217 in ('--help-commands', 'egg_info', '--version', 'clean')): 218 # For these actions, neither Numpy nor Cython is required. 219 # 220 # They are required to succeed when pip is used to install PyStan 221 # when, for example, Numpy is not yet present. 222 try: 223 from setuptools import setup 224 except ImportError: 225 from distutils.core import setup 226 dist = setup(**metadata) 227 else: 228 import distutils.core 229 distutils.core._setup_stop_after = 'commandline' 230 from distutils.core import setup 231 try: 232 from Cython.Build import cythonize 233 # FIXME: if header only works, no need for numpy.distutils at all 234 from numpy.distutils.command import install 235 except ImportError: 236 raise SystemExit("Cython>=0.22 and NumPy are required.") 237 238 metadata['ext_modules'] = cythonize(extensions) 239 dist = setup(**metadata) 240 241 metadata['cmdclass'] = {'install': install.install} 242 try: 243 dist.run_commands() 244 except KeyboardInterrupt: 245 raise SystemExit("Interrupted") 246 except (IOError, os.error) as exc: 247 from distutils.util import grok_environment_error 248 error = grok_environment_error(exc) 249 except (DistutilsError, CCompilerError) as msg: 250 raise SystemExit("error: " + str(msg)) 251 252 253if __name__ == '__main__': 254 setup_package() 255