Spec-Zone.ru › CMake 3.11

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. Правила системы сборки гарантируют, что исполняемый файл будет собран перед выполнением команды.

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

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

По умолчанию команда 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, чтобы создать пакет фреймворка OS X или 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)

Библиотеки типа OBJECT не могут использоваться в правой части команды target_link_libraries(). Они также не могут использоваться в качестве TARGET в использовании команды add_custom_command(TARGET). Они могут устанавливаться и будут экспортироваться как библиотека интерфейса.

Хотя библиотеки объектов не могут быть указаны напрямую в вызовах команды target_link_libraries(), они могут подключаться косвенно, используя библиотеку интерфейса, для которой свойство INTERFACE_SOURCES установлено в имя $<TARGET_OBJECTS:objlib>.

Хотя библиотеки объектов не могут использоваться как 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. Библиотека требует, чтобы исполняемые файлы были скомпилированы как position-independent-code, а исполняемый файл не был скомпилирован как position-independent-code, поэтому выдаётся диагностическое сообщение.

Требования lib1 и lib2 несовместимы. Одно из них требует, чтобы исполняемые файлы были скомпилированы как position-independent-code, а другое — нет. Поскольку 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-tree) и деревом установки (install-tree). Выражения генератора 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_library().
  • На платформах 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_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.

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

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

Целевые объекты-псевдонимы

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

Основной случай использования 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.11/manual/cmake-buildsystem.7.html

Spec-Zone.ru

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