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.
This commit is contained in:
Eugene Shalygin 2021-07-07 14:41:34 +02:00 committed by Brad King
parent d49b507bb6
commit a2e9fe38e4
15 changed files with 99 additions and 6 deletions

View File

@ -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

View File

@ -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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -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`:

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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;
}

View File

@ -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\.

View File

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

View File

@ -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\)

View File

@ -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.")

View File

@ -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)