Skip to main content

Finding External Packages

Which dependency mechanism?

CMake offers three, in rough order of preference:

  • find_package() (this page) — use a dependency that's already installed on the system.
  • FetchContentdownload 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:

  1. FindPackage.cmake - in CMAKE_MODULE_PATH
  2. PackageConfig.cmake - in various locations

Config Mode (Preferred)

find_package(MyPackage CONFIG)

Searches for:

  • MyPackageConfig.cmake
  • mypackage-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
Automatic Detection

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})
Use Imported Targets

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:

Using a custom module
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()
Common Issues
  1. Wrong package name

    find_package(opencv) # Wrong
    find_package(OpenCV) # Correct (case-sensitive!)
  2. Missing components

    find_package(Qt5) # No components
    find_package(Qt5 COMPONENTS Core Widgets) # Specify
  3. Package not in standard location

    cmake -DCMAKE_PREFIX_PATH=/custom/install ..
  4. Config file not found

    cmake -DMyPackage_DIR=/path/to/cmake/config ..

Best Practices

Recommendations
  1. Use REQUIRED for mandatory packages

    find_package(Threads REQUIRED)
  2. Prefer imported targets

    target_link_libraries(app PRIVATE Package::Component)
  3. Check version explicitly

    find_package(Boost 1.70 REQUIRED)
  4. Handle optional packages gracefully

    find_package(Optional)
    if(Optional_FOUND)
    target_link_libraries(app PRIVATE Optional::Optional)
    endif()
  5. 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})