Finding External Packages
CMake offers three, in rough order of preference:
find_package()(this page) — use a dependency that's already installed on the system.- FetchContent — download and build a dependency as part of your configure step; the modern default when you can't assume it's installed.
- ExternalProject — build a dependency at build time as a separate project (for non-CMake builds or superbuilds).
Prefer find_package when the library is available, and fall back to FetchContent otherwise.
What is find_package()?
find_package() locates external libraries and sets up variables/targets for using them.
Basic Usage
find_package(PackageName [version] [REQUIRED] [COMPONENTS ...])
Examples:
# Find any version
find_package(OpenCV)
# Find specific version or newer
find_package(Boost 1.70)
# Require package (fail if not found)
find_package(Threads REQUIRED)
# Find with components
find_package(Qt5 COMPONENTS Core Widgets REQUIRED)
How It Works
CMake searches for one of two files:
- FindPackage.cmake - in
CMAKE_MODULE_PATH - PackageConfig.cmake - in various locations
Config Mode (Preferred)
find_package(MyPackage CONFIG)
Searches for:
MyPackageConfig.cmakemypackage-config.cmake
Where:
/usr/lib/cmake/MyPackage//usr/local/lib/cmake/MyPackage/<PackageName>_DIR
Module Mode
find_package(MyPackage MODULE)
Searches for:
FindMyPackage.cmake
Where:
CMAKE_MODULE_PATH- CMake's built-in modules
CMake tries both modes automatically. Use CONFIG or MODULE to force one.
Common Packages
Threads
find_package(Threads REQUIRED)
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE Threads::Threads)
OpenSSL
find_package(OpenSSL REQUIRED)
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE
OpenSSL::SSL
OpenSSL::Crypto
)
Boost
find_package(Boost 1.70 REQUIRED COMPONENTS
filesystem
system
regex
)
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE
Boost::filesystem
Boost::system
Boost::regex
)
Qt5/Qt6
find_package(Qt5 COMPONENTS
Core
Widgets
Network
REQUIRED
)
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE
Qt5::Core
Qt5::Widgets
Qt5::Network
)
OpenCV
find_package(OpenCV REQUIRED)
add_executable(vision main.cpp)
target_link_libraries(vision PRIVATE ${OpenCV_LIBS})
# Or with imported target (if available)
target_link_libraries(vision PRIVATE opencv_core opencv_imgproc)
Python
find_package(Python3 COMPONENTS Interpreter Development REQUIRED)
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE Python3::Python)
Version Requirements
# Exact version
find_package(MyPackage 2.1.3 EXACT REQUIRED)
# Minimum version
find_package(MyPackage 1.5 REQUIRED)
# Version range (CMake 3.19+)
find_package(MyPackage 1.0...2.0 REQUIRED)
Components
# Find specific components
find_package(Boost COMPONENTS
filesystem
regex
REQUIRED
)
# Optional components
find_package(Boost REQUIRED COMPONENTS filesystem)
find_package(Boost COMPONENTS regex) # Optional
if(TARGET Boost::regex)
target_compile_definitions(myapp PRIVATE HAS_BOOST_REGEX)
target_link_libraries(myapp PRIVATE Boost::regex)
endif()
Checking Results
Variables Set
Most packages set these:
<Package>_FOUND- Whether package was found<Package>_VERSION- Version found<Package>_INCLUDE_DIRS- Include directories<Package>_LIBRARIES- Libraries to link
find_package(ZLIB)
if(ZLIB_FOUND)
message(STATUS "ZLIB version: ${ZLIB_VERSION}")
message(STATUS "ZLIB include: ${ZLIB_INCLUDE_DIRS}")
message(STATUS "ZLIB libraries: ${ZLIB_LIBRARIES}")
target_include_directories(myapp PRIVATE ${ZLIB_INCLUDE_DIRS})
target_link_libraries(myapp PRIVATE ${ZLIB_LIBRARIES})
endif()
Modern Imported Targets (Preferred)
find_package(ZLIB REQUIRED)
# Modern way - use imported target
target_link_libraries(myapp PRIVATE ZLIB::ZLIB)
# Old way - manual include/link
target_include_directories(myapp PRIVATE ${ZLIB_INCLUDE_DIRS})
target_link_libraries(myapp PRIVATE ${ZLIB_LIBRARIES})
Modern packages provide Package::Component targets. These are self-contained and handle includes/links automatically.
Handling Optional Packages
# Try to find optional package
find_package(OptionalLib)
if(OptionalLib_FOUND)
message(STATUS "OptionalLib found, enabling feature")
target_compile_definitions(myapp PRIVATE HAS_OPTIONAL_LIB)
target_link_libraries(myapp PRIVATE OptionalLib::OptionalLib)
else()
message(STATUS "OptionalLib not found, disabling feature")
endif()
With option:
option(USE_OPTIONALLIB "Use OptionalLib if available" ON)
if(USE_OPTIONALLIB)
find_package(OptionalLib)
if(OptionalLib_FOUND)
target_link_libraries(myapp PRIVATE OptionalLib::OptionalLib)
else()
message(WARNING "OptionalLib requested but not found")
endif()
endif()
Specifying Package Location
Hint Paths
# Set before find_package
set(MyPackage_DIR "/path/to/MyPackageConfig.cmake")
find_package(MyPackage REQUIRED)
# Or from command line
cmake -DMyPackage_DIR=/path/to/cmake ..
CMAKE_PREFIX_PATH
list(APPEND CMAKE_PREFIX_PATH "/opt/mypackage")
find_package(MyPackage REQUIRED)
# Or from command line
cmake -DCMAKE_PREFIX_PATH=/opt/mypackage ..
Environment Variables
# Set package location
export MyPackage_DIR=/path/to/package/cmake
cmake ..
Custom Module Path
# Add custom Find modules
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules")
find_package(CustomPackage REQUIRED)
Directory structure:
project/
├── CMakeLists.txt
└── cmake/
└── modules/
└── FindCustomPackage.cmake
Writing Custom Find Modules
When a library ships neither a CMake config package nor a bundled module, you can write your
own FindXxx.cmake that locates its headers and libraries and exposes a clean imported target. That
is a topic in itself — the full walkthrough (find_path/find_library,
find_package_handle_standard_args, version extraction, imported targets) lives on the
Find Modules page. From a consumer's side you only point CMake at
the module directory:
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules")
find_package(MyLib REQUIRED) # picks up cmake/modules/FindMyLib.cmake
target_link_libraries(app PRIVATE MyLib::MyLib)
Practical Examples
Multi-Package Application
cmake_minimum_required(VERSION 3.15)
project(MultiPackageApp)
# System libraries
find_package(Threads REQUIRED)
find_package(ZLIB REQUIRED)
# Graphics
find_package(OpenGL REQUIRED)
find_package(glfw3 REQUIRED)
# Optional
find_package(OpenCV)
add_executable(app
src/main.cpp
src/graphics.cpp
)
# Required dependencies
target_link_libraries(app PRIVATE
Threads::Threads
ZLIB::ZLIB
OpenGL::GL
glfw
)
# Optional dependency
if(OpenCV_FOUND)
target_compile_definitions(app PRIVATE HAS_OPENCV)
target_link_libraries(app PRIVATE ${OpenCV_LIBS})
endif()
Version-Specific Features
find_package(Boost 1.70 REQUIRED COMPONENTS filesystem)
if(Boost_VERSION VERSION_GREATER_EQUAL "1.75")
message(STATUS "Using Boost 1.75+ features")
target_compile_definitions(app PRIVATE BOOST_NEW_API)
endif()
Platform-Specific Packages
if(WIN32)
find_package(WindowsSDK REQUIRED)
target_link_libraries(app PRIVATE WindowsSDK::Core)
elseif(APPLE)
find_library(COCOA_LIBRARY Cocoa REQUIRED)
target_link_libraries(app PRIVATE ${COCOA_LIBRARY})
elseif(UNIX)
find_package(X11 REQUIRED)
target_link_libraries(app PRIVATE X11::X11)
endif()
Troubleshooting
Package Not Found
find_package(MyPackage)
if(NOT MyPackage_FOUND)
message(STATUS "MyPackage not found. Tried:")
message(STATUS " CMAKE_PREFIX_PATH: ${CMAKE_PREFIX_PATH}")
message(STATUS " CMAKE_MODULE_PATH: ${CMAKE_MODULE_PATH}")
message(STATUS " MyPackage_DIR: ${MyPackage_DIR}")
message(FATAL_ERROR "Please install MyPackage or set MyPackage_DIR")
endif()
-
Wrong package name
find_package(opencv) # Wrongfind_package(OpenCV) # Correct (case-sensitive!) -
Missing components
find_package(Qt5) # No componentsfind_package(Qt5 COMPONENTS Core Widgets) # Specify -
Package not in standard location
cmake -DCMAKE_PREFIX_PATH=/custom/install .. -
Config file not found
cmake -DMyPackage_DIR=/path/to/cmake/config ..
Best Practices
-
Use REQUIRED for mandatory packages
find_package(Threads REQUIRED) -
Prefer imported targets
target_link_libraries(app PRIVATE Package::Component) -
Check version explicitly
find_package(Boost 1.70 REQUIRED) -
Handle optional packages gracefully
find_package(Optional)if(Optional_FOUND)target_link_libraries(app PRIVATE Optional::Optional)endif() -
Provide helpful messages
if(NOT Package_FOUND)message(STATUS "Package not found. Install with:")message(STATUS " Ubuntu: sudo apt install libpackage-dev")message(STATUS " macOS: brew install package")endif()
Quick Reference
# Basic
find_package(Package REQUIRED)
# With version
find_package(Package 2.0 REQUIRED)
# With components
find_package(Package COMPONENTS comp1 comp2 REQUIRED)
# Optional
find_package(Package)
if(Package_FOUND)
# use it
endif()
# Force config mode
find_package(Package CONFIG REQUIRED)
# Specify location
set(Package_DIR /path/to/cmake)
find_package(Package REQUIRED)
# Use imported target (preferred)
target_link_libraries(app PRIVATE Package::Package)
# Old style (avoid)
target_include_directories(app PRIVATE ${Package_INCLUDE_DIRS})
target_link_libraries(app PRIVATE ${Package_LIBRARIES})