Spec-Zone.ru › CMake 3.29

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 позволяют запрашивать данные из реестра.

Формальный синтаксис, заданный с использованием обозначения БНФ с обычными расширениями, для запроса реестра следующий:

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

Мы опишем, как создать простой модуль 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: непосредственно над определениями этих макросов или функций.

Реализация модуля Find может начаться ниже блока документации. Теперь нужно найти фактические библиотеки и так далее. Код здесь, очевидно, будет отличаться от модуля к модулю (работа с этим, в конце концов, и есть суть модулей Find), но часто используется общая схема для библиотек.

Сначала мы пытаемся использовать 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–2024 Kitware, Inc. and Contributors
Licensed under the BSD 3-clause License.
https://cmake.org/cmake/help/v3.29/manual/cmake-developer.7.html

Spec-Zone.ru

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