Введение
Раньше подключение сторонней библиотеки в C++-проект означало либо ручную установку через системный пакетный менеджер, либо копирование исходников в репозиторий (vendoring), либо submodule Git с дополнительной настройкой сборки. Модуль CMake FetchContent решает эту задачу декларативно: указываете источник (Git-репозиторий, архив, локальный путь), и CMake сам скачивает, настраивает и подключает библиотеку как часть сборки текущего проекта. В статье разберём, как подключить стороннюю библиотеку (на примере nlohmann/json) в Qt-проект через FetchContent.
Концепция
FetchContent_Declare() описывает, откуда брать зависимость (обычно через GIT_REPOSITORY и GIT_TAG для фиксации конкретной версии), а FetchContent_MakeAvailable() фактически скачивает её (если ещё не скачана) и добавляет в сборку через add_subdirectory() — после этого таргеты библиотеки доступны для target_link_libraries() так же, как если бы они были найдены через find_package().
Преимущество FetchContent перед git submodule — версия зависимости фиксируется прямо в CMakeLists.txt, а не требует отдельной команды git submodule update, и весь процесс воспроизводим «из коробки» при первой настройке проекта. Минус — повторная загрузка при каждой чистой сборке (если не настроено кеширование), что замедляет CI без дополнительных мер.
Пример кода: Подключение сторонних библиотек
cmake_minimum_required(VERSION 3.21)
project(MyApp LANGUAGES CXX)
include(FetchContent)
FetchContent_Declare(
nlohmann_json
GIT_REPOSITORY https://github.com/nlohmann/json.git
GIT_TAG v3.11.3
)
FetchContent_Declare(
fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
GIT_TAG 10.2.1
)
FetchContent_MakeAvailable(nlohmann_json fmt)
find_package(Qt6 REQUIRED COMPONENTS Core)
add_executable(MyApp main.cpp)
target_link_libraries(MyApp PRIVATE
Qt6::Core
nlohmann_json::nlohmann_json
fmt::fmt
)
// main.cpp
#include <QCoreApplication>
#include <nlohmann/json.hpp>
#include <fmt/core.h>
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
nlohmann::json config;
config["name"] = "MyApp";
config["version"] = 1;
fmt::print("Конфигурация: {}\n", config.dump());
return 0;
}
Пояснения к коду
Каждая зависимость объявляется отдельным вызовом FetchContent_Declare() с фиксированной версией через GIT_TAG — это важно для воспроизводимости сборки на разных машинах и в CI. Один вызов FetchContent_MakeAvailable() с несколькими именами одновременно скачивает и подключает обе библиотеки. После этого их таргеты (nlohmann_json::nlohmann_json, fmt::fmt) используются в target_link_libraries точно так же, как таргеты, найденные через find_package.
В коде main.cpp обе библиотеки используются совместно с Qt — FetchContent не накладывает ограничений на совместное использование сторонних библиотек с Qt-классами в одном проекте.
Подводные камни
- Повторная загрузка зависимостей при каждой чистой сборке заметно увеличивает время CI — для ускорения стоит настроить кеширование папки
_depsмежду запусками CI или использоватьFETCHCONTENT_FULLY_DISCONNECTED/локальное зеркало для офлайн-сборок. - Отсутствие фиксации тега (
GIT_TAGуказывает на ветку, напримерmain) делает сборку невоспроизводимой — содержимое зависимости в будущем может измениться без какого-либо изменения в самом проекте, что приводит к трудноуловимым регрессиям. Всегда фиксируйте конкретный тег или коммит. - Конфликт версий одной и той же зависимости, если несколько подключаемых библиотек сами объявляют
FetchContent_Declareдля общей сторонней зависимости с разными версиями — CMake использует первую объявленную версию, что может привести к несовместимости с ожиданиями второй библиотеки. - Долгая первичная настройка проекта из-за необходимости клонировать репозитории зависимостей по сети — для крупных зависимостей (например, с большой историей коммитов) стоит использовать
GIT_SHALLOW TRUEдля поверхностного клонирования без полной истории.