Введение
CMake стал стандартом сборки для проектов на C++ и Qt. Qt 6 сам собирается с его помощью, а Qt Creator создаёт новые проекты на CMake по умолчанию. Но синтаксис CMake поначалу кажется непривычным. В этой статье собраны полезные команды CMake и встроенные переменные, которые нужны начинающим в первую очередь: от вывода сообщений и работы с переменными до подключения библиотек и организации проекта.
Минимальный CMakeLists.txt
Начнём с самого простого проекта из одного файла:
cmake_minimum_required(VERSION 3.16)
project(hello VERSION 1.0 LANGUAGES CXX)
add_executable(hello main.cpp)
Команда cmake_minimum_required() задаёт минимальную версию CMake. project() объявляет имя проекта, версию и языки. add_executable() создаёт исполняемый файл из перечисленных исходников. Для библиотеки используется add_library(name STATIC|SHARED файлы).
Сборка проекта из командной строки
cmake -S . -B build # конфигурация
cmake --build build # сборка
cmake --build build --config Release # для MSVC и Xcode
Флаг -S указывает каталог с исходниками, -B — каталог сборки. Всегда собирайте проект в отдельной папке, чтобы не смешивать сгенерированные файлы с исходным кодом.
Полезные команды CMake
message() — вывод сообщений
Аналог printf для отладки скриптов сборки:
message(STATUS "Это информационное сообщение.")
message(WARNING "Это предупреждение!")
message(FATAL_ERROR "Это ошибка, которая остановит конфигурацию.")
set() — переменные
set(MY_VARIABLE "Hello") # создать или изменить переменную
set(SOURCES src/main.cpp src/file1.cpp) # создать список
message(STATUS "${MY_VARIABLE}") # вывести значение
Значение переменной получают через ${ИМЯ}. Все переменные в CMake — строки, а списки хранятся как строки с разделителем «;».
option() — параметры сборки
Команда option() создаёт логический параметр, который пользователь может изменить при конфигурации:
option(BUILD_TESTS "Собирать тесты" ON)
if(BUILD_TESTS)
add_subdirectory(tests)
endif()
Значение задаётся из командной строки: cmake -B build -DBUILD_TESTS=OFF.
list() — работа со списками
list(APPEND SOURCES src/file2.cpp) # добавить элемент
list(REMOVE_ITEM SOURCES src/old.cpp) # удалить элемент
list(LENGTH SOURCES count) # получить длину
message(STATUS "Файлов: ${count}")
if() / elseif() / else() / endif() — условия
if(WIN32)
message(STATUS "Это Windows.")
elseif(APPLE)
message(STATUS "Это macOS.")
elseif(UNIX)
message(STATUS "Это Linux или другой Unix.")
endif()
В условиях также работают проверки DEFINED VAR, EXISTS путь, TARGET имя и сравнения STREQUAL, VERSION_GREATER.
foreach() — циклы
foreach(src IN LISTS SOURCES)
message(STATUS "Исходник: ${src}")
endforeach()
Полезные встроенные переменные
CMAKE_BUILD_TYPE— тип сборки: Debug, Release, RelWithDebInfo, MinSizeRel. По умолчанию не задан. Задаётся так:cmake -B build -DCMAKE_BUILD_TYPE=Debug.CMAKE_CXX_STANDARD— стандарт C++ (11, 14, 17, 20, 23).PROJECT_NAME— имя текущего проекта.PROJECT_SOURCE_DIR— корень исходников текущего проекта.CMAKE_SOURCE_DIR— корень самого верхнего проекта,CMAKE_CURRENT_SOURCE_DIR— каталог текущего CMakeLists.txt.CMAKE_BINARY_DIRиPROJECT_BINARY_DIR— каталог сборки.CMAKE_CURRENT_LIST_DIR— каталог файла, который выполняется сейчас. Удобно во включаемых .cmake-файлах.
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) # не откатываться на старый стандарт
Подключение библиотек
Внешние библиотеки ищутся командой find_package(), а подключаются через target_link_libraries(). Так подключается Qt:
find_package(Qt6 REQUIRED COMPONENTS Widgets)
target_link_libraries(hello PRIVATE Qt6::Widgets)
Ключевые слова видимости определяют, передаются ли настройки дальше: PRIVATE — только для этой цели, PUBLIC — для неё и всех, кто её подключает, INTERFACE — только для подключающих.
Советы по организации проекта
- Одна папка — один CMakeLists.txt. В корневом файле подключайте подкаталоги командой
add_subdirectory(subfolder). - Используйте современный target-based подход. Вместо глобальных
include_directories()иlink_libraries()применяйте командыtarget_*, они меняют настройки только одной цели. - Перечисляйте исходники явно.
file(GLOB ...)не замечает новые файлы без повторной конфигурации. - Всегда собирайте в отдельной папке build и не добавляйте её в систему контроля версий.
target_include_directories(my_target PUBLIC include)
target_compile_features(my_target PRIVATE cxx_std_17)
target_link_libraries(my_target PRIVATE external_lib)
Заключение
Этих команд и переменных достаточно, чтобы уверенно читать и писать CMake-скрипты для учебных и небольших проектов: объявить цель, задать стандарт C++, добавить параметры сборки, подключить Qt и другие библиотеки. Для углублённого изучения смотрите официальную документацию CMake и CMakeLists.txt крупных открытых проектов.