default search paths for CMake include() vs. find_package() - cmake

I have VTK6 installed on my Debian machine and it places all its CMake files under
$ ls /usr/lib/cmake/vtk-6.3/
[...]
VTKConfig.cmake
vtkModuleAPI.cmake
[...]
When I do
find_package(VTK)
in another project, it all works out fine. However,
include(vtkModuleAPI)
yields the error
include could not find load file:
vtkModuleAPI
I had always been under the impression that find_package() and include share the same search paths, specifically CMAKE_MODULE_PATH. Apparently that's not correct.
Note that
SET(CMAKE_MODULE_PATH "/usr/lib/cmake/vtk-6.3")
include(vtkModuleAPI)
does work.
Also note that I'm using CMake 3.5, so there no longer is a FindVTK.cmake as it used to be.
What are the default search paths for find_package() and include()? Why is vtkModuleAPI.cmake not found?

There are two modes of find_package, which have many differences:
Module mode tries to locate FindXXX.cmake file. The file is searched under directories listed in CMAKE_MODULE_PATH plus under directory where CMake is installed.
Config mode tries to locate XXXConfig.cmake file. The file is searched under directories listed in CMAKE_PREFIX_PATH and some other, system-specific variables. (Full algorithm see in the documentation, linked at the beginning of the post).
Command include searches modules only under directories in CMAKE_MODULE_PATH and special CMake module directory.
As you can see, command include and command find_package in module mode uses similar search paths. But in your case, VTKConfig.cmake can be searched only in config mode of find_package, which uses completely different search algorithm.
In case of VTK, CMake has shipped FindVTK.cmake file, which is used when you call find_package(VTK). But inside, this script uses find_package(VTK QUIET NO_MODULE).
If this call locates file /usr/lib/cmake/vtk-6.3/VTKConfig.cmake, it executes this script, and the script includes vtkModuleAPI.cmake one.
If your VTKConfig.cmake is not located by CMake, you may help it by setting VTK_DIR variable to /usr/lib/cmake/vtk-6.3/.
[Starting with CMake-3.1, FindVTK.cmake is no longer shipped with CMake, so find_package(VTK) immediately tries to locate VTKConfig.cmake].
In any case, modules in directory /usr/lib/cmake/vtk-6.3/ shouldn't be included directly: this directory is private for VTK.

find_package(VTK) uses FindVTK.cmake (in it's module mode, c.f. docu on find_package()), which is shipped by CMake and (in your case) should be located in /usr/share/cmake/Modules.
After adding /usr/lib/cmake/vtk-6.3 to CMAKE_MODULE_PATH, find_package(VTK) will still use the same FindVTK.cmake module.
In case you want to use another FindVTK.cmake module, prepend the path to that FindVTK.cmake module to CMAKE_MODULE_PATH.
include() will not use a find module and only sees files located in the CMAKE_MODULE_PATH.

Related

How to set a specific package path for CMakeLists.txt

My colleague wrote a CMakeLists.txt, which contains things as below:
find_package(OpenCV 3 REQUIRED
COMPONENTS
opencv_core
opencv_imgproc
opencv_imgcodecs
CONFIG
)
As the project needs these components of Opencv3, my colleague downloaded the whole Opencv3, of course, it works.
But the whole Opencv3 is too big, so I get only the necessary lib files: libopencv_core.so, libopencv_imgproc.so and libopencv_imgcodecs.so and try to replace the whole Opencv3. The three so files are put here: /opt/opencv3/.
I don't know how to tell the CMakeLists.txt to look for the components of Opencv3 at the specific path instead of the path by default.
I'm totally a newbie on writing CMakeLists.txt...
CMake find_package() finds and configure project dependencies in CMake using two modes of operation, Module and Config, as described in its documentation.
In Module mode (not your case), it looks for a Find<package>.cmake file inside CMAKE_MODULE_PATH. This file searches for header files and libraries and set the necessary CMake variables in case of success.
In your case it is using Config, as requested by the CONFIG keyword. In Config mode CMake looks for for a <name>Config.cmake file or <lowercase-name>-config.cmake.
These config files describe the version of the dependency and the location of header files and library modules.
So you should look for OpenCVConfig.cmake or opencv-config.cmake in CMAKE_PREFIX_PATH or in OpenCV_DIR.
Please note that you have to set(OpenCV_DIR ...) before calling find_package().

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.

set PKG_CONFIG_PATH in cmake

I have built opencv locally and installed it to a local directory (not the system default ). opencv.pc is present under a folder pkgconfig in this local folder. How can I find this opencv.pc from cmake, because I want to link and include opencv files from my program.
pkg_search_module(<PREFIX> [REQUIRED] [QUIET] <MODULE> [<MODULE>]*)
does not have any parameter in which I can force the command to use a specific path (similar to HINTS with find_package()) and not the system default.
So basically .cmake works:
find_package(OpenCV REQUIRED HINTS "my/path/to/share/OpenCVConfig.cmake")
but I would like to use a opencv.pc located under my/path/to/pkgconfig/opencv.pc.
After doing some research and a hint from the #OlivierM, I found the answer.
Here are the steps:
Method I :
CMAKE_PREFIX_PATH can be set to find the .pc files
set(CMAKE_PREFIX_PATH "${CMAKE_SOURCE_DIR}/libs/opencv-install")
Method II
A second method is to use the PKG_CONFIG_PATH, which is a system environment variable to look for .pc files.
set(ENV{PKG_CONFIG_PATH} "${CMAKE_SOURCE_DIR}/libs/opencv-install/lib/pkgconfig")
Irrespective of which method you use,
For old (traditional) CMake:
find_package(PkgConfig REQUIRED)
pkg_search_module(PKG_OPENCV REQUIRED opencv) # this looks for opencv.pc file
Please note that the PKG_OPENCV variable can be named anything. Whatever it is is named, its used as a prefix. For example if you name ABCD, then include directories will be ABCD_INCLUDE_DIRS
The variable PKG_OPENCV_INCLUDE_DIRS and PKG_OPENCV_LIBRARIES contains the header files (compile stage) and libraries (link stage) respectively.
One very important thing I noticed was that the variable PKG_OPENCV_LIBRARIES just provides the libraries and not the library path during the link stage. In order to use the library path as well in one command, one has to use
PKG_OPENCV_LDFLAGS
This variable contains the library path as well as all the libraries listed in the package config file.
for examaple:
include_directories(${PKG_OPENCV_INCLUDE_DIRS})
target_link_libraries (FINAL_BINARY ${PKG_OPENCV_LDFLAGS})
For modern CMake:
In modern CMake we don't want variables, we want targets.
find_package(PkgConfig REQUIRED)
# this looks for opencv.pc file and creates a new target
# IMPORTED_TARGET requires CMake >= 3.6.3
pkg_search_module(PKG_OPENCV REQUIRED IMPORTED_TARGET opencv)
All variables will still be created for backwards compatibility, but IMPORTED_TARGET will create a target you can use in your project which will automatically propagate all of its build and usage requirements:
target_link_libraries(my_proj PRIVATE PkgConfig::PKG_OPENCV)
You could set PKG_CONFIG_PATH with the CMake line:
set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/my/path/to/pkgconfig")
I did this workaround in this file
Interestingly, it seems CMake 3.1 extends PKG_CONFIG_PATH with some CMake variable see: https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=3df51470
I would propose you to call cmake with custom PKG_CONFIG_PATH variable, like below:
PKG_CONFIG_PATH=$PKG_CONFIG_PATH:my/path/to/pkgconfig cmake <some args>
Or can make PKG_CONFIG_PATH update to be permanent for whole bash session:
$ export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:my/path/to/pkgconfig
$ cmake <some args>

CMake find package files, how are the config files used?

I have a project that uses a 3rd party library (let's call it somelib) for which I wrote a cmake file to search for it.
This is the somelibConfig.cmake file I wrote and placed in /usr/local/lib/cmake/somelib/:
FIND_LIBRARY(somelib_LIBRARY somelib
PATHS /usr/local/lib
NO_DEFAULT_PATH
)
SET(somelib_LIBRARIES ${somelib_LIBRARY})
FIND_PATH(somelib_INCLUDE_DIR somelib.hpp
PATHS /usr/local/include/somelib
NO_DEFAULT_PATH
)
SET(somelib_INCLUDE_DIRS ${somelib_INCLUDE_DIR})
Then, if I do find_package(somelib REQUIRED) it works ok.
However, if I move and rename somelibConfig.cmake to myproject/CMakeModules/Findsomelib.cmake (this directory is added to CMAKE_MODULE_PATH), after find_package I see that variables somelib_INCLUDE_DIRS and somelib_LIBRARY are correctly filled, but somelib_FOUND is not set (and even so, find_package does not abort the compilation).
Is that *Config.cmake valid for a Find*.cmake?
How is it possible that all the variables but the *_FOUND one are set?
Why does not find_package with REQUIRED abort the compilation if *_FOUND is not set?
Config files and find-modules are fundamentally different.
http://www.cmake.org/cmake/help/v3.0/manual/cmake-packages.7.html
Only the developers of somelib ship a config file (if they do). If they don't, then you need to write a find-module to find somelib. Such a find-module should not be copied to /usr/local as you did. Just keep it with your project and ask the somelib developers to ship a config file instead. config files shipped by upstream is superior to find modules written by you. It doesn't matter if somelib upstream does not use cmake. Both Qt and LLVM ship config files when using non-cmake buildsystems.
One example of inferiority is that when writing a find-module you need to set the _FOUND variable. More information about writing find-modules is here:
http://www.cmake.org/cmake/help/v3.0/manual/cmake-developer.7.html#manual:cmake-developer%287%29
If you are searching in default library folder your parameters should not contain NO_DEFAULT_PATH.
Try this,
SET(libraryName "somelibrary.so") #in linux .a or .so
FIND_LIBRARY(LIBRARY ${libraryName}
PATHS "/usr/local/lib/cmake/somelib/"
)
MESSAGE("library path ${LIBRARY})
If this was successful, LIBRARY_FOUND will be set.
P.S: Note the quotes

CMake cannot resolve runtime directory path

After running cmake CMakeLists.txt
I get the following warning
CMake Warning at src/CMakeLists.txt:32 (add_executable):
Cannot generate a safe runtime search path for target MMPEditor because
files in some directories may conflict with libraries in implicit
directories:
runtime library [libQt5Widgets.so.5] in /usr/lib/x86_64-linux-gnu may be hidden by files in:
/home/ch/Qt/5.2.1/gcc_64/lib
runtime library [libQt5Core.so.5] in /usr/lib/x86_64-linux-gnu may be hidden by files in:
/home/ch/Qt/5.2.1/gcc_64/lib
runtime library [libQt5Gui.so.5] in /usr/lib/x86_64-linux-gnu may be hidden by files in:
/home/ch/Qt/5.2.1/gcc_64/lib
runtime library [libQt5OpenGL.so.5] in /usr/lib/x86_64-linux-gnu may be hidden by files in:
/home/ch/Qt/5.2.1/gcc_64/lib
Some of these libraries may not be found correctly.
What does it mean for one file to be hidden by another and how can I allow CMake to determine which is the right folder to link to?
What the warning actually implies:
The project requests linking with the shared library, which is contained in two directories. That is, both these directories have the file named like foo.so.x.y.
CMake perfectly understands which library file is chosen for linking. That is, it may pass the "right" directory to the runtime loader, so the loader will search the library there.
For some reason, CMake cannot prevent the loader to search under the "wrong" directory too. E.g. that directory is searched by default, or it contains other libraries needed for the same executable.
As a result, CMake cannot guarantee that, when you run the executable, the loader will find the proper library.
For examples, the message
runtime library [libQt5Widgets.so.5] in /usr/lib/x86_64-linux-gnu may be hidden by files in:
/home/ch/Qt/5.2.1/gcc_64/lib
means, that the project requests to link with the library libQt5Widgets.so.5 located in the directory /usr/lib/x86_64-linux-gnu.
But library with the same name exists also in the directory /home/ch/Qt/5.2.1/gcc_64/lib, which CMake treats as "implicit directory", where the loader will search in any case. (Probably, because this directory is listed in the variable LD_LIBRARY_PATH).
The consequences of the warning:
The project can be configured without the errors.
The project can be built without errors.
But when run the executable, the loaded may load wrong library. And that could lead to unpredictable results.
Because unpredictable results on running are rarely acceptable, it is better to fix the warning. Possible ways include the following:
Make sure that CMake chooses the library which you are actually intended to use.
E.g. you could actually intend to use the custom installation of QT under /home/ch/Qt/5.2.1/gcc_64. In that case you need to hint CMake about your intentions. E.g. by setting CMAKE_PREFIX_PATH variable.
Uninstall the library located in the "wrong" directory.
E.g. if you have newer version of the library and never intend to use the older one, then for avoid confusion it is better to uninstall the later.
If the "wrong" directory is searched by the loader because it is included into the variable LD_LIBRARY_PATH, then set this variable to not contain that directory.
CMake is able to correctly build projects for run without LD_LIBRARY_PATH settings.
If "wrong" directory is searched by the loader because it contains some other libraries, eliminate usage of that libraries in your executable.
If you have two "repositories" of libraries on your PC, then compatibility between libraries is guaranteed only within a single repo. Mixed usage of libraries could lead to incompatibility problems at runtime.
If you're dealing with find_library
find_library(LIBRARY_NAME PATHS "/usr/lib/x86_64-linux-gnu" NO_DEFAULT_PATH) where
PATHS stands for the exact path to the libs
NO_DEFAULT_PATH means, that cmake will not search anywhere else
check the values of lib and include paths with message(status, ${LIBRARY_NAME})
If you're dealing with find_package:
It's a bit more complicated than the previous example, but it's essentially the same.
For each package you have to run find_package for:
Create file with name Find<Packagename>.cmake, e. g. if you're looking for cppunit, you'll have to create FindCPPUNIT.cmake.
In that file, you'll have to run find_path on include files and find_library on lib files, like in "If you're dealing with find_library".
find_path(CPPUNIT_INCLUDE_DIR PATHS "/usr/include/x86_64-linux-gnu" NO_DEFAULT_PATH)
find_library(CPPUNIT_LIBRARY PATHS "/usr/lib/x86_64-linux-gnu" NO_DEFAULT_PATH)
And then you have to add the path to the file to CMAKE_MODULE_PATH.
Your system libraries are conflicting with your local custom build Qt libraries. Its a warning but you might not get expected results in your application because of this. You need to tell CMake that it should exclude system path while searching for libraries in your CMakeModule. From this documentation
If NO_DEFAULT_PATH is specified, then no additional paths are added to
the search.
Also in same documentation one more flag is mentioned NO_CMAKE_SYSTEM_PATH which only include platform specific default paths.