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 для отражения этого и предоставить любые переменные, макросы и импортированные цели, необходимые для использования пакета. Модуль поиска полезен в тех случаях, когда библиотека верхнего уровня не предоставляет пакет конфигурационного файла.
Традиционный подход заключается в использовании переменных для всего, включая библиотеки и исполняемые файлы: см. раздел Стандартные имена переменных ниже. Это то, что делают большинство существующих модулей поиска, предоставляемых 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модуля. Для данного модуля не должно быть более одной переменной такого формата, установленной в значение 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
Мы опишем, как создать простой модуль поиска для библиотеки 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}
)
В качестве альтернативы, если библиотека доступна с несколькими конфигурациями, вы можете использовать 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.30/manual/cmake-developer.7.html