-
Notifications
You must be signed in to change notification settings - Fork 0
/
setup.py
238 lines (193 loc) · 8.58 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
# -*- coding: utf-8 -*-
"""Setup for the MOE webapp."""
import os
import shlex
import shutil
import subprocess
import sys
import warnings
from collections import namedtuple
from moe import __version__
from setuptools import setup, find_packages
from setuptools.command.install import install
try:
import sysconfig
except ImportError:
from distutils import sysconfig
here = os.path.abspath(os.path.dirname(__file__))
README = open(os.path.join(here, 'README.md')).read()
VERSION = __version__
CLASSIFIERS = """
Development Status :: 4 - Beta
Intended Audience :: Science/Research
Intended Audience :: Developers
Programming Language :: C++
Programming Language :: Python
Topic :: Software Development
Topic :: Scientific/Engineering
Operating System :: Unix
Operating System :: MacOS
"""
# If you change something here, change it in requirements.txt
requires = [
'pyramid',
'pyramid_mako',
'WebError',
'pytest',
'webtest',
'simplejson',
'numpy',
'scipy',
'colander',
'sphinx',
'breathe',
'sphinxcontrib-httpdomain',
'sphinx_rtd_theme',
]
MoeExecutable = namedtuple('MoeExecutable', ['env_var', 'exe_name'])
def find_path(moe_executable):
"""Return the path for an executable, or None if it cannot be found.
Performs the search in the following way:
1. Check the env var MOE_<EXECUTABLE>
2. Tries to find the executable in the system $PATH
"""
# First we see if the env var is set
path = os.environ.get(moe_executable.env_var, None)
if path is not None:
return path
# Try to guess where the executable is
for prefix in os.environ.get("PATH").split(os.pathsep):
potential_path = os.path.join(prefix, moe_executable.exe_name)
if os.path.isfile(potential_path):
path = potential_path
print "Could not find env var {0:s} for {1:s}, using {2:s} from $PATH".format(moe_executable.env_var, moe_executable.exe_name, path)
break
return path
class InstallCppComponents(install):
"""Install required C++ components."""
def run(self):
"""Run the install."""
install.run(self)
# Copy so we do not overwrite the user's environment later.
env = os.environ.copy()
# Sometimes we want to manually build the C++ (like in Docker)
if env.get('MOE_NO_BUILD_CPP', 'False') == 'True':
return
package_dir = os.path.join(self.install_lib, 'moe')
build_dir = os.path.join(package_dir, 'build')
cmake_path = find_path(
MoeExecutable(
env_var='MOE_CMAKE_PATH',
exe_name='cmake',
)
)
cmake_options = env.get('MOE_CMAKE_OPTS', '')
if cmake_options == '':
print "MOE_CMAKE_OPTS not set. Passing no extra args to cmake."
else:
print "Passing '{0:s}' args from MOE_CMAKE_OPTS to cmake.".format(cmake_options)
# Set env dict with cc and/or cxx path
# To find C/C++ compilers, we first try read MOE_CC/CXX_PATH and if they exist, we write to CC/CXX.
# Then we read and pass CC/CXX to cmake if they are set.
if 'MOE_CC_PATH' in env:
env['CC'] = env['MOE_CC_PATH']
if 'MOE_CXX_PATH' in env:
env['CXX'] = env['MOE_CXX_PATH']
if 'CC' in env:
print "Passing CC={0:s} to cmake.".format(env['CC'])
if 'CXX' in env:
print "Passing CXX={0:s} to cmake.".format(env['CXX'])
# rm the build directory if it exists
if os.path.exists(build_dir):
shutil.rmtree(build_dir)
os.mkdir(build_dir)
local_build_dir = os.path.join(here, 'moe', 'build')
if os.path.exists(local_build_dir):
shutil.rmtree(local_build_dir)
os.mkdir(local_build_dir)
cpp_location = os.path.join(here, 'moe', 'optimal_learning', 'cpp')
# Reformat options string: options & args that are separated by whitespace on the command line
# must be passed to subprocess.Popen in separate list elements.
cmake_options_split = shlex.split(cmake_options)
# Get info on Python paths (for the currently running Python) that we need to discover
# header/library locations for MOE
includepy, libdir, instsoname = sysconfig.get_config_vars('INCLUDEPY', 'LIBDIR', 'INSTSONAME')
# The meaning of 'LIBDIR' and 'INSTSONAME' is platform-specific. Handle the various cases.
# Initial value: 'LIBDIR' usually contains what we want, but one OSX case requires a different path.
moe_python_library_base = libdir
if not sys.platform.startswith('darwin'):
# For other platforms (only Linux is tested!) libdir + instsoname yields the shared object location.
if not sys.platform.startswith('linux'):
warnings.warn("Automatic path discovery untested outside of OSX and Linux. Taking our best guess.\n"
"Please check PYTHON_INCLUDE_DIR and PYTHON_LIBRARY paths below.")
else:
# on OSX versions that we tested against, LIBDIR looks like:
# /opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib
# and INSTSONAME looks like:
# Python.framework/Versions/2.7/Python
# What we want is:
# /opt/local/Library/Frameworks/Python.framework/Versions/2.7/Python
# This is easy in Linux b/c INSTSONAME is the name of a dynamic library file
# in foo/bar/lib whereas in OSX it's a partial path.
# Instead, remove the overlapping part of both paths to construct the result.
instsoname_components = instsoname.rsplit('/', 1)
framework_subpath = instsoname_components[0] # Probably is similar to 'Python.framework/Versions/2.7'
libdir_base, libdir_framework_subpath, libdir_subpath = libdir.rpartition(framework_subpath)
if not libdir_base and not libdir_framework_subpath:
# Did not find framework_subpath; the expected overlap isn't there so just join libdir + instsoname
warnings.warn("Unexpected OSX library paths.\n"
"Please check PYTHON_INCLUDE_DIR and PYTHON_LIBRARY paths below.")
else:
# Found framework_subpath overlap so skip the overlapped components
moe_python_library_base = libdir_base
moe_python_include_dir = includepy
moe_python_library = os.path.join(moe_python_library_base, instsoname)
# Print the Python paths we found so that the user can verify them if something goes wrong.
print 'PYTHON_INCLUDE_DIR (Expected full path to where Python.h is found): {0:s}'.format(moe_python_include_dir)
print 'PYTHON_LIBRARY (Expected path to Python shared object; e.g., libpython2.7.so or .dylib): {0:s}'.format(moe_python_library)
# Build the full cmake command using properly tokenized options
cmake_full_command = [
cmake_path,
'-DMOE_PYTHON_INCLUDE_DIR=' + moe_python_include_dir,
'-DMOE_PYTHON_LIBRARY=' + moe_python_library,
]
cmake_full_command.extend(cmake_options_split)
cmake_full_command.append(cpp_location)
# Run cmake
proc = subprocess.Popen(
cmake_full_command,
cwd=local_build_dir,
env=env,
)
proc.wait()
# Compile everything
proc = subprocess.Popen(["make"], cwd=local_build_dir, env=env)
proc.wait()
GPP_so = os.path.join(local_build_dir, 'GPP.so')
build_init = os.path.join(local_build_dir, '__init__.py')
shutil.copyfile(GPP_so, os.path.join(build_dir, 'GPP.so'))
shutil.copyfile(build_init, os.path.join(build_dir, '__init__.py'))
setup(name='MOE',
version=VERSION,
description='Metric Optimization Engine',
long_description=README,
classifiers=[_f for _f in CLASSIFIERS.split('\n') if _f],
author="Scott Clark and Eric Liu",
author_email='[email protected]',
url='https://github.com/Yelp/MOE',
keywords='bayesian global optimization optimal learning expected improvement experiment design',
packages=find_packages(),
include_package_data=True,
zip_safe=False,
install_requires=requires,
tests_require=requires,
test_suite="moe",
entry_points="""\
[paste.app_factory]
main = moe:main
""",
paster_plugins=['pyramid'],
cmdclass={
'install': InstallCppComponents,
},
)