From 90dead024c5adee57e5ea3f3c90aed297d41ce3a Mon Sep 17 00:00:00 2001
From: Robert Maynard <robert.maynard@kitware.com>
Date: Fri, 25 Sep 2020 14:58:18 -0400
Subject: [PATCH] CheckCompilerFlag: unified way to check compiler flags per
 language

---
 Help/manual/cmake-modules.7.rst           |   1 +
 Help/module/CheckCompilerFlag.rst         |   1 +
 Help/release/dev/check-source-modules.rst |   4 +
 Modules/CheckCCompilerFlag.cmake          |  26 ++----
 Modules/CheckCXXCompilerFlag.cmake        |  26 ++----
 Modules/CheckCompilerFlag.cmake           | 105 ++++++++++++++++++++++
 Modules/CheckFortranCompilerFlag.cmake    |  28 +-----
 Modules/CheckOBJCCompilerFlag.cmake       |  27 +-----
 Modules/CheckOBJCXXCompilerFlag.cmake     |  27 +-----
 9 files changed, 128 insertions(+), 117 deletions(-)
 create mode 100644 Help/module/CheckCompilerFlag.rst
 create mode 100644 Modules/CheckCompilerFlag.cmake

diff --git a/Help/manual/cmake-modules.7.rst b/Help/manual/cmake-modules.7.rst
index d364198795..14af14996c 100644
--- a/Help/manual/cmake-modules.7.rst
+++ b/Help/manual/cmake-modules.7.rst
@@ -45,6 +45,7 @@ These modules are loaded using the :command:`include` command.
    /module/CheckOBJCXXSourceRuns
    /module/CheckPIESupported
    /module/CheckPrototypeDefinition
+   /module/CheckCompilerFlag
    /module/CheckSourceCompiles
    /module/CheckSourceRuns
    /module/CheckStructHasMember
diff --git a/Help/module/CheckCompilerFlag.rst b/Help/module/CheckCompilerFlag.rst
new file mode 100644
index 0000000000..bcf19a8535
--- /dev/null
+++ b/Help/module/CheckCompilerFlag.rst
@@ -0,0 +1 @@
+.. cmake-module:: ../../Modules/CheckCompilerFlag.cmake
diff --git a/Help/release/dev/check-source-modules.rst b/Help/release/dev/check-source-modules.rst
index 9a7785a8fb..e6cccbecd1 100644
--- a/Help/release/dev/check-source-modules.rst
+++ b/Help/release/dev/check-source-modules.rst
@@ -8,3 +8,7 @@ check-source-modules
 * The :module:`CheckSourceRuns` module has been added to
   generalize :module:`CheckCSourceRuns` and
   :module:`CheckCXXSourceRuns` to more languages.
+
+* The :module:`CheckCompilerFlag` module has been added to
+  generalize :module:`CheckCCompilerFlag` and
+  :module:`CheckCXXCompilerFlag` to more languages.
diff --git a/Modules/CheckCCompilerFlag.cmake b/Modules/CheckCCompilerFlag.cmake
index 1452b51866..f835f29692 100644
--- a/Modules/CheckCCompilerFlag.cmake
+++ b/Modules/CheckCCompilerFlag.cmake
@@ -34,24 +34,8 @@ effect or even a specific one is beyond the scope of this module.
 
 include_guard(GLOBAL)
 include(CheckCSourceCompiles)
-include(CMakeCheckCompilerFlagCommonPatterns)
-
-function(check_c_compiler_flag _flag _var)
-  set(CMAKE_REQUIRED_DEFINITIONS "${_flag}")
-
-  # Normalize locale during test compilation.
-  set(_locale_vars LC_ALL LC_MESSAGES LANG)
-  foreach(v IN LISTS _locale_vars)
-    set(_locale_vars_saved_${v} "$ENV{${v}}")
-    set(ENV{${v}} C)
-  endforeach()
-  check_compiler_flag_common_patterns(_common_patterns)
-  check_c_source_compiles("int main(void) { return 0; }" ${_var}
-    # Some compilers do not fail with a bad flag
-    FAIL_REGEX "command line option .* is valid for .* but not for C" # GNU
-    ${_common_patterns}
-    )
-  foreach(v IN LISTS _locale_vars)
-    set(ENV{${v}} ${_locale_vars_saved_${v}})
-  endforeach()
-endfunction()
+include(CheckCompilerFlag)
+
+macro (CHECK_C_COMPILER_FLAG _FLAG _RESULT)
+  check_compiler_flag(C "${_FLAG}" ${_RESULT})
+endmacro ()
diff --git a/Modules/CheckCXXCompilerFlag.cmake b/Modules/CheckCXXCompilerFlag.cmake
index 544e9ac60c..ce49ae3e69 100644
--- a/Modules/CheckCXXCompilerFlag.cmake
+++ b/Modules/CheckCXXCompilerFlag.cmake
@@ -34,24 +34,8 @@ effect or even a specific one is beyond the scope of this module.
 
 include_guard(GLOBAL)
 include(CheckCXXSourceCompiles)
-include(CMakeCheckCompilerFlagCommonPatterns)
-
-function(check_cxx_compiler_flag _flag _var)
-  set(CMAKE_REQUIRED_DEFINITIONS "${_flag}")
-
-  # Normalize locale during test compilation.
-  set(_locale_vars LC_ALL LC_MESSAGES LANG)
-  foreach(v IN LISTS _locale_vars)
-    set(_locale_vars_saved_${v} "$ENV{${v}}")
-    set(ENV{${v}} C)
-  endforeach()
-  check_compiler_flag_common_patterns(_common_patterns)
-  check_cxx_source_compiles("int main() { return 0; }" ${_var}
-    # Some compilers do not fail with a bad flag
-    FAIL_REGEX "command line option .* is valid for .* but not for C\\\\+\\\\+" # GNU
-    ${_common_patterns}
-    )
-  foreach(v IN LISTS _locale_vars)
-    set(ENV{${v}} ${_locale_vars_saved_${v}})
-  endforeach()
-endfunction()
+include(CheckCompilerFlag)
+
+macro (CHECK_CXX_COMPILER_FLAG _FLAG _RESULT)
+  check_compiler_flag(CXX "${_FLAG}" ${_RESULT})
+endmacro ()
diff --git a/Modules/CheckCompilerFlag.cmake b/Modules/CheckCompilerFlag.cmake
new file mode 100644
index 0000000000..9223009fd1
--- /dev/null
+++ b/Modules/CheckCompilerFlag.cmake
@@ -0,0 +1,105 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+#[=======================================================================[.rst:
+CheckCompilerFlag
+---------------------
+
+.. versionadded:: 3.19
+
+Check whether the compiler supports a given flag.
+
+.. command:: check_compiler_flag
+
+  .. code-block:: cmake
+
+    check_compiler_flag(<lang> <flag> <var>)
+
+Check that the ``<flag>`` is accepted by the compiler without a diagnostic.
+Stores the result in an internal cache entry named ``<var>``.
+
+This command temporarily sets the ``CMAKE_REQUIRED_DEFINITIONS`` variable
+and calls the ``check_source_compiles(<LANG>`` function from the
+:module:`CheckSourceCompiles` module.  See documentation of that
+module for a listing of variables that can otherwise modify the build.
+
+A positive result from this check indicates only that the compiler did not
+issue a diagnostic message when given the flag.  Whether the flag has any
+effect or even a specific one is beyond the scope of this module.
+
+.. note::
+  Since the :command:`try_compile` command forwards flags from variables
+  like :variable:`CMAKE_<LANG>_FLAGS <CMAKE_<LANG>_FLAGS>`, unknown flags
+  in such variables may cause a false negative for this check.
+#]=======================================================================]
+
+include_guard(GLOBAL)
+include(CheckSourceCompiles)
+include(CMakeCheckCompilerFlagCommonPatterns)
+
+cmake_policy(PUSH)
+cmake_policy(SET CMP0054 NEW) # if() quoted variables not dereferenced
+cmake_policy(SET CMP0057 NEW) # if() supports IN_LIST
+
+function(CHECK_COMPILER_FLAG _lang _flag _var)
+
+  if(_lang STREQUAL C)
+    set(_lang_src "int main(void) { return 0; }")
+    set(_lang_fail_regex FAIL_REGEX "command line option .* is valid for .* but not for C")
+  elseif(_lang STREQUAL CXX)
+    set(_lang_src "int main() { return 0; }")
+    set(_lang_fail_regex FAIL_REGEX "command line option .* is valid for .* but not for C\\+\\+")
+  elseif(_lang STREQUAL Fortran)
+    set(_lang_src "       program test\n       stop\n       end program")
+    set(_lang_fail_regex FAIL_REGEX "command line option .* is valid for .* but not for Fortran")
+  elseif(_lang STREQUAL OBJC)
+    set(_lang_src [=[
+#ifndef __OBJC__
+#  error "Not an Objective-C compiler"
+#endif
+int main(void) { return 0; }]=])
+    set(_lang_fail_regex FAIL_REGEX "command line option .* is valid for .* but not for Objective-C" # GNU
+                         FAIL_REGEX "argument unused during compilation: .*") # Clang
+  elseif(_lang STREQUAL OBJCXX)
+    set(_lang_src [=[
+#ifndef __OBJC__
+#  error "Not an Objective-C++ compiler"
+#endif
+int main(void) { return 0; }]=])
+    set(_lang_fail_regex FAIL_REGEX "command line option .* is valid for .* but not for Objective-C\\+\\+" # GNU
+                         FAIL_REGEX "argument unused during compilation: .*") # Clang
+  else()
+    message (SEND_ERROR "check_compiler_flag: ${_lang}: unknown language.")
+    return()
+  endif()
+
+  get_property (_supported_languages GLOBAL PROPERTY ENABLED_LANGUAGES)
+  if (NOT _lang IN_LIST _supported_languages)
+    message (SEND_ERROR "check_compiler_flag: ${_lang}: needs to be enabled before use.")
+    return()
+  endif()
+
+  set(CMAKE_REQUIRED_DEFINITIONS ${_flag})
+
+  # Normalize locale during test compilation.
+  set(_locale_vars LC_ALL LC_MESSAGES LANG)
+  foreach(v IN LISTS _locale_vars)
+    set(_locale_vars_saved_${v} "$ENV{${v}}")
+    set(ENV{${v}} C)
+  endforeach()
+
+  check_compiler_flag_common_patterns(_common_patterns)
+  check_source_compiles(${_lang}
+    "${_lang_src}"
+    ${_var}
+    ${_lang_fail_regex}
+    ${_common_patterns}
+    )
+
+  foreach(v IN LISTS _locale_vars)
+    set(ENV{${v}} ${_locale_vars_saved_${v}})
+  endforeach()
+  set(${_var} "${${_var}}" PARENT_SCOPE)
+endfunction ()
+
+cmake_policy(POP)
diff --git a/Modules/CheckFortranCompilerFlag.cmake b/Modules/CheckFortranCompilerFlag.cmake
index b8fac97cc5..0f5cf9a248 100644
--- a/Modules/CheckFortranCompilerFlag.cmake
+++ b/Modules/CheckFortranCompilerFlag.cmake
@@ -36,30 +36,8 @@ effect or even a specific one is beyond the scope of this module.
 
 include_guard(GLOBAL)
 include(CheckFortranSourceCompiles)
-include(CMakeCheckCompilerFlagCommonPatterns)
+include(CheckCompilerFlag)
 
-macro (CHECK_Fortran_COMPILER_FLAG _FLAG _RESULT)
-  set(SAFE_CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS}")
-  set(CMAKE_REQUIRED_DEFINITIONS "${_FLAG}")
-
-  # Normalize locale during test compilation.
-  set(_CheckFortranCompilerFlag_LOCALE_VARS LC_ALL LC_MESSAGES LANG)
-  foreach(v ${_CheckFortranCompilerFlag_LOCALE_VARS})
-    set(_CheckFortranCompilerFlag_SAVED_${v} "$ENV{${v}}")
-    set(ENV{${v}} C)
-  endforeach()
-  CHECK_COMPILER_FLAG_COMMON_PATTERNS(_CheckFortranCompilerFlag_COMMON_PATTERNS)
-  CHECK_Fortran_SOURCE_COMPILES("       program test\n       stop\n       end program" ${_RESULT}
-    # Some compilers do not fail with a bad flag
-    FAIL_REGEX "command line option .* is valid for .* but not for Fortran" # GNU
-    ${_CheckFortranCompilerFlag_COMMON_PATTERNS}
-    )
-  foreach(v ${_CheckFortranCompilerFlag_LOCALE_VARS})
-    set(ENV{${v}} ${_CheckFortranCompilerFlag_SAVED_${v}})
-    unset(_CheckFortranCompilerFlag_SAVED_${v})
-  endforeach()
-  unset(_CheckFortranCompilerFlag_LOCALE_VARS)
-  unset(_CheckFortranCompilerFlag_COMMON_PATTERNS)
-
-  set (CMAKE_REQUIRED_DEFINITIONS "${SAFE_CMAKE_REQUIRED_DEFINITIONS}")
+macro (CHECK_FORTRAN_COMPILER_FLAG _FLAG _RESULT)
+  check_compiler_flag(Fortran "${_FLAG}" ${_RESULT})
 endmacro ()
diff --git a/Modules/CheckOBJCCompilerFlag.cmake b/Modules/CheckOBJCCompilerFlag.cmake
index a98f4293c9..df9d724bbf 100644
--- a/Modules/CheckOBJCCompilerFlag.cmake
+++ b/Modules/CheckOBJCCompilerFlag.cmake
@@ -36,31 +36,8 @@ effect or even a specific one is beyond the scope of this module.
 
 include_guard(GLOBAL)
 include(CheckOBJCSourceCompiles)
-include(CMakeCheckCompilerFlagCommonPatterns)
+include(CheckCompilerFlag)
 
 macro (CHECK_OBJC_COMPILER_FLAG _FLAG _RESULT)
-  set(SAFE_CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS}")
-  set(CMAKE_REQUIRED_DEFINITIONS "${_FLAG}")
-
-   # Normalize locale during test compilation.
-  set(_CheckOBJCCompilerFlag_LOCALE_VARS LC_ALL LC_MESSAGES LANG)
-  foreach(v ${_CheckOBJCCompilerFlag_LOCALE_VARS})
-    set(_CheckOBJCCompilerFlag_SAVED_${v} "$ENV{${v}}")
-    set(ENV{${v}} OBJC)
-  endforeach()
-  CHECK_COMPILER_FLAG_COMMON_PATTERNS(_CheckOBJCCompilerFlag_COMMON_PATTERNS)
-  CHECK_OBJC_SOURCE_COMPILES("#ifndef __OBJC__\n#  error \"Not an Objective-C compiler\"\n#endif\nint main(void) { return 0; }" ${_RESULT}
-    # Some compilers do not fail with a bad flag
-    FAIL_REGEX "command line option .* is valid for .* but not for Objective-C" # GNU
-    FAIL_REGEX "argument unused during compilation: .*" # Clang
-    ${_CheckOBJCCompilerFlag_COMMON_PATTERNS}
-    )
-  foreach(v ${_CheckOBJCCompilerFlag_LOCALE_VARS})
-    set(ENV{${v}} ${_CheckOBJCCompilerFlag_SAVED_${v}})
-    unset(_CheckOBJCCompilerFlag_SAVED_${v})
-  endforeach()
-  unset(_CheckOBJCCompilerFlag_LOCALE_VARS)
-  unset(_CheckOBJCCompilerFlag_COMMON_PATTERNS)
-
-  set (CMAKE_REQUIRED_DEFINITIONS "${SAFE_CMAKE_REQUIRED_DEFINITIONS}")
+  check_compiler_flag(OBJC "${_FLAG}" ${_RESULT})
 endmacro ()
diff --git a/Modules/CheckOBJCXXCompilerFlag.cmake b/Modules/CheckOBJCXXCompilerFlag.cmake
index 7944ab07a0..6e01bcc951 100644
--- a/Modules/CheckOBJCXXCompilerFlag.cmake
+++ b/Modules/CheckOBJCXXCompilerFlag.cmake
@@ -36,31 +36,8 @@ effect or even a specific one is beyond the scope of this module.
 
 include_guard(GLOBAL)
 include(CheckOBJCXXSourceCompiles)
-include(CMakeCheckCompilerFlagCommonPatterns)
+include(CheckCompilerFlag)
 
 macro (CHECK_OBJCXX_COMPILER_FLAG _FLAG _RESULT)
-  set(SAFE_CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS}")
-  set(CMAKE_REQUIRED_DEFINITIONS "${_FLAG}")
-
-  # Normalize locale during test compilation.
-  set(_CheckOBJCXXCompilerFlag_LOCALE_VARS LC_ALL LC_MESSAGES LANG)
-  foreach(v ${_CheckOBJCXXCompilerFlag_LOCALE_VARS})
-    set(_CheckOBJCXXCompilerFlag_SAVED_${v} "$ENV{${v}}")
-    set(ENV{${v}} OBJCXX)
-  endforeach()
-  CHECK_COMPILER_FLAG_COMMON_PATTERNS(_CheckOBJCXXCompilerFlag_COMMON_PATTERNS)
-  CHECK_OBJCXX_SOURCE_COMPILES("#ifndef __OBJC__\n#  error \"Not an Objective-C++ compiler\"\n#endif\nint main(void) { return 0; }" ${_RESULT}
-    # Some compilers do not fail with a bad flag
-    FAIL_REGEX "command line option .* is valid for .* but not for Objective-C\\\\+\\\\+" # GNU
-    FAIL_REGEX "argument unused during compilation: .*" # Clang
-    ${_CheckOBJCXXCompilerFlag_COMMON_PATTERNS}
-    )
-  foreach(v ${_CheckOBJCXXCompilerFlag_LOCALE_VARS})
-    set(ENV{${v}} ${_CheckOBJCXXCompilerFlag_SAVED_${v}})
-    unset(_CheckOBJCXXCompilerFlag_SAVED_${v})
-  endforeach()
-  unset(_CheckOBJCXXCompilerFlag_LOCALE_VARS)
-  unset(_CheckOBJCXXCompilerFlag_COMMON_PATTERNS)
-
-  set (CMAKE_REQUIRED_DEFINITIONS "${SAFE_CMAKE_REQUIRED_DEFINITIONS}")
+  check_compiler_flag(OBJCXX "${_FLAG}" ${_RESULT})
 endmacro ()
-- 
GitLab