Spec-Zone.ru › CMake 3.15

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

На этом этапе необходимо предоставить пользователям модуля «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.15/manual/cmake-developer.7.html

Spec-Zone.ru

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