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(). Это тип, который загружается как плагин с помощью методов выполнения. Если библиотека не экспортирует какие-либо незаявленные символы (например, Windows DLL ресурсов, C++/CLI DLL), требуется, чтобы библиотека не была SHARED библиотекой, потому что CMake ожидает, что SHARED библиотеки экспортируют по крайней мере один символ.
add_library(archive MODULE 7z.cpp)
Фреймворки Apple
SHARED библиотека может быть помечена свойством цели FRAMEWORK для создания пакета фреймворка 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_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 цели 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установлено. - На 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 также может быть прочитан из импортированной цели, хотя редко есть причина для этого. Команды, такие как 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 — это библиотеки только для заголовков. Начиная с 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.27/manual/cmake-buildsystem.7.html