-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathpython-wheel.cmake
209 lines (174 loc) · 7.18 KB
/
python-wheel.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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
include(python-wheel-globals)
set(PY_WHEEL_SETUP_FILE "${CMAKE_CURRENT_LIST_DIR}/setup.py.in" CACHE INTERNAL "")
set(TEST_WHEEL_BASH "${CMAKE_CURRENT_LIST_DIR}/test-wheel.bash" CACHE INTERNAL "")
set(PY_CHANGE_TAG_FILE "${CMAKE_CURRENT_LIST_DIR}/change-wheel-tag-macos.py" CACHE INTERNAL "")
# Target for building all added wheels
add_custom_target(wheel ALL)
# Function for transforming a CMake string array LIST
# to a python array OUT (without brackets).
macro (to_python_list_string LIST OUT)
list(TRANSFORM ${LIST} PREPEND "'")
list(TRANSFORM ${LIST} APPEND "'")
list(JOIN ${LIST} ", " ${OUT})
endmacro()
# Copy SOURCE's target file, or if SOURCE is an interface-lib:
# all linked target files to the destination DEST. Add the
# command to the target TARGET.
function (_copy_target TARGET SOURCE DEST)
get_target_property(source_type ${SOURCE} TYPE)
if (source_type STREQUAL "INTERFACE_LIBRARY")
get_target_property(source_libs ${SOURCE} INTERFACE_LINK_LIBRARIES)
foreach (source_lib IN LISTS source_libs)
add_custom_command(TARGET ${TARGET}
COMMAND ${CMAKE_COMMAND} -E copy "$<TARGET_FILE:${source_lib}>" "${DEST}")
endforeach()
else()
add_custom_command(TARGET ${TARGET}
COMMAND ${CMAKE_COMMAND} -E copy "$<TARGET_FILE:${SOURCE}>" "${DEST}")
endif()
endfunction()
function (add_wheel WHEEL_TARGET)
set(Python_FIND_VIRTUALENV FIRST) # Favor venv over system install
find_package(Python3 COMPONENTS Interpreter Development REQUIRED)
# Parse arguments
cmake_parse_arguments(WHEEL
""
"NAME;AUTHOR;URL;EMAIL;PYTHON_REQUIRES;VERSION;DESCRIPTION;README_PATH;LICENSE_PATH"
"TARGET_DEPENDENCIES;MODULE_DEPENDENCIES;DEPLOY_FILES;SUBMODULES;SCRIPTS;PYTHON_PACKAGE_DIRS" ${ARGN})
to_python_list_string(WHEEL_MODULE_DEPENDENCIES WHEEL_MODULE_DEPENDENCIES_PYLIST)
if (NOT WHEEL_VERSION)
message(FATAL_ERROR "Missing wheel version.")
endif()
if (NOT WHEEL_AUTHOR)
message(FATAL_ERROR "Missing wheel author.")
endif()
if (NOT WHEEL_URL)
set(WHEEL_URL "")
endif()
if (NOT WHEEL_EMAIL)
set(WHEEL_EMAIL "")
endif()
if (NOT WHEEL_PYTHON_REQUIRES)
set(WHEEL_PYTHON_REQUIRES ">=3.8")
endif()
if (NOT WHEEL_LICENSE_PATH)
# Default license file
set(WHEEL_LICENSE_PATH "${CMAKE_SOURCE_DIR}/LICENSE")
endif()
if (NOT WHEEL_NAME)
set(WHEEL_NAME "${WHEEL_TARGET}")
endif()
# Set up wheel build dir
set(WHEEL_LIB_DIR "${CMAKE_CURRENT_BINARY_DIR}/${WHEEL_NAME}.wheel")
set(WHEEL_PACKAGE_DIR "${WHEEL_LIB_DIR}/${WHEEL_NAME}")
# Create the package directory and __init__.py
file(REMOVE_RECURSE "${WHEEL_LIB_DIR}")
file(MAKE_DIRECTORY "${WHEEL_LIB_DIR}")
file(MAKE_DIRECTORY "${WHEEL_PACKAGE_DIR}")
file(WRITE "${WHEEL_PACKAGE_DIR}/__init__.py" "from .${WHEEL_TARGET} import *")
foreach(SUBMODULE IN LISTS WHEEL_SUBMODULES)
string(REPLACE "." "/" SUBMODULE_DIR ${SUBMODULE})
file(MAKE_DIRECTORY "${WHEEL_PACKAGE_DIR}/${SUBMODULE_DIR}")
# Create repeating dots by removing all non-dot characters from the submodule string
string(REGEX REPLACE "[^\\.]" "" DOTS ${SUBMODULE})
# Correctly adjust the import statement considering depth
file(WRITE "${WHEEL_PACKAGE_DIR}/${SUBMODULE_DIR}/__init__.py" "from .${DOTS}.${WHEEL_TARGET}.${SUBMODULE} import *")
endforeach()
# Only one wheel allowed per project.
file(GLOB LOCAL_WHEEL_LIST "${CMAKE_CURRENT_BINARY_DIR}/*.wheel")
list(LENGTH LOCAL_WHEEL_LIST LOCAL_WHEEL_LIST_LENGTH)
if (LOCAL_WHEEL_LIST_LENGTH GREATER 1)
message(FATAL_ERROR "You cannot create more than one wheel in the same binary dir.")
elseif (LOCAL_WHEEL_LIST_LENGTH LESS 1)
message(FATAL_ERROR "This should not happen.")
endif()
# Copy module + dependencies into build dir
add_custom_target(${WHEEL_TARGET}-copy-files)
_copy_target(${WHEEL_TARGET}-copy-files ${WHEEL_TARGET} "${WHEEL_PACKAGE_DIR}")
add_dependencies(${WHEEL_TARGET}-copy-files ${WHEEL_TARGET})
foreach (dep IN LISTS WHEEL_TARGET_DEPENDENCIES)
if (TARGET ${dep})
add_dependencies(${WHEEL_TARGET} ${dep})
_copy_target(${WHEEL_TARGET}-copy-files ${dep} ${WHEEL_PACKAGE_DIR})
else()
message(FATAL_ERROR "Not a target ${dep}")
endif()
endforeach()
if (WHEEL_LICENSE_PATH)
add_custom_command(TARGET ${WHEEL_TARGET}-copy-files
COMMAND ${CMAKE_COMMAND} -E copy "${WHEEL_LICENSE_PATH}" "${WHEEL_PACKAGE_DIR}/LICENSE.txt")
endif()
if (WHEEL_README_PATH)
add_custom_command(TARGET ${WHEEL_TARGET}-copy-files
COMMAND ${CMAKE_COMMAND} -E copy "${WHEEL_README_PATH}" "${WHEEL_PACKAGE_DIR}/README.txt")
endif()
if (WHEEL_DEPLOY_FILES)
foreach (file IN LISTS WHEEL_DEPLOY_FILES)
add_custom_command(TARGET ${WHEEL_TARGET}-copy-files
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "Copying file from ${file} to ${WHEEL_PACKAGE_DIR}"
COMMAND ${CMAKE_COMMAND} -E copy "${file}" "${WHEEL_PACKAGE_DIR}/")
endforeach()
endif()
if (WHEEL_SCRIPTS)
foreach (file IN LISTS WHEEL_SCRIPTS)
add_custom_command(TARGET ${WHEEL_TARGET}-copy-files
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "Copying script from ${file} to ${WHEEL_PACKAGE_DIR}"
COMMAND ${CMAKE_COMMAND} -E copy "${file}" "${WHEEL_PACKAGE_DIR}/"
)
endforeach()
endif()
to_python_list_string(WHEEL_SCRIPTS WHEEL_SCRIPTS_PYLIST)
set(WHEEL_PROJECTS "${WHEEL_NAME}")
if (WHEEL_PYTHON_PACKAGE_DIRS)
foreach (path IN LISTS WHEEL_PYTHON_PACKAGE_DIRS)
get_filename_component(path_last_dir ${path} NAME)
add_custom_command(TARGET ${WHEEL_TARGET}-copy-files
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "Copying directory from ${path} to ${WHEEL_LIB_DIR}/${path_last_dir}"
COMMAND ${CMAKE_COMMAND} -E copy_directory "${path}" "${WHEEL_LIB_DIR}/${path_last_dir}"
)
list(APPEND WHEEL_PROJECTS ${path_last_dir})
endforeach()
endif()
to_python_list_string(WHEEL_PROJECTS WHEEL_PYTHON_PACKAGE_DIRS_PYLIST)
set(SETUP_FILE "${CMAKE_CURRENT_BINARY_DIR}/setup.py")
configure_file("${PY_WHEEL_SETUP_FILE}" "${SETUP_FILE}")
if(APPLE)
set(EXTRA_ARGS COMMAND "${Python3_EXECUTABLE}" "${PY_CHANGE_TAG_FILE}" "${WHEEL_DEPLOY_DIRECTORY}" "${WHEEL_NAME}")
else()
set(EXTRA_ARGS "")
endif()
add_custom_target(${WHEEL_TARGET}-setup-py
COMMAND
"${Python3_EXECUTABLE}" "-m" "pip" "wheel"
"${CMAKE_CURRENT_BINARY_DIR}"
"--no-deps"
"-w" "${WHEEL_DEPLOY_DIRECTORY}"
${EXTRA_ARGS})
add_dependencies(${WHEEL_TARGET}-setup-py ${WHEEL_TARGET}-copy-files ${WHEEL_TARGET})
add_dependencies(wheel ${WHEEL_TARGET}-setup-py)
set_target_properties(${WHEEL_TARGET} PROPERTIES
BUILD_RPATH "\$ORIGIN;" # Override hardcoded RPATH
INSTALL_RPATH "\$ORIGIN;"
)
endfunction()
function (add_wheel_test TEST_NAME)
set(Python_FIND_VIRTUALENV FIRST) # Favor venv over system install
find_package(Python3 COMPONENTS Interpreter Development REQUIRED)
# Parse arguments
cmake_parse_arguments(WHEEL_TEST
""
"WORKING_DIRECTORY"
"COMMANDS" ${ARGN})
add_test(
NAME
${TEST_NAME}
WORKING_DIRECTORY
"${WHEEL_TEST_WORKING_DIRECTORY}"
COMMAND
bash "${TEST_WHEEL_BASH}"
-w "${WHEEL_DEPLOY_DIRECTORY}"
${WHEEL_TEST_COMMANDS})
endfunction()