Spec-Zone.ru › CMake 3.13

cmake-buildsystem(7)

  • Введение
  • Бинарные цели
    • Бинарные исполняемые файлы
    • Типы бинарных библиотек
      • Обычные библиотеки
        • Фреймворки Apple
      • Библиотеки объектов
  • Требования к спецификации сборки и использованию
    • Свойства целевых объектов
    • Транзитивные требования к использованию
    • Совместимые свойства интерфейса
    • Отладка происхождения свойств
    • Спецификация сборки с выражениями генератора
      • Директории включаемых файлов и требования к использованию
    • Связывание библиотек и выражения генератора
    • Артефакты вывода
      • Артефакты вывода во время выполнения
      • Артефакты вывода библиотек
      • Артефакты вывода архивов
    • Команды, ограниченные директориями
  • Псевдоцели
    • Импортированные цели
    • Целевые псевдонимы
    • Библиотеки интерфейса

Введение

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

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

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

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

archive определена как статическая библиотека — архив, содержащий объекты, скомпилированные из 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() определяет статическую библиотеку, если не указан тип. Тип может быть указан при использовании команды:

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. MACOSX_FRAMEWORK_IDENTIFIER устанавливает ключ CFBundleIdentifier, и он однозначно идентифицирует пакет.

add_library(MyFramework SHARED MyFramework.cpp)
set_target_properties(MyFramework PROPERTIES
  FRAMEWORK TRUE
  FRAMEWORK_VERSION A
  MACOSX_FRAMEWORK_IDENTIFIER org.cmake.MyFramework
)

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

Тип библиотеки 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)

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

Библиотеки объектов не могут использоваться в качестве 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 ссылается на оба, и они находятся в конфликте, выдается диагностическое сообщение.

Для «совместимости» свойство 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. Система сборки для таких IDE, как 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 для потребителей импортированных целевых объектов.

Если бинарный целевой объект связывается транзитивно с фреймворком Mac OX, каталог 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 задано.

Свойства целевого объекта 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 как исполняемый файл.

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

Также возможно определить глобальную в системе сборки цель IMPORTED типа %%%CODE_BLOCK_289%%.

См. руководство 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 и является изменяемой, но в остальном аналогична цели IMPORTED.

Она может указывать требования к использованию, такие как 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.

Основное применение интерфейсных библиотек — это библиотеки только для заголовков.

add_library(Eigen INTERFACE)
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, это:

  • Свойства, соответствующие INTERFACE_*
  • Встроенные свойства, соответствующие COMPATIBLE_INTERFACE_*
  • EXPORT_NAME
  • IMPORTED
  • NAME
  • Свойства, соответствующие IMPORTED_LIBNAME_*
  • Свойства, соответствующие MAP_IMPORTED_CONFIG_*

Библиотеки INTERFACE могут быть установлены и экспортированы. Любое содержимое, на которое они ссылаются, должно быть установлено отдельно:

add_library(Eigen INTERFACE)
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
    ${CMAKE_CURRENT_SOURCE_DIR}/src/eigen.h
    ${CMAKE_CURRENT_SOURCE_DIR}/src/vector.h
    ${CMAKE_CURRENT_SOURCE_DIR}/src/matrix.h
  DESTINATION include/Eigen
)

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

Spec-Zone.ru

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