cmake_language
Новое в версии 3.18.
Вызов мета-операций для команд CMake.
Синтаксис
cmake_language(CALL <command> [<arg>...]) cmake_language(EVAL CODE <code>...) cmake_language(DEFER <options>... CALL <command> [<arg>...]) cmake_language(SET_DEPENDENCY_PROVIDER <command> SUPPORTED_METHODS <methods>...) cmake_language(GET_MESSAGE_LOG_LEVEL <out-var>) cmake_language(EXIT <exit-code>)
Введение
Эта команда будет вызывать мета-операции для встроенных команд CMake или команд, созданных с помощью команд macro() или function().
cmake_language не вводит новую переменную или область применения политики.
Вызов команд
-
cmake_language(CALL <command> [<arg>...]) -
Вызывает именованную
<command>с заданными аргументами (если они есть). Например, код:set(message_command "message") cmake_language(CALL ${message_command} STATUS "Hello World!")эквивалентен
message(STATUS "Hello World!")
Примечание
Для обеспечения согласованности кода следующие команды запрещены:
-
if/elseif/else/endif -
block/endblock -
while/endwhile -
foreach/endforeach -
function/endfunction -
macro/endmacro
-
Вычисление кода
-
cmake_language(EVAL CODE <code>...) -
Вычисляет
<code>...как код CMake.Например, код:
set(A TRUE) set(B TRUE) set(C TRUE) set(condition "(A AND B) OR C") cmake_language(EVAL CODE " if (${condition}) message(STATUS TRUE) else() message(STATUS FALSE) endif()" )эквивалентен
set(A TRUE) set(B TRUE) set(C TRUE) set(condition "(A AND B) OR C") file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/eval.cmake " if (${condition}) message(STATUS TRUE) else() message(STATUS FALSE) endif()" ) include(${CMAKE_CURRENT_BINARY_DIR}/eval.cmake)
Отложенные вызовы
Новое в версии 3.19.
-
cmake_language(DEFER <options>... CALL <command> [<arg>...]) -
Планирует вызов именованной
<command>с заданными аргументами (если они есть) на более позднее время. По умолчанию отложенные вызовы выполняются так, как если бы они были написаны в конце файлаCMakeLists.txtтекущей директории, за исключением того, что они выполняются даже после вызоваreturn(). Ссылки на переменные в аргументах вычисляются в момент выполнения отложенного вызова.Доступны следующие параметры:
-
DIRECTORY <dir> -
Планирует вызов для конца заданной директории вместо текущей директории.
<dir>может ссылаться на директорию исходного кода или соответствующую директорию двоичных файлов. Относительные пути рассматриваются как относительные к текущей директории исходного кода.Заданная директория должна быть известна CMake, являясь либо корневой директорией, либо добавленной с помощью
add_subdirectory(). Кроме того, заданная директория не должна быть ещё обработана. Это означает, что она может быть текущей директорией или одной из её предков. -
ID <id> -
Указывает идентификатор для отложенного вызова.
<id>не может быть пустым и не может начинаться с заглавной буквыA-Z.<id>может начинаться с подчёркивания (_) только в том случае, если он был сгенерирован автоматически предыдущим вызовом, который использовалID_VARдля получения идентификатора. -
ID_VAR <var> -
Указывает переменную, в которой нужно сохранить идентификатор отложенного вызова. Если
ID <id>не указано, будет сгенерирован новый идентификатор, и сгенерированный идентификатор будет начинаться с подчёркивания (_).
Текущий список запланированных отложенных вызовов можно получить:
cmake_language(DEFER [DIRECTORY <dir>] GET_CALL_IDS <var>)
Это сохранит в
<var>список, разделённый точкой с запятой идентификаторов отложенных вызовов. Идентификаторы относятся к области директории, в которую были отложены вызовы (т. е. где они будут выполнены), что может отличаться от области, в которой они были созданы. ПараметрDIRECTORYможет быть использован для указания области, для которой необходимо получить идентификаторы вызовов. Если этот параметр не указан, будут возвращены идентификаторы вызовов для текущей области директории.Подробности конкретного вызова можно получить по его идентификатору:
cmake_language(DEFER [DIRECTORY <dir>] GET_CALL <id> <var>)
Это сохранит в
<var>список, разделённый точкой с запятой, в котором первый элемент — имя вызываемой команды, а остальные элементы — её неучтённые аргументы (любые содержащие;символы включаются буквально и не могут быть отличимы от нескольких аргументов). Если несколько вызовов запланированы с одинаковым идентификатором, это возвращает первый из них. Если в указанной областиDIRECTORY(или текущей области директории, если параметрDIRECTORYне указан) нет вызовов с заданным идентификатором, в переменную записывается пустая строка.Отложенные вызовы могут быть отменены по их идентификатору:
cmake_language(DEFER [DIRECTORY <dir>] CANCEL_CALL <id>...)
Это отменяет все отложенные вызовы, соответствующие любым из заданных идентификаторов в указанной области
DIRECTORY(или текущей области директории, если параметрDIRECTORYне указан). Неизвестные идентификаторы игнорируются. -
Примеры отложенных вызовов
Например, код:
cmake_language(DEFER CALL message "${deferred_message}")
cmake_language(DEFER ID_VAR id CALL message "Canceled Message")
cmake_language(DEFER CANCEL_CALL ${id})
message("Immediate Message")
set(deferred_message "Deferred Message")
выводит:
Immediate Message Deferred Message
Canceled Message никогда не выводится, потому что его команда отменена. Ссылка на переменную deferred_message не вычисляется до места вызова, поэтому она может быть установлена после планирования отложенного вызова.
Для немедленного вычисления ссылок на переменные при планировании отложенного вызова оберните его с помощью cmake_language(EVAL). Однако обратите внимание, что аргументы будут перевычислены в отложенном вызове, хотя этого можно избежать, используя скобочные аргументы. Например:
set(deferred_message "Deferred Message 1")
set(re_evaluated [[${deferred_message}]])
cmake_language(EVAL CODE "
cmake_language(DEFER CALL message [[${deferred_message}]])
cmake_language(DEFER CALL message \"${re_evaluated}\")
")
message("Immediate Message")
set(deferred_message "Deferred Message 2")
также выводит:
Immediate Message Deferred Message 1 Deferred Message 2
Поставщики зависимостей
Новое в версии 3.24.
Примечание
Подробное введение в эту функцию можно найти в Руководстве по использованию зависимостей.
-
cmake_language(SET_DEPENDENCY_PROVIDER <command> SUPPORTED_METHODS <methods>...) -
Когда вызывается
find_package()илиFetchContent_MakeAvailable(), вызов может быть перенаправлен поставщику зависимостей, который затем имеет возможность выполнить запрос. Если запрос относится к одной из<methods>, указанных при установке поставщика, CMake вызывает метод<command>поставщика с набором аргументов, специфичных для метода. Если поставщик не выполняет запрос, или если поставщик не поддерживает метод запроса, или поставщик не установлен, используется встроенная реализацияfind_package()илиFetchContent_MakeAvailable()для выполнения запроса обычным образом.При установке поставщика можно указать один или несколько из следующих значений для
<methods>.-
FIND_PACKAGE -
Команда поставщика принимает запросы
find_package(). -
FETCHCONTENT_MAKEAVAILABLE_SERIAL -
Команда поставщика принимает запросы
FetchContent_MakeAvailable(). Ожидается, что каждая зависимость будет подаваться команде поставщика по одной, а не весь список сразу.
В любой момент времени может быть установлен только один поставщик. Если поставщик уже установлен при вызове
cmake_language(SET_DEPENDENCY_PROVIDER), новый поставщик заменяет ранее установленного. Указанный<command>должен уже существовать при вызовеcmake_language(SET_DEPENDENCY_PROVIDER). В качестве специального случая, предоставление пустой строки для<command>и отсутствие<methods>удалит любого ранее установленного поставщика.Поставщик зависимостей может быть установлен только при обработке одного из файлов, указанных в переменной
CMAKE_PROJECT_TOP_LEVEL_INCLUDES. Таким образом, поставщики зависимостей могут быть установлены только в рамках первого вызоваproject(). Вызовcmake_language(SET_DEPENDENCY_PROVIDER)вне этого контекста приведёт к ошибке.Новое в версии 3.30: Глобальное свойство
PROPAGATE_TOP_LEVEL_INCLUDES_TO_TRY_COMPILEможет быть установлено, если поставщик зависимостей также хочет быть включён в вызовыtry_compile()на уровне всего проекта.Примечание
Выбор поставщика зависимостей всегда должен быть под контролем пользователя. Для удобства проект может предоставить файл, который пользователи могут перечислить в своей переменной
CMAKE_PROJECT_TOP_LEVEL_INCLUDES, но использование такого файла должно быть на усмотрение пользователя. -
Команды поставщика
Поставщики определяют единственное <command> для обработки запросов. Название команды должно быть специфичным для поставщика, а не слишком общим, чтобы его могли использовать и другие поставщики. Это позволяет пользователям комбинировать различные поставщики в собственных пользовательских поставщиках. Рекомендуемая форма — xxx_provide_dependency(), где xxx — это часть, специфичная для поставщика (например, vcpkg_provide_dependency(), conan_provide_dependency(), ourcompany_provide_dependency() и так далее).
xxx_provide_dependency(<method> [<method-specific-args>...])
Поскольку некоторые методы ожидают, что определённые переменные будут установлены в области вызова, команда поставщика обычно реализуется как макрос, а не как функция. Это гарантирует, что она не создаёт новую область видимости переменных.
Аргументы, которые CMake передает поставщику зависимостей, зависят от типа запроса. Первый аргумент всегда — метод, и он всегда будет одним из <methods>, указанных при настройке поставщика.
-
FIND_PACKAGE -
В
<method-specific-args>будет содержаться всё, что передано вызовуfind_package(), запросившему зависимость. Поэтому первым из этих<method-specific-args>всегда будет имя зависимости. Имена зависимостей чувствительны к регистру в этом методе, потому чтоfind_package()также обрабатывает их как чувствительные к регистру.Если команда поставщика удовлетворяет запрос, она должна установить ту же переменную, которую ожидает
find_package(). Для зависимости с именемdepNameпоставщик должен установитьdepName_FOUNDв значение true, если удовлетворил запрос. Если поставщик возвращается без установки этой переменной, CMake посчитает, что запрос не был удовлетворен, и вернётся к встроенной реализации.Если поставщику необходимо вызвать встроенную реализацию
find_package()в рамках обработки, он может сделать это, включив ключевое словоBYPASS_PROVIDERв качестве одного из аргументов. -
FETCHCONTENT_MAKEAVAILABLE_SERIAL -
В
<method-specific-args>будет содержаться всё, что передано вызовуFetchContent_Declare(), соответствующему запрошенной зависимости, за исключением следующих случаев:- Если
SOURCE_DIRилиBINARY_DIRне были частью исходных объявленных аргументов, они будут добавлены со своими значениями по умолчанию. - Если
FETCHCONTENT_TRY_FIND_PACKAGE_MODEустановлено вNEVER, любыеFIND_PACKAGE_ARGSбудут опущены. - Ключевое слово
OVERRIDE_FIND_PACKAGEвсегда опускается.
Первый из
<method-specific-args>всегда будет именем зависимости. Имена зависимостей нечувствительны к регистру в этом методе, посколькуFetchContentтакже обрабатывает их как нечувствительные к регистру.Если поставщик выполняет запрос, он должен вызвать
FetchContent_SetPopulated(), передавая имя зависимости в качестве первого аргумента. АргументыSOURCE_DIRиBINARY_DIRв этой команде следует указывать только в том случае, если поставщик делает каталоги исходного кода и сборки зависимости доступными точно так же, как команда встроенногоFetchContent_MakeAvailable().Если поставщик возвращается без вызова
FetchContent_SetPopulated()для указанной зависимости, CMake посчитает, что запрос не был удовлетворен, и вернётся к встроенной реализации.Обратите внимание, что пустые аргументы могут иметь значение для этого метода (например, пустая строка после ключевого слова
GIT_SUBMODULES). Поэтому, если эти аргументы передаются другой команде, необходимо проявлять особую осторожность, чтобы избежать того, что такие аргументы будут утеряны.Если
FETCHCONTENT_SOURCE_DIR_<uppercaseDepName>установлено, тогда поставщик зависимостей никогда не увидит запросы на зависимость<depName>для этого метода. Когда пользователь устанавливает такую переменную, он явно переопределяет источник этой зависимости, и несёт ответственность за то, что его переопределённая версия соответствует всем требованиям к этой зависимости и совместима с любыми другими частями проекта, использующими её. В зависимости от значенияFETCHCONTENT_TRY_FIND_PACKAGE_MODEи того, был ли передан параметрOVERRIDE_FIND_PACKAGEвFetchContent_Declare(), установкаFETCHCONTENT_SOURCE_DIR_<uppercaseDepName>также может помешать поставщику зависимостей увидеть запросы на вызовfind_package(depName). - Если
Примеры поставщиков
В первом примере перехватываются только вызовы find_package(). Команда поставщика запускает внешнюю утилиту, которая копирует соответствующие артефакты в директорию, специфичную для поставщика, если эта утилита знает о зависимости. Затем она полагается на встроенную реализацию для поиска этих артефактов. Вызовы FetchContent_MakeAvailable() не будут обрабатываться поставщиком.
# Always ensure we have the policy settings this provider expects
cmake_minimum_required(VERSION 3.24)
set(MYCOMP_PROVIDER_INSTALL_DIR ${CMAKE_BINARY_DIR}/mycomp_packages
CACHE PATH "The directory this provider installs packages to"
)
# Tell the built-in implementation to look in our area first, unless
# the find_package() call uses NO_..._PATH options to exclude it
list(APPEND CMAKE_MODULE_PATH ${MYCOMP_PROVIDER_INSTALL_DIR}/cmake)
list(APPEND CMAKE_PREFIX_PATH ${MYCOMP_PROVIDER_INSTALL_DIR})
macro(mycomp_provide_dependency method package_name)
execute_process(
COMMAND some_tool ${package_name} --installdir ${MYCOMP_PROVIDER_INSTALL_DIR}
COMMAND_ERROR_IS_FATAL ANY
)
endmacro()
cmake_language(
SET_DEPENDENCY_PROVIDER mycomp_provide_dependency
SUPPORTED_METHODS FIND_PACKAGE
)
Пользователь, как правило, использует указанный выше файл следующим образом:
cmake -DCMAKE_PROJECT_TOP_LEVEL_INCLUDES=/path/to/mycomp_provider.cmake ...
Следующий пример демонстрирует поставщика, который принимает оба метода, но обрабатывает только одну конкретную зависимость. Он обеспечивает поставку Google Test с помощью FetchContent, но оставляет все другие зависимости для выполнения встроенной реализацией CMake. Он принимает несколько различных имён, что демонстрирует один из способов обойти проекты, которые жёстко задают необычный или нежелательный способ добавления этой конкретной зависимости в сборку. В примере также показано, как использовать команду list() для сохранения переменных, которые могут быть перезаписаны вызовом FetchContent_MakeAvailable().
cmake_minimum_required(VERSION 3.24)
# Because we declare this very early, it will take precedence over any
# details the project might declare later for the same thing
include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG e2239ee6043f73722e7aa812a459f54a28552929 # release-1.11.0
)
# Both FIND_PACKAGE and FETCHCONTENT_MAKEAVAILABLE_SERIAL methods provide
# the package or dependency name as the first method-specific argument.
macro(mycomp_provide_dependency method dep_name)
if("${dep_name}" MATCHES "^(gtest|googletest)$")
# Save our current command arguments in case we are called recursively
list(APPEND mycomp_provider_args ${method} ${dep_name})
# This will forward to the built-in FetchContent implementation,
# which detects a recursive call for the same thing and avoids calling
# the provider again if dep_name is the same as the current call.
FetchContent_MakeAvailable(googletest)
# Restore our command arguments
list(POP_BACK mycomp_provider_args dep_name method)
# Tell the caller we fulfilled the request
if("${method}" STREQUAL "FIND_PACKAGE")
# We need to set this if we got here from a find_package() call
# since we used a different method to fulfill the request.
# This example assumes projects only use the gtest targets,
# not any of the variables the FindGTest module may define.
set(${dep_name}_FOUND TRUE)
elseif(NOT "${dep_name}" STREQUAL "googletest")
# We used the same method, but were given a different name to the
# one we populated with. Tell the caller about the name it used.
FetchContent_SetPopulated(${dep_name}
SOURCE_DIR "${googletest_SOURCE_DIR}"
BINARY_DIR "${googletest_BINARY_DIR}"
)
endif()
endif()
endmacro()
cmake_language(
SET_DEPENDENCY_PROVIDER mycomp_provide_dependency
SUPPORTED_METHODS
FIND_PACKAGE
FETCHCONTENT_MAKEAVAILABLE_SERIAL
)
Последний пример демонстрирует, как изменить аргументы вызова find_package(). Он принудительно добавляет ключевое слово QUIET ко всем таким вызовам. Он использует ключевое слово BYPASS_PROVIDER для предотвращения рекурсивного вызова команды поставщика для той же зависимости.
cmake_minimum_required(VERSION 3.24)
macro(mycomp_provide_dependency method)
find_package(${ARGN} BYPASS_PROVIDER QUIET)
endmacro()
cmake_language(
SET_DEPENDENCY_PROVIDER mycomp_provide_dependency
SUPPORTED_METHODS FIND_PACKAGE
)
Получение текущего уровня ведения журнала сообщений
Новая версия с 3.25.
-
cmake_language(GET_MESSAGE_LOG_LEVEL <output_variable>) -
Записывает текущий уровень ведения журнала
message()в заданный<output_variable>.См.
message()для возможных уровней ведения журнала.Текущий уровень ведения журнала сообщений может быть установлен либо с помощью опции командной строки
--log-levelпрограммыcmake(1), либо с помощью переменнойCMAKE_MESSAGE_LOG_LEVEL.Если заданы как опция командной строки, так и переменная, приоритет имеет опция командной строки. Если ни то, ни другое не задано, возвращается уровень ведения журнала по умолчанию.
Завершение сценариев
Новая версия с 3.29.
-
cmake_language(EXIT <exit-code>) -
Завершить выполнение текущего сценария
cmake -Pи выйти с кодом<exit-code>.Эта команда работает только в режиме обработки сценариев. Если она используется вне этого контекста, она вызовет ошибку.
Значение
<exit-code>должно быть неотрицательным. Если<exit-code>отрицательно, поведение не определено (например, в Windows код ошибки -1 становится0xffffffff, а в Linux — 255). Коды выхода, превышающие 255, могут не поддерживаться базовой оболочкой или платформой, а некоторые оболочки могут трактовать значения, превышающие 125, особым образом. Поэтому рекомендуется указывать значение<exit-code>в диапазоне от 0 до 125.
© 2000–2024 Kitware, Inc. and Contributors
Licensed under the BSD 3-clause License.
https://cmake.org/cmake/help/v3.30/command/cmake_language.html