You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
509 lines
19 KiB
509 lines
19 KiB
cmake_language
|
|
--------------
|
|
|
|
.. versionadded:: 3.18
|
|
|
|
Call meta-operations on CMake commands.
|
|
|
|
Synopsis
|
|
^^^^^^^^
|
|
|
|
.. parsed-literal::
|
|
|
|
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>)
|
|
|
|
Introduction
|
|
^^^^^^^^^^^^
|
|
|
|
This command will call meta-operations on built-in CMake commands or
|
|
those created via the :command:`macro` or :command:`function` commands.
|
|
|
|
``cmake_language`` does not introduce a new variable or policy scope.
|
|
|
|
Calling Commands
|
|
^^^^^^^^^^^^^^^^
|
|
|
|
.. signature::
|
|
cmake_language(CALL <command> [<arg>...])
|
|
|
|
Calls the named ``<command>`` with the given arguments (if any).
|
|
For example, the code:
|
|
|
|
.. code-block:: cmake
|
|
|
|
set(message_command "message")
|
|
cmake_language(CALL ${message_command} STATUS "Hello World!")
|
|
|
|
is equivalent to
|
|
|
|
.. code-block:: cmake
|
|
|
|
message(STATUS "Hello World!")
|
|
|
|
.. note::
|
|
To ensure consistency of the code, the following commands are not allowed:
|
|
|
|
* ``if`` / ``elseif`` / ``else`` / ``endif``
|
|
* ``block`` / ``endblock``
|
|
* ``while`` / ``endwhile``
|
|
* ``foreach`` / ``endforeach``
|
|
* ``function`` / ``endfunction``
|
|
* ``macro`` / ``endmacro``
|
|
|
|
Evaluating Code
|
|
^^^^^^^^^^^^^^^
|
|
|
|
.. signature::
|
|
cmake_language(EVAL CODE <code>...)
|
|
:target: EVAL
|
|
|
|
Evaluates the ``<code>...`` as CMake code.
|
|
|
|
For example, the code:
|
|
|
|
.. code-block:: 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()"
|
|
)
|
|
|
|
is equivalent to
|
|
|
|
.. code-block:: cmake
|
|
|
|
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)
|
|
|
|
Deferring Calls
|
|
^^^^^^^^^^^^^^^
|
|
|
|
.. versionadded:: 3.19
|
|
|
|
.. signature::
|
|
cmake_language(DEFER <options>... CALL <command> [<arg>...])
|
|
|
|
Schedules a call to the named ``<command>`` with the given arguments (if any)
|
|
to occur at a later time. By default, deferred calls are executed as if
|
|
written at the end of the current directory's ``CMakeLists.txt`` file,
|
|
except that they run even after a :command:`return` call. Variable
|
|
references in arguments are evaluated at the time the deferred call is
|
|
executed.
|
|
|
|
The options are:
|
|
|
|
``DIRECTORY <dir>``
|
|
Schedule the call for the end of the given directory instead of the
|
|
current directory. The ``<dir>`` may reference either a source
|
|
directory or its corresponding binary directory. Relative paths are
|
|
treated as relative to the current source directory.
|
|
|
|
The given directory must be known to CMake, being either the top-level
|
|
directory or one added by :command:`add_subdirectory`. Furthermore,
|
|
the given directory must not yet be finished processing. This means
|
|
it can be the current directory or one of its ancestors.
|
|
|
|
``ID <id>``
|
|
Specify an identification for the deferred call.
|
|
The ``<id>`` may not be empty and may not begin with a capital letter ``A-Z``.
|
|
The ``<id>`` may begin with an underscore (``_``) only if it was generated
|
|
automatically by an earlier call that used ``ID_VAR`` to get the id.
|
|
|
|
``ID_VAR <var>``
|
|
Specify a variable in which to store the identification for the
|
|
deferred call. If ``ID <id>`` is not given, a new identification
|
|
will be generated and the generated id will start with an underscore (``_``).
|
|
|
|
The currently scheduled list of deferred calls may be retrieved:
|
|
|
|
.. code-block:: cmake
|
|
|
|
cmake_language(DEFER [DIRECTORY <dir>] GET_CALL_IDS <var>)
|
|
|
|
This will store in ``<var>`` a :ref:`semicolon-separated list <CMake Language
|
|
Lists>` of deferred call ids. The ids are for the directory scope in which
|
|
the calls have been deferred to (i.e. where they will be executed), which can
|
|
be different to the scope in which they were created. The ``DIRECTORY``
|
|
option can be used to specify the scope for which to retrieve the call ids.
|
|
If that option is not given, the call ids for the current directory scope
|
|
will be returned.
|
|
|
|
Details of a specific call may be retrieved from its id:
|
|
|
|
.. code-block:: cmake
|
|
|
|
cmake_language(DEFER [DIRECTORY <dir>] GET_CALL <id> <var>)
|
|
|
|
This will store in ``<var>`` a :ref:`semicolon-separated list <CMake Language
|
|
Lists>` in which the first element is the name of the command to be
|
|
called, and the remaining elements are its unevaluated arguments (any
|
|
contained ``;`` characters are included literally and cannot be distinguished
|
|
from multiple arguments). If multiple calls are scheduled with the same id,
|
|
this retrieves the first one. If no call is scheduled with the given id in
|
|
the specified ``DIRECTORY`` scope (or the current directory scope if no
|
|
``DIRECTORY`` option is given), this stores an empty string in the variable.
|
|
|
|
Deferred calls may be canceled by their id:
|
|
|
|
.. code-block:: cmake
|
|
|
|
cmake_language(DEFER [DIRECTORY <dir>] CANCEL_CALL <id>...)
|
|
|
|
This cancels all deferred calls matching any of the given ids in the specified
|
|
``DIRECTORY`` scope (or the current directory scope if no ``DIRECTORY`` option
|
|
is given). Unknown ids are silently ignored.
|
|
|
|
Deferred Call Examples
|
|
""""""""""""""""""""""
|
|
|
|
For example, the code:
|
|
|
|
.. code-block:: cmake
|
|
|
|
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")
|
|
|
|
prints::
|
|
|
|
Immediate Message
|
|
Deferred Message
|
|
|
|
The ``Canceled Message`` is never printed because its command is
|
|
canceled. The ``deferred_message`` variable reference is not evaluated
|
|
until the call site, so it can be set after the deferred call is scheduled.
|
|
|
|
In order to evaluate variable references immediately when scheduling a
|
|
deferred call, wrap it using ``cmake_language(EVAL)``. However, note that
|
|
arguments will be re-evaluated in the deferred call, though that can be
|
|
avoided by using bracket arguments. For example:
|
|
|
|
.. code-block:: cmake
|
|
|
|
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")
|
|
|
|
also prints::
|
|
|
|
Immediate Message
|
|
Deferred Message 1
|
|
Deferred Message 2
|
|
|
|
.. _dependency_providers:
|
|
|
|
Dependency Providers
|
|
^^^^^^^^^^^^^^^^^^^^
|
|
|
|
.. versionadded:: 3.24
|
|
|
|
.. note:: A high-level introduction to this feature can be found in the
|
|
:ref:`Using Dependencies Guide <dependency_providers_overview>`.
|
|
|
|
.. signature::
|
|
cmake_language(SET_DEPENDENCY_PROVIDER <command>
|
|
SUPPORTED_METHODS <methods>...)
|
|
|
|
When a call is made to :command:`find_package` or
|
|
:command:`FetchContent_MakeAvailable`, the call may be forwarded to a
|
|
dependency provider which then has the opportunity to fulfill the request.
|
|
If the request is for one of the ``<methods>`` specified when the provider
|
|
was set, CMake calls the provider's ``<command>`` with a set of
|
|
method-specific arguments. If the provider does not fulfill the request,
|
|
or if the provider doesn't support the request's method, or no provider
|
|
is set, the built-in :command:`find_package` or
|
|
:command:`FetchContent_MakeAvailable` implementation is used to fulfill
|
|
the request in the usual way.
|
|
|
|
One or more of the following values can be specified for the ``<methods>``
|
|
when setting the provider:
|
|
|
|
``FIND_PACKAGE``
|
|
The provider command accepts :command:`find_package` requests.
|
|
|
|
``FETCHCONTENT_MAKEAVAILABLE_SERIAL``
|
|
The provider command accepts :command:`FetchContent_MakeAvailable`
|
|
requests. It expects each dependency to be fed to the provider command
|
|
one at a time, not the whole list in one go.
|
|
|
|
Only one provider can be set at any point in time. If a provider is already
|
|
set when ``cmake_language(SET_DEPENDENCY_PROVIDER)`` is called, the new
|
|
provider replaces the previously set one. The specified ``<command>`` must
|
|
already exist when ``cmake_language(SET_DEPENDENCY_PROVIDER)`` is called.
|
|
As a special case, providing an empty string for the ``<command>`` and no
|
|
``<methods>`` will discard any previously set provider.
|
|
|
|
The dependency provider can only be set while processing one of the files
|
|
specified by the :variable:`CMAKE_PROJECT_TOP_LEVEL_INCLUDES` variable.
|
|
Thus, dependency providers can only be set as part of the first call to
|
|
:command:`project`. Calling ``cmake_language(SET_DEPENDENCY_PROVIDER)``
|
|
outside of that context will result in an error.
|
|
|
|
.. note::
|
|
The choice of dependency provider should always be under the user's control.
|
|
As a convenience, a project may choose to provide a file that users can
|
|
list in their :variable:`CMAKE_PROJECT_TOP_LEVEL_INCLUDES` variable, but
|
|
the use of such a file should always be the user's choice.
|
|
|
|
Provider commands
|
|
"""""""""""""""""
|
|
|
|
Providers define a single ``<command>`` to accept requests. The name of
|
|
the command should be specific to that provider, not something overly
|
|
generic that another provider might also use. This enables users to compose
|
|
different providers in their own custom provider. The recommended form is
|
|
``xxx_provide_dependency()``, where ``xxx`` is the provider-specific part
|
|
(e.g. ``vcpkg_provide_dependency()``, ``conan_provide_dependency()``,
|
|
``ourcompany_provide_dependency()``, and so on).
|
|
|
|
.. code-block:: cmake
|
|
|
|
xxx_provide_dependency(<method> [<method-specific-args>...])
|
|
|
|
Because some methods expect certain variables to be set in the calling scope,
|
|
the provider command should typically be implemented as a macro rather than a
|
|
function. This ensures it does not introduce a new variable scope.
|
|
|
|
The arguments CMake passes to the dependency provider depend on the type of
|
|
request. The first argument is always the method, and it will only ever
|
|
be one of the ``<methods>`` that was specified when setting the provider.
|
|
|
|
``FIND_PACKAGE``
|
|
The ``<method-specific-args>`` will be everything passed to the
|
|
:command:`find_package` call that requested the dependency. The first of
|
|
these ``<method-specific-args>`` will therefore always be the name of the
|
|
dependency. Dependency names are case-sensitive for this method because
|
|
:command:`find_package` treats them case-sensitively too.
|
|
|
|
If the provider command fulfills the request, it must set the same variable
|
|
that :command:`find_package` expects to be set. For a dependency named
|
|
``depName``, the provider must set ``depName_FOUND`` to true if it fulfilled
|
|
the request. If the provider returns without setting this variable, CMake
|
|
will assume the request was not fulfilled and will fall back to the
|
|
built-in implementation.
|
|
|
|
If the provider needs to call the built-in :command:`find_package`
|
|
implementation as part of its processing, it can do so by including the
|
|
``BYPASS_PROVIDER`` keyword as one of the arguments.
|
|
|
|
``FETCHCONTENT_MAKEAVAILABE_SERIAL``
|
|
The ``<method-specific-args>`` will be everything passed to the
|
|
:command:`FetchContent_Declare` call that corresponds to the requested
|
|
dependency, with the following exceptions:
|
|
|
|
* If ``SOURCE_DIR`` or ``BINARY_DIR`` were not part of the original
|
|
declared arguments, they will be added with their default values.
|
|
* If :variable:`FETCHCONTENT_TRY_FIND_PACKAGE_MODE` is set to ``NEVER``,
|
|
any ``FIND_PACKAGE_ARGS`` will be omitted.
|
|
* The ``OVERRIDE_FIND_PACKAGE`` keyword is always omitted.
|
|
|
|
The first of the ``<method-specific-args>`` will always be the name of the
|
|
dependency. Dependency names are case-insensitive for this method because
|
|
:module:`FetchContent` also treats them case-insensitively.
|
|
|
|
If the provider fulfills the request, it should call
|
|
:command:`FetchContent_SetPopulated`, passing the name of the dependency as
|
|
the first argument. The ``SOURCE_DIR`` and ``BINARY_DIR`` arguments to that
|
|
command should only be given if the provider makes the dependency's source
|
|
and build directories available in exactly the same way as the built-in
|
|
:command:`FetchContent_MakeAvailable` command.
|
|
|
|
If the provider returns without calling :command:`FetchContent_SetPopulated`
|
|
for the named dependency, CMake will assume the request was not fulfilled
|
|
and will fall back to the built-in implementation.
|
|
|
|
Note that empty arguments may be significant for this method (e.g. an empty
|
|
string following a ``GIT_SUBMODULES`` keyword). Therefore, if forwarding
|
|
these arguments on to another command, extra care must be taken to avoid such
|
|
arguments being silently dropped.
|
|
|
|
If ``FETCHCONTENT_SOURCE_DIR_<uppercaseDepName>`` is set, then the
|
|
dependency provider will never see requests for the ``<depName>`` dependency
|
|
for this method. When the user sets such a variable, they are explicitly
|
|
overriding where to get that dependency from and are taking on the
|
|
responsibility that their overriding version meets any requirements for that
|
|
dependency and is compatible with whatever else in the project uses it.
|
|
Depending on the value of :variable:`FETCHCONTENT_TRY_FIND_PACKAGE_MODE`
|
|
and whether the ``OVERRIDE_FIND_PACKAGE`` option was given to
|
|
:command:`FetchContent_Declare`, having
|
|
``FETCHCONTENT_SOURCE_DIR_<uppercaseDepName>`` set may also prevent the
|
|
dependency provider from seeing requests for a ``find_package(depName)``
|
|
call too.
|
|
|
|
Provider Examples
|
|
"""""""""""""""""
|
|
|
|
This first example only intercepts :command:`find_package` calls. The
|
|
provider command runs an external tool which copies the relevant artifacts
|
|
into a provider-specific directory, if that tool knows about the dependency.
|
|
It then relies on the built-in implementation to then find those artifacts.
|
|
:command:`FetchContent_MakeAvailable` calls would not go through the provider.
|
|
|
|
.. code-block:: cmake
|
|
:caption: 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
|
|
)
|
|
|
|
The user would then typically use the above file like so::
|
|
|
|
cmake -DCMAKE_PROJECT_TOP_LEVEL_INCLUDES=/path/to/mycomp_provider.cmake ...
|
|
|
|
The next example demonstrates a provider that accepts both methods, but
|
|
only handles one specific dependency. It enforces providing Google Test
|
|
using :module:`FetchContent`, but leaves all other dependencies to be
|
|
fulfilled by CMake's built-in implementation. It accepts a few different
|
|
names, which demonstrates one way of working around projects that hard-code
|
|
an unusual or undesirable way of adding this particular dependency to the
|
|
build. The example also demonstrates how to use the :command:`list` command
|
|
to preserve variables that may be overwritten by a call to
|
|
:command:`FetchContent_MakeAvailable`.
|
|
|
|
.. code-block:: cmake
|
|
:caption: 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
|
|
)
|
|
|
|
The final example demonstrates how to modify arguments to a
|
|
:command:`find_package` call. It forces all such calls to have the
|
|
``QUIET`` keyword. It uses the ``BYPASS_PROVIDER`` keyword to prevent
|
|
calling the provider command recursively for the same dependency.
|
|
|
|
.. code-block:: cmake
|
|
:caption: 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
|
|
)
|
|
|
|
Getting current message log level
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
.. versionadded:: 3.25
|
|
|
|
.. _query_message_log_level:
|
|
|
|
.. signature::
|
|
cmake_language(GET_MESSAGE_LOG_LEVEL <output_variable>)
|
|
|
|
Writes the current :command:`message` logging level
|
|
into the given ``<output_variable>``.
|
|
|
|
See :command:`message` for the possible logging levels.
|
|
|
|
The current message logging level can be set either using the
|
|
:option:`--log-level <cmake --log-level>`
|
|
command line option of the :manual:`cmake(1)` program or using
|
|
the :variable:`CMAKE_MESSAGE_LOG_LEVEL` variable.
|
|
|
|
If both the command line option and the variable are set, the command line
|
|
option takes precedence. If neither are set, the default logging level
|
|
is returned.
|