1
0
mirror of https://github.com/Kitware/CMake.git synced 2025-10-14 19:08:07 +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::size_type pos = 0;
std::string::size_type lastPos = pos;
// stack of { Generator Expression Name, Start Position of Value }
std::stack<std::pair<std::string, std::string::size_type>> genexps;
std::stack<char const*> starts; // indices of "$<"
std::stack<char const*> colons; // indices of ":"
while ((pos = input.find("$<", lastPos)) != std::string::npos) {
result += input.substr(lastPos, pos - lastPos);
starts.push(input.c_str() + pos);
pos += 2;
char const* c = input.c_str() + pos;
char const* cName = c;
char const* const cStart = c;
for (; *c; ++c) {
if (cmGeneratorExpression::StartsWithGeneratorExpression(c)) {
starts.push(c);
++c;
cName = c + 1;
continue;
}
if (c[0] == ':' && cName) {
genexps.push({ input.substr(pos + (cName - cStart), c - cName),
pos + (c + 1 - cStart) });
cName = nullptr;
} else if (c[0] == '>') {
if (!cName && !genexps.empty()) {
if (collected) {
(*collected)[genexps.top().first].push_back(input.substr(
genexps.top().second, pos + c - cStart - genexps.top().second));
}
genexps.pop();
if (c[0] == ':') {
if (colons.size() < starts.size()) {
colons.push(c);
}
if (genexps.empty()) {
} else if (c[0] == '>') {
if (collected && !starts.empty() && !colons.empty()) {
(*collected)[std::string(starts.top() + 2, colons.top())].push_back(
std::string(colons.top() + 1, c));
}
if (!starts.empty()) {
starts.pop();
}
if (!colons.empty()) {
colons.pop();
}
if (starts.empty()) {
break;
}
}
@@ -202,7 +205,7 @@ static std::string extractAllGeneratorExpressions(
pos += traversed;
lastPos = pos;
}
if (genexps.empty()) {
if (starts.empty()) {
result += input.substr(lastPos);
}
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(HexTooManyArgs)
run_cmake(HexNotEnoughArgs)
run_cmake(GenexpStrip)