I am using CMake with a custom toolchain that I built using yocto. I have a problem though, the toolchain has sysroot for the target machine and one for the build machine.
CMake keeps finding the libraries in the build system sysroot only.
For example I am using:
find_package(libxml2)
But it always keeps finding libxml2 in the build system sysroot instead of the target sysroot. How can I tell it to only look in the target sysroot?
How can I tell it to look in the target sysroot only?
There is a family of CMake variables CMAKE_FIND_ROOT_PATH_MODE_*, which adjusts search strategy for different CMake commands:
BOTH value means that both target and host (build) paths are searched. This is also a default behavior, when a variable is not set.
ONLY value means that only target is searched.
NEVER value means, that only host is searched.
List of variables:
CMAKE_FIND_ROOT_PATH_MODE_LIBRARY affects on find_library() calls
CMAKE_FIND_ROOT_PATH_MODE_INCLUDE affects on find_path() and find_file() calls
CMAKE_FIND_ROOT_PATH_MODE_PACKAGE affects on find_package() in CONFIG mode (when *Config.cmake file is searched).
CMAKE_FIND_ROOT_PATH_MODE_PROGRAM affects on find_program() call.
Generally, concrete find_package() call may be affected by all of these variables. In case of searching libraries, it is usually suffificient to set only 3 of them:
# Search libraries only under *target* paths.
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
Variables CMAKE_FIND_ROOT_PATH_MODE_* are normally set in toolchain files.
Related
As per the document, which says:
CMAKE_BUILD_RPATH¶ New in version 3.8.
Semicolon-separated list specifying runtime path (RPATH) entries to
add to binaries linked in the build tree (for platforms that support
it). The entries will not be used for binaries in the install tree.
See also the CMAKE_INSTALL_RPATH variable.
This is used to initialize the BUILD_RPATH target property for all
targets.
As per the document, which says:
CMAKE_INSTALL_RPATH¶ The rpath to use for installed targets.
A semicolon-separated list specifying the rpath to use in installed
targets (for platforms that support it). This is used to initialize
the target property INSTALL_RPATH for all targets.
How to understand the differences between CMAKE_BUILD_RPATH and CMAKE_INSTALL_RPATH in the right way?
A small and clear example is welcome.
When building binaries one can set the RPATH to support library path resolution at runtime.
There are two scenarios for which to build binaries. The first an obvious scenario is to debug a program. This means the binary will be built and (normally) executed from the location it has been built. Details can vary but in general it is under the cmake build directory.
This means if you build for example two libraries in your project libA and libB. libA dynamically linking libB. This means both libraries are located somewhere in the binary path. To run a binary in the build path with runtime dependency resolution you CAN specify the CMAKE_BUILD_PATH or the recommended target property BUILD_RPATH with an absolute or relative value.
lib
location
rpath
libA
/home/user/my_tool/build/liba
./../libb/ or /home/user/my_tool/build/libb
libB
/home/user/my_tool/build/libb
Then you can smoothly run your binary from the build path and everything should work without modifying the LD_LIBRARY_PATH system environment variable for dependency lookup.
The same applies to the RPATH if the binary gets installed (cmake install). In this case the value of the RPATH could be different. To accommodate this there are these two CMake features to distinguish between the scenarios.
lib
location
rpath
libA
/usr/lib/my_tool
. or /usr/bin/my_tool
libB
/usr/lib/my_tool
I have a toolchain.cmake defined for the platform I'm building for and in it I've specified the location where I want my files installed. However, when I run make install, the files go to the default location /usr/..... My toolchain.cmake is setup as follows:
# this one is important
SET(CMAKE_SYSTEM_NAME Linux)
#this one not so much
SET(CMAKE_SYSTEM_VERSION 1)
# specify the cross compiler
SET(CMAKE_C_COMPILER /usr/local/naoqi-sdk-2.1.4.13-mac64/ctc-mac64-atom-2.1.4.13/cross/bin/i686-aldebaran-linux-gnu-gcc)
SET(CMAKE_CXX_COMPILER /usr/local/naoqi-sdk-2.1.4.13-mac64/ctc-mac64-atom-2.1.4.13/cross/bin/i686-aldebaran-linux-gnu-g++)
# where is the target environment
SET(CMAKE_FIND_ROOT_PATH /usr/local/naoqi-sdk-2.1.4.13-mac64/ctc-mac64-atom-2.1.4.13/cross/i686-aldebaran-linux-gnu/sysroot)
SET(CMAKE_SYSROOT /usr/local/naoqi-sdk-2.1.4.13-mac64/ctc-mac64-atom-2.1.4.13/cross/i686-aldebaran-linux-gnu/sysroot)
# search for programs in the build host directories
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY)
# for libraries and headers in the target directories
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
SET(THREADS_PTHREAD_ARG 1)
SET(CMAKE_INSTALL_FULL_INCLUDEDIR /usr/local/naoqi-sdk-2.1.4.13-mac64/ctc-mac64-atom-2.1.4.13/cross/i686-aldebaran-linux-gnu/sysroot/usr/local/include)
SET(CMAKE_INSTALL_FULL_LIBDIR /usr/local/naoqi-sdk-2.1.4.13-mac64/ctc-mac64-atom-2.1.4.13/cross/i686-aldebaran-linux-gnu/sysroot/usr/local/lib)
SET(CMAKE_INSTALL_FULL_MANDIR /usr/local/naoqi-sdk-2.1.4.13-mac64/ctc-mac64-atom-2.1.4.13/cross/i686-aldebaran-linux-gnu/sysroot/share/man)
SET(CMAKE_INSTALL_PREFIX /usr/local/naoqi-sdk-2.1.4.13-mac64/ctc-mac64-atom-2.1.4.13/cross/i686-aldebaran-linux-gnu/sysroot)
SET(CMAKE_STAGING_PREFIX /usr/local/naoqi-sdk-2.1.4.13-mac64/ctc-mac64-atom-2.1.4.13/cross/i686-aldebaran-linux-gnu/sysroot)
As can be seen, I've gone overkill on setting paths, but none of them work. In order to have make install place the file in the correct location, I have to pass -DCMAKE_INSTALL_PREFIX=<path> in the cmake command as shown below:
cmake -G "Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=~/myScripts/Toolchain-Naoqi.2.1.4.13.cmake -DCMAKE_INSTALL_PREFIX=/usr/local/naoqi-sdk-2.1.4.13-mac64/ctc-mac64-atom-2.1.4.13/cross/i686-aldebaran-linux-gnu/sysroot ..
It would be nice not to have the path set in my toolchain.cmake as it applies to all the projects I'm building.
The reason why your paths are overwritten is that the call to project() comes after the execution of this Toolchain file. The project() function, among other things, sets CMAKE_INSTALL_PREFIX to some default value.
It is good practice not to specify CMAKE_INSTALL_PREFIX in your CMakeLists.txt, to ensure cross-platform compatibility. For example, your approach would prevent someone with a non-Unix-like system of using your file.
If you really want to go on and specify it within the code, I see two options:
Hardcode CMAKE_INSTALL_PREFIX in one of the CMakeLists.txt of your project, i.e. after the call to project().
Provide a custom 'platform' file, located in some_folder/Platform/<My_Platform>.cmake, and add some_folder to your CMAKE_MODULE_PATH. In this platform file, you then specify CMAKE_INSTALL_PREFIX, such that you don't need to clobber your other CMakeLists.txt (my assumption here is that you only need this installation folder for this specific platform).
It looks like this issue is caused by a bug in CMake itself, based on an old bug report in their former bug tracker, which was closed without fixing the problem.
The issue is summarised as
Specifying CMAKE_INSTALL_PREFIX in a CMAKE_TOOLCHAIN_FILE file has no
effect
which exactly describes the problem you are encountering.
For what it's worth, I also encounter this same problem when trying to cross-compile with CMake v3.5.1 using a toolchain file, so I don't think that the issue has been fixed.
The migrated bug report in CMake's current bug tracker may be found here.
Besides the workaround you have identified involving setting CMAKE_INSTALL_PREFIX inline in the install command, a more permanent solution might be reached by petitioning the CMake team to fix this bug, by adding your comments to the conversation in the migrated bug report.
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'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.
I have a difficult time in understanding the difference between CMAKE_INSTALL_PREFIX and CMAKE_INSTALL_RPATH.
If I understand well, CMAKE_INSTALL_PREFIX is the prefixed directory that will be installed. Therefore, if I use the following script for installation:
project(hello)
add_library(hello hello.h hello.cpp)
set(CMAKE_INSTALL_PREFIX "c:/ABC/DEF")
INSTALL(TARGETS hello EXPORT hello_export
RUNTIME DESTINATION bin
LIBRARY DESTINATION bin
ARCHIVE DESTINATION lib
FRAMEWORK DESTINATION bin
INCLUDES DESTINATION include
)
Then the static library will be installed in C:/ABC/DEF/lib.
Then, my question is what's the point of using CMAKE_INSTALL_RPATH?
On a system which supports paths of the form c:/ABC/DEF (i.e. Windows), none. Windows binaries don't have a notion of rpath.
On systems which do have DT_RPATH and DT_RUNPATH (= those which use ELF binaries), the CMake variable CMAKE_INSTALL_RPATH is used to set up the value of DT_RPATH (or DT_RUNPATH) tags which will be written into the binaries at installation.
This is explained at CMake RPATH handling.
On Unix systems, dynamic libraries are searched for in a system-defined list of directories. (/etc/ld.so.conf -- Windows does this in its own way that is so convoluted that it usually boils down to "just use PATH". 😉)
If you install a library (like the one you just compiled) in a custom directory, not in that list, it will not be found if you run a dependent executable. RPATH is one way to fix this.
See the Wiki page linked above for details.
Firstly, CMAKE_INSTALL_PREFIX determines a "root" for the installed location of headers, libraries, executables, and other resources.
On a system which does not support the notion of a "search hierachy" for dependencies, CMAKE_INSTALL_RPATH is not used. However, on ELF-based systems (e.g. Linux) and Mach-based systems (e.g. macOS 10.5 and later) a set of additional locations to search can be set in executables and dynamic libraries (e.g. .so/.dylib files); this is the "Rpath" and you can set it during cmake's install phase, either for all targets by setting CMAKE_INSTALL_RPATH or for individual targets by setting INSTALL_RPATH on that target.
Static libraries are not dynamic (obviously!) so, CMAKE_INSTALL_RPATH has no utility at all for static libraries.
When installing dynamic objects, CMake will write the Rpath into the dynamic object provided CMAKE_SKIP_RPATH and CMAKE_SKIP_INSTALL_RPATH are both false. By default, the Rpath written will be set to CMAKE_INSTALL_PREFIX followed by the library destination, e.g. CMAKE_INSTALL_PREFIX/lib. On Linux systems, this would by default see an Rpath of /usr/local/lib written as Rpath.
You can examine the Rpath on Linux thus:
readelf -d libmylib.so
which produces something like:
0x000000000000000f (RPATH) Library rpath: [/usr/local/lib]
or on macOS:
otool -l libmylib.dylib | grep -A 2 LC_RPATH
which produces something like:
cmd LC_RPATH
cmdsize 40
path #loader_path/../Frameworks (offset 12)
To override the install Rpath you can set the variable CMAKE_INSTALL_RPATH. E.g. on Linux:
set(CMAKE_INSTALL_RPATH "\$ORIGIN/../lib")
or on macOS:
set(CMAKE_INSTALL_RPATH "#loader_path/../lib")