Что такое CMake
CMake — это мета-система сборки (build system generator), которая не компилирует код напрямую, а генерирует файлы для нативных систем сборки (Makefile, Visual Studio проекты, Xcode проекты и т.д.). Основная философия CMake — написать один раз, собирать везде.
CMake использует декларативный язык, где вы описываете что нужно собрать, а не как это делать. Это делает скрипты кроссплатформенными и легко поддерживаемыми.
Базовые команды проекта
cmake_minimum_required()
Назначение: Указывает минимальную версию CMake.
# Всегда должна быть первой командой в CMakeLists.txt cmake_minimum_required(VERSION 3.10) # Или с политикой FATAL_ERROR cmake_minimum_required(VERSION 3.20 FATAL_ERROR)
Почему это важно: Разные версии CMake имеют разный синтаксис и возможности. Указание версии гарантирует совместимость.
project()
Назначение: Определяет проект и его свойства.
# Базовая форма
project(MyProject)
# С дополнительными параметрами
project(
MyAwesomeApp
VERSION 1.2.3 # Задает версию проекта
DESCRIPTION "My application"
LANGUAGES C CXX # Языки проекта (C, CXX, Fortran, etc.)
HOMEPAGE_URL "https://example.com"
)
# Переменные, создаваемые командой project():
# ${PROJECT_NAME} # Имя проекта
# ${PROJECT_VERSION} # Полная версия
# ${PROJECT_VERSION_MAJOR} # Старшая версия
# ${PROJECT_VERSION_MINOR} # Младшая версия
# ${PROJECT_VERSION_PATCH} # Патч-версия
# ${PROJECT_SOURCE_DIR} # Путь к исходникам
# ${PROJECT_BINARY_DIR} # Путь к папке сборки
add_subdirectory()
Назначение: Добавляет поддиректорию с собственным CMakeLists.txt.
# Простая форма
add_subdirectory(src)
# С указанием бинарной директории
add_subdirectory(external/libpng bin/libpng)
# Условное добавление
if(USE_TESTS)
add_subdirectory(tests)
endif()
Лучшие практики: Разбивайте большие проекты на поддиректории с отдельными CMakeLists.txt.
Команды для создания таргетов
add_executable()
Назначение: Создает исполняемый файл.
# Простая форма
add_executable(myapp main.cpp)
# С несколькими файлами
add_executable(myapp
main.cpp
utils.cpp
gui/mainwindow.cpp
)
# Использование переменных для списка файлов
set(SOURCES
src/main.cpp
src/core.cpp
src/gui.cpp
)
add_executable(${PROJECT_NAME} ${SOURCES})
# Интерфейсная библиотека (CMake 3.19+)
add_executable(myapp_exe)
target_sources(myapp_exe PRIVATE main.cpp)
add_library()
Назначение: Создает библиотеку.
# Статическая библиотека (.a, .lib)
add_library(mylib STATIC
lib.cpp
lib.hpp
)
# Динамическая/разделяемая библиотека (.so, .dll, .dylib)
add_library(mylib SHARED
lib.cpp
lib.hpp
)
# Объектная библиотека (только объектные файлы)
add_library(myobjects OBJECT obj1.cpp obj2.cpp)
# Интерфейсная библиотека (без исходного кода)
add_library(mylib_interface INTERFACE)
# Импортированная библиотека (внешняя)
add_library(external_lib STATIC IMPORTED)
set_target_properties(external_lib PROPERTIES
IMPORTED_LOCATION "/path/to/libexternal.a"
)
# Библиотека псевдоним
add_library(mylib SHARED lib.cpp)
add_library(mylib::mylib ALIAS mylib) # Для современного использования
Типы библиотек и их применение
| Тип | Файлы | Использование |
|---|---|---|
STATIC | .a (Linux), .lib (Windows) | Включается в исполняемый файл |
SHARED | .so, .dll, .dylib | Загружается во время выполнения |
OBJECT | .o, .obj | Промежуточные объектные файлы |
INTERFACE | Нет файлов | Только свойства и зависимости |
MODULE | .so, .dll | Плагины, загружаемые во время выполнения |
target_sources()
Назначение: Добавляет исходные файлы к существующему таргету.
add_executable(myapp) # Сначала создаем пустой таргет
target_sources(myapp PRIVATE
src/main.cpp
src/utils.cpp
)
# Можно добавлять файлы условно
if(WIN32)
target_sources(myapp PRIVATE src/win32.cpp)
else()
target_sources(myapp PRIVATE src/posix.cpp)
endif()
Ключевые слова:
PRIVATE— только для этого таргетаPUBLIC— для этого таргета и тех, кто его используетINTERFACE— только для тех, кто использует этот таргет
Команды управления файлами и директориями
include_directories() (устаревший, но часто встречается)
Назначение: Добавляет директории для поиска заголовочных файлов.
# Глобальное добавление (влияет на все таргеты)
include_directories(
${PROJECT_SOURCE_DIR}/include
${PROJECT_SOURCE_DIR}/src
)
# Предпочтительнее использовать:
target_include_directories(mylib PUBLIC include)
target_include_directories() (современный подход)
Назначение: Добавляет пути для заголовочных файлов для конкретного таргета.
add_library(mylib lib.cpp)
# PUBLIC - заголовки нужны и пользователям библиотеки
target_include_directories(mylib PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
# PRIVATE - только для сборки библиотеки
target_include_directories(mylib PRIVATE src)
# INTERFACE - только для пользователей библиотеки
target_include_directories(mylib_interface INTERFACE include)
# Пример с генератором выражений
target_include_directories(mylib SYSTEM PUBLIC
$<INSTALL_PREFIX>/include
)
link_directories() (стараться избегать)
# Не рекомендуется
link_directories(/usr/local/lib /opt/libs)
# Вместо этого используйте:
find_library(MY_LIB mylib PATHS /usr/local/lib)
target_link_libraries(myapp ${MY_LIB})
target_link_directories() (CMake 3.13+)
target_link_directories(myapp
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/libs>
$<INSTALL_INTERFACE:lib>
)
target_link_libraries()
Назначение: Связывает таргеты между собой.
# Простая форма
target_link_libraries(myapp mylib)
# С несколькими библиотеками
target_link_libraries(myapp
PRIVATE
mylib1
mylib2::mylib2 # Именованный таргет
${CMAKE_THREAD_LIBS_INIT}
PUBLIC
common_lib # Передается потребителям
INTERFACE
header_only_lib # Только интерфейс
)
# С дополнительными флагами линковки (старый стиль)
target_link_libraries(myapp
-Wl,--as-needed
-lpthread
mylib
)
Команды для работы с файлами
# Поиск файлов
file(GLOB SOURCE_FILES "src/*.cpp")
file(GLOB_RECURSE HEADER_FILES "include/*.h" "src/*.hpp")
# Копирование файлов
configure_file(config.h.in config.h)
file(COPY resources DESTINATION ${CMAKE_BINARY_DIR})
# Чтение/запись файлов
file(READ "${PROJECT_SOURCE_DIR}/VERSION" PROJECT_VERSION)
file(WRITE "${CMAKE_BINARY_DIR}/info.txt" "Built: ${CMAKE_BUILD_TYPE}")
# Операции с файлами
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/generated)
file(REMOVE ${CMAKE_BINARY_DIR}/temp.txt)
# Генерация файлов
file(GENERATE OUTPUT ${CMAKE_BINARY_DIR}/output.txt
CONTENT "Project: ${PROJECT_NAME}\nVersion: ${PROJECT_VERSION}")
Команды настройки компиляции
Флаги компиляции
# Глобальные флаги (не рекомендуется)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
# Для конкретного таргета (предпочтительно)
target_compile_options(myapp PRIVATE
-Wall
-Wextra
-Werror
$<$<CXX_COMPILER_ID:MSVC>:/W4> # Для MSVC
$<$<CXX_COMPILER_ID:GNU,Clang>:-Wpedantic>
)
# Флаги для разных конфигураций
target_compile_options(myapp
PRIVATE
$<$<CONFIG:Debug>:-O0 -g>
$<$<CONFIG:Release>:-O3 -DNDEBUG>
$<$<CONFIG:RelWithDebInfo>:-O2 -g>
)
# Определения препроцессора
target_compile_definitions(myapp
PRIVATE
VERSION=\"${PROJECT_VERSION}\"
HAVE_CONFIG_H
PUBLIC
MYLIB_EXPORTS # Для DLL
INTERFACE
MYLIB_HEADER_ONLY
)
Настройка стандарта языка
# Старый стиль
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# Для конкретного таргета (CMake 3.8+)
target_compile_features(myapp
PUBLIC
cxx_std_17 # Требует C++17
PRIVATE
cxx_constexpr # Конкретные фичи
cxx_auto_type
)
# Разные стандарты для разных таргетов
set_target_properties(app1 PROPERTIES
CXX_STANDARD 14
CXX_STANDARD_REQUIRED ON
)
set_target_properties(app2 PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
)
Настройка свойств таргетов
set_target_properties(myapp PROPERTIES
# Имя выходного файла
OUTPUT_NAME "myapplication"
PREFIX "" # Убрать префикс
SUFFIX "" # Убрать суффикс
# Версия библиотеки
VERSION 1.2.3
SOVERSION 1
# Позиционно-независимый код
POSITION_INDEPENDENT_CODE ON
# Флаги линковки
LINK_FLAGS "-Wl,--export-dynamic"
LINKER_LANGUAGE CXX
# Инсталляционные пути
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
# Особенности компилятора
CXX_VISIBILITY_PRESET hidden
VISIBILITY_INLINES_HIDDEN ON
# Windows специфика
WIN32_EXECUTABLE ON # Приложение Windows (не консольное)
MACOSX_BUNDLE ON # Приложение macOS
)
Автоматические обработчики (для Qt, ресурсов и т.д.)
# Автоматический запуск MOC для Qt
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
# Параллельная обработка
set(CMAKE_AUTOMOC_PARALLEL ON)
# Автоматическая обработка ресурсов
set_source_files_properties(resources.qrc PROPERTIES
AUTORCC ON
)
Команды для поиска зависимостей
find_package()
# Базовая форма
find_package(OpenSSL REQUIRED)
# С компонентами
find_package(Qt6 REQUIRED COMPONENTS Core Widgets Network)
# С опциями
find_package(Boost 1.70 REQUIRED
COMPONENTS filesystem system thread
OPTIONAL_COMPONENTS python
)
# Разные режимы поиска
find_package(OpenCV MODULE) # Искать FindOpenCV.cmake
find_package(OpenCV CONFIG) # Искать OpenCVConfig.cmake
# Переменные после find_package():
# <Package>_FOUND - Найден ли пакет
# <Package>_INCLUDE_DIRS - Директории заголовков
# <Package>_LIBRARIES - Библиотеки
# <Package>_VERSION - Версия пакета
if(OpenSSL_FOUND)
message(STATUS "OpenSSL found: ${OpenSSL_VERSION}")
target_link_libraries(myapp OpenSSL::SSL OpenSSL::Crypto)
endif()
find_library(), find_path(), find_program()
# Поиск библиотеки
find_library(ZLIB_LIBRARY
NAMES z zlib zlib1
PATHS /usr/local/lib /opt/lib
PATH_SUFFIXES lib lib64
REQUIRED
)
# Поиск пути к заголовкам
find_path(ZLIB_INCLUDE_DIR
NAMES zlib.h
PATHS /usr/local/include /opt/include
DOC "Path to zlib headers"
)
# Поиск исполняемого файла
find_program(PYTHON_EXECUTABLE
NAMES python3 python
PATHS /usr/bin /usr/local/bin
REQUIRED
)
# Использование результатов
if(ZLIB_LIBRARY AND ZLIB_INCLUDE_DIR)
add_library(zlib STATIC IMPORTED)
set_target_properties(zlib PROPERTIES
IMPORTED_LOCATION ${ZLIB_LIBRARY}
INTERFACE_INCLUDE_DIRECTORIES ${ZLIB_INCLUDE_DIR}
)
endif()
pkg_check_modules() (для pkg-config)
find_package(PkgConfig REQUIRED)
# Поиск пакета
pkg_check_modules(GTK3 REQUIRED gtk+-3.0)
# Использование
target_include_directories(myapp PUBLIC ${GTK3_INCLUDE_DIRS})
target_link_libraries(myapp ${GTK3_LIBRARIES})
target_compile_options(myapp PRIVATE ${GTK3_CFLAGS_OTHER})
# Статическое связывание
pkg_check_modules(GTK3_STATIC gtk+-3.0 IMPORTED_TARGET STATIC)
if(GTK3_STATIC_FOUND)
target_link_libraries(myapp PkgConfig::GTK3_STATIC)
endif()
Команды управления переменными
set()
# Простая переменная
set(MY_VARIABLE "Hello World")
# Список
set(SOURCE_FILES main.cpp utils.cpp gui.cpp)
# или
set(SOURCE_FILES "main.cpp;utils.cpp;gui.cpp")
# Кэшированная переменная (видна в cmake-gui)
set(USE_FEATURE_X ON CACHE BOOL "Enable feature X")
# Переменная окружения
set(ENV{PATH} "$ENV{PATH}:${CMAKE_BINARY_DIR}/bin")
# Свойства глобальные
set(CMAKE_BUILD_TYPE Debug CACHE STRING "Build type")
# Разные типы кэшированных переменных
set(MY_STRING "default" CACHE STRING "String variable")
set(MY_PATH "/usr/local" CACHE PATH "Path variable")
set(MY_FILE "/etc/config.conf" CACHE FILEPATH "File path")
set(MY_LIST "a;b;c" CACHE STRING "List variable")
# Сброс кэшированных переменных
unset(MY_VARIABLE CACHE)
list() — операции со списками
# Создание списка
list(APPEND SOURCES main.cpp utils.cpp)
list(APPEND INCLUDES include src)
# Удаление элементов
list(REMOVE_ITEM SOURCES deprecated.cpp)
list(REMOVE_DUPLICATES INCLUDES)
# Поиск в списке
list(FIND SOURCES "main.cpp" INDEX)
if(INDEX EQUAL -1)
message(WARNING "main.cpp not found!")
endif()
# Сортировка
list(SORT SOURCES)
# Получение элементов
list(GET SOURCES 0 FIRST_FILE) # Первый элемент
list(LENGTH SOURCES NUM_SOURCES) # Длина списка
# Фильтрация
list(FILTER SOURCES INCLUDE REGEX "\\.cpp$")
list(FILTER SOURCES EXCLUDE REGEX "test_")
# Преобразование в строку
list(JOIN SOURCES ", " SOURCES_STR)
# Подсписок
list(SUBLIST SOURCES 0 3 FIRST_THREE) # Элементы 0,1,2
# Обход списка
foreach(FILE ${SOURCES})
message(STATUS "File: ${FILE}")
endforeach()
option() — пользовательские опции
# Булевы опции
option(BUILD_TESTS "Build tests" ON)
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
option(ENABLE_ASAN "Enable Address Sanitizer" OFF)
# Использование
if(BUILD_TESTS)
add_subdirectory(tests)
endif()
# Зависимые опции
option(USE_OPENGL "Use OpenGL" ON)
if(USE_OPENGL)
option(USE_OPENGL_ES "Use OpenGL ES" OFF)
endif()
string() — операции со строками
# Преобразование регистра
string(TOLOWER "${PROJECT_NAME}" PROJECT_NAME_LOWER)
string(TOUPPER "${PROJECT_NAME}" PROJECT_NAME_UPPER)
# Поиск и замена
string(FIND "${PATH}" "src" INDEX)
string(REPLACE "old" "new" NEW_STRING "${OLD_STRING}")
# Регулярные выражения
string(REGEX MATCH "[0-9]+" VERSION_NUMBER "${FULL_VERSION}")
string(REGEX REPLACE "^v([0-9.]+)$" "\\1" CLEAN_VERSION "${TAG}")
# Подстроки
string(SUBSTRING "${FULL_STRING}" 0 5 PREFIX)
# Длина
string(LENGTH "${MY_STRING}" STR_LEN)
# Конкатенация
string(CONCAT FULL_NAME ${FIRST} " " ${LAST})
# Разделение и соединение
string(JOIN ":" PATH_PARTS ${PATH_LIST})
string(REPLACE ":" ";" PATH_LIST "${PATH_STRING}")
# Удаление пробелов
string(STRIP "${STRING_WITH_SPACES}" CLEAN_STRING)
# Генерация случайной строки
string(RANDOM LENGTH 10 ALPHABET "ABCDEF0123456789" RANDOM_ID)
math() — математические операции
# Основные операции
math(EXPR RESULT "1 + 2 * 3") # RESULT = 7
math(EXPR VERSION_CODE "${MAJOR} << 16 | ${MINOR} << 8 | ${PATCH}")
# Проверка выражений
math(EXPR IS_64BIT "8 * 8")
if(IS_64BIT EQUAL 64)
set(ARCH "x64")
endif()
# Генерация последовательности
set(NUMBERS "")
foreach(I RANGE 1 10)
math(EXPR SQUARE "${I} * ${I}")
list(APPEND NUMBERS ${SQUARE})
endforeach()
Условные операторы и циклы
Условные операторы
# Базовый if
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
message(STATUS "Building for Linux")
endif()
# Полный синтаксис
if(condition1)
# code...
elseif(condition2)
# code...
else()
# code...
endif()
# Логические операторы
if(NOT DISABLE_FEATURE AND (PLATFORM_WIN32 OR PLATFORM_MAC))
# code...
endif()
# Проверка существования
if(DEFINED MY_VARIABLE)
# переменная определена
endif()
if(EXISTS ${FILE_PATH})
# файл существует
endif()
if(IS_DIRECTORY ${DIR_PATH})
# это директория
endif()
if(COMMAND some_function)
# команда/функция существует
endif()
# Сравнение версий
if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.14")
# используем новые возможности
endif()
Циклы
# foreach по списку
foreach(SOURCE_FILE ${SOURCE_FILES})
message(STATUS "Processing: ${SOURCE_FILE}")
# Можно использовать break() или continue()
if(${SOURCE_FILE} MATCHES "test_")
continue()
endif()
endforeach()
# foreach по диапазону
foreach(I RANGE 1 10 2) # от 1 до 10 с шагом 2
message(STATUS "Number: ${I}")
endforeach()
# foreach с ZIP (CMake 3.17+)
foreach(HEADER SOURCE IN ZIP_LISTS HEADERS SOURCES)
message(STATUS "${HEADER} -> ${SOURCE}")
endforeach()
# while цикл
set(COUNTER 0)
while(COUNTER LESS 10)
math(EXPR COUNTER "${COUNTER} + 1")
message(STATUS "Counter: ${COUNTER}")
endwhile()
Генераторные выражения
# Условные выражения для времени генерации
target_compile_options(myapp PRIVATE
$<$<CONFIG:Debug>:-O0 -g3>
$<$<CONFIG:Release>:-O3 -DNDEBUG>
)
# Проверка компилятора
target_compile_definitions(myapp PRIVATE
$<$<CXX_COMPILER_ID:MSVC>:_CRT_SECURE_NO_WARNINGS>
$<$<CXX_COMPILER_ID:GNU>:_GNU_SOURCE>
)
# Проверка платформы
target_link_libraries(myapp PRIVATE
$<$<PLATFORM_ID:Windows>:winmm>
$<$<PLATFORM_ID:Linux>:pthread>
)
# Комбинации условий
target_compile_options(myapp PRIVATE
$<$<AND:$<CONFIG:Debug>,$<CXX_COMPILER_ID:GNU>>:-fsanitize=address>
)
# Получение свойств таргета
add_custom_command(OUTPUT generated.h
COMMAND generator
$<TARGET_FILE:myapp>
$<TARGET_PROPERTY:myapp,OUTPUT_NAME>
)
# Распространенные генераторные выражения:
# $<CONFIG:cfg> - true для конфигурации cfg
# $<PLATFORM_ID:pid> - true для платформы pid
# $<CXX_COMPILER_ID:id> - true для компилятора id
# $<BOOL:value> - преобразование в bool
# $<AND:cond1,cond2> - логическое И
# $<OR:cond1,cond2> - логическое ИЛИ
# $<NOT:cond> - логическое НЕ
# $<TARGET_FILE:target> - полный путь к файлу таргета
# $<TARGET_PROPERTY:target,prop> - свойство таргета
Функции и макросы
function()
# Определение функции
function(print_target_info target_name)
# Аргументы: ARGC - количество, ARGV - все, ARGN - дополнительные
message(STATUS "Target: ${target_name}")
message(STATUS "All args: ${ARGV}")
message(STATUS "Extra args: ${ARGN}")
# Локальные переменные
set(LOCAL_VAR "local" PARENT_SCOPE) # для передачи наружу
# Возврат значения через set PARENT_SCOPE
set(${target_name}_FOUND TRUE PARENT_SCOPE)
endfunction()
# Использование
print_target_info(myapp SOURCES LIBS)
macro()
# Макросы работают как текстовые подстановки
macro(setup_target target type)
if(${type} STREQUAL "EXECUTABLE")
add_executable(${target} ${ARGN})
elseif(${type} STREQUAL "LIBRARY")
add_library(${target} ${ARGN})
endif()
target_compile_features(${target} PRIVATE cxx_std_17)
endmacro()
# Использование
setup_target(myapp EXECUTABLE main.cpp utils.cpp)
setup_target(mylib LIBRARY SHARED lib.cpp)
# Различия function vs macro:
# - В функциях переменные локальны, в макросах - видны снаружи
# - Макросы могут изменять переменные вызывающей области
# - Функции создают новую область видимости
Полезные пользовательские функции
# Функция для добавления тестов
function(add_unit_test test_name)
add_executable(${test_name} ${ARGN})
target_link_libraries(${test_name} gtest_main mylib)
add_test(NAME ${test_name} COMMAND ${test_name})
# Установка свойств теста
set_tests_properties(${test_name} PROPERTIES
TIMEOUT 30
LABELS "unit"
)
endfunction()
# Функция настройки компилятора
function(configure_compiler_warnings target)
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR
CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
target_compile_options(${target} PRIVATE
-Wall
-Wextra
-Wpedantic
-Werror
$<$<CONFIG:Debug>:-Wshadow -Wconversion>
)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
target_compile_options(${target} PRIVATE
/W4
/WX
)
endif()
endfunction()
# Функция для версионирования
function(generate_version_header)
# Генерация заголовочного файла с версией
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/version.h.in
${CMAKE_CURRENT_BINARY_DIR}/version.h
)
# Добавление в таргет
target_sources(${PROJECT_NAME} PRIVATE
${CMAKE_CURRENT_BINARY_DIR}/version.h
)
target_include_directories(${PROJECT_NAME} PRIVATE
${CMAKE_CURRENT_BINARY_DIR}
)
endfunction()
Продвинутые команды и современные практики
add_custom_command()
# Генерация файлов во время сборки
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/generated.cpp
COMMAND python3 ${CMAKE_SOURCE_DIR}/scripts/generate.py
--output ${CMAKE_BINARY_DIR}/generated.cpp
DEPENDS ${CMAKE_SOURCE_DIR}/scripts/generate.py
${CMAKE_SOURCE_DIR}/data/input.txt
COMMENT "Generating source file"
VERBATIM
)
# Пост-обработка (после компиляции)
add_custom_command(TARGET myapp POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
$<TARGET_FILE:myapp>
${CMAKE_BINARY_DIR}/dist/
COMMENT "Copying executable to dist folder"
)
# Предобработка (перед линковкой)
add_custom_command(TARGET mylib PRE_LINK
COMMAND objcopy --add-section .custom=data.bin mylib.o
COMMENT "Adding custom section"
)
add_custom_target()
# Цели, не создающие файлы
add_custom_target(doc
COMMAND doxygen ${CMAKE_SOURCE_DIR}/Doxyfile
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Generating documentation"
)
# Зависимости
add_custom_target(package ALL
DEPENDS myapp mylib doc
COMMAND cpack -G ZIP
)
# Псевдоцели (как make clean)
add_custom_target(clean-all
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}
COMMENT "Removing build directory"
)
Импорт таргетов
# Импорт библиотеки с настройкой
add_library(External::Lib STATIC IMPORTED)
# Разные пути для разных конфигураций
set_target_properties(External::Lib PROPERTIES
IMPORTED_LOCATION_DEBUG "/path/to/lib_debug.a"
IMPORTED_LOCATION_RELEASE "/path/to/lib_release.a"
INTERFACE_INCLUDE_DIRECTORIES "/path/to/include"
INTERFACE_COMPILE_DEFINITIONS "USE_EXTERNAL_LIB"
)
# Использование как обычного таргета
target_link_libraries(myapp PRIVATE External::Lib)
Современные практики (Modern CMake)
# 1. Используйте target_*, а не глобальные команды
# ПЛОХО:
include_directories(include)
add_executable(app main.cpp)
target_link_libraries(app mylib)
# ХОРОШО:
add_executable(app main.cpp)
target_include_directories(app PRIVATE include)
target_link_libraries(app PRIVATE mylib)
# 2. Используйте генераторные выражения для кроссплатформенности
target_compile_options(app PRIVATE
$<$<CXX_COMPILER_ID:GNU,Clang>:-Wall>
$<$<CXX_COMPILER_ID:MSVC>:/W4>
)
# 3. Экспортируйте зависимости правильно
add_library(mylib lib.cpp)
target_include_directories(mylib
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
PRIVATE
src
)
# 4. Используйте псевдонимы для таргетов
add_library(mylib SHARED lib.cpp)
add_library(MyProject::mylib ALIAS mylib)
# 5. Создайте конфигурационный файл
configure_file(config.h.in config.h @ONLY)
target_include_directories(app PRIVATE ${CMAKE_BINARY_DIR})
Команды для установки и упаковки
install()
# Установка таргетов
install(TARGETS myapp mylib
EXPORT MyProjectTargets
RUNTIME DESTINATION bin # Исполняемые файлы
LIBRARY DESTINATION lib # Библиотеки (.so, .dylib)
ARCHIVE DESTINATION lib # Статические библиотеки (.a, .lib)
INCLUDES DESTINATION include # Заголовочные файлы
)
# Установка файлов
install(FILES
README.md
LICENSE
DESTINATION .
)
install(DIRECTORY include/
DESTINATION include
FILES_MATCHING PATTERN "*.h" PATTERN "*.hpp"
)
# Установка экспорта
install(EXPORT MyProjectTargets
FILE MyProjectConfig.cmake
NAMESPACE MyProject::
DESTINATION lib/cmake/MyProject
