Введение
В заключительной статье цикла CMake (и текущего общего объёма всего плана статей) собираем итоговый проект, объединяющий все темы цикла — многомодульная структура (с несколькими внутренними библиотеками), интеграция Qt и OpenCV через find_package (статьи 578, и циклы Qt/OpenCV основного плана), тестирование через FetchContent+CTest (статьи 579, 583), и итоговая установка/упаковка через install()/CPack (статьи 582-583).
Концепция
Структура итогового проекта: корневой CMakeLists.txt, подключающий несколько поддиректорий через add_subdirectory — внутреннюю библиотеку обработки изображений (используя OpenCV, статья 561, с правильной видимостью зависимостей, статья 575), Qt-приложение, использующее эту библиотеку и предоставляющее графический интерфейс, и отдельную директорию тестов с FetchContent-подключённым GoogleTest. Корневой файл также настраивает CPack для финальной упаковки, а внутренняя библиотека экспортируется через install(EXPORT ...) для потенциального использования другими, внешними проектами.
Пример кода: Итоговый мини-проект
# CMakeLists.txt — корневой файл, объединяющий всю структуру проекта
cmake_minimum_required(VERSION 3.16)
project(ImageProcessingApp VERSION 1.0.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
option(BUILD_TESTING_SUPPORT "Собирать модульные тесты" ON)
add_subdirectory(libs/image_processing) # внутренняя библиотека — статьи 574-576
add_subdirectory(app) # Qt-приложение, использующее библиотеку
if(BUILD_TESTING_SUPPORT)
enable_testing()
add_subdirectory(tests) # статьи 579, 583
endif()
# CPack — упаковка финального результата, статья 583
set(CPACK_PACKAGE_NAME "ImageProcessingApp")
set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
include(CPack)
# libs/image_processing/CMakeLists.txt — внутренняя библиотека с OpenCV-зависимостью
find_package(OpenCV REQUIRED) # статья 561, 578
add_library(ImageProcessing image_processing.cpp)
target_include_directories(ImageProcessing PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
target_link_libraries(ImageProcessing PUBLIC ${OpenCV_LIBS}) # PUBLIC — OpenCV-типы видны в публичном API библиотеки
target_compile_features(ImageProcessing PUBLIC cxx_std_20)
install(TARGETS ImageProcessing EXPORT ImageProcessingTargets LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)
install(DIRECTORY include/ DESTINATION include)
install(EXPORT ImageProcessingTargets FILE ImageProcessingTargets.cmake
NAMESPACE ImageProcessing:: DESTINATION lib/cmake/ImageProcessing)
# app/CMakeLists.txt — Qt-приложение, потребляющее внутреннюю библиотеку
find_package(Qt6 REQUIRED COMPONENTS Widgets)
qt_standard_project_setup()
qt_add_executable(ImageProcessingApp main.cpp main_window.cpp main_window.h)
target_link_libraries(ImageProcessingApp PRIVATE Qt6::Widgets ImageProcessing)
install(TARGETS ImageProcessingApp RUNTIME DESTINATION bin) # для финального дистрибутива конечному пользователю
# tests/CMakeLists.txt — тесты через FetchContent + CTest
include(FetchContent)
FetchContent_Declare(googletest GIT_REPOSITORY https://github.com/google/googletest.git GIT_TAG v1.14.0)
FetchContent_MakeAvailable(googletest)
add_executable(ImageProcessingTests test_image_processing.cpp)
target_link_libraries(ImageProcessingTests PRIVATE ImageProcessing gtest gtest_main)
include(GoogleTest)
gtest_discover_tests(ImageProcessingTests)
Пояснения к коду
Корневой CMakeLists.txt показывает структуру «оркестратора» — он сам не содержит реальной логики компиляции, лишь подключает три логически независимые поддиректории и настраивает CPack для итоговой упаковки, причём option(BUILD_TESTING_SUPPORT ...) (статья 577) даёт пользователю/CI возможность собирать проект без тестовой инфраструктуры, если она не нужна для конкретного сценария сборки (например, финальная, production-сборка для распространения, где тесты избыточны). libs/image_processing/CMakeLists.txt демонстрирует осознанный выбор PUBLIC для OpenCV-зависимости (статья 575) — поскольку публичный заголовочный файл библиотеки, скорее всего, использует cv::Mat (статья 562 цикла OpenCV) в сигнатурах своих публичных функций, потребители этой библиотеки (включая app/) автоматически получают доступ к необходимым заголовкам и библиотекам OpenCV без отдельного, явного вызова find_package(OpenCV) в app/CMakeLists.txt. Полный комплект install()-команд в библиотеке (бинарный файл, заголовки, экспорт целей) показывает применение всех элементов статьи 582 для реальной готовности библиотеки к использованию внешними проектами, тогда как tests/CMakeLists.txt демонстрирует комбинацию FetchContent (статья 579) и gtest_discover_tests (статья 583) для полноценной, детально интегрированной тестовой инфраструктуры.
Подводные камни
- Несогласованность между
BUILD_INTERFACE/INSTALL_INTERFACEпутей внутренней библиотеки и реальной структурой директорий после установки, обнаруживаемая только при попытке реально установить проект и использовать его как внешнюю зависимость в отдельном, независимом тестовом проекте (статья 582) — для итогового, многомодульного проекта эта проверка особенно важна, поскольку ошибка в путях установки внутренней библиотеки может остаться незамеченной на протяжении всей обычной разработки (где библиотека всегда используется в рамках того же, единого процесса сборки, без реальной, отдельной установки), проявляясь только при попытке полноценного, «настоящего» использования установленного пакета. - Избыточно тесная связанность между модулями проекта, где, например, тестовый модуль или сам Qt-интерфейс приобретают прямые, не предусмотренные архитектурой зависимости от внутренних деталей реализации библиотеки обработки изображений (а не только от её продуманного, явного публичного API) — это нарушает саму цель модульной структуры проекта (изоляция, возможность независимого развития и тестирования каждого модуля), и видимость зависимостей (статья 575) должна явно отражать реальные, продуманные архитектурные границы между логическими частями итогового проекта.
- Недостаточная проверка совместной работы всех частей проекта при выключенном через
option()тестировании — посколькуBUILD_TESTING_SUPPORTпозволяет полностью пропустить сборку и конфигурацию тестового модуля, стоит явно убедиться, что основная функциональность проекта (библиотека и приложение) собираются корректно и в этой, «упрощённой» конфигурации без тестов, особенно если в системе CI (статьи 446-454 DevOps-цикла) тестируется преимущественно одна, конкретная комбинация опций сборки, не обязательно покрывающая все реально поддерживаемые комбинации настроек. - Игнорирование необходимости явного указания версии всех зависимостей (Qt, OpenCV, GoogleTest) в документации проекта или, что предпочтительнее, через автоматизированную проверку в самом
CMakeLists.txt— для долгосрочной поддерживаемости и воспроизводимости сборки итогового, многомодульного проекта явная фиксация (или хотя бы документирование) минимально требуемых и протестированных версий каждой внешней зависимости (аналогично статье 579 про важность фиксацииGIT_TAG) помогает избежать неожиданных проблем совместимости при попытке собрать проект спустя длительное время или на существенно отличающемся от исходного окружении разработки.
Цикл «CMake глубоко» (статьи 573-584) завершён — от философии и минимального примера через цели, видимость зависимостей, свойства, переменные/кеш, find_package, FetchContent, генераторные выражения, кросс-компиляцию, экспорт пакетов, до CTest/CPack и итогового многомодульного проекта. Общий план статей теперь включает 584 темы.