Always call `find_package(MPI)`.
authorM. Eric Irrgang <ericirrgang@gmail.com>
Sun, 12 Sep 2021 12:20:31 +0000 (15:20 +0300)
committerM. Eric Irrgang <ericirrgang@gmail.com>
Sun, 12 Sep 2021 12:23:38 +0000 (15:23 +0300)
It is getting increasingly hard to coordinate MPI requirements across
the toolkit that a GROMACS or its CMake infrastructure may be involved
in.

This change allows is to call `find_package(MPI)` exactly once in the
GROMACS CMake configuration and begins to normalize infrastructure on
the modern CMake FindMPI.cmake module, which has been substantially
updated since the last major revisions to gmxManageMPI.cmake.

Also, try to decouple and compartmentalize GMX_MPI, GMX_LIB_MPI, and
the presence of MPI in the build system.

Follow-up changes can continue to refine the dependent use cases and
allow more coordination of the different MPI requirements in different
build configurations.

Refs #3672
Also refs #3776

cmake/gmxManageMPI.cmake

index f315bed582dd2adcc7615f75804d4fc40697b37c..1ae95db1a1cc6ff15f9ec291d2b70ca11c500a17 100644 (file)
 # To help us fund GROMACS development, we humbly ask that you cite
 # the research papers on the package. Check out http://www.gromacs.org.
 
-# Manage the MPI setup, assuming that CMAKE_C_COMPILER is an MPI
-# (wrapper) compiler.
 if (GMX_MPI)
     if (GMX_THREAD_MPI)
         message(STATUS "MPI is not compatible with thread-MPI. Disabling thread-MPI.")
         set(GMX_THREAD_MPI OFF CACHE BOOL
             "Build a thread-MPI-based multithreaded version of GROMACS (not compatible with MPI)" FORCE)
     endif ()
+    set(GMX_LIB_MPI 1)
+else ()
+    set(GMX_LIB_MPI 0)
+endif ()
 
-    # Test the CMAKE_C_COMPILER for being an MPI (wrapper) compiler
-    TRY_COMPILE(MPI_FOUND ${CMAKE_BINARY_DIR}
-                "${CMAKE_SOURCE_DIR}/cmake/TestMPI.cpp"
-                COMPILE_DEFINITIONS)
+# Manage the MPI setup.
+# Note that we may want to execute tests or Python with MPI,
+# even if we are not using an MPI-enabled GROMACS build.
+#TODO(#3672): find_package(MPI COMPONENTS ...)
+find_package(MPI)
+if (GMX_LIB_MPI)
+    if (NOT MPI_CXX_FOUND)
+        message(FATAL_ERROR
+                "MPI support requested, but no suitable MPI compiler found. Either set the "
+                "MPI_CXX_COMPILER to the MPI compiler wrapper (often called mpicxx or mpic++), "
+                "set CMAKE_CXX_COMPILER to a default-MPI-enabled compiler, "
+                "or set the variables reported missing for MPI_CXX above.")
+    elseif (MPI_CXX_VERSION VERSION_LESS 2.0)
+        message(FATAL_ERROR "MPI version 2.0 or higher is required. Please update your MPI library.")
+    endif ()
+    # TODO(#3672): Replace usage of `MPI_LINKER_FLAGS` with MPI_LINK_FLAGS,
+    #   MPI_<lang>_LINK_FLAGS, or target_link_libraries(... MPI::MPI_CXX)
+    set(MPI_LINKER_FLAGS ${MPI_CXX_LINK_FLAGS})
+    separate_arguments(MPI_CXX_LINK_FLAGS)
+    #TODO(#3672, #3776): These should be acquired through the MPI::MPI_CXX target.
+    include_directories(SYSTEM ${MPI_CXX_INCLUDE_PATH})
+    list(APPEND GMX_COMMON_LIBRARIES ${MPI_CXX_LIBRARIES})
+endif ()
 
-    # If CMAKE_C_COMPILER is not a MPI wrapper. Try to find MPI using cmake module as fall-back.
-    if (NOT MPI_FOUND)
-        find_package(MPI)
-        if (MPI_CXX_VERSION VERSION_LESS 2.0)
-            message(FATAL_ERROR "MPI version 2.0 or higher is required. Please update your MPI library.")
-        endif ()
-        if (MPI_CXX_FOUND)
-            set(MPI_COMPILE_FLAGS ${MPI_CXX_COMPILE_FLAGS})
-            separate_arguments(MPI_COMPILE_FLAGS)
-            set(MPI_LINKER_FLAGS ${MPI_CXX_LINK_FLAGS})
-            separate_arguments(MPI_CXX_LINK_FLAGS)
-            include_directories(SYSTEM ${MPI_CXX_INCLUDE_PATH})
-            list(APPEND GMX_COMMON_LIBRARIES ${MPI_CXX_LIBRARIES})
-        endif ()
-        set(MPI_FOUND ${MPI_CXX_FOUND})
+if (GMX_LIB_MPI)
+    # Test for and warn about unsuitable MPI versions
+    # Find path of the mpi compilers
+    if (${MPI_CXX_FOUND})
+        get_filename_component(_mpi_c_compiler_path "${MPI_CXX_COMPILER}" PATH)
+        get_filename_component(_mpiexec_path "${MPIEXEC_EXECUTABLE}" PATH)
     else ()
-        # The following defaults are based on FindMPI.cmake in cmake
-        # 3.1.2. (That package does not actually do any detection of the
-        # flags, but if it ever does then we should re-visit how we use
-        # the package.) If we are compiling with an MPI wrapper
-        # compiler, then MPI_FOUND will be set above, and will mean that
-        # none of these cache variables are populated by the package. We
-        # need to do it manually so that test drivers can work using the
-        # standard machinery for CMake + FindMPI.cmake.  Users will need
-        # to set these to suit their MPI setup in order for tests to
-        # work.
-
-        find_program(MPIEXEC
-                     NAMES mpiexec mpirun lamexec srun aprun poe
-                     HINTS ${MPI_HOME} $ENV{MPI_HOME}
-                     PATH_SUFFIXES bin
-                     DOC "Executable for running MPI programs.")
-
-        set(MPIEXEC_NUMPROC_FLAG "-np" CACHE STRING "Flag used by MPI to specify the number of processes for MPIEXEC; the next option will be the number of processes.")
-        set(MPIEXEC_PREFLAGS "" CACHE STRING "These flags will be directly before the executable that is being run by MPIEXEC.")
-        set(MPIEXEC_POSTFLAGS "" CACHE STRING "These flags will come after all flags given to MPIEXEC.")
-        set(MPIEXEC_MAX_NUMPROCS "2" CACHE STRING "Maximum number of processors available to run MPI applications.")
-        mark_as_advanced(MPIEXEC MPIEXEC_NUMPROC_FLAG MPIEXEC_PREFLAGS MPIEXEC_POSTFLAGS MPIEXEC_MAX_NUMPROCS)
+        get_filename_component(_cmake_c_compiler_path "${CMAKE_C_COMPILER}" PATH)
+        get_filename_component(_cmake_cxx_compiler_path "${CMAKE_CXX_COMPILER}" PATH)
     endif ()
 
-    if (MPI_FOUND)
-        # Find path of the mpi compilers
-        if (${MPI_CXX_FOUND})
-            get_filename_component(_mpi_c_compiler_path "${MPI_CXX_COMPILER}" PATH)
-            get_filename_component(_mpiexec_path "${MPIEXEC}" PATH)
-        else ()
-            get_filename_component(_cmake_c_compiler_path "${CMAKE_C_COMPILER}" PATH)
-            get_filename_component(_cmake_cxx_compiler_path "${CMAKE_CXX_COMPILER}" PATH)
-        endif ()
-
-        # Test for and warn about unsuitable MPI versions
-        #
-        # Execute the ompi_info binary with the full path of the compiler wrapper
-        # found, otherwise we run the risk of false positives.
-        find_file(MPI_INFO_BIN ompi_info
-                  HINTS ${_mpi_c_compiler_path} ${_mpiexec_path}
-                  ${_cmake_c_compiler_path} ${_cmake_cxx_compiler_path}
-                  NO_DEFAULT_PATH
-                  NO_SYSTEM_ENVIRONMENT_PATH
-                  NO_CMAKE_SYSTEM_PATH)
-        if (MPI_INFO_BIN)
-            exec_program(${MPI_INFO_BIN}
-                         ARGS -v ompi full
-                         OUTPUT_VARIABLE OPENMPI_TYPE
-                         RETURN_VALUE OPENMPI_EXEC_RETURN)
-            if (OPENMPI_EXEC_RETURN EQUAL 0)
-                string(REGEX REPLACE ".*Open MPI: \([0-9]+\\.[0-9]*\\.?[0-9]*\).*" "\\1" OPENMPI_VERSION ${OPENMPI_TYPE})
-                if (OPENMPI_VERSION VERSION_LESS "1.4.1")
-                    MESSAGE(WARNING
-                            "CMake found OpenMPI version ${OPENMPI_VERSION} on your system. "
-                            "There are known problems with GROMACS and OpenMPI version < 1.4.1. "
-                            "Please consider updating your OpenMPI if your MPI wrapper compilers "
-                            "are using the above OpenMPI version.")
-                endif ()
-                if (OPENMPI_VERSION VERSION_EQUAL "1.8.6")
-                    MESSAGE(WARNING
-                            "CMake found OpenMPI version ${OPENMPI_VERSION} on your system. "
-                            "This OpenMPI version is known to leak memory with GROMACS,"
-                            "please update to a more recent version. ")
-                endif ()
-                unset(OPENMPI_VERSION)
-                unset(OPENMPI_TYPE)
-                unset(OPENMPI_EXEC_RETURN)
+    # Execute the ompi_info binary with the full path of the compiler wrapper
+    # found, otherwise we run the risk of false positives.
+    find_file(MPI_INFO_BIN ompi_info
+              HINTS ${_mpi_c_compiler_path} ${_mpiexec_path}
+              ${_cmake_c_compiler_path} ${_cmake_cxx_compiler_path}
+              NO_DEFAULT_PATH
+              NO_SYSTEM_ENVIRONMENT_PATH
+              NO_CMAKE_SYSTEM_PATH)
+    if (MPI_INFO_BIN)
+        exec_program(${MPI_INFO_BIN}
+                     ARGS -v ompi full
+                     OUTPUT_VARIABLE OPENMPI_TYPE
+                     RETURN_VALUE OPENMPI_EXEC_RETURN)
+        if (OPENMPI_EXEC_RETURN EQUAL 0)
+            string(REGEX REPLACE ".*Open MPI: \([0-9]+\\.[0-9]*\\.?[0-9]*\).*" "\\1" OPENMPI_VERSION ${OPENMPI_TYPE})
+            if (OPENMPI_VERSION VERSION_LESS "1.4.1")
+                MESSAGE(WARNING
+                        "CMake found OpenMPI version ${OPENMPI_VERSION} on your system. "
+                        "There are known problems with GROMACS and OpenMPI version < 1.4.1. "
+                        "Please consider updating your OpenMPI if your MPI wrapper compilers "
+                        "are using the above OpenMPI version.")
+            endif ()
+            if (OPENMPI_VERSION VERSION_EQUAL "1.8.6")
+                MESSAGE(WARNING
+                        "CMake found OpenMPI version ${OPENMPI_VERSION} on your system. "
+                        "This OpenMPI version is known to leak memory with GROMACS,"
+                        "please update to a more recent version. ")
             endif ()
+            unset(OPENMPI_VERSION)
+            unset(OPENMPI_TYPE)
+            unset(OPENMPI_EXEC_RETURN)
         endif ()
-        unset(MPI_INFO_BIN CACHE)
+    endif ()
+    unset(MPI_INFO_BIN CACHE)
 
-        # Execute the mpiname binary with the full path of the compiler wrapper
-        # found, otherwise we run the risk of false positives.
-        find_file(MPINAME_BIN mpiname
-                  HINTS ${_mpi_c_compiler_path}
-                  ${_cmake_c_compiler_path} ${_cmake_cxx_compiler_path}
-                  NO_DEFAULT_PATH
-                  NO_SYSTEM_ENVIRONMENT_PATH
-                  NO_CMAKE_SYSTEM_PATH)
-        if (MPINAME_BIN)
-            exec_program(${MPINAME_BIN}
-                         ARGS -n -v
-                         OUTPUT_VARIABLE MVAPICH2_TYPE
-                         RETURN_VALUE MVAPICH2_EXEC_RETURN)
-            if (MVAPICH2_EXEC_RETURN EQUAL 0)
-                string(REGEX MATCH "MVAPICH2" MVAPICH2_NAME ${MVAPICH2_TYPE})
-                # Want to check for MVAPICH2 in case some other library supplies mpiname
-                string(REGEX REPLACE "MVAPICH2 \([0-9]+\\.[0-9]*[a-z]?\\.?[0-9]*\)" "\\1" MVAPICH2_VERSION ${MVAPICH2_TYPE})
-                if (${MVAPICH2_NAME} STREQUAL "MVAPICH2" AND MVAPICH2_VERSION VERSION_LESS "1.5")
-                    # This test works correctly even with 1.5a1
-                    MESSAGE(WARNING
-                            "CMake found MVAPICH2 version ${MVAPICH2_VERSION} on your system. "
-                            "There are known problems with GROMACS and MVAPICH2 version < 1.5. "
-                            "Please consider updating your MVAPICH2 if your MPI wrapper compilers "
-                            "are using the above MVAPICH2 version.")
-                endif ()
-                unset(MVAPICH2_VERSION)
-                unset(MVAPICH2_NAME)
-                unset(MVAPICH2_TYPE)
-                unset(MVAPICH2_EXEC_RETURN)
+    # Execute the mpiname binary with the full path of the compiler wrapper
+    # found, otherwise we run the risk of false positives.
+    find_file(MPINAME_BIN mpiname
+              HINTS ${_mpi_c_compiler_path}
+              ${_cmake_c_compiler_path} ${_cmake_cxx_compiler_path}
+              NO_DEFAULT_PATH
+              NO_SYSTEM_ENVIRONMENT_PATH
+              NO_CMAKE_SYSTEM_PATH)
+    if (MPINAME_BIN)
+        exec_program(${MPINAME_BIN}
+                     ARGS -n -v
+                     OUTPUT_VARIABLE MVAPICH2_TYPE
+                     RETURN_VALUE MVAPICH2_EXEC_RETURN)
+        if (MVAPICH2_EXEC_RETURN EQUAL 0)
+            string(REGEX MATCH "MVAPICH2" MVAPICH2_NAME ${MVAPICH2_TYPE})
+            # Want to check for MVAPICH2 in case some other library supplies mpiname
+            string(REGEX REPLACE "MVAPICH2 \([0-9]+\\.[0-9]*[a-z]?\\.?[0-9]*\)" "\\1" MVAPICH2_VERSION ${MVAPICH2_TYPE})
+            if (${MVAPICH2_NAME} STREQUAL "MVAPICH2" AND MVAPICH2_VERSION VERSION_LESS "1.5")
+                # This test works correctly even with 1.5a1
+                MESSAGE(WARNING
+                        "CMake found MVAPICH2 version ${MVAPICH2_VERSION} on your system. "
+                        "There are known problems with GROMACS and MVAPICH2 version < 1.5. "
+                        "Please consider updating your MVAPICH2 if your MPI wrapper compilers "
+                        "are using the above MVAPICH2 version.")
             endif ()
+            unset(MVAPICH2_VERSION)
+            unset(MVAPICH2_NAME)
+            unset(MVAPICH2_TYPE)
+            unset(MVAPICH2_EXEC_RETURN)
         endif ()
-        unset(MPINAME_BIN CACHE)
-    else ()
-        message(FATAL_ERROR
-                "MPI support requested, but no MPI compiler found. Either set the "
-                "C-compiler (CMAKE_C_COMPILER) to the MPI compiler (often called mpicc), "
-                "or set the variables reported missing for MPI_CXX above.")
     endif ()
+    unset(MPINAME_BIN CACHE)
+endif () # end if(GMX_LIB_MPI)
 
-    set(GMX_LIB_MPI 1)
+# Look for MPI process launchers that may be missed, especially if we didn't
+# need to find the full MPI library build system support.
+if (NOT MPIEXEC_EXECUTABLE)
+    find_program(MPIEXEC
+                 NAMES mpiexec mpirun lamexec srun aprun poe
+                 HINTS ${MPI_HOME} $ENV{MPI_HOME}
+                 PATH_SUFFIXES bin
+                 DOC "Executable for running MPI programs.")
+
+    set(MPIEXEC_EXECUTABLE "$MPIEXEC")
+    set(MPIEXEC_NUMPROC_FLAG "-np" CACHE STRING "Flag used by MPI to specify the number of processes for MPIEXEC; the next option will be the number of processes.")
+    set(MPIEXEC_PREFLAGS "" CACHE STRING "These flags will be directly before the executable that is being run by MPIEXEC.")
+    set(MPIEXEC_POSTFLAGS "" CACHE STRING "These flags will come after all flags given to MPIEXEC.")
+    set(MPIEXEC_MAX_NUMPROCS "2" CACHE STRING "Maximum number of processors available to run MPI applications.")
+    mark_as_advanced(MPIEXEC MPIEXEC_NUMPROC_FLAG MPIEXEC_PREFLAGS MPIEXEC_POSTFLAGS MPIEXEC_MAX_NUMPROCS)
 endif ()