forked from arbor-sim/arbor
-
Notifications
You must be signed in to change notification settings - Fork 0
/
setup.py
249 lines (210 loc) · 8.83 KB
/
setup.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
import os
import sys
import setuptools
import pathlib
from setuptools import Extension
from setuptools.command.build_ext import build_ext
from setuptools.command.install import install
import subprocess
try:
from wheel.bdist_wheel import bdist_wheel
WHEEL_INSTALLED = True
except:
#wheel package not installed.
WHEEL_INSTALLED = False
pass
# Singleton class that holds the settings configured using command line
# options. This information has to be stored in a singleton so that it
# can be passed between different stages of the build, and because pip
# has strange behavior between different versions.
class CL_opt:
instance = None
def __init__(self):
if not CL_opt.instance:
CL_opt.instance = {'mpi': False,
'gpu': 'none',
'vec': False,
'arch': 'none',
'neuroml': True,
'bundled': True,
'makejobs': 2}
def settings(self):
return CL_opt.instance
def cl_opt():
return CL_opt().settings()
# extend user_options the same way for all Command()s
user_options_ = [
('mpi', None, 'enable mpi support (requires MPI library)'),
('gpu=', None, 'enable nvidia cuda support (requires cudaruntime and nvcc) or amd hip support. Supported values: '
'none, cuda, cuda-clang, hip'),
('vec', None, 'enable vectorization'),
('arch=', None, 'cpu architecture, e.g. haswell, skylake, armv8.2-a+sve, znver2 (default native).'),
('neuroml', None, 'enable parsing neuroml morphologies in Arbor (requires libxml)'),
('sysdeps', None, 'don\'t use bundled 3rd party C++ dependencies (pybind11 and json). This flag forces use of dependencies installed on the system.'),
('makejobs=', None, 'the amount of jobs to run `make` with.')
]
# VERSION is in the same path as setup.py
here = os.path.abspath(os.path.dirname(__file__))
with open(os.path.join(here, 'VERSION')) as version_file:
version_ = version_file.read().strip()
# Get the contents of the readme
with open(os.path.join(here, 'python/readme.md'), encoding='utf-8') as f:
long_description = f.read()
def check_cmake():
try:
out = subprocess.check_output(['cmake', '--version'])
return True
except OSError:
return False
class _command_template:
"""
Override a setuptools-like command to augment the command line options.
Needs to appear before the command class in the class's argument list for
correct MRO.
Examples
--------
.. code-block: python
class install_command(_command_template, install):
pass
class complex_command(_command_template, mixin1, install):
def initialize_options(self):
# Both here and in `mixin1`, a `super` call is required
super().initialize_options()
# ...
"""
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
cls.user_options = super().user_options + user_options_
def initialize_options(self):
super().initialize_options()
self.mpi = None
self.gpu = None
self.arch = None
self.vec = None
self.neuroml = None
self.sysdeps = None
self.makejobs = 2
def finalize_options(self):
super().finalize_options()
try:
self.makejobs = int(self.makejobs)
except ValueError:
err = True
else:
err = False
if err or self.makejobs < 1:
raise AssertionError('makejobs must be a strictly positive integer')
def run(self):
# The options are stored in global variables:
opt = cl_opt()
# mpi : build with MPI support (boolean).
opt['mpi'] = self.mpi is not None
# gpu : compile for AMD/NVIDIA GPUs and choose compiler (string).
opt['gpu'] = "none" if self.gpu is None else self.gpu
# vec : generate SIMD vectorized kernels for CPU micro-architecture (boolean).
opt['vec'] = self.vec is not None
# arch : target CPU micro-architecture (string).
opt['arch'] = 'none' if self.arch is None else self.arch
# neuroml : compile with neuroml support for morphologies.
opt['neuroml'] = self.neuroml is not None
# bundled : use bundled/git-submoduled 3rd party libraries.
# By default use bundled libs.
opt['bundled'] = self.sysdeps is None
# makejobs : specify amount of jobs.
# By default 2.
opt['makejobs'] = int(self.makejobs)
super().run()
class install_command(_command_template, install):
pass
if WHEEL_INSTALLED:
class bdist_wheel_command(_command_template, bdist_wheel):
pass
class cmake_extension(Extension):
def __init__(self, name):
Extension.__init__(self, name, sources=[])
class cmake_build(build_ext):
def run(self):
if not check_cmake():
raise RuntimeError('CMake is not available. CMake 3.12 is required.')
# The path where CMake will be configured and Arbor will be built.
build_directory = os.path.abspath(self.build_temp)
# The path where the package will be copied after building.
lib_directory = os.path.abspath(self.build_lib)
# The path where the Python package will be compiled.
source_path = build_directory + '/python/arbor'
# Where to copy the package after it is built, so that whatever the next phase is
# can copy it into the target 'prefix' path.
dest_path = lib_directory + '/arbor'
opt = cl_opt()
cmake_args = [
'-DARB_WITH_PYTHON=on',
'-DPYTHON_EXECUTABLE=' + sys.executable,
'-DARB_WITH_MPI={}'.format( 'on' if opt['mpi'] else 'off'),
'-DARB_VECTORIZE={}'.format('on' if opt['vec'] else 'off'),
'-DARB_ARCH={}'.format(opt['arch']),
'-DARB_GPU={}'.format(opt['gpu']),
'-DARB_WITH_NEUROML={}'.format( 'on' if opt['neuroml'] else 'off'),
'-DARB_USE_BUNDLED_LIBS={}'.format('on' if opt['bundled'] else 'off'),
'-DCMAKE_BUILD_TYPE=Release' # we compile with debug symbols in release mode.
]
print('-'*5, 'command line arguments: {}'.format(opt))
print('-'*5, 'cmake arguments: {}'.format(cmake_args))
build_args = ['--config', 'Release']
# Assuming Makefiles
build_args += ['--', f'-j{opt["makejobs"]}']
env = os.environ.copy()
env['CXXFLAGS'] = '{}'.format(env.get('CXXFLAGS', ''))
if not os.path.exists(self.build_temp):
os.makedirs(self.build_temp)
# CMakeLists.txt is in the same directory as this setup.py file
cmake_list_dir = os.path.abspath(os.path.dirname(__file__))
print('-'*20, 'Configure CMake')
subprocess.check_call(['cmake', cmake_list_dir] + cmake_args,
cwd=self.build_temp, env=env)
print('-'*20, 'Build')
cmake_cmd = ['cmake', '--build', '.'] + build_args
subprocess.check_call(cmake_cmd,
cwd=self.build_temp)
# Copy from build path to some other place from whence it will later be installed.
# ... or something like that
# ... setuptools is an enigma monkey patched on a mystery
if not os.path.exists(dest_path):
os.makedirs(dest_path, exist_ok=True)
self.copy_tree(source_path, dest_path)
setuptools.setup(
name='arbor',
version=version_,
python_requires='>=3.6',
install_requires=['numpy'],
setup_requires=[],
zip_safe=False,
ext_modules=[cmake_extension('arbor')],
cmdclass={
'build_ext': cmake_build,
'install': install_command,
'bdist_wheel': bdist_wheel_command,
} if WHEEL_INSTALLED else {
'build_ext': cmake_build,
'install': install_command,
},
author='The Arbor dev team.',
url='https://github.com/arbor-sim/arbor',
description='High performance simulation of networks of multicompartment neurons.',
long_description=long_description,
long_description_content_type='text/markdown',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Science/Research',
'License :: OSI Approved :: BSD License',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: C++',
],
project_urls={
'Source': 'https://github.com/arbor-sim/arbor',
'Documentation': 'https://docs.arbor-sim.org',
'Bug Reports': 'https://github.com/arbor-sim/arbor/issues',
},
)