From 8a2f62cc18ece0ebfed0ff6abf53d419d43d2fa1 Mon Sep 17 00:00:00 2001 From: Marc Chevrier Date: Mon, 1 Apr 2019 11:19:54 +0200 Subject: FindPython*: Add capability to control virtual env handling. Fixes: #19097 --- Help/release/dev/FindPython-virtual-env.rst | 5 ++ Modules/FindPython.cmake | 22 ++++++-- Modules/FindPython/Support.cmake | 65 ++++++++++++++++++++-- Modules/FindPython2.cmake | 20 +++++-- Modules/FindPython3.cmake | 24 ++++++-- Tests/FindPython/CMakeLists.txt | 11 ++++ Tests/FindPython/VirtualEnv/CMakeLists.txt | 42 ++++++++++++++ .../FindPython/VirtualEnv/VirtualEnvDefault.cmake | 6 ++ Tests/FindPython/VirtualEnv/VirtualEnvOnly.cmake | 16 ++++++ .../FindPython/VirtualEnv/VirtualEnvStandard.cmake | 7 +++ 10 files changed, 198 insertions(+), 20 deletions(-) create mode 100644 Help/release/dev/FindPython-virtual-env.rst create mode 100644 Tests/FindPython/VirtualEnv/CMakeLists.txt create mode 100644 Tests/FindPython/VirtualEnv/VirtualEnvDefault.cmake create mode 100644 Tests/FindPython/VirtualEnv/VirtualEnvOnly.cmake create mode 100644 Tests/FindPython/VirtualEnv/VirtualEnvStandard.cmake diff --git a/Help/release/dev/FindPython-virtual-env.rst b/Help/release/dev/FindPython-virtual-env.rst new file mode 100644 index 0000000..5489dc2 --- /dev/null +++ b/Help/release/dev/FindPython-virtual-env.rst @@ -0,0 +1,5 @@ +FindPython-virtual-env +---------------------- + +* Modules :module:`FindPython3`, :module:`FindPython2` and :module:`FindPython` + gain capability to control how virtual environments are handled. diff --git a/Modules/FindPython.cmake b/Modules/FindPython.cmake index 1c134e2..c5074e8 100644 --- a/Modules/FindPython.cmake +++ b/Modules/FindPython.cmake @@ -145,18 +145,30 @@ Hints * ``NEVER``: Never try to use registry. ``CMAKE_FIND_FRAMEWORK`` - On OS X the :variable:`CMAKE_FIND_FRAMEWORK` variable determine the order of + On macOS the :variable:`CMAKE_FIND_FRAMEWORK` variable determine the order of preference between Apple-style and unix-style package components. .. note:: Value ``ONLY`` is not supported so ``FIRST`` will be used instead. -.. note:: +``Python_FIND_VIRTUALENV`` + This variable defines the handling of virtual environments. It is meaningfull + only when a virtual environment is active (i.e. the ``activate`` script has + been evaluated). In this case, it takes precedence over + ``Python_FIND_REGISTRY`` and ``CMAKE_FIND_FRAMEWORK`` variables. + The ``Python_FIND_VIRTUALENV`` variable can be set to empty or one of the + following: - If a Python virtual environment is configured, set variable - ``Python_FIND_REGISTRY`` (Windows) or ``CMAKE_FIND_FRAMEWORK`` (macOS) with - value ``LAST`` or ``NEVER`` to select it preferably. + * ``FIRST``: The virtual environment is used before any other standard + paths to look-up for the interpreter. This is the default. + * ``ONLY``: Only the virtual environment is used to look-up for the + interpreter. + * ``STANDARD``: The virtual environment is not used to look-up for the + interpreter. In this case, variable ``Python_FIND_REGISTRY`` (Windows) + or ``CMAKE_FIND_FRAMEWORK`` (macOS) can be set with value ``LAST`` or + ``NEVER`` to select preferably the interpreter from the virtual + environment. Commands ^^^^^^^^ diff --git a/Modules/FindPython/Support.cmake b/Modules/FindPython/Support.cmake index 1236bf8..0868989 100644 --- a/Modules/FindPython/Support.cmake +++ b/Modules/FindPython/Support.cmake @@ -298,6 +298,22 @@ else() set (_${_PYTHON_PREFIX}_FIND_REGISTRY "FIRST") endif() +# virtual environments handling +if (DEFINED ENV{VIRTUAL_ENV}) + if (DEFINED ${_PYTHON_PREFIX}_FIND_VIRTUALENV) + if (NOT ${_PYTHON_PREFIX}_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY|STANDARD)$") + message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: ${${_PYTHON_PREFIX}_FIND_VIRTUALENV}: invalid value for '${_PYTHON_PREFIX}_FIND_VIRTUALENV'. 'FIRST', 'ONLY' or 'IGNORE' expected.") + set (_${_PYTHON_PREFIX}_FIND_VIRTUALENV "FIRST") + else() + set (_${_PYTHON_PREFIX}_FIND_VIRTUALENV ${${_PYTHON_PREFIX}_FIND_VIRTUALENV}) + endif() + else() + set (_${_PYTHON_PREFIX}_FIND_VIRTUALENV FIRST) + endif() +else() + set (_${_PYTHON_PREFIX}_FIND_VIRTUALENV STANDARD) +endif() + unset (_${_PYTHON_PREFIX}_REQUIRED_VARS) unset (_${_PYTHON_PREFIX}_CACHED_VARS) @@ -318,6 +334,30 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_VERSION}) + # Virtual environments handling + if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY)$") + find_program (${_PYTHON_PREFIX}_EXECUTABLE + NAMES python${_${_PYTHON_PREFIX}_VERSION} + python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} + python + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ENV VIRTUAL_ENV + PATH_SUFFIXES bin Scripts + NO_CMAKE_PATH + NO_CMAKE_ENVIRONMENT_PATH + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + + _python_validate_interpreter (${_${_PYTHON_PREFIX}_VERSION}) + if (${_PYTHON_PREFIX}_EXECUTABLE) + break() + endif() + if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV STREQUAL "ONLY") + continue() + endif() + endif() + # Apple frameworks handling if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") find_program (${_PYTHON_PREFIX}_EXECUTABLE @@ -423,7 +463,8 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) endif() endforeach() - if (NOT ${_PYTHON_PREFIX}_EXECUTABLE) + if (NOT ${_PYTHON_PREFIX}_EXECUTABLE AND + NOT _${_PYTHON_PREFIX}_FIND_VIRTUALENV STREQUAL "ONLY") # No specific version found. Retry with generic names # try using HINTS find_program (${_PYTHON_PREFIX}_EXECUTABLE @@ -685,18 +726,32 @@ if ("Development" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS # if python interpreter is found, use its location and version to ensure consistency # between interpreter and development environment unset (_${_PYTHON_PREFIX}_PREFIX) + unset (_${_PYTHON_PREFIX}_EXEC_PREFIX) + unset (_${_PYTHON_PREFIX}_BASE_EXEC_PREFIX) if (${_PYTHON_PREFIX}_Interpreter_FOUND) execute_process (COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c - "import sys; from distutils import sysconfig; sys.stdout.write(sysconfig.PREFIX)" + "import sys; from distutils import sysconfig; sys.stdout.write(sysconfig.EXEC_PREFIX)" RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT - OUTPUT_VARIABLE _${_PYTHON_PREFIX}_PREFIX + OUTPUT_VARIABLE _${_PYTHON_PREFIX}_EXEC_PREFIX ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (_${_PYTHON_PREFIX}_RESULT) - unset (_${_PYTHON_PREFIX}_PREFIX) + unset (_${_PYTHON_PREFIX}_EXEC_PREFIX) + endif() + + if (NOT ${_PYTHON_PREFIX}_FIND_VIRTUALENV STREQUAL "STANDARD") + execute_process (COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c + "import sys; from distutils import sysconfig; sys.stdout.write(sysconfig.BASE_EXEC_PREFIX)" + RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT + OUTPUT_VARIABLE _${_PYTHON_PREFIX}_BASE_EXEC_PREFIX + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (_${_PYTHON_PREFIX}_RESULT) + unset (_${_PYTHON_PREFIX}_BASE_EXEC_PREFIX) + endif() endif() endif() - set (_${_PYTHON_PREFIX}_HINTS "${_${_PYTHON_PREFIX}_PREFIX}" "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR) + set (_${_PYTHON_PREFIX}_HINTS "${_${_PYTHON_PREFIX}_EXEC_PREFIX}" "${_${_PYTHON_PREFIX}_BASE_EXEC_PREFIX}" "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR) foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS) string (REPLACE "." "" _${_PYTHON_PREFIX}_VERSION_NO_DOTS ${_${_PYTHON_PREFIX}_VERSION}) diff --git a/Modules/FindPython2.cmake b/Modules/FindPython2.cmake index b9c0b6b..a2be84f 100644 --- a/Modules/FindPython2.cmake +++ b/Modules/FindPython2.cmake @@ -153,11 +153,23 @@ Hints Value ``ONLY`` is not supported so ``FIRST`` will be used instead. -.. note:: +``Python2_FIND_VIRTUALENV`` + This variable defines the handling of virtual environments. It is meaningfull + only when a virtual environment is active (i.e. the ``activate`` script has + been evaluated). In this case, it takes precedence over + ``Python2_FIND_REGISTRY`` and ``CMAKE_FIND_FRAMEWORK`` variables. + The ``Python2_FIND_VIRTUALENV`` variable can be set to empty or one of the + following: - If a Python virtual environment is configured, set variable - ``Python_FIND_REGISTRY`` (Windows) or ``CMAKE_FIND_FRAMEWORK`` (macOS) with - value ``LAST`` or ``NEVER`` to select it preferably. + * ``FIRST``: The virtual environment is used before any other standard + paths to look-up for the interpreter. This is the default. + * ``ONLY``: Only the virtual environment is used to look-up for the + interpreter. + * ``STANDARD``: The virtual environment is not used to look-up for the + interpreter. In this case, variable ``Python2_FIND_REGISTRY`` (Windows) + or ``CMAKE_FIND_FRAMEWORK`` (macOS) can be set with value ``LAST`` or + ``NEVER`` to select preferably the interpreter from the virtual + environment. Commands ^^^^^^^^ diff --git a/Modules/FindPython3.cmake b/Modules/FindPython3.cmake index c2f3384..3409554 100644 --- a/Modules/FindPython3.cmake +++ b/Modules/FindPython3.cmake @@ -137,7 +137,7 @@ Hints ``Python3_FIND_REGISTRY`` On Windows the ``Python3_FIND_REGISTRY`` variable determine the order of preference between registry and environment variables. - the ``Python3_FIND_REGISTRY`` variable can be set to empty or one of the + The ``Python3_FIND_REGISTRY`` variable can be set to empty or one of the following: * ``FIRST``: Try to use registry before environment variables. @@ -146,18 +146,30 @@ Hints * ``NEVER``: Never try to use registry. ``CMAKE_FIND_FRAMEWORK`` - On OS X the :variable:`CMAKE_FIND_FRAMEWORK` variable determine the order of + On macOS the :variable:`CMAKE_FIND_FRAMEWORK` variable determine the order of preference between Apple-style and unix-style package components. .. note:: Value ``ONLY`` is not supported so ``FIRST`` will be used instead. -.. note:: +``Python3_FIND_VIRTUALENV`` + This variable defines the handling of virtual environments. It is meaningfull + only when a virtual environment is active (i.e. the ``activate`` script has + been evaluated). In this case, it takes precedence over + ``Python3_FIND_REGISTRY`` and ``CMAKE_FIND_FRAMEWORK`` variables. + The ``Python3_FIND_VIRTUALENV`` variable can be set to empty or one of the + following: - If a Python virtual environment is configured, set variable - ``Python_FIND_REGISTRY`` (Windows) or ``CMAKE_FIND_FRAMEWORK`` (macOS) with - value ``LAST`` or ``NEVER`` to select it preferably. + * ``FIRST``: The virtual environment is used before any other standard + paths to look-up for the interpreter. This is the default. + * ``ONLY``: Only the virtual environment is used to look-up for the + interpreter. + * ``STANDARD``: The virtual environment is not used to look-up for the + interpreter. In this case, variable ``Python3_FIND_REGISTRY`` (Windows) + or ``CMAKE_FIND_FRAMEWORK`` (macOS) can be set with value ``LAST`` or + ``NEVER`` to select preferably the interpreter from the virtual + environment. Commands ^^^^^^^^ diff --git a/Tests/FindPython/CMakeLists.txt b/Tests/FindPython/CMakeLists.txt index 38211a4..d6f50e7 100644 --- a/Tests/FindPython/CMakeLists.txt +++ b/Tests/FindPython/CMakeLists.txt @@ -68,6 +68,17 @@ if(CMake_TEST_FindPython) --build-options ${build_options} --test-command ${CMAKE_CTEST_COMMAND} -V -C $ ) + + add_test(NAME FindPython.VirtualEnv COMMAND + ${CMAKE_CTEST_COMMAND} -C $ + --build-and-test + "${CMake_SOURCE_DIR}/Tests/FindPython/VirtualEnv" + "${CMake_BINARY_DIR}/Tests/FindPython/VirtualEnv" + ${build_generator_args} + --build-project TestVirtualEnv + --build-options ${build_options} + --test-command ${CMAKE_CTEST_COMMAND} -V -C $ + ) endif() if(CMake_TEST_FindPython_NumPy) diff --git a/Tests/FindPython/VirtualEnv/CMakeLists.txt b/Tests/FindPython/VirtualEnv/CMakeLists.txt new file mode 100644 index 0000000..64ba201 --- /dev/null +++ b/Tests/FindPython/VirtualEnv/CMakeLists.txt @@ -0,0 +1,42 @@ +cmake_minimum_required(VERSION 3.1) + +project(TestVirtualEnv LANGUAGES NONE) + +include(CTest) + +find_package(Python3 REQUIRED COMPONENTS Interpreter) +if (NOT Python3_FOUND) + message (FATAL_ERROR "Fail to found Python 3") +endif() + +set (Python3_VIRTUAL_ENV "${CMAKE_CURRENT_BINARY_DIR}/py3venv") + +execute_process (COMMAND "${Python3_EXECUTABLE}" -m venv "${Python3_VIRTUAL_ENV}" + RESULT_VARIABLE result + OUTPUT_VARIABLE outputs + ERROR_VARIABLE outputs) +if (result) + message (FATAL_ERROR "Fail to create virtual environment: ${outputs}") +endif() + +add_test(NAME FindPython3.VirtualEnvDefault + COMMAND "${CMAKE_COMMAND}" -E env --unset=PYTHONHOME + "VIRTUAL_ENV=${Python3_VIRTUAL_ENV}" + "${CMAKE_COMMAND}" "-DPYTHON3_VIRTUAL_ENV=${Python3_VIRTUAL_ENV}" + -P "${CMAKE_CURRENT_LIST_DIR}/VirtualEnvDefault.cmake") + +add_test(NAME FindPython3.VirtualEnvOnly + COMMAND "${CMAKE_COMMAND}" -E env --unset=PYTHONHOME + "VIRTUAL_ENV=${Python3_VIRTUAL_ENV}" + "${CMAKE_COMMAND}" "-DPYTHON3_VIRTUAL_ENV=${Python3_VIRTUAL_ENV}" + -P "${CMAKE_CURRENT_LIST_DIR}/VirtualEnvOnly.cmake") +add_test(NAME FindPython3.UnsetVirtualEnvOnly + COMMAND "${CMAKE_COMMAND}" -E env --unset=PYTHONHOME + --unset=VIRTUAL_ENV + "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_LIST_DIR}/VirtualEnvOnly.cmake") + +add_test(NAME FindPython3.VirtualEnvStandard + COMMAND "${CMAKE_COMMAND}" -E env --unset=PYTHONHOME + "VIRTUAL_ENV=${Python3_VIRTUAL_ENV}" + "${CMAKE_COMMAND}" "-DPYTHON3_VIRTUAL_ENV=${Python3_VIRTUAL_ENV}" + -P "${CMAKE_CURRENT_LIST_DIR}/VirtualEnvStandard.cmake") diff --git a/Tests/FindPython/VirtualEnv/VirtualEnvDefault.cmake b/Tests/FindPython/VirtualEnv/VirtualEnvDefault.cmake new file mode 100644 index 0000000..020ecac --- /dev/null +++ b/Tests/FindPython/VirtualEnv/VirtualEnvDefault.cmake @@ -0,0 +1,6 @@ + +find_package (Python3 REQUIRED) + +if (NOT Python3_EXECUTABLE MATCHES "^${PYTHON3_VIRTUAL_ENV}/.+") + message (FATAL_ERROR "Fail to use virtual environment") +endif() diff --git a/Tests/FindPython/VirtualEnv/VirtualEnvOnly.cmake b/Tests/FindPython/VirtualEnv/VirtualEnvOnly.cmake new file mode 100644 index 0000000..29a4924 --- /dev/null +++ b/Tests/FindPython/VirtualEnv/VirtualEnvOnly.cmake @@ -0,0 +1,16 @@ + +# +# Virtual environment is defined for python3 +# Trying to find a python2 using only virtual environment +# It is expecting to fail if a virtual environment is active and to success otherwise. +# +set (Python2_FIND_VIRTUALENV ONLY) +find_package (Python2 QUIET) + +if (PYTHON3_VIRTUAL_ENV AND Python2_FOUND) + message (FATAL_ERROR "Python2 unexpectedly found.") +endif() + +if (NOT PYTHON3_VIRTUAL_ENV AND NOT Python2_FOUND) + message (FATAL_ERROR "Fail to find Python2.") +endif() diff --git a/Tests/FindPython/VirtualEnv/VirtualEnvStandard.cmake b/Tests/FindPython/VirtualEnv/VirtualEnvStandard.cmake new file mode 100644 index 0000000..89f27d8 --- /dev/null +++ b/Tests/FindPython/VirtualEnv/VirtualEnvStandard.cmake @@ -0,0 +1,7 @@ + +set (Python3_FIND_VIRTUALENV STANDARD) +find_package (Python3 REQUIRED) + +if (Python3_EXECUTABLE MATCHES "^${PYTHON3_VIRTUAL_ENV}/.+") + message (FATAL_ERROR "Python3 virtual env unexpectedly found.") +endif() -- cgit v0.12