cmake/Help/policy/CMP0199.rst
Matthew Woehlke 35d5a4fd6d GenEx: Partially restore pre-CMP0199 behavior of $<CONFIG>
Modify the implementation of policy CMP0199 to only remove the oddball
configuration map matching of `$<CONFIG>` in `NEW` mode, restoring the
old behavior of matching BOTH the consumer's configuration and the
selected configuration of the imported target. It turns out that users
are more dependent on the former than the latter, and while matching
more than one thing is still dodgy, we will likely need to introduce a
new generator expression to match the selected configuration of the
imported target.

Meanwhile, `$<CONFIG>` on targets imported from CPS still only matches
the selected configuration of the imported target, which is the behavior
specified by CPS. However, this can only happen for `$<CONFIG>`
expressions that were generated internally during import.

Update documentation and test cases accordingly.

Fixes: #27487
Fixes: #27495
2026-01-12 10:58:08 -05:00

98 lines
3.8 KiB
ReStructuredText

CMP0199
-------
.. versionadded:: 4.2
:genex:`$<CONFIG:cfgs>` does not match mapped configurations that are not
selected.
Historically, when a :genex:`$<CONFIG:cfgs>` generator expression appeared in
the properties of an imported target, it would match (that is, evaluate to
``1``) if any of the ``cfgs`` matched *any* of the following:
1. The selected configuration of the imported target being consumed.
2. The configuration of the consuming target.
3. *Any* of the configurations in the :prop_tgt:`MAP_IMPORTED_CONFIG_<CONFIG>`
of the imported target being consumed
(where ``<CONFIG>`` is the configuration of the consuming target),
*whether or not such configurations are valid for the imported target*.
This can result in expressions which are intended to be mutually exclusive
being concurrently evaluated. This can be especially problematic if the value
of a compile definition is intended to be determined by the configuration, as
this lack of exclusivity could result in redefinition.
CMake 4.2 and above prefer to consider *only* the configuration of the
consuming target and (when applicable) the selected configuration of the
imported target; that is, (2) and (1) in the above list. Unfortunately,
because users rely on both of these, this policy is not able to fully prevent
multiple unique ``$<CONFIG:cfg>`` expressions from matching concurrently.
This policy provides compatibility with projects that rely on the historical
behavior. The ``OLD`` behavior for this policy is to retain the historic
behavior as described above. The ``NEW`` behavior is to consider only the
configurations of the consuming and consumed targets.
.. note::
This policy only applies to generator expressions being evaluated as part of
the usage requirements of imported targets which are not imported from |CPS|
packages.
For non-imported targets, both the historic and ongoing behavior is to
consider only the configuration of the consuming target. (The selected
configuration of a non-imported target is always the active build
configuration, which is necessarily the same as the consuming target's
configuration.)
For targets imported from |CPS| packages, **only** the configuration of the
consumed imported target is considered, regardless of the policy setting.
.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 4.2
.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
.. include:: include/STANDARD_ADVICE.rst
.. include:: include/DEPRECATED.rst
Examples
^^^^^^^^
Consider the following imported libraries:
.. code-block:: cmake
add_library(test1 INTERFACE IMPORTED)
set_target_properties(test1 PROPERTIES
IMPORTED_CONFIGURATIONS "DEBUG"
INTERFACE_COMPILE_DEFINITIONS
"$<$<CONFIG:debug>:DEBUG>;$<$<CONFIG:release>:RELEASE>"
)
add_library(test2 INTERFACE IMPORTED)
set_target_properties(test2 PROPERTIES
IMPORTED_CONFIGURATIONS "TEST"
INTERFACE_COMPILE_DEFINITIONS
"$<$<CONFIG:debug>:DEBUG>;$<$<CONFIG:example>:EXAMPLE>;$<$<CONFIG:test>:TEST>"
MAP_IMPORTED_CONFIG_RELEASE "DEBUG;EXAMPLE;TEST"
)
Assume that the consuming project is built in the ``Release`` configuration.
A consumer of ``test1`` will see both ``DEBUG`` and ``RELEASE`` defined,
regardless of the policy setting; ``$<CONFIG:debug>`` evaluates to ``1``
because the selected configuration of ``test1`` is ``DEBUG``, and
``$<CONFIG:release>`` evaluates to ``1`` because the consumer's configuration
is ``Release`` (keeping in mind that configuration matching is
case-insensitive).
Under the ``OLD`` policy, a consumer of ``test2`` would see all of ``DEBUG``,
``EXAMPLE`` and ``TEST`` defined; ``$<CONFIG:debug>``, ``$<CONFIG:example>``
and ``$<CONFIG:test>`` all evaluate to ``1`` because all of these
configurations appear in ``MAP_IMPORTED_CONFIG_RELEASE``.
Under the ``NEW`` policy, a consumer of ``test2`` will see only ``TEST``
defined.
.. |CPS| replace:: Common Package Specification