CMake: find_package()

What You Should Know & Your Actions

When we need to add an external project as the dependency, we will use command find_package in CMakeLists.txt to let CMake know where the header files and libraries are located, such that they can be found and linked properly later on.

It is ideal that the developer of the external project provides a <package>Config.cmake file (or a <lower-case-name>-config.cmake file) for us/users, and this file will be cached into database (in the default installation path) after we sudo make install this external project. You can check if they are available by the locate command.

# example commands
locate OpenCVConfig.cmake
locate gflags-config.cmake

# the results (if available)
/usr/share/OpenCV/OpenCVConfig.cmake
/usr/lib/x86_64-linux-gnu/cmake/gflags/gflags-config.cmake

If they are available, you only need to write one single line in the CMakeLists.txt file. For example,

find_package(PCL)   # it will work given that PCLConfig.cmake is available

Unfortunately, there are certain packages that do not provide (they should!) this CMake configuration file (or the files are not adopted by Ubuntu/apt). In this case, we need to use Find<package>.cmake file instead to locate this external project. We can write it on our own, but it is often the case that someone has done it before. So go ahead and find an available one to use.

After you find a good Find<package>.cmake file, we often place it under a folder named cmake or cmake-modules in the root directory of the current project (in parallel to include, src, etc.), and then add the path to this folder to a CMake variable CMAKE_MODULE_PATH (which is empty by default). With this setup, CMake will be able to look for your customized Find<package>.cmake file according to this CMake module path. What you need to do in CMakeLists.txt file is the following two lines of code.

list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake-modules")
find_package(Glog) # works given that FindGlog.cmake is placed under cmake-modules

Summary

  • The CMake command find_package has two modes (Config mode and Module mode) to find and load the CMake configuration of an external project.

  • In Config mode, it will look for a <package>Config.cmake or<lower-case-name>-config.cmake file in the default install path in Ubuntu system.

  • In Module mode, it will look for a Find<package>.cmake file in the path specified by variable CMAKE_MODULE_PATH ; this variable is empty unless set by user.

  • Module mode has higher priority than Config mode, which means that you can overwrite the default Config file by providing your own FindXXX file.

More Technical Details

According to the official documentation and this stackoverflow answer, find_package() has two modes: "Module" mode and "Config" mode. If no module is found and the MODULE option is not given, the command proceeds to Config mode.

Module mode

  • It will look for a file called Find<package>.cmake, first in CMAKE_MODULE_PATH (empty by default), and then under cmake installation path <CMAKE_ROOT>/Modules (e.g., /usr/share/cmake-3.5/Modules). Some example default modules include FindCUDA.cmake or FindOpenGL.cmake.

  • Therefore, to add our own cmake modules or overwrite system cmake modules, we can add Find<package>.cmake files in a folder called cmake and do the following in CMakeLists.txt, to make the best use of CMAKE_MODULE_PATH.

  • list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")

Config mode

  • It will look for a file called <package>Config.cmake or <lower-case-name>-config.cmake under CMAKE_INSTALL_PREFIX (default to /usr/local). These cmake files are installed when we do sudo make install for 3rd party libraries. Examples are as follows (excerpts from command output).

  • -- Installing: /usr/local/share/OpenCV/OpenCVConfig.cmake
    -- Up-to-date: /usr/local/share/sophus/cmake/SophusConfig.cmake
    -- Up-to-date: /usr/local/lib/cmake/Ceres/CeresConfig.cmake
    -- Up-to-date: /usr/local/lib/cmake/g2o/g2oConfig.cmake
    -- Up-to-date: /usr/local/lib/cmake/GTest/GTestConfig.cmake
    -- Up-to-date: /usr/local/lib/cmake/DBoW3/DBoW3Config.cmake
    -- Up-to-date: /usr/local/lib/cmake/Pangolin/PangolinConfig.cmake

References

Commands

Commonly used commands to figure out where packages are installed.

  • dpkg -L <package-name>

  • apt list <package-name>

  • locate <package>Config.cmake

To set flags

  • set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")

  • set(CMAKE_BUILD_TYPE RelWithDebInfo)

  • cmake -DCMAKE_BUILD_TYPE=Debug

Note that all names marked by <xxx> should be replaced by their actual file names or package names. Case sensitive!

Actions Needed After A Package Is Found

In general, once a package is found, a new variable <package>_FOUND will be generated and set to true. Also, there will be a few more variables loaded into the compilation process, such as <package>_INCLUDE_DIR , <package>_INCLUDE_DIRS, <package>_LIBRARIES, <package>_LIBS. Then you can use them to let CMake know where to look for header files and libraries. For example,

find_package(Glog REQUIRED) # REQUIRED: will stop compilation if package is missing
include_directories(${GLOG_INCLUDE_DIRS})

add_executable (main src/main.cpp)
target_link_libraries (main ${GLOG_LIBRARIES})

Note that different projects may adopt different variable names. You need to make sure the spelling is completely correct. (Should it be LIBRARIES or LIBS?)

Typically, the variable names are expected to be documented in the official documentation of this external project. (They should!) If not, you will need to dig into <lower-case-name>-config.cmake or <package>Config.cmake or Find<package>.cmake files, which is where they are defined.

Last updated