cmake-buildsystem(7)
- Введение
Введение
Система сборки CMake организована как набор логических целей высокого уровня. Каждая цель соответствует исполняемому файлу или библиотеке, или является пользовательской целью, содержащей пользовательские команды. Зависимости между целями выражаются в системе сборки для определения порядка сборки и правил регенерации в ответ на изменения.
Бинарные цели
Исполняемые файлы и библиотеки определяются с помощью команд add_executable() и add_library(). Результирующие двоичные файлы имеют соответствующие PREFIX, SUFFIX и расширения для целевой платформы. Зависимости между бинарными целями выражаются с помощью команды target_link_libraries():
add_library(archive archive.cpp zip.cpp lzma.cpp) add_executable(zipapp zipapp.cpp) target_link_libraries(zipapp archive)
archive определена как STATIC библиотека – архив, содержащий объекты, скомпилированные из archive.cpp, zip.cpp, и lzma.cpp. zipapp определена как исполняемый файл, образованный компиляцией и линковкой zipapp.cpp. При линковке исполняемого файла zipapp, статическая библиотека archive подключается.
Бинарные исполняемые файлы
Команда add_executable() определяет целевой исполняемый файл:
add_executable(mytool mytool.cpp)
Команды, такие как add_custom_command(), генерирующие правила, которые выполняются во время сборки, могут прозрачно использовать целевой файл EXECUTABLE как исполняемый файл COMMAND. Правила системы сборки будут гарантировать, что исполняемый файл будет собран перед попыткой выполнить команду.
Типы бинарных библиотек
Обычные библиотеки
По умолчанию, команда add_library() определяет STATIC библиотеку, если не указан тип. Тип может быть указан при использовании команды:
add_library(archive SHARED archive.cpp zip.cpp lzma.cpp)
add_library(archive STATIC archive.cpp zip.cpp lzma.cpp)
Переменная BUILD_SHARED_LIBS может быть включена для изменения поведения команды add_library() на построение общих библиотек по умолчанию.
В контексте определения системы сборки в целом, почти не имеет значения, являются ли конкретные библиотеки SHARED или STATIC — команды, спецификации зависимостей и другие API работают аналогичным образом независимо от типа библиотеки. Тип библиотеки MODULE отличается тем, что, как правило, не подключается — он не используется в правой части команды target_link_libraries(). Это тип, который загружается как плагин с помощью методов времени выполнения. Если библиотека не экспортирует какие-либо независящие от управления символы (например, DLL ресурсов Windows, DLL C++/CLI), необходимо, чтобы библиотека не была SHARED библиотекой, потому что CMake ожидает, что SHARED библиотеки экспортируют по крайней мере один символ.
add_library(archive MODULE 7z.cpp)
Фреймворки Apple
SHARED библиотека может быть помечена свойством цели FRAMEWORK для создания пакета macOS или iOS Framework Bundle. 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)
Этап линковки (или архивирования) этих других целей будет использовать файлы объектов из OBJECT библиотек, которые непосредственно подключены. Кроме того, требования к использованию OBJECT библиотек будут учтены при компиляции исходных файлов в этих других целях. Более того, эти требования к использованию будут распространяться транзитивно на зависимые от этих других целей.
Библиотеки объектов нельзя использовать как TARGET в использовании подписи команды add_custom_command(TARGET). Однако список объектов можно использовать с помощью add_custom_command(OUTPUT) или file(GENERATE) с использованием $<TARGET_OBJECTS:objlib>.
Требования к спецификации сборки и использованию
Команды target_include_directories(), target_compile_definitions() и target_compile_options() задают спецификации сборки и требования к использованию бинарных целей. Эти команды заполняют свойства цели INCLUDE_DIRECTORIES, COMPILE_DEFINITIONS и COMPILE_OPTIONS соответственно, а также свойства цели INTERFACE_INCLUDE_DIRECTORIES, INTERFACE_COMPILE_DEFINITIONS и INTERFACE_COMPILE_OPTIONS.
У каждой команды есть режим PRIVATE, PUBLIC и INTERFACE. Режим PRIVATE заполняет только не-INTERFACE_ вариант целевого свойства, а режим INTERFACE заполняет только INTERFACE_ варианты. Режим PUBLIC заполняет оба варианта соответствующего целевого свойства. Каждая команда может быть вызвана с несколькими использованиями каждого ключевого слова:
target_compile_definitions(archive PRIVATE BUILDING_WITH_LZMA INTERFACE USING_ARCHIVE_LIB )
Обратите внимание, что требования к использованию не предназначены для того, чтобы сделать последующее использование конкретных COMPILE_OPTIONS или COMPILE_DEFINITIONS и т.п. для удобства, а только для удобства. Содержание свойств должно быть требованиями, а не просто рекомендациями или удобством.
См. раздел Создание переносимых пакетов руководства cmake-packages(7) для обсуждения дополнительных мер предосторожности, которые необходимо принять при указании требований к использованию при создании пакетов для распространения.
Свойства целевых объектов
Содержимое свойств целевого объекта INCLUDE_DIRECTORIES, COMPILE_DEFINITIONS и COMPILE_OPTIONS используется при компиляции исходных файлов бинарного целевого объекта.
Элементы в INCLUDE_DIRECTORIES добавляются в строку компиляции с префиксами -I или -isystem и в порядке появления в значении свойства.
Элементы в COMPILE_DEFINITIONS имеют префиксы -D или /D и добавляются в строку компиляции в неопределённом порядке. Свойство целевого объекта DEFINE_SYMBOL также добавляется в качестве компиляционной директивы как специальный удобный случай для SHARED и MODULE библиотек.
Элементы в COMPILE_OPTIONS экранируются для оболочки и добавляются в порядке появления в значении свойства. Некоторые компиляционные опции имеют специальную обработку, такую как POSITION_INDEPENDENT_CODE.
Содержимое свойств целевого объекта INTERFACE_INCLUDE_DIRECTORIES, INTERFACE_COMPILE_DEFINITIONS и INTERFACE_COMPILE_OPTIONS являются требованиями к использованию — они указывают содержимое, которое потребители должны использовать для правильной компиляции и линковки с целевым объектом, на котором они появляются. Для любого бинарного целевого объекта содержимое каждого INTERFACE_ свойства на каждом целевом объекте, указанном в команде target_link_libraries(), используется:
set(srcs archive.cpp zip.cpp)
if (LZMA_FOUND)
list(APPEND srcs lzma.cpp)
endif()
add_library(archive SHARED ${srcs})
if (LZMA_FOUND)
# The archive library sources are compiled with -DBUILDING_WITH_LZMA
target_compile_definitions(archive PRIVATE BUILDING_WITH_LZMA)
endif()
target_compile_definitions(archive INTERFACE USING_ARCHIVE_LIB)
add_executable(consumer)
# Link consumer to archive and consume its usage requirements. The consumer
# executable sources are compiled with -DUSING_ARCHIVE_LIB.
target_link_libraries(consumer archive)
Поскольку часто требуется добавить исходную директорию и соответствующую директорию построения в INCLUDE_DIRECTORIES, переменная CMAKE_INCLUDE_CURRENT_DIR может быть включена для удобного добавления соответствующих директорий в INCLUDE_DIRECTORIES всех целевых объектов. Переменная CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE может быть включена для добавления соответствующих директорий в INTERFACE_INCLUDE_DIRECTORIES всех целевых объектов. Это упрощает использование целевых объектов в нескольких различных директориях с помощью команды target_link_libraries().
Транзитивные требования к использованию
Требования к использованию целевого объекта могут транзитивно распространяться на зависимые объекты. Команда target_link_libraries() имеет ключевые слова PRIVATE, INTERFACE и PUBLIC для управления распространением.
add_library(archive archive.cpp) target_compile_definitions(archive INTERFACE USING_ARCHIVE_LIB) add_library(serialization serialization.cpp) target_compile_definitions(serialization INTERFACE USING_SERIALIZATION_LIB) add_library(archiveExtras extras.cpp) target_link_libraries(archiveExtras PUBLIC archive) target_link_libraries(archiveExtras PRIVATE serialization) # archiveExtras is compiled with -DUSING_ARCHIVE_LIB # and -DUSING_SERIALIZATION_LIB add_executable(consumer consumer.cpp) # consumer is compiled with -DUSING_ARCHIVE_LIB target_link_libraries(consumer archiveExtras)
Так как archive является PUBLIC зависимостью archiveExtras, требования к использованию распространяются на consumer тоже. Так как serialization является PRIVATE зависимостью archiveExtras, требования к использованию не распространяются на consumer.
В общем случае, зависимость должна быть указана в использовании target_link_libraries() с ключевым словом PRIVATE, если она используется только реализацией библиотеки, а не в заголовочных файлах. Если зависимость дополнительно используется в заголовочных файлах библиотеки (например, для наследования классов), то она должна быть указана как PUBLIC зависимость. Зависимость, которая не используется реализацией библиотеки, а только её заголовочными файлами, должна быть указана как INTERFACE зависимость. Команда target_link_libraries() может быть вызвана с несколькими использованиями каждого ключевого слова:
target_link_libraries(archiveExtras PUBLIC archive PRIVATE serialization )
Требования к использованию распространяются путем чтения INTERFACE_ вариантов свойств целевого объекта из зависимостей и добавления значений к не-INTERFACE_ вариантам операнда. Например, INTERFACE_INCLUDE_DIRECTORIES зависимостей считывается и добавляется к INCLUDE_DIRECTORIES операнда. В случаях, когда порядок важен и поддерживается, а порядок, полученный из вызовов target_link_libraries(), не позволяет правильно скомпилировать, использование соответствующей команды для непосредственного задания свойства может обновить порядок.
Например, если для связанных библиотек целевого объекта требуется порядок lib1 lib2 lib3, но для директорий включаемых файлов нужен порядок lib3 lib1 lib2.
target_link_libraries(myExe lib1 lib2 lib3) target_include_directories(myExe PRIVATE $<TARGET_PROPERTY:lib3,INTERFACE_INCLUDE_DIRECTORIES>)
Обратите внимание, что необходимо соблюдать осторожность при указании требований к использованию для целевых объектов, которые будут экспортированы для установки с помощью команды install(EXPORT). См. Создание пакетов для получения дополнительной информации.
Совместимые свойства интерфейса
Некоторые свойства целевого объекта должны быть совместимы между целевым объектом и интерфейсом каждой зависимости. Например, свойство целевого объекта POSITION_INDEPENDENT_CODE может указывать булево значение о том, должен ли целевой объект быть скомпилирован как независимый от положения код, что имеет платформенно-специфические последствия. Целевой объект также может указать требование к использованию INTERFACE_POSITION_INDEPENDENT_CODE, чтобы сообщить, что потребители должны быть скомпилированы как независимые от положения.
add_executable(exe1 exe1.cpp) set_property(TARGET exe1 PROPERTY POSITION_INDEPENDENT_CODE ON) add_library(lib1 SHARED lib1.cpp) set_property(TARGET lib1 PROPERTY INTERFACE_POSITION_INDEPENDENT_CODE ON) add_executable(exe2 exe2.cpp) target_link_libraries(exe2 lib1)
Здесь, как exe1, так и exe2 будут скомпилированы как независимые от положения. lib1 также будет скомпилирован как независимый от положения, так как это значение по умолчанию для библиотек SHARED. Если зависимости имеют противоречивые, несовместимые требования, cmake(1) выводит диагностическое сообщение:
add_library(lib1 SHARED lib1.cpp) set_property(TARGET lib1 PROPERTY INTERFACE_POSITION_INDEPENDENT_CODE ON) add_library(lib2 SHARED lib2.cpp) set_property(TARGET lib2 PROPERTY INTERFACE_POSITION_INDEPENDENT_CODE OFF) add_executable(exe1 exe1.cpp) target_link_libraries(exe1 lib1) set_property(TARGET exe1 PROPERTY POSITION_INDEPENDENT_CODE OFF) add_executable(exe2 exe2.cpp) target_link_libraries(exe2 lib1 lib2)
Требование lib1 INTERFACE_POSITION_INDEPENDENT_CODE несовместимо со свойством POSITION_INDEPENDENT_CODE целевого объекта exe1. Библиотека требует, чтобы потребители были скомпилированы как позиционно-независимый код, в то время как исполняемый файл — нет. Поэтому выдаётся диагностическое сообщение.
Требования lib1 и lib2 несовместимы. Одно из них требует, чтобы потребители были скомпилированы как позиционно-независимый код, а другое — нет. Так как exe2 ссылается на оба, и они противоречат друг другу, выдаётся диагностическое сообщение.
Для совместимости свойство 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 на потребителях импортированных целевых объектов.
Если целевой объект бинарного типа связан транзитивно с 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.
Также возможно определить глобальный в системе построения целевой объект типа GLOBAL IMPORTED.
См. руководство cmake-packages(7) для получения дополнительной информации о создании пакетов с целевыми объектами типа IMPORTED.
Целевые объекты-псевдонимы
Целевой объект-псевдоним — это имя, которое может использоваться взаимозаменяемо с именем бинарного целевого объекта в контекстах только для чтения. Например, целевые объекты-псевдонимы применяются для исполняемых файлов примеров или для тестов единиц, сопровождающих библиотеку, которые могут быть частью одной системы построения или быть построены отдельно в зависимости от конфигурации пользователя.
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)
Целевые объекты-псевдонимы не могут быть изменены, установлены или экспортированы. Они полностью локальны для описания системы построения. Проверить, является ли имя именем целевого объекта-псевдонима, можно, прочитав свойство ALIASED_TARGET из него:
get_target_property(_aliased Upstream::lib1 ALIASED_TARGET)
if(_aliased)
message(STATUS "The name Upstream::lib1 is an ALIAS for ${_aliased}.")
endif()
Интерфейсные библиотеки
Целевой объект-библиотека интерфейса не имеет свойства 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_NAMEEXPORT_PROPERTIESIMPORTEDMANUALLY_ADDED_DEPENDENCIESNAME- Свойства, соответствующие
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–2020 Kitware, Inc. and Contributors
Licensed under the BSD 3-clause License.
https://cmake.org/cmake/help/v3.16/manual/cmake-buildsystem.7.html