Spec-Zone.ru › CMake 3.29

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.

ID_VAR <var>

Указывает переменную, в которой будет храниться идентификатор отложенного вызова. Если ID <id> не задан, будет сгенерирован новый идентификатор, и сгенерированный id будет начинаться с нижнего подчеркивания (_).

Текущий запланированный список отложенных вызовов может быть получен:

cmake_language(DEFER [DIRECTORY <dir>] GET_CALL_IDS <var>)

Это позволит сохранить в <var> список отложенных вызовов, разделенных точкой с запятой, идентификаторов. Идентификаторы относятся к области каталога, в которую отложены вызовы (т.е. где они будут выполнены), которая может отличаться от области, в которой они были созданы. Параметр DIRECTORY может использоваться для указания области, для которой необходимо получить идентификаторы вызовов. Если этот параметр не задан, будут возвращены идентификаторы вызовов для текущей области каталога.

Подробности определенного вызова могут быть получены из его id:

cmake_language(DEFER [DIRECTORY <dir>] GET_CALL <id> <var>)

Это позволит сохранить в <var> список отложенных вызовов, разделенных точкой с запятой, в котором первый элемент — имя вызываемой команды, а остальные элементы — её невычисленные аргументы (любые содержащиеся символы ; включаются буквально и не могут быть отличимы от нескольких аргументов). Если несколько вызовов запланированы с одинаковым id, возвращается первый из них. Если ни один вызов не запланирован с заданным id в указанной области 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) вне этого контекста приведет к ошибке.

Примечание

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

Аргументы вызова find_package(), запросившего зависимость. Первым из этих аргументов всегда будет имя зависимости. Имена зависимостей чувствительны к регистру для этого метода, поскольку find_package() также обрабатывает их как чувствительные к регистру.

Если команда-поставщик удовлетворяет запрос, она должна установить ту же переменную, которую ожидает find_package(). Для зависимости с именем depName, поставщик должен установить depName_FOUND в значение true, если запрос удовлетворён. Если поставщик возвращается, не установив эту переменную, CMake предположит, что запрос не был удовлетворён, и вернётся к встроенной реализации.

Если поставщику необходимо вызвать встроенную реализацию find_package() в рамках своей обработки, он может сделать это, включив ключевое слово BYPASS_PROVIDER в качестве одного из аргументов.

FETCHCONTENT_MAKEAVAILABLE_SERIAL

Аргументы вызова FetchContent_Declare(), соответствующего запрошенной зависимости, с исключениями:

  • Если SOURCE_DIR или BINARY_DIR не были частью первоначальных аргументов объявления, они будут добавлены со значениями по умолчанию.
  • Если FETCHCONTENT_TRY_FIND_PACKAGE_MODE установлено в NEVER, любые FIND_PACKAGE_ARGS будут опущены.
  • Ключевое слово OVERRIDE_FIND_PACKAGE всегда опускается.

Первым из аргументов всегда будет имя зависимости. Имена зависимостей нечувствительны к регистру для этого метода, так как 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.

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

Spec-Zone.ru

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