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. 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.
Также возможно определить глобально доступный в системе сборки целевой объект IMPORTED GLOBAL.
См. руководство 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.
Основное применение библиотек 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.17/manual/cmake-buildsystem.7.html