The TRANSFORM action registry held one live action instance per action for
the whole process, and each instance carried per-call state: a raw Selector
pointer, REPLACE's helper, APPEND/PREPEND's operands. That was harmless
while every action ran to completion uninterrupted, but commit c7af6e94d8
(list(TRANSFORM): Add PREDICATE selector, 2026-04-08, v4.4.0-rc1) added a
selector that runs a user function once per element, interleaved with the
transform. User code reentering list(TRANSFORM) with the same action now
rebinds the shared instance mid-flight. That produces silently wrong
results, and a use-after-free once any element is transformed after the
reentering one. It needs no unusual code to hit: a predicate calling
find_package(Python) reaches list(TRANSFORM ... REPLACE) inside FindPython's
own module.
Make action objects immutable and per-call. Operands and the selector become
constructor arguments, Initialize is deleted, and the registry becomes a
constexpr table of metadata with a MakeTransformAction factory. The
InSelection guard, duplicated in all eight actions, moves into the base class.
This also closes a second hole in the same machinery. TransformActionApply
overrode only the vector form of Initialize, so transform(APPLY, "f",
selector) dispatched to the empty two-argument virtual in the base, left
Selector null, and dereferenced it. With no virtual Initialize left to
inherit, an APPLY action without a cmMakefile is no longer constructible, so
the throwing stub that guarded the vector form is no longer needed.
Close a third, from commit 651f82642c (Add APPLY action for list(TRANSFORM),
2026-04-08, v4.4.0-rc1): the cmMakefile overload performs APPLY
unconditionally but validated only the arity of the action passed to it.
APPEND, PREPEND and APPLY all take one argument, so transform(APPEND, "x",
makefile) passed validation and then silently ran APPLY, calling "x" as a
function instead of appending it. Reject any action but APPLY up front.
That path is unreachable from CMake code, since HandleTransformCommand only
selects the overload for APPLY, and Tests/CMakeLib has no cmMakefile to drive
it with, so it carries no test.
Document the predicate's evaluation order while here. It runs once per
element, immediately before that element would be transformed. The manual
did not state the timing, which matters precisely because a predicate that
reenters list(TRANSFORM) observes the outer call mid-flight.
Fixes: #28031
Prepare for a genex-driven SORT comparator without changing behavior: let
cmList::sort() accept a caller-supplied comparison function, and give the
$<LIST:SORT> COMPARE:/CASE:/ORDER: option parsing a single definition shared
with the canned handler. The new feature can then reuse both rather than
duplicating the sort plumbing and the option syntax.
Issue: #27892
Give the AT/FOR/REGEX selector syntax shared by the $<LIST:TRANSFORM> actions a
single definition, so its diagnostics and index/REGEX semantics cannot drift
between actions. Also let a caller learn which elements a selector picks
without running a transform, as groundwork for actions that drive their own
per-element loop.
Issue: #27892
Allow users to provide a custom comparison function for `list(SORT)`.
The comparator is validated for strict weak ordering at runtime to
produce a clear CMake error rather than a platform-dependent abort.
Closes: #27761
Add a new `PREDICATE` selector to `list(TRANSFORM)` that uses a
user-defined function or macro to decide which elements to transform.
The predicate callable receives each element value and an output
variable name. It must set the output variable to a truthy or falsy
value in PARENT_SCOPE; only elements for which the predicate returns
true are passed to the transform action.
Adds a shared PredicateEvaluator helper class used by both TRANSFORM
PREDICATE and (in a subsequent commit) FILTER PREDICATE.
Includes parser wiring, error and success tests covering all existing
actions (TOUPPER, TOLOWER, REPLACE, STRIP, GENEX_STRIP, APPEND,
PREPEND, APPLY) combined with the PREDICATE selector.
Issue: #27761
Add a new APPLY action to list(TRANSFORM) that invokes a user-defined
function or macro for each element of the list, enabling arbitrary
per-element transformations.
The callable receives the current element value and an output variable
name, and must set the output variable in PARENT_SCOPE to the
transformed value.
Includes documentation, release notes, and tests for both function
and macro callables, OUTPUT_VARIABLE, error cases, and selector
combinations (AT, FOR, REGEX).
Refs: #27761
When doing successive matches, track the input start and current search
start positions separately to prevent the `^` anchor from matching in
the middle of the string. Add policy CMP0186 to provide compatibility.
Issue: #26629Fixes: #16899
Run the `clang-format.bash` script to update all our C and C++ code to a
new style defined by `.clang-format`, now with "east const" enforcement.
Use `clang-format` version 18.
* If you reached this commit for a line in `git blame`, re-run the blame
operation starting at the parent of this commit to see older history
for the content.
* See the parent commit for instructions to rebase a change across this
style transition commit.
Issue: #26123