In CMake, is there a way to make built-in Find scripts to prioritize a custom directory that we specify? Because especially in windows, module finding scripts usually can't detect the module in, for example visual studio directories. Therefore I usually have to manually set the paths for the external libraries which is pretty tiring. Instead, I want those scripts to look in a custom directory, let's say 'dependencies' folder in the main project first so that I can directly put those externals in that folder which is much easier than putting them into the VC folder or manually setting the paths.
Setting CMAKE_PREFIX_PATH variable serves exactly these purposes: hinting find_* function about location of searched item.
While description of this variable doesn't note about find_package function, the variable affects it indirectly: the most of Find<name>.cmake scripts use find_library and find_path functions. Note, that all find_* functions have precise algorithm for search items, and paths constructed with CMAKE_PREFIX_PATH are checked before system ones.
Moreover, CMAKE_PREFIX_PATH affects some other search procedures. E.g., if 3rd party package provides <name>Config.cmake script instead of Find<name>.cmake one, this script is also searched for using this variable. pkg_check_modules also uses CMAKE_PREFIX_PATH to search for .pc files describing the package.
CMAKE_PREFIX_PATH variable can be set as environment one (in platform-depending and usage-specific way), as parameter to cmake call:
cmake -DCMAKE_PREFIX_PATH=<additional-path> <other-parameters>
or within CMakeLists.txt file. In the last case it is better to append search directories, so user of you package is able to set the variable too for search packages not shipped with your project:
list(APPEND CMAKE_PREFIX_PATH "${CMAKE_SOURCE_DIR}/dependencies")
Note, that variable CMAKE_PREFIX_PATH doesn't affect searching for FindXXX.cmake script itself. For specifying the directory where the script is located, use CMAKE_MODULE_PATH variable.
Related
Upon reading about CMake policy 74 - https://cmake.org/cmake/help/latest/policy/CMP0074.html
[...] now searches prefixes specified by the _ROOT CMake variable and the ROOT environment variable. Package roots are maintained as a stack so nested calls to all find* commands inside find modules and config packages also search the roots as prefixes.
Does this mean <PackageName>_ROOT need not be included in the find_* paths explicitly?
Does this mean <PackageName>_ROOT will be checked automatically?
Yes, but this only applies to CMake 3.12 and greater, as stated in the policy documentation:
In CMake 3.12 and above the find_package(<PackageName>) command now searches prefixes specified by the <PackageName>_ROOT CMake variable and the <PackageName>_ROOT environment variable.
This also only applies to find_* commands within find modules and config packages.
This behavior is stated explicitly in the documentation for all of the find_* commands. For example, here is a snippet from the find_library() documentation (important section bolded):
If NO_DEFAULT_PATH is not specified, the search process is as follows:
If called from within a find module or any other script loaded by a call to find_package(<PackageName>), search prefixes unique to the current package being found. Specifically, look in the <PackageName>_ROOT CMake variable and the <PackageName>_ROOT environment variable. The package root variables are maintained as a stack, so if called from nested find modules or config packages, root paths from the parent’s find module or config package will be searched after paths from the current module or package. In other words, the search order would be <CurrentPackage>_ROOT, ENV{<CurrentPackage>_ROOT}, <ParentPackage>_ROOT, ENV{<ParentPackage>_ROOT}, etc. This can be skipped if NO_PACKAGE_ROOT_PATH is passed or by setting the CMAKE_FIND_USE_PACKAGE_ROOT_PATH to FALSE.
Also note, the <PackageName>_ROOT variable will not be used for the search if the find module is called with NO_DEFAULT_PATH or NO_PACKAGE_ROOT_PATH.
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.
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.
In CMake, is there a way to make built-in Find scripts to prioritize a custom directory that we specify? Because especially in windows, module finding scripts usually can't detect the module in, for example visual studio directories. Therefore I usually have to manually set the paths for the external libraries which is pretty tiring. Instead, I want those scripts to look in a custom directory, let's say 'dependencies' folder in the main project first so that I can directly put those externals in that folder which is much easier than putting them into the VC folder or manually setting the paths.
Setting CMAKE_PREFIX_PATH variable serves exactly these purposes: hinting find_* function about location of searched item.
While description of this variable doesn't note about find_package function, the variable affects it indirectly: the most of Find<name>.cmake scripts use find_library and find_path functions. Note, that all find_* functions have precise algorithm for search items, and paths constructed with CMAKE_PREFIX_PATH are checked before system ones.
Moreover, CMAKE_PREFIX_PATH affects some other search procedures. E.g., if 3rd party package provides <name>Config.cmake script instead of Find<name>.cmake one, this script is also searched for using this variable. pkg_check_modules also uses CMAKE_PREFIX_PATH to search for .pc files describing the package.
CMAKE_PREFIX_PATH variable can be set as environment one (in platform-depending and usage-specific way), as parameter to cmake call:
cmake -DCMAKE_PREFIX_PATH=<additional-path> <other-parameters>
or within CMakeLists.txt file. In the last case it is better to append search directories, so user of you package is able to set the variable too for search packages not shipped with your project:
list(APPEND CMAKE_PREFIX_PATH "${CMAKE_SOURCE_DIR}/dependencies")
Note, that variable CMAKE_PREFIX_PATH doesn't affect searching for FindXXX.cmake script itself. For specifying the directory where the script is located, use CMAKE_MODULE_PATH variable.
What's the cmake equivalent of autoconf's AC_ARG_WITH? In autoconf I can us AC_ARG_WITH to create a '--with-' command line argument to configure that lets me pass a path to a SDK and under that path are the headers and libraries. How do I do the same thing in cmake? Should I read the path from an env var?
cmake executable accepts variables' definitions in command line in form
-D<var_name>[:<TYPE>]=<value>
(:[<TYPE>] part is noted in cmake documentation, but it can be omitted).
Such variables are automatically added to the CMake cache, and can be used by project's cmake script.
For 3d-party project's installation path common idiom is:
CMakeLists.txt:
find_library(SDK_LIB sdk PATHS ${SDK_DIR} PATH_SUFFIXES lib)
find_path(SDK_INCLUDE_DIR sdk.h PATHS ${SDK_DIR} PATH_SUFFIXES include)
If SDK_DIR variable is set, its value (with appropriate suffix) will be used for search SDK library (SDK_LIB) and include directory (SDK_INCLUDE_DIR).
If the variable is not set, or search based on it's value has been failed, search will be continued in other places, including system-default ones.
Actually, tuning of package's paths in CMake is much more flexible than one provided with AC_ARG_WITH in autotools. E.g., one can pass common root(s) of all 3d-party packages using CMAKE_PREFIX_PATH variable, or common root(s) for all libraries using CMAKE_LIBRARY_PATH. See documentation on find_library and other find_* commands for more details.
Many of 3d-party packages provide Find<name>.cmake and/or <name>Config.cmake scripts, so them can be searched simply using find_package command. These scripts (and find_package itself) provide ways for tuning search paths, so your package needn't to bother of path's tuning at all.