Spec-Zone.ru › CMake 3.17

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 , который использует подход установки переменных (вместо или дополнительно к созданию импортированных целевых объектов), для сохранения согласованности между модулями поиска следует использовать следующие имена переменных. Обратите внимание, что все переменные начинаются с Xxx_ для предотвращения конфликтов с другими модулями поиска; то же самое относится к макросам, функциям и импортированным целевым объектам.

Xxx_INCLUDE_DIRS

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

Xxx_LIBRARIES

Библиотеки для линковки с использованием Xxx. Они должны содержать полные пути. Это не должно быть записью кэша.

Xxx_DEFINITIONS

Определения для использования при компиляции кода, использующего Xxx. Это действительно не должно включать параметры, такие как -DHAS_JPEG, которые клиентский файл исходного кода использует для определения, следует ли #include <jpeg.h>.

Xxx_EXECUTABLE

Местоположение инструмента Xxx.

Xxx_Yyy_EXECUTABLE

Местоположение инструмента Yyy, входящего в состав Xxx.

Xxx_LIBRARY_DIRS

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

Xxx_ROOT_DIR

Местоположение базового каталога Xxx.

Xxx_VERSION_Yy

Ожидать версию Yy, если true. Убедитесь, что истинными являются не более чем одно из этих значений.

Xxx_WRAP_Yy

Если False, не пытайтесь использовать соответствующую команду обертки CMake.

Xxx_Yy_FOUND

Если False, необязательная часть Yy системы Xxx недоступна.

Xxx_FOUND

Установите в значение false или не определено, если Xxx не найден или не должен использоваться.

Xxx_NOT_FOUND_MESSAGE

Должно устанавливаться файлами конфигурации в случае, если им было установлено Xxx_FOUND в значение FALSE. Содержимое сообщения будет выводиться командой find_package() и find_package_handle_standard_args() для информирования пользователя о проблеме.

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

Xxx_LIBRARY

Путь к библиотеке Xxx (например, для использования с find_library()).

Xxx_Yy_LIBRARY

Путь к библиотеке Yy, которая входит в систему Xxx. Она может быть или не быть необходимой для использования Xxx.

Xxx_INCLUDE_DIR

Местоположение заголовков для использования библиотеки Xxx.

Xxx_Yy_INCLUDE_DIR

Местоположение заголовков для использования библиотеки Yy системы Xxx.

Для предотвращения перегрузки пользователей настройками старайтесь оставлять как можно больше вариантов вне кэша, оставляя по крайней мере один параметр, который может быть использован для отключения использования модуля или поиска отсутствующей библиотеки (например, Xxx_ROOT_DIR). По той же причине большинство параметров кэша помечайте как расширенные. Для пакетов, предоставляющих как отладочные, так и релизные двоичные файлы, принято создавать переменные кэша с суффиксом _LIBRARY_<CONFIG>, например, Foo_LIBRARY_RELEASE и Foo_LIBRARY_DEBUG.

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

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

Рассмотрим создание простого модуля поиска для библиотеки 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}
)

Если у вас есть хороший способ получения версии (например, из заголовочного файла), вы можете использовать эту информацию для установки 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–2020 Kitware, Inc. and Contributors
Licensed under the BSD 3-clause License.
https://cmake.org/cmake/help/v3.17/manual/cmake-developer.7.html

Spec-Zone.ru

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