CMake helper for multichoice options.
authorTeemu Murtola <teemu.murtola@gmail.com>
Fri, 24 May 2013 09:46:20 +0000 (12:46 +0300)
committerGerrit Code Review <gerrit@gerrit.gromacs.org>
Sat, 21 Sep 2013 13:03:24 +0000 (15:03 +0200)
Add a helper routine to create CMake input options with multiple
choices.  In addition to creating the cache variable, it does
case-insensitive checking of user input, and transforms the input to
uppercase.  It also automatically adds the list of allowed values to the
description and to the error message if the input is not valid.

Doesn't change the behavior much (except for CMake GUI that may honor
the STRINGS property; it appears ccmake doesn't), but makes the code
easier to maintain: changing the list of values in one place propagates
everywhere except to the actual logic of using the variable, and the
code behaves more uniformly for all of these values.

Change-Id: Ic8f674e5ac29c19bac7c846b1970689f71dc2384

CMakeLists.txt
cmake/gmxOptionUtilities.cmake [new file with mode: 0644]

index ae04ed474fb877b37ab58757d2310c6f6b611e5e..691c47e6b5542685fa8e5914815a4e05dc5c394e 100644 (file)
@@ -110,12 +110,15 @@ if(CMAKE_HOST_UNIX)
 endif()
 
 ########################################################################
+# User input options                                                   #
+########################################################################
+include(gmxOptionUtilities)
+
 option(GMX_COOL_QUOTES "Enable Gromacs cool quotes" ON)
 mark_as_advanced(GMX_COOL_QUOTES)
 
 set(CMAKE_PREFIX_PATH "" CACHE STRING "Extra locations to search for external libraries and tools (give directory without lib, bin, or include)")
-# User input options                                                   #
-########################################################################
+
 option(GMX_DOUBLE "Use double precision (much slower, use only if you really need it)" OFF)
 option(GMX_MPI    "Build a parallel (message-passing) version of GROMACS" OFF)
 option(GMX_THREAD_MPI  "Build a thread-MPI-based multithreaded version of GROMACS (not compatible with MPI)" ON)
@@ -128,6 +131,7 @@ mark_as_advanced(GMX_FAHCORE)
 include(gmxManageGPU)
 
 include(gmxDetectAcceleration)
+set(GMX_SUGGESTED_CPU_ACCELERATION "None")
 if(NOT DEFINED GMX_CPU_ACCELERATION)
     if(CMAKE_CROSSCOMPILING)
         if("${CMAKE_SYSTEM_NAME}" MATCHES "BlueGeneQ")
@@ -140,18 +144,30 @@ if(NOT DEFINED GMX_CPU_ACCELERATION)
     endif(CMAKE_CROSSCOMPILING)
 endif(NOT DEFINED GMX_CPU_ACCELERATION)
 
-set(GMX_CPU_ACCELERATION "@GMX_SUGGESTED_CPU_ACCELERATION@"
-    CACHE STRING "Accelerated CPU kernels. Pick one of: None, SSE2, SSE4.1, AVX_128_FMA, AVX_256, IBM_QPX, Sparc64_HPC_ACE")
+gmx_option_multichoice(
+    GMX_CPU_ACCELERATION
+    "Acceleration for CPU kernels and compiler optimization"
+    "${GMX_SUGGESTED_CPU_ACCELERATION}"
+    None SSE2 SSE4.1 AVX_128_FMA AVX_256 IBM_QPX Sparc64_HPC_ACE)
+
+gmx_option_multichoice(
+    GMX_FFT_LIBRARY
+    "FFT library"
+    "fftw3"
+    fftw3 mkl "fftpack[built-in]")
 
-set(GMX_FFT_LIBRARY "fftw3" 
-    CACHE STRING "FFT library choices: fftw3,mkl,fftpack[built-in]")
 option(GMX_BUILD_OWN_FFTW "Download and build FFTW 3 during the GROMACS build process, rather than fall back on the really slow fftpack." OFF)
 mark_as_advanced(GMX_BUILD_OWN_FFTW)
 option(GMX_DISABLE_FFTW_MEASURE 
        "Do not optimize FFTW setups (not needed with SSE)" OFF)
 mark_as_advanced(GMX_DISABLE_FFTW_MEASURE)
-set(GMX_QMMM_PROGRAM "none" 
-    CACHE STRING "QM package choices: none,gaussian,mopac,gamess,orca")
+
+gmx_option_multichoice(
+    GMX_QMMM_PROGRAM
+    "QM package for QM/MM"
+    None
+    none gaussian mopac gamess orca)
+
 option(GMX_BROKEN_CALLOC "Work around broken calloc()" OFF)
 mark_as_advanced(GMX_BROKEN_CALLOC)
 option(GMX_MPI_IN_PLACE "Enable MPI_IN_PLACE for MPIs that have it defined" ON)
@@ -646,7 +662,6 @@ include(gmxTestAVXMaskload)
 # GMX_<arch>_<feature-set> that are exported to the C code to specify
 # CPU features that should be used. This means that the logic for
 # requiring such backward compatibility is all located here.
-string(TOUPPER ${GMX_CPU_ACCELERATION} GMX_CPU_ACCELERATION)
 if(${GMX_CPU_ACCELERATION} STREQUAL "NONE")
     # nothing to do
 elseif(${GMX_CPU_ACCELERATION} STREQUAL "SSE2")
@@ -854,13 +869,12 @@ elseif(${GMX_CPU_ACCELERATION} STREQUAL "IBM_QPX")
     endif()
 elseif(${GMX_CPU_ACCELERATION} STREQUAL "SPARC64_HPC_ACE")
     set(GMX_CPU_ACCELERATION_SPARC64_HPC_ACE 1)
-else(${GMX_CPU_ACCELERATION} STREQUAL "NONE")
-    MESSAGE(FATAL_ERROR "Unrecognized option for accelerated kernels: ${GMX_CPU_ACCELERATION}. Pick one of None, SSE2, SSE4.1, AVX_128_FMA, AVX_256, IBM_QPX, Sparc64_HPC_ACE")
-endif(${GMX_CPU_ACCELERATION} STREQUAL "NONE")
+else()
+    gmx_invalid_option_value(GMX_CPU_ACCELERATION)
+endif()
 set(ACCELERATION_QUIETLY TRUE CACHE INTERNAL "")
 
 # Process QM/MM Settings
-string(TOUPPER ${GMX_QMMM_PROGRAM} GMX_QMMM_PROGRAM)
 if(${GMX_QMMM_PROGRAM} STREQUAL "GAUSSIAN")
     set(GMX_QMMM_GAUSSIAN 1)
 elseif(${GMX_QMMM_PROGRAM} STREQUAL "MOPAC")
@@ -871,12 +885,11 @@ elseif(${GMX_QMMM_PROGRAM} STREQUAL "ORCA")
     set(GMX_QMMM_ORCA 1)
 elseif(${GMX_QMMM_PROGRAM} STREQUAL "NONE")
     # nothing to do
-else(${GMX_QMMM_PROGRAM} STREQUAL "GAUSSIAN")
-    MESSAGE(FATAL_ERROR "Invalid QM/MM program option: ${GMX_QMMM_PROGRAM}. Choose one of: Gaussian, Mopac, Gamess, Orca, None")
-endif(${GMX_QMMM_PROGRAM} STREQUAL "GAUSSIAN")
+else()
+    gmx_invalid_option_value(GMX_QMMM_PROGRAM)
+endif()
 
 # Process FFT library settings
-string(TOUPPER ${GMX_FFT_LIBRARY} GMX_FFT_LIBRARY)
 set(PKG_FFT "")
 set(PKG_FFT_LIBS "")
 set(MKL_LIBRARIES_FORMAT_DESCRIPTION "Use full paths to library files, in the right order, and separated by semicolons.")
@@ -983,7 +996,7 @@ elseif(${GMX_FFT_LIBRARY} STREQUAL "FFTPACK")
         MESSAGE(STATUS "Using internal FFT library - fftpack")
     endif()
 else(${GMX_FFT_LIBRARY} STREQUAL "FFTW3")
-    MESSAGE(FATAL_ERROR "Invalid FFT library setting: ${GMX_FFT_LIBRARY}. Choose one of: fftw3, mkl, fftpack")
+    gmx_invalid_option_value(GMX_FFT_LIBRARY)
 endif(${GMX_FFT_LIBRARY} STREQUAL "FFTW3")
 set(GMX_FFT_LIBRARY_PREVIOUS_VALUE ${GMX_FFT_LIBRARY} CACHE INTERNAL "Previous value for GMX_FFT_LIBRARY, so that we can detect when it changes.")
 
diff --git a/cmake/gmxOptionUtilities.cmake b/cmake/gmxOptionUtilities.cmake
new file mode 100644 (file)
index 0000000..1545f56
--- /dev/null
@@ -0,0 +1,90 @@
+#
+# This file is part of the GROMACS molecular simulation package.
+#
+# Copyright (c) 2013, by the GROMACS development team, led by
+# David van der Spoel, Berk Hess, 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.
+
+#
+# Helper functions for managing more complex options
+#
+
+# Creates a string cache variable with multiple choices
+#
+# Usage:
+#   gmx_option_multichoice(VAR "Description" DEFAULT_VALUE
+#                          Value1 Value2 ... ValueN)
+# Output:
+#   VAR is set in the cache and in the caller's scope. The caller can assume
+#   that it is always one of the provided values, converted to uppercase.
+#
+# Main benefit is that the list of allowed values only needs to be provided
+# once, and gets used in multiple contexts:
+#   1. It is automatically added to the description.
+#   2. It is set as the STRINGS property of the created cache variable for use
+#      with CMake GUIs.
+#   3. The user-provided value is checked against the list, and a fatal error
+#      is produced if the value is not known.  The caller does not need to
+#      produce good error messages in cases where it may be necessary to check
+#      for the validity again.
+# As a special case, any "[built-in]" string in the allowed values is ignored
+# when checking the user-provided value, but is added to all user-visible
+# messages.
+#
+# It appears that ccmake does not use the STRINGS property, but perhaps some
+# day...
+#
+function(GMX_OPTION_MULTICHOICE NAME DESCRIPTION DEFAULT)
+    # Some processing of the input values
+    string(REPLACE ";" ", " _allowed_comma_separated "${ARGN}")
+    set(_description "${DESCRIPTION}. Pick one of: ${_allowed_comma_separated}")
+    string(REPLACE "[built-in]" "" _allowed "${ARGN}")
+
+    # Set the cache properties
+    set(${NAME} ${DEFAULT} CACHE STRING ${_description})
+    set_property(CACHE ${NAME} PROPERTY STRINGS ${_allowed})
+
+    # Check that the value is one of the allowed
+    set(_org_value "${${NAME}}")
+    string(TOUPPER "${${NAME}}" ${NAME})
+    string(TOUPPER "${_allowed}" _allowed_as_upper)
+    list(FIND _allowed_as_upper "${${NAME}}" _found_index)
+    if (_found_index EQUAL -1)
+        message(FATAL_ERROR "Invalid value for ${NAME}: ${_org_value}.  "
+                            "Pick one of: ${_allowed_comma_separated}")
+    endif ()
+    # Always provide the upper-case value to the caller
+    set(${NAME} "${${NAME}}" PARENT_SCOPE)
+endfunction()
+
+# Convenience function for reporting a fatal error for an invalid input value
+function(GMX_INVALID_OPTION_VALUE NAME)
+    message(FATAL_ERROR "Invalid value for ${NAME}: ${${NAME}}")
+endfunction()