forked from rraju1/cmake-buildscripts
-
Notifications
You must be signed in to change notification settings - Fork 0
/
PythonBuildConfig.cmake
186 lines (142 loc) · 7.64 KB
/
PythonBuildConfig.cmake
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
option(BUILD_PYTHON "Whether to build the Python programs and libraries." ${HAS_PYTHON})
if(BUILD_PYTHON AND NOT HAS_PYTHON)
message(WARNING "BUILD_PYTHON is enabled, but python was not found. Python packages will be disabled until Python is found.")
# create a local variable shadowing the cache variable
set(BUILD_PYTHON FALSE)
endif()
if(BUILD_PYTHON)
#------------------------------------------------------------------------------
# Checks the selected python is compatible with Amber.
#
# Fails if it is not.
#
# We try to aggregate all missing packages into a single error message.
#------------------------------------------------------------------------------
option(SKIP_PYTHON_PACKAGE_CHECKS "If true, the buildscript will not verify that you have the needed Python packages to run Amber's Python programs." FALSE)
if(NOT SKIP_PYTHON_PACKAGECHECKS)
# check "normal" packages
# --------------------------------------------------------------------
check_python_package(numpy HAVE_NUMPY)
check_python_package(scipy HAVE_SCIPY)
check_python_package(matplotlib HAVE_MATPLOTLIB)
if(AMBER_RELEASE)
# cython is not needed since pytraj will have been pre-cythonized
set(HAVE_CYTHON TRUE)
else()
check_python_package(cython HAVE_CYTHON)
endif()
if(NOT (HAVE_NUMPY AND HAVE_SCIPY AND HAVE_MATPLOTLIB AND HAVE_CYTHON))
set(ERROR_MESSAGE "Missing required Python packages:")
# add missing packages to string
foreach(PACKAGE numpy scipy matplotlib cython)
string(TOUPPER ${PACKAGE} PACKAGE_UCASE)
if(NOT HAVE_${PACKAGE_UCASE})
set(ERROR_MESSAGE "${ERROR_MESSAGE} ${PACKAGE}")
endif()
endforeach()
set(ERROR_MESSAGE "${ERROR_MESSAGE}. Please install these and try again. If you cannot install them, you may set BUILD_PYTHON to FALSE to \
skip building Python packages, or set DOWNLOAD_MINICONDA to TRUE to create a python environment automatically.")
message(FATAL_ERROR ${ERROR_MESSAGE})
endif()
# --------------------------------------------------------------------
# tkinter's capitalization changes based on the python version
check_python_package(tkinter HAVE_TKINTER)
check_python_package(Tkinter HAVE_TKINTER)
if(NOT HAVE_TKINTER)
message(FATAL_ERROR "Could not find the Python Tkinter package. You must install tk through your package manager (python-tk on Ubuntu, tk on Arch),\
and the tkinter Python package will get installed. If you cannot get Tkinter, disable BUILD_PYTHON to skip building Python packages, or enable DOWNLOAD_MINICONDA.")
endif()
# --------------------------------------------------------------------
# this one has a different error message
check_python_package(distutils.sysconfig HAVE_DISTUTILS_SYSCONFIG)
if(NOT HAVE_DISTUTILS_SYSCONFIG)
message(FATAL_ERROR "You need to install the Python development headers!")
endif()
endif()
#-------------------------------------------------------------------------------
# Build parts of installation commands
#-------------------------------------------------------------------------------
# for SOME REASON, things don't work properly on Windows unless the Python prefix argument uses backslashes.
# I have NO IDEA why
# so we have to execute this bit of code in every Python program's cmake_install.cmake to create CMAKE_INSTALL_PREFIX_BS
if(WIN32)
# I am so sorry for this mess.
# I will tell you that of the below variable references, "$ENV{DESTDIR}" and "${CMAKE_INSTALL_PREFIX}" are escaped, and not evaluated until install time.
# "${CMAKE_INSTALL_POSTFIX}" is not escaped, so it is evaluated now.
set(FIX_BACKSLASHES_CMD "string(REPLACE \"/\" \"\\\\\" CMAKE_INSTALL_PREFIX_BS \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_POSTFIX}\")")
else()
set(FIX_BACKSLASHES_CMD "set(CMAKE_INSTALL_PREFIX_BS \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_POSTFIX}\")")
endif()
# Amber's Python programs must be installed with the PYTHONPATH set to the install directory
# so determine the Python prefix relative to AMBERHOME
if(NOT ((DEFINED PREFIX_RELATIVE_PYTHONPATH) AND ("${PYTHON_EXECUTABLE}" STREQUAL "${PY_INTERP_FOR_RELATIVE_PYTHONPATH}")))
# determine key for INSTALL_SCHEMES
if(TARGET_WINDOWS)
set(SCHEME_KEY "nt")
else()
set(SCHEME_KEY "unix_prefix")
endif()
# this was REALLY hard to figure out. Seriously, I've created entire programs' build files in less time than it took to get this command working on all systems.
# This command peeks into the guts of distutils to see what the package installer thinks the install prefix is.
# Any solution involving sys.prefix doesn't work on all Linux distros since that seems to be incorrect on some installations
execute_process(
COMMAND "${PYTHON_EXECUTABLE}"
-c "import sys; from distutils.command.install import INSTALL_SCHEMES; print(INSTALL_SCHEMES['${SCHEME_KEY}']['purelib'].replace('$py_version_short', (str.split(sys.version))[0][0:3]).replace('$base', ''))"
RESULT_VARIABLE PYTHONPATH_CMD_RESULT
OUTPUT_VARIABLE PYTHONPATH_CMD_OUTPUT
ERROR_VARIABLE PYTHONPATH_CMD_STDERR
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT "${PYTHONPATH_CMD_RESULT}" EQUAL 0)
message(FATAL_ERROR "Failed to determine relative PYTHONPATH: python command failed with error ${PYTHONPATH_CMD_STDERR}")
endif()
# convert backslashes to forward slashes
file(TO_CMAKE_PATH "${PYTHONPATH_CMD_OUTPUT}" PYTHONPATH_CMD_OUTPUT)
message(STATUS "Python relative site-packages location: <prefix>${PYTHONPATH_CMD_OUTPUT}")
set(PREFIX_RELATIVE_PYTHONPATH "${PYTHONPATH_CMD_OUTPUT}" CACHE INTERNAL "Install folder of Python modules relative to the prefix they're installed to with setup.py install --prefix.")
set(PY_INTERP_FOR_RELATIVE_PYTHONPATH ${PYTHON_EXECUTABLE} CACHE INTERNAL "The python interpreter used to run the PREFIX_RELATIVE_PYTHONPATH check" FORCE)
endif()
set(PYTHONPATH_SET_CMD "\"PYTHONPATH=\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_POSTFIX}${PREFIX_RELATIVE_PYTHONPATH}\"")
# argument to force Python packages to get installed into the Amber install dir
set(PYTHON_PREFIX_ARG \"--prefix=\${CMAKE_INSTALL_PREFIX_BS}\")
if(MINGW)
set(PYTHON_COMPILER_ARG "--compiler=mingw32")
# force Python to use the MinGW compiler
set(PYTHON_CXX_ENVVAR_ARG CXX=${CMAKE_CXX_COMPILER})
else()
set(PYTHON_COMPILER_ARG "")
if(CROSSCOMPILE)
set(PYTHON_CXX_ENVVAR_ARG CXX=${CMAKE_CXX_COMPILER})
else()
# allow Python to use whatever compiler it wants, since object file formats are usually the same on Unix
set(PYTHON_CXX_ENVVAR_ARG "")
endif()
endif()
# We also need to define MS_WIN64 on 64 bit windows
if(${CMAKE_SYSTEM_NAME} STREQUAL Windows AND ${TARGET_ARCH} STREQUAL x86_64)
set(WIN64_DEFINE_ARG -DMS_WIN64)
else()
set(WIN64_DEFINE_ARG "")
endif()
#Macro to install a python library using distutils when make install is run.
#Runs the setup.py in the current source directory
#Args: arguments to pass to the python script
macro(install_python_library) # ARGUMENTS
list_to_space_separated(ARGN_SPC ${ARGN})
install(CODE "
${FIX_BACKSLASHES_CMD}
execute_process(
COMMAND \"${CMAKE_COMMAND}\" -E env
${PYTHONPATH_SET_CMD}
\"${PYTHON_EXECUTABLE}\"
./setup.py build -b \"${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/python-build\"
install -f ${PYTHON_PREFIX_ARG}
\"--install-scripts=\${CMAKE_INSTALL_PREFIX_BS}bin\"
${ARGN_SPC}
WORKING_DIRECTORY \"${CMAKE_CURRENT_SOURCE_DIR}\")"
COMPONENT Python)
endmacro(install_python_library)
else() # BUILD_PYTHON disabled
macro(install_python_library) # ARGUMENTS
#do nothing
endmacro(install_python_library)
endif()