Cmake

CMake: полезные команды и переменные для начинающих

обновлено 39 просмотров

Введение

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 крупных открытых проектов.