What use is find_package() when you need to specify CMAKE_MODULE_PATH?
Categories:
Demystifying find_package() and CMAKE_MODULE_PATH in CMake

Explore the nuances of CMake's find_package() command and understand when and why CMAKE_MODULE_PATH becomes essential for locating custom or non-standard packages.
CMake's find_package() command is a cornerstone for managing external dependencies in C++ projects. It automates the process of locating libraries, headers, and other package components, making your build system robust and portable. However, its behavior can sometimes be a source of confusion, especially when dealing with packages not installed in standard system locations or when creating your own Find<PackageName>.cmake modules. This article delves into the mechanics of find_package() and clarifies the critical role of CMAKE_MODULE_PATH in guiding CMake to the right places.
How find_package() Works: A Search Strategy Overview
At its core, find_package() attempts to locate a package by searching for specific files. It operates in two main modes: Module mode and Config mode. The search order and the files it looks for differ slightly between these modes, but the general principle involves checking a series of predefined and user-specified paths.
In Module mode, find_package(<PackageName>) looks for a file named Find<PackageName>.cmake. This file contains custom logic written by the package maintainer (or you) to locate the package's components. In Config mode, find_package(<PackageName> CONFIG) or find_package(<PackageName> REQUIRED) (which implies CONFIG mode if a module is not found) looks for <PackageName>Config.cmake or <packagename>-config.cmake files. These files are typically generated by the package's build system and provide direct information about the package's installation.
The search paths include system-wide locations, environment variables, and crucially, paths specified by CMAKE_MODULE_PATH and CMAKE_PREFIX_PATH.
flowchart TD
A[find_package(PackageName)] --> B{Module Mode?};
B -- Yes --> C[Search CMAKE_MODULE_PATH];
C --> D[Search CMAKE_ROOT/Modules];
D --> E{Find<PackageName>.cmake found?};
E -- Yes --> F[Package Found (Module Mode)];
E -- No --> G{Config Mode?};
B -- No --> G;
G -- Yes --> H[Search CMAKE_PREFIX_PATH];
H --> I[Search system paths (e.g., /usr/local, /usr)];
I --> J{<PackageName>Config.cmake found?};
J -- Yes --> K[Package Found (Config Mode)];
J -- No --> L[Package Not Found];Simplified flow of CMake's find_package() search strategy.
The Role of CMAKE_MODULE_PATH
CMAKE_MODULE_PATH is a CMake list variable that holds a list of directories where CMake should look for Find<PackageName>.cmake files. It is specifically relevant when you are using or developing custom Find modules that are not part of CMake's built-in modules or when a package's Find module is installed in a non-standard location.
Consider a scenario where you've written a FindMyCustomLib.cmake module for a proprietary library, or you've downloaded a third-party Find module that isn't installed system-wide. Without CMAKE_MODULE_PATH, CMake wouldn't know where to look for this file, leading to find_package() failures.
By adding your custom module's directory to CMAKE_MODULE_PATH, you explicitly tell CMake: "Hey, when you're looking for Find<PackageName>.cmake, also check these directories." This provides a flexible way to extend CMake's package-finding capabilities without modifying system-wide CMake installations.
# In your project's CMakeLists.txt
# Define a directory where your custom Find modules reside
set(MY_FIND_MODULES_DIR "${CMAKE_SOURCE_DIR}/cmake/modules")
# Add this directory to CMAKE_MODULE_PATH
# It's generally good practice to append to avoid overwriting existing paths
list(APPEND CMAKE_MODULE_PATH "${MY_FIND_MODULES_DIR}")
# Now, find_package() will look in MY_FIND_MODULES_DIR for FindMyCustomLib.cmake
find_package(MyCustomLib REQUIRED)
Adding a custom directory to CMAKE_MODULE_PATH.
CMAKE_MODULE_PATH is for Find<PackageName>.cmake files, CMAKE_PREFIX_PATH is used to find <PackageName>Config.cmake files or general installation prefixes. Often, you might need to set both if you're dealing with non-standard installations of both types of packages.When to Use CMAKE_MODULE_PATH vs. CMAKE_PREFIX_PATH
Understanding the distinction between CMAKE_MODULE_PATH and CMAKE_PREFIX_PATH is crucial for effective dependency management:
CMAKE_MODULE_PATH: Use this when you have aFind<PackageName>.cmakescript that CMake needs to discover. This is common for packages that don't provide their ownConfigfiles, or when you're writing your own custom finding logic.CMAKE_PREFIX_PATH: Use this when a package provides aConfigfile (e.g.,FooConfig.cmake) and is installed in a non-standard prefix. CMake will search forFooConfig.cmakewithinCMAKE_PREFIX_PATH/<PackageName>/(lib/cmake|share/cmake)or directly inCMAKE_PREFIX_PATH/<PackageName>. This is the more common scenario for well-behaved third-party libraries.
In many cases, if you're installing a package to a custom location, you might need to set CMAKE_PREFIX_PATH to the installation root. If that package also relies on a custom Find module that isn't in a standard location, then CMAKE_MODULE_PATH might also be necessary.
# Example: Setting both paths
# Assume a custom installation prefix for a library
set(MY_LIB_INSTALL_PREFIX "/opt/my_custom_libs")
list(APPEND CMAKE_PREFIX_PATH "${MY_LIB_INSTALL_PREFIX}")
# Assume a custom Find module for another dependency
set(MY_CUSTOM_FIND_MODULES "${CMAKE_SOURCE_DIR}/cmake/find_modules")
list(APPEND CMAKE_MODULE_PATH "${MY_CUSTOM_FIND_MODULES}")
find_package(MyConfigLib REQUIRED)
find_package(MyModuleLib REQUIRED)
Demonstrating the use of both CMAKE_PREFIX_PATH and CMAKE_MODULE_PATH.
CMAKE_MODULE_PATH or CMAKE_PREFIX_PATH in your main CMakeLists.txt if your project is meant to be portable. Prefer using relative paths (e.g., ${CMAKE_SOURCE_DIR}/cmake) or allowing users to set these variables via the command line (cmake -D CMAKE_MODULE_PATH=/path/to/modules ...).