C++ CMake find_package Complete Guide | Integrating External Libraries

C++ CMake find_package Complete Guide | Integrating External Libraries

이 글의 핵심

CMake find_package: locate Boost, OpenSSL, Qt with imported targets, CONFIG vs Module mode, and custom Find*.cmake—dependency integration for C++ builds.

What is find_package? Why is it needed?

Problem Scenario: The External Library Linking Nightmare

The Problem: You want to use the Boost library. The header path is /usr/include/boost, and the library is located at /usr/lib/libboost_filesystem.so. However, on macOS, it’s installed via Homebrew at /opt/homebrew/include, and on Windows, it’s in C:\boost. Hardcoding paths for each platform makes maintenance impossible.

The Solution: find_package automatically locates libraries installed on the system and provides header paths, library paths, and version information. By simply using find_package(Boost REQUIRED), CMake finds the library and creates targets like Boost::filesystem for you.

flowchart LR
    subgraph find["find_package(Boost)"]
        search["Search system paths"]
    end
    subgraph system["System"]
        linux["/usr/lib/libboost*.so"]
        mac["/opt/homebrew/lib"]
        win["C:/boost/lib"]
    end
    subgraph result["Result"]
        target["Creates Boost::filesystem target"]
        vars["Boost_FOUND, Boost_VERSION variables"]
    end
    search --> linux
    search --> mac
    search --> win
    linux --> target
    mac --> target
    win --> target
    search --> vars

Table of Contents

  1. Basic Usage
  2. Examples for Common Libraries
  3. Config vs Module Mode
  4. Writing a Custom FindModule
  5. Common Issues and Solutions
  6. Production Patterns
  7. Complete Example: Integrating Multiple Libraries

1. Basic Usage

Required Packages

find_package(Boost REQUIRED)

if(Boost_FOUND)
    message(STATUS "Boost found: ${Boost_VERSION}")
else()
    message(FATAL_ERROR "Boost not found")
endif()

Optional Packages

find_package(SomeLib)

if(SomeLib_FOUND)
    target_link_libraries(myapp PRIVATE SomeLib::SomeLib)
else()
    message(WARNING "SomeLib not found, using fallback")
endif()

Specifying Versions

# Minimum version
find_package(Boost 1.70 REQUIRED)

# Exact version
find_package(Boost 1.75 EXACT REQUIRED)

# Version range (CMake 3.19+)
find_package(Boost 1.70...1.80 REQUIRED)

Specifying Components

find_package(Boost REQUIRED COMPONENTS
    filesystem
    system
    thread
)

target_link_libraries(myapp PRIVATE
    Boost::filesystem
    Boost::system
    Boost::thread
)

2. Examples for Common Libraries

Boost

find_package(Boost 1.70 REQUIRED COMPONENTS filesystem system)

add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE
    Boost::filesystem
    Boost::system
)

OpenSSL

find_package(OpenSSL REQUIRED)

add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE
    OpenSSL::SSL
    OpenSSL::Crypto
)

Qt5

find_package(Qt5 REQUIRED COMPONENTS Core Widgets)

add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE
    Qt5::Core
    Qt5::Widgets
)

# Automatically run Qt MOC
set_target_properties(myapp PROPERTIES
    AUTOMOC ON
    AUTOUIC ON
    AUTORCC ON
)

Google Test

find_package(GTest REQUIRED)

add_executable(tests test.cpp)
target_link_libraries(tests PRIVATE
    GTest::gtest
    GTest::gtest_main
)

OpenCV

find_package(OpenCV REQUIRED)

add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE ${OpenCV_LIBS})
target_include_directories(myapp PRIVATE ${OpenCV_INCLUDE_DIRS})

Using pkg-config

find_package(PkgConfig REQUIRED)

pkg_check_modules(CURL REQUIRED libcurl)
pkg_check_modules(SQLITE REQUIRED sqlite3)

add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE
    ${CURL_LIBRARIES}
    ${SQLITE_LIBRARIES}
)
target_include_directories(myapp PRIVATE
    ${CURL_INCLUDE_DIRS}
    ${SQLITE_INCLUDE_DIRS}
)

3. Config vs Module Mode

This mode is used when a library provides a SomeLibConfig.cmake or somelib-config.cmake file. Most modern libraries provide Config files.

find_package(fmt REQUIRED)
# If fmt provides fmtConfig.cmake, it will be found automatically
target_link_libraries(myapp PRIVATE fmt::fmt)

Search Paths:

  • <prefix>/lib/cmake/<name>/
  • <prefix>/share/<name>/
  • Paths specified in CMAKE_PREFIX_PATH

Module Mode

This mode uses CMake’s built-in Find<Name>.cmake module.

find_package(CURL REQUIRED)
# Uses CMake's FindCURL.cmake module
target_link_libraries(myapp PRIVATE CURL::libcurl)

Search Paths:

  • CMAKE_MODULE_PATH
  • Built-in CMake module paths

Choosing a Mode

# Config mode only
find_package(SomeLib CONFIG REQUIRED)

# Module mode only
find_package(SomeLib MODULE REQUIRED)

# Automatic (Config first, fallback to Module)
find_package(SomeLib REQUIRED)

4. Writing a Custom FindModule

Example: FindMyLib.cmake

# cmake/FindMyLib.cmake

# Find headers
find_path(MyLib_INCLUDE_DIR
    NAMES mylib/mylib.h
    PATHS /usr/include /usr/local/include
)

# Find libraries
find_library(MyLib_LIBRARY
    NAMES mylib
    PATHS /usr/lib /usr/local/lib
)

# Check version (optional)
if(MyLib_INCLUDE_DIR)
    file(READ "${MyLib_INCLUDE_DIR}/mylib/version.h" VERSION_CONTENT)
    string(REGEX MATCH "MYLIB_VERSION \"([0-9.]+)\"" _ "${VERSION_CONTENT}")
    set(MyLib_VERSION ${CMAKE_MATCH_1})
endif()

# Handle results
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(MyLib
    REQUIRED_VARS MyLib_LIBRARY MyLib_INCLUDE_DIR
    VERSION_VAR MyLib_VERSION
)

# Create target
if(MyLib_FOUND AND NOT TARGET MyLib::MyLib)
    add_library(MyLib::MyLib UNKNOWN IMPORTED)
    set_target_properties(MyLib::MyLib PROPERTIES
        IMPORTED_LOCATION "${MyLib_LIBRARY}"
        INTERFACE_INCLUDE_DIRECTORIES "${MyLib_INCLUDE_DIR}"
    )
endif()

Usage

# CMakeLists.txt
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")

find_package(MyLib REQUIRED)

add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE MyLib::MyLib)

5. Common Issues and Solutions

Issue 1: Could not find package

Symptom: CMake Error: Could not find a package configuration file provided by "SomeLib".

Cause: The library is not installed or is located in an unknown path.

# Solution 1: Install the package
sudo apt install libboost-dev  # Linux
brew install boost             # macOS
vcpkg install boost            # vcpkg

# Solution 2: Specify CMAKE_PREFIX_PATH
cmake -DCMAKE_PREFIX_PATH=/custom/install/path ..

# Solution 3: Use environment variables
export CMAKE_PREFIX_PATH=/usr/local:$CMAKE_PREFIX_PATH
cmake ..

# Solution 4: Use package-specific hint variables
cmake -DBoost_ROOT=/usr/local/boost ..

Issue 2: Version Mismatch

Symptom: Could not find a configuration file for package "Boost" that is compatible with requested version "1.80".

Cause: Installed version is lower than the required version.

# Solution 1: Relax version requirements
find_package(Boost 1.70 REQUIRED)  # 1.80 → 1.70

# Solution 2: Check version and conditionally proceed
find_package(Boost 1.70)
if(Boost_VERSION VERSION_LESS 1.80)
    message(WARNING "Boost 1.80+ recommended, found ${Boost_VERSION}")
endif()

Issue 3: Missing Components

Symptom: Unable to find the requested Boost libraries. Boost component "filesystem" not found.

Cause: The required component is not installed.

# Linux
sudo apt install libboost-filesystem-dev

# macOS
brew install boost

# Windows (vcpkg)
vcpkg install boost-filesystem

Issue 4: Config vs Module Confusion

Symptom: find_package succeeds, but no target is created.

Cause: Found the package in Module mode, but variable names differ.

# Module mode (FindCURL.cmake)
find_package(CURL REQUIRED)
# Variables: CURL_INCLUDE_DIRS, CURL_LIBRARIES
target_include_directories(myapp PRIVATE ${CURL_INCLUDE_DIRS})
target_link_libraries(myapp PRIVATE ${CURL_LIBRARIES})

# Config mode (CURLConfig.cmake)
find_package(CURL REQUIRED)
# Target: CURL::libcurl
target_link_libraries(myapp PRIVATE CURL::libcurl)

Issue 5: Multiple Versions Installed

Symptom: The wrong version is selected.

# Specify the path to the desired version
cmake -DBoost_ROOT=/usr/local/boost-1.80 ..

# Or add priority paths to CMAKE_PREFIX_PATH
cmake -DCMAKE_PREFIX_PATH="/usr/local/boost-1.80;/usr/local" ..

6. Production Patterns

Pattern 1: Optional Dependencies

option(USE_OPENSSL "Use OpenSSL for encryption" ON)

if(USE_OPENSSL)
    find_package(OpenSSL)
    if(OpenSSL_FOUND)
        target_link_libraries(myapp PRIVATE OpenSSL::SSL)
        target_compile_definitions(myapp PRIVATE USE_OPENSSL)
    else()
        message(WARNING "OpenSSL not found, using fallback")
    endif()
endif()

Pattern 2: Version Check and Feature Branching

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(myapp PRIVATE BOOST_NEW_API)
endif()

Pattern 3: Choosing Between Multiple Packages

# Prefer OpenSSL, fallback to mbedTLS
find_package(OpenSSL)
if(OpenSSL_FOUND)
    target_link_libraries(myapp PRIVATE OpenSSL::SSL)
    target_compile_definitions(myapp PRIVATE USE_OPENSSL)
else()
    find_package(mbedTLS REQUIRED)
    target_link_libraries(myapp PRIVATE mbedTLS::mbedtls)
    target_compile_definitions(myapp PRIVATE USE_MBEDTLS)
endif()

Pattern 4: Printing Package Information

find_package(Boost REQUIRED COMPONENTS filesystem)

message(STATUS "Boost version: ${Boost_VERSION}")
message(STATUS "Boost include: ${Boost_INCLUDE_DIRS}")
message(STATUS "Boost libraries: ${Boost_LIBRARIES}")

Pattern 5: Custom Search Paths

# Add local FindModule path for the project
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")

# Add custom prefix paths
list(APPEND CMAKE_PREFIX_PATH "/opt/mylibs")

find_package(MyLib REQUIRED)

7. Complete Example: Integrating Multiple Libraries

Project: HTTP Server (Boost + OpenSSL + spdlog)

cmake_minimum_required(VERSION 3.20)
project(HttpServer VERSION 1.0.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Required packages
find_package(Boost 1.70 REQUIRED COMPONENTS
    system
    thread
    filesystem
)

find_package(OpenSSL REQUIRED)

# Optional packages
find_package(spdlog)

# Executable
add_executable(http_server
    src/main.cpp
    src/server.cpp
    src/request_handler.cpp
)

# Linking
target_link_libraries(http_server PRIVATE
    Boost::system
    Boost::thread
    Boost::filesystem
    OpenSSL::SSL
    OpenSSL::Crypto
)

# Use spdlog if available
if(spdlog_FOUND)
    target_link_libraries(http_server PRIVATE spdlog::spdlog)
    target_compile_definitions(http_server PRIVATE USE_SPDLOG)
else()
    message(WARNING "spdlog not found, using std::cout")
endif()

# Include directories
target_include_directories(http_server PRIVATE
    ${CMAKE_SOURCE_DIR}/include
)

main.cpp

#include <boost/asio.hpp>
#include <openssl/ssl.h>

#ifdef USE_SPDLOG
#include <spdlog/spdlog.h>
#define LOG(msg) spdlog::info(msg)
#else
#include <iostream>
#define LOG(msg) std::cout << msg << '\n'
#endif

int main() {
    LOG("HTTP Server starting...");
    
    boost::asio::io_context io;
    // Server logic...
    
    return 0;
}

Variables for Each Package

PackageTargetVariables (Module Mode)
BoostBoost::filesystemBoost_FOUND, Boost_INCLUDE_DIRS, Boost_LIBRARIES
OpenSSLOpenSSL::SSL, OpenSSL::CryptoOPENSSL_FOUND, OPENSSL_INCLUDE_DIR, OPENSSL_LIBRARIES
CURLCURL::libcurlCURL_FOUND, CURL_INCLUDE_DIRS, CURL_LIBRARIES
ZLIBZLIB::ZLIBZLIB_FOUND, ZLIB_INCLUDE_DIRS, ZLIB_LIBRARIES
GTestGTest::gtest, GTest::gtest_mainGTEST_FOUND, GTEST_INCLUDE_DIRS, GTEST_LIBRARIES

Recommendation: Always use targets (e.g., Boost::filesystem) when available. They are type-safe and automatically propagate PUBLIC/PRIVATE properties.


Summary

ConceptDescription
find_packageAutomatically finds external libraries
REQUIREDMarks a package as mandatory (errors if not found)
COMPONENTSSpecifies library components
Config ModeUses <Name>Config.cmake (recommended)
Module ModeUses Find<Name>.cmake
TargetsUse Boost::filesystem style (recommended)
VariablesUse ${Boost_LIBRARIES} style (legacy)

find_package is a core feature of CMake that allows you to find and link external libraries in a platform-independent way.


FAQ

Q1: find_package vs pkg-config?

A: find_package is the native CMake method for finding Config/Module files, while pkg-config is a Unix-specific tool for reading .pc files. Most modern libraries provide CMake Config files, so find_package is recommended.

Q2: Targets vs Variables?

A: Use targets (e.g., Boost::filesystem) whenever possible. They are more robust and automatically handle PUBLIC/PRIVATE propagation. Config mode typically provides targets, while Module mode often provides variables.

Q3: What happens if I omit REQUIRED?

A: If the package is not found, no error is raised, but the <Name>_FOUND variable is set to FALSE. This is useful for optional dependencies.

Q4: What is CMAKE_PREFIX_PATH?

A: It is an additional search path for CMake to find packages. Use it to locate libraries installed in non-standard locations. Separate multiple paths with a semicolon: -DCMAKE_PREFIX_PATH="/opt/lib1;/opt/lib2".

Q5: What if find_package fails?

A:

  1. Check if the library is installed (apt list --installed, brew list).
  2. Add the installation path to CMAKE_PREFIX_PATH.
  3. Use package-specific hint variables (Boost_ROOT, Qt5_DIR, etc.).
  4. Enable debug mode with CMAKE_FIND_DEBUG_MODE=ON to trace the search process.

Q6: Where can I learn more about find_package?

A:

TL;DR: Use find_package to automatically locate and link external libraries. Next, consider reading about Conan Package Manager for more advanced dependency management.