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

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

Related

cmake find all directories by name on the include path and add them to the include path

I am working with a library that nominally stores its internal headers in a directory that is not itself on the include path, although its parent is. Including the intended entry point header ends up failing because it links to other internal headers via quoted include without the directory name.
I do #include <SDL2/SDL.h> which the compiler does find, in /usr/include/SDL2/SDL.h on my system, but it then fails to find "begin_code.h" which is included several layers deeper in SDLs internal header code.
In file included from /usr/include/SDL2/SDL.h:32:
In file included from /usr/include/SDL2/SDL_main.h:25:
In file included from /usr/include/SDL2/SDL_stdinc.h:31:
In file included from /usr/include/SDL2/SDL_config.h:4:
In file included from /usr/include/x86_64-linux-gnu/SDL2/_real_SDL_config.h:33:
/usr/include/x86_64-linux-gnu/SDL2/SDL_platform.h:179:10: fatal error: 'begin_code.h' file not found
#include "begin_code.h"
^~~~~~~~~~~~~~
1 error generated.
Adding -iquote /usr/include/SDL2 manually works in my case, but what about in build environments where the SDL2 headers were downloaded to some local directory? The point of cmake is to work with local configurations that vary, so adding a hard-coded single path based on platform would be dumb. I want some future person who wants to compile my code with the SDL2 headers downloaded to ~/projects/headers/SDL2 to be able to compile after specifying only ~/projects/headers to their include path, for example, so they don't have to deal with SDLs internal issues.
It seems to me that all I need is to iterate on every dir on the -iquote path and, if it contains a directory name SDL2, add that directory to the -iquote. Does cmake make available the (system configuration dependent) -iquote path as an traversable list?
This question is my attempt to rephrase this unasnwered question for clarity.
Edit: I get that cmake is not responsible for fixing the issue, but cmake (or, rather, a CMakeList.txt file in my project) should be capable of working around this SDL bug. Hard-coding the assumed path is only reliable for build systems that install SDL2 headers via some standard package manager. I've never seen a unix dev manually download header files and stick them in the system include path, for fear that they might be overwritten or otherwise conflict with a future install of an official headers package. There are other valid places to put include files, so cmake should be able to search them. Isn't eliminating hard-coded paths half the point of cmake?
CMake doesn't provide a way for a custom iteration over include directories.
Instead, you could reformulate your intentions into the form "find a directory with the given header".
That form is expressed with command find_path, which is a natural way in CMake for search include directories.
E.g. that call:
# Task for CMake: Find a directory with "begin_code.h" header in it.
# Possibly, this is subdirectory 'SDL2' of a "normal" include directory.
find_path(SDL2_INCLUDE_DIR_1 "begin_code.h" PATH_SUFFIXES "SDL2")
will fill the variable SDL2_INCLUDE_DIR_1 with the directory containing the header begin_code.h.
This way works perfectly in case of local installation of SDL2, if that installation is hinted for CMake with CMAKE_PREFIX_PATH variable. For support other hints, e.g. SDL2DIR environment variable, you need to add appropriate PATHS options to your call:
find_path(SDL2_INCLUDE_DIR_1 "begin_code.h" PATH_SUFFIXES "SDL2" PATHS ENV SDL2DIR)
If you feel that SDL2 developers could rename the problematic file, but expect that file to be near the SDL2.h, then you could change the above command to search SDL2.h instead of begin_code.h:
find_path(SDL2_INCLUDE_DIR_1 "SDL2.h" PATH_SUFFIXES "SDL2" PATHS ENV SDL2DIR)

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.

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

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.

Package & library management & installation, and interface with cmake

I have a specific question which serves as context for a more general question.
There is a scientific package called LAMMPS, and it is usually used as an executable. However, it supports use as a "library". To try to do things right, I put it in /usr/local/lib/lammps. It contains a lammps/src/ directory, which has around 40 source files. Using the instructions provided, I compiled lammps as a .so file in lammps/src/liblammps_serial.so.
I also have separate code in "~/code/ljtube/". This uses cmake to try to find the library. Thus, I wrote a FindLAMMPS.txt so that I could use
FIND_PACKAGE (lammps)
in my CMakeLists. I modified the libtool config file to search in /usr/local/ successfully. I found that it searches in /usr/local/lib/ for a .so file and in /usr/local/include/ for a .h file. So I made a dynamic link to the .so file in /usr/local/lib/, and I copied the .h file from the lammps/src/ to /usr/local/include/.
CMake can now find those two files, but it cannot link to anything else in lammps/src/. It seems absurd to need to make a separate FIND_PACKAGE for each of the .h's I want to include (group.h, fix.h, force.h, pair.h, etc.). It also seems ridiculous to dump the whole package of .h files into the /usr/local/include/ directory. I will be using this code both locally and on a cluster, and possibly distributing it to other group members.
How can I make CMake find what I want to find without hard coding in the location of /usr/local/lib/lammps/src/? Phrased more generically, how should I manage large packages like these to make them easy to link to in the code I write, even if the original developer did not use the best conventions?
(As a side note, I am using a shared library because it seems like the right choice, but I'm not especially married to it. Should I be using a static library? Is there a way for CMake to find an already-compiled library relative to the current source directory, and might that be a better way to implement this? I know that I will be using LAMMPS in multiple projects, so having a local shared copy superficially seems to make the most sense.)
Normally a find_package call yields a variable specifying the path to the "includes" folder of the package. This would then be added in the caller's CMakeLists.txt via include_directories.
For example, to use find_package for boost, you could do:
find_package(Boost) # sets ${Boost_INCLUDE_DIRS} and ${Boost_LIBRARIES}
if(Boost_FOUND)
include_directories(${Boost_INCLUDE_DIRS})
add_executable(foo foo.cc)
target_link_libraries(foo ${Boost_LIBRARIES})
endif()
Regarding your side note, you could use find_library and/or find_path to find the library and its headers given a known location.
Both these commands can be invoked in such a way as to avoid searching in common locations, e.g. by setting PATHS to the known location and using NO_DEFAULT_PATH in the find commands.
Another alternative is for your projects to make use of the ExternalProject_Add function which is described in more detail in this article. From this article:
The ExternalProject_Add function makes it possible to say “download this project from the internet, run its configure step, build it and install it”
A downside to this approach is that each of your projects would end up with its own copy of the third party sources and lib.