1
0
mirror of https://github.com/Kitware/CMake.git synced 2025-10-16 05:26:58 +08:00

string(GENEX_STRIP): Fix regression on nested generator expressions

Since commit 13c7bb5b0c (cmGeneratorExpression: Update strip function to
collect parsed expressions, 2025-04-08), the logic to strip generator
expressions from a string made incorrect assumptions about the contents of
generator expressions, leading certain cases to be stripped incorrectly.

Clean up the logic and fix broken behavior, and add test coverage with
`string(GENEX_STRIP)`.

Fixes: #27133
This commit is contained in:
Martin Duffy
2025-08-13 08:12:58 -04:00
committed by Brad King
parent 2a2c2e0b26
commit 8227028e49
3 changed files with 59 additions and 17 deletions

View File

@@ -164,33 +164,36 @@ static std::string extractAllGeneratorExpressions(
std::string result; std::string result;
std::string::size_type pos = 0; std::string::size_type pos = 0;
std::string::size_type lastPos = pos; std::string::size_type lastPos = pos;
// stack of { Generator Expression Name, Start Position of Value } std::stack<char const*> starts; // indices of "$<"
std::stack<std::pair<std::string, std::string::size_type>> genexps; std::stack<char const*> colons; // indices of ":"
while ((pos = input.find("$<", lastPos)) != std::string::npos) { while ((pos = input.find("$<", lastPos)) != std::string::npos) {
result += input.substr(lastPos, pos - lastPos); result += input.substr(lastPos, pos - lastPos);
starts.push(input.c_str() + pos);
pos += 2; pos += 2;
char const* c = input.c_str() + pos; char const* c = input.c_str() + pos;
char const* cName = c;
char const* const cStart = c; char const* const cStart = c;
for (; *c; ++c) { for (; *c; ++c) {
if (cmGeneratorExpression::StartsWithGeneratorExpression(c)) { if (cmGeneratorExpression::StartsWithGeneratorExpression(c)) {
starts.push(c);
++c; ++c;
cName = c + 1;
continue; continue;
} }
if (c[0] == ':' && cName) { if (c[0] == ':') {
genexps.push({ input.substr(pos + (cName - cStart), c - cName), if (colons.size() < starts.size()) {
pos + (c + 1 - cStart) }); colons.push(c);
cName = nullptr; }
} else if (c[0] == '>') { } else if (c[0] == '>') {
if (!cName && !genexps.empty()) { if (collected && !starts.empty() && !colons.empty()) {
if (collected) { (*collected)[std::string(starts.top() + 2, colons.top())].push_back(
(*collected)[genexps.top().first].push_back(input.substr( std::string(colons.top() + 1, c));
genexps.top().second, pos + c - cStart - genexps.top().second));
} }
genexps.pop(); if (!starts.empty()) {
starts.pop();
} }
if (genexps.empty()) { if (!colons.empty()) {
colons.pop();
}
if (starts.empty()) {
break; break;
} }
} }
@@ -202,7 +205,7 @@ static std::string extractAllGeneratorExpressions(
pos += traversed; pos += traversed;
lastPos = pos; lastPos = pos;
} }
if (genexps.empty()) { if (starts.empty()) {
result += input.substr(lastPos); result += input.substr(lastPos);
} }
return cmGeneratorExpression::StripEmptyListElements(result); return cmGeneratorExpression::StripEmptyListElements(result);

View File

@@ -0,0 +1,37 @@
function(test_strip input expected)
string(GENEX_STRIP "${input}" strip)
if (NOT strip STREQUAL expected)
message(FATAL_ERROR "message(GENEXP_STRIP \"${input}\")
evaluated to \"${strip}\"
expected \"${expected}\"")
endif()
endfunction()
test_strip( # Simple case
"$<BOOL:1>"
""
)
test_strip( # LHS contains generator expression
"$<$<CONFIG:Release>:NDEBUG>;DEBUG"
"DEBUG"
)
test_strip( # RHS contains generator expression
"$<AND:1,$<BOOL:TRUE>>"
""
)
test_strip( # Empty and unfinished expressions
"$<>$<$<>"
"$<$<>"
)
test_strip( # Multiple independent expressions
"$<IF:TRUE,TRUE,FALSE> / $<IF:TRUE,TRUE,FALSE>"
" / "
)
test_strip( # Multiple : in one expression
"$<1:2:3>"
""
)
test_strip( # Multiple case
"1$<AND:1,0>2$<IF:$<$<BOOL:1>:$<CONFIG:RELEASE>>,TRUE,FALSE>3"
"123"
)

View File

@@ -56,3 +56,5 @@ run_cmake(RepeatNegativeCount)
run_cmake(Hex) run_cmake(Hex)
run_cmake(HexTooManyArgs) run_cmake(HexTooManyArgs)
run_cmake(HexNotEnoughArgs) run_cmake(HexNotEnoughArgs)
run_cmake(GenexpStrip)