From ee4673c1ae1e4a1aa4687412717567c2ffbb501b Mon Sep 17 00:00:00 2001
From: Ben Boeckel <ben.boeckel@kitware.com>
Date: Mon, 9 Dec 2019 08:19:35 -0500
Subject: FPHSA: detect package name mismatches

The `FPHSA_NAME_MISMATCHED` variable may be set if this is intentional
(but should be cleared after the call to not affect other FPHSA calls).
It may also be passed via the `NAME_MISMATCHED` option for new-signature
FPHSA calls.
---
 Help/release/dev/fphsa-detect-name-mismatch.rst    |  5 ++
 Modules/FindPackageHandleStandardArgs.cmake        | 56 ++++++++++++++++++++--
 Tests/RunCMake/FPHSA/FindNameMismatch.cmake        |  4 ++
 Tests/RunCMake/FPHSA/FindNameMismatchOld.cmake     |  4 ++
 .../FPHSA/FindNameMismatchSuppressed.cmake         |  6 +++
 .../FPHSA/FindNameMismatchSuppressedArg.cmake      |  4 ++
 .../FPHSA/FindNameMismatchSuppressedCompat.cmake   |  6 +++
 Tests/RunCMake/FPHSA/NameMismatch-stderr.txt       | 23 +++++++++
 Tests/RunCMake/FPHSA/NameMismatch.cmake            |  7 +++
 Tests/RunCMake/FPHSA/RunCMakeTest.cmake            |  1 +
 10 files changed, 113 insertions(+), 3 deletions(-)
 create mode 100644 Help/release/dev/fphsa-detect-name-mismatch.rst
 create mode 100644 Tests/RunCMake/FPHSA/FindNameMismatch.cmake
 create mode 100644 Tests/RunCMake/FPHSA/FindNameMismatchOld.cmake
 create mode 100644 Tests/RunCMake/FPHSA/FindNameMismatchSuppressed.cmake
 create mode 100644 Tests/RunCMake/FPHSA/FindNameMismatchSuppressedArg.cmake
 create mode 100644 Tests/RunCMake/FPHSA/FindNameMismatchSuppressedCompat.cmake
 create mode 100644 Tests/RunCMake/FPHSA/NameMismatch-stderr.txt
 create mode 100644 Tests/RunCMake/FPHSA/NameMismatch.cmake

diff --git a/Help/release/dev/fphsa-detect-name-mismatch.rst b/Help/release/dev/fphsa-detect-name-mismatch.rst
new file mode 100644
index 0000000..be51a43
--- /dev/null
+++ b/Help/release/dev/fphsa-detect-name-mismatch.rst
@@ -0,0 +1,5 @@
+fphsa-name-mismatch
+-------------------
+
+* The :module:`FindPackageHandleStandardArgs` module learned to check the
+  package name passed in for typo mistakes.
diff --git a/Modules/FindPackageHandleStandardArgs.cmake b/Modules/FindPackageHandleStandardArgs.cmake
index d824ee8..a7c3eae 100644
--- a/Modules/FindPackageHandleStandardArgs.cmake
+++ b/Modules/FindPackageHandleStandardArgs.cmake
@@ -27,6 +27,7 @@ valid filepaths.
       [VERSION_VAR <version-var>]
       [HANDLE_COMPONENTS]
       [CONFIG_MODE]
+      [NAME_MISMATCHED]
       [REASON_FAILURE_MESSAGE <reason-failure-message>]
       [FAIL_MESSAGE <custom-failure-message>]
       )
@@ -90,6 +91,12 @@ valid filepaths.
     Specify a custom failure message instead of using the default
     generated message.  Not recommended.
 
+  ``NAME_MISMATCHED``
+    Indicate that the ``<PackageName>`` does not match
+    ``${CMAKE_FIND_PACKAGE_NAME}``. This is usually a mistake and raises a
+    warning, but it may be intentional for usage of the command for components
+    of a larger package.
+
 Example for the simple signature:
 
 .. code-block:: cmake
@@ -106,6 +113,17 @@ used or not.  If it is found, success will be reported, including
 the content of the first ``<required-var>``.  On repeated CMake runs,
 the same message will not be printed again.
 
+.. note::
+
+  If ``<PackageName>`` does not match ``CMAKE_FIND_PACKAGE_NAME`` for the
+  calling module, a warning that there is a mismatch is given. The
+  ``FPHSA_NAME_MISMATCHED`` variable may be set to bypass the warning if using
+  the old signature and the ``NAME_MISMATCHED`` argument using the new
+  signature. To avoid forcing the caller to require newer versions of CMake for
+  usage, the variable's value will be used if defined when the
+  ``NAME_MISMATCHED`` argument is not passed for the new signature (but using
+  both is an error)..
+
 Example for the full signature:
 
 .. code-block:: cmake
@@ -190,15 +208,32 @@ endmacro()
 
 function(FIND_PACKAGE_HANDLE_STANDARD_ARGS _NAME _FIRST_ARG)
 
-# Set up the arguments for `cmake_parse_arguments`.
-  set(options  CONFIG_MODE  HANDLE_COMPONENTS)
+  # Set up the arguments for `cmake_parse_arguments`.
+  set(options  CONFIG_MODE  HANDLE_COMPONENTS NAME_MISMATCHED)
   set(oneValueArgs  FAIL_MESSAGE  REASON_FAILURE_MESSAGE VERSION_VAR  FOUND_VAR)
   set(multiValueArgs REQUIRED_VARS)
 
-# Check whether we are in 'simple' or 'extended' mode:
+  # Check whether we are in 'simple' or 'extended' mode:
   set(_KEYWORDS_FOR_EXTENDED_MODE  ${options} ${oneValueArgs} ${multiValueArgs} )
   list(FIND _KEYWORDS_FOR_EXTENDED_MODE "${_FIRST_ARG}" INDEX)
 
+  unset(FPHSA_NAME_MISMATCHED_override)
+  if (DEFINED FPHSA_NAME_MISMATCHED)
+    # If the variable NAME_MISMATCHED variable is set, error if it is passed as
+    # an argument. The former is for old signatures, the latter is for new
+    # signatures.
+    list(FIND ARGN "NAME_MISMATCHED" name_mismatched_idx)
+    if (NOT name_mismatched_idx EQUAL "-1")
+      message(FATAL_ERROR
+        "The `NAME_MISMATCHED` argument may only be specified by the argument or "
+        "the variable, not both.")
+    endif ()
+
+    # But use the variable if it is not an argument to avoid forcing minimum
+    # CMake version bumps for calling modules.
+    set(FPHSA_NAME_MISMATCHED_override "${FPHSA_NAME_MISMATCHED}")
+  endif ()
+
   if(${INDEX} EQUAL -1)
     set(FPHSA_FAIL_MESSAGE ${_FIRST_ARG})
     set(FPHSA_REQUIRED_VARS ${ARGN})
@@ -227,6 +262,21 @@ function(FIND_PACKAGE_HANDLE_STANDARD_ARGS _NAME _FIRST_ARG)
     endif()
   endif()
 
+  if (DEFINED FPHSA_NAME_MISMATCHED_override)
+    set(FPHSA_NAME_MISMATCHED "${FPHSA_NAME_MISMATCHED_override}")
+  endif ()
+
+  if (DEFINED CMAKE_FIND_PACKAGE_NAME
+      AND NOT FPHSA_NAME_MISMATCHED
+      AND NOT _NAME STREQUAL CMAKE_FIND_PACKAGE_NAME)
+    message(AUTHOR_WARNING
+      "The package name passed to `find_package_handle_standard_args` "
+      "(${_NAME}) does not match the name of the calling package "
+      "(${CMAKE_FIND_PACKAGE_NAME}). This can lead to problems in calling "
+      "code that expects `find_package` result variables (e.g., `_FOUND`) "
+      "to follow a certain pattern.")
+  endif ()
+
 # now that we collected all arguments, process them
 
   if("x${FPHSA_FAIL_MESSAGE}" STREQUAL "xDEFAULT_MSG")
diff --git a/Tests/RunCMake/FPHSA/FindNameMismatch.cmake b/Tests/RunCMake/FPHSA/FindNameMismatch.cmake
new file mode 100644
index 0000000..540aa67
--- /dev/null
+++ b/Tests/RunCMake/FPHSA/FindNameMismatch.cmake
@@ -0,0 +1,4 @@
+set("${CMAKE_FIND_PACKAGE_NAME}_MODULE" "${CMAKE_CURRENT_LIST_FILE}")
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(NAMEMISMATCH REQUIRED_VARS "${CMAKE_FIND_PACKAGE_NAME}_MODULE")
+set("${CMAKE_FIND_PACKAGE_NAME}_FOUND" 1)
diff --git a/Tests/RunCMake/FPHSA/FindNameMismatchOld.cmake b/Tests/RunCMake/FPHSA/FindNameMismatchOld.cmake
new file mode 100644
index 0000000..d155ea7
--- /dev/null
+++ b/Tests/RunCMake/FPHSA/FindNameMismatchOld.cmake
@@ -0,0 +1,4 @@
+set("${CMAKE_FIND_PACKAGE_NAME}_MODULE" "${CMAKE_CURRENT_LIST_FILE}")
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(NAMEMISMATCH "old signature" "${CMAKE_FIND_PACKAGE_NAME}_MODULE")
+set("${CMAKE_FIND_PACKAGE_NAME}_FOUND" 1)
diff --git a/Tests/RunCMake/FPHSA/FindNameMismatchSuppressed.cmake b/Tests/RunCMake/FPHSA/FindNameMismatchSuppressed.cmake
new file mode 100644
index 0000000..042a59a
--- /dev/null
+++ b/Tests/RunCMake/FPHSA/FindNameMismatchSuppressed.cmake
@@ -0,0 +1,6 @@
+set("${CMAKE_FIND_PACKAGE_NAME}_MODULE" "${CMAKE_CURRENT_LIST_FILE}")
+include(FindPackageHandleStandardArgs)
+set(FPHSA_NAME_MISMATCHED 1)
+find_package_handle_standard_args(NAMEMISMATCH "old signature" "${CMAKE_FIND_PACKAGE_NAME}_MODULE")
+unset(FPHSA_NAME_MISMATCHED)
+set("${CMAKE_FIND_PACKAGE_NAME}_FOUND" 1)
diff --git a/Tests/RunCMake/FPHSA/FindNameMismatchSuppressedArg.cmake b/Tests/RunCMake/FPHSA/FindNameMismatchSuppressedArg.cmake
new file mode 100644
index 0000000..6a0e964
--- /dev/null
+++ b/Tests/RunCMake/FPHSA/FindNameMismatchSuppressedArg.cmake
@@ -0,0 +1,4 @@
+set("${CMAKE_FIND_PACKAGE_NAME}_MODULE" "${CMAKE_CURRENT_LIST_FILE}")
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(NAMEMISMATCH NAME_MISMATCHED REQUIRED_VARS "${CMAKE_FIND_PACKAGE_NAME}_MODULE")
+set("${CMAKE_FIND_PACKAGE_NAME}_FOUND" 1)
diff --git a/Tests/RunCMake/FPHSA/FindNameMismatchSuppressedCompat.cmake b/Tests/RunCMake/FPHSA/FindNameMismatchSuppressedCompat.cmake
new file mode 100644
index 0000000..791cfee
--- /dev/null
+++ b/Tests/RunCMake/FPHSA/FindNameMismatchSuppressedCompat.cmake
@@ -0,0 +1,6 @@
+set("${CMAKE_FIND_PACKAGE_NAME}_MODULE" "${CMAKE_CURRENT_LIST_FILE}")
+include(FindPackageHandleStandardArgs)
+set(FPHSA_NAME_MISMATCHED 1)
+find_package_handle_standard_args(NAMEMISMATCH REQUIRED_VARS "${CMAKE_FIND_PACKAGE_NAME}_MODULE")
+unset(FPHSA_NAME_MISMATCHED)
+set("${CMAKE_FIND_PACKAGE_NAME}_FOUND" 1)
diff --git a/Tests/RunCMake/FPHSA/NameMismatch-stderr.txt b/Tests/RunCMake/FPHSA/NameMismatch-stderr.txt
new file mode 100644
index 0000000..722b50b
--- /dev/null
+++ b/Tests/RunCMake/FPHSA/NameMismatch-stderr.txt
@@ -0,0 +1,23 @@
+CMake Warning \(dev\) at .*/Modules/FindPackageHandleStandardArgs.cmake:[0-9]+ \(message\):
+  The package name passed to `find_package_handle_standard_args`
+  \(NAMEMISMATCH\) does not match the name of the calling package
+  \(NameMismatch\).  This can lead to problems in calling code that expects
+  `find_package` result variables \(e.g., `_FOUND`\) to follow a certain
+  pattern.
+Call Stack \(most recent call first\):
+  FindNameMismatch.cmake:3 \(find_package_handle_standard_args\)
+  NameMismatch.cmake:3 \(find_package\)
+  CMakeLists.txt:3 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\) at .*/Modules/FindPackageHandleStandardArgs.cmake:[0-9]+ \(message\):
+  The package name passed to `find_package_handle_standard_args`
+  \(NAMEMISMATCH\) does not match the name of the calling package
+  \(NameMismatchOld\).  This can lead to problems in calling code that expects
+  `find_package` result variables \(e.g., `_FOUND`\) to follow a certain
+  pattern.
+Call Stack \(most recent call first\):
+  FindNameMismatchOld.cmake:3 \(find_package_handle_standard_args\)
+  NameMismatch.cmake:4 \(find_package\)
+  CMakeLists.txt:3 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/FPHSA/NameMismatch.cmake b/Tests/RunCMake/FPHSA/NameMismatch.cmake
new file mode 100644
index 0000000..9ca3cc6
--- /dev/null
+++ b/Tests/RunCMake/FPHSA/NameMismatch.cmake
@@ -0,0 +1,7 @@
+set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}")
+
+find_package(NameMismatch REQUIRED)
+find_package(NameMismatchOld REQUIRED)
+find_package(NameMismatchSuppressed REQUIRED)
+find_package(NameMismatchSuppressedCompat REQUIRED)
+find_package(NameMismatchSuppressedArg REQUIRED)
diff --git a/Tests/RunCMake/FPHSA/RunCMakeTest.cmake b/Tests/RunCMake/FPHSA/RunCMakeTest.cmake
index f3e6c3e..286915d 100644
--- a/Tests/RunCMake/FPHSA/RunCMakeTest.cmake
+++ b/Tests/RunCMake/FPHSA/RunCMakeTest.cmake
@@ -1,6 +1,7 @@
 include(RunCMake)
 
 run_cmake(BadFoundVar)
+run_cmake(NameMismatch)
 
 # The pseudo module will "find" a package with the given version. Check if the
 # version selection code in FPHSA works correctly.
-- 
cgit v0.12