Lesson 17 · Organize your project
Find dependencies with find_package
find_package in config versus module mode, imported targets, REQUIRED, versions, and the CMAKE_PREFIX_PATH that fixes not found.
Your program needs a library that is already installed on the system. fmt, OpenSSL, Qt. find_package is the command that locates it and hands you its imported targets.
The one-liner you will write a thousand times
find_package(fmt REQUIRED)
target_link_libraries(app PRIVATE fmt::fmt)
That is the pattern. Find it, then link the imported target it provides (lesson 14).
Config mode versus module mode
find_package(foo) tries two strategies, in order:
| Module mode | Config mode | |
|---|---|---|
| Searches for | FindFoo.cmake, a CMake script | fooConfig.cmake or foo-config.cmake (and .cps files in CMake 4.x) |
| Who ships it | CMake bundles about 100 Find*.cmake modules. The package usually does not | The package itself, installed next to its files |
| Reliability | Heuristic, guesses paths | Exact. The package knows its own layout |
| Example | find_package(Threads) gives Threads::Threads | find_package(fmt) finds fmtConfig.cmake and gives fmt::fmt |
Config files are installed by the package itself, so they know exactly where their files are. That makes them more reliable than Find modules, which have to guess. Config mode is where the ecosystem went. Modern libraries ship *Config.cmake files, and you will write one in lesson 23.
The keywords that matter
# REQUIRED. Fail hard if missing. For dependencies you cannot work without.
find_package(fmt REQUIRED)
# QUIET. No not-found chatter. Still errors on a REQUIRED failure.
find_package(fmt REQUIRED QUIET)
# Version. Require at least 9.0.
find_package(fmt 9.0 REQUIRED)
# CONFIG. Skip module mode. Only look for *Config.cmake.
find_package(fmt CONFIG REQUIRED)
# OPTIONAL_COMPONENTS. Try to find extras, do not fail if absent.
find_package(fmt COMPONENTS format chrono)
Why find_package(Threads) is special
Threads is a CMake module. There is no installed package.
find_package(Threads REQUIRED) # module mode
target_link_libraries(app PRIVATE Threads::Threads)
CMake’s FindThreads.cmake runs the platform knowledge for you. So use module mode for things CMake knows, and config mode for installed libraries. You usually do not choose. find_package tries module first, then config.
“Not found”, the fix that works most of the time
CMake Error at CMakeLists.txt:5 (find_package):
By not providing "Findfmt.cmake" in CMAKE_MODULE_PATH this project has
asked CMake to find a package configuration file provided by "fmt", but
CMake did not find one.
The library is installed, but in a non-standard place. CMake does not know where. Tell it:
cmake -S . -B build -DCMAKE_PREFIX_PATH=/opt/fmt
CMAKE_PREFIX_PATH is the list of install prefixes to search. Homebrew installs on macOS sometimes need:
cmake -S . -B build -DCMAKE_PREFIX_PATH="$(brew --prefix)"
Tip
The error message is honest. It tells you exactly what it looked for and where. Read it twice before googling. Usually the answer, install the package or add its prefix to
CMAKE_PREFIX_PATH, is right there in the text.
Pitfall
Old tutorials show
find_package(Foo)plusinclude(${FOO_USE_FILE})or hand-linking${FOO_LIBRARIES}. Modern packages provide imported targets. Prefer them (lesson 14). If a package only sets variables,FOO_LIBRARIESandFOO_INCLUDE_DIRSare a valid fallback, but prefer packages that provide targets.
Read the docs