Spec-Zone.ru › CMake 3.25

cmake-developer(7)

  • Введение
  • Доступ к реестру Windows

    • Запрос реестра Windows
    • Поиск с использованием реестра Windows
  • Модули поиска

    • Стандартные имена переменных
    • Пример модуля поиска

Введение

Данное руководство предназначено для разработчиков, работающих с 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API