Spec-Zone.ru › CMake 3.21

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, C++/CLI DLL), необходимо, чтобы библиотека не была 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_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.

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

target_compile_definitions(exe1 PRIVATE
    $<$<CONFIG:Debug>:DEBUG_BUILD>
)

Параметр CONFIG сравнивается с регистром независимо от конфигурации, которая создается. При наличии целевых объектов IMPORTED, содержимое MAP_IMPORTED_CONFIG_DEBUG также учитывается этим выражением.

Некоторые системы построения, сгенерированные cmake(1), имеют предопределённую конфигурацию построения, установленную в переменной CMAKE_BUILD_TYPE. Система построения для сред разработки, таких как Visual Studio и Xcode, генерируется независимо от конфигурации построения, и фактическая конфигурация построения неизвестна до времени построения. Поэтому код, такой как

string(TOLOWER ${CMAKE_BUILD_TYPE} _type)
if (_type STREQUAL debug)
  target_compile_definitions(exe1 PRIVATE DEBUG_BUILD)
endif()

может показаться работоспособным для генераторов Makefile и Ninja генераторов, но не является переносимым для генераторов IDE. Кроме того, конфигурационные сопоставления IMPORTED не учитываются кодом такого типа, поэтому его следует избегать.

Унарное выражение генератора 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() выполняют схожую функцию, но действуют на уровне каталога для удобства.

Псевдоцели

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

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

Цель 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, доступную во всей системе построения.

См. руководство 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 целевые объекты не являются изменяемыми, устанавливаемыми или экспортируемыми. Они полностью локальны для описания системы сборки. Можно проверить, является ли имя 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.21/manual/cmake-buildsystem.7.html

Spec-Zone.ru

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