Difference between find_dependency and find_package in cmake? - cmake

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.

Related

What's the difference between find_package(MPI) and FindMPI?

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.

CMake find_dependency does not respect the behaviour in the package documentation?

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.

Where should find_package be called in CMakeLists.txt?

Including external libraries in a cmake project is usually performed using find_package().
But in a large multi-application / multi-library project, it is typical for some 3rd-party and/or system libraries to be used by multiple applications and libraries.
Where should find_package() for these common libraries be called?
In the CMakeLists.txt file for each executable/library that needs them?
Or, once in a top-level CMakeLists.txt file?
The first options seems to be a more modular approach, but the associated find_package() scripts are executed for each library/executable that uses them. This can slow down the configuration step.
The second option is more efficient, but looks a bit too much like a global variable to me.
I would distinguish between subprojects/-directories that might be standalone packages on their own (or already are) and those that exclusively reflect the source code structure.
In the first case, find_package should clearly be in the subdirectory CMakeLists.txt to allow for extracting the subdirectory for a standalone build or distribution. Inefficiency of the configuration step should not be a concern here, as the configuration of a build is not performed that often.
In the second case, I would prefer find_package calls in the toplevel CMakeLists.txt. If you don't use the variables set by these calls to populate global variables but use target_link_libraries instead (as suggested in the comments), this should be clean enough. Sometimes though, found packages do not export proper targets to link against, but rather define some include path variables and the like. Then, it should be easy to define your own INTERFACE library that encapsulate these flags/paths as usage requirements and propagate them transitively when linked via target_link_libraries again.

Cmake override find_package for a given target

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.

what is the difference between find_package and pkg_search_module

All I can say about those commands right now is pkg_search_module has some odd usage, where I have to write the dependency that I would like to have two times. instead of just once like in find_package
pkg_search_module(ZLIB REQUIRED zlib)
#seriously two times zlib and once in only capital caseā€½
find_package(ZLIB)
#much cleaner but for some odd reason find_package does not work all the time
pkg_search_module uses the pkg-config tool to determine the location of the requested library. This is mostly useful on systems where pkg-config is already in use, so you do not need to replicate all the information for CMake. Note that this approach has potential portability issues, since it requires pkg-config to be setup correctly on the build machine.
find_package on the other hand is CMake's very own mechanism for solving the same problem. For this to work you either need a CMake find script for the requested library (CMake already ships with a couple of those, but you can easily write your own) or alternatively a package config script provided by the requested library itself. In either case you might have to adjust your CMAKE_MODULE_PATH for CMake to be able to find the respective script.
The choice which one to use is quite simple:
Always prefer package config scripts, if provided by the library.
Use find scripts as a fallback for libraries that are not aware of CMake.
Use pkg_search_module if you want to exploit synergies with pkg-config. In particular, note that it is possible to implement a find script using pkg_search_module as one method of obtaining the required information.