From 8227028e493fb76d77cc51f3f72fb772701dbc6c Mon Sep 17 00:00:00 2001 From: Martin Duffy Date: Wed, 13 Aug 2025 08:12:58 -0400 Subject: [PATCH] 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 --- Source/cmGeneratorExpression.cxx | 37 +++++++++++++----------- Tests/RunCMake/string/GenexpStrip.cmake | 37 ++++++++++++++++++++++++ Tests/RunCMake/string/RunCMakeTest.cmake | 2 ++ 3 files changed, 59 insertions(+), 17 deletions(-) create mode 100644 Tests/RunCMake/string/GenexpStrip.cmake diff --git a/Source/cmGeneratorExpression.cxx b/Source/cmGeneratorExpression.cxx index f35ee52e5f..f6bcb57a3b 100644 --- a/Source/cmGeneratorExpression.cxx +++ b/Source/cmGeneratorExpression.cxx @@ -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> genexps; + std::stack starts; // indices of "$<" + std::stack 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); diff --git a/Tests/RunCMake/string/GenexpStrip.cmake b/Tests/RunCMake/string/GenexpStrip.cmake new file mode 100644 index 0000000000..9de498cf11 --- /dev/null +++ b/Tests/RunCMake/string/GenexpStrip.cmake @@ -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 + "$" + "" +) +test_strip( # LHS contains generator expression + "$<$:NDEBUG>;DEBUG" + "DEBUG" +) +test_strip( # RHS contains generator expression + "$>" + "" +) +test_strip( # Empty and unfinished expressions + "$<>$<$<>" + "$<$<>" +) +test_strip( # Multiple independent expressions + "$ / $" + " / " +) +test_strip( # Multiple : in one expression + "$<1:2:3>" + "" +) +test_strip( # Multiple case + "1$2$:$>,TRUE,FALSE>3" + "123" +) diff --git a/Tests/RunCMake/string/RunCMakeTest.cmake b/Tests/RunCMake/string/RunCMakeTest.cmake index b0aabb6ca2..7279407b14 100644 --- a/Tests/RunCMake/string/RunCMakeTest.cmake +++ b/Tests/RunCMake/string/RunCMakeTest.cmake @@ -56,3 +56,5 @@ run_cmake(RepeatNegativeCount) run_cmake(Hex) run_cmake(HexTooManyArgs) run_cmake(HexNotEnoughArgs) + +run_cmake(GenexpStrip)