Cmake override find_package for a given target - cmake

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.

Related

Can I use any fuzzy search tricks in CMake find_library?

My motivation comes from this:
I want to find a library named libboost_python38.so, if I use find_library command like this, I cannot find the library:
find_library(USD_BOOST_PYTHON boost_python HINTS ${USD_LIBRARY_DIRECTORY})
Only when I use this command, the library can be found:
find_library(USD_BOOST_PYTHON boost_python38 HINTS ${USD_LIBRARY_DIRECTORY})
This method will make my cmake file lose some cross platform capabilities, so I hope to search this library by some fuzzy search method. Can I do this in a better way?
No particularly, but you can use the NAMES parameter, and pass a few candid names that find_library should look for, see here. You can also provide a few different PATH and PATH_SUFFIXES which could help.
If you still need more logic to find your library, then you most likely need to deal with them before your find_library call, e.g., conclude the name of your library beforehand, put it in a variable, and pass that as the name of the library to the find_library.
Don't use find_library for this purpose. Instead make use of the fact that boost comes with cmake configuration scripts and use find_package.
find_package(Boost REQUIRED COMPONENTS python)
target_link_libraries(my_target PRIVATE Boost::python)
This has the added benefit of any dependencies being added to my_target automatically alongside the necessary include directories.
If you've installed the package to the default location, no additional info should be required. If you've built boost yourself and installed it to a non-standard location, you may need to provide info about the install location, e.g. by adding the install location to CMAKE_PREFIX_PATH.
Note: find_package has additional parameters that allow you to restrict the acceptable boost version if desired.

Is there anyway to find library path dynamically in CMake?

I have libcurl.so in three different paths. lets say /usr/lib , /opt/a/.../lib/, /opt/b/.../lib/
I want to link proper library while building. How to write CMakeLists.txt to do that?
As of now I have hard coded to find it in /usr/lib/
project (mylib)
find_library(LIB_CURL_LIBRARY NAMES curl HINTS "/usr/lib/")
target_link_libraries (mylib curl)
Command find_library actually searches many places, which can also be adjusted by a user (the person, who uses the CMake project with find_library call, but do not modifies CMakeLists.txt).
Also, via HINTS and PATHS you (as the project's developer) may add more hints to search, and these hints could also be made modifiable by a user.
You may find complete description about search paths in documentation.
When decide how to make find_library to search in the specific path, you need to "classify" origin that path. Some common cases:
Is the path a standard one for specific OS or distro? If so, CMake usually searches this path by default.
Does the path come from the custom installation of the package? If so, the user could assign installation prefix to some variable (e.g. XXX_ROOT, where XXX is a package name or abbreviation), which is used as PATHS or HINTS in your find_library call.
Does the path come from the custom installation prefix, common for many packages? If so, the user could assign that common prefix to CMAKE_PREFIX_PATH variable, and find_library automatically will take this prefix into account.
Note, that find_library is usually used in a FindXXX.cmake module (which is activated via find_package(XXX)). Such module could include additional logic for find additional possible library locations according to the system introspection.

Best practices with CMake with non-standard include and library directories

I have been trying to build Mozilla RR on a Linux box at work using CMake. We have a slightly eccentric arrangement where shared libraries are stored on network drives in locations like /sw/external/product-name/linux64_g63.dll/. Further, I have built some dependencies for the project in $HOME/sw/. (I am not a sudoer on this box.)
I am rather baffled as how I am supposed to communicate to CMake to look in non-standard directories. So far I have fudged:
PKG_CONFIG_PATH=$HOME/sw/capnproto-0.6.1/lib/pkconfig \
CC=gcc-6.3 CXX=g++-6.3 \
cmake \
-DCMAKE_INSTALL_PREFIX=$HOME/sw/rr-5.1.0 \
-DPYTHON_EXECUTABLE=$HOME/bin/python2 \
-DCMAKE_FIND_ROOT_PATH=$HOME/sw/libseccomp-2.2.3/ \
../src/
Which is obviously not a scalable solution, but it does at least complete the configuration successfully and emit some Makefiles.
If I omit -DCMAKE_FIND_ROOT_PATH=$HOME/sw/libseccomp-2.2.3/, CMake fails, complaining about a missing libseccomp-2.2.3 dependency. But it works if I do have that definition, telling me that CMake understands where the libseccomp-2.2.3 files are and so will properly add the paths to the necessary compiler invocations.
However, make does not succeed, because gcc fails to find a required header file from the libseccomp probject. Examining make VERBOSE=1, I find that CMake hasn't added -I$HOME/sw/libseccomp-2.2.3/include to the gcc invocation.
I feel like this is not the right approach. The other answers I have looked at tell me to modify the CMakeLists.txt file, but surely
that is not going to be scalable across multiple CMake projects, and
for each project, that will need me to maintain a separate CMakeLists.txt file for every platform (Solaris/Linux/Darwin/Cygwin) I build the software on.
Is there a canonical solution to solving this problem? Perhaps a per-site configuration file that will tell CMake how to find libraries and headers, for all projects I build on that site?
Your approach is correct, but cmake is never told to include SECCOMP - see end of this post.
The way cmake can be informed about custom dependency directory depends on how the dependency is searched (i.e. on what is written in CMakeLists.txt).
find_package/find_library/find_path/find_program
If dependency is found with one of above-mentioned commands, custom search directories can be easily added with CMAKE_PREFIX_PATH. There is no need to add full path to include, lib or bin - when package root is added find_-command will check appropriate sub-directories. CMAKE_PREFIX_PATH can be also set with environment variable.
Second option is CMAKE_FIND_ROOT_PATH. Every path added to CMAKE_FIND_ROOT_PATH list treated as separate root directory and is searched before system root directory.
Note that CMAKE_FIND_ROOT_PATH will be ignored by find_-commands with NO_CMAKE_FIND_ROOT_PATH argument.
Following four variables may be used to tune the usage of CMAKE_FIND_ROOT_PATH:
CMAKE_FIND_ROOT_PATH_MODE_PACKAGE
CMAKE_FIND_ROOT_PATH_MODE_INCLUDE
CMAKE_FIND_ROOT_PATH_MODE_LIBRARY
CMAKE_FIND_ROOT_PATH_MODE_PROGRAM
When use of host system default libraries is undesired setting CMAKE_FIND_ROOT_PATH_MODE_INCLUDE and CMAKE_FIND_ROOT_PATH_MODE_LIBRARY to ONLY is a good practice. If dependency library or header is not found in CMAKE_FIND_ROOT_PATH the configuration will fail. If cmake is allowed search system paths too, it is most likely that errors will occur during linking step or even runtime.
See find_package docs for more details.
find_package only
All above applies to find_package command too.
find_package can operate in two modes MODULE and CONFIG.
In MODULE mode cmake uses Find[PackageName].cmake script (module) to find dependent package. CMake comes with large number of modules and custom modules can be added with CMAKE_MODULE_PATH variable. Often find-modules can be informed about custom search paths via environment or cmake variables.
E.g. FindGTest.cmake searches path stored in GTEST_ROOT variable.
If no find module is available, find_package enters CONFIG mode. If a dependency package provides [PackageName]Config.cmake or [LowercasePackageName]-config.cmake cmake can be easily informed about that package with [PackageName]_DIR variable.
Example:
CMakeLists.txt contains:
find_package(Qt5)
FindQt5.cmake is not available, but ~/Qt5/Qt5.8/lib/cmake/Qt5Config.cmake file exists, so add
-DQt5_DIR="${HOME}/Qt5/Qt5.8/lib/cmake"
to cmake call.
pkg-config
CMake can use information provided by external pkg-config tool. It is usually done with pkg_check_modules command. Directory used by pkg-config can be customized with PKG_CONFIG_PATH environment variable. According to cmake documentation instead of setting PKG_CONFIG_PATH, custom .pc-files directories can be added via CMAKE_PREFIX_PATH. If CMake version is pre-3.1, PKG_CONFIG_USE_CMAKE_PREFIX_PATH have to be set to TRUE(ON) to enable this feature.
Methods of customizing dependencies search path is defined by CMakeLists.txt content. There is no universal solution here.
And now back to missing SECCOMP headers...
In CMakeLists.txt SECCOMP header is found with
find_path(SECCOMP NAMES "linux/seccomp.h")
but I cannot find any command telling CMake to use the found header. For example:
target_include_directories(<target_name> ${SECCOMP})
or globally:
include_directories(${SECCOMP})
I belive that CMakeLists.txt should be fixed. It is not a platform dependent solution.

CMake does not find includes / libraries

I want to use some third-party headers (or a library) in a project that uses CMake. But it does not find the headers (the library). Why does CMake not find it?
CMake's find routines look for headers and libraries at some specific places. This includes the PATH variable, and the default locations for installed software, e.g., for many Linuces /usr/bin. Additionally, it evaluates the CMake variable CMAKE_PREFIX_PATH.
You have two possibilities to help CMake finding the required files:
Check whether your software is properly installed. For self-compiled software, that's usually done by make install or similar. If you use packages (RPM or deb), they are in general installed and can be found with the PATH variable.
If you don't want or can install the software, add its path to the CMAKE_PREFIX_PATH variable. Either pass it to the CMake call cmake -DCMAKE_PREFIX_PATH=/path/to/software .. or edit/add the according field in the CMake-GUI.
You have to delete the CMakeCache.txt, otherwise CMake will not find the library, because it does not check but use the cached result. Re-run CMake and it should work.
Evaluation order
If you have multiple versions of a library on your system, add the one you want to use to the CMAKE_PREFIX_PATH as the variables gets evaluated prior to the system path variables.
Module-specific variables
Some modules offer specific variables like mylib_DIR or mylib_ROOT to indicate the search path. Its use is discouraged and they are only left for backwards-compatibility. New modules don't have these modules and commits adding such variables are rejected by the CMake developers.
Documentation
More details on how CMake searches files and in which order can be found in the documentation: https://cmake.org/cmake/help/latest/command/find_library.html

What is the default search path for find_package in windows using cmake?

I am porting some code over to windows and my cmake checks for the package Libavahi using
find_package(Libavahi)
I have the headers, dll, etc. but I'm not sure where to place these such that cmake will find them.
Where can I put these files to be found by cmake? They're in a folder called usr.
I see that the module path is specified using:
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
but I'm wondering if there is a default location that will be searched as well
The CMake manual fully specifies the rather complicated search order for the different find_* commands. Unfortunately, since Windows lacks a default directory structure à la /usr/local/lib, it is hard to come up with reasonable defaults here.
One of the most reliable ways of managing directories is through environment variable hints. You simply add an $ENV{MY_VAR} to the HINTS section of the find command and then document that environment variable in your project's readme. Most users that are capable of compiling a C++ program know how to use environment variables, and it is way more convenient than having to give the path on the command line every time (although it never hurts to leave that as an additional option).
For find_package CMake offers a special mechanism on Windows called the package registry. CMake maintains a list of package information in the Windows registry under HKEY_CURRENT_USER\Software\Kitware\CMake\Packages\. Packages build from source can register there using the export command. Other projects build later on the same machine will then be able to find that package without additional configuration. This is quite powerful if you need to build a lot of interdependent projects from source on the same machine.
Update: Starting with version 3.12, CMake now implicitly considers the <PackageName>_Root environment variable a HINT for every find_package call.
In the newer versions of cmake, you can use the --debug-find option to list the directories that cmake is searching through. Somethin like:
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_TOOLS=ON --debug-find .