cmake-developer(7)
Введение
Данное руководство предназначено для разработчиков, работающих с cmake-language(7) кодом, независимо от того, пишут ли они собственные модули, создают ли собственные системы сборки или работают с самим CMake.
Чтобы принять участие в разработке CMake, обратитесь к https://cmake.org/get-involved/. Там находятся ссылки на инструкции по участию, которые, в свою очередь, ведут к руководствам для разработчиков самого CMake.
Доступ к реестру Windows
CMake предоставляет некоторые возможности для доступа к реестру на Windows платформах.
Запрос реестра Windows
Новое в версии 3.24.
Команда cmake_host_system_information() предоставляет возможность запроса реестра на локальном компьютере. Подробнее см. cmake_host_system(QUERY_WINDOWS_REGISTRY).
Поиск с использованием реестра Windows
Изменено в версии 3.24.
Параметры HINTS и PATHS команд find_file(), find_library(), find_path(), find_program() и find_package() на платформе Windows позволяют запросы к реестру.
Формальный синтаксис, заданный с использованием обозначений BNF с обычными расширениями, следующий:
registry_query ::= '[' sep_definition? root_key
((key_separator sub_key)? (value_separator value_name_)?)? ']'
sep_definition ::= '{' value_separator '}'
root_key ::= 'HKLM' | 'HKEY_LOCAL_MACHINE' | 'HKCU' | 'HKEY_CURRENT_USER' |
'HKCR' | 'HKEY_CLASSES_ROOT' | 'HKCC' | 'HKEY_CURRENT_CONFIG' |
'HKU' | 'HKEY_USERS'
sub_key ::= element (key_separator element)*
key_separator ::= '/' | '\\'
value_separator ::= element | ';'
value_name ::= element | '(default)'
element ::= character\+
character ::= <any character except key_separator and value_separator>
Необязательный элемент sep_definition позволяет указать строку, используемую для разделения sub_key и value_name. Если не указано, используется символ ;. Несколько элементов registry_query могут быть указаны в качестве части пути.
# example using default separator
find_file(... PATHS "/root/[HKLM/Stuff;InstallDir]/lib[HKLM\\\\Stuff;Architecture]")
# example using different specified separators
find_library(... HINTS "/root/[{|}HKCU/Stuff|InstallDir]/lib[{@@}HKCU\\\\Stuff@@Architecture]")
Если элемент value_name не указан или имеет специальное имя (default), будет возвращено содержимое значения по умолчанию, если таковое имеется. Поддерживаемые типы для value_name:
-
REG_SZ. -
REG_EXPAND_SZ. Возвращаемые данные расширены. -
REG_DWORD. -
REG_QWORD.
При неудачном запросе к реестру, обычно из-за отсутствия ключа или неподдерживаемого типа данных, строка /REGISTRY-NOTFOUND подставляется вместо выражения запроса [].
Модули поиска
«Модуль поиска» — это Find<PackageName>.cmake файл, который загружается командой find_package() при вызове для <PackageName>.
Основная задача модуля поиска — определить, доступен ли пакет, установить переменную <PackageName>_FOUND для отражения этого и предоставить любые переменные, макросы и импортированные цели, необходимые для использования пакета. Модуль поиска полезен в тех случаях, когда библиотека верхнего уровня не предоставляет пакет с конфигурационным файлом.
Традиционный подход — использовать переменные для всего, включая библиотеки и исполняемые файлы: см. раздел Стандартные имена переменных ниже. Именно так работают большинство существующих модулей поиска, предоставляемых CMake.
Более современный подход — максимально соответствовать пакетам с конфигурационными файлами, предоставляя импортированные цели. Это преимущество заключается в передаче требований к использованию зависимостей потребителям.
В обоих случаях (или даже при предоставлении как переменных, так и импортированных целей) модули поиска должны обеспечить обратную совместимость со старыми версиями, имеющими то же имя.
Модуль FindFoo.cmake обычно загружается командой:
find_package(Foo [major[.minor[.patch[.tweak]]]]
[EXACT] [QUIET] [REQUIRED]
[[COMPONENTS] [components...]]
[OPTIONAL_COMPONENTS components...]
[NO_POLICY_SCOPE])
См. документацию по find_package() для подробностей о том, какие переменные устанавливаются для модуля поиска. Большая часть из них обрабатывается с помощью FindPackageHandleStandardArgs.
Вкратце, модуль должен находить только версии пакета, совместимые с запрошенной версией, как описано семейством переменных Foo_FIND_VERSION . Если Foo_FIND_QUIETLY установлено в значение true, то сообщение о неудаче поиска пакета (включая все сообщения об ошибках) должно быть предотвращено. Если Foo_FIND_REQUIRED установлено в значение true, модуль должен выдать сообщение FATAL_ERROR в случае, если пакет не найден. Если ни одно из них не установлено в значение true, то сообщение о неудаче поиска пакета должно быть некритическим.
Пакеты, ищущие несколько полунезависимых частей (например, наборы библиотек), должны искать компоненты, указанные в Foo_FIND_COMPONENTS (если оно задано) и устанавливать Foo_FOUND в значение true только в том случае, если для каждого искомого компонента <c>, который не был найден, Foo_FIND_REQUIRED_<c> не установлено в значение true. Аргумент HANDLE_COMPONENTS команды find_package_handle_standard_args() может быть использован для реализации этого.
Если Foo_FIND_COMPONENTS не задано, то поиск необходимых модулей остается на усмотрение модуля поиска, но должен быть задокументирован.
Для внутренней реализации общепринято использовать переменные, начинающиеся с символа подчеркивания, только для временного использования.
Стандартные имена переменных
Для модуля FindXxx.cmake , который использует подход установки переменных (вместо или в дополнение к созданию импортированных целей), должны использоваться следующие имена переменных для сохранения согласованности между модулями поиска. Обратите внимание, что все переменные начинаются с Xxx_, что (если не указано иное) должно точно совпадать с именем FindXxx.cmake файла, включая регистр. Этот префикс в именах переменных предотвращает конфликты с переменными других модулей поиска. Такой же шаблон должен применяться ко всем макросам, функциям и импортированным целям, определённым модулем поиска.
-
Xxx_INCLUDE_DIRS -
Окончательный набор каталогов включения, перечисленных в одной переменной для использования кодом клиента. Это не должно быть записью кэша (обратите внимание, что это также означает, что эта переменная не должна использоваться в качестве переменной результата команды
find_path()— см.Xxx_INCLUDE_DIRниже для этого). -
Xxx_LIBRARIES -
Библиотеки для использования с модулем. Они могут быть целевыми элементами CMake, полными абсолютными путями к двоичному файлу библиотеки или именем библиотеки, которую компоновщик должен найти в своем пути поиска. Это не должно быть записью кэша (обратите внимание, что это также означает, что эта переменная не должна использоваться в качестве переменной результата команды
find_library()— см.Xxx_LIBRARYниже для этого). -
Xxx_DEFINITIONS -
Определения компиляции для использования при компиляции кода, использующего модуль. В него действительно не должны включаться такие параметры, как
-DHAS_JPEG, которые файл исходного кода клиента использует для определения того, нужно ли#include <jpeg.h> -
Xxx_EXECUTABLE -
Полный абсолютный путь к исполняемому файлу. В этом случае
Xxxможет не быть именем модуля, а именем инструмента (обычно преобразованным в верхний регистр), предполагая, что у инструмента есть такое общеизвестное имя, что маловероятно существование другого инструмента с таким же именем. Было бы целесообразно использовать это как переменную результата командыfind_program(). -
Xxx_YYY_EXECUTABLE -
Аналогично
Xxx_EXECUTABLE, за исключением того, что здесьXxxвсегда является именем модуля, аYYY— именем инструмента (снова, обычно в верхнем регистре). Предпочтительнее использовать этот формат, если имя инструмента не слишком известно или может конфликтовать с другим инструментом. Для большей согласованности предпочтительнее использовать этот формат, если модуль предоставляет более одного исполняемого файла. -
Xxx_LIBRARY_DIRS -
Необязательно, окончательный набор каталогов библиотек, перечисленных в одной переменной для использования кодом клиента. Это не должно быть записью кэша.
-
Xxx_ROOT_DIR -
Местоположение базового каталога модуля.
-
Xxx_VERSION_VV -
Переменные такого формата указывают, является ли предоставляемый модуль
XxxверсииVVмодуля. Для данного модуля не должно быть более одной переменной такого формата, установленной в значение true. Например, модульBarryмог эволюционировать на протяжении многих лет и пройти несколько различных основных версий. Версия 3 модуляBarryможет установить переменнуюBarry_VERSION_3в значение true, в то время как более старая версия модуля может установитьBarry_VERSION_2в значение true вместо этого. Ошибка произойдёт, если переменныеBarry_VERSION_3иBarry_VERSION_2оба будут установлены в значение true. -
Xxx_WRAP_YY -
Когда переменная такого формата устанавливается в значение false, это указывает на то, что соответствующая обёртковая команда не должна использоваться. Обёртковая команда зависит от модуля, она может быть подразумеваемой именем модуля или может быть указана частью
YYпеременной. -
Xxx_Yy_FOUND -
Для переменных такого формата,
Yy— имя компонента для модуля. Оно должно точно совпадать с одним из допустимых имён компонентов, которые могут быть переданы командеfind_package()для модуля. Если переменная такого формата установлена в значение false, это означает, что компонентYyмодуляXxxне был найден или недоступен. Переменные такого формата обычно используются для необязательных компонентов, чтобы вызывающая сторона могла проверить, доступен ли необязательный компонент. -
Xxx_FOUND -
Когда команда
find_package()возвращается к вызывающей стороне, эта переменная будет установлена в значение true, если модуль был успешно найден. -
Xxx_NOT_FOUND_MESSAGE -
Должна устанавливаться файлами конфигурации в случае, если она установила
Xxx_FOUNDв значение FALSE. Сообщение в ней будет выведено командойfind_package()и командойfind_package_handle_standard_args()для информирования пользователя о проблеме. Используйте её вместо вызоваmessage()непосредственно для сообщения о причине неудачного поиска модуля или пакета. -
Xxx_RUNTIME_LIBRARY_DIRS -
Необязательно, путь поиска динамических библиотек во время выполнения для использования при запуске исполняемого файла, связанного с динамическими библиотеками. Список должен использоваться кодом пользователя для создания
PATHв Windows илиLD_LIBRARY_PATHв UNIX. Это не должно быть записью кэша. -
Xxx_VERSION -
Полная строка версии найденного пакета, если есть. Обратите внимание, что многие существующие модули предоставляют
Xxx_VERSION_STRINGвместо этого. -
Xxx_VERSION_MAJOR -
Основная версия найденного пакета, если есть.
-
Xxx_VERSION_MINOR -
Дополнительная версия найденного пакета, если есть.
-
Xxx_VERSION_PATCH -
Версия исправления найденного пакета, если есть.
Следующие имена обычно не должны использоваться в файлах CMakeLists.txt. Они предназначены для использования модулями Find для указания и кэширования расположений определённых файлов или каталогов. Пользователи обычно могут устанавливать и редактировать эти переменные для управления поведением модулей Find (например, вводя путь к библиотеке вручную):
-
Xxx_LIBRARY -
Путь к библиотеке. Используйте этот формат только когда модуль предоставляет одну библиотеку. Это уместно использовать в качестве переменной результата в команде
find_library(). -
Xxx_Yy_LIBRARY -
Путь к библиотеке
Yyпредоставляемой модулемXxx. Используйте этот формат, когда модуль предоставляет более одной библиотеки или когда другие модули также могут предоставлять библиотеку с тем же именем. Этот формат также уместно использовать в качестве переменной результата в командеfind_library(). -
Xxx_INCLUDE_DIR -
Когда модуль предоставляет только одну библиотеку, эта переменная может использоваться для указания местоположения заголовков для использования библиотеки (или, точнее, пути, который потребители библиотеки должны добавить в свой путь поиска заголовков). Это уместно использовать в качестве переменной результата в команде
find_path(). -
Xxx_Yy_INCLUDE_DIR -
Если модуль предоставляет более одной библиотеки или когда другие модули также могут предоставлять библиотеку с тем же именем, рекомендуется использовать этот формат для указания местоположения заголовков для использования библиотеки
Yyпредоставляемой модулем. Снова, это уместно использовать в качестве переменной результата в командеfind_path().
Чтобы предотвратить перегрузку пользователей настройками, старайтесь сохранять как можно больше параметров вне кэша, оставляя как минимум один параметр, который может быть использован для отключения использования модуля или поиска ненайденной библиотеки (например, Xxx_ROOT_DIR). По той же причине, отмечайте большинство параметров кэша как расширенные. Для пакетов, которые предоставляют как отладочные, так и релизные двоичные файлы, обычно создаются переменные кэша с суффиксом _LIBRARY_<CONFIG>, например Foo_LIBRARY_RELEASE и Foo_LIBRARY_DEBUG. Модуль SelectLibraryConfigurations может быть полезен в таких случаях.
Хотя это стандартные имена переменных, вы должны обеспечить обратную совместимость для любых старых имён, которые фактически использовались. Убедитесь, что вы комментируете их как устаревшие, чтобы никто не начал их использовать.
Пример модуля Find
Мы опишем, как создать простой модуль поиска для библиотеки Foo.
В начале модуля должен быть указан лицензионный текст, за которым следует пустая строка, а затем — Комментарий в скобках. Комментарий должен начинаться с .rst:, чтобы указать, что остальная часть его содержимого — документация в формате reStructuredText. Например:
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. #[=======================================================================[.rst: FindFoo ------- Finds the Foo library. Imported Targets ^^^^^^^^^^^^^^^^ This module provides the following imported targets, if found: ``Foo::Foo`` The Foo library Result Variables ^^^^^^^^^^^^^^^^ This will define the following variables: ``Foo_FOUND`` True if the system has the Foo library. ``Foo_VERSION`` The version of the Foo library which was found. ``Foo_INCLUDE_DIRS`` Include directories needed to use Foo. ``Foo_LIBRARIES`` Libraries needed to link to Foo. Cache Variables ^^^^^^^^^^^^^^^ The following cache variables may also be set: ``Foo_INCLUDE_DIR`` The directory containing ``foo.h``. ``Foo_LIBRARY`` The path to the Foo library. #]=======================================================================]
Документация модуля состоит из:
- Заголовок с подчеркнутым именем модуля.
- Простого описания того, что ищет модуль. Для некоторых пакетов может потребоваться более подробное описание. Если есть предостережения или другие детали, о которых пользователи модуля должны знать, укажите их здесь.
- Раздел, перечисляющий импортированные цели, предоставляемые модулем, если таковые имеются.
- Раздел, перечисляющий переменные результатов, предоставляемые модулем.
- Необязательно, раздел, перечисляющий переменные кэша, используемые модулем, если таковые имеются.
Если пакет предоставляет какие-либо макросы или функции, они должны быть перечислены в дополнительном разделе, но могут быть задокументированы дополнительными .rst: блоками комментариев непосредственно перед определением этих макросов или функций.
Реализация модуля поиска может начаться под блоком документации. Теперь нужно найти фактические библиотеки и так далее. Код здесь, очевидно, будет отличаться от модуля к модулю (работа с этим, в конце концов, и есть цель модулей поиска), но часто существует общая схема для библиотек.
Сначала мы пытаемся использовать pkg-config для поиска библиотеки. Обратите внимание, что мы не можем полагаться на это, так как оно может быть недоступно, но оно даёт хорошую отправную точку.
find_package(PkgConfig) pkg_check_modules(PC_Foo QUIET Foo)
Это должно определить некоторые переменные, начинающиеся с PC_Foo_, которые содержат информацию из файла Foo.pc.
Теперь нам нужно найти библиотеки и включить файлы; мы используем информацию из pkg-config для предоставления подсказок CMake о том, где искать.
find_path(Foo_INCLUDE_DIR
NAMES foo.h
PATHS ${PC_Foo_INCLUDE_DIRS}
PATH_SUFFIXES Foo
)
find_library(Foo_LIBRARY
NAMES foo
PATHS ${PC_Foo_LIBRARY_DIRS}
)
В качестве альтернативы, если библиотека доступна с несколькими конфигурациями, вы можете использовать SelectLibraryConfigurations, чтобы автоматически установить переменную Foo_LIBRARY:
find_library(Foo_LIBRARY_RELEASE
NAMES foo
PATHS ${PC_Foo_LIBRARY_DIRS}/Release
)
find_library(Foo_LIBRARY_DEBUG
NAMES foo
PATHS ${PC_Foo_LIBRARY_DIRS}/Debug
)
include(SelectLibraryConfigurations)
select_library_configurations(Foo)
Если у вас есть удобный способ получения версии (например, из заголовочного файла), вы можете использовать эту информацию для установки Foo_VERSION (хотя обратите внимание, что модули поиска традиционно использовали Foo_VERSION_STRING, поэтому вы можете установить оба). В противном случае попробуйте использовать информацию из pkg-config
set(Foo_VERSION ${PC_Foo_VERSION})
Теперь мы можем использовать FindPackageHandleStandardArgs для выполнения большей части остальной работы.
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Foo
FOUND_VAR Foo_FOUND
REQUIRED_VARS
Foo_LIBRARY
Foo_INCLUDE_DIR
VERSION_VAR Foo_VERSION
)
Это проверит, что REQUIRED_VARS содержат значения (которые не заканчиваются на -NOTFOUND) и установит Foo_FOUND соответствующим образом. Также будут кэшированы эти значения. Если Foo_VERSION установлено, и требуемая версия была передана в find_package(), она проверит запрошенную версию по отношению к версии в Foo_VERSION. Также будут выведены соответствующие сообщения; обратите внимание, что если пакет был найден, то будет выведено содержимое первой требуемой переменной, чтобы указать, где он был найден.
На этом этапе нам нужно предоставить способ для пользователей модуля поиска привязки к библиотеке или библиотекам, которые были найдены. Существует два подхода, как обсуждалось в разделе «Модули поиска» выше. Традиционный подход с использованием переменных выглядит следующим образом
if(Foo_FOUND)
set(Foo_LIBRARIES ${Foo_LIBRARY})
set(Foo_INCLUDE_DIRS ${Foo_INCLUDE_DIR})
set(Foo_DEFINITIONS ${PC_Foo_CFLAGS_OTHER})
endif()
Если было найдено более одной библиотеки, все они должны быть включены в эти переменные (см. раздел «Стандартные имена переменных» для получения дополнительной информации).
При предоставлении импортированных целевых объектов они должны быть снабжены именованным пространством (отсюда префикс Foo::); CMake распознает, что значения, переданные в target_link_libraries(), содержащие :: в своем имени, являются импортированными целевыми объектами (а не просто именами библиотек), и будет генерировать соответствующие диагностические сообщения, если этот целевой объект не существует (см. политику CMP0028).
if(Foo_FOUND AND NOT TARGET Foo::Foo)
add_library(Foo::Foo UNKNOWN IMPORTED)
set_target_properties(Foo::Foo PROPERTIES
IMPORTED_LOCATION "${Foo_LIBRARY}"
INTERFACE_COMPILE_OPTIONS "${PC_Foo_CFLAGS_OTHER}"
INTERFACE_INCLUDE_DIRECTORIES "${Foo_INCLUDE_DIR}"
)
endif()
Следует отметить, что свойства INTERFACE_INCLUDE_DIRECTORIES и аналогичные должны содержать только информацию о самом целевом объекте, а не о его зависимостях. Вместо этого эти зависимости также должны быть целевыми объектами, и CMake необходимо сообщить, что они являются зависимостями этого целевого объекта. Затем CMake автоматически объединит всю необходимую информацию.
Тип целевого объекта IMPORTED, созданного в команде add_library(), всегда может быть указан как UNKNOWN тип. Это упрощает код в тех случаях, когда могут быть найдены статические или динамические варианты, и CMake определит тип, проверив файлы.
Если библиотека доступна с несколькими конфигурациями, свойство целевого объекта IMPORTED_CONFIGURATIONS также должно быть заполнено:
if(Foo_FOUND)
if (NOT TARGET Foo::Foo)
add_library(Foo::Foo UNKNOWN IMPORTED)
endif()
if (Foo_LIBRARY_RELEASE)
set_property(TARGET Foo::Foo APPEND PROPERTY
IMPORTED_CONFIGURATIONS RELEASE
)
set_target_properties(Foo::Foo PROPERTIES
IMPORTED_LOCATION_RELEASE "${Foo_LIBRARY_RELEASE}"
)
endif()
if (Foo_LIBRARY_DEBUG)
set_property(TARGET Foo::Foo APPEND PROPERTY
IMPORTED_CONFIGURATIONS DEBUG
)
set_target_properties(Foo::Foo PROPERTIES
IMPORTED_LOCATION_DEBUG "${Foo_LIBRARY_DEBUG}"
)
endif()
set_target_properties(Foo::Foo PROPERTIES
INTERFACE_COMPILE_OPTIONS "${PC_Foo_CFLAGS_OTHER}"
INTERFACE_INCLUDE_DIRECTORIES "${Foo_INCLUDE_DIR}"
)
endif()
Вариант RELEASE должен быть указан первым в свойстве, чтобы вариант был выбран, если пользователь использует конфигурацию, которая не является точной копией ни одного из перечисленных IMPORTED_CONFIGURATIONS.
Большинство переменных кэша должны быть скрыты в интерфейсе ccmake до тех пор, пока пользователь явно не попросит их отредактировать.
mark_as_advanced( Foo_INCLUDE_DIR Foo_LIBRARY )
Если этот модуль заменяет более старую версию, вам необходимо установить переменные совместимости, чтобы свести к минимуму возможные нарушения.
# compatibility variables
set(Foo_VERSION_STRING ${Foo_VERSION})
© 2000–2022 Kitware, Inc. and Contributors
Licensed under the BSD 3-clause License.
https://cmake.org/cmake/help/v3.25/manual/cmake-developer.7.html