Add generator expression $<ARGS>

Fixes: #28122
This commit is contained in:
Marc Chevrier committed 2026-10-01 19:09:35 +02:00
1 parent d03678cc5a
commit c35312e1e7
13 files changed
+131 -1

No files matched your search

+24 -1
View File
@@ -3634,8 +3634,31 @@ Multi-level Expression Evaluation
echo $<TARGET_GENEX_EVAL:foo,$<TARGET_PROPERTY:foo,CUSTOM_KEYS>>
)
Support Expressions
-------------------
Conversions
^^^^^^^^^^^
.. genex:: $<ARGS:cmake-list>
.. versionadded:: 4.5
Transform a :ref:`semicolon-separated list <CMake Language Lists>` into a
comma-separated list as expected by many generator expressions.
.. code-block:: cmake
# check if the current config is a debug one
## Converting a cmake list by hand does not give the expected result
$<CONFIG:$<LIST:JOIN,$<GLOBAL_PROPERTY:DEBUG_CONFIGURATIONS>,$<COMMA>>>
## correct way to proceed
$<CONFIG:$<ARGS:$<GLOBAL_PROPERTY:DEBUG_CONFIGURATIONS>>>
Escaped Characters
------------------
^^^^^^^^^^^^^^^^^^
These expressions evaluate to specific string literals. Use them in place of
the actual string literal where you need to prevent them from having their
+6
View File
@@ -0,0 +1,6 @@
GenEx-ARGS
----------
* CMake gains the :genex:`$<ARGS>` generator expression to convert a
:ref:`semicolon-separated list <CMake Language Lists>` into a comma-separated
list.
+32
View File
@@ -2,6 +2,11 @@
file LICENSE.rst or https://cmake.org/licensing for details. */
#include "cmGeneratorExpressionEvaluator.h"
#include <iterator>
#include <cm/string_view>
#include <cmext/string_view>
#ifndef CMAKE_BOOTSTRAP
# include <cm3p/json/value.h>
#endif
@@ -111,6 +116,33 @@ std::string GeneratorExpressionContent::Evaluate(
return std::string();
}
if (!parameters.empty()) {
cm::string_view ARGS_BEGIN = "<ARGS>"_s;
cm::string_view ARGS_END = "</ARGS>"_s;
std::vector<std::string> args;
// split any parameter with the prefix <ARGS>:
for (auto const& param : parameters) {
if (cmHasPrefix(param, ARGS_BEGIN) && cmHasSuffix(param, ARGS_END)) {
auto values = param.substr(ARGS_BEGIN.length(),
param.length() - ARGS_BEGIN.length() -
ARGS_END.length());
if (values.empty()) {
args.push_back(std::move(values));
continue;
}
std::vector<std::string> tokens =
cmTokenize(values, ',', cmTokenizerMode::New);
args.insert(args.end(), std::make_move_iterator(tokens.begin()),
std::make_move_iterator(tokens.end()));
} else {
args.push_back(param);
}
}
parameters = std::move(args);
}
{
#ifndef CMAKE_BOOTSTRAP
auto execProfilingRAII =
+25
View File
@@ -360,6 +360,30 @@ static const struct BoolNode : public cmGeneratorExpressionNode
}
} boolNode;
static const struct ArgsNode : public cmGeneratorExpressionNode
{
ArgsNode() {} // NOLINT(modernize-use-equals-default)
int NumExpectedParameters() const override { return ZeroOrMoreParameters; }
std::string Evaluate(std::vector<std::string> const& parameters,
cm::GenEx::Evaluation*,
GeneratorExpressionContent const*,
cmGeneratorExpressionDAGChecker*) const override
{
if (parameters.empty()) {
return std::string{};
}
if (parameters.size() == 1 && parameters[0].empty()) {
return parameters.front();
}
cmList args{ parameters.begin(), parameters.end(),
cmList::ExpandElements::Yes, cmList::EmptyElements::Yes };
return cmStrCat("<ARGS>", args.join(","_s), "</ARGS>");
}
} argsNode;
static const struct IfNode : public cmGeneratorExpressionNode
{
IfNode() {} // NOLINT(modernize-use-equals-default)
@@ -6460,6 +6484,7 @@ cmGeneratorExpressionNode const* cmGeneratorExpressionNode::GetNode(
{ "AND", &andNode },
{ "OR", &orNode },
{ "NOT", &notNode },
{ "ARGS", &argsNode },
{ "C_COMPILER_ID", &cCompilerIdNode },
{ "CXX_COMPILER_ID", &cxxCompilerIdNode },
{ "OBJC_COMPILER_ID", &objcCompilerIdNode },
+1
View File
@@ -573,6 +573,7 @@ add_RunCMake_test(GenEx-LINK_GROUP)
add_RunCMake_test(GenEx-TARGET_FILE -DLINKER_SUPPORTS_PDB=${LINKER_SUPPORTS_PDB})
add_RunCMake_test(GenEx-TARGET_IMPORT_FILE)
add_RunCMake_test(GenEx-GENEX_EVAL)
add_RunCMake_test(GenEx-ARGS)
add_RunCMake_test(GenEx-GLOBAL_PROPERTY)
add_RunCMake_test(GenEx-RULE_PROPERTY)
add_RunCMake_test(GenEx-SOURCE_EXISTS)
+5
View File
@@ -0,0 +1,5 @@
cmake_minimum_required(VERSION 4.0...4.4)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
@@ -0,0 +1,11 @@
include(RunCMake)
run_cmake(no-arguments)
run_cmake(list-conversion)
if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
run_cmake_with_options(simple -DCMAKE_CONFIGURATION_TYPES=Debug)
else()
run_cmake_with_options(simple -DCMAKE_BUILD_TYPE=Debug)
endif()
@@ -0,0 +1,6 @@
file(READ "${RunCMake_TEST_BINARY_DIR}/list-conversion-generated.txt" content)
set(reference "<ARGS>AA,BB</ARGS>|<ARGS>CC,DD</ARGS>|<ARGS>EE,FF,GG</ARGS>")
if(NOT content MATCHES "${reference}")
set(RunCMake_TEST_FAILED "actual content:\n [[${content}]]\nbut expected:\n [[${reference}]]")
endif()
@@ -0,0 +1,6 @@
set_property(GLOBAL PROPERTY COMMA_LIST "AA,BB")
set_property(GLOBAL PROPERTY CMAKE_LIST "CC;DD")
set_property(GLOBAL PROPERTY MIX_LIST "EE,FF;GG")
file(GENERATE OUTPUT list-conversion-generated.txt CONTENT "$<ARGS:$<GLOBAL_PROPERTY:COMMA_LIST>>|$<ARGS:$<GLOBAL_PROPERTY:CMAKE_LIST>>|$<ARGS:$<GLOBAL_PROPERTY:MIX_LIST>>")
@@ -0,0 +1,5 @@
file(READ "${RunCMake_TEST_BINARY_DIR}/no-arguments-generated.txt" content)
if(NOT content MATCHES "><|><")
set(RunCMake_TEST_FAILED "actual content:\n [[${content}]]\nbut expected:\n [[><|><]]")
endif()
@@ -0,0 +1,2 @@
file(GENERATE OUTPUT no-arguments-generated.txt CONTENT ">$<ARGS><|>$<ARGS:><")
@@ -0,0 +1,5 @@
file(READ "${RunCMake_TEST_BINARY_DIR}/simple-generated.txt" content)
if(NOT content MATCHES "1")
set(RunCMake_TEST_FAILED "actual content:\n [[${content}]]\nbut expected:\n [[1]]")
endif()
+3
View File
@@ -0,0 +1,3 @@
set_property(GLOBAL PROPERTY DEBUG_CONGIFURATIONS Debug Other)
file(GENERATE OUTPUT simple-generated.txt CONTENT "$<CONFIG:$<ARGS:$<GLOBAL_PROPERTY:DEBUG_CONGIFURATIONS>>>")