mirror of
https://github.com/Kitware/CMake.git
synced 2026-08-04 14:50:23 +00:00
list: Reject macros as APPLY/PREDICATE/COMPARATOR arguments
Fixes: #27781
This commit is contained in:
@@ -139,7 +139,7 @@ Modification
|
||||
:ref:`string(REGEX) <Regex Specification>`.
|
||||
|
||||
``PREDICATE``
|
||||
Specify a user-defined callable as a predicate.
|
||||
Specify a user-defined :command:`function` as a predicate.
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
@@ -148,11 +148,11 @@ Modification
|
||||
.. versionadded:: 4.4
|
||||
|
||||
``<function>`` is a user-defined :command:`function` that acts as a
|
||||
unary predicate. The callable must accept exactly two parameters: the
|
||||
input value and the name of an output variable. The callable must set the
|
||||
unary predicate. The function must accept exactly two parameters: the
|
||||
input value and the name of an output variable. The function must set the
|
||||
output variable to a boolean value in the calling scope.
|
||||
The output variable is interpreted using standard CMake boolean evaluation.
|
||||
If the callable does not set the output variable, it is an error.
|
||||
If the function does not set the output variable, it is an error.
|
||||
|
||||
Example:
|
||||
|
||||
@@ -290,9 +290,9 @@ Modification
|
||||
See policy :policy:`CMP0186`.
|
||||
|
||||
``APPLY``
|
||||
Invoke a user-defined callable for each element of the list.
|
||||
The callable must accept exactly two parameters: the input value and the
|
||||
name of an output variable. The callable must set the output variable
|
||||
Invoke a user-defined :command:`function` for each element of the list.
|
||||
The function must accept exactly two parameters: the input value and the
|
||||
name of an output variable. The function must set the output variable
|
||||
in the calling scope.
|
||||
|
||||
.. signature::
|
||||
@@ -356,8 +356,8 @@ Modification
|
||||
list(TRANSFORM <list> <ACTION> REGEX <regular_expression> ...)
|
||||
|
||||
``PREDICATE``
|
||||
Specify a user-defined callable as a predicate.
|
||||
Only elements for which the callable returns a true value will be
|
||||
Specify a user-defined :command:`function` as a predicate.
|
||||
Only elements for which the function returns a true value will be
|
||||
transformed.
|
||||
|
||||
.. code-block:: cmake
|
||||
@@ -368,9 +368,9 @@ Modification
|
||||
|
||||
``<function>`` is a user-defined :command:`function` with exactly
|
||||
two formal parameters: the input value and the name of an output
|
||||
variable. The callable must set the output variable to a boolean
|
||||
variable. The function must set the output variable to a boolean
|
||||
value. Standard CMake boolean evaluation is used.
|
||||
If the callable does not set the output variable, it is an error.
|
||||
If the function does not set the output variable, it is an error.
|
||||
|
||||
Example:
|
||||
|
||||
@@ -450,21 +450,21 @@ Ordering
|
||||
Sorts the list in descending order.
|
||||
|
||||
Instead of the built-in ``COMPARE`` methods, a user-defined comparison
|
||||
function may be used with the ``COMPARATOR`` keyword.
|
||||
:command:`function` may be used with the ``COMPARATOR`` keyword.
|
||||
|
||||
.. versionadded:: 4.4
|
||||
|
||||
``COMPARATOR`` is mutually exclusive with ``COMPARE``. The ``CASE``
|
||||
and ``ORDER`` options may still be used alongside ``COMPARATOR``:
|
||||
the ``CASE`` filter is applied to both values before they are passed to
|
||||
the callable, and ``ORDER DESCENDING`` reverses the comparison by
|
||||
the function, and ``ORDER DESCENDING`` reverses the comparison by
|
||||
swapping the two arguments.
|
||||
|
||||
The callable must accept exactly three parameters: two input values and
|
||||
The function must accept exactly three parameters: two input values and
|
||||
the name of an output variable. It must set the output variable to a
|
||||
boolean value (``TRUE`` if the first value should come before the second,
|
||||
``FALSE`` otherwise) in the calling scope.
|
||||
If the callable does not set the output variable, it is an error.
|
||||
If the function does not set the output variable, it is an error.
|
||||
|
||||
The comparator must define a
|
||||
`strict weak ordering <https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings>`_.
|
||||
|
||||
@@ -2,9 +2,9 @@ list-PREDICATE
|
||||
--------------
|
||||
|
||||
* The :command:`list(TRANSFORM)` command gained a new ``PREDICATE`` selector
|
||||
that invokes a user-defined callable to decide which elements are
|
||||
that invokes a user-defined :command:`function` to decide which elements are
|
||||
transformed.
|
||||
|
||||
* The :command:`list(FILTER)` command gained a new ``PREDICATE`` mode
|
||||
that invokes a user-defined callable to decide which elements are
|
||||
that invokes a user-defined :command:`function` to decide which elements are
|
||||
included or excluded, complementing the existing ``REGEX`` mode.
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <utility>
|
||||
|
||||
#include <cm/memory>
|
||||
#include <cm/optional>
|
||||
|
||||
#include "cmsys/RegularExpression.hxx"
|
||||
|
||||
@@ -24,6 +25,7 @@
|
||||
#include "cmMakefile.h"
|
||||
#include "cmRange.h"
|
||||
#include "cmState.h"
|
||||
#include "cmStateTypes.h"
|
||||
#include "cmStringAlgorithms.h"
|
||||
#include "cmStringReplaceHelper.h"
|
||||
#include "cmSystemTools.h"
|
||||
@@ -96,10 +98,18 @@ void RequireFunction(cmMakefile const& makefile,
|
||||
std::string const& functionName,
|
||||
std::string const& errorPrefix)
|
||||
{
|
||||
if (!makefile.GetState()->GetCommand(functionName)) {
|
||||
cm::optional<cmStateEnums::CommandType> type =
|
||||
makefile.GetState()->GetCommandType(functionName);
|
||||
if (!type) {
|
||||
throw cmList::transform_error(
|
||||
cmStrCat(errorPrefix, ": unknown function \"", functionName, "\"."));
|
||||
}
|
||||
if (*type == cmStateEnums::CommandType::Macro) {
|
||||
throw cmList::transform_error(
|
||||
cmStrCat(errorPrefix, ": macro \"", functionName,
|
||||
"\" may not be used here;"
|
||||
" define it as a function() instead."));
|
||||
}
|
||||
}
|
||||
|
||||
class PredicateEvaluator
|
||||
|
||||
@@ -12,22 +12,6 @@ message("mylist was: ${mylist}")
|
||||
list(FILTER mylist EXCLUDE PREDICATE starts_with_filter)
|
||||
message("mylist is: ${mylist}")
|
||||
|
||||
# EXCLUDE with macro predicate
|
||||
macro(is_short input result)
|
||||
string(LENGTH "${input}" _len)
|
||||
if(_len LESS 6)
|
||||
set(${result} TRUE)
|
||||
else()
|
||||
set(${result} FALSE)
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
set(mylist ab cdefgh ij klmnop qr)
|
||||
list(FILTER mylist EXCLUDE PREDICATE is_short)
|
||||
if(NOT mylist STREQUAL "cdefgh;klmnop")
|
||||
message(FATAL_ERROR "FILTER(EXCLUDE PREDICATE macro) is \"${mylist}\", expected \"cdefgh;klmnop\"")
|
||||
endif()
|
||||
|
||||
# EXCLUDE on empty list
|
||||
set(empty_list "")
|
||||
list(FILTER empty_list EXCLUDE PREDICATE starts_with_filter)
|
||||
|
||||
@@ -7,28 +7,11 @@ function(starts_with_filter input result)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Predicate as macro
|
||||
macro(is_short input result)
|
||||
string(LENGTH "${input}" _len)
|
||||
if(_len LESS 6)
|
||||
set(${result} TRUE)
|
||||
else()
|
||||
set(${result} FALSE)
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
set(mylist FILTER_THIS_BIT DO_NOT_FILTER_THIS thisisanitem FILTER_THIS_THING)
|
||||
message("mylist was: ${mylist}")
|
||||
list(FILTER mylist INCLUDE PREDICATE starts_with_filter)
|
||||
message("mylist is: ${mylist}")
|
||||
|
||||
# INCLUDE with macro predicate
|
||||
set(mylist ab cdefgh ij klmnop qr)
|
||||
list(FILTER mylist INCLUDE PREDICATE is_short)
|
||||
if(NOT mylist STREQUAL "ab;ij;qr")
|
||||
message(FATAL_ERROR "FILTER(INCLUDE PREDICATE macro) is \"${mylist}\", expected \"ab;ij;qr\"")
|
||||
endif()
|
||||
|
||||
# INCLUDE on empty list
|
||||
set(empty_list "")
|
||||
list(FILTER empty_list INCLUDE PREDICATE starts_with_filter)
|
||||
|
||||
1
Tests/RunCMake/list/FILTER-PREDICATE-Macro-result.txt
Normal file
1
Tests/RunCMake/list/FILTER-PREDICATE-Macro-result.txt
Normal file
@@ -0,0 +1 @@
|
||||
1
|
||||
5
Tests/RunCMake/list/FILTER-PREDICATE-Macro-stderr.txt
Normal file
5
Tests/RunCMake/list/FILTER-PREDICATE-Macro-stderr.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
^CMake Error at FILTER-PREDICATE-Macro\.cmake:11 \(list\):
|
||||
list sub-command FILTER, mode PREDICATE: macro "is_short" may not be used
|
||||
here; define it as a function\(\) instead\.
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists\.txt:3 \(include\)$
|
||||
11
Tests/RunCMake/list/FILTER-PREDICATE-Macro.cmake
Normal file
11
Tests/RunCMake/list/FILTER-PREDICATE-Macro.cmake
Normal file
@@ -0,0 +1,11 @@
|
||||
macro(is_short input result)
|
||||
string(LENGTH "${input}" _len)
|
||||
if(_len LESS 4)
|
||||
set(${result} TRUE)
|
||||
else()
|
||||
set(${result} FALSE)
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
set(mylist alpha bravo charlie)
|
||||
list(FILTER mylist INCLUDE PREDICATE is_short)
|
||||
@@ -38,6 +38,7 @@ run_cmake(FILTER-REGEX-InvalidOperator)
|
||||
run_cmake(FILTER-REGEX-Valid0)
|
||||
run_cmake(FILTER-REGEX-Valid1)
|
||||
run_cmake(FILTER-PREDICATE-UnknownFunction)
|
||||
run_cmake(FILTER-PREDICATE-Macro)
|
||||
run_cmake(FILTER-PREDICATE-NoOutput)
|
||||
run_cmake(FILTER-PREDICATE-TooManyArguments)
|
||||
run_cmake(FILTER-PREDICATE-Include)
|
||||
@@ -71,6 +72,7 @@ run_cmake(TRANSFORM-REPLACE-InvalidReplace1)
|
||||
run_cmake(TRANSFORM-REPLACE-InvalidReplace2)
|
||||
run_cmake(TRANSFORM-APPLY-NoFunction)
|
||||
run_cmake(TRANSFORM-APPLY-UnknownFunction)
|
||||
run_cmake(TRANSFORM-APPLY-Macro)
|
||||
run_cmake(TRANSFORM-APPLY-NoOutput)
|
||||
# 'selector' oriented tests
|
||||
run_cmake(TRANSFORM-Selector-REGEX-NoArguments)
|
||||
@@ -89,6 +91,7 @@ run_cmake(TRANSFORM-Selector-FOR-NegativeStepArgument)
|
||||
run_cmake(TRANSFORM-Selector-FOR-BackwardsRange)
|
||||
run_cmake(TRANSFORM-Selector-PREDICATE-NoArguments)
|
||||
run_cmake(TRANSFORM-Selector-PREDICATE-UnknownFunction)
|
||||
run_cmake(TRANSFORM-Selector-PREDICATE-Macro)
|
||||
run_cmake(TRANSFORM-Selector-PREDICATE-NoOutput)
|
||||
# 'output' oriented tests
|
||||
run_cmake(TRANSFORM-Output-OUTPUT_VARIABLE-NoArguments)
|
||||
@@ -115,6 +118,7 @@ run_cmake(SORT-DuplicateCompareOption)
|
||||
run_cmake(SORT-DuplicateCaseOption)
|
||||
run_cmake(SORT-NoCaseOption)
|
||||
run_cmake(SORT-COMPARATOR-UnknownFunction)
|
||||
run_cmake(SORT-COMPARATOR-Macro)
|
||||
run_cmake(SORT-COMPARATOR-NoOutput)
|
||||
run_cmake(SORT-COMPARATOR-CompareConflict)
|
||||
run_cmake(SORT-COMPARATOR-CompareConflictReverse)
|
||||
|
||||
1
Tests/RunCMake/list/SORT-COMPARATOR-Macro-result.txt
Normal file
1
Tests/RunCMake/list/SORT-COMPARATOR-Macro-result.txt
Normal file
@@ -0,0 +1 @@
|
||||
1
|
||||
5
Tests/RunCMake/list/SORT-COMPARATOR-Macro-stderr.txt
Normal file
5
Tests/RunCMake/list/SORT-COMPARATOR-Macro-stderr.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
^CMake Error at SORT-COMPARATOR-Macro\.cmake:10 \(list\):
|
||||
list sub-command SORT, COMPARATOR: macro "string_less_macro" may not be
|
||||
used here; define it as a function\(\) instead\.
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists\.txt:3 \(include\)$
|
||||
10
Tests/RunCMake/list/SORT-COMPARATOR-Macro.cmake
Normal file
10
Tests/RunCMake/list/SORT-COMPARATOR-Macro.cmake
Normal file
@@ -0,0 +1,10 @@
|
||||
macro(string_less_macro a b result)
|
||||
if(${a} STRLESS ${b})
|
||||
set(${result} TRUE)
|
||||
else()
|
||||
set(${result} FALSE)
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
set(mylist c a b)
|
||||
list(SORT mylist COMPARATOR string_less_macro)
|
||||
@@ -7,15 +7,6 @@ function(string_less a b result)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Comparator as macro
|
||||
macro(string_less_macro a b result)
|
||||
if(${a} STRLESS ${b})
|
||||
set(${result} TRUE)
|
||||
else()
|
||||
set(${result} FALSE)
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
# Comparator: string length comparison
|
||||
function(shorter_first a b result)
|
||||
string(LENGTH "${a}" len_a)
|
||||
@@ -34,13 +25,6 @@ if(NOT mylist STREQUAL "a;b;c")
|
||||
message(FATAL_ERROR "SORT(COMPARATOR function) is \"${mylist}\", expected \"a;b;c\"")
|
||||
endif()
|
||||
|
||||
## COMPARATOR with macro
|
||||
set(mylist c a b)
|
||||
list(SORT mylist COMPARATOR string_less_macro)
|
||||
if(NOT mylist STREQUAL "a;b;c")
|
||||
message(FATAL_ERROR "SORT(COMPARATOR macro) is \"${mylist}\", expected \"a;b;c\"")
|
||||
endif()
|
||||
|
||||
## COMPARATOR with ORDER DESCENDING
|
||||
set(mylist c a b)
|
||||
list(SORT mylist COMPARATOR string_less ORDER DESCENDING)
|
||||
|
||||
1
Tests/RunCMake/list/TRANSFORM-APPLY-Macro-result.txt
Normal file
1
Tests/RunCMake/list/TRANSFORM-APPLY-Macro-result.txt
Normal file
@@ -0,0 +1 @@
|
||||
1
|
||||
5
Tests/RunCMake/list/TRANSFORM-APPLY-Macro-stderr.txt
Normal file
5
Tests/RunCMake/list/TRANSFORM-APPLY-Macro-stderr.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
^CMake Error at TRANSFORM-APPLY-Macro\.cmake:6 \(list\):
|
||||
list sub-command TRANSFORM, action APPLY: macro "wrap_angles" may not be
|
||||
used here; define it as a function\(\) instead\.
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists\.txt:3 \(include\)$
|
||||
6
Tests/RunCMake/list/TRANSFORM-APPLY-Macro.cmake
Normal file
6
Tests/RunCMake/list/TRANSFORM-APPLY-Macro.cmake
Normal file
@@ -0,0 +1,6 @@
|
||||
macro(wrap_angles in out)
|
||||
set(${out} "<${in}>")
|
||||
endmacro()
|
||||
|
||||
set(mylist alpha bravo charlie)
|
||||
list(TRANSFORM mylist APPLY wrap_angles)
|
||||
@@ -8,11 +8,6 @@ function(add_prefix in out)
|
||||
set(${out} "prefix_${in}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Define a transform macro: wrap in angle brackets
|
||||
macro(wrap_angles in out)
|
||||
set(${out} "<${in}>")
|
||||
endmacro()
|
||||
|
||||
set(mylist alpha bravo charlie delta)
|
||||
|
||||
# Basic APPLY - all elements
|
||||
@@ -74,19 +69,6 @@ if(NOT output STREQUAL "prefix_alpha;prefix_bravo;prefix_charlie;prefix_delta")
|
||||
message(FATAL_ERROR "TRANSFORM(APPLY add_prefix) is \"${output}\", expected is \"prefix_alpha;prefix_bravo;prefix_charlie;prefix_delta\"")
|
||||
endif()
|
||||
|
||||
# APPLY with a macro
|
||||
set(mylist alpha bravo charlie)
|
||||
list(TRANSFORM mylist APPLY wrap_angles OUTPUT_VARIABLE output)
|
||||
if(NOT output STREQUAL "<alpha>;<bravo>;<charlie>")
|
||||
message(FATAL_ERROR "TRANSFORM(APPLY macro) is \"${output}\", expected is \"<alpha>;<bravo>;<charlie>\"")
|
||||
endif()
|
||||
|
||||
# APPLY with macro and selector
|
||||
list(TRANSFORM mylist APPLY wrap_angles AT 0 2 OUTPUT_VARIABLE output)
|
||||
if(NOT output STREQUAL "<alpha>;bravo;<charlie>")
|
||||
message(FATAL_ERROR "TRANSFORM(APPLY macro AT) is \"${output}\", expected is \"<alpha>;bravo;<charlie>\"")
|
||||
endif()
|
||||
|
||||
# APPLY on empty list
|
||||
set(empty_list "")
|
||||
list(TRANSFORM empty_list APPLY add_src_prefix OUTPUT_VARIABLE output)
|
||||
|
||||
@@ -7,16 +7,6 @@ function(starts_with_b input result)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Predicate as macro
|
||||
macro(is_short input result)
|
||||
string(LENGTH "${input}" _len)
|
||||
if(_len LESS 4)
|
||||
set(${result} TRUE)
|
||||
else()
|
||||
set(${result} FALSE)
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
set(mylist alpha bravo charlie bravo_two delta)
|
||||
|
||||
# Basic PREDICATE with TOUPPER - only items starting with "b" are uppercased
|
||||
@@ -36,13 +26,6 @@ if(NOT mylist STREQUAL "alpha;BRAVO;charlie;BRAVO_TWO;delta")
|
||||
message(FATAL_ERROR "TRANSFORM(TOUPPER PREDICATE in-place) is \"${mylist}\", expected \"alpha;BRAVO;charlie;BRAVO_TWO;delta\"")
|
||||
endif()
|
||||
|
||||
# PREDICATE with macro
|
||||
set(mylist ab cde fg hijklm no)
|
||||
list(TRANSFORM mylist TOUPPER PREDICATE is_short OUTPUT_VARIABLE output)
|
||||
if(NOT output STREQUAL "AB;CDE;FG;hijklm;NO")
|
||||
message(FATAL_ERROR "TRANSFORM(TOUPPER PREDICATE macro) is \"${output}\", expected \"AB;CDE;FG;hijklm;NO\"")
|
||||
endif()
|
||||
|
||||
# PREDICATE combined with APPLY
|
||||
function(add_prefix in out)
|
||||
set(${out} "prefix_${in}" PARENT_SCOPE)
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,5 @@
|
||||
^CMake Error at TRANSFORM-Selector-PREDICATE-Macro\.cmake:11 \(list\):
|
||||
list sub-command TRANSFORM, selector PREDICATE: macro "is_short" may not be
|
||||
used here; define it as a function\(\) instead\.
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists\.txt:3 \(include\)$
|
||||
11
Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-Macro.cmake
Normal file
11
Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-Macro.cmake
Normal file
@@ -0,0 +1,11 @@
|
||||
macro(is_short input result)
|
||||
string(LENGTH "${input}" _len)
|
||||
if(_len LESS 4)
|
||||
set(${result} TRUE)
|
||||
else()
|
||||
set(${result} FALSE)
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
set(mylist alpha bravo charlie)
|
||||
list(TRANSFORM mylist TOUPPER PREDICATE is_short)
|
||||
Reference in New Issue
Block a user