Spec-Zone.ru › CMake

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_language(TRACE <boolean> ...)

Введение

Эта команда вызывает метаоперации для встроенных команд 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: Если поставщик зависимостей должен быть активен также при вызовах try_compile() для всего проекта, можно задать глобальное свойство PROPAGATE_TOP_LEVEL_INCLUDES_TO_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() не будут проходить через поставщика.

mycomp_provider.cmake
# 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().

mycomp_provider.cmake
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 используется для предотвращения рекурсивного вызова команды поставщика для той же зависимости.

mycomp_provider.cmake
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.

Управление трассировкой

Добавлено в версии 4.2.

cmake_language(TRACE ON [EXPAND])
cmake_language(TRACE OFF)

Подкоманда TRACE управляет трассировкой выполняемых команд и макросов CMake во время работы текущего процесса. Если трассировка включена, ее вывод записывается в том же формате, что и при запуске CMake с параметрами командной строки cmake --trace или cmake --trace-expand.

Области действия трассировки могут быть вложенными. Одновременно могут быть активны несколько вызовов TRACE ON, и каждый вызов TRACE OFF деактивирует один уровень вложенности.

Если CMake запущен с параметрами cmake --trace или cmake --trace-expand, эти параметры переопределяют настройки и принудительно включают трассировку во всем процессе независимо от вызовов cmake_language(TRACE OFF). В таких случаях команду по-прежнему можно вызвать, но она не повлияет на состояние трассировки.

© 2000–2026 Kitware, Inc. and Contributors
Licensed under the BSD 3-clause License.
https://cmake.org/cmake/help/latest/command/cmake_language.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API