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, C++/CLI DLL), требуется, чтобы библиотека не была SHARED библиотекой, поскольку CMake ожидает, что SHARED библиотеки экспортируют по крайней мере один символ.
add_library(archive MODULE 7z.cpp)
Фреймворки Apple
SHARED библиотека может быть помечена свойством цели FRAMEWORK для создания пакета macOS или iOS Framework Bundle. Библиотека со свойством цели FRAMEWORK должна также установить свойство цели FRAMEWORK_VERSION. Это свойство обычно устанавливается в значение "A" по соглашениям macOS. MACOSX_FRAMEWORK_IDENTIFIER устанавливает ключ CFBundleIdentifier, и он уникально идентифицирует пакет.
add_library(MyFramework SHARED MyFramework.cpp) set_target_properties(MyFramework PROPERTIES FRAMEWORK TRUE FRAMEWORK_VERSION A # Version "A" is macOS convention MACOSX_FRAMEWORK_IDENTIFIER org.cmake.MyFramework )
Библиотеки объектов
Тип библиотеки OBJECT определяет неархивированное собрание файлов объектов, полученных в результате компиляции заданных исходных файлов. Коллекция файлов объектов может быть использована как входные данные для других целей с помощью синтаксиса $<TARGET_OBJECTS:name>. Это generator expression, который можно использовать для предоставления содержимого библиотеки 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 ссылается на оба, и они находятся в конфликте, выводится сообщение об ошибке CMake:
CMake Error: The INTERFACE_POSITION_INDEPENDENT_CODE property of "lib2" does not agree with the value of POSITION_INDEPENDENT_CODE already determined for "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.
Целевые объекты-псевдонимы
Целевой объект-псевдоним ALIAS — это имя, которое можно использовать взаимозаменяемо с именем целевого объекта-исполняемого файла в контекстах только для чтения. Основное применение целевых объектов-псевдонимов ALIAS — это, например, исполняемые файлы для примеров или модульных тестов, которые могут быть частью той же системы сборки или собираться отдельно в зависимости от конфигурации пользователя.
add_library(lib1 lib1.cpp)
install(TARGETS lib1 EXPORT lib1Export ${dest_args})
install(EXPORT lib1Export NAMESPACE Upstream:: ${other_args})
add_library(Upstream::lib1 ALIAS lib1)
В другом каталоге можно условно связать целевой объект Upstream::lib1, который может быть целевым объектом IMPORTED из пакета или целевым объектом ALIAS , если он собирается как часть той же системы сборки.
if (NOT TARGET Upstream::lib1) find_package(lib1 REQUIRED) endif() add_executable(exe1 exe1.cpp) target_link_libraries(exe1 Upstream::lib1)
ALIAS целевые объекты не являются изменяемыми, устанавливаемыми или экспортируемыми. Они полностью локальны для описания системы сборки. Можно проверить, является ли имя 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.
Он может указывать требования к использованию, такие как 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.
Начиная с CMake 3.19, целевой объект INTERFACE библиотеки может необязательно содержать исходные файлы. Библиотека интерфейса, содержащая исходные файлы, будет включена как целевой объект сборки в сгенерированной системе сборки. Она не компилирует исходные файлы, но может содержать пользовательские команды для генерации других исходных файлов. Кроме того, среды разработки IDE отобразят исходные файлы как часть целевого объекта для интерактивного чтения и редактирования.
Основной пример использования библиотек INTERFACE — это библиотеки только для заголовков.
add_library(Eigen INTERFACE
src/eigen.h
src/vector.h
src/matrix.h
)
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 могут быть установлены и экспортированы. Любой контент, на который они ссылаются, должен быть установлен отдельно:
set(Eigen_headers
src/eigen.h
src/vector.h
src/matrix.h
)
add_library(Eigen INTERFACE ${Eigen_headers})
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 ${Eigen_headers}
DESTINATION include/Eigen
)
© 2000–2021 Kitware, Inc. and Contributors
Licensed under the BSD 3-clause License.
https://cmake.org/cmake/help/v3.20/manual/cmake-buildsystem.7.html