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