Explorar o código

find_package: Add variable to make package REQUIRED

Add a `CMAKE_REQUIRE_FIND_PACKAGE_<PackageName>` variable is complement
to `CMAKE_DISABLE_FIND_PACKAGE_<PackageName>` with just the opposite
behaviour: it turns non-required find_package call into the required one.

While optional package dependencies usually result in simple and clean
build logic, sometimes people want to be sure those optional
dependencies will be found and used. Examples are reproducible builds
and build instructions for 3rd parties. People choose to make
find_package calls REQUIRED and put them behind an option(). Such
workarounds blend build logic with build environment management and do
not look elegant.
Eugene Shalygin %!s(int64=4) %!d(string=hai) anos
pai
achega
a2e9fe38e4

+ 1 - 0
Auxiliary/vim/syntax/cmake.vim

@@ -2807,6 +2807,7 @@ syn keyword cmakeKWfind_package contained
             \ ABI
             \ BUNDLE
             \ CMAKE_DISABLE_FIND_PACKAGE_
+            \ CMAKE_REQUIRE_FIND_PACKAGE_
             \ CMAKE_FIND_ROOT_PATH_BOTH
             \ COMPONENTS
             \ CONFIG

+ 9 - 2
Help/command/find_package.rst

@@ -448,8 +448,15 @@ which the file is found.  The :variable:`CMAKE_FIND_PACKAGE_RESOLVE_SYMLINKS`
 variable may be set to ``TRUE`` before calling ``find_package`` in order
 to resolve symbolic links and store the real path to the file.
 
-Every non-REQUIRED ``find_package`` call can be disabled by setting the
-:variable:`CMAKE_DISABLE_FIND_PACKAGE_<PackageName>` variable to ``TRUE``.
+Every non-REQUIRED ``find_package`` call can be disabled or made REQUIRED:
+
+* Setting the :variable:`CMAKE_DISABLE_FIND_PACKAGE_<PackageName>` variable
+  to ``TRUE`` disables the package.
+
+* Setting the :variable:`CMAKE_REQUIRE_FIND_PACKAGE_<PackageName>` variable
+  to ``TRUE`` makes the package REQUIRED.
+
+Setting both variables to ``TRUE`` simultaneously is an error.
 
 Package File Interface Variables
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

+ 3 - 1
Help/manual/cmake-packages.7.rst

@@ -74,7 +74,9 @@ package.
 
 By setting the :variable:`CMAKE_DISABLE_FIND_PACKAGE_<PackageName>` variable to
 ``TRUE``, the ``<PackageName>`` package will not be searched, and will always
-be ``NOTFOUND``.
+be ``NOTFOUND``. Likewise, setting the
+:variable:`CMAKE_REQUIRE_FIND_PACKAGE_<PackageName>` to ``TRUE`` will make the
+package REQUIRED.
 
 .. _`Config File Packages`:
 

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

@@ -230,6 +230,7 @@ Variables that Change Behavior
    /variable/CMAKE_PROJECT_INCLUDE_BEFORE
    /variable/CMAKE_PROJECT_PROJECT-NAME_INCLUDE
    /variable/CMAKE_PROJECT_PROJECT-NAME_INCLUDE_BEFORE
+   /variable/CMAKE_REQUIRE_FIND_PACKAGE_PackageName
    /variable/CMAKE_SKIP_INSTALL_ALL_DEPENDENCY
    /variable/CMAKE_STAGING_PREFIX
    /variable/CMAKE_SUBLIME_TEXT_2_ENV_SETTINGS

+ 5 - 0
Help/release/dev/find_package-required-var.rst

@@ -0,0 +1,5 @@
+find_package-required-var
+-------------------------
+
+* The :variable:`CMAKE_REQUIRE_FIND_PACKAGE_<PackageName>` variable was added
+  to turn a non-REQUIRED :command:`find_package` call into a REQUIRED one.

+ 2 - 0
Help/variable/CMAKE_DISABLE_FIND_PACKAGE_PackageName.rst

@@ -14,3 +14,5 @@ the package has already been found in a previous CMake run, the
 variables which have been stored in the cache will still be there.  In
 that case it is recommended to remove the cache variables for this
 package from the cache using the cache editor or :manual:`cmake(1)` ``-U``
+
+See also the :variable:`CMAKE_REQUIRE_FIND_PACKAGE_<PackageName>` variable.

+ 14 - 0
Help/variable/CMAKE_REQUIRE_FIND_PACKAGE_PackageName.rst

@@ -0,0 +1,14 @@
+CMAKE_REQUIRE_FIND_PACKAGE_<PackageName>
+----------------------------------------
+
+.. versionadded:: 3.22
+
+Variable for making :command:`find_package` call ``REQUIRED``.
+
+Every non-``REQUIRED`` :command:`find_package` call in a project can be
+turned into ``REQUIRED`` by setting the variable
+``CMAKE_REQUIRE_FIND_PACKAGE_<PackageName>`` to ``TRUE``.
+This can be used to assert assumptions about build environment and to
+ensure the build will fail early if they do not hold.
+
+See also the :variable:`CMAKE_DISABLE_FIND_PACKAGE_<PackageName>` variable.

+ 21 - 3
Source/cmFindPackageCommand.cxx

@@ -478,17 +478,35 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
       this->VersionMaxPatch, this->VersionMaxTweak);
   }
 
+  const std::string makePackageRequiredVar =
+    cmStrCat("CMAKE_REQUIRE_FIND_PACKAGE_", this->Name);
+  const bool makePackageRequiredSet =
+    this->Makefile->IsOn(makePackageRequiredVar);
+  if (makePackageRequiredSet) {
+    if (this->Required) {
+      this->Makefile->IssueMessage(
+        MessageType::WARNING,
+        cmStrCat("for module ", this->Name,
+                 " already called with REQUIRED, thus ",
+                 makePackageRequiredVar, " has no effect."));
+    } else {
+      this->Required = true;
+    }
+  }
+
   std::string disableFindPackageVar =
     cmStrCat("CMAKE_DISABLE_FIND_PACKAGE_", this->Name);
   if (this->Makefile->IsOn(disableFindPackageVar)) {
     if (this->Required) {
       this->SetError(
-        cmStrCat("for module ", this->Name, " called with REQUIRED, but ",
-                 disableFindPackageVar,
+        cmStrCat("for module ", this->Name,
+                 (makePackageRequiredSet
+                    ? " was made REQUIRED with " + makePackageRequiredVar
+                    : " called with REQUIRED, "),
+                 " but ", disableFindPackageVar,
                  " is enabled. A REQUIRED package cannot be disabled."));
       return false;
     }
-
     return true;
   }
 

+ 1 - 0
Tests/RunCMake/find_package/MissingNormalForceRequired-result.txt

@@ -0,0 +1 @@
+1

+ 20 - 0
Tests/RunCMake/find_package/MissingNormalForceRequired-stderr.txt

@@ -0,0 +1,20 @@
+CMake Error at MissingNormalForceRequired.cmake:2 \(find_package\):
+  No "FindNotHere.cmake" found in CMAKE_MODULE_PATH\.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Warning \(dev\) at MissingNormalForceRequired.cmake:2 \(find_package\):
+  FindNotHere.cmake must either be part of this project itself, in this case
+  adjust CMAKE_MODULE_PATH so that it points to the correct location inside
+  its source tree\.
+
+  Or it must be installed by a package which has already been found via
+  find_package\(\)\.  In this case make sure that package has indeed been found
+  and adjust CMAKE_MODULE_PATH to contain the location where that package has
+  installed FindNotHere\.cmake\.  This must be a location provided by that
+  package.  This error in general means that the buildsystem of this project
+  is relying on a Find-module without ensuring that it is actually available\.
+
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it\.

+ 3 - 0
Tests/RunCMake/find_package/MissingNormalForceRequired.cmake

@@ -0,0 +1,3 @@
+set(CMAKE_REQUIRE_FIND_PACKAGE_NotHere ON)
+find_package(NotHere MODULE)
+message(FATAL_ERROR "This error must not be reachable.")

+ 1 - 0
Tests/RunCMake/find_package/RequiredOptionValuesClash-result.txt

@@ -0,0 +1 @@
+1

+ 11 - 0
Tests/RunCMake/find_package/RequiredOptionValuesClash-stderr.txt

@@ -0,0 +1,11 @@
+CMake Error at RequiredOptionValuesClash.cmake:4 \(find_package\):
+  find_package for module Foo was made REQUIRED with
+  CMAKE_REQUIRE_FIND_PACKAGE_Foo but CMAKE_DISABLE_FIND_PACKAGE_Foo is
+  enabled.  A REQUIRED package cannot be disabled.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at RequiredOptionValuesClash.cmake:5 \(message\):
+  This error must not be reachable\.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)

+ 5 - 0
Tests/RunCMake/find_package/RequiredOptionValuesClash.cmake

@@ -0,0 +1,5 @@
+set(CMAKE_DISABLE_FIND_PACKAGE_Foo ON)
+set(CMAKE_REQUIRE_FIND_PACKAGE_Foo ON)
+
+find_package(Foo)
+message(FATAL_ERROR "This error must not be reachable.")

+ 2 - 0
Tests/RunCMake/find_package/RunCMakeTest.cmake

@@ -6,6 +6,7 @@ run_cmake(ComponentRequiredAndOptional)
 run_cmake(FromPATHEnv)
 run_cmake(FromPrefixPath)
 run_cmake(MissingNormal)
+run_cmake(MissingNormalForceRequired)
 run_cmake(MissingNormalRequired)
 run_cmake(MissingNormalVersion)
 run_cmake(MissingNormalWarnNoModuleOld)
@@ -23,6 +24,7 @@ run_cmake(PackageRootNestedConfig)
 run_cmake(PackageRootNestedModule)
 run_cmake(PolicyPush)
 run_cmake(PolicyPop)
+run_cmake(RequiredOptionValuesClash)
 run_cmake(SetFoundFALSE)
 run_cmake(WrongVersion)
 run_cmake(WrongVersionConfig)