mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-09-25 04:09:36 +03:00
GenEx: factor out reusable SORT comparator and option-parsing helpers
Prepare for a genex-driven SORT comparator without changing behavior: let cmList::sort() accept a caller-supplied comparison function, and give the $<LIST:SORT> COMPARE:/CASE:/ORDER: option parsing a single definition shared with the canned handler. The new feature can then reuse both rather than duplicating the sort plumbing and the option syntax. Issue: #27892
This commit is contained in:
@@ -2130,6 +2130,96 @@ std::string EvaluateTransformPredicate(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class SortOptionResult
|
||||||
|
{
|
||||||
|
NotRecognized, // `arg` is not a SORT option keyword
|
||||||
|
Parsed, // recognized and applied to `sortConfig`
|
||||||
|
Error, // recognized but malformed or duplicate (already reported)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Parse one $<LIST:SORT> colon-option (COMPARE:/CASE:/ORDER:) into sortConfig.
|
||||||
|
SortOptionResult ParseSortOption(std::string const& arg,
|
||||||
|
cmList::SortConfiguration& sortConfig,
|
||||||
|
cm::GenEx::Evaluation* eval,
|
||||||
|
GeneratorExpressionContent const* content)
|
||||||
|
{
|
||||||
|
using SortConfig = cmList::SortConfiguration;
|
||||||
|
auto const COMPARE = "COMPARE:"_s;
|
||||||
|
auto const CASE = "CASE:"_s;
|
||||||
|
auto const ORDER = "ORDER:"_s;
|
||||||
|
|
||||||
|
if (cmHasPrefix(arg, COMPARE)) {
|
||||||
|
if (sortConfig.Compare != SortConfig::CompareMethod::DEFAULT) {
|
||||||
|
reportError(eval, content->GetOriginalExpression(),
|
||||||
|
"sub-command SORT, COMPARE option has been specified "
|
||||||
|
"multiple times.");
|
||||||
|
return SortOptionResult::Error;
|
||||||
|
}
|
||||||
|
auto option = cm::string_view{ arg.c_str() + COMPARE.length() };
|
||||||
|
if (option == "STRING"_s) {
|
||||||
|
sortConfig.Compare = SortConfig::CompareMethod::STRING;
|
||||||
|
} else if (option == "FILE_BASENAME"_s) {
|
||||||
|
sortConfig.Compare = SortConfig::CompareMethod::FILE_BASENAME;
|
||||||
|
} else if (option == "NATURAL"_s) {
|
||||||
|
sortConfig.Compare = SortConfig::CompareMethod::NATURAL;
|
||||||
|
} else {
|
||||||
|
reportError(eval, content->GetOriginalExpression(),
|
||||||
|
cmStrCat("sub-command SORT, an invalid COMPARE option has "
|
||||||
|
"been specified: \"",
|
||||||
|
option, "\"."));
|
||||||
|
return SortOptionResult::Error;
|
||||||
|
}
|
||||||
|
return SortOptionResult::Parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmHasPrefix(arg, CASE)) {
|
||||||
|
if (sortConfig.Case != SortConfig::CaseSensitivity::DEFAULT) {
|
||||||
|
reportError(eval, content->GetOriginalExpression(),
|
||||||
|
"sub-command SORT, CASE option has been specified multiple "
|
||||||
|
"times.");
|
||||||
|
return SortOptionResult::Error;
|
||||||
|
}
|
||||||
|
auto option = cm::string_view{ arg.c_str() + CASE.length() };
|
||||||
|
if (option == "SENSITIVE"_s) {
|
||||||
|
sortConfig.Case = SortConfig::CaseSensitivity::SENSITIVE;
|
||||||
|
} else if (option == "INSENSITIVE"_s) {
|
||||||
|
sortConfig.Case = SortConfig::CaseSensitivity::INSENSITIVE;
|
||||||
|
} else {
|
||||||
|
reportError(eval, content->GetOriginalExpression(),
|
||||||
|
cmStrCat("sub-command SORT, an invalid CASE option has been "
|
||||||
|
"specified: \"",
|
||||||
|
option, "\"."));
|
||||||
|
return SortOptionResult::Error;
|
||||||
|
}
|
||||||
|
return SortOptionResult::Parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmHasPrefix(arg, ORDER)) {
|
||||||
|
if (sortConfig.Order != SortConfig::OrderMode::DEFAULT) {
|
||||||
|
reportError(eval, content->GetOriginalExpression(),
|
||||||
|
"sub-command SORT, ORDER option has been specified multiple "
|
||||||
|
"times.");
|
||||||
|
return SortOptionResult::Error;
|
||||||
|
}
|
||||||
|
auto option = cm::string_view{ arg.c_str() + ORDER.length() };
|
||||||
|
if (option == "ASCENDING"_s) {
|
||||||
|
sortConfig.Order = SortConfig::OrderMode::ASCENDING;
|
||||||
|
} else if (option == "DESCENDING"_s) {
|
||||||
|
sortConfig.Order = SortConfig::OrderMode::DESCENDING;
|
||||||
|
} else {
|
||||||
|
reportError(
|
||||||
|
eval, content->GetOriginalExpression(),
|
||||||
|
cmStrCat("sub-command SORT, an invalid ORDER option has been "
|
||||||
|
"specified: \"",
|
||||||
|
option, "\"."));
|
||||||
|
return SortOptionResult::Error;
|
||||||
|
}
|
||||||
|
return SortOptionResult::Parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SortOptionResult::NotRecognized;
|
||||||
|
}
|
||||||
|
|
||||||
// Parse the optional trailing selector of a $<LIST:TRANSFORM,...> action
|
// Parse the optional trailing selector of a $<LIST:TRANSFORM,...> action
|
||||||
// (AT <i>... / FOR <start> <stop> [<step>] / REGEX <re>) into a
|
// (AT <i>... / FOR <start> <stop> [<step>] / REGEX <re>) into a
|
||||||
// cmList::TransformSelector. Returns nullptr (after reporting via `eval`) on
|
// cmList::TransformSelector. Returns nullptr (after reporting via `eval`) on
|
||||||
@@ -2706,97 +2796,19 @@ static const struct ListNode : public cmGeneratorExpressionNode
|
|||||||
false)) {
|
false)) {
|
||||||
auto list = GetList(args.front());
|
auto list = GetList(args.front());
|
||||||
args.advance(1);
|
args.advance(1);
|
||||||
auto const COMPARE = "COMPARE:"_s;
|
cmList::SortConfiguration sortConfig;
|
||||||
auto const CASE = "CASE:"_s;
|
|
||||||
auto const ORDER = "ORDER:"_s;
|
|
||||||
using SortConfig = cmList::SortConfiguration;
|
|
||||||
SortConfig sortConfig;
|
|
||||||
for (auto const& arg : args) {
|
for (auto const& arg : args) {
|
||||||
if (cmHasPrefix(arg, COMPARE)) {
|
switch (ParseSortOption(arg, sortConfig, ev, cnt)) {
|
||||||
if (sortConfig.Compare !=
|
case SortOptionResult::Parsed:
|
||||||
SortConfig::CompareMethod::DEFAULT) {
|
break;
|
||||||
reportError(ev, cnt->GetOriginalExpression(),
|
case SortOptionResult::Error:
|
||||||
"sub-command SORT, COMPARE option has been "
|
|
||||||
"specified multiple times.");
|
|
||||||
return std::string{};
|
return std::string{};
|
||||||
}
|
case SortOptionResult::NotRecognized:
|
||||||
auto option =
|
|
||||||
cm::string_view{ arg.c_str() + COMPARE.length() };
|
|
||||||
if (option == "STRING"_s) {
|
|
||||||
sortConfig.Compare = SortConfig::CompareMethod::STRING;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (option == "FILE_BASENAME"_s) {
|
|
||||||
sortConfig.Compare =
|
|
||||||
SortConfig::CompareMethod::FILE_BASENAME;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (option == "NATURAL"_s) {
|
|
||||||
sortConfig.Compare = SortConfig::CompareMethod::NATURAL;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
reportError(
|
|
||||||
ev, cnt->GetOriginalExpression(),
|
|
||||||
cmStrCat(
|
|
||||||
"sub-command SORT, an invalid COMPARE option has been "
|
|
||||||
"specified: \"",
|
|
||||||
option, "\"."));
|
|
||||||
return std::string{};
|
|
||||||
}
|
|
||||||
if (cmHasPrefix(arg, CASE)) {
|
|
||||||
if (sortConfig.Case !=
|
|
||||||
SortConfig::CaseSensitivity::DEFAULT) {
|
|
||||||
reportError(ev, cnt->GetOriginalExpression(),
|
reportError(ev, cnt->GetOriginalExpression(),
|
||||||
"sub-command SORT, CASE option has been "
|
cmStrCat("sub-command SORT, option \"", arg,
|
||||||
"specified multiple times.");
|
"\" is invalid."));
|
||||||
return std::string{};
|
return std::string{};
|
||||||
}
|
|
||||||
auto option = cm::string_view{ arg.c_str() + CASE.length() };
|
|
||||||
if (option == "SENSITIVE"_s) {
|
|
||||||
sortConfig.Case = SortConfig::CaseSensitivity::SENSITIVE;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (option == "INSENSITIVE"_s) {
|
|
||||||
sortConfig.Case = SortConfig::CaseSensitivity::INSENSITIVE;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
reportError(
|
|
||||||
ev, cnt->GetOriginalExpression(),
|
|
||||||
cmStrCat(
|
|
||||||
"sub-command SORT, an invalid CASE option has been "
|
|
||||||
"specified: \"",
|
|
||||||
option, "\"."));
|
|
||||||
return std::string{};
|
|
||||||
}
|
}
|
||||||
if (cmHasPrefix(arg, ORDER)) {
|
|
||||||
if (sortConfig.Order != SortConfig::OrderMode::DEFAULT) {
|
|
||||||
reportError(ev, cnt->GetOriginalExpression(),
|
|
||||||
"sub-command SORT, ORDER option has been "
|
|
||||||
"specified multiple times.");
|
|
||||||
return std::string{};
|
|
||||||
}
|
|
||||||
auto option =
|
|
||||||
cm::string_view{ arg.c_str() + ORDER.length() };
|
|
||||||
if (option == "ASCENDING"_s) {
|
|
||||||
sortConfig.Order = SortConfig::OrderMode::ASCENDING;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (option == "DESCENDING"_s) {
|
|
||||||
sortConfig.Order = SortConfig::OrderMode::DESCENDING;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
reportError(
|
|
||||||
ev, cnt->GetOriginalExpression(),
|
|
||||||
cmStrCat(
|
|
||||||
"sub-command SORT, an invalid ORDER option has been "
|
|
||||||
"specified: \"",
|
|
||||||
option, "\"."));
|
|
||||||
return std::string{};
|
|
||||||
}
|
|
||||||
reportError(ev, cnt->GetOriginalExpression(),
|
|
||||||
cmStrCat("sub-command SORT, option \"", arg,
|
|
||||||
"\" is invalid."));
|
|
||||||
return std::string{};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return list.sort(sortConfig).to_string();
|
return list.sort(sortConfig).to_string();
|
||||||
|
|||||||
+19
-5
@@ -382,7 +382,9 @@ cmList& cmList::sort(SortConfiguration cfg)
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
cmList& cmList::sort(SortConfiguration cfg, cmMakefile& makefile)
|
cmList& cmList::sort(
|
||||||
|
SortConfiguration cfg,
|
||||||
|
std::function<bool(std::string const&, std::string const&)> comparator)
|
||||||
{
|
{
|
||||||
SortConfiguration config{ cfg };
|
SortConfiguration config{ cfg };
|
||||||
|
|
||||||
@@ -394,11 +396,10 @@ cmList& cmList::sort(SortConfiguration cfg, cmMakefile& makefile)
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ComparatorEvaluator evaluator(config.ComparatorFunction, makefile);
|
|
||||||
StringSorter sorter(
|
StringSorter sorter(
|
||||||
config, [&evaluator](std::string const& a, std::string const& b) {
|
config, [&comparator](std::string const& a, std::string const& b) {
|
||||||
bool result = evaluator(a, b);
|
bool result = comparator(a, b);
|
||||||
if (result && evaluator(b, a)) {
|
if (result && comparator(b, a)) {
|
||||||
throw cmList::transform_error(
|
throw cmList::transform_error(
|
||||||
"sub-command SORT, COMPARATOR: function does not induce a strict "
|
"sub-command SORT, COMPARATOR: function does not induce a strict "
|
||||||
"weak ordering. The comparator returned TRUE for both (a, b) and "
|
"weak ordering. The comparator returned TRUE for both (a, b) and "
|
||||||
@@ -414,6 +415,19 @@ cmList& cmList::sort(SortConfiguration cfg, cmMakefile& makefile)
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmList& cmList::sort(SortConfiguration cfg, cmMakefile& makefile)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
ComparatorEvaluator evaluator(cfg.ComparatorFunction, makefile);
|
||||||
|
return this->sort(
|
||||||
|
cfg, [&evaluator](std::string const& a, std::string const& b) {
|
||||||
|
return evaluator(a, b);
|
||||||
|
});
|
||||||
|
} catch (transform_error& e) {
|
||||||
|
throw std::invalid_argument(e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
using transform_type = std::function<std::string(std::string const&)>;
|
using transform_type = std::function<std::string(std::string const&)>;
|
||||||
using transform_error = cmList::transform_error;
|
using transform_error = cmList::transform_error;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <functional>
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -873,6 +874,9 @@ public:
|
|||||||
};
|
};
|
||||||
cmList& sort(SortConfiguration config = SortConfiguration{});
|
cmList& sort(SortConfiguration config = SortConfiguration{});
|
||||||
cmList& sort(SortConfiguration config, cmMakefile& makefile);
|
cmList& sort(SortConfiguration config, cmMakefile& makefile);
|
||||||
|
cmList& sort(
|
||||||
|
SortConfiguration config,
|
||||||
|
std::function<bool(std::string const&, std::string const&)> comparator);
|
||||||
|
|
||||||
// exception raised on error during transform operations
|
// exception raised on error during transform operations
|
||||||
class transform_error : public std::runtime_error
|
class transform_error : public std::runtime_error
|
||||||
|
|||||||
Reference in New Issue
Block a user