From bb2dd483c292787c83f45e572c57302a253df961 Mon Sep 17 00:00:00 2001 From: "M. Eric Irrgang" Date: Thu, 23 Apr 2020 14:43:55 +0000 Subject: [PATCH] Use CMake 3.12+ facilities for detecting Python. Consolidate Python detection in the main CMakeLists.txt before the first usage in a subdirectory. Use FindPython3.cmake instead of FindPythonInterp.cmake. Populate the legacy PYTHON_EXECUTABLE variable for compatibility. Fixes #2998 --- CMakeLists.txt | 22 +----- cmake/FindPythonModule.cmake | 1 - cmake/gmxPythonDiscovery.cmake | 70 +++++++++++++++++++ cmake/gmxVersionInfo.cmake | 5 +- docs/CMakeLists.txt | 2 +- docs/doxygen/CMakeLists.txt | 2 +- docs/gmxapi/userguide/install.rst | 11 ++- docs/release-notes/2021/major/portability.rst | 12 +++- python_packaging/CMakeLists.txt | 7 +- tests/CMakeLists.txt | 4 +- 10 files changed, 98 insertions(+), 38 deletions(-) create mode 100644 cmake/gmxPythonDiscovery.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 64fb2f5079..1443c44094 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,6 +68,9 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) find_package(LibStdCpp) +# Python is first referenced in gmxVersionInfo, so we perform the search early +# to find a suitable installation for all components. +include(gmxPythonDiscovery) # Set up common version variables, as well as general information about # the build tree (whether the build is from a source package or from a git # repository). Also declares a few functions that will be used for generating @@ -840,25 +843,6 @@ option(GMX_PYTHON_PACKAGE "Configure gmxapi Python package" OFF) mark_as_advanced(GMX_PYTHON_PACKAGE) if (NOT GMX_BUILD_MDRUN_ONLY) - # Note: Though only documented as an output variable, PYTHON_EXECUTABLE is - # also effective as a CMake input variable to effectively hint the location - # of the Python interpreter. This may be helpful in environments with both - # Python 2 and Python 3 on the default PATH. - # Ref: https://cmake.org/cmake/help/latest/module/FindPythonInterp.html - if(FIND_PACKAGE_MESSAGE_DETAILS_PythonInterp) - # Keep quiet on subsequent runs of cmake - set(PythonInterp_FIND_QUIETLY ON) - endif() - # Older CMake versions might not search for Python newer than 3.7. - set(Python_ADDITIONAL_VERSIONS 3.8) - if(GMX_PYTHON_PACKAGE) - find_package(PythonInterp 3.6 REQUIRED) - # Note: PythonLibs will be found later by pybind11. - # TODO: (Issue #2998) When CMake >= 3.12 is required, update detection. - # I.e. find_package(Python3 3.6 COMPONENTS Interpreter Development REQUIRED) - else() - find_package(PythonInterp 3.6) - endif() find_package(ImageMagick QUIET COMPONENTS convert) include(gmxTestImageMagick) GMX_TEST_IMAGEMAGICK(IMAGE_CONVERT_POSSIBLE) diff --git a/cmake/FindPythonModule.cmake b/cmake/FindPythonModule.cmake index 8fbac87163..a5328dd8b8 100644 --- a/cmake/FindPythonModule.cmake +++ b/cmake/FindPythonModule.cmake @@ -34,7 +34,6 @@ # Adapted from code posted on cmake-users by Mark Moll (the execute_process() # call remains, but other things have been rewritten for nicer behavior). -find_package(PythonInterp 3.6) function (find_python_module module) string(TOUPPER ${module} _module_upper) diff --git a/cmake/gmxPythonDiscovery.cmake b/cmake/gmxPythonDiscovery.cmake new file mode 100644 index 0000000000..21a1743f66 --- /dev/null +++ b/cmake/gmxPythonDiscovery.cmake @@ -0,0 +1,70 @@ +# +# This file is part of the GROMACS molecular simulation package. +# +# Copyright (c) 2020, by the GROMACS development team, led by +# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl, +# and including many others, as listed in the AUTHORS file in the +# top-level source directory and at http://www.gromacs.org. +# +# GROMACS is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# GROMACS is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with GROMACS; if not, see +# http://www.gnu.org/licenses, or write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# If you want to redistribute modifications to GROMACS, please +# consider that scientific software is very special. Version +# control is crucial - bugs must be traceable. We will be happy to +# consider code for inclusion in the official distribution, but +# derived work must not be called official GROMACS. Details are found +# in the README & COPYING files - if they are missing, get the +# official version at http://www.gromacs.org. +# +# To help us fund GROMACS development, we humbly ask that you cite +# the research papers on the package. Check out http://www.gromacs.org. + +# Perform Python installation discovery early and in one place, for consistency. +# +# Note: If necessary, the Python location can be hinted with Python3_ROOT_DIR +# For additional parameters affecting Python installation discovery, see +# https://cmake.org/cmake/help/latest/module/FindPython3.html#hints +if(FIND_PACKAGE_MESSAGE_DETAILS_Python3) + # Keep quiet on subsequent runs of cmake + set(Python3_FIND_QUIETLY ON) + set(PythonInterp_FIND_QUIETLY ON) +endif() +# Older CMake versions might not search for Python newer than 3.7. +set(Python_ADDITIONAL_VERSIONS 3.8) +# We advocate using Python venvs to manage package availability, so by default +# we want to preferentially discover user-space software. +set(Python3_FIND_REGISTRY LAST) +# Make package discovery consistent with Unix behavior and our documented +# suggestions for installing dependencies. +set(CMAKE_FIND_FRAMEWORK LAST) +if(GMX_PYTHON_PACKAGE) + find_package(Python3 3.6 COMPONENTS Interpreter Development) + if (NOT Python3_FOUND OR NOT Python3_Development_FOUND) + message(FATAL_ERROR "Could not locate Python development requirements. \ + Provide appropriate CMake hints or set GMX_PYTHON_PACKAGE=OFF") + endif () +else() + find_package(Python3 3.6 COMPONENTS Interpreter) +endif() +# Other components, such as pybind and googletest, may expect the +# PYTHON_EXECUTABLE variable from pre-3.12 FindPythonInterp.cmake. +if (Python3_Interpreter_FOUND) + set(PYTHON_EXECUTABLE ${Python3_EXECUTABLE} CACHE FILEPATH "Location hint for Python interpreter.") +endif () +# We've already generated all of the output we need, even though other subcomponents +# may call find_package(PythonInterp) later on. +set(Python3_FIND_QUIETLY ON) +set(PythonInterp_FIND_QUIETLY ON) diff --git a/cmake/gmxVersionInfo.cmake b/cmake/gmxVersionInfo.cmake index 7a2b56344e..136b8893c8 100644 --- a/cmake/gmxVersionInfo.cmake +++ b/cmake/gmxVersionInfo.cmake @@ -337,9 +337,6 @@ set(SET_OF_DIRECTORIES_TO_CHECKSUM "${PROJECT_SOURCE_DIR}/src") list(APPEND SET_OF_DIRECTORIES_TO_CHECKSUM "${PROJECT_SOURCE_DIR}/python_packaging") # Due to the limitations for passing a list as arguments, we make the directories a string here string(REPLACE ";" ":" DIRECTORIES_TO_CHECKSUM_STRING "${SET_OF_DIRECTORIES_TO_CHECKSUM}") -# Try to find python for the checksumming script -set(PythonInterp_FIND_QUIETLY ON) -find_package(PythonInterp 3.6) # Rules to create the VersionInfo.cmake file. # For git info, the sequence is: @@ -437,7 +434,7 @@ set(CHECKSUM_FILE "${PROJECT_SOURCE_DIR}/src/reference_checksum") # not been tampered with. # Note: The RUN_ALWAYS here is to regenerate the hash file only, it does not # mean that the target is run in all builds -if (PYTHONINTERP_FOUND) +if (PYTHON_EXECUTABLE) gmx_add_custom_output_target(reference_checksum RUN_ALWAYS OUTPUT ${CHECKSUM_FILE} COMMAND ${PYTHON_EXECUTABLE} diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index ee70c62f97..3d8ce9b2e0 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -704,7 +704,7 @@ set(HTML_BUILD_NOT_POSSIBLE_REASON) set(HTML_BUILD_WARNINGS) # Next, turn it off if any of the preconditions are unsatisified -if (NOT PythonInterp_FOUND) +if (NOT Python3_Interpreter_FOUND) set(HTML_BUILD_IS_POSSIBLE OFF) set(HTML_BUILD_NOT_POSSIBLE_REASON "Python is required") elseif (NOT SPHINX_FOUND) diff --git a/docs/doxygen/CMakeLists.txt b/docs/doxygen/CMakeLists.txt index 91ba04eaf9..0625ecb04d 100644 --- a/docs/doxygen/CMakeLists.txt +++ b/docs/doxygen/CMakeLists.txt @@ -58,7 +58,7 @@ gmx_dependent_option( mark_as_advanced(GMX_COMPACT_DOXYGEN) set(USE_PYTHON_SCRIPTS OFF) -if (PYTHONINTERP_FOUND) +if (PYTHON_EXECUTABLE) set(USE_PYTHON_SCRIPTS ON) endif() diff --git a/docs/gmxapi/userguide/install.rst b/docs/gmxapi/userguide/install.rst index adfe44938c..a696c11e5c 100644 --- a/docs/gmxapi/userguide/install.rst +++ b/docs/gmxapi/userguide/install.rst @@ -483,9 +483,14 @@ extract Python docstrings. Sometimes the build environment can choose a different Python interpreter than the one you intended. -You can set the ``PYTHON_EXECUTABLE`` CMake variable to explicitly choose the -Python interpreter for your chosen installation. -For example: ``-DPYTHON_EXECUTABLE=\`which python\``` +You can set the ``Python3_ROOT`` or ``CMAKE_PREFIX_PATH`` CMake variable to +explicitly choose the Python installation or *venv* directory. + +If you use pyenv or pyenv-virtualenv to dynamically manage your Python version, +you can help identify a particular version with ``pyenv version-name`` and the +directory with ``pyenv prefix {version}``. For example:: + + -DPython3_ROOT=$(pyenv prefix $(pyenv version-name)) Docker web server ----------------- diff --git a/docs/release-notes/2021/major/portability.rst b/docs/release-notes/2021/major/portability.rst index fdcf01640e..36940a8833 100644 --- a/docs/release-notes/2021/major/portability.rst +++ b/docs/release-notes/2021/major/portability.rst @@ -1,12 +1,20 @@ Portability ^^^^^^^^^^^ -Supported Python versions -""""""""""""""""""""""""" +Python environment +"""""""""""""""""" Where Python is required, `CPython `__ versions 3.6 to 3.8 are supported. +CMake now detects Python using +`FindPython3 `__. +If you previously used ``PYTHON_EXECUTABLE`` to hint the location of the Python +interpreter, you should instead specify the Python "root" or "prefix" path +(the directory containing ``./bin/python3``) with CMake variable +``Python3_ROOT`` or ``CMAKE_PREFIX_PATH``. As other infrastructure evolves, +``PYTHON_EXECUTABLE`` may cease to have the desired effect without warning. + .. Note to developers! Please use """"""" to underline the individual entries for fixed issues in the subfolders, otherwise the formatting on the webpage is messed up. diff --git a/python_packaging/CMakeLists.txt b/python_packaging/CMakeLists.txt index 8bf6573374..6d0e7212e9 100644 --- a/python_packaging/CMakeLists.txt +++ b/python_packaging/CMakeLists.txt @@ -1,7 +1,7 @@ # # This file is part of the GROMACS molecular simulation package. # -# Copyright (c) 2019, by the GROMACS development team, led by +# Copyright (c) 2019,2020, by the GROMACS development team, led by # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl, # and including many others, as listed in the AUTHORS file in the # top-level source directory and at http://www.gromacs.org. @@ -90,10 +90,7 @@ # (system or user) GROMACS installation and (user) Python environment. For a # system-wide Python environment, the package needs to be built and installed # (to the ``site-packages`` directory) for each supported Python interpreter. -# ``setup.py`` can just be invoked with different Python interpreters. We can -# add CMake infrastructure to allow multiple/repeated PYTHON_EXECUTABLE -# specification at the GROMACS project level if such a use case is important to -# HPC site administrators or Linux distribution packagers. +# ``setup.py`` can just be invoked with different Python interpreters. # # To drive the packaging of a Python package distribution archive # by a higher-level CMake configuration, a CMakeLists.txt file at this level would diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 051d6d9686..e8b65a3ed8 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -290,10 +290,10 @@ if(GMX_PHYSICAL_VALIDATION) # End copied from regression tests. # - if (NOT PYTHONINTERP_FOUND) + if (NOT Python3_Interpreter_FOUND) message(FATAL_ERROR "Python not found. Physical validation requires python. \ - Install python, set PYTHON_EXECUTABLE to a valid python location, \ + Install python, set Python3_ROOT_DIR or PYTHON_EXECUTABLE to a valid location, \ or set GMX_PHYSICAL_VALIDATION=OFF to disable the physical validation tests.") endif() # -- 2.22.0