Can I use any fuzzy search tricks in CMake find_library? - cmake

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.

Related

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.

Installing a CMake library: also ship find modules for the dependencies?

My CMake library, MyLibrary, has a dependency for OtherLibrary, that I import with a non-standard FindOtherLibrary.cmake.
My library depends on OtherLibrary publicly:
target_link_libraries(MyLibrary PUBLIC OtherLibrary::OtherLibrary)
When I install MyLibrary (together with MyLibraryConfig.cmake), and users want to link against it, they therefore need to import OtherLibrary.
Is there a good practice regarding how to distribute FindOtherLibrary.cmake along MyLibrary?
Ideally, one could make things even easier for users of MyLibrary by importing OtherLibrary automatically from the installed config file MyLibraryConfig.cmake, if it contains something like
include(CMakeFindDependencyMacro)
find_dependency(OtherLibrary)
and knows where FindOtherLibrary.cmake is.
Is this at all possible?
I ended up finding a solution to my question.
In principle, it does what #utopia suggested, but in an automated fashion: the end user of my library does not need to set up (or even know about) FindOtherLibrary.cmake. It will be imported automatically by MyLibraryConfig.cmake.
To do so, I install FindOtherLibrary.cmake along MyLibraryConfig.cmake:
install(FILES
/path/to/MyLibraryConfig.cmake
DESTINATION
lib/cmake/MyLibrary
)
install(FILES
/path/to/FindOtherLibrary.cmake
DESTINATION
lib/cmake/MyLibrary/Modules
)
And in MyLibraryConfig.cmake I set up how to import it:
include(CMakeFindDependencyMacro)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_LIST_DIR}/Modules/")
find_dependency(OtherLibrary REQUIRED)
Note that I set the variable CMAKE_MODULE_PATH because it is not possible to specify the location of find modules in find_package or find_dependency (works only for config mode).
"Transitive" behavior for module-mode find_package() is not supported.
In fact, I don't believe it is even possible since it requires modifying downstream CMake module path with information you would not have available to you. That's one of the reasons there is a config-mode find_package() (see here).
To be clear, a user of your library, which has a dependency on a FindModule library, has no choice but to know how to get a copy of the FindModule script and add it to their CMake module path. This is typically done through documentation. You as the author of a library that uses the FindModule cannot shortcut that process for the end user in any general way. So, there is no "good practice" for such a process.
Otherwise, good practice is to use FindModules only for non-CMake projects and use Config.cmake for CMake projects. If a dependent CMake library has no Config.cmake, you're out of luck (tell them they need it to support CMake in a Bug/Issue report).

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.

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

How to link a static library to an executable using CMake

In CMake, we use TARGET_LINK_LIBRARIES() to link a shared library to an library/executable.
For example:
TARGET_LINK_LIBRARIES(ExecutableName xxx)
where ExecutableName - is the name of executable
xxx - is the library name.
As of my understanding CMake searches for "libxxx.so" at the paths mentioned in LINK_DIRECTORIES() macro.
But if I have a third party library with name "libxxx.a" then how do I link the library to the executable using CMake.
Thank you for your help in advance!
You should always try to give either full paths or CMake targets to target_link_libraries.
Since you do not seem to build the dependency as part of the CMake project, the only way to obtain a CMake target to link against, is to create an imported target. This is usually quite tedious to do manuall, so unless the dependency already provides a config file with an imported target, you probably do not want to go down that road. Imported targets are the most convenient to use, but only if you can get CMake to write the for you.
So, absolute paths it is then. You obviously would not want to hardcode absolute library paths in your CMakeLists. As pointed out in your question, the desired behavior is that you specify just the name of a library and CMake should be able to figure out its location automatically. This is exactly what find_library does for you.
To link against a library xxx, you would do something like this:
find_library(LIB_LOCATION xxx)
target_link_libraries(ExecutableName ${LIB_LOCATION})
Note that find_library provides a plethora of options to further specify where to look for the requested library. Get rid of your existing link_directories call and add the respective paths as hints to find_library instead.
This approach is both more flexible when porting your CMake code to other platforms and more easy to debug if something goes wrong than your initial approach.
Just specifying the library filename should work:
TARGET_LINK_LIBRARIES(ExecutableName libxxx.a)
and
TARGET_LINK_LIBRARIES(ExecutableName xxx)
actually should work too as that would not look for the .so but for a libxxx.a file in the search paths.