Using installed libraries: find_package

CMake · Practical · 12 min

Almost every project depends on other libraries; find_package is the standard way to consume them.

find_package(fmt 10 REQUIRED) asks CMake to find an installed package and fail with a clear message if it's missing. On success the package provides imported targets, usually namespaced, which you link like your own:

target_link_libraries(app PRIVATE fmt::fmt)

Imported targets carry the include paths, definitions and transitive dependencies, so one line is all you need.

There are two search modes:

  • Config mode: the package ships a fmtConfig.cmake (or fmt-config.cmake) file, installed with it. This is what modern libraries provide and what package managers generate.
  • Module mode: CMake (or your project) supplies a FindXxx.cmake script that searches for headers and libraries. CMake ships modules for common system libraries: Threads, ZLIB, OpenSSL, CURL, Python, Boost (newer Boost versions provide config files instead).

By default CMake tries module mode first, then config mode. find_package(fmt CONFIG REQUIRED) forces config mode.

If a package is installed in a non-standard location, tell CMake: -DCMAKE_PREFIX_PATH=/opt/fmt (a list of install prefixes), or -Dfmt_DIR=/path/to/dir/containing/fmtConfig.cmake. Package managers such as vcpkg and Conan work by installing packages and pointing CMake at them through a toolchain file or presets.

Example

cmake_minimum_required(VERSION 3.25)
project(fetcher LANGUAGES CXX)

find_package(Threads REQUIRED)                # module mode: FindThreads.cmake ships with CMake
find_package(ZLIB 1.2 REQUIRED)               # FindZLIB -> ZLIB::ZLIB
find_package(fmt 10 CONFIG REQUIRED)          # config mode: fmtConfig.cmake from the install
find_package(OpenSSL)                         # optional
message(STATUS "zlib ${ZLIB_VERSION}, OpenSSL found: ${OpenSSL_FOUND}")

add_executable(fetcher main.cpp)
target_link_libraries(fetcher PRIVATE fmt::fmt ZLIB::ZLIB Threads::Threads)
if(OpenSSL_FOUND)
  target_link_libraries(fetcher PRIVATE OpenSSL::SSL)
  target_compile_definitions(fetcher PRIVATE HAVE_OPENSSL=1)
endif()

# $ cmake -S . -B build -DCMAKE_PREFIX_PATH="$HOME/.local;/opt/fmt"

Remember

  • find_package(Name REQUIRED) then link the imported target Name::Name
  • Config mode uses the package's own Config.cmake; module mode a FindXxx.cmake
  • Point CMake at custom installs with CMAKE_PREFIX_PATH

Watch out for

  • Linking ${ZLIB_LIBRARIES} and adding include dirs by hand when an imported target ZLIB::ZLIB exists and carries everything.

Check yourself

  1. find_package(fmt CONFIG REQUIRED) fails although fmt is installed in /opt/fmt. What is the usual fix?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: cmake.org/cmake/help/latest/command/find_package.html