Skip to content

Commit

Permalink
Add cmake python utils (#4)
Browse files Browse the repository at this point in the history
* Add cmake python utils

* Add functions to create targets for
  * pip_install_local
  * pip_install_dist
  * build_wheel
  * install_wheel
* Add functions/macros to use python virtualvenv in cmake

Signed-off-by: Andreas Heinrich <andreas.heinrich@rwth-aachen.de>

* Apply suggestions from code review

Signed-off-by: Andreas Heinrich <andreas.heinrich@rwth-aachen.de>

---------

Signed-off-by: Andreas Heinrich <andreas.heinrich@rwth-aachen.de>
  • Loading branch information
andistorm authored Jun 13, 2024
1 parent fbe883f commit 0f3dd54
Show file tree
Hide file tree
Showing 4 changed files with 275 additions and 2 deletions.
4 changes: 2 additions & 2 deletions everest-cmake-config-version.cmake
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
set(PACKAGE_VERSION 0.2.1)
set(PACKAGE_VERSION 0.3)

if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION)
set(PACKAGE_VERSION_EXACT TRUE)
elseif (PACKAGE_FIND_VERSION_MAJOR STREQUAL "0")
if (PACKAGE_FIND_VERSION_MINOR GREATER "2")
if (PACKAGE_FIND_VERSION_MINOR GREATER "3")
set(PACKAGE_VERSION_UNSUITABLE TRUE)
else ()
set(PACKAGE_VERSION_COMPATIBLE TRUE)
Expand Down
2 changes: 2 additions & 0 deletions everest-cmake-config.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ if (everest_cmake_FOUND)
endif()

include("${CMAKE_CURRENT_LIST_DIR}/edm.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/python-package-helpers.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/python-virtualenv.cmake")

macro (evc_include FILE)
include("${everest-cmake_DIR}/3rd_party/${FILE}.cmake")
Expand Down
128 changes: 128 additions & 0 deletions python-package-helpers.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
function (ev_create_pip_install_dist_target)
cmake_parse_arguments(
"EV_CREATE_PIP_INSTALL_DIST_TARGET"
""
"PACKAGE_NAME"
"DEPENDS"
${ARGN}
)

add_custom_target(${EV_CREATE_PIP_INSTALL_DIST_TARGET_PACKAGE_NAME}_pip_install_dist
# Remove build dir from pip
COMMAND
${CMAKE_COMMAND} -E remove_directory build
COMMAND
${Python3_EXECUTABLE} -m pip install --force-reinstall .
WORKING_DIRECTORY
${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS
${EV_CREATE_PIP_INSTALL_DIST_TARGET_DEPENDS}
COMMENT
"Installing ${EV_CREATE_PIP_INSTALL_DIST_TARGET_PACKAGE_NAME} from distribution"
)
endfunction()

function (ev_create_pip_install_local_target)
cmake_parse_arguments(
"EV_CREATE_PIP_INSTALL_LOCAL_TARGET"
""
"PACKAGE_NAME"
"DEPENDS"
${ARGN}
)

add_custom_target(${EV_CREATE_PIP_INSTALL_LOCAL_TARGET_PACKAGE_NAME}_pip_install_local
# Remove build dir from pip
COMMAND
${CMAKE_COMMAND} -E remove_directory build
COMMAND
${Python3_EXECUTABLE} -m pip install --force-reinstall -e .
WORKING_DIRECTORY
${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS
${EV_CREATE_PIP_INSTALL_LOCAL_TARGET_DEPENDS}
COMMENT
"Installing ${EV_CREATE_PIP_INSTALL_LOCAL_TARGET_PACKAGE_NAME} via user-mode from build"
)
endfunction()

function(ev_create_pip_install_targets)
cmake_parse_arguments(
"EV_CREATE_PIP_INSTALL_TARGETS"
""
"PACKAGE_NAME"
"DIST_DEPENDS;LOCAL_DEPENDS"
${ARGN}
)
ev_create_pip_install_dist_target(
PACKAGE_NAME ${EV_CREATE_PIP_INSTALL_TARGETS_PACKAGE_NAME}
DEPENDS ${EV_CREATE_PIP_INSTALL_TARGETS_DIST_DEPENDS}
)
ev_create_pip_install_local_target(
PACKAGE_NAME ${EV_CREATE_PIP_INSTALL_TARGETS_PACKAGE_NAME}
DEPENDS ${EV_CREATE_PIP_INSTALL_TARGETS_LOCAL_DEPENDS}
)
endfunction()

function (ev_create_python_wheel_targets)
cmake_parse_arguments(
"EV_CREATE_PYTHON_WHEEL_TARGETS"
""
"PACKAGE_NAME"
"DEPENDS;INSTALL_PREFIX"
${ARGN}
)

if (NOT DEFINED ${EV_CREATE_PYTHON_WHEEL_TARGETS_INSTALL_PREFIX})
if ("${${PROJECT_NAME}_WHEEL_INSTALL_PREFIX}" STREQUAL "")
message(FATAL_ERROR
"No install prefix for wheel specified, please set ${PROJECT_NAME}_WHEEL_INSTALL_PREFIX, use the INSTALL_PREFIX argument or use macro ev_setup_cmake_variables_python_wheel() to set a default value."
)
else()
set(EV_CREATE_PYTHON_WHEEL_TARGETS_INSTALL_PREFIX ${${PROJECT_NAME}_WHEEL_INSTALL_PREFIX})
endif()
endif()

set(WHEEL_OUTDIR ${CMAKE_CURRENT_BINARY_DIR}/dist)
add_custom_target(${EV_CREATE_PYTHON_WHEEL_TARGETS_PACKAGE_NAME}_build_wheel
# Remove build dir from pip
COMMAND
${CMAKE_COMMAND} -E remove_directory build
COMMAND
${Python3_EXECUTABLE} -m build --wheel --outdir ${WHEEL_OUTDIR} .
WORKING_DIRECTORY
${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS
${EV_CREATE_PYTHON_WHEEL_TARGETS_DEPENDS}
COMMENT
"Building wheel for ${EV_CREATE_PYTHON_WHEEL_TARGETS_PACKAGE_NAME}"
)

add_custom_target(${EV_CREATE_PYTHON_WHEEL_TARGETS_PACKAGE_NAME}_install_wheel
COMMAND
${CMAKE_COMMAND} -E make_directory ${EV_CREATE_PYTHON_WHEEL_TARGETS_INSTALL_PREFIX}
COMMAND
${CMAKE_COMMAND} -E copy_directory ${WHEEL_OUTDIR} ${EV_CREATE_PYTHON_WHEEL_TARGETS_INSTALL_PREFIX}/
WORKING_DIRECTORY
${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS
${EV_CREATE_PYTHON_WHEEL_TARGETS_PACKAGE_NAME}_build_wheel
COMMENT
"Copy wheel for ${EV_CREATE_PYTHON_WHEEL_TARGETS_PACKAGE_NAME} to ${EV_CREATE_PYTHON_WHEEL_TARGETS_INSTALL_PREFIX}"
)
endfunction()

macro(ev_setup_cmake_variables_python_wheel)
set(${PROJECT_NAME}_WHEEL_INSTALL_PREFIX "" CACHE PATH "Path to install python wheels to")
if (${PROJECT_NAME}_WHEEL_INSTALL_PREFIX STREQUAL "")
if(${WHEEL_INSTALL_PREFIX})
set(${PROJECT_NAME}_DEFAULT_WHEEL_INSTALL_PREFIX "${WHEEL_INSTALL_PREFIX}")
message(STATUS "${PROJET_NAME}_WHEEL_INSTALL_PREFIX not set, using default: WHEEL_INSTALL_PREFIX=${${PROJECT_NAME}_DEFAULT_WHEEL_INSTALL_PREFIX}")
else()
set(${PROJECT_NAME}_DEFAULT_WHEEL_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/../dist-wheels")
message(STATUS "${PROJECT_NAME}_WHEEL_INSTALL_PREFIX not set, using default: \${CMAKE_INSTALL_PREFIX}/../dist-wheels=${${PROJECT_NAME}_DEFAULT_WHEEL_INSTALL_PREFIX}")
endif()
set(${PROJECT_NAME}_WHEEL_INSTALL_PREFIX "${${PROJECT_NAME}_DEFAULT_WHEEL_INSTALL_PREFIX}" CACHE PATH "Path to install python wheels to" FORCE)
endif()
message(STATUS "${PROJECT_NAME}_WHEEL_INSTALL_PREFIX=${${PROJECT_NAME}_WHEEL_INSTALL_PREFIX}")
endmacro()
143 changes: 143 additions & 0 deletions python-virtualenv.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
macro(ev_create_python_venv)
cmake_parse_arguments(
"EV_CREATE_PYTHON_VENV"
"INCLUDE_SYSTEM_SITE_PACKAGES"
"PATH_TO_VENV"
""
${ARGN}
)
ev_is_directory_python_venv(
DIRECTORY "${EV_CREATE_PYTHON_VENV_PATH_TO_VENV}"
RESULT_VAR "EV_CREATE_PYTHON_VENV_IS_DIRECTORY_PYTHON_VENV"
)
if(${EV_CREATE_PYTHON_VENV_IS_DIRECTORY_PYTHON_VENV})
message(FATAL_ERROR "Directory is already a python venv: ${EV_CREATE_PYTHON_VENV_PATH_TO_VENV}")
endif()
find_package(Python3
REQUIRED
COMPONENTS
Interpreter
Development
)
set(EV_CREATE_PYTHON_VENV_SYSTEM_SITE_PACKAGES_FLAG "")
if(${EV_CREATE_PYTHON_VENV_INCLUDE_SYSTEM_SITE_PACKAGES})
set(EV_CREATE_PYTHON_VENV_SYSTEM_SITE_PACKAGES_FLAG "--system-site-packages")
endif()
execute_process(
COMMAND
${Python3_EXECUTABLE} -m venv ${EV_CREATE_PYTHON_VENV_SYSTEM_SITE_PACKAGES_FLAG} ${EV_CREATE_PYTHON_VENV_PATH_TO_VENV}
)
message(STATUS "Created python venv: ${EV_CREATE_PYTHON_VENV_PATH_TO_VENV}")
endmacro()

macro(ev_activate_python_venv)
cmake_parse_arguments(
"EV_ACTIVATE_PYTHON_VENV"
""
"PATH_TO_VENV"
""
${ARGN}
)
ev_is_directory_python_venv(
DIRECTORY "${EV_ACTIVATE_PYTHON_VENV_PATH_TO_VENV}"
RESULT_VAR "EV_CREATE_PYTHON_VENV_IS_DIRECTORY_PYTHON_VENV"
)
if(NOT ${EV_CREATE_PYTHON_VENV_IS_DIRECTORY_PYTHON_VENV})
message(FATAL_ERROR "Directory is not a python venv: ${EV_ACTIVATE_PYTHON_VENV_PATH_TO_VENV}")
endif()
set(ENV{VIRTUAL_ENV} "${EV_ACTIVATE_PYTHON_VENV_PATH_TO_VENV}")
set(Python3_FIND_VIRTUALENV FIRST)
unset(Python3_EXECUTABLE)
find_package(Python3
REQUIRED
COMPONENTS
Interpreter
Development
)
message(STATUS "Activated python venv: Python3_EXECUTABLE: ${Python3_EXECUTABLE}")
endmacro()

macro(ev_deactivate_python_venv)
unset(ENV{VIRTUAL_ENV})
unset(Python3_EXECUTABLE)
set(Python3_FIND_VIRTUALENV STANDARD)
find_package(Python3
REQUIRED
COMPONENTS
Interpreter
Development
)
message(STATUS "Deactivated python venv: Python3_EXECUTABLE: ${Python3_EXECUTABLE}")
endmacro()

function(ev_is_directory_python_venv)
cmake_parse_arguments(
"EV_IS_DIRECTORY_PYTHON_VENV"
""
"DIRECTORY;RESULT_VAR"
""
${ARGN}
)
message(DEBUG "Checking if directory is a python venv: ${EV_IS_DIRECTORY_PYTHON_VENV_DIRECTORY}")
if(EXISTS "${EV_IS_DIRECTORY_PYTHON_VENV_DIRECTORY}/pyvenv.cfg")
message(DEBUG "Directory is a python venv: ${EV_IS_DIRECTORY_PYTHON_VENV_DIRECTORY}")
set(${EV_IS_DIRECTORY_PYTHON_VENV_RESULT_VAR} TRUE PARENT_SCOPE)
return()
else()
message(DEBUG "Directory is not a python venv: ${EV_IS_DIRECTORY_PYTHON_VENV_DIRECTORY}")
set(${EV_IS_DIRECTORY_PYTHON_VENV_RESULT_VAR} FALSE PARENT_SCOPE)
return()
endif()
endfunction()

function(ev_is_python_venv_active)
cmake_parse_arguments(
"EV_IS_PYTHON_VENV_ACTIVE"
""
"RESULT_VAR"
""
${ARGN}
)
if(DEFINED ENV{VIRTUAL_ENV})
message(DEBUG "Python venv is active: $ENV{VIRTUAL_ENV}")
set(${EV_IS_PYTHON_VENV_ACTIVE_RESULT_VAR} TRUE PARENT_SCOPE)
return()
else()
message(DEBUG "Python venv is not active")
set(${EV_IS_PYTHON_VENV_ACTIVE_RESULT_VAR} FALSE PARENT_SCOPE)
return()
endif()
endfunction()

macro(ev_setup_python_executable)
cmake_parse_arguments(
"EV_SETUP_PYTHON_EXECUTABLE"
""
"USE_PYTHON_VENV;PYTHON_VENV_PATH"
""
${ARGN}
)
if(NOT ${EV_SETUP_PYTHON_EXECUTABLE_USE_PYTHON_VENV})
find_package(Python3
REQUIRED
COMPONENTS
Interpreter
Development
)
else()
message(STATUS "Using python venv: ${${PROJECT_NAME}_PYTHON_VENV_PATH}")
ev_is_directory_python_venv(
DIRECTORY ${${PROJECT_NAME}_PYTHON_VENV_PATH}
RESULT_VAR IS_DIRECTORY_PYTHON_VENV
)
if(NOT ${IS_DIRECTORY_PYTHON_VENV})
ev_create_python_venv(
PATH_TO_VENV
"${${PROJECT_NAME}_PYTHON_VENV_PATH}"
INCLUDE_SYSTEM_SITE_PACKAGES
)
endif()
ev_activate_python_venv(PATH_TO_VENV "${${PROJECT_NAME}_PYTHON_VENV_PATH}")
endif()
message(STATUS "Set up python executable with Python3_EXECUTABLE=${Python3_EXECUTABLE}")
endmacro()

0 comments on commit 0f3dd54

Please sign in to comment.