Spec-Zone.ru › CMake 3.16

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

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

На данном этапе необходимо предоставить пользователям модуля find способ ссылки на найденную библиотеку или библиотеки. Существует два подхода, как обсуждалось в разделе «Модули поиска» выше. Традиционный подход с использованием переменных выглядит так:

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.16/manual/cmake-developer.7.html

Spec-Zone.ru

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