Spec-Zone.ru › CMake 3.22

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, если пакет не найден. Если ни то, ни другое не установлено, он должен выводить сообщение об ошибке, если не найдет пакет.

Пакеты, которые находят несколько полунезависимых частей (например, наборы библиотек), должны искать компоненты, перечисленные в 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

Версия исправления найденного пакета, если она есть.

END_OF_DOCUMENT_MARKER

Следующие имена обычно не должны использоваться в файлах CMakeLists.txt. Они предназначены для использования модулями поиска для указания и кэширования расположения определенных файлов или каталогов. Пользователи обычно могут устанавливать и изменять эти переменные для управления поведением модулей поиска (например, вводя путь к библиотеке вручную):

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

Spec-Zone.ru

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