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 , который выбирает подход установки переменных (вместо или дополнительно к созданию импортированных целей), следует использовать следующие имена переменных, чтобы сохранить согласованность между модулями Find. Обратите внимание, что все переменные начинаются с Xxx_, что (если не указано иное) должно точно совпадать с именем файла FindXxx.cmake, включая регистр. Этот префикс в именах переменных гарантирует, что они не конфликтуют с переменными других модулей Find. Такой же шаблон следует использовать для всех макросов, функций и импортированных целей, определенных модулем поиска.
-
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 -
Дополняющая версия найденного пакета, если таковой имеется.
Следующие имена обычно не должны использоваться в файлах CMakeLists.txt. Они предназначены для использования модулями Find для указания и кэширования местоположений определенных файлов или каталогов. Пользователи обычно могут установить и изменить эти переменные для управления поведением модулей Find (например, вводя путь к библиотеке вручную):
-
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 может быть полезен в таких случаях.
Хотя это стандартные имена переменных, вы должны обеспечить обратную совместимость для любых старых имен, которые фактически использовались. Убедитесь, что вы комментируете их как устаревшие, чтобы никто не начал их использовать.
Пример модуля Find
Мы опишем, как создать простой модуль Find для библиотеки 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: блоками комментариев непосредственно над местом определения этих макросов или функций.
Реализация модуля Find может начинаться ниже блока документации. Теперь нужно найти фактические библиотеки и т. д. Код здесь, очевидно, будет отличаться от модуля к модулю (работа с этим, в конце концов, и есть суть модулей Find), но существует общая схема для библиотек.
Сначала мы попытаемся использовать 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.21/manual/cmake-developer.7.html