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 для создания пакета фреймворка OS X или iOS. MACOSX_FRAMEWORK_IDENTIFIER устанавливает ключ CFBundleIdentifier и уникально идентифицирует пакет.
add_library(MyFramework SHARED MyFramework.cpp) set_target_properties(MyFramework PROPERTIES FRAMEWORK TRUE FRAMEWORK_VERSION A MACOSX_FRAMEWORK_IDENTIFIER org.cmake.MyFramework )
Библиотеки объектов
Тип библиотеки OBJECT также не подключается. Он определяет неархивную коллекцию объектных файлов, полученных в результате компиляции заданных исходных файлов. Коллекция объектных файлов может использоваться в качестве исходных данных для других целевых объектов:
add_library(archive OBJECT archive.cpp zip.cpp lzma.cpp) add_library(archiveExtras STATIC $<TARGET_OBJECTS:archive> extras.cpp) add_executable(test_exe $<TARGET_OBJECTS:archive> test.cpp)
Библиотеки типа OBJECT не могут использоваться в правой части команды target_link_libraries(). Они также не могут использоваться в качестве TARGET при использовании синтаксиса команды add_custom_command(TARGET). Они могут быть установлены и будут экспортированы как библиотека интерфейса.
Хотя библиотеки объектов не могут быть названы напрямую в вызовах команды target_link_libraries(), они могут быть «подключены» косвенно с помощью библиотеки интерфейса, свойство целевого объекта INTERFACE_SOURCES которого установлено для именования $<TARGET_OBJECTS:objlib>.
Хотя библиотеки объектов не могут использоваться как TARGET при использовании синтаксиса команды add_custom_command(TARGET), список объектов может использоваться командой add_custom_command(OUTPUT) или file(GENERATE) с помощью $<TARGET_OBJECTS:objlib>.
Требования к спецификации сборки и использованию
Команды target_include_directories(), target_compile_definitions() и target_compile_options() задают спецификации сборки и требования к использованию бинарных целей. Команды заполняют свойства цели INCLUDE_DIRECTORIES, COMPILE_DEFINITIONS и COMPILE_OPTIONS соответственно и/или свойства цели INTERFACE_INCLUDE_DIRECTORIES, INTERFACE_COMPILE_DEFINITIONS и INTERFACE_COMPILE_OPTIONS.
Каждая из команд имеет режим PRIVATE, PUBLIC и INTERFACE. Режим PRIVATE заполняет только не-INTERFACE_ вариант свойства цели, а режим INTERFACE заполняет только INTERFACE_ варианты. Режим PUBLIC заполняет оба варианта соответствующего свойства цели. Каждая команда может быть вызвана с несколькими использованиями каждого ключевого слова:
target_compile_definitions(archive PRIVATE BUILDING_WITH_LZMA INTERFACE USING_ARCHIVE_LIB )
Обратите внимание, что требования к использованию не предназначены для того, чтобы заставить downstream использовать определенные 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 активна в момент создания целевого объекта 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() выполняют аналогичную функцию, но действуют на уровне каталога для удобства, а не уровня целевого объекта.
Псевдоцелевые объекты
Некоторые типы целевых объектов не представляют выходные данные системы сборки, а только входные данные, такие как внешние зависимости, псевдонимы или другие не относящиеся к сборке артефакты. Псевдоцелевые объекты не представлены в сгенерированной системе сборки.
Импортированные целевые объекты
Целевой объект 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 также может быть прочитано из целевого объекта IMPORTED, хотя для этого редко есть причина. Команды, такие как add_custom_command(), могут прозрачно использовать целевой объект типа IMPORTED как исполняемый файл COMMAND.
Область определения целевого объекта IMPORTED — это каталог, где он был определен. К нему можно получить доступ и использовать его из подкаталогов, но не из родительских каталогов или каталогов-братьев. Область действия аналогична области действия переменной CMake.
Также можно определить глобально доступный в системе сборки целевой объект IMPORTED типа GLOBAL.
См. руководство cmake-packages(7) для получения дополнительной информации о создании пакетов с целевыми объектами IMPORTED.
Целевые объекты-псевдонимы
Целевой объект-псевдоним — это имя, которое можно использовать взаимозаменяемо с именем целевого объекта бинарной программы в контекстах только для чтения. Пример использования целевых объектов-псевдонимов — исполняемые файлы примеров или 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)
В другом каталоге мы можем безусловно ссылаться на целевой объект IMPORTED, который может быть целевым объектом 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_NAMEIMPORTEDNAME- Свойства, соответствующие
IMPORTED_LIBNAME_* - Свойства, соответствующие
MAP_IMPORTED_CONFIG_*
INTERFACE библиотеки могут быть установлены и экспортированы. Любое содержимое, на которое они ссылаются, должно быть установлено отдельно:
add_library(Eigen INTERFACE)
target_include_directories(Eigen INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
$<INSTALL_INTERFACE:include/Eigen>
)
install(TARGETS Eigen EXPORT eigenExport)
install(EXPORT eigenExport NAMESPACE Upstream::
DESTINATION lib/cmake/Eigen
)
install(FILES
${CMAKE_CURRENT_SOURCE_DIR}/src/eigen.h
${CMAKE_CURRENT_SOURCE_DIR}/src/vector.h
${CMAKE_CURRENT_SOURCE_DIR}/src/matrix.h
DESTINATION include/Eigen
)
© 2000–2019 Kitware, Inc. and Contributors
Licensed under the BSD 3-clause License.
https://cmake.org/cmake/help/v3.10/manual/cmake-buildsystem.7.html