PageRenderTime 27ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/setup.py

https://github.com/jobovy/galpy
Python | 326 lines | 273 code | 24 blank | 29 comment | 68 complexity | e789aa76bf8cac275f08b774f29c720b MD5 | raw file
  1. import distutils.ccompiler
  2. import distutils.sysconfig as sysconfig
  3. import glob
  4. import os
  5. import os.path
  6. import platform
  7. import subprocess
  8. import sys
  9. from distutils.command.build_ext import build_ext as build_ext
  10. from distutils.core import Extension
  11. from distutils.errors import DistutilsPlatformError
  12. from setuptools import setup
  13. PY3= sys.version > '3'
  14. WIN32= platform.system() == 'Windows'
  15. no_compiler = False # Flag for cases where we are sure there is no compiler exists in user's system
  16. long_description= ''
  17. previous_line= ''
  18. with open('README.md') as dfile:
  19. for line in dfile:
  20. if not 'image' in line and not 'target' in line \
  21. and not 'DETAILED' in line and not '**main**' in line \
  22. and not '**development' in line \
  23. and not 'DETAILED' in previous_line:
  24. long_description+= line
  25. previous_line= line
  26. # Parse options; current options
  27. # --no-openmp: compile without OpenMP support
  28. # --coverage: compile with gcov support
  29. # --compiler= set the compiler by hand
  30. # --single_ext: compile all of the C code into a single extension (just for testing, do not use this)
  31. galpy_c_libraries = ['m','gsl','gslcblas','gomp']
  32. if WIN32:
  33. # On Windows it's unnecessary and erroneous to include m
  34. galpy_c_libraries.remove('m')
  35. # Windows does not need 'gomp' whether compiled with OpenMP or not
  36. galpy_c_libraries.remove('gomp')
  37. #Option to use Intel compilers
  38. try:
  39. compiler_option_pos = ['--compiler=' in opt for opt in sys.argv]\
  40. .index(True)
  41. except ValueError:
  42. use_intel_compiler= False
  43. else:
  44. use_intel_compiler= 'intel' in sys.argv[compiler_option_pos].split('=')[1]
  45. if use_intel_compiler and not WIN32:
  46. import numpy.distutils.intelccompiler
  47. elif use_intel_compiler and WIN32:
  48. import __intelcompiler
  49. if use_intel_compiler: # OpenMP by default included for Intel, see #416
  50. galpy_c_libraries.remove('gomp')
  51. #Option to forego OpenMP
  52. try:
  53. openmp_pos = sys.argv.index('--no-openmp')
  54. except ValueError:
  55. if "PYODIDE" in os.environ:
  56. extra_compile_args= ["-DNO_OMP"]
  57. galpy_c_libraries.remove('gomp')
  58. else:
  59. extra_compile_args = ["-fopenmp" if not WIN32 else "/openmp"]
  60. else:
  61. del sys.argv[openmp_pos]
  62. extra_compile_args= ["-DNO_OMP"]
  63. if not WIN32: # Because windows guarantee do not have 'gomp' in the list
  64. galpy_c_libraries.remove('gomp')
  65. #Option to track coverage
  66. try:
  67. coverage_pos = sys.argv.index('--coverage')
  68. except ValueError:
  69. extra_link_args= []
  70. else:
  71. del sys.argv[coverage_pos]
  72. extra_compile_args.extend(["-O0","--coverage","-D USING_COVERAGE"])
  73. extra_link_args= ["--coverage"]
  74. #Option to compile everything into a single extension
  75. try:
  76. single_ext_pos = sys.argv.index('--single_ext')
  77. except ValueError:
  78. single_ext= False
  79. else:
  80. del sys.argv[single_ext_pos]
  81. single_ext= True
  82. #Option to not compile any extension
  83. try:
  84. no_ext_pos = sys.argv.index('--no_ext')
  85. except ValueError:
  86. no_ext= False
  87. else:
  88. del sys.argv[no_ext_pos]
  89. no_ext= True
  90. #code to check the GSL version; list cmd w/ shell=True only works on Windows
  91. # (https://docs.python.org/3/library/subprocess.html#converting-argument-sequence)
  92. cmd= ['gsl-config','--version']
  93. try:
  94. if sys.version_info < (2,7): #subprocess.check_output does not exist
  95. gsl_version= subprocess.Popen(cmd,shell=sys.platform.startswith('win'),
  96. stdout=subprocess.PIPE).communicate()[0]
  97. else:
  98. gsl_version= \
  99. subprocess.check_output(cmd,
  100. shell=sys.platform.startswith('win'))
  101. except (OSError,subprocess.CalledProcessError):
  102. if "PYODIDE" in os.environ:
  103. gsl_version= ['2','7']
  104. else:
  105. gsl_version= ['0','0']
  106. else:
  107. if PY3:
  108. gsl_version= gsl_version.decode('utf-8')
  109. gsl_version= gsl_version.split('.')
  110. extra_compile_args.append("-D GSL_MAJOR_VERSION=%s" % (gsl_version[0]))
  111. #HACK for testing
  112. #gsl_version= ['0','0']
  113. # MSVC: inline does not exist (not C99!); default = not necessarily actual, but will have to do for now...
  114. # Note for the futureL could now get the actual compiler in the BuildExt class
  115. # below
  116. if distutils.ccompiler.get_default_compiler().lower() == 'msvc':
  117. extra_compile_args.append("-Dinline=__inline")
  118. # only msvc compiler can be tested with initialize(), msvc is a default on windows
  119. # check for 'msvc' not WIN32, user can use other compiler like 'mingw32', in such case compiler exists for them
  120. try:
  121. test_compiler = distutils.ccompiler.new_compiler()
  122. test_compiler.initialize() # try to initialize a test compiler to see if compiler presented
  123. except DistutilsPlatformError: # this error will be raised if no compiler in the system
  124. no_compiler = True
  125. # To properly export GSL symbols on Windows, need to defined GSL_DLL and WIN32
  126. if WIN32:
  127. extra_compile_args.append("-DGSL_DLL")
  128. extra_compile_args.append("-DWIN32")
  129. #main C extension
  130. galpy_c_src= ['galpy/util/bovy_symplecticode.c', 'galpy/util/bovy_rk.c',
  131. 'galpy/util/leung_dop853.c','galpy/util/bovy_coords.c']
  132. galpy_c_src.extend(glob.glob('galpy/potential/potential_c_ext/*.c'))
  133. galpy_c_src.extend(glob.glob('galpy/potential/interppotential_c_ext/*.c'))
  134. galpy_c_src.extend(glob.glob('galpy/util/interp_2d/*.c'))
  135. galpy_c_src.extend(glob.glob('galpy/orbit/orbit_c_ext/*.c'))
  136. galpy_c_src.extend(glob.glob('galpy/actionAngle/actionAngle_c_ext/*.c'))
  137. galpy_c_include_dirs= ['galpy/util',
  138. 'galpy/util/interp_2d',
  139. 'galpy/potential/potential_c_ext',
  140. 'galpy/potential/interppotential_c_ext',
  141. 'galpy/orbit/orbit_c_ext',
  142. 'galpy/actionAngle/actionAngle_c_ext']
  143. #actionAngleTorus C extension (files here, so we can compile a single extension if so desidered)
  144. actionAngleTorus_c_src= \
  145. glob.glob('galpy/actionAngle/actionAngleTorus_c_ext/*.cc')
  146. actionAngleTorus_c_src.extend(\
  147. glob.glob('galpy/actionAngle/actionAngleTorus_c_ext/torus/src/*.cc'))
  148. actionAngleTorus_c_src.extend(\
  149. ['galpy/actionAngle/actionAngleTorus_c_ext/torus/src/utils/CHB.cc',
  150. 'galpy/actionAngle/actionAngleTorus_c_ext/torus/src/utils/Err.cc',
  151. 'galpy/actionAngle/actionAngleTorus_c_ext/torus/src/utils/Compress.cc',
  152. 'galpy/actionAngle/actionAngleTorus_c_ext/torus/src/utils/Numerics.cc',
  153. 'galpy/actionAngle/actionAngleTorus_c_ext/torus/src/utils/PJMNum.cc'])
  154. actionAngleTorus_c_src.extend(\
  155. glob.glob('galpy/potential/potential_c_ext/*.c'))
  156. actionAngleTorus_c_src.extend(\
  157. glob.glob('galpy/orbit/orbit_c_ext/integrateFullOrbit.c'))
  158. actionAngleTorus_c_src.extend(glob.glob('galpy/util/interp_2d/*.c'))
  159. actionAngleTorus_c_src.extend(glob.glob('galpy/util/*.c'))
  160. actionAngleTorus_include_dirs= \
  161. ['galpy/actionAngle/actionAngleTorus_c_ext',
  162. 'galpy/actionAngle/actionAngleTorus_c_ext/torus/src',
  163. 'galpy/actionAngle/actionAngleTorus_c_ext/torus/src/utils',
  164. 'galpy/actionAngle/actionAngle_c_ext',
  165. 'galpy/util/interp_2d',
  166. 'galpy/util',
  167. 'galpy/orbit/orbit_c_ext',
  168. 'galpy/potential/potential_c_ext']
  169. if single_ext: #add the code and libraries for the actionAngleTorus extensions
  170. if os.path.exists('galpy/actionAngle/actionAngleTorus_c_ext/torus/src'):
  171. galpy_c_src.extend(actionAngleTorus_c_src)
  172. galpy_c_src= list(set(galpy_c_src))
  173. galpy_c_include_dirs.extend(actionAngleTorus_include_dirs)
  174. galpy_c_include_dirs= list(set(galpy_c_include_dirs))
  175. #Installation of this extension using the GSL may (silently) fail, if the GSL
  176. #is built for the wrong architecture, on Mac you can install the GSL correctly
  177. #using
  178. #brew install gsl --universal
  179. galpy_c= Extension('libgalpy',
  180. sources=galpy_c_src,
  181. libraries=galpy_c_libraries,
  182. include_dirs=galpy_c_include_dirs,
  183. extra_compile_args=extra_compile_args,
  184. extra_link_args=extra_link_args)
  185. ext_modules=[]
  186. if float(gsl_version[0]) >= 1. \
  187. and (float(gsl_version[0]) >= 2. or float(gsl_version[1]) >= 14.):
  188. galpy_c_incl= True
  189. ext_modules.append(galpy_c)
  190. else:
  191. galpy_c_incl= False
  192. # Add the actionAngleTorus extension (src and include specified above)
  193. actionAngleTorus_c= Extension('libgalpy_actionAngleTorus',
  194. sources=actionAngleTorus_c_src,
  195. libraries=galpy_c_libraries,
  196. include_dirs=actionAngleTorus_include_dirs,
  197. extra_compile_args=extra_compile_args,
  198. extra_link_args=extra_link_args)
  199. if float(gsl_version[0]) >= 1. \
  200. and (float(gsl_version[0]) >= 2. or float(gsl_version[1]) >= 14.) and \
  201. os.path.exists('galpy/actionAngle/actionAngleTorus_c_ext/torus/src') \
  202. and not single_ext:
  203. actionAngleTorus_c_incl= True
  204. ext_modules.append(actionAngleTorus_c)
  205. else:
  206. actionAngleTorus_c_incl= False
  207. # Test whether compiler allows for the -fopenmp flag and all other flags
  208. # to guard against compilation errors
  209. # (https://stackoverflow.com/a/54518348)
  210. def compiler_has_flag(compiler,flagname):
  211. """Test whether a given compiler supports a given option"""
  212. import tempfile
  213. from distutils.errors import CompileError
  214. with tempfile.NamedTemporaryFile('w', suffix='.cpp') as f:
  215. f.write('int main (int argc, char **argv) { return 0; }')
  216. try:
  217. compiler.compile([f.name], extra_postargs=[flagname])
  218. except CompileError:
  219. return False
  220. return True
  221. # Now need to subclass BuildExt to access the compiler used and check flags
  222. class BuildExt(build_ext):
  223. def build_extensions(self):
  224. ct= self.compiler.compiler_type
  225. if ct == 'unix':
  226. for ext in self.extensions:
  227. # only add flags which pass the flag_filter
  228. extra_compile_args= []
  229. for flag in ext.extra_compile_args:
  230. if compiler_has_flag(self.compiler,flag):
  231. extra_compile_args.append(flag)
  232. ext.extra_compile_args= extra_compile_args
  233. build_ext.build_extensions(self)
  234. setup(cmdclass=dict(build_ext=BuildExt), # this to allow compiler check above
  235. name='galpy',
  236. version='1.8.1.dev0',
  237. description='Galactic Dynamics in python',
  238. author='Jo Bovy',
  239. author_email='bovy@astro.utoronto.ca',
  240. license='New BSD',
  241. long_description=long_description,
  242. long_description_content_type='text/markdown',
  243. url='http://github.com/jobovy/galpy',
  244. package_dir = {'galpy/': ''},
  245. packages=['galpy','galpy/orbit','galpy/potential',
  246. 'galpy/df','galpy/util','galpy/snapshot',
  247. 'galpy/actionAngle'],
  248. package_data={'galpy/orbit':['named_objects.json'],
  249. 'galpy/df':['data/*.sav'],
  250. "": ["README.md","README.dev","LICENSE","AUTHORS.rst"]},
  251. include_package_data=True,
  252. install_requires=['setuptools','numpy>=1.7','scipy','matplotlib'],
  253. ext_modules=ext_modules if not no_compiler and not no_ext else None,
  254. classifiers=[
  255. "Development Status :: 6 - Mature",
  256. "Intended Audience :: Science/Research",
  257. "License :: OSI Approved :: BSD License",
  258. "Operating System :: OS Independent",
  259. "Programming Language :: C",
  260. "Programming Language :: Python :: 3.7",
  261. "Programming Language :: Python :: 3.8",
  262. "Programming Language :: Python :: 3.9",
  263. "Programming Language :: Python :: 3.10",
  264. "Topic :: Scientific/Engineering :: Astronomy",
  265. "Topic :: Scientific/Engineering :: Physics"]
  266. )
  267. def print_gsl_message(num_messages=1):
  268. if num_messages > 1:
  269. this_str= 'these installations'
  270. else:
  271. this_str= 'this installation'
  272. print('If you believe that %s should have worked, make sure\n(1) that the GSL include/ directory can be found by the compiler (you might have to edit CFLAGS for this: export CFLAGS="$CFLAGS -I/path/to/gsl/include/", or equivalent for C-type shells; replace /path/to/gsl/include/ with the actual path to the include directory),\n(2) that the GSL library can be found by the linker (you might have to edit LDFLAGS for this: export LDFLAGS="$LDFLAGS -L/path/to/gsl/lib/", or equivalent for C-type shells; replace /path/to/gsl/lib/ with the actual path to the lib directory),\n(3) and that `gsl-config --version` returns the correct version' % this_str)
  273. num_gsl_warn= 0
  274. if not galpy_c_incl:
  275. num_gsl_warn+= 1
  276. print('\033[91;1m'+'WARNING: galpy C library not installed because your GSL version < 1.14'+'\033[0m')
  277. if not actionAngleTorus_c_incl and not single_ext:
  278. num_gsl_warn+= 1
  279. print('\033[91;1m'+'WARNING: galpy action-angle-torus C library not installed because your GSL version < 1.14 or because you did not first download the torus code as explained in the installation guide in the html documentation'+'\033[0m')
  280. if num_gsl_warn > 0:
  281. print_gsl_message(num_messages=num_gsl_warn)
  282. print('\033[1m'+'These warning messages about the C code do not mean that the python package was not installed successfully'+'\033[0m')
  283. print('\033[1m'+'Finished installing galpy'+'\033[0m')
  284. print('You can run the test suite using `pytest -v tests/` to check the installation (but note that the test suite currently takes about 50 minutes to run)')
  285. #if single_ext, symlink the other (non-compiled) extensions to libgalpy.so (use EXT_SUFFIX for python3 compatibility)
  286. if PY3:
  287. _ext_suffix= sysconfig.get_config_var('EXT_SUFFIX')
  288. else:
  289. _ext_suffix= '.so'
  290. if single_ext:
  291. if not os.path.exists('libgalpy_actionAngleTorus%s' % _ext_suffix) \
  292. and os.path.exists('galpy/actionAngle/actionAngleTorus_c_ext/torus/src'):
  293. os.symlink('libgalpy%s' % _ext_suffix,
  294. 'libgalpy_actionAngleTorus%s' % _ext_suffix)