Bladeren bron

find_package: Add option to prefer Config mode

Add a `CMAKE_FIND_PACKAGE_PREFER_CONFIG` variable to tell
`find_package` calls to look for a package configuration file
first even if a find module is available.

Fixes: #16805, #19236
Cristian Adam 6 jaren geleden
bovenliggende
commit
26a99da206

+ 7 - 0
Help/command/find_package.rst

@@ -59,6 +59,13 @@ for finding the package, checking the version, and producing any needed
 messages.  Some find-modules provide limited or no support for versioning;
 check the module documentation.
 
+If the ``MODULE`` option is not specfied in the above signature,
+CMake first searches for the package using Module mode. Then, if the
+package is not found, it searches again using Config mode. A user
+may set the variable :variable:`CMAKE_FIND_PACKAGE_PREFER_CONFIG` to
+``TRUE`` to direct CMake first search using Config mode before falling
+back to Module mode.
+
 Full Signature and Config Mode
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 

+ 1 - 0
Help/manual/cmake-variables.7.rst

@@ -172,6 +172,7 @@ Variables that Change Behavior
    /variable/CMAKE_FIND_NO_INSTALL_PREFIX
    /variable/CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY
    /variable/CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY
+   /variable/CMAKE_FIND_PACKAGE_PREFER_CONFIG
    /variable/CMAKE_FIND_PACKAGE_RESOLVE_SYMLINKS
    /variable/CMAKE_FIND_PACKAGE_WARN_NO_MODULE
    /variable/CMAKE_FIND_ROOT_PATH

+ 6 - 0
Help/release/dev/find-package-prefer-config.rst

@@ -0,0 +1,6 @@
+find-package-prefer-config
+--------------------------
+
+* Variable :variable:`CMAKE_FIND_PACKAGE_PREFER_CONFIG` was added to tell
+  :command:`find_package` calls to look for a package configuration
+  file first even if a find module is available.

+ 27 - 0
Help/variable/CMAKE_FIND_PACKAGE_PREFER_CONFIG.rst

@@ -0,0 +1,27 @@
+CMAKE_FIND_PACKAGE_PREFER_CONFIG
+---------------------------------
+
+Tell :command:`find_package` to try "Config" mode before "Module" mode if no
+mode was specified.
+
+The command :command:`find_package` operates without an explicit mode when
+the reduced signature is used without the ``MODULE`` option. In this case,
+by default, CMake first tries Module mode by searching for a
+``Find<pkg>.cmake`` module.  If it fails, CMake then searches for the package
+using Config mode.
+
+Set ``CMAKE_FIND_PACKAGE_PREFER_CONFIG`` to ``TRUE`` to tell
+:command:`find_package` to first search using Config mode before falling back
+to Module mode.
+
+This variable may be useful when a developer has compiled a custom version of
+a common library and wishes to link it to a dependent project.  If this
+variable is set to ``TRUE``, it would prevent a dependent project's call
+to :command:`find_package` from selecting the default library located by the
+system's ``Find<pkg>.cmake`` module before finding the developer's custom
+built library.
+
+Once this variable is set, it is the responsibility of the exported
+``<pkg>Config.cmake`` files to provide the same result variables as the
+``Find<pkg>.cmake`` modules so that dependent projects can use them
+interchangeably.

+ 3 - 0
Help/variable/CMAKE_FIND_PACKAGE_WARN_NO_MODULE.rst

@@ -17,3 +17,6 @@ Set ``CMAKE_FIND_PACKAGE_WARN_NO_MODULE`` to ``TRUE`` to tell
 :command:`find_package` to warn when it implicitly assumes Config mode.  This
 helps developers enforce use of an explicit mode in all calls to
 :command:`find_package` within a project.
+
+This variable has no effect if :variable:`CMAKE_FIND_PACKAGE_PREFER_CONFIG` is
+set to ``TRUE``.

+ 53 - 42
Source/cmFindPackageCommand.cxx

@@ -499,50 +499,61 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args,
 
   // See if there is a Find<PackageName>.cmake module.
   bool loadedPackage = false;
-  if (this->UseFindModules && this->FindPackageUsingModuleMode()) {
-    loadedPackage = true;
-  } else {
-    // Handle CMAKE_FIND_PACKAGE_WARN_NO_MODULE (warn when CONFIG mode is
-    // implicitly assumed)
-    if (this->UseFindModules && this->UseConfigFiles &&
-        this->Makefile->IsOn("CMAKE_FIND_PACKAGE_WARN_NO_MODULE")) {
-      std::ostringstream aw;
-      if (this->RequiredCMakeVersion >= CMake_VERSION_ENCODE(2, 8, 8)) {
-        aw << "find_package called without either MODULE or CONFIG option and "
-              "no Find"
-           << this->Name
-           << ".cmake module is in CMAKE_MODULE_PATH.  "
-              "Add MODULE to exclusively request Module mode and fail if "
-              "Find"
-           << this->Name
-           << ".cmake is missing.  "
-              "Add CONFIG to exclusively request Config mode and search for a "
-              "package configuration file provided by "
-           << this->Name << " (" << this->Name << "Config.cmake or "
-           << cmSystemTools::LowerCase(this->Name) << "-config.cmake).  ";
-      } else {
-        aw << "find_package called without NO_MODULE option and no "
-              "Find"
-           << this->Name
-           << ".cmake module is in CMAKE_MODULE_PATH.  "
-              "Add NO_MODULE to exclusively request Config mode and search "
-              "for a "
-              "package configuration file provided by "
-           << this->Name << " (" << this->Name << "Config.cmake or "
-           << cmSystemTools::LowerCase(this->Name)
-           << "-config.cmake).  "
-              "Otherwise make Find"
-           << this->Name
-           << ".cmake available in "
-              "CMAKE_MODULE_PATH.";
-      }
-      aw << "\n"
-            "(Variable CMAKE_FIND_PACKAGE_WARN_NO_MODULE enabled this "
-            "warning.)";
-      this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, aw.str());
+  if (this->Makefile->IsOn("CMAKE_FIND_PACKAGE_PREFER_CONFIG")) {
+    if (this->UseConfigFiles && this->FindPackageUsingConfigMode()) {
+      loadedPackage = true;
+    } else if (this->FindPackageUsingModuleMode()) {
+      loadedPackage = true;
     }
-    if (this->FindPackageUsingConfigMode()) {
+  } else {
+    if (this->UseFindModules && this->FindPackageUsingModuleMode()) {
       loadedPackage = true;
+    } else {
+      // Handle CMAKE_FIND_PACKAGE_WARN_NO_MODULE (warn when CONFIG mode is
+      // implicitly assumed)
+      if (this->UseFindModules && this->UseConfigFiles &&
+          this->Makefile->IsOn("CMAKE_FIND_PACKAGE_WARN_NO_MODULE")) {
+        std::ostringstream aw;
+        if (this->RequiredCMakeVersion >= CMake_VERSION_ENCODE(2, 8, 8)) {
+          aw << "find_package called without either MODULE or CONFIG option "
+                "and "
+                "no Find"
+             << this->Name
+             << ".cmake module is in CMAKE_MODULE_PATH.  "
+                "Add MODULE to exclusively request Module mode and fail if "
+                "Find"
+             << this->Name
+             << ".cmake is missing.  "
+                "Add CONFIG to exclusively request Config mode and search for "
+                "a "
+                "package configuration file provided by "
+             << this->Name << " (" << this->Name << "Config.cmake or "
+             << cmSystemTools::LowerCase(this->Name) << "-config.cmake).  ";
+        } else {
+          aw << "find_package called without NO_MODULE option and no "
+                "Find"
+             << this->Name
+             << ".cmake module is in CMAKE_MODULE_PATH.  "
+                "Add NO_MODULE to exclusively request Config mode and search "
+                "for a "
+                "package configuration file provided by "
+             << this->Name << " (" << this->Name << "Config.cmake or "
+             << cmSystemTools::LowerCase(this->Name)
+             << "-config.cmake).  "
+                "Otherwise make Find"
+             << this->Name
+             << ".cmake available in "
+                "CMAKE_MODULE_PATH.";
+        }
+        aw << "\n"
+              "(Variable CMAKE_FIND_PACKAGE_WARN_NO_MODULE enabled this "
+              "warning.)";
+        this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, aw.str());
+      }
+
+      if (this->FindPackageUsingConfigMode()) {
+        loadedPackage = true;
+      }
     }
   }
 

+ 35 - 1
Tests/FindPackageTest/CMakeLists.txt

@@ -541,7 +541,41 @@ endif()
 set(SortLib_DIR "" CACHE FILEPATH "Wipe out find results for testing." FORCE)
 unset(SortLib_VERSION)
 
-
 unset(CMAKE_FIND_PACKAGE_SORT_ORDER)
 unset(CMAKE_FIND_PACKAGE_SORT_DIRECTION)
 set(CMAKE_PREFIX_PATH )
+
+############################################################################
+##Test FIND_PACKAGE CMAKE_FIND_PACKAGE_PREFER_CONFIG
+
+set(CMAKE_PREFIX_PATH ${CMAKE_CURRENT_SOURCE_DIR}/PreferConfig)
+set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/PreferConfig)
+
+# prefer module mode
+set(CMAKE_FIND_PACKAGE_PREFER_CONFIG OFF)
+unset(ABC_FOUND)
+unset(ABC_CONFIG)
+
+find_package(ABC)
+if(NOT ABC_FOUND)
+    message(SEND_ERROR "Did not find ABC package")
+endif()
+if(ABC_CONFIG)
+    message(SEND_ERROR "Incorrectly found ABC in CONFIG mode, expected to find it with MODULE mode")
+endif()
+
+# Now prefer config mode
+set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON)
+unset(ABC_FOUND)
+unset(ABC_CONFIG)
+
+find_package(ABC)
+if(NOT ABC_FOUND)
+    message(SEND_ERROR "Did not find ABC package")
+endif()
+if(NOT ABC_CONFIG)
+    message(SEND_ERROR "Incorrectly found ABC in MODULE mode, expected to find it with CONFIG mode")
+endif()
+
+set(CMAKE_FIND_PACKAGE_PREFER_CONFIG OFF)
+set(CMAKE_PREFIX_PATH)

+ 1 - 0
Tests/FindPackageTest/PreferConfig/ABCConfig.cmake

@@ -0,0 +1 @@
+set(ABC_FOUND TRUE)

+ 1 - 0
Tests/FindPackageTest/PreferConfig/FindABC.cmake

@@ -0,0 +1 @@
+set(ABC_FOUND TRUE)