🔝 Retour au Sommaire
Objectif : Maîtriser
find_package()— le mécanisme principal de CMake pour localiser des bibliothèques pré-installées sur le système — en comprenant ses deux modes de fonctionnement (Config et Module), les cibles importées qu'il produit, la gestion des versions et des composants, et les techniques de diagnostic quand une bibliothèque n'est pas trouvée.
find_package() répond à une question simple : « Cette bibliothèque est-elle présente sur le système, et si oui, où sont ses headers, ses binaires et ses dépendances ? »
Quand l'appel réussit, CMake crée des cibles importées (imported targets) qui encapsulent toutes ces informations. Vous consommez ensuite ces cibles via target_link_libraries(), exactement comme n'importe quelle autre cible du projet :
find_package(OpenSSL REQUIRED)
target_link_libraries(my_lib PRIVATE OpenSSL::SSL OpenSSL::Crypto) En coulisse, OpenSSL::SSL porte les include directories d'OpenSSL, les flags de compilation nécessaires, et le chemin vers libssl.so ou libssl.a. Le consommateur n'a besoin de connaître aucun de ces détails — la cible importée s'en charge.
CMake utilise deux stratégies distinctes pour localiser une bibliothèque. Comprendre cette distinction est essentiel pour diagnostiquer les problèmes de détection.
Dans ce mode, CMake cherche un fichier de configuration fourni par la bibliothèque elle-même. Ce fichier s'appelle <Package>Config.cmake ou <package>-config.cmake et est installé avec la bibliothèque (typiquement dans lib/cmake/<Package>/).
/usr/local/lib/cmake/spdlog/
├── spdlogConfig.cmake ← Fichier de configuration principal
├── spdlogConfigVersion.cmake ← Vérification de version
└── spdlogTargets.cmake ← Définition des cibles importées
Le fichier Config est le mécanisme le plus fiable car il est écrit par les développeurs de la bibliothèque, qui connaissent exactement la structure de leur installation. Les bibliothèques C++ modernes fournissent presque toujours des fichiers Config quand elles utilisent CMake comme build system.
Comment CMake trouve le fichier Config :
CMake parcourt une liste de chemins de recherche dans un ordre défini. Les principaux, sous Linux :
- Les chemins listés dans
CMAKE_PREFIX_PATH(variable CMake ou variable d'environnement) - Les chemins listés dans
<Package>_DIR(variable CMake) - Les chemins systèmes standards :
/usr,/usr/local,/opt - Les répertoires enregistrés dans le registre système (principalement Windows)
Pour chaque préfixe, CMake cherche dans plusieurs sous-répertoires :
<prefix>/lib/cmake/<Package>/
<prefix>/share/cmake/<Package>/
<prefix>/lib/<arch>/cmake/<Package>/
<prefix>/cmake/
Si aucun fichier Config n'est trouvé, CMake bascule sur le mode Module. Il cherche alors un fichier Find<Package>.cmake — un script de recherche qui localise la bibliothèque par lui-même, en cherchant des headers et des bibliothèques à des emplacements connus.
CMake embarque une collection de modules de recherche pour les bibliothèques courantes (OpenSSL, ZLIB, Threads, Curses, etc.). Ces fichiers se trouvent dans le répertoire d'installation de CMake :
ls /usr/share/cmake-3.31/Modules/Find*.cmake | head
# FindOpenSSL.cmake
# FindZLIB.cmake
# FindThreads.cmake
# FindBoost.cmake
# ...Vous pouvez aussi écrire vos propres modules Find dans le répertoire cmake/ de votre projet (ajouté à CMAKE_MODULE_PATH — voir section 26.1).
Le mode Module est un mécanisme historique, moins précis que le mode Config. Les modules Find doivent deviner la structure d'installation de la bibliothèque, ce qui peut échouer sur des installations non standard. Quand une bibliothèque propose un fichier Config, il est toujours préférable de l'utiliser.
# Forcer le mode Config uniquement (échoue s'il n'y a pas de fichier Config)
find_package(spdlog CONFIG REQUIRED)
# Forcer le mode Module uniquement (échoue s'il n'y a pas de Find*.cmake)
find_package(OpenSSL MODULE REQUIRED)Sans mot-clé, CMake essaie d'abord Config, puis Module. C'est le comportement par défaut et le plus courant.
find_package(<Package> [version] [EXACT] [QUIET] [REQUIRED]
[COMPONENTS comp1 comp2 ...]
[OPTIONAL_COMPONENTS comp3 ...]
[CONFIG|MODULE]
[PATHS path1 path2 ...]
[HINTS hint1 hint2 ...]
)Détaillons les paramètres les plus importants.
# Obligatoire — la configuration échoue si OpenSSL n'est pas trouvé
find_package(OpenSSL REQUIRED)
# Optionnel — la configuration continue, on vérifie manuellement
find_package(ZLIB QUIET)
if(ZLIB_FOUND)
message(STATUS "ZLIB trouvé — compression activée")
target_link_libraries(my_lib PRIVATE ZLIB::ZLIB)
target_compile_definitions(my_lib PRIVATE HAS_ZLIB=1)
else()
message(STATUS "ZLIB absent — compression désactivée")
endif()REQUIRED transforme l'absence de la bibliothèque en erreur fatale. Sans REQUIRED, CMake positionne la variable <Package>_FOUND à TRUE ou FALSE et continue. QUIET supprime les messages d'information (utile pour les dépendances optionnelles où un message « not found » serait trompeur).
# Version minimale : 1.15 ou supérieure
find_package(spdlog 1.15 CONFIG REQUIRED)
# Version exacte : uniquement 3.4.0
find_package(protobuf 3.4.0 EXACT CONFIG REQUIRED)La vérification de version repose sur le fichier <Package>ConfigVersion.cmake (mode Config) ou sur la logique du module Find (mode Module). Si la version installée ne satisfait pas la contrainte, find_package échoue comme si la bibliothèque n'avait pas été trouvée.
Certaines bibliothèques sont organisées en composants — des sous-ensembles indépendants qu'on peut demander individuellement.
# Boost : ne demander que filesystem et system
find_package(Boost 1.84 REQUIRED COMPONENTS filesystem system)
target_link_libraries(my_lib PRIVATE Boost::filesystem Boost::system)
# Qt6 : demander Widgets et Network
find_package(Qt6 REQUIRED COMPONENTS Widgets Network)
target_link_libraries(my_app PRIVATE Qt6::Widgets Qt6::Network) Chaque composant produit sa propre cible importée. Demander uniquement les composants nécessaires réduit les dépendances transitives et accélère la configuration.
OPTIONAL_COMPONENTS permet de demander des composants sans rendre leur absence fatale :
find_package(Boost 1.84 REQUIRED
COMPONENTS filesystem system
OPTIONAL_COMPONENTS json
)
# Boost::filesystem et Boost::system sont garantis
# Boost::json peut être absent — vérifier avant utilisationQuand une bibliothèque est installée dans un emplacement non standard, vous pouvez guider CMake :
find_package(CustomLib REQUIRED
HINTS ${CMAKE_SOURCE_DIR}/vendor/customlib
PATHS /opt/customlib
)HINTS est consulté avant les chemins système — c'est une suggestion prioritaire. PATHS est consulté après — c'est un fallback. En pratique, HINTS est plus couramment utilisé.
L'approche recommandée pour les installations non standard est toutefois de positionner CMAKE_PREFIX_PATH au moment de la configuration, plutôt que d'encoder des chemins dans les CMakeLists.txt :
cmake -B build -G Ninja -DCMAKE_PREFIX_PATH="/opt/customlib;/opt/another_lib"Cela garde les CMakeLists.txt portables et indépendants des chemins locaux.
Le résultat le plus important d'un find_package réussi est la création de cibles importées. La convention de nommage est <Package>::<composant> :
| Package | Cibles importées | Description |
|---|---|---|
| OpenSSL | OpenSSL::SSL, OpenSSL::Crypto |
Bibliothèques SSL et crypto |
| Threads | Threads::Threads |
Support threading portable |
| ZLIB | ZLIB::ZLIB |
Compression zlib |
| GTest | GTest::gtest, GTest::gtest_main, GTest::gmock |
Google Test et Mock |
| Protobuf | protobuf::libprotobuf, protobuf::protoc |
Protocol Buffers |
| spdlog | spdlog::spdlog |
Logging structuré |
| fmt | fmt::fmt |
Formatage (pré-C++23) |
| nlohmann_json | nlohmann_json::nlohmann_json |
JSON header-only |
Ces cibles portent toutes les propriétés nécessaires : include directories (marqués SYSTEM automatiquement), bibliothèques à lier, flags de compilation, et dépendances transitives. C'est pourquoi un simple target_link_libraries() suffit — tout le reste est automatique.
Certains modules Find positionnent aussi des variables en plus des cibles importées, pour la compatibilité avec l'ancien style CMake :
find_package(OpenSSL REQUIRED)
# Cibles importées (modern CMake — à utiliser) :
# OpenSSL::SSL
# OpenSSL::Crypto
# Variables (ancien style — à éviter si possible) :
# OPENSSL_FOUND → TRUE
# OPENSSL_INCLUDE_DIR → /usr/include/openssl
# OPENSSL_LIBRARIES → /usr/lib/x86_64-linux-gnu/libssl.so;...
# OPENSSL_VERSION → 3.2.1Les variables restent utiles pour le diagnostic ou pour des cas où la cible importée n'existe pas (vieux modules Find). Mais pour la consommation, utilisez toujours les cibles importées, jamais les variables :
# ❌ Ancien style — pas de propagation, pas de détection de typo
include_directories(${OPENSSL_INCLUDE_DIR})
target_link_libraries(my_lib ${OPENSSL_LIBRARIES})
# ✅ Modern CMake — propagation complète, typo détectée
target_link_libraries(my_lib PRIVATE OpenSSL::SSL)Voici les dépendances les plus fréquemment rencontrées dans les projets C++ sous Ubuntu, avec la commande d'installation système et l'appel CMake correspondant :
# Installation des bibliothèques système courantes
sudo apt install \
libssl-dev \ # OpenSSL
zlib1g-dev \ # ZLIB
libboost-all-dev \ # Boost
libprotobuf-dev \ # Protobuf
libcurl4-openssl-dev # CURL# Dans CMakeLists.txt
find_package(OpenSSL REQUIRED) # → OpenSSL::SSL, OpenSSL::Crypto
find_package(Threads REQUIRED) # → Threads::Threads (pas de -dev à installer)
find_package(ZLIB REQUIRED) # → ZLIB::ZLIB
find_package(Boost 1.84 REQUIRED # → Boost::filesystem, etc.
COMPONENTS filesystem system)
find_package(Protobuf REQUIRED) # → protobuf::libprotobuf
find_package(CURL REQUIRED) # → CURL::libcurl Le paquet -dev (ex: libssl-dev) est essentiel : il contient les headers et les fichiers CMake/pkg-config nécessaires à la compilation. Le paquet sans -dev (ex: libssl3) ne contient que les .so de runtime, insuffisants pour find_package.
Un find_package qui ne trouve pas la bibliothèque est l'un des problèmes les plus courants en CMake. Voici une méthode de diagnostic systématique.
CMake fournit généralement un message explicite :
CMake Error at CMakeLists.txt:12 (find_package):
Could not find a package configuration file provided by "spdlog"
(requested version 1.15) with any of the following names:
spdlogConfig.cmake
spdlog-config.cmake
Add the installation prefix of "spdlog" to CMAKE_PREFIX_PATH or set
"spdlog_DIR" to a directory containing one of the above files.
Ce message indique trois choses : CMake cherchait un fichier Config (mode Config), il ne l'a pas trouvé, et il vous dit exactement quoi faire pour l'aider.
C'est la cause n°1 sous Ubuntu. Les fichiers CMake sont dans le paquet -dev :
# Vérifier si le paquet de développement est installé
dpkg -l | grep libssl-dev
# Chercher quel paquet fournit un fichier CMake spécifique
apt-file search spdlogConfig.cmake# Avant l'appel find_package
set(CMAKE_FIND_DEBUG_MODE TRUE)
find_package(spdlog REQUIRED)
set(CMAKE_FIND_DEBUG_MODE FALSE) Ou depuis la ligne de commande, pour tout le projet :
cmake -B build -G Ninja --debug-findCMake affichera alors chaque répertoire parcouru, chaque fichier cherché, et la raison de l'échec. La sortie est verbeuse mais exhaustive — elle montre exactement où CMake a cherché et ce qu'il a trouvé (ou pas).
Si la bibliothèque est installée dans un emplacement non standard :
# Via CMAKE_PREFIX_PATH (recommandé)
cmake -B build -G Ninja -DCMAKE_PREFIX_PATH="/opt/spdlog;/opt/fmt"
# Via la variable spécifique au package
cmake -B build -G Ninja -Dspdlog_DIR=/opt/spdlog/lib/cmake/spdlogCMAKE_PREFIX_PATH est la méthode préférable car elle s'applique à tous les find_package() du projet. <Package>_DIR est utile pour cibler un seul package spécifique.
# Trouver où le fichier Config est installé
find / -name "spdlogConfig.cmake" 2>/dev/null
# Ou plus ciblé
find /usr /opt -name "*Config.cmake" -path "*/spdlog/*" 2>/dev/nullCertaines bibliothèques C (ou anciennes bibliothèques C++) ne fournissent ni fichier Config CMake, ni module Find dans CMake. Dans ce cas, vous pouvez écrire votre propre module de recherche.
Le fichier se place dans le répertoire cmake/ de votre projet (ajouté à CMAKE_MODULE_PATH — voir section 26.1) et suit la convention Find<Package>.cmake :
# cmake/FindLibUV.cmake
# Module de recherche pour libuv (https://libuv.org)
find_path(LIBUV_INCLUDE_DIR
NAMES uv.h
PATHS /usr/include /usr/local/include
)
find_library(LIBUV_LIBRARY
NAMES uv uv1
PATHS /usr/lib /usr/local/lib
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(LibUV
REQUIRED_VARS LIBUV_LIBRARY LIBUV_INCLUDE_DIR
)
# Créer la cible importée (modern CMake)
if(LibUV_FOUND AND NOT TARGET LibUV::LibUV)
add_library(LibUV::LibUV UNKNOWN IMPORTED)
set_target_properties(LibUV::LibUV PROPERTIES
IMPORTED_LOCATION "${LIBUV_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${LIBUV_INCLUDE_DIR}"
)
endif()Les points clés de ce module :
find_path()cherche un header caractéristique de la bibliothèque dans les chemins courants ;find_library()cherche le fichier bibliothèque (.soou.a) ;find_package_handle_standard_args()est un helper CMake qui gère la logiqueREQUIRED/QUIET/version et positionne<Package>_FOUND;- la création d'une cible importée
LibUV::LibUVest essentielle pour que les consommateurs puissent utiliser la bibliothèque viatarget_link_libraries()de manière moderne.
Ce module s'utilise ensuite de manière transparente :
find_package(LibUV REQUIRED)
target_link_libraries(my_lib PRIVATE LibUV::LibUV) 💡 Conseil : avant d'écrire votre propre module Find, vérifiez si la bibliothèque supporte
pkg-config. Si c'est le cas, le moduleFindPkgConfigde CMake peut la localiser automatiquement :find_package(PkgConfig REQUIRED) pkg_check_modules(LIBUV REQUIRED IMPORTED_TARGET libuv) target_link_libraries(my_lib PRIVATE PkgConfig::LIBUV)C'est souvent plus simple et plus robuste qu'un module Find manuel.
Quand vous utilisez un gestionnaire de paquets, find_package() reste la commande de consommation — mais le gestionnaire prépare le terrain en amont.
Conan génère des fichiers toolchain et de configuration CMake. Le workflow typique :
# 1. Conan installe les dépendances et génère les fichiers CMake
conan install . --output-folder=build --build=missing
# 2. CMake utilise le toolchain Conan et trouve les packages
cmake -B build -G Ninja \
-DCMAKE_TOOLCHAIN_FILE=build/conan_toolchain.cmake \
-DCMAKE_BUILD_TYPE=ReleaseLe conan_toolchain.cmake configure CMAKE_PREFIX_PATH pour que vos find_package() trouvent les bibliothèques installées par Conan. Aucune modification de vos CMakeLists.txt n'est nécessaire.
vcpkg fonctionne de manière similaire, via son propre toolchain :
cmake -B build -G Ninja \
-DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmakeLà encore, vos find_package() fonctionnent tels quels — vcpkg injecte les bons chemins de recherche.
L'intérêt de cette architecture est que vos CMakeLists.txt restent agnostiques vis-à-vis du gestionnaire de paquets. Un utilisateur peut consommer votre projet avec Conan, vcpkg, des paquets système (apt), ou une installation manuelle — les mêmes find_package() fonctionnent dans tous les cas.
| Aspect | Recommandation |
|---|---|
| Mode | Préférer Config, fallback Module |
| Consommation | Toujours via les cibles importées (Package::Component), jamais via les variables |
| Dépendances critiques | REQUIRED — échec immédiat si absent |
| Dépendances optionnelles | QUIET + test de <Package>_FOUND |
| Version | Toujours spécifier une version minimale pour les dépendances critiques |
| Emplacement non standard | CMAKE_PREFIX_PATH (global) ou <Package>_DIR (ciblé) |
| Diagnostic | --debug-find ou CMAKE_FIND_DEBUG_MODE |
| Bibliothèque sans Config ni Find | Écrire un Find<Package>.cmake ou utiliser pkg-config |
À suivre : La sous-section 26.3.2 couvre
FetchContent— le mécanisme de CMake pour télécharger et intégrer automatiquement des dépendances au moment de la configuration, sans installation préalable.
⏭️ FetchContent