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 )
Обратите внимание, что требования к использованию не предназначены для того, чтобы заставить последующие пользователи использовать конкретные 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-tree) и деревом установки (install-tree). Выражения генератора 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 из импортированного целевого объекта, хотя редко есть причина для этого. Такие команды, как add_custom_command(), могут прозрачно использовать целевой объект типа IMPORTED EXECUTABLE в качестве исполняемого файла COMMAND.
Область определения целевого объекта IMPORTED — это каталог, в котором он был определён. К нему можно получить доступ и использовать из подкаталогов, но не из родительских каталогов или каталогов-братьев. Область действия аналогична области действия переменной cmake.
Также возможно определить GLOBAL целевой объект IMPORTED, который доступен глобально в системе сборки.
См. руководство cmake-packages(7) для получения дополнительной информации о создании пакетов с целевыми объектами IMPORTED.
Целевые объекты-псевдонимы
Целевой объект-псевдоним 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 не изменяемы, не устанавливаются и не экспортируются. Они полностью локальны для описания системы сборки. Можно проверить, является ли имя именем псевдонима, прочитав свойство 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_NAMEIMPORTEDNAMENO_SYSTEM_FROM_IMPORTED- Свойства, соответствующие
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.9/manual/cmake-buildsystem.7.html