Spec-Zone.ru › CMake 3.22

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 также должна установить свойство цели 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 на потребителей импортированных целей.

Если бинарная цель транзитивно связана с 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(), как и с любым другим обычным целевым объектом.

END_OF_DOCUMENT_MARKER

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.

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

См. руководство cmake-packages(7) для получения дополнительной информации о создании пакетов с целями IMPORTED.

Цели-псевдонимы

Цель-псевдоним 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 не изменяемы, не устанавливаются и не экспортируются. Они полностью локальны для описания системы сборки. Можно проверить, является ли имя именем цели-псевдонима, прочитав свойство 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 — это библиотеки только для заголовков.

add_library(Eigen INTERFACE
  src/eigen.h
  src/vector.h
  src/matrix.h
  )
target_include_directories(Eigen INTERFACE
  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
  $<INSTALL_INTERFACE:include/Eigen>
)

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

Здесь требования к использованию цели 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 могут быть установлены и экспортированы. Любое содержимое, на которое они ссылаются, должно быть установлено отдельно:

set(Eigen_headers
  src/eigen.h
  src/vector.h
  src/matrix.h
  )
add_library(Eigen INTERFACE ${Eigen_headers})
target_include_directories(Eigen INTERFACE
  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
  $<INSTALL_INTERFACE:include/Eigen>
)

install(TARGETS Eigen EXPORT eigenExport)
install(EXPORT eigenExport NAMESPACE Upstream::
  DESTINATION lib/cmake/Eigen
)
install(FILES ${Eigen_headers}
  DESTINATION include/Eigen
)

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

Spec-Zone.ru

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