Spec-Zone.ru › CMake 3.25

cmake-buildsystem(7)

  • Введение
  • Бинарные цели

    • Бинарные исполняемые файлы
    • Типы бинарных библиотек

      • Обычные библиотеки

        • Фреймворки Apple
      • Библиотеки объектов
  • Спецификация построения и требования к использованию

    • Свойства целей
    • Требования к транзитивному использованию
    • Свойства совместимого интерфейса
    • Отладка происхождения свойств
    • Спецификация построения с выражениями генератора

      • Директории включаемых файлов и требования к использованию
    • Связывание библиотек и выражения генератора
    • Выходные артефакты

      • Выходные артефакты во время выполнения
      • Выходные артефакты библиотек
      • Выходные артефакты архивов
    • Команды, привязанные к директории
  • Конфигурации сборки

    • Чувствительность к регистру
    • По умолчанию и настраиваемые конфигурации
  • Псевдо-цели

    • Импортированные цели
    • Цели-псевдонимы
    • Библиотеки интерфейса

Введение

Система построения на основе CMake организована как набор логических целей высокого уровня. Каждая цель соответствует исполняемому файлу или библиотеке, или является пользовательской целью, содержащей пользовательские команды. Зависимости между целями выражаются в системе построения для определения порядка построения и правил перегенерации в ответ на изменения.

Бинарные цели

Исполняемые файлы и библиотеки определяются с помощью команд add_executable() и add_library(). Результирующие бинарные файлы имеют соответствующие PREFIX, SUFFIX и расширения для целевой платформы. Зависимости между бинарными целями выражаются с помощью команды target_link_libraries():

add_library(archive archive.cpp zip.cpp lzma.cpp)
add_executable(zipapp zipapp.cpp)
target_link_libraries(zipapp archive)

archive определена как STATIC библиотека — архив, содержащий объекты, скомпилированные из archive.cpp, zip.cpp, и lzma.cpp. zipapp определена как исполняемый файл, сформированный путём компиляции и компоновки zipapp.cpp. При компоновке исполняемого файла zipapp подключается статическая библиотека archive.

Бинарные исполняемые файлы

Команда add_executable() определяет исполняемую цель:

add_executable(mytool mytool.cpp)

Команды, такие как add_custom_command(), которые генерируют правила, которые нужно выполнить во время сборки, могут прозрачно использовать цель типа EXECUTABLE как исполняемый файл COMMAND. Правила системы сборки гарантируют, что исполняемый файл будет построен перед попыткой выполнить команду.

Типы бинарных библиотек

Обычные библиотеки

По умолчанию команда add_library() определяет STATIC библиотеку, если не указан тип. Тип может быть указан при использовании команды:

add_library(archive SHARED archive.cpp zip.cpp lzma.cpp)
add_library(archive STATIC archive.cpp zip.cpp lzma.cpp)

Переменная BUILD_SHARED_LIBS может быть включена, чтобы изменить поведение add_library() по умолчанию на построение динамических библиотек.

В контексте определения всей системы сборки, в значительной степени не имеет значения, являются ли конкретные библиотеки SHARED или STATIC — команды, спецификации зависимостей и другие API работают аналогично независимо от типа библиотеки. Тип библиотеки MODULE отличается тем, что обычно не подключается — он не используется в правой части команды target_link_libraries(). Это тип, который загружается как плагин с помощью методов времени выполнения. Если библиотека не экспортирует никаких необработанных символов (например, DLL ресурсов Windows, DLL C++/CLI), требуется, чтобы библиотека не была SHARED библиотекой, поскольку CMake ожидает, что SHARED библиотеки экспортируют по крайней мере один символ.

add_library(archive MODULE 7z.cpp)
Фреймворки Apple

Библиотека SHARED может быть помечена свойством цели FRAMEWORK для создания пакета фреймворка macOS или iOS. Библиотека со свойством цели FRAMEWORK также должна установить свойство цели FRAMEWORK_VERSION. Это свойство обычно устанавливается в значение "A" по умолчанию для macOS. MACOSX_FRAMEWORK_IDENTIFIER устанавливает ключ CFBundleIdentifier, который однозначно идентифицирует пакет.

add_library(MyFramework SHARED MyFramework.cpp)
set_target_properties(MyFramework PROPERTIES
  FRAMEWORK TRUE
  FRAMEWORK_VERSION A # Version "A" is macOS convention
  MACOSX_FRAMEWORK_IDENTIFIER org.cmake.MyFramework
)

Библиотеки объектов

Тип библиотеки OBJECT определяет неархивную коллекцию файлов объектов, полученных в результате компиляции указанных исходных файлов. Коллекция файлов объектов может быть использована в качестве исходных данных для других целей с помощью синтаксиса $<TARGET_OBJECTS:name>. Это generator expression, который может быть использован для предоставления содержимого библиотеки OBJECT другим целям:

add_library(archive OBJECT archive.cpp zip.cpp lzma.cpp)

add_library(archiveExtras STATIC $<TARGET_OBJECTS:archive> extras.cpp)

add_executable(test_exe $<TARGET_OBJECTS:archive> test.cpp)

Этап компоновки (или архивирования) этих других целей будет использовать файлы объектов в дополнение к файлам из их собственных источников.

В качестве альтернативы, библиотеки объектов могут быть подключены к другим целям:

add_library(archive OBJECT archive.cpp zip.cpp lzma.cpp)

add_library(archiveExtras STATIC extras.cpp)
target_link_libraries(archiveExtras PUBLIC archive)

add_executable(test_exe test.cpp)
target_link_libraries(test_exe archive)

Этап компоновки (или архивирования) этих других целей будет использовать файлы объектов из OBJECT библиотек, которые непосредственно подключены. Кроме того, требования к использованию OBJECT библиотек будут соблюдены при компиляции исходных файлов в этих других целях. Кроме того, эти требования к использованию будут транзитивно распространяться на зависимые цели этих других целей.

Библиотеки объектов не могут использоваться в качестве TARGET в использовании команды add_custom_command(TARGET). Однако список объектов может быть использован командой add_custom_command(OUTPUT) или file(GENERATE) с помощью $<TARGET_OBJECTS:objlib>.

Спецификация построения и требования к использованию

Команды target_include_directories(), target_compile_definitions() и target_compile_options() задают спецификации сборки и требования к использованию бинарных целей. Эти команды заполняют свойства цели INCLUDE_DIRECTORIES, COMPILE_DEFINITIONS и COMPILE_OPTIONS соответственно, а также свойства цели INTERFACE_INCLUDE_DIRECTORIES, INTERFACE_COMPILE_DEFINITIONS и INTERFACE_COMPILE_OPTIONS.

Каждая из команд имеет режим PRIVATE, PUBLIC и INTERFACE. Режим PRIVATE заполняет только не-INTERFACE_ вариант свойства цели, а режим INTERFACE заполняет только INTERFACE_ варианты. Режим PUBLIC заполняет оба варианта соответствующего свойства цели. Каждая команда может быть вызвана с множественным использованием каждого ключевого слова:

target_compile_definitions(archive
  PRIVATE BUILDING_WITH_LZMA
  INTERFACE USING_ARCHIVE_LIB
)

Обратите внимание, что требования к использованию не предназначены для удобства использования конкретных значений COMPILE_OPTIONS или COMPILE_DEFINITIONS и т. д. Содержимое свойств должно являться требованиями, а не просто рекомендациями или удобствами.

См. раздел Создание переносимых пакетов руководства cmake-packages(7) для обсуждения дополнительной заботы, которая должна быть предпринята при указании требований к использованию при создании пакетов для перераспределения.

Свойства целей

Содержимое свойств цели INCLUDE_DIRECTORIES, COMPILE_DEFINITIONS и COMPILE_OPTIONS используется при компиляции исходных файлов бинарной цели.

Элементы в INCLUDE_DIRECTORIES добавляются к строке компиляции с префиксами -I или -isystem в порядке их появления в значении свойства.

Элементы в COMPILE_DEFINITIONS имеют префиксы -D или /D и добавляются к строке компиляции в неопределенном порядке. Свойство цели DEFINE_SYMBOL также добавляется как определение компиляции в качестве специального удобства для библиотек SHARED и MODULE.

Элементы в COMPILE_OPTIONS экранируются для оболочки и добавляются в порядке их появления в значении свойства. Несколько опций компиляции имеют специальную обработку, например POSITION_INDEPENDENT_CODE.

Содержимое свойств цели INTERFACE_INCLUDE_DIRECTORIES, INTERFACE_COMPILE_DEFINITIONS и INTERFACE_COMPILE_OPTIONS являются требованиями к использованию — они задают содержимое, которое потребители должны использовать для правильной компиляции и компоновки с целевым объектом, к которому они относятся. Для любой бинарной цели содержимое каждого свойства INTERFACE_ для каждой цели, указанной в команде target_link_libraries(), используется:

set(srcs archive.cpp zip.cpp)
if (LZMA_FOUND)
  list(APPEND srcs lzma.cpp)
endif()
add_library(archive SHARED ${srcs})
if (LZMA_FOUND)
  # The archive library sources are compiled with -DBUILDING_WITH_LZMA
  target_compile_definitions(archive PRIVATE BUILDING_WITH_LZMA)
endif()
target_compile_definitions(archive INTERFACE USING_ARCHIVE_LIB)

add_executable(consumer)
# Link consumer to archive and consume its usage requirements. The consumer
# executable sources are compiled with -DUSING_ARCHIVE_LIB.
target_link_libraries(consumer archive)

Поскольку часто требуется добавить исходный каталог и соответствующий каталог сборки в INCLUDE_DIRECTORIES, переменная CMAKE_INCLUDE_CURRENT_DIR может быть включена для удобного добавления соответствующих каталогов в INCLUDE_DIRECTORIES всех целей. Переменная CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE может быть включена для добавления соответствующих каталогов в INTERFACE_INCLUDE_DIRECTORIES всех целей. Это упрощает использование целей в нескольких разных каталогах с помощью команды target_link_libraries().

Транзитивные требования к использованию

Требования к использованию целевого объекта могут транзитивно распространяться на зависимые объекты. Команда target_link_libraries() имеет ключевые слова PRIVATE, INTERFACE и PUBLIC для управления распространением.

add_library(archive archive.cpp)
target_compile_definitions(archive INTERFACE USING_ARCHIVE_LIB)

add_library(serialization serialization.cpp)
target_compile_definitions(serialization INTERFACE USING_SERIALIZATION_LIB)

add_library(archiveExtras extras.cpp)
target_link_libraries(archiveExtras PUBLIC archive)
target_link_libraries(archiveExtras PRIVATE serialization)
# archiveExtras is compiled with -DUSING_ARCHIVE_LIB
# and -DUSING_SERIALIZATION_LIB

add_executable(consumer consumer.cpp)
# consumer is compiled with -DUSING_ARCHIVE_LIB
target_link_libraries(consumer archiveExtras)

Поскольку archive является PUBLIC зависимостью от archiveExtras, требования к использованию также распространяются на consumer.

Поскольку serialization является PRIVATE зависимостью от archiveExtras, требования к использованию не распространяются на consumer.

Как правило, зависимость должна быть указана в использовании target_link_libraries() с ключевым словом PRIVATE, если она используется только реализацией библиотеки, а не в заголовочных файлах. Если зависимость дополнительно используется в заголовочных файлах библиотеки (например, для наследования классов), то она должна быть указана как PUBLIC зависимость. Зависимость, которая не используется реализацией библиотеки, а только её заголовочными файлами, должна быть указана как INTERFACE зависимость. Команда target_link_libraries() может быть вызвана с множественным использованием каждого ключевого слова:

target_link_libraries(archiveExtras
  PUBLIC archive
  PRIVATE serialization
)

Требования к использованию распространяются путем считывания вариантов целевых свойств из зависимостей INTERFACE_ и добавления значений к вариантам операнда, не являющимся INTERFACE_. Например, INTERFACE_INCLUDE_DIRECTORIES зависимостей считывается и добавляется к INCLUDE_DIRECTORIES операнда. В случаях, когда порядок важен и поддерживается, а порядок, полученный из вызовов target_link_libraries(), не позволяет выполнить корректную компиляцию, использование соответствующей команды для прямого задания свойства может обновить порядок.

Например, если связанные библиотеки для целевого объекта должны быть указаны в порядке lib1 lib2 lib3, но каталоги включения должны быть указаны в порядке lib3 lib1 lib2.

target_link_libraries(myExe lib1 lib2 lib3)
target_include_directories(myExe
  PRIVATE $<TARGET_PROPERTY:lib3,INTERFACE_INCLUDE_DIRECTORIES>)

Обратите внимание, что необходимо соблюдать осторожность при указании требований к использованию для целевых объектов, которые будут экспортированы для установки с помощью команды install(EXPORT). См. раздел Создание пакетов для получения дополнительной информации.

Совместимые свойства интерфейса

Некоторые свойства целевого объекта должны быть совместимы между целевым объектом и интерфейсом каждой зависимости. Например, свойство целевого объекта POSITION_INDEPENDENT_CODE может задавать булево значение, указывающее, должен ли целевой объект компилироваться как код, независимый от позиции, что имеет последствия, зависящие от платформы. Целевой объект также может указать требование к использованию INTERFACE_POSITION_INDEPENDENT_CODE, чтобы указать, что потребители должны компилироваться как код, независимый от позиции.

add_executable(exe1 exe1.cpp)
set_property(TARGET exe1 PROPERTY POSITION_INDEPENDENT_CODE ON)

add_library(lib1 SHARED lib1.cpp)
set_property(TARGET lib1 PROPERTY INTERFACE_POSITION_INDEPENDENT_CODE ON)

add_executable(exe2 exe2.cpp)
target_link_libraries(exe2 lib1)

В данном случае, как exe1, так и exe2 будут скомпилированы как код, независимый от позиции. lib1 также будет скомпилирован как код, независимый от позиции, так как это значение по умолчанию для библиотек SHARED. Если у зависимостей есть конфликтующие несовместимые требования, cmake(1), то выводится диагностическое сообщение:

add_library(lib1 SHARED lib1.cpp)
set_property(TARGET lib1 PROPERTY INTERFACE_POSITION_INDEPENDENT_CODE ON)

add_library(lib2 SHARED lib2.cpp)
set_property(TARGET lib2 PROPERTY INTERFACE_POSITION_INDEPENDENT_CODE OFF)

add_executable(exe1 exe1.cpp)
target_link_libraries(exe1 lib1)
set_property(TARGET exe1 PROPERTY POSITION_INDEPENDENT_CODE OFF)

add_executable(exe2 exe2.cpp)
target_link_libraries(exe2 lib1 lib2)

Требование lib1 INTERFACE_POSITION_INDEPENDENT_CODE не является "совместимым" со свойством POSITION_INDEPENDENT_CODE целевого объекта exe1. Библиотека требует, чтобы потребители были скомпилированы как код, независимый от позиции, в то время как исполняемый файл задает компиляцию не как код, независимый от позиции, поэтому выводится диагностическое сообщение.

Требования lib1 и lib2 не являются "совместимыми". Одно из них требует, чтобы потребители были скомпилированы как код, независимый от позиции, а другое — нет. Так как exe2 ссылается на оба, и они конфликтуют, выдаётся сообщение об ошибке CMake.

CMake Error: The INTERFACE_POSITION_INDEPENDENT_CODE property of "lib2" does
not agree with the value of POSITION_INDEPENDENT_CODE already determined
for "exe2".

Чтобы быть "совместимым", свойство POSITION_INDEPENDENT_CODE, если оно задано, должно быть одинаковым в булевом смысле, со свойством INTERFACE_POSITION_INDEPENDENT_CODE всех зависимостей, на которые это свойство задано транзитивно.

Это свойство "совместимого требования интерфейса" может быть расширено на другие свойства путем указания свойства в содержимом свойства целевого объекта COMPATIBLE_INTERFACE_BOOL. Каждое указанное свойство должно быть совместимо между целевым объектом-потребителем и соответствующим свойством с префиксом INTERFACE_ каждой зависимости.

add_library(lib1Version2 SHARED lib1_v2.cpp)
set_property(TARGET lib1Version2 PROPERTY INTERFACE_CUSTOM_PROP ON)
set_property(TARGET lib1Version2 APPEND PROPERTY
  COMPATIBLE_INTERFACE_BOOL CUSTOM_PROP
)

add_library(lib1Version3 SHARED lib1_v3.cpp)
set_property(TARGET lib1Version3 PROPERTY INTERFACE_CUSTOM_PROP OFF)

add_executable(exe1 exe1.cpp)
target_link_libraries(exe1 lib1Version2) # CUSTOM_PROP will be ON

add_executable(exe2 exe2.cpp)
target_link_libraries(exe2 lib1Version2 lib1Version3) # Diagnostic

Небулевы свойства также могут участвовать в вычислениях "совместимого интерфейса". Свойства, указанные в свойстве COMPATIBLE_INTERFACE_STRING, должны быть либо не заданными, либо сравниваться с той же строкой среди всех транзитивно указанных зависимостей. Это может быть полезно для предотвращения связывания нескольких несовместимых версий библиотеки вместе через транзитивные требования целевого объекта.

add_library(lib1Version2 SHARED lib1_v2.cpp)
set_property(TARGET lib1Version2 PROPERTY INTERFACE_LIB_VERSION 2)
set_property(TARGET lib1Version2 APPEND PROPERTY
  COMPATIBLE_INTERFACE_STRING LIB_VERSION
)

add_library(lib1Version3 SHARED lib1_v3.cpp)
set_property(TARGET lib1Version3 PROPERTY INTERFACE_LIB_VERSION 3)

add_executable(exe1 exe1.cpp)
target_link_libraries(exe1 lib1Version2) # LIB_VERSION will be "2"

add_executable(exe2 exe2.cpp)
target_link_libraries(exe2 lib1Version2 lib1Version3) # Diagnostic

Свойство целевого объекта COMPATIBLE_INTERFACE_NUMBER_MAX указывает, что содержимое будет вычисляться численно, и будет рассчитано максимальное число среди всех указанных:

add_library(lib1Version2 SHARED lib1_v2.cpp)
set_property(TARGET lib1Version2 PROPERTY INTERFACE_CONTAINER_SIZE_REQUIRED 200)
set_property(TARGET lib1Version2 APPEND PROPERTY
  COMPATIBLE_INTERFACE_NUMBER_MAX CONTAINER_SIZE_REQUIRED
)

add_library(lib1Version3 SHARED lib1_v3.cpp)
set_property(TARGET lib1Version3 PROPERTY INTERFACE_CONTAINER_SIZE_REQUIRED 1000)

add_executable(exe1 exe1.cpp)
# CONTAINER_SIZE_REQUIRED will be "200"
target_link_libraries(exe1 lib1Version2)

add_executable(exe2 exe2.cpp)
# CONTAINER_SIZE_REQUIRED will be "1000"
target_link_libraries(exe2 lib1Version2 lib1Version3)

Аналогично, свойство COMPATIBLE_INTERFACE_NUMBER_MIN может использоваться для расчета минимального численного значения свойства от зависимостей.

Каждое рассчитанное значение "совместимого" свойства может быть прочитано в потребителе во время генерации с помощью выражений генератора.

Обратите внимание, что для каждой зависимости набор свойств, указанных в каждом свойстве совместимого интерфейса, не должен пересекаться с набором, указанным в любом другом свойстве.

Отладка происхождения свойства

Поскольку спецификации сборки могут определяться зависимостями, отсутствие локальности кода, который создает целевой объект, и кода, отвечающего за установку спецификаций сборки, может затруднить понимание кода. cmake(1) предоставляет средства отладки для печати происхождения содержимого свойств, которые могут определяться зависимостями. Свойства, которые могут быть отлажены, перечислены в документации переменной CMAKE_DEBUG_TARGET_PROPERTIES.

set(CMAKE_DEBUG_TARGET_PROPERTIES
  INCLUDE_DIRECTORIES
  COMPILE_DEFINITIONS
  POSITION_INDEPENDENT_CODE
  CONTAINER_SIZE_REQUIRED
  LIB_VERSION
)
add_executable(exe1 exe1.cpp)

В случае свойств, перечисленных в COMPATIBLE_INTERFACE_BOOL или COMPATIBLE_INTERFACE_STRING, вывод отладки показывает, какой целевой объект был ответственен за установку свойства, и какие другие зависимости также определяли свойство. В случае COMPATIBLE_INTERFACE_NUMBER_MAX и COMPATIBLE_INTERFACE_NUMBER_MIN, вывод отладки показывает значение свойства от каждой зависимости и определяет, определяет ли это значение новое крайнее значение.

Спецификация сборки с выражениями генератора

Спецификации сборки могут использовать выражения генератора generator expressions, содержащие условное содержимое или известное только во время генерации. Например, рассчитанное "совместимое" значение свойства может быть прочитано с помощью выражения TARGET_PROPERTY.

add_library(lib1Version2 SHARED lib1_v2.cpp)
set_property(TARGET lib1Version2 PROPERTY
  INTERFACE_CONTAINER_SIZE_REQUIRED 200)
set_property(TARGET lib1Version2 APPEND PROPERTY
  COMPATIBLE_INTERFACE_NUMBER_MAX CONTAINER_SIZE_REQUIRED
)

add_executable(exe1 exe1.cpp)
target_link_libraries(exe1 lib1Version2)
target_compile_definitions(exe1 PRIVATE
    CONTAINER_SIZE=$<TARGET_PROPERTY:CONTAINER_SIZE_REQUIRED>
)

В этом случае исходные файлы exe1 будут скомпилированы с -DCONTAINER_SIZE=200.

Унарное выражение генератора TARGET_PROPERTY и выражение генератора TARGET_POLICY оцениваются в контексте целевого объекта-потребителя. Это означает, что спецификация требования к использованию может быть оценена по-разному в зависимости от потребителя:

add_library(lib1 lib1.cpp)
target_compile_definitions(lib1 INTERFACE
  $<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,EXECUTABLE>:LIB1_WITH_EXE>
  $<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,SHARED_LIBRARY>:LIB1_WITH_SHARED_LIB>
  $<$<TARGET_POLICY:CMP0041>:CONSUMER_CMP0041_NEW>
)

add_executable(exe1 exe1.cpp)
target_link_libraries(exe1 lib1)

cmake_policy(SET CMP0041 NEW)

add_library(shared_lib shared_lib.cpp)
target_link_libraries(shared_lib lib1)

Исполняемый файл exe1 будет скомпилирован с -DLIB1_WITH_EXE, в то время как общая библиотека shared_lib будет скомпилирована с -DLIB1_WITH_SHARED_LIB и -DCONSUMER_CMP0041_NEW, так как политика CMP0041 имеет значение NEW в момент создания целевого объекта shared_lib.

Выражение BUILD_INTERFACE оборачивает требования, которые используются только при потреблении из целевого объекта в той же системе сборки или при потреблении из целевого объекта, экспортированного в каталог сборки с помощью команды export(). Выражение INSTALL_INTERFACE оборачивает требования, которые используются только при потреблении из целевого объекта, который был установлен и экспортирован с помощью команды install(EXPORT).

add_library(ClimbingStats climbingstats.cpp)
target_compile_definitions(ClimbingStats INTERFACE
  $<BUILD_INTERFACE:ClimbingStats_FROM_BUILD_LOCATION>
  $<INSTALL_INTERFACE:ClimbingStats_FROM_INSTALLED_LOCATION>
)
install(TARGETS ClimbingStats EXPORT libExport ${InstallArgs})
install(EXPORT libExport NAMESPACE Upstream::
        DESTINATION lib/cmake/ClimbingStats)
export(EXPORT libExport NAMESPACE Upstream::)

add_executable(exe1 exe1.cpp)
target_link_libraries(exe1 ClimbingStats)

В этом случае исполняемый файл exe1 будет скомпилирован с помощью -DClimbingStats_FROM_BUILD_LOCATION. Команды экспорта генерируют цели IMPORTED с пропущенными либо INSTALL_INTERFACE, либо BUILD_INTERFACE, а маркер *_INTERFACE удаляется. Отдельный проект, использующий пакет ClimbingStats, будет содержать:

find_package(ClimbingStats REQUIRED)

add_executable(Downstream main.cpp)
target_link_libraries(Downstream Upstream::ClimbingStats)

В зависимости от того, был ли пакет ClimbingStats использован из расположения сборки или из расположения установки, цель Downstream будет скомпилирована с использованием -DClimbingStats_FROM_BUILD_LOCATION или -DClimbingStats_FROM_INSTALL_LOCATION. Дополнительную информацию о пакетах и экспорте см. в руководстве cmake-packages(7).

Требования к директориям включения и их использованию

При указании требований к использованию и при использовании с выражениями генератора, директории включения требуют некоторого особого рассмотрения. Команда target_include_directories() принимает как относительные, так и абсолютные директории включения:

add_library(lib1 lib1.cpp)
target_include_directories(lib1 PRIVATE
  /absolute/path
  relative/path
)

Относительные пути интерпретируются относительно каталога исходного кода, в котором появляется команда. В свойстве INTERFACE_INCLUDE_DIRECTORIES целей IMPORTED относительные пути запрещены.

В случаях, когда используется нетривиальное выражение генератора, выражение INSTALL_PREFIX может использоваться внутри аргумента выражения INSTALL_INTERFACE. Это маркер-замена, который расширяется до префикса установки при импорте потребляющим проектом.

Требования к использованию директорий включения обычно различаются между деревом сборки и деревом установки. Выражения генератора BUILD_INTERFACE и INSTALL_INTERFACE могут использоваться для описания отдельных требований к использованию, в зависимости от места использования. Относительные пути разрешены внутри выражения INSTALL_INTERFACE и интерпретируются относительно префикса установки. Например:

add_library(ClimbingStats climbingstats.cpp)
target_include_directories(ClimbingStats INTERFACE
  $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/generated>
  $<INSTALL_INTERFACE:/absolute/path>
  $<INSTALL_INTERFACE:relative/path>
  $<INSTALL_INTERFACE:$<INSTALL_PREFIX>/$<CONFIG>/generated>
)

Предоставляются два удобных API, относящихся к требованиям использования директорий включения. Переменная CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE может быть включена, что эквивалентно:

set_property(TARGET tgt APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES
  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR};${CMAKE_CURRENT_BINARY_DIR}>
)

для каждой затронутой цели. Удобная функция для установленных целей – это компонент INCLUDES DESTINATION, с командой install(TARGETS):

install(TARGETS foo bar bat EXPORT tgts ${dest_args}
  INCLUDES DESTINATION include
)
install(EXPORT tgts ${other_args})
install(FILES ${headers} DESTINATION include)

Это эквивалентно добавлению ${CMAKE_INSTALL_PREFIX}/include к свойству INTERFACE_INCLUDE_DIRECTORIES каждой из установленных целей IMPORTED при генерации командой install(EXPORT).

При использовании INTERFACE_INCLUDE_DIRECTORIES импортируемой цели, записи в свойстве обрабатываются как SYSTEM директории включения, как если бы они были перечислены в INTERFACE_SYSTEM_INCLUDE_DIRECTORIES зависимости. Это может привести к пропусканию предупреждений компилятора для заголовков, найденных в этих каталогах. Данное поведение для импортируемых целей может быть контролируемо установкой свойства NO_SYSTEM_FROM_IMPORTED для потребителей импортируемых целей или установкой свойства IMPORTED_NO_SYSTEM для самих импортируемых целей.

Если бинарная цель транзитивно связана с macOS FRAMEWORK, то директория Headers фреймворка также обрабатывается как требование к использованию. Это имеет тот же эффект, что и передача каталога фреймворка в качестве каталога включения.

Связывание библиотек и выражения генератора

Как и спецификации сборки, link libraries могут быть указаны с условиями выражений генератора. Однако, поскольку потребление требований к использованию основано на сборке из зависимых библиотек, существует дополнительное ограничение, что ссылки на зависимости должны образовывать «ориентированный ациклический граф». То есть, если ссылка на цель зависит от значения свойства цели, то это свойство цели не может зависеть от ссылок на зависимости:

add_library(lib1 lib1.cpp)
add_library(lib2 lib2.cpp)
target_link_libraries(lib1 PUBLIC
  $<$<TARGET_PROPERTY:POSITION_INDEPENDENT_CODE>:lib2>
)
add_library(lib3 lib3.cpp)
set_property(TARGET lib3 PROPERTY INTERFACE_POSITION_INDEPENDENT_CODE ON)

add_executable(exe1 exe1.cpp)
target_link_libraries(exe1 lib1 lib3)

Поскольку значение свойства POSITION_INDEPENDENT_CODE цели exe1 зависит от связанных библиотек (lib3), а ребро связи exe1 определяется тем же свойством POSITION_INDEPENDENT_CODE, приведенный выше граф зависимостей содержит цикл. cmake(1) выводит сообщение об ошибке.

Артефакты вывода

Цели системы сборки, созданные командами add_library() и add_executable(), создают правила для создания бинарных выходов. Точное местоположение бинарных файлов можно определить только во время генерации, так как оно может зависеть от конфигурации сборки и языка компоновки зависимых библиотек и т. д. TARGET_FILE, TARGET_LINKER_FILE и родственные выражения могут использоваться для доступа к имени и местоположению сгенерированных бинарных файлов. Однако для библиотек OBJECT эти выражения не работают, поскольку для таких библиотек нет одного файла, относящегося к выражениям.

Существует три типа артефактов вывода, которые могут быть созданы целями, как подробно описано в следующих разделах. Их классификация отличается между платформами DLL и не-DLL. Все системы на базе Windows, включая Cygwin, являются платформами DLL.

Артефакты выполнения

Артефактом выполнения цели системы сборки может быть:

  • Исполняемый файл (например, .exe) исполняемой цели, созданной командой add_executable().
  • На платформах DLL: исполняемый файл (например, .dll) цели разделяемой библиотеки, созданной командой add_library() с опцией SHARED.

Свойства цели RUNTIME_OUTPUT_DIRECTORY и RUNTIME_OUTPUT_NAME могут использоваться для управления местоположениями и именами артефактов выполнения в дереве сборки.

Артефакты библиотек

Артефактом библиотеки цели системы сборки может быть:

  • Файл загружаемого модуля (например, .dll или .so ) целевой библиотеки модуля, созданной командой add_library() с опцией MODULE.
  • На платформах, не являющихся DLL: файл разделяемой библиотеки (например, .so или .dylib ) целевой разделяемой библиотеки, созданной командой add_library() с опцией SHARED.

Свойства цели LIBRARY_OUTPUT_DIRECTORY и LIBRARY_OUTPUT_NAME могут использоваться для управления местоположениями и именами артефактов библиотек в дереве сборки.

Артефакты архивов

Артефактом архива цели системы сборки может быть:

  • Файл статической библиотеки (например, .lib или .a) целевого объекта статической библиотеки, созданного командой add_library() с параметром STATIC.
  • На платформах DLL: файл импортной библиотеки (например, .lib) целевого объекта динамической библиотеки, созданного командой add_library() с параметром SHARED . Этот файл гарантированно существует только в том случае, если библиотека экспортирует хотя бы один неуправляемый символ.
  • На платформах DLL: файл импортной библиотеки (например, .lib) целевого объекта исполняемого файла, созданного командой add_executable(), когда свойство целевого объекта ENABLE_EXPORTS установлено.
  • На платформах AIX: файл импорта линковщика (например, .imp) целевого объекта исполняемого файла, созданного командой add_executable(), когда свойство целевого объекта ENABLE_EXPORTS установлено.

Свойства целевого объекта ARCHIVE_OUTPUT_DIRECTORY и ARCHIVE_OUTPUT_NAME могут использоваться для управления расположением и именами артефактов вывода архивов в дереве сборки.

Команды, действующие на директории

Команды target_include_directories(), target_compile_definitions() и target_compile_options() оказывают влияние только на один целевой объект за раз. Команды add_compile_definitions(), add_compile_options() и include_directories() имеют аналогичную функцию, но работают на уровне директории для удобства.

Конфигурации сборки

Конфигурации определяют параметры для определённого типа сборки, такие как Release или Debug. Способ их задания зависит от типа используемого generator. Для генераторов с одной конфигурацией, таких как Генераторы Makefile и Ninja, конфигурация задаётся на этапе конфигурирования переменной CMAKE_BUILD_TYPE. Для генераторов с несколькими конфигурациями, таких как Visual Studio, Xcode и Ninja Multi-Config, конфигурация выбирается пользователем на этапе сборки, и CMAKE_BUILD_TYPE игнорируется. В случае с несколькими конфигурациями, набор доступных конфигураций задаётся на этапе конфигурирования переменной CMAKE_CONFIGURATION_TYPES, но фактическая используемая конфигурация не может быть известна до этапа сборки. Это различие часто неправильно понимают, что приводит к проблематичному коду, такому как следующий:

# WARNING: This is wrong for multi-config generators because they don't use
#          and typically don't even set CMAKE_BUILD_TYPE
string(TOLOWER ${CMAKE_BUILD_TYPE} build_type)
if (build_type STREQUAL debug)
  target_compile_definitions(exe1 PRIVATE DEBUG_BUILD)
endif()

Вместо этого следует использовать Generator expressions для корректной обработки логики, специфичной для конфигурации, независимо от используемого генератора. Например:

# Works correctly for both single and multi-config generators
target_compile_definitions(exe1 PRIVATE
  $<$<CONFIG:Debug>:DEBUG_BUILD>
)

В присутствии целевых объектов IMPORTED, содержимое MAP_IMPORTED_CONFIG_DEBUG также учитывается вышеуказанным выражением $<CONFIG:Debug>.

Регистронезависимость

CMAKE_BUILD_TYPE и CMAKE_CONFIGURATION_TYPES ведут себя как и другие переменные, то есть любые строковые сравнения с их значениями будут регистрозависимыми. Выражение генератора $<CONFIG> также сохраняет регистр конфигурации, заданной пользователем или значения по умолчанию CMake. Например:

# NOTE: Don't use these patterns, they are for illustration purposes only.

set(CMAKE_BUILD_TYPE Debug)
if(CMAKE_BUILD_TYPE STREQUAL DEBUG)
  # ... will never get here, "Debug" != "DEBUG"
endif()
add_custom_target(print_config ALL
  # Prints "Config is Debug" in this single-config case
  COMMAND ${CMAKE_COMMAND} -E echo "Config is $<CONFIG>"
  VERBATIM
)

set(CMAKE_CONFIGURATION_TYPES Debug Release)
if(DEBUG IN_LIST CMAKE_CONFIGURATION_TYPES)
  # ... will never get here, "Debug" != "DEBUG"
endif()

В отличие от этого, CMake обрабатывает тип конфигурации регистронезависимо при внутреннем использовании в местах, где поведение зависит от конфигурации. Например, выражение генератора $<CONFIG:Debug> будет равно 1 для конфигурации не только Debug, но также DEBUG, debug или даже DeBuG. Поэтому вы можете указывать типы конфигураций в CMAKE_BUILD_TYPE и CMAKE_CONFIGURATION_TYPES с любой комбинацией верхнего и нижнего регистра, хотя существуют сильные соглашения (см. следующий раздел). Если вам необходимо проверить значение в строковых сравнениях, всегда предварительно преобразуйте значение в верхний или нижний регистр и соответствующим образом скорректируйте проверку.

Конфигурации по умолчанию и настраиваемые конфигурации

По умолчанию CMake определяет ряд стандартных конфигураций:

  • Debug
  • Release
  • RelWithDebInfo
  • MinSizeRel

В генераторах с несколькими конфигурациями переменная CMAKE_CONFIGURATION_TYPES будет заполняться (возможно, подмножеством) вышеперечисленного списка по умолчанию, если не переопределено проектом или пользователем. Фактическая используемая конфигурация выбирается пользователем во время сборки.

Для генераторов с одной конфигурацией конфигурация задаётся с помощью переменной CMAKE_BUILD_TYPE на этапе конфигурирования и не может быть изменена на этапе сборки. Значение по умолчанию часто будет не совпадать с вышеуказанными стандартными конфигурациями и вместо этого будет пустой строкой. Распространённое заблуждение заключается в том, что это то же самое, что Debug, но это не так. Пользователи всегда должны явно указывать тип сборки, чтобы избежать этой распространённой проблемы.

Вышеперечисленные стандартные типы конфигураций обеспечивают разумное поведение на большинстве платформ, но их можно расширить для обеспечения других типов. Каждая конфигурация определяет набор переменных флагов компилятора и линковщика для используемого языка. Эти переменные следуют соглашению CMAKE_<LANG>_FLAGS_<CONFIG>, где <CONFIG> всегда представляет собой название конфигурации в верхнем регистре. При определении пользовательского типа конфигурации убедитесь, что эти переменные установлены соответствующим образом, обычно как переменные кэша.

Псевдоцелевые объекты

Некоторые типы целевых объектов не представляют собой выходные данные системы сборки, а только входные данные, такие как внешние зависимости, псевдонимы или другие артефакты, не относящиеся к сборке. Псевдоцелевые объекты не отображаются в генерируемой системе сборки.

Импортированные целевые объекты

Целевой объект IMPORTED представляет собой существующую зависимость. Обычно такие целевые объекты определяются пакетной зависимостью верхнего уровня и должны рассматриваться как неизменяемые. После объявления целевого объекта IMPORTED можно настроить его свойства, используя стандартные команды, такие как target_compile_definitions(), target_include_directories(), target_compile_options() или target_link_libraries(), как и с любым другим стандартным целевым объектом.

Для целевых объектов IMPORTED могут быть заданы те же свойства требований к использованию, что и для бинарных целевых объектов, такие как INTERFACE_INCLUDE_DIRECTORIES, INTERFACE_COMPILE_DEFINITIONS, INTERFACE_COMPILE_OPTIONS, INTERFACE_LINK_LIBRARIES и INTERFACE_POSITION_INDEPENDENT_CODE.

Значение свойства LOCATION также может быть прочитано из целевого объекта IMPORTED, хотя вряд ли есть причина для этого. Команды, такие как add_custom_command(), могут прозрачно использовать целевой объект IMPORTED EXECUTABLE как исполняемый файл COMMAND.

Область определения целевого объекта IMPORTED — это директория, в которой он был определён. К нему можно получить доступ и использовать его из поддиректорий, но не из родительских или соседних директорий. Область действия аналогична области действия переменной CMake.

Также можно определить целевой объект IMPORTED GLOBAL, который доступен глобально в системе сборки.

Дополнительные сведения о создании пакетов с целевыми объектами IMPORTED см. в руководстве cmake-packages(7).

Псевдонимы целевых объектов

Целевой объект ALIAS — это имя, которое может использоваться взаимозаменяемо с именем бинарного целевого объекта в контекстах только для чтения. Основное применение целевых объектов ALIAS — это, например, исполняемые файлы для примеров или модульных тестов, которые входят в состав одной системы сборки или собираются отдельно в зависимости от конфигурации пользователя.

add_library(lib1 lib1.cpp)
install(TARGETS lib1 EXPORT lib1Export ${dest_args})
install(EXPORT lib1Export NAMESPACE Upstream:: ${other_args})

add_library(Upstream::lib1 ALIAS lib1)

В другой директории мы можем безусловно ссылаться на целевой объект Upstream::lib1, который может быть целевым объектом IMPORTED из пакета или целевым объектом ALIAS при сборке в рамках одной системы сборки.

if (NOT TARGET Upstream::lib1)
  find_package(lib1 REQUIRED)
endif()
add_executable(exe1 exe1.cpp)
target_link_libraries(exe1 Upstream::lib1)

Целевые объекты ALIAS не изменяемы, не устанавливаются и не экспортируются. Они полностью локальны для описания системы сборки. Можно проверить, является ли имя именем ALIAS объекта, прочитав свойство ALIASED_TARGET:

get_target_property(_aliased Upstream::lib1 ALIASED_TARGET)
if(_aliased)
  message(STATUS "The name Upstream::lib1 is an ALIAS for ${_aliased}.")
endif()

Интерфейсные библиотеки

Целевой объект библиотеки INTERFACE не компилирует исходные файлы и не создаёт артефакт библиотеки на диске, поэтому у него нет свойства LOCATION.

Он может указывать требования к использованию, такие как INTERFACE_INCLUDE_DIRECTORIES, INTERFACE_COMPILE_DEFINITIONS, INTERFACE_COMPILE_OPTIONS, INTERFACE_LINK_LIBRARIES, INTERFACE_SOURCES и INTERFACE_POSITION_INDEPENDENT_CODE. Только режимы INTERFACE команд target_include_directories(), target_compile_definitions(), target_compile_options(), target_sources() и target_link_libraries() могут быть использованы с библиотеками INTERFACE.

Начиная с CMake 3.19, целевой объект библиотеки INTERFACE может необязательно содержать исходные файлы. Интерфейсная библиотека, содержащая исходные файлы, будет включена в качестве целевого объекта сборки в сгенерированной системе сборки. Она не компилирует исходные файлы, но может содержать пользовательские команды для генерации других исходных файлов. Кроме того, среды разработки покажут исходные файлы как часть целевого объекта для интерактивного чтения и редактирования.

Основное применение для библиотек INTERFACE — это библиотеки только для заголовков. Начиная с CMake 3.23, файлы заголовков могут быть связаны с библиотекой путём добавления их в набор заголовков с помощью команды target_sources():

add_library(Eigen INTERFACE)

target_sources(Eigen INTERFACE
  FILE_SET HEADERS
    BASE_DIRS src
    FILES src/eigen.h src/vector.h src/matrix.h
)

add_executable(exe1 exe1.cpp)
target_link_libraries(exe1 Eigen)

Когда мы указываем FILE_SET здесь, BASE_DIRS по умолчанию становятся каталогами включения в требованиях к использованию целевого объекта Eigen. Требования к использованию целевого объекта используются при компиляции, но не влияют на компоновку.

Другое применение — использование полностью ориентированного на целевые объекты дизайна для требований к использованию:

add_library(pic_on INTERFACE)
set_property(TARGET pic_on PROPERTY INTERFACE_POSITION_INDEPENDENT_CODE ON)
add_library(pic_off INTERFACE)
set_property(TARGET pic_off PROPERTY INTERFACE_POSITION_INDEPENDENT_CODE OFF)

add_library(enable_rtti INTERFACE)
target_compile_options(enable_rtti INTERFACE
  $<$<OR:$<COMPILER_ID:GNU>,$<COMPILER_ID:Clang>>:-rtti>
)

add_executable(exe1 exe1.cpp)
target_link_libraries(exe1 pic_on enable_rtti)

Таким образом, спецификация сборки exe1 выражена полностью как связанные целевые объекты, а сложность флагов, специфичных для компилятора, инкапсулирована в целевом объекте библиотеки INTERFACE.

Библиотеки INTERFACE могут быть установлены и экспортированы. Мы можем установить набор заголовков по умолчанию вместе с целевым объектом:

add_library(Eigen INTERFACE)

target_sources(Eigen INTERFACE
  FILE_SET HEADERS
    BASE_DIRS src
    FILES src/eigen.h src/vector.h src/matrix.h
)

install(TARGETS Eigen EXPORT eigenExport
  FILE_SET HEADERS DESTINATION include/Eigen)
install(EXPORT eigenExport NAMESPACE Upstream::
  DESTINATION lib/cmake/Eigen
)

Здесь заголовки, определённые в наборе заголовков, устанавливаются в include/Eigen. Путь установки автоматически становится каталогом включения, который является требованием к использованию потребителей.

© 2000–2022 Kitware, Inc. and Contributors
Licensed under the BSD 3-clause License.
https://cmake.org/cmake/help/v3.25/manual/cmake-buildsystem.7.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API