Spec-Zone.ru › CMake 3.21

cmake-developer(7)

  • Введение
  • Модули поиска

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

Введение

Данное руководство предназначено для разработчиков, работающих с cmake-language(7) кодом, будь то создание собственных модулей, написание собственных систем сборки или работа над самим CMake.

Чтобы принять участие в разработке CMake, см. https://cmake.org/get-involved/. Там есть ссылки на инструкции по внесению вклада, которые, в свою очередь, ведут к руководствам для разработчиков самого CMake.

Модули поиска

«Модуль поиска» — это 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 , который выбирает подход установки переменных (вместо или дополнительно к созданию импортированных целей), следует использовать следующие имена переменных, чтобы сохранить согласованность между модулями Find. Обратите внимание, что все переменные начинаются с Xxx_, что (если не указано иное) должно точно совпадать с именем файла FindXxx.cmake, включая регистр. Этот префикс в именах переменных гарантирует, что они не конфликтуют с переменными других модулей Find. Такой же шаблон следует использовать для всех макросов, функций и импортированных целей, определенных модулем поиска.

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–2021 Kitware, Inc. and Contributors
Licensed under the BSD 3-clause License.
https://cmake.org/cmake/help/v3.21/manual/cmake-developer.7.html

Spec-Zone.ru

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