cmake-buildsystem(7)
- Введение
- Бинарные цели
- Требования к спецификации сборки и использованию
- Псевдоцели
Введение
Система сборки CMake организована как набор логических целей высокого уровня. Каждая цель соответствует исполняемому файлу или библиотеке или является пользовательской целью, содержащей пользовательские команды. Зависимости между целями выражены в системе сборки для определения порядка сборки и правил регенерации в ответ на изменения.
Бинарные цели
Исполняемые файлы и библиотеки определяются с помощью команд add_executable() и add_library(). Результирующие двоичные файлы имеют соответствующие префиксы, суффиксы и расширения для целевой платформы. Зависимости между бинарными целями выражаются с помощью команды target_link_libraries():
add_library(archive archive.cpp zip.cpp lzma.cpp) add_executable(zipapp zipapp.cpp) target_link_libraries(zipapp archive)
archive определена как статическая библиотека — архив, содержащий объекты, скомпилированные из archive.cpp, zip.cpp, и lzma.cpp. zipapp определена как исполняемый файл, сформированный путём компиляции и компоновки zipapp.cpp. При компоновке исполняемого файла zipapp, подключается статическая библиотека archive.
Бинарные исполняемые файлы
Команда add_executable() определяет исполняемую цель:
add_executable(mytool mytool.cpp)
Команды, такие как add_custom_command(), которые генерируют правила, которые должны быть выполнены во время сборки, могут прозрачно использовать цель EXECUTABLE в качестве исполняемого файла COMMAND. Правила системы сборки гарантируют, что исполняемый файл будет собран перед попыткой выполнения команды.
Типы бинарных библиотек
Обычные библиотеки
По умолчанию команда add_library() определяет статическую библиотеку, если не указан тип. Тип может быть указан при использовании команды:
add_library(archive SHARED archive.cpp zip.cpp lzma.cpp)
add_library(archive STATIC archive.cpp zip.cpp lzma.cpp)
Переменная BUILD_SHARED_LIBS может быть включена для изменения поведения add_library() для построения динамических библиотек по умолчанию.
В контексте определения системы сборки в целом, в значительной степени не имеет значения, являются ли конкретные библиотеки SHARED или STATIC — команды, спецификации зависимостей и другие API работают аналогично независимо от типа библиотеки. Тип библиотеки MODULE отличается тем, что он обычно не подключается — он не используется в правой части команды target_link_libraries(). Это тип, который загружается как плагин с помощью методов времени выполнения. Если библиотека не экспортирует никакие необработанные символы (например, DLL ресурсов Windows, DLL C++/CLI), необходимо, чтобы библиотека не была библиотекой SHARED, так как CMake ожидает, что библиотеки SHARED экспортируют по крайней мере один символ.
add_library(archive MODULE 7z.cpp)
Фреймворки Apple
Библиотека SHARED может быть помечена свойством цели FRAMEWORK для создания пакета Framework Bundle для 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_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 зависимостью от archive, требования к его использованию не распространяются на consumer.
В общем случае зависимость должна быть указана в использовании команды target_link_libraries() с ключевым словом PRIVATE, если она используется только реализацией библиотеки, а не в заголовочных файлах. Если зависимость дополнительно используется в заголовочных файлах библиотеки (например, для наследования классов), то она должна быть указана как PUBLIC зависимость. Зависимость, которая не используется реализацией библиотеки, а только её заголовочными файлами, должна быть указана как INTERFACE зависимость. Команда target_link_libraries() может быть вызвана с несколькими использованиями каждого ключевого слова:
target_link_libraries(archiveExtras PUBLIC archive PRIVATE serialization )
Требования к использованию распространяются путём чтения вариантов свойств целевого объекта INTERFACE_ из зависимостей и добавления значений к вариантам операнда, не являющимся INTERFACE_. Например, INTERFACE_INCLUDE_DIRECTORIES зависимостей считывается и добавляется к INCLUDE_DIRECTORIES операнда. В тех случаях, когда порядок важен и поддерживается, а порядок, полученный из вызовов target_link_libraries(), не позволяет выполнить корректную компиляцию, использование соответствующей команды для непосредственного задания свойства может обновить порядок.
Например, если связанные библиотеки для целевого объекта должны быть указаны в порядке lib1 lib2 lib3, но каталоги включения должны быть указаны в порядке lib3 lib1 lib2:
target_link_libraries(myExe lib1 lib2 lib3) target_include_directories(myExe PRIVATE $<TARGET_PROPERTY:lib3,INTERFACE_INCLUDE_DIRECTORIES>)
Обратите внимание, что необходимо соблюдать осторожность при указании требований к использованию целевых объектов, которые будут экспортироваться для установки с помощью команды install(EXPORT). Для получения дополнительной информации см. Создание пакетов.
Совместимые свойства интерфейса
Некоторые свойства целевых объектов должны быть совместимы между целевым объектом и интерфейсом каждой зависимости. Например, свойство целевого объекта POSITION_INDEPENDENT_CODE может задавать булево значение того, должен ли целевой объект компилироваться как независимый от позиции код, что имеет платформенно-специфические последствия. Целевой объект также может указать требование к использованию INTERFACE_POSITION_INDEPENDENT_CODE, чтобы сообщить, что потребители должны быть скомпилированы как независимые от позиции.
add_executable(exe1 exe1.cpp) set_property(TARGET exe1 PROPERTY POSITION_INDEPENDENT_CODE ON) add_library(lib1 SHARED lib1.cpp) set_property(TARGET lib1 PROPERTY INTERFACE_POSITION_INDEPENDENT_CODE ON) add_executable(exe2 exe2.cpp) target_link_libraries(exe2 lib1)
Здесь как exe1, так и exe2 будут скомпилированы как независимые от позиции. lib1 также будет скомпилирован как независимый от позиции, так как это значение по умолчанию для библиотек SHARED. Если у зависимостей есть конфликтующие несовместимые требования cmake(1) выдаёт диагностическое сообщение:
add_library(lib1 SHARED lib1.cpp) set_property(TARGET lib1 PROPERTY INTERFACE_POSITION_INDEPENDENT_CODE ON) add_library(lib2 SHARED lib2.cpp) set_property(TARGET lib2 PROPERTY INTERFACE_POSITION_INDEPENDENT_CODE OFF) add_executable(exe1 exe1.cpp) target_link_libraries(exe1 lib1) set_property(TARGET exe1 PROPERTY POSITION_INDEPENDENT_CODE OFF) add_executable(exe2 exe2.cpp) target_link_libraries(exe2 lib1 lib2)
Требование lib1 INTERFACE_POSITION_INDEPENDENT_CODE не является «совместимым» со свойством POSITION_INDEPENDENT_CODE целевого объекта exe1. Библиотека требует, чтобы потребители были построены как независимые от позиции, в то время как исполняемый файл указывает на то, чтобы не строиться как независимые от позиции, поэтому выдаётся диагностическое сообщение.
Требования lib1 и lib2 не являются «совместимыми». Одно из них требует, чтобы потребители были построены как позиционно-независимый код, а другое требует, чтобы потребители не были построены как позиционно-независимый код. Поскольку exe2 ссылается на оба, а они находятся в конфликте, выдаётся диагностика.
Чтобы быть «совместимыми», свойство POSITION_INDEPENDENT_CODE, если оно задано, должно быть таким же, в булевом смысле, как и свойство INTERFACE_POSITION_INDEPENDENT_CODE всех транзитивно указанных зависимостей, для которых это свойство задано.
Это свойство «требования совместимого интерфейса» может быть расширено на другие свойства путём указания свойства в содержании целевого свойства COMPATIBLE_INTERFACE_BOOL. Каждое указанное свойство должно быть совместимым между целевым потребителем и соответствующим свойством с префиксом INTERFACE_ каждой зависимости:
add_library(lib1Version2 SHARED lib1_v2.cpp) set_property(TARGET lib1Version2 PROPERTY INTERFACE_CUSTOM_PROP ON) set_property(TARGET lib1Version2 APPEND PROPERTY COMPATIBLE_INTERFACE_BOOL CUSTOM_PROP ) add_library(lib1Version3 SHARED lib1_v3.cpp) set_property(TARGET lib1Version3 PROPERTY INTERFACE_CUSTOM_PROP OFF) add_executable(exe1 exe1.cpp) target_link_libraries(exe1 lib1Version2) # CUSTOM_PROP will be ON add_executable(exe2 exe2.cpp) target_link_libraries(exe2 lib1Version2 lib1Version3) # Diagnostic
Небулевые свойства также могут участвовать в вычислениях «совместимого интерфейса». Свойства, указанные в свойстве COMPATIBLE_INTERFACE_STRING, должны быть либо не указаны, либо сравниваться с одной и той же строкой среди всех транзитивно указанных зависимостей. Это может быть полезно для того, чтобы убедиться, что несколько несовместимых версий библиотеки не связаны вместе через транзитивные требования целевого объекта:
add_library(lib1Version2 SHARED lib1_v2.cpp) set_property(TARGET lib1Version2 PROPERTY INTERFACE_LIB_VERSION 2) set_property(TARGET lib1Version2 APPEND PROPERTY COMPATIBLE_INTERFACE_STRING LIB_VERSION ) add_library(lib1Version3 SHARED lib1_v3.cpp) set_property(TARGET lib1Version3 PROPERTY INTERFACE_LIB_VERSION 3) add_executable(exe1 exe1.cpp) target_link_libraries(exe1 lib1Version2) # LIB_VERSION will be "2" add_executable(exe2 exe2.cpp) target_link_libraries(exe2 lib1Version2 lib1Version3) # Diagnostic
Целевое свойство COMPATIBLE_INTERFACE_NUMBER_MAX указывает, что содержимое будет вычислено численно, и будет вычислен максимальный номер среди всех указанных:
add_library(lib1Version2 SHARED lib1_v2.cpp) set_property(TARGET lib1Version2 PROPERTY INTERFACE_CONTAINER_SIZE_REQUIRED 200) set_property(TARGET lib1Version2 APPEND PROPERTY COMPATIBLE_INTERFACE_NUMBER_MAX CONTAINER_SIZE_REQUIRED ) add_library(lib1Version3 SHARED lib1_v3.cpp) set_property(TARGET lib1Version3 PROPERTY INTERFACE_CONTAINER_SIZE_REQUIRED 1000) add_executable(exe1 exe1.cpp) # CONTAINER_SIZE_REQUIRED will be "200" target_link_libraries(exe1 lib1Version2) add_executable(exe2 exe2.cpp) # CONTAINER_SIZE_REQUIRED will be "1000" target_link_libraries(exe2 lib1Version2 lib1Version3)
Аналогично, свойство COMPATIBLE_INTERFACE_NUMBER_MIN может использоваться для вычисления минимального численного значения свойства из зависимостей.
Каждое вычисленное значение свойства «совместимости» может быть прочитано в потребителе во время генерации с помощью выражений генератора.
Обратите внимание, что для каждой зависимой цели множество свойств, указанных в каждом свойстве совместимого интерфейса, не должно пересекаться с множеством, указанным в любом другом свойстве.
Отладка происхождения свойств
Поскольку спецификации построения могут определяться зависимостями, отсутствие локальности кода, создающего цель и кода, ответственного за установку спецификаций построения, может затруднить понимание кода. cmake(1) предоставляет средство отладки для вывода происхождения содержимого свойств, которые могут определяться зависимостями. Свойства, которые можно отлаживать, перечислены в документации переменной CMAKE_DEBUG_TARGET_PROPERTIES:
set(CMAKE_DEBUG_TARGET_PROPERTIES INCLUDE_DIRECTORIES COMPILE_DEFINITIONS POSITION_INDEPENDENT_CODE CONTAINER_SIZE_REQUIRED LIB_VERSION ) add_executable(exe1 exe1.cpp)
В случае свойств, перечисленных в COMPATIBLE_INTERFACE_BOOL или COMPATIBLE_INTERFACE_STRING, вывод отладки показывает, какой целевой объект был ответственен за установку свойства, и какие другие зависимости также определили свойство. В случае COMPATIBLE_INTERFACE_NUMBER_MAX и COMPATIBLE_INTERFACE_NUMBER_MIN, вывод отладки показывает значение свойства из каждой зависимости и определяет, определяет ли это значение новое крайнее значение.
Спецификация построения с выражениями генератора
Спецификации построения могут использовать generator expressions, содержащие содержимое, которое может быть условным или известным только на стадии генерации. Например, вычисленное «совместимое» значение свойства можно прочитать с помощью выражения TARGET_PROPERTY.
add_library(lib1Version2 SHARED lib1_v2.cpp)
set_property(TARGET lib1Version2 PROPERTY
INTERFACE_CONTAINER_SIZE_REQUIRED 200)
set_property(TARGET lib1Version2 APPEND PROPERTY
COMPATIBLE_INTERFACE_NUMBER_MAX CONTAINER_SIZE_REQUIRED
)
add_executable(exe1 exe1.cpp)
target_link_libraries(exe1 lib1Version2)
target_compile_definitions(exe1 PRIVATE
CONTAINER_SIZE=$<TARGET_PROPERTY:CONTAINER_SIZE_REQUIRED>
)
В этом случае исходные файлы exe1 будут скомпилированы с -DCONTAINER_SIZE=200.
Спецификации построения, определяемые конфигурацией, могут удобно задаваться с помощью выражения генератора CONFIG.
target_compile_definitions(exe1 PRIVATE
$<$<CONFIG:Debug>:DEBUG_BUILD>
)
Параметр CONFIG сравнивается регистронезависимо с конфигурацией, которая создаётся. В присутствии целевых объектов IMPORTED, содержимое MAP_IMPORTED_CONFIG_DEBUG также учитывается этим выражением.
Некоторые системы построения, сгенерированные с помощью cmake(1), имеют предопределённую конфигурацию построения, заданную в переменной CMAKE_BUILD_TYPE. Система построения для таких IDE, как Visual Studio и Xcode, генерируется независимо от конфигурации построения, и фактическая конфигурация построения неизвестна до этапа построения. Поэтому код, подобный
string(TOLOWER ${CMAKE_BUILD_TYPE} _type)
if (_type STREQUAL debug)
target_compile_definitions(exe1 PRIVATE DEBUG_BUILD)
endif()
может работать для генераторов на основе Makefile и Ninja, но не является переносимым для генераторов IDE. Кроме того, сопоставления конфигураций IMPORTED не учитываются с таким кодом, поэтому от него следует отказаться.
Унарное выражение генератора TARGET_PROPERTY и выражение генератора TARGET_POLICY оцениваются в контексте целевого потребителя. Это означает, что спецификация требования использования может быть оценена по-разному в зависимости от потребителя:
add_library(lib1 lib1.cpp) target_compile_definitions(lib1 INTERFACE $<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,EXECUTABLE>:LIB1_WITH_EXE> $<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,SHARED_LIBRARY>:LIB1_WITH_SHARED_LIB> $<$<TARGET_POLICY:CMP0041>:CONSUMER_CMP0041_NEW> ) add_executable(exe1 exe1.cpp) target_link_libraries(exe1 lib1) cmake_policy(SET CMP0041 NEW) add_library(shared_lib shared_lib.cpp) target_link_libraries(shared_lib lib1)
Исполняемый файл exe1 будет скомпилирован с -DLIB1_WITH_EXE, в то время как общая библиотека shared_lib будет скомпилирована с -DLIB1_WITH_SHARED_LIB и -DCONSUMER_CMP0041_NEW, поскольку политика CMP0041 имеет значение NEW в момент создания целевого объекта shared_lib.
Выражение BUILD_INTERFACE оборачивает требования, которые используются только при использовании их из целевого объекта в той же системе построения или при использовании их из целевого объекта, экспортированного в каталог построения с помощью команды export(). Выражение INSTALL_INTERFACE оборачивает требования, которые используются только при использовании их из целевого объекта, который был установлен и экспортирован с помощью команды install(EXPORT):
add_library(ClimbingStats climbingstats.cpp)
target_compile_definitions(ClimbingStats INTERFACE
$<BUILD_INTERFACE:ClimbingStats_FROM_BUILD_LOCATION>
$<INSTALL_INTERFACE:ClimbingStats_FROM_INSTALLED_LOCATION>
)
install(TARGETS ClimbingStats EXPORT libExport ${InstallArgs})
install(EXPORT libExport NAMESPACE Upstream::
DESTINATION lib/cmake/ClimbingStats)
export(EXPORT libExport NAMESPACE Upstream::)
add_executable(exe1 exe1.cpp)
target_link_libraries(exe1 ClimbingStats)
В этом случае исполняемый файл exe1 будет скомпилирован с -DClimbingStats_FROM_BUILD_LOCATION.
Команды экспорта генерируют целевые объекты IMPORTED с опущенными INSTALL_INTERFACE или BUILD_INTERFACE, и маркером *_INTERFACE удалён. Отдельный проект, использующий пакет ClimbingStats, будет содержать:
find_package(ClimbingStats REQUIRED) add_executable(Downstream main.cpp) target_link_libraries(Downstream Upstream::ClimbingStats)
В зависимости от того, использовался ли пакет ClimbingStats из расположения построения или расположения установки, целевой объект Downstream будет скомпилирован либо с -DClimbingStats_FROM_BUILD_LOCATION, либо с -DClimbingStats_FROM_INSTALL_LOCATION. Дополнительные сведения о пакетах и экспорте см. в руководстве cmake-packages(7).
Директории включения и требования использования
Директории включения требуют особого внимания при указании в качестве требований использования и при использовании с выражениями генератора. Команда target_include_directories() принимает как относительные, так и абсолютные директории включения:
add_library(lib1 lib1.cpp) target_include_directories(lib1 PRIVATE /absolute/path relative/path )
Относительные пути интерпретируются относительно каталога исходных кодов, где появляется команда. Относительные пути не допускаются в свойстве INTERFACE_INCLUDE_DIRECTORIES целевых объектов IMPORTED.
В случаях, когда используется нетривиальное выражение генератора, выражение INSTALL_PREFIX может использоваться в аргументе выражения INSTALL_INTERFACE. Это маркер замены, который расширяется до префикса установки при импорте в потребляющий проект.
Требования использования директорий включения обычно отличаются между деревом построения и деревом установки. Выражения генератора BUILD_INTERFACE и INSTALL_INTERFACE могут использоваться для описания отдельных требований использования, в зависимости от местоположения использования. Относительные пути разрешены внутри выражения INSTALL_INTERFACE и интерпретируются относительно префикса установки. Например:
add_library(ClimbingStats climbingstats.cpp)
target_include_directories(ClimbingStats INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/generated>
$<INSTALL_INTERFACE:/absolute/path>
$<INSTALL_INTERFACE:relative/path>
$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/$<CONFIG>/generated>
)
Предоставляются два удобных API, относящихся к требованиям использования каталогов включения. Переменная CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE может быть включена, с эквивалентным эффектом:
set_property(TARGET tgt APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR};${CMAKE_CURRENT_BINARY_DIR}>
)
для каждого затронутого целевого объекта. Удобство для установленных целевых объектов — это компонент INCLUDES DESTINATION с командой install(TARGETS):
install(TARGETS foo bar bat EXPORT tgts ${dest_args}
INCLUDES DESTINATION include
)
install(EXPORT tgts ${other_args})
install(FILES ${headers} DESTINATION include)
Это эквивалентно добавлению ${CMAKE_INSTALL_PREFIX}/include к INTERFACE_INCLUDE_DIRECTORIES каждого из установленных IMPORTED целевых объектов при генерации командой install(EXPORT).
Когда INTERFACE_INCLUDE_DIRECTORIES импортированного целевого объекта используется, записи в свойстве рассматриваются как SYSTEM каталоги включения, как если бы они были перечислены в INTERFACE_SYSTEM_INCLUDE_DIRECTORIES зависимости. Это может привести к пропуску предупреждений компилятора о заголовочных файлах, найденных в этих каталогах. Это поведение для Импортированных целевых объектов может быть контролировано свойством целевого объекта NO_SYSTEM_FROM_IMPORTED.
Если бинарный целевой объект транзитивно связан с фреймворком Mac OX, каталог Headers фреймворка также рассматривается как требование использования. Это имеет тот же эффект, что и передача каталога фреймворка как каталога включения.
Связывание библиотек и выражения генератора
Как и спецификации сборки, link libraries могут быть указаны с условиями выражений генератора. Однако, поскольку использование требований основано на сборе из связанных зависимостей, существует дополнительное ограничение, что связанные зависимости должны образовывать «ориентированный ациклический граф». То есть, если связывание с целевым объектом зависит от значения свойства целевого объекта, это свойство целевого объекта не может зависеть от связанных зависимостей:
add_library(lib1 lib1.cpp) add_library(lib2 lib2.cpp) target_link_libraries(lib1 PUBLIC $<$<TARGET_PROPERTY:POSITION_INDEPENDENT_CODE>:lib2> ) add_library(lib3 lib3.cpp) set_property(TARGET lib3 PROPERTY INTERFACE_POSITION_INDEPENDENT_CODE ON) add_executable(exe1 exe1.cpp) target_link_libraries(exe1 lib1 lib3)
Поскольку значение свойства POSITION_INDEPENDENT_CODE целевого объекта exe1 зависит от связанных библиотек (lib3), а ребро связывания exe1 определяется тем же свойством POSITION_INDEPENDENT_CODE, граф зависимостей выше содержит цикл. cmake(1) выводит диагностику в этом случае.
Выходные артефакты
Целевые объекты системы сборки, созданные командами add_library() и add_executable(), создают правила для создания двоичных выходных данных. Точное расположение выходных двоичных файлов может быть определено только во время генерации, потому что оно может зависеть от конфигурации сборки и языка связывания связанных зависимостей и т. д. TARGET_FILE, TARGET_LINKER_FILE и связанные выражения могут использоваться для доступа к имени и расположению сгенерированных двоичных файлов. Однако эти выражения не работают для OBJECT библиотек, так как для таких библиотек нет одного файла, который был бы релевантным для этих выражений.
Существует три типа выходных артефактов, которые могут быть созданы целевыми объектами, как подробно описано в следующих разделах. Их классификация отличается для платформ DLL и платформ, не являющихся DLL. Все системы на базе Windows, включая Cygwin, являются платформами DLL.
Выходные артефакты времени выполнения
Выходной артефакт времени выполнения целевого объекта системы сборки может быть:
- Исполняемый файл (например,
.exe) исполняемого целевого объекта, созданного командойadd_executable(). - На платформах DLL: исполняемый файл (например,
.dll) целевого объекта общей библиотеки, созданного командойadd_library()с опциейSHARED.
Свойства целевых объектов RUNTIME_OUTPUT_DIRECTORY и RUNTIME_OUTPUT_NAME могут использоваться для управления расположением и именами выходных артефактов времени выполнения в дереве сборки.
Выходные артефакты библиотек
Выходной артефакт библиотеки целевого объекта системы сборки может быть:
- Файл модуля загрузки (например,
.dllили.so) целевого объекта модульной библиотеки, созданного командойadd_library()с опциейMODULE. - На платформах, не являющихся DLL: файл общей библиотеки (например,
.soили.dylib) целевого объекта общей библиотеки, созданного командойadd_library()с опциейSHARED.
Свойства целевых объектов LIBRARY_OUTPUT_DIRECTORY и LIBRARY_OUTPUT_NAME могут использоваться для управления расположением и именами выходных артефактов библиотек в дереве сборки.
Выходные артефакты архивов
Выходной артефакт архива целевого объекта системы сборки может быть:
- Файл статической библиотеки (например,
.libили.a) целевого объекта статической библиотеки, созданного командойadd_library()с опциейSTATIC. - На платформах DLL: файл импортной библиотеки (например,
.lib) целевого объекта общей библиотеки, созданного командойadd_library()с опциейSHARED. Этот файл гарантированно существует только в том случае, если библиотека экспортирует по крайней мере один неуправляемый символ. - На платформах DLL: файл импортной библиотеки (например,
.lib) целевого объекта исполняемого файла, созданного командойadd_executable(), когда свойство целевого объектаENABLE_EXPORTSустановлено.
Свойства целевых объектов ARCHIVE_OUTPUT_DIRECTORY и ARCHIVE_OUTPUT_NAME могут использоваться для управления расположением и именами выходных артефактов архивов в дереве сборки.
Команды, ограниченные каталогом
Команды target_include_directories(), target_compile_definitions() и target_compile_options() влияют только на один целевой объект за раз. Команды add_definitions(), add_compile_options() и include_directories() выполняют аналогичную функцию, но работают в масштабе каталога вместо целевого объекта для удобства.
Псевдоцелевые объекты
Некоторые типы целевых объектов не представляют выходных данных системы сборки, а только входные данные, такие как внешние зависимости, псевдонимы или другие объекты, не связанные со сборкой. Псевдоцелевые объекты не представлены в сгенерированной системе сборки.
END_OF_DOCUMENT_MARKERИмпортированные цели
Цепь IMPORTED представляет собой предварительно существующую зависимость. Обычно такие цели определяются пакетом вышестоящего уровня и должны рассматриваться как неизменяемые. Невозможно использовать цель IMPORTED в левой части команды target_compile_definitions(), target_include_directories(), target_compile_options() или target_link_libraries(), так как это будет попыткой её изменения. Цели IMPORTED предназначены для использования только в правой части этих команд.
Цели IMPORTED могут иметь те же свойства требований к использованию, что и цели бинарных типов, такие как INTERFACE_INCLUDE_DIRECTORIES, INTERFACE_COMPILE_DEFINITIONS, INTERFACE_COMPILE_OPTIONS, INTERFACE_LINK_LIBRARIES и INTERFACE_POSITION_INDEPENDENT_CODE.
Расположение LOCATION также может считываться из импортированной цели, хотя для этого редко есть причина. Команды, такие как add_custom_command(), могут прозрачно использовать цель типа IMPORTED EXECUTABLE как исполняемый файл COMMAND.
Область определения цели IMPORTED — это директория, в которой она была определена. К ней можно получить доступ и использовать её из поддиректорий, но не из родительских или соседних директорий. Область действия аналогична области действия переменной CMake.
Также можно определить GLOBAL IMPORTED цель, доступную глобально в системе сборки.
См. руководство cmake-packages(7) для получения дополнительной информации о создании пакетов с целями IMPORTED.
Цели-псевдонимы
Цепь ALIAS — это имя, которое можно использовать взаимозаменяемо с именем цели бинарного типа в контекстах только для чтения. Основной сценарий использования целей ALIAS — это, например, исполняемые файлы для примеров или unit-тестов, сопровождающие библиотеку, которая может быть частью той же системы сборки или собираться отдельно в зависимости от конфигурации пользователя.
add_library(lib1 lib1.cpp)
install(TARGETS lib1 EXPORT lib1Export ${dest_args})
install(EXPORT lib1Export NAMESPACE Upstream:: ${other_args})
add_library(Upstream::lib1 ALIAS lib1)
В другой директории мы можем безусловно связать исполняемый файл с целью Upstream::lib1, которая может быть целью IMPORTED из пакета или целью ALIAS , если она собрана как часть той же системы сборки.
if (NOT TARGET Upstream::lib1) find_package(lib1 REQUIRED) endif() add_executable(exe1 exe1.cpp) target_link_libraries(exe1 Upstream::lib1)
Цели ALIAS не изменяемы, не устанавливаются и не экспортируются. Они полностью локальны для описания системы сборки. Можно проверить, является ли имя именем ALIAS, прочитав свойство ALIASED_TARGET из него:
get_target_property(_aliased Upstream::lib1 ALIASED_TARGET)
if(_aliased)
message(STATUS "The name Upstream::lib1 is an ALIAS for ${_aliased}.")
endif()
Интерфейсные библиотеки
Цепь INTERFACE не имеет LOCATION и является изменяемой, но в остальном похожа на цель IMPORTED.
Она может определять требования к использованию, такие как INTERFACE_INCLUDE_DIRECTORIES, INTERFACE_COMPILE_DEFINITIONS, INTERFACE_COMPILE_OPTIONS, INTERFACE_LINK_LIBRARIES, INTERFACE_SOURCES и INTERFACE_POSITION_INDEPENDENT_CODE. Только режимы INTERFACE команд target_include_directories(), target_compile_definitions(), target_compile_options(), target_sources() и target_link_libraries() могут быть использованы с INTERFACE библиотеками.
Основной случай использования 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_NAMEIMPORTEDNAME- Свойства, соответствующие
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.7/manual/cmake-buildsystem.7.html