Following documentation regarding CMake package creation, it is stated that:
The find_dependency macro also sets ClimbingStats_FOUND to False if the dependency is not found, along with a diagnostic that the ClimbingStats package can not be used without the Stats package.
Yet, we are observing that this is only the case if ClimbingStats is requested from downstream without the REQUIRED argument.
Not required
find_package(ClimbingStats CONFIG)
Then we get the behaviour described above, i.e.:
Found package configuration file:
...
but it set ClimbingStats_FOUND to FALSE so the package "ClimbingStats" is considered to be NOT FOUND.
Reason given by package:
ClimbingStats could not be found because dependency Stats could not be found.
Required
If we make the ClimbingStats package required instead:
find_package(ClimbingStats CONFIG REQUIRED)
Then the error does not mention ClimbingStats at all
Could not find a package configuration file provided by "Stats" with any of
the following names:
StatsConfig.cmake
stats-config.cmake
Add the installation prefix of "Stats" to CMAKE_PREFIX_PATH or set
"Stats_DIR" to a directory containing one of the above files. If "Stats"
provides a separate development package or SDK, be sure it has been
installed.
Are we misunderstanding the documentation linked above, or is this a bug in CMake behaviour?
Is there a way to require a package and still get the descriptive error message (like in the first example) when its upstreams are not found?
Are we misunderstanding the documentation linked above, or is this a bug in CMake behavior?
Looks like a bug in CMake documentation.
The documentation for find_dependency states:
find_dependency forwards the correct parameters for QUIET and REQUIRED which were passed to the original find_package() call.
So, when outer find_package() was called with REQUIRED keyword, inner find_package() is called with that keyword too.
Exactly this behavior is observed in your case: the error message is generated by call
find_package(stats REQUIRED)
and the caller code has no chance to process its FALSE result for make the error message more descriptive.
Is there a way to require a package and still get the descriptive error message (like in the first example) when its upstreams are not found?
As far as I understand, more "native" behavior of find_dependency would be to not forward REQUIRED keyword to the inner find_package, but check the result of that find_package manually, and emit appropriate message if it is FALSE.
You may submit feature request to CMake tracker about that.
Related
The CMake find_package() command sometimes prints a message, indication that the package has been found, and possibly the found version.
What are the condition(s) for this message being printed, other than QUIET not being used?
Note: Asking about CMake 3.22 in case it matters.
I've tested this with a few configurations and made the following observations:
The version script and the package configuration script or the find script can ignore quiet and print as much as they like; the presence of the QUIET option seems more like a suggestion than something enforced by cmake.
CMake itself seems to print the version number of unsuitable packages automatically, if no suitable package is found and
REQUIRED is specified, or
QUIET isn't specified
If a suitable version of the package is found, nothing is printed, regardless of whether REQUIRED and/or QUIET are present.
(Tested with the Windows version of CMake 3.22.1. The "basic signature" was used in all tests.)
when using
find_package(OpenSSL MODULE REQUIRED)
I got the following output
-- Found OpenSSL: /usr/local/lib/libcrypto.so (found version "2.0.0")
but when i use
find_package(OpenSSL REQUIRED PATHS /usr/local/lib/libcrypto.so)
I am getting an error.
Could not find a package configuration file provided by "OpenSSL" with any of the following names: OpenSSLConfig.cmake openssl-config.cmake
can anyone explain why this is happening and also is it the coorect way of using the find_package in the config mode.
You are essentially using 2 different behaviors.
# This way is using Basic Signature and Module Mode
# https://cmake.org/cmake/help/latest/command/find_package.html?highlight=find_package#basic-signature-and-module-mode
find_package(OpenSSL MODULE REQUIRED)
"
The command has two modes by which it searches for packages: "Module" mode and "Config" mode. The above signature selects Module mode. If no module is found the command falls back to Config mode, described below. This fall back is disabled if the MODULE option is given.
In Module mode, CMake searches for a file called Find.cmake. The file is first searched in the CMAKE_MODULE_PATH, then among the Find Modules provided by the CMake installation. If the file is found, it is read and processed by CMake. It is responsible 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.
"
So essentially in the first one you are using the FindOpenSLL cmake module.
"https://cmake.org/cmake/help/latest/module/FindOpenSSL.html?highlight=openssl"
Where as in the second one you are saying to not use the module CMake has written for you. So of course it is confused as to where to look for.
As stated in the documentation for CMake 3.0, find_package(MPI) and FindMPI seem to be interchangeable? But my questions are:
What are the differences between these two?
Is that also the case for the latest version?
Also, does FindMPI provide anything similar to the option REQUIRED in find_package()?
Because FindMPI is one of the Find Modules provided by the CMake installation, the find_package(MPI) and include(FindMPI) calls are essentially equivalent. (The include() is required here to load the module; simply writing FindMPI in a CMake file will result in an error.)
The find_package() command has two modes: MODULE and CONFIG. The default is MODULE mode, and from the find_package() documentation:
In Module mode, CMake searches for a file called Find<PackageName>.cmake. The file is first searched in the CMAKE_MODULE_PATH, then among the Find Modules provided by the CMake installation.
Therefore, find_package(MPI) will search for a file called FindMPI.cmake, which is equivalent to the command include(FindMPI). This holds up, unless you have another FindMPI.cmake file defined in your CMAKE_MODULE_PATH.
While they essentially equivalent commands, calling find_package() is typically more useful, as it lets you pass arguments, such as REQUIRED, to further specify how the settings of the external project are loaded.
I seem to figure it out. find_package() and FindMPI are two different things.
While find_package() is a CMake scripting command, FindMPI is a CMake Module.
As stated in the documentation of find_package(), one can select the "Module" mode by which it searches for packages. That means, when one calls find_package(MPI), it will make use of the FindMPI module (written in the FindMPI.cmake file) to search for the MPI library.
Similar things when you try to find other packages, all of which are listed here.
In package-Config.cmake, if I do find_package instead of find_dependency, what would be the effect on my application? In what scenarios it will break?
According to documentation, find_dependency is just a wrapper around find_package:
It is designed to be used in a Package Configuration File (<package>Config.cmake). find_dependency forwards the correct parameters for QUIET and REQUIRED which were passed to the original find_package() call. Any additional arguments specified are forwarded to find_package().
If the dependency could not be found it sets an informative diagnostic message and calls return() to end processing of the calling package configuration file and return to the find_package() command that loaded it.
If you would use, e.g., find_package(REQUIRED) instead of find_dependency, and the package won't be found, then it will be difficult for user to understand, that the error is related with outer package, not only with the inner one. Also, if outer call of find_package doesn't use REQUIRE option, it is inconsistent to perform inner call with that option.
We have a CMakeLists.txt that links (for instance) opencv to our various binaries. This is done as follow:
find_package(OpenCV REQUIRED core imgproc highgui contrib)
target_link_library(XXX opencv_core)
We also would like to allow the person building the library to provide its own opencv library. It seems that this could be done setting -DCMAKE_PREFIX_PATH to the right path.
cmake -DCMAKE_PREFIX_PATH=".../mybuild/include;.../mybuild/lib" .
However I would like to be sure the library used is exactly the one specified by the client (i.e. if there is nothing in /mybuild/lib the configuration fails).
How can I allow somebody building the library to override the library used ? (if nothing is specified it should fall back to find_package-s)
In short
If the package provides <package>Config.cmake script, user may specify <package>_DIR CMake variable for locate this script.
Searching other places in that case may be disabled with NO_DEFAULT_PATH option for find_package().
If a package is searched with Find<package>.cmake script, there is no (generic) way for disable searching other places if user provides hint variable but it is wrong.
Explanations
Firstly, CMAKE_PREFIX_PATH provides additional installation tree for all packages. As this variable is applied to all find_package() calls, it is not wise to require all packages to be found under it.
When talk about the ways for specify installation directory for specific package, we need to distinguish two kinds of "find" scripts:
<package>Config.cmake (or some alternative names, see find_package documentation).
These scripts are shipped with the packages themselves. And there is universal way for user to specify location of such packages: CMake variable <package>_DIR, which should point to the directory with *Config.cmake script.
While default behaviour of find_package() is treating <package>_DIR variable as an additional hint, passing NO_DEFAULT_PATH option disables all implicit paths:
if(<package>_DIR) # Variable is set by the user or by previous `cmake` run.
# Search only under given directory.
find_package(<package> NO_DEFAULT_PATH)
else()
# Search everywhere (as documented for 'find_package').
find_package(<package>)
endif()
Find<package>.cmake.
This script either is shipped with CMake or should be shipped with the project.
Most of such scripts allows to hint about package location with variable (CMake or environment one) like <package>_DIR, <package>_ROOT or so.
However, almost all such scripts treat hint variable only as additional search place, so if variable is set to wrong value, they simply ignore it. And without modifying the script you cannot change that behavior.