cmake-developer(7)
Введение
Данное руководство предназначено для разработчиков, работающих с cmake-language(7) кодом, будь то при написании собственных модулей, создании собственных систем сборки или работе над самим CMake.
Для участия в разработке CMake обратитесь к https://cmake.org/get-involved/. Там вы найдете ссылки на инструкции по участию, которые, в свою очередь, ведут к руководствам разработчика для самого CMake.
Доступ к реестру Windows
CMake предоставляет некоторые средства для доступа к реестру на Windows платформах.
Запрос реестра Windows
Добавлена в версии 3.24.
Команда cmake_host_system_information() предлагает возможность запроса реестра на локальном компьютере. Подробнее см. cmake_host_system(QUERY_WINDOWS_REGISTRY).
Поиск с использованием реестра Windows
Изменено в версии 3.24.
Параметры HINTS и PATHS команд find_file(), find_library(), find_path(), find_program() и find_package() на Windows платформе предоставляют возможность запроса реестра.
Формальный синтаксис, указанный с использованием обозначения БНФ с обычными расширениями, для запроса реестра следующий:
registry_query ::= '[' sep_definition? root_key
((key_separator sub_key)? (value_separator value_name_)?)? ']'
sep_definition ::= '{' value_separator '}'
root_key ::= 'HKLM' | 'HKEY_LOCAL_MACHINE' | 'HKCU' | 'HKEY_CURRENT_USER' |
'HKCR' | 'HKEY_CLASSES_ROOT' | 'HKCC' | 'HKEY_CURRENT_CONFIG' |
'HKU' | 'HKEY_USERS'
sub_key ::= element (key_separator element)*
key_separator ::= '/' | '\\'
value_separator ::= element | ';'
value_name ::= element | '(default)'
element ::= character\+
character ::= <any character except key_separator and value_separator>
Необязательный элемент sep_definition позволяет указать строку, используемую для разделения sub_key от элемента value_name. Если он не указан, используется символ ;. Несколько элементов registry_query могут быть указаны как часть пути.
# example using default separator
find_file(... PATHS "/root/[HKLM/Stuff;InstallDir]/lib[HKLM\\\\Stuff;Architecture]")
# example using different specified separators
find_library(... HINTS "/root/[{|}HKCU/Stuff|InstallDir]/lib[{@@}HKCU\\\\Stuff@@Architecture]")
Если элемент value_name не указан или имеет специальное имя (default), возвращается содержимое значения по умолчанию, если оно есть. Поддерживаемые типы для value_name:
-
REG_SZ. -
REG_EXPAND_SZ. Возвращаемые данные расширяются. -
REG_DWORD. -
REG_QWORD.
При неудачном запросе в реестр, как правило, из-за отсутствия ключа или неподдерживаемого типа данных, для выражения запроса [] используется строка /REGISTRY-NOTFOUND.
Модули поиска
«Модуль поиска» — это Find<PackageName>.cmake файл, загружаемый командой find_package() при вызове для <PackageName>.
Основная задача модуля поиска — определить, доступен ли пакет, установить переменную <PackageName>_FOUND для отражения этого и предоставить любые переменные, макросы и импортированные цели, необходимые для использования пакета. Модуль поиска полезен в тех случаях, когда библиотека upstream не предоставляет пакет конфигурационного файла.
Традиционный подход заключается в использовании переменных для всего, включая библиотеки и исполняемые файлы: см. раздел Стандартные имена переменных ниже. Большинство существующих модулей поиска, предоставляемых 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_, которое (если не указано иное) должно точно совпадать с именем файла FindXxx.cmake, включая регистр. Это префикс в именах переменных гарантирует, что они не конфликтуют с переменными других модулей поиска. Такая же структура должна использоваться для любых макросов, функций и импортированных целей, определенных модулем поиска.
-
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модуля. Для данного модуля не должно быть более одной переменной этого типа, установленной в значение «истина». Например, модульBarryмог эволюционировать на протяжении многих лет и пройти ряд различных основных версий. Версия 3 модуляBarryможет установить переменнуюBarry_VERSION_3в значение «истина», тогда как более старая версия модуля может установитьBarry_VERSION_2в значение «истина» вместо этого. Ошибка возникнет, если иBarry_VERSION_3, иBarry_VERSION_2будут установлены в значение «истина». -
Xxx_WRAP_YY -
Когда переменная этой формы установлена в значение «ложь», это указывает на то, что соответствующая оборачивающая команда не должна использоваться. Оборачивающая команда зависит от модуля, она может быть подразумеваемой именем модуля или быть указанной частью
YYпеременной. -
Xxx_Yy_FOUND -
Для переменных этой формы
Yy— это имя компонента для модуля. Оно должно точно совпадать с одним из допустимых имён компонентов, которые могут быть переданы командеfind_package()для модуля. Если переменная этой формы установлена в значение «ложь», это означает, что компонентYyмодуляXxxне был найден или недоступен. Переменные этой формы обычно используются для необязательных компонентов, чтобы вызывающий мог проверить, доступен ли необязательный компонент. -
Xxx_FOUND -
Когда команда
find_package()возвращается вызывающей стороне, эта переменная будет установлена в значение «истина», если модуль был успешно найден. -
Xxx_NOT_FOUND_MESSAGE -
Должна устанавливаться файлами конфигурации в случае, если она установила
Xxx_FOUNDв значение «ЛОЖЬ». Сообщение, содержащееся в ней, будет выводиться командой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 может быть полезен в таких случаях.
Хотя это стандартные имена переменных, вы должны обеспечить обратную совместимость для любых старых имён, которые фактически использовались. Убедитесь, что вы прокомментируете их как устаревшие, чтобы никто не начал их использовать.
Пример модуля поиска
Мы опишем, как создать простой модуль поиска для библиотеки 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) if(PKG_CONFIG_FOUND) pkg_check_modules(PC_Foo QUIET Foo) endif()
Это должно определить некоторые переменные, начинающиеся с 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–2024 Kitware, Inc. and Contributors
Licensed under the BSD 3-clause License.
https://cmake.org/cmake/help/v3.31/manual/cmake-developer.7.html