cmake-buildsystem(7)
- Введение
Введение
Система сборки на основе CMake организована как набор логических высокоуровневых целей. Каждая цель соответствует исполняемому файлу или библиотеке, или является пользовательской целью, содержащей пользовательские команды. Зависимости между целями выражены в системе сборки для определения порядка сборки и правил перегенерации в ответ на изменения.
Бинарные цели
Исполняемые файлы и библиотеки определяются с помощью команд add_executable() и add_library(). Результирующие бинарные файлы имеют соответствующие PREFIX, SUFFIX и расширения для целевой платформы. Зависимости между бинарными целями выражаются с помощью команды target_link_libraries():
add_library(archive archive.cpp zip.cpp lzma.cpp) add_executable(zipapp zipapp.cpp) target_link_libraries(zipapp archive)
archive определена как STATIC библиотека — архив, содержащий объекты, скомпилированные из archive.cpp, zip.cpp, и lzma.cpp. zipapp определена как исполняемый файл, сформированный путём компиляции и компоновки zipapp.cpp. При компоновке исполняемого файла zipapp, компонуется статическая библиотека archive.
Бинарные исполняемые файлы
Команда add_executable() определяет целевой исполняемый файл:
add_executable(mytool mytool.cpp)
Команды, такие как add_custom_command(), которые генерируют правила, которые выполняются во время сборки, могут прозрачно использовать целевой файл EXECUTABLE как исполняемый файл COMMAND. Правила системы сборки будут гарантировать, что исполняемый файл будет собран перед попыткой выполнения команды.
Типы бинарных библиотек
Обычные библиотеки
По умолчанию команда add_library() определяет STATIC библиотеку, если не указан тип. Тип может быть указан при использовании команды:
add_library(archive SHARED archive.cpp zip.cpp lzma.cpp)
add_library(archive STATIC archive.cpp zip.cpp lzma.cpp)
Переменная BUILD_SHARED_LIBS может быть включена для изменения поведения команды add_library() по умолчанию, чтобы она строила динамические библиотеки.
В контексте определения системы сборки в целом, принципиально не важно, являются ли конкретные библиотеки SHARED или STATIC — команды, спецификации зависимостей и другие API работают аналогичным образом независимо от типа библиотеки. Тип библиотеки MODULE отличается тем, что обычно не компонуется — он не используется в правой части команды target_link_libraries(). Это тип, который загружается как плагин с помощью методов времени выполнения. Если библиотека не экспортирует какие-либо необработанные символы (например, DLL ресурсов Windows, DLL C++/CLI), то библиотека не должна быть SHARED библиотекой, так как CMake ожидает, что SHARED библиотеки экспортируют по крайней мере один символ.
add_library(archive MODULE 7z.cpp)
Фреймворки Apple
Библиотека SHARED может быть помечена свойством цели FRAMEWORK для создания пакета Framework Bundle для macOS или iOS. Библиотека со свойством 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.
Унарное выражение генератора 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 установленной цели определяет это поведение (см. свойство EXPORT_NO_SYSTEM для изменения установленного значения для цели). Также можно изменить то, как потребители интерпретируют системное поведение импортированных целей, задав свойство цели 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 целевой библиотеки зависит от связанных библиотек (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установлено. - На macOS: файл импорта компоновщика (например,
.tbd) целевого объекта динамической библиотеки, созданного командойadd_library()с опциейSHAREDи когда свойство целевого объекта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() выполняют похожую функцию, но работают на уровне директорий, для удобства.
Конфигурации сборки
Конфигурации определяют характеристики конкретного типа сборки, например Release или Debug. Способ указания зависит от типа используемого generator. Для генераторов с одной конфигурацией, таких как Генераторы Makefile и Ninja, конфигурация указывается во время настройки переменной CMAKE_BUILD_TYPE. Для генераторов с несколькими конфигурациями, таких как Visual Studio, Xcode и Ninja Multi-Config, конфигурация выбирается пользователем во время сборки, и CMAKE_BUILD_TYPE игнорируется. В случае с несколькими конфигурациями набор *доступных* конфигураций указывается во время настройки переменной CMAKE_CONFIGURATION_TYPES, но фактическая используемая конфигурация неизвестна до стадии сборки. Это различие часто неправильно понимается, что приводит к проблематичному коду, подобному следующему:
# WARNING: This is wrong for multi-config generators because they don't use
# and typically don't even set CMAKE_BUILD_TYPE
string(TOLOWER ${CMAKE_BUILD_TYPE} build_type)
if (build_type STREQUAL debug)
target_compile_definitions(exe1 PRIVATE DEBUG_BUILD)
endif()
Вместо этого следует использовать Generator expressions, чтобы правильно обрабатывать логику, специфичную для конфигурации, независимо от используемого генератора. Например:
# Works correctly for both single and multi-config generators target_compile_definitions(exe1 PRIVATE $<$<CONFIG:Debug>:DEBUG_BUILD> )
В присутствии целевых объектов IMPORTED, содержимое MAP_IMPORTED_CONFIG_DEBUG также учитывается выражением $<CONFIG:Debug> выше.
Регистрозависимость
CMAKE_BUILD_TYPE и CMAKE_CONFIGURATION_TYPES подобны другим переменным, и любые строковые сравнения с их значениями будут регистрозависимыми. Генераторское выражение $<CONFIG> также сохраняет регистр конфигурации, установленный пользователем или по умолчанию CMake. Например:
# NOTE: Don't use these patterns, they are for illustration purposes only.
set(CMAKE_BUILD_TYPE Debug)
if(CMAKE_BUILD_TYPE STREQUAL DEBUG)
# ... will never get here, "Debug" != "DEBUG"
endif()
add_custom_target(print_config ALL
# Prints "Config is Debug" in this single-config case
COMMAND ${CMAKE_COMMAND} -E echo "Config is $<CONFIG>"
VERBATIM
)
set(CMAKE_CONFIGURATION_TYPES Debug Release)
if(DEBUG IN_LIST CMAKE_CONFIGURATION_TYPES)
# ... will never get here, "Debug" != "DEBUG"
endif()
В отличие от этого, CMake обрабатывает тип конфигурации регистронезависимо, когда использует его во внутренних частях, изменяющих поведение в зависимости от конфигурации. Например, генераторское выражение $<CONFIG:Debug> будет оцениваться как 1 для конфигурации не только Debug, но и DEBUG, debug или даже DeBuG. Поэтому вы можете указать типы конфигурации в CMAKE_BUILD_TYPE и CMAKE_CONFIGURATION_TYPES с любой комбинацией заглавных и строчных букв, хотя существуют сильные соглашения (см. следующий раздел). Если вам необходимо проверить значение в строковых сравнениях, всегда сначала преобразуйте значение в верхний или нижний регистр и соответственно скорректируйте проверку.
Конфигурации по умолчанию и пользовательские конфигурации
По умолчанию CMake определяет ряд стандартных конфигураций:
DebugReleaseRelWithDebInfoMinSizeRel
В генераторах с несколькими конфигурациями переменная CMAKE_CONFIGURATION_TYPES будет заполнена (возможно, подмножеством) вышеперечисленного списка по умолчанию, если не переопределено проектом или пользователем. Фактическая используемая конфигурация выбирается пользователем во время сборки.
Для генераторов с одной конфигурацией, конфигурация указывается с помощью переменной CMAKE_BUILD_TYPE во время настройки и не может быть изменена во время сборки. Значение по умолчанию часто будет не совпадать с вышеуказанными стандартными конфигурациями, а вместо этого будет пустой строкой. Распространённое заблуждение заключается в том, что это то же самое, что Debug, но это не так. Пользователи должны всегда явно указывать тип сборки, чтобы избежать этой распространённой проблемы.
Вышеперечисленные стандартные типы конфигураций обеспечивают разумное поведение на большинстве платформ, но их можно расширить, чтобы предоставить другие типы. Каждая конфигурация определяет набор переменных флагов компилятора и компоновщика для используемого языка. Эти переменные следуют соглашению CMAKE_<LANG>_FLAGS_<CONFIG>, где <CONFIG> всегда имя конфигурации в верхнем регистре. При определении пользовательского типа конфигурации убедитесь, что эти переменные установлены должным образом, как правило, в качестве переменных кэша.
Псевдоцелевые объекты
Некоторые типы целевых объектов не представляют выходные данные системы сборки, а только входные данные, такие как внешние зависимости, псевдонимы или другие артефакты, не участвующие в построении. Псевдоцелевые объекты не отображаются в генерируемой системе сборки.
Импортированные целевые объекты
Целевой объект 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.
Также можно определить GLOBAL IMPORTED целевой объект, к которому можно получить доступ глобально в системе сборки.
См. руководство 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 библиотека может необязательно содержать исходные файлы. Интерфейсная библиотека, содержащая исходные файлы, будет включена в качестве целевого объекта сборки в сгенерированной системе сборки. Она не компилирует исходные файлы, но может содержать пользовательские команды для генерации других исходных файлов. Кроме того, среды разработки отобразят исходные файлы как часть целевого объекта для интерактивного чтения и редактирования.
Основное применение INTERFACE библиотек — это header-only библиотеки. Начиная с CMake 3.23, заголовочные файлы могут быть связаны с библиотекой путём добавления их в набор заголовков с помощью команды target_sources():
add_library(Eigen INTERFACE)
target_sources(Eigen PUBLIC
FILE_SET HEADERS
BASE_DIRS src
FILES src/eigen.h src/vector.h src/matrix.h
)
add_executable(exe1 exe1.cpp)
target_link_libraries(exe1 Eigen)
Когда мы здесь указываем FILE_SET, BASE_DIRS , которые мы определяем, автоматически становятся каталогами включения в требованиях использования для целевого объекта 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 библиотеки могут быть установлены и экспортированы. Мы можем установить набор заголовков по умолчанию вместе с целевым объектом:
add_library(Eigen INTERFACE)
target_sources(Eigen INTERFACE
FILE_SET HEADERS
BASE_DIRS src
FILES src/eigen.h src/vector.h src/matrix.h
)
install(TARGETS Eigen EXPORT eigenExport
FILE_SET HEADERS DESTINATION include/Eigen)
install(EXPORT eigenExport NAMESPACE Upstream::
DESTINATION lib/cmake/Eigen
)
Здесь заголовки, определённые в наборе заголовков, устанавливаются в include/Eigen. Путь установки автоматически становится каталогом включения, который является требованием использования для потребителей.
© 2000–2024 Kitware, Inc. and Contributors
Licensed under the BSD 3-clause License.
https://cmake.org/cmake/help/v3.29/manual/cmake-buildsystem.7.html