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 или теми, что созданы с помощью команд 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>

Планирует вызов для конца указанной директории вместо текущей. Указанная директория может ссылаться на директорию исходного кода или соответствующую бинарную директорию. Относительные пути обрабатываются относительно текущей директории исходного кода.

Указанная директория должна быть известна CMake, являясь либо корневой директорией, либо добавленной с помощью add_subdirectory(). Кроме того, указанная директория не должна ещё быть завершена. Это означает, что она может быть текущей директорией или одной из её предков.

ID <id>

Указывает идентификатор для отложенного вызова. Идентификатор не может быть пустым и не может начинаться с заглавной буквы A-Z. Идентификатор может начинаться с подчёркивания (_) только если он был сгенерирован автоматически предыдущим вызовом, который использовал 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() не будут проходить через поставщика.

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

Эта команда работает только в режиме обработки скриптов script mode. При использовании вне этого контекста вызовет ошибку.

Значение <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/latest/command/cmake_language.html

Spec-Zone.ru

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