Spec-Zone.ru › CMake 3.23

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. Такая же схема должна быть применена ко всем макросам, функциям и импортированным целям, определённым модулем 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 может быть полезен в таких случаях.

Хотя это стандартные имена переменных, вы должны обеспечить обратную совместимость с любыми старыми именами, которые фактически использовались. Убедитесь, что вы прокомментировали их как устаревшие, чтобы никто не начал их использовать.

Пример модуля поиска

Мы опишем, как создать простой модуль поиска для библиотеки 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.23/manual/cmake-developer.7.html

Spec-Zone.ru

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