-
Notifications
You must be signed in to change notification settings - Fork 6
/
setup.py
359 lines (301 loc) · 12.6 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
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
## @file setup.py
## @brief Python distutils code for libSBML Python module (including dependencies)
## @author Michael Hucka
## @author Ben Bornstein
## @author Ben Kovitz
## @author Frank Bergmann ([email protected])
##
##<!---------------------------------------------------------------------------
## This file is part of libSBML. Please visit http://sbml.org for more
## information about SBML, and the latest version of libSBML.
##
## Copyright (C) 2013-2018 jointly by the following organizations:
## 1. California Institute of Technology, Pasadena, CA, USA
## 2. EMBL European Bioinformatics Institute (EMBL-EBI), Hinxton, UK
## 3. University of Heidelberg, Heidelberg, Germany
##
## Copyright 2005-2010 California Institute of Technology.
## Copyright 2002-2005 California Institute of Technology and
## Japan Science and Technology Corporation.
##
## This library is free software; you can redistribute it and/or modify it
## under the terms of the GNU Lesser General Public License as published by
## the Free Software Foundation. A copy of the license agreement is provided
## in the file named "LICENSE.txt" included with this software distribution
## and also available online as http://sbml.org/software/libsbml/license.html
##----------------------------------------------------------------------- -->*/
import errno
import os
import sys
import shutil
import platform
import sysconfig
from os.path import abspath, exists, join, split
from setuptools import setup, Extension
from setuptools.command.build_ext import build_ext
def get_python_include():
temp = os.getenv('PYTHON_INCLUDE_DIR')
if temp:
return temp
path = sysconfig.get_paths()['include']
if exists(path):
return path
# for whatever reason 2.7 on centos returns a wrong path here
return sysconfig.get_config_vars()['INCLUDEPY']
def get_win_python_lib():
vars = sysconfig.get_config_vars()
for k in ['prefix', 'installed_base', 'installed_platbase']:
if k not in vars:
continue
path = os.path.join(vars[k], 'libs', 'python' + vars['py_version_nodot'] + '.lib')
if os.path.exists(path):
return path
return None
def prepend_variables(args, variables):
for var in variables:
temp = os.getenv(var)
if temp:
if var == 'CMAKE_GENERATOR':
args = ['-G', temp] + args
else:
args.insert(0, '-D' + var + '=' +temp)
return args
def get_lib_full_path(path, partial):
for file in os.listdir(path):
if partial in file:
return os.path.join(path, file)
return None
def makedirs(folder, *args, **kwargs):
try:
return os.makedirs(folder, exist_ok=True, *args, **kwargs)
except TypeError:
# Unexpected arguments encountered
pass
try:
# Should work is TypeError was caused by exist_ok, eg., Py2
return os.makedirs(folder, *args, **kwargs)
except OSError as e:
if e.errno != errno.EEXIST:
raise
if os.path.isfile(folder):
# folder is a file, raise OSError just like os.makedirs() in Py3
raise
def get_dir_if_exists(variable, default):
value = os.getenv(variable, default)
value = abspath(value)
if not exists(value):
return None
return value
global SRC_DIR
SRC_DIR = get_dir_if_exists('LIBSBML_SRC_DIR', './libsbml_source')
global DEP_DIR
DEP_DIR = get_dir_if_exists('LIBSBML_DEP_DIR', './libsbml_dependencies/')
DEP_DIR32 = get_dir_if_exists('LIBSBML_DEP_DIR_32', '../win_libsbml_dependencies_32/')
DEP_DIR64 = get_dir_if_exists('LIBSBML_DEP_DIR_64', '../win_libsbml_dependencies_64/')
packages = ['comp', 'fbc', 'layout', 'qual', 'groups', 'multi', 'render', 'distrib', 'spatial']
if not os.getenv('LIBSBML_EXPERIMENTAL'):
package_name = 'python-libsbml'
else:
package_name = 'python-libsbml-experimental'
packages += ['arrays', 'dyn', 'requiredelements']
if not SRC_DIR:
src_defined = os.getenv('LIBSBML_SRC_DIR')
if src_defined:
raise ValueError("LibSBML Source defined as: {0}, but coun't be found".format(src_defined))
else:
raise ValueError("LibSBML Source not specified or not present, define LIBSBML_SRC_DIR.")
print ("Using libSBML from: {0}".format(SRC_DIR))
version_file_name = join(SRC_DIR, 'VERSION.txt')
print ("Using VERSION.txt: {0}".format(version_file_name))
if not exists(version_file_name):
print(os.listdir(SRC_DIR))
raise ValueError("Invalid libSBML Source directory, no VERSION.txt file")
with open(version_file_name, 'r') as version_file:
VERSION = version_file.readline().strip()
print ("Creating: {0}".format(package_name))
print ("Version is: {0}".format(VERSION))
print ("building for python: {0}".format(sys.version))
if not exists('libsbml'):
makedirs('libsbml')
class CMakeExtension(Extension):
"""Override the default setuptools extension building."""
def __init__(self, name, sources=(), **kwargs):
"""Initialize by passing on arguments."""
# Don't invoke the original `build_ext` for this special extension.
try:
super(CMakeExtension, self).__init__(name=name, sources=list(sources), **kwargs)
except:
Extension.__init__(self, name, list(sources), **kwargs)
class CMakeBuild(build_ext):
"""Override `build_ext` to then register it in the command classes."""
def run(self):
"""
Call Cmake and build every extension.
Overrides parent's method.
"""
for ext in self.extensions:
self.build_cmake(ext)
try:
super(CMakeBuild, self).run()
except:
build_ext.run(self)
def build_cmake(self, extension):
"""Configure `extension` with CMake and build modules."""
cwd = os.getcwd()
build_temp = self.build_temp
suffix = build_temp[build_temp.find('temp.') + 5:]
if '/' in suffix:
suffix = suffix[:suffix.rfind('/')]
if '\\' in suffix:
suffix = suffix[:suffix.rfind('\\')]
ext_dir = self.get_ext_fullpath(extension.name)
makedirs(build_temp)
target_lib_path = abspath(ext_dir)
target_dir_path, name = split(target_lib_path)
makedirs(target_dir_path)
makedirs(join(cwd, 'libsbml'))
print ('name: {0}'.format(name))
print ('build temp: {0}'.format(build_temp))
print ('extension name: {0}'.format(extension.name))
print ('extension dir: {0}'.format(ext_dir))
print ('target_dir_path: {0}'.format(target_dir_path))
print ('target_lib_path: {0}'.format(target_lib_path))
print ('suffix: {0}'.format(suffix))
print ('cwd: {0}'.format(cwd))
# example of cmake args
config = 'Debug' if self.debug else 'Release'
print ('name: {0}, tmp: {1}'.format(name, build_temp))
is_osx = platform.system() == 'Darwin'
is_win = platform.system() == 'Windows'
is_win_32 = is_win and ('win32' in name or 'win32' in build_temp)
cmake_args = [
'-DCMAKE_BUILD_TYPE=' + config,
'-DWITH_STATIC_RUNTIME=ON'
]
cmake_args = prepend_variables(cmake_args, [
'CMAKE_CXX_COMPILER',
'CMAKE_C_COMPILER',
'CMAKE_LINKER',
'CMAKE_GENERATOR'
])
if is_win_32:
if not '-G' in str(cmake_args):
cmake_args.append('-A')
cmake_args.append('win32')
if is_osx:
if 'arm64' in suffix:
cmake_args.append('-DCMAKE_OSX_ARCHITECTURES=arm64')
else:
cmake_args.append('-DCMAKE_OSX_ARCHITECTURES=x86_64')
# example of build args
build_args = [
'--config', config,
'--'
]
global DEP_DIR
if not self.dry_run:
print("compiling dependencies")
dep_suffix = sysconfig.get_platform()
dep_build_dir = os.path.join(cwd, 'build_dependencies_' + dep_suffix)
dep_inst_dir = os.path.join(cwd, 'install_dependencies_' + dep_suffix)
if not exists(dep_inst_dir):
dep_src_dir = os.path.join(cwd, 'libsbml_dependencies')
makedirs(dep_build_dir)
os.chdir(dep_build_dir)
self.spawn(['cmake', dep_src_dir] + cmake_args
+ [
'-DCMAKE_INSTALL_PREFIX=' + dep_inst_dir,
'-DWITH_BZIP2=ON',
'-DWITH_CHECK=OFF',
'-DWITH_EXPAT=ON',
'-DWITH_XERCES=OFF',
'-DWITH_ICONV=OFF',
'-DWITH_LIBXML=OFF',
]
)
self.spawn(['cmake', '--build', '.', '--target', 'install'] + build_args)
os.chdir(cwd)
DEP_DIR = dep_inst_dir
libsbml_args = [
'-DWITH_EXPAT=ON',
'-DWITH_LIBXML=OFF',
'-DWITH_SWIG=ON',
'-DWITH_ZLIB=ON',
'-DWITH_PYTHON=ON',
'-DPYTHON_EXECUTABLE=' + sys.executable,
'-DPYTHON_INCLUDE_DIR=' + get_python_include()
]
for package in packages:
libsbml_args.append('-DENABLE_{0}=ON'.format(package.upper()))
libsbml_args = prepend_variables(libsbml_args, [
'SWIG_DIR',
'SWIG_EXECUTABLE'
])
if not is_win:
libsbml_args.append('-DPYTHON_USE_DYNAMIC_LOOKUP=ON')
else:
lib_path = get_win_python_lib()
if lib_path is not None:
libsbml_args.append('-DPYTHON_LIBRARY={0}'.format(lib_path))
cmake_args = cmake_args + libsbml_args
if DEP_DIR:
zlib = get_lib_full_path(os.path.join(DEP_DIR, 'lib'), 'zlib')
if not zlib:
zlib = get_lib_full_path(os.path.join(DEP_DIR, 'lib'), 'zdll')
cmake_args.append('-DLIBSBML_DEPENDENCY_DIR=' + DEP_DIR)
if is_win_32:
if DEP_DIR32:
cmake_args.append('-DLIBSBML_DEPENDENCY_DIR=' + DEP_DIR32)
elif is_win:
if DEP_DIR64:
cmake_args.append('-DLIBSBML_DEPENDENCY_DIR=' + DEP_DIR64)
os.chdir(build_temp)
self.spawn(['cmake', SRC_DIR] + cmake_args)
if not self.dry_run:
self.spawn(['cmake', '--build', '.', '--target', 'binding_python_lib'] + build_args)
# at this point the build should be complete, and we have all the files
# neeed in the temp build_folder
init_py2 = None
init_py3 = None
dst_file = join(target_dir_path, '__init__.py')
for root, dirs, files in os.walk(".", topdown=False):
for name in files:
# 1. find pyd and copy to target_lib_path
if name.endswith('.pyd') or name == '_libsbml.so' or name == '_libsbml.dylib':
pyd_file = join(root, name)
print('copying pyd file to output file')
shutil.copyfile(pyd_file, target_lib_path)
# 2. get scripts and copy to target_lib_path.parent.__init__.py corresponding to version
if name == 'libsbml.py':
src_file = join(root, name)
shutil.copyfile(src_file, dst_file)
if name == 'libsbml2.py':
init_py2 = join(root, name)
if name == 'libsbml3.py':
init_py3 = join(root, name)
if init_py2 and exists(init_py2) and sys.version_info.major == 2:
shutil.copyfile(init_py2, dst_file)
if init_py3 and exists(init_py3) and sys.version_info.major == 3:
shutil.copyfile(init_py3, dst_file)
os.chdir(cwd)
setup(name = package_name,
version = VERSION,
description = "LibSBML Python API",
long_description = ("LibSBML is a library for reading, writing and "+
"manipulating the Systems Biology Markup Language "+
"(SBML). It is written in ISO C and C++, supports "+
"SBML Levels 1, 2 and 3, and runs on Linux, Microsoft "+
"Windows, and Apple MacOS X. For more information "+
"about SBML, please see http://sbml.org."),
license = "LGPL",
author = "SBML Team",
author_email = "[email protected]",
url = "http://sbml.org",
packages = ["libsbml"],
package_dir = {'libsbml': 'libsbml'},
ext_package = "libsbml",
ext_modules=[CMakeExtension('_libsbml')],
cmdclass={
'build_ext': CMakeBuild,
}
)