summaryrefslogtreecommitdiff
path: root/Source/cmGeneratorExpressionNode.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'Source/cmGeneratorExpressionNode.cxx')
-rw-r--r--Source/cmGeneratorExpressionNode.cxx715
1 files changed, 705 insertions, 10 deletions
diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx
index a47366b4a1..727931c579 100644
--- a/Source/cmGeneratorExpressionNode.cxx
+++ b/Source/cmGeneratorExpressionNode.cxx
@@ -635,22 +635,48 @@ public:
using Arguments = Range<std::vector<std::string>>;
-bool CheckPathParametersEx(cmGeneratorExpressionContext* ctx,
- const GeneratorExpressionContent* cnt,
- cm::string_view option, std::size_t count,
- int required = 1, bool exactly = true)
+bool CheckGenExParameters(cmGeneratorExpressionContext* ctx,
+ const GeneratorExpressionContent* cnt,
+ cm::string_view genex, cm::string_view option,
+ std::size_t count, int required = 1,
+ bool exactly = true)
{
if (static_cast<int>(count) < required ||
(exactly && static_cast<int>(count) > required)) {
+ std::string nbParameters;
+ switch (required) {
+ case 1:
+ nbParameters = "one parameter";
+ break;
+ case 2:
+ nbParameters = "two parameters";
+ break;
+ case 3:
+ nbParameters = "three parameters";
+ break;
+ case 4:
+ nbParameters = "four parameters";
+ break;
+ default:
+ nbParameters = cmStrCat(std::to_string(required), " parameters");
+ }
reportError(ctx, cnt->GetOriginalExpression(),
- cmStrCat("$<PATH:", option, "> expression requires ",
- (exactly ? "exactly" : "at least"), ' ',
- (required == 1 ? "one parameter" : "two parameters"),
+ cmStrCat("$<", genex, ':', option, "> expression requires ",
+ (exactly ? "exactly" : "at least"), ' ', nbParameters,
'.'));
return false;
}
return true;
};
+
+bool CheckPathParametersEx(cmGeneratorExpressionContext* ctx,
+ const GeneratorExpressionContent* cnt,
+ cm::string_view option, std::size_t count,
+ int required = 1, bool exactly = true)
+{
+ return CheckGenExParameters(ctx, cnt, "PATH"_s, option, count, required,
+ exactly);
+}
bool CheckPathParameters(cmGeneratorExpressionContext* ctx,
const GeneratorExpressionContent* cnt,
cm::string_view option, const Arguments& args,
@@ -658,6 +684,7 @@ bool CheckPathParameters(cmGeneratorExpressionContext* ctx,
{
return CheckPathParametersEx(ctx, cnt, option, args.size(), required);
};
+
std::string ToString(bool isTrue)
{
return isTrue ? "1" : "0";
@@ -1108,6 +1135,670 @@ static const struct PathEqualNode : public cmGeneratorExpressionNode
}
} pathEqualNode;
+namespace {
+inline bool CheckListParametersEx(cmGeneratorExpressionContext* ctx,
+ const GeneratorExpressionContent* cnt,
+ cm::string_view option, std::size_t count,
+ int required = 1, bool exactly = true)
+{
+ return CheckGenExParameters(ctx, cnt, "LIST"_s, option, count, required,
+ exactly);
+}
+inline bool CheckListParameters(cmGeneratorExpressionContext* ctx,
+ const GeneratorExpressionContent* cnt,
+ cm::string_view option, const Arguments& args,
+ int required = 1)
+{
+ return CheckListParametersEx(ctx, cnt, option, args.size(), required);
+};
+
+inline cmList GetList(std::string const& list)
+{
+ return list.empty() ? cmList{} : cmList{ list, cmList::EmptyElements::Yes };
+}
+
+bool GetNumericArgument(const std::string& arg, int& value)
+{
+ try {
+ std::size_t pos;
+
+ value = std::stoi(arg, &pos);
+ if (pos != arg.length()) {
+ // this is not a number
+ return false;
+ }
+ } catch (const std::invalid_argument&) {
+ return false;
+ }
+
+ return true;
+}
+
+bool GetNumericArguments(
+ cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt,
+ Arguments const& args, std::vector<int>& indexes,
+ cmList::ExpandElements expandElements = cmList::ExpandElements::No)
+{
+ using IndexRange = cmRange<Arguments::const_iterator>;
+ IndexRange arguments(args.begin(), args.end());
+ cmList list;
+ if (expandElements == cmList::ExpandElements::Yes) {
+ list = cmList{ args.begin(), args.end(), expandElements };
+ arguments = IndexRange{ list.begin(), list.end() };
+ }
+
+ for (auto const& value : arguments) {
+ int index;
+ if (!GetNumericArgument(value, index)) {
+ reportError(ctx, cnt->GetOriginalExpression(),
+ cmStrCat("index: \"", value, "\" is not a valid index"));
+ return false;
+ }
+ indexes.push_back(index);
+ }
+ return true;
+}
+}
+
+static const struct ListNode : public cmGeneratorExpressionNode
+{
+ ListNode() {} // NOLINT(modernize-use-equals-default)
+
+ int NumExpectedParameters() const override { return TwoOrMoreParameters; }
+
+ bool AcceptsArbitraryContentParameter() const override { return true; }
+
+ std::string Evaluate(
+ const std::vector<std::string>& parameters,
+ cmGeneratorExpressionContext* context,
+ const GeneratorExpressionContent* content,
+ cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
+ {
+ static std::unordered_map<
+ cm::string_view,
+ std::function<std::string(cmGeneratorExpressionContext*,
+ const GeneratorExpressionContent*,
+ Arguments&)>>
+ listCommands{
+ { "LENGTH"_s,
+ [](cmGeneratorExpressionContext* ctx,
+ const GeneratorExpressionContent* cnt,
+ Arguments& args) -> std::string {
+ if (CheckListParameters(ctx, cnt, "LENGTH"_s, args)) {
+ return std::to_string(GetList(args.front()).size());
+ }
+ return std::string{};
+ } },
+ { "GET"_s,
+ [](cmGeneratorExpressionContext* ctx,
+ const GeneratorExpressionContent* cnt,
+ Arguments& args) -> std::string {
+ if (CheckListParametersEx(ctx, cnt, "GET"_s, args.size(), 2,
+ false)) {
+ auto list = GetList(args.front());
+ if (list.empty()) {
+ reportError(ctx, cnt->GetOriginalExpression(),
+ "given empty list");
+ return std::string{};
+ }
+
+ std::vector<int> indexes;
+ if (!GetNumericArguments(ctx, cnt, args.advance(1), indexes,
+ cmList::ExpandElements::Yes)) {
+ return std::string{};
+ }
+ try {
+ return list.get_items(indexes.begin(), indexes.end())
+ .to_string();
+ } catch (std::out_of_range& e) {
+ reportError(ctx, cnt->GetOriginalExpression(), e.what());
+ return std::string{};
+ }
+ }
+ return std::string{};
+ } },
+ { "JOIN"_s,
+ [](cmGeneratorExpressionContext* ctx,
+ const GeneratorExpressionContent* cnt,
+ Arguments& args) -> std::string {
+ if (CheckListParameters(ctx, cnt, "JOIN"_s, args, 2)) {
+ return GetList(args.front()).join(args[1]);
+ }
+ return std::string{};
+ } },
+ { "SUBLIST"_s,
+ [](cmGeneratorExpressionContext* ctx,
+ const GeneratorExpressionContent* cnt,
+ Arguments& args) -> std::string {
+ if (CheckListParameters(ctx, cnt, "SUBLIST"_s, args, 3)) {
+ auto list = GetList(args.front());
+ if (!list.empty()) {
+ std::vector<int> indexes;
+ if (!GetNumericArguments(ctx, cnt, args.advance(1), indexes)) {
+ return std::string{};
+ }
+ if (indexes[0] < 0) {
+ reportError(ctx, cnt->GetOriginalExpression(),
+ cmStrCat("begin index: ", indexes[0],
+ " is out of range 0 - ",
+ list.size() - 1));
+ return std::string{};
+ }
+ if (indexes[1] < -1) {
+ reportError(ctx, cnt->GetOriginalExpression(),
+ cmStrCat("length: ", indexes[1],
+ " should be -1 or greater"));
+ return std::string{};
+ }
+ try {
+ return list
+ .sublist(static_cast<cmList::size_type>(indexes[0]),
+ static_cast<cmList::size_type>(indexes[1]))
+ .to_string();
+ } catch (std::out_of_range& e) {
+ reportError(ctx, cnt->GetOriginalExpression(), e.what());
+ return std::string{};
+ }
+ }
+ }
+ return std::string{};
+ } },
+ { "FIND"_s,
+ [](cmGeneratorExpressionContext* ctx,
+ const GeneratorExpressionContent* cnt,
+ Arguments& args) -> std::string {
+ if (CheckListParameters(ctx, cnt, "FIND"_s, args, 2)) {
+ auto list = GetList(args.front());
+ auto index = list.find(args[1]);
+ return index == cmList::npos ? "-1" : std::to_string(index);
+ }
+ return std::string{};
+ } },
+ { "APPEND"_s,
+ [](cmGeneratorExpressionContext* ctx,
+ const GeneratorExpressionContent* cnt,
+ Arguments& args) -> std::string {
+ if (CheckListParametersEx(ctx, cnt, "APPEND"_s, args.size(), 2,
+ false)) {
+ auto list = args.front();
+ args.advance(1);
+ return cmList::append(args.begin(), args.end(), list);
+ }
+ return std::string{};
+ } },
+ { "PREPEND"_s,
+ [](cmGeneratorExpressionContext* ctx,
+ const GeneratorExpressionContent* cnt,
+ Arguments& args) -> std::string {
+ if (CheckListParametersEx(ctx, cnt, "PREPEND"_s, args.size(), 2,
+ false)) {
+ auto list = args.front();
+ args.advance(1);
+ return cmList::prepend(args.begin(), args.end(), list);
+ }
+ return std::string{};
+ } },
+ { "INSERT"_s,
+ [](cmGeneratorExpressionContext* ctx,
+ const GeneratorExpressionContent* cnt,
+ Arguments& args) -> std::string {
+ if (CheckListParametersEx(ctx, cnt, "INSERT"_s, args.size(), 3,
+ false)) {
+ int index;
+ if (!GetNumericArgument(args[1], index)) {
+ reportError(
+ ctx, cnt->GetOriginalExpression(),
+ cmStrCat("index: \"", args[1], "\" is not a valid index"));
+ return std::string{};
+ }
+ try {
+ auto list = GetList(args.front());
+ args.advance(2);
+ list.insert_items(index, args.begin(), args.end());
+ return list.to_string();
+ } catch (std::out_of_range& e) {
+ reportError(ctx, cnt->GetOriginalExpression(), e.what());
+ return std::string{};
+ }
+ }
+ return std::string{};
+ } },
+ { "POP_BACK"_s,
+ [](cmGeneratorExpressionContext* ctx,
+ const GeneratorExpressionContent* cnt,
+ Arguments& args) -> std::string {
+ if (CheckListParameters(ctx, cnt, "POP_BACK"_s, args)) {
+ auto list = GetList(args.front());
+ if (!list.empty()) {
+ list.pop_back();
+ return list.to_string();
+ }
+ }
+ return std::string{};
+ } },
+ { "POP_FRONT"_s,
+ [](cmGeneratorExpressionContext* ctx,
+ const GeneratorExpressionContent* cnt,
+ Arguments& args) -> std::string {
+ if (CheckListParameters(ctx, cnt, "POP_FRONT"_s, args)) {
+ auto list = GetList(args.front());
+ if (!list.empty()) {
+ list.pop_front();
+ return list.to_string();
+ }
+ }
+ return std::string{};
+ } },
+ { "REMOVE_DUPLICATES"_s,
+ [](cmGeneratorExpressionContext* ctx,
+ const GeneratorExpressionContent* cnt,
+ Arguments& args) -> std::string {
+ if (CheckListParameters(ctx, cnt, "REMOVE_DUPLICATES"_s, args)) {
+ return GetList(args.front()).remove_duplicates().to_string();
+ }
+ return std::string{};
+ } },
+ { "REMOVE_ITEM"_s,
+ [](cmGeneratorExpressionContext* ctx,
+ const GeneratorExpressionContent* cnt,
+ Arguments& args) -> std::string {
+ if (CheckListParametersEx(ctx, cnt, "REMOVE_ITEM"_s, args.size(),
+ 2, false)) {
+ auto list = GetList(args.front());
+ args.advance(1);
+ cmList items{ args.begin(), args.end(),
+ cmList::ExpandElements::Yes };
+ return list.remove_items(items.begin(), items.end()).to_string();
+ }
+ return std::string{};
+ } },
+ { "REMOVE_AT"_s,
+ [](cmGeneratorExpressionContext* ctx,
+ const GeneratorExpressionContent* cnt,
+ Arguments& args) -> std::string {
+ if (CheckListParametersEx(ctx, cnt, "REMOVE_AT"_s, args.size(), 2,
+ false)) {
+ auto list = GetList(args.front());
+ std::vector<int> indexes;
+ if (!GetNumericArguments(ctx, cnt, args.advance(1), indexes,
+ cmList::ExpandElements::Yes)) {
+ return std::string{};
+ }
+ try {
+ return list.remove_items(indexes.begin(), indexes.end())
+ .to_string();
+ } catch (std::out_of_range& e) {
+ reportError(ctx, cnt->GetOriginalExpression(), e.what());
+ return std::string{};
+ }
+ }
+ return std::string{};
+ } },
+ { "FILTER"_s,
+ [](cmGeneratorExpressionContext* ctx,
+ const GeneratorExpressionContent* cnt,
+ Arguments& args) -> std::string {
+ if (CheckListParameters(ctx, cnt, "FILTER"_s, args, 3)) {
+ auto const& op = args[1];
+ if (op != "INCLUDE"_s && op != "EXCLUDE"_s) {
+ reportError(
+ ctx, cnt->GetOriginalExpression(),
+ cmStrCat("sub-command FILTER does not recognize operator \"",
+ op, "\". It must be either INCLUDE or EXCLUDE."));
+ return std::string{};
+ }
+ try {
+ return GetList(args.front())
+ .filter(args[2],
+ op == "INCLUDE"_s ? cmList::FilterMode::INCLUDE
+ : cmList::FilterMode::EXCLUDE)
+ .to_string();
+ } catch (std::invalid_argument&) {
+ reportError(
+ ctx, cnt->GetOriginalExpression(),
+ cmStrCat("sub-command FILTER, failed to compile regex \"",
+ args[2], "\"."));
+ return std::string{};
+ }
+ }
+ return std::string{};
+ } },
+ { "TRANSFORM"_s,
+ [](cmGeneratorExpressionContext* ctx,
+ const GeneratorExpressionContent* cnt,
+ Arguments& args) -> std::string {
+ if (CheckListParametersEx(ctx, cnt, "TRANSFORM"_s, args.size(), 2,
+ false)) {
+ auto list = GetList(args.front());
+ if (!list.empty()) {
+ struct ActionDescriptor
+ {
+ ActionDescriptor(std::string name)
+ : Name(std::move(name))
+ {
+ }
+ ActionDescriptor(std::string name,
+ cmList::TransformAction action, int arity)
+ : Name(std::move(name))
+ , Action(action)
+ , Arity(arity)
+ {
+ }
+
+ operator const std::string&() const { return this->Name; }
+
+ std::string Name;
+ cmList::TransformAction Action;
+ int Arity = 0;
+ };
+
+ static std::set<
+ ActionDescriptor,
+ std::function<bool(const std::string&, const std::string&)>>
+ descriptors{
+ { { "APPEND", cmList::TransformAction::APPEND, 1 },
+ { "PREPEND", cmList::TransformAction::PREPEND, 1 },
+ { "TOUPPER", cmList::TransformAction::TOUPPER, 0 },
+ { "TOLOWER", cmList::TransformAction::TOLOWER, 0 },
+ { "STRIP", cmList::TransformAction::STRIP, 0 },
+ { "REPLACE", cmList::TransformAction::REPLACE, 2 } },
+ [](const std::string& x, const std::string& y) {
+ return x < y;
+ }
+ };
+
+ auto descriptor = descriptors.find(args.advance(1).front());
+ if (descriptor == descriptors.end()) {
+ reportError(ctx, cnt->GetOriginalExpression(),
+ cmStrCat(" sub-command TRANSFORM, ",
+ args.front(), " invalid action."));
+ return std::string{};
+ }
+
+ // Action arguments
+ args.advance(1);
+ if (args.size() < descriptor->Arity) {
+ reportError(ctx, cnt->GetOriginalExpression(),
+ cmStrCat("sub-command TRANSFORM, action ",
+ descriptor->Name, " expects ",
+ descriptor->Arity, " argument(s)."));
+ return std::string{};
+ }
+ std::vector<std::string> arguments;
+ if (descriptor->Arity > 0) {
+ arguments = std::vector<std::string>(
+ args.begin(), args.begin() + descriptor->Arity);
+ args.advance(descriptor->Arity);
+ }
+
+ const std::string REGEX{ "REGEX" };
+ const std::string AT{ "AT" };
+ const std::string FOR{ "FOR" };
+ std::unique_ptr<cmList::TransformSelector> selector;
+
+ try {
+ // handle optional arguments
+ while (!args.empty()) {
+ if ((args.front() == REGEX || args.front() == AT ||
+ args.front() == FOR) &&
+ selector) {
+ reportError(ctx, cnt->GetOriginalExpression(),
+ cmStrCat("sub-command TRANSFORM, selector "
+ "already specified (",
+ selector->GetTag(), ")."));
+
+ return std::string{};
+ }
+
+ // REGEX selector
+ if (args.front() == REGEX) {
+ if (args.advance(1).empty()) {
+ reportError(
+ ctx, cnt->GetOriginalExpression(),
+ "sub-command TRANSFORM, selector REGEX expects "
+ "'regular expression' argument.");
+ return std::string{};
+ }
+
+ selector = cmList::TransformSelector::New<
+ cmList::TransformSelector::REGEX>(args.front());
+
+ args.advance(1);
+ continue;
+ }
+
+ // AT selector
+ if (args.front() == AT) {
+ args.advance(1);
+ // get all specified indexes
+ std::vector<cmList::index_type> indexes;
+ while (!args.empty()) {
+ cmList indexList{ args.front() };
+ for (auto const& index : indexList) {
+ int value;
+
+ if (!GetNumericArgument(index, value)) {
+ // this is not a number, stop processing
+ reportError(
+ ctx, cnt->GetOriginalExpression(),
+ cmStrCat("sub-command TRANSFORM, selector AT: '",
+ index, "': unexpected argument."));
+ return std::string{};
+ }
+ indexes.push_back(value);
+ }
+ args.advance(1);
+ }
+
+ if (indexes.empty()) {
+ reportError(ctx, cnt->GetOriginalExpression(),
+ "sub-command TRANSFORM, selector AT "
+ "expects at least one "
+ "numeric value.");
+ return std::string{};
+ }
+
+ selector = cmList::TransformSelector::New<
+ cmList::TransformSelector::AT>(std::move(indexes));
+
+ continue;
+ }
+
+ // FOR selector
+ if (args.front() == FOR) {
+ if (args.advance(1).size() < 2) {
+ reportError(ctx, cnt->GetOriginalExpression(),
+ "sub-command TRANSFORM, selector FOR "
+ "expects, at least,"
+ " two arguments.");
+ return std::string{};
+ }
+
+ cmList::index_type start = 0;
+ cmList::index_type stop = 0;
+ cmList::index_type step = 1;
+ bool valid = false;
+
+ if (GetNumericArgument(args.front(), start) &&
+ GetNumericArgument(args.advance(1).front(), stop)) {
+ valid = true;
+ }
+
+ if (!valid) {
+ reportError(
+ ctx, cnt->GetOriginalExpression(),
+ "sub-command TRANSFORM, selector FOR expects, "
+ "at least, two numeric values.");
+ return std::string{};
+ }
+ // try to read a third numeric value for step
+ if (!args.advance(1).empty()) {
+ if (!GetNumericArgument(args.front(), step)) {
+ // this is not a number
+ step = -1;
+ }
+ args.advance(1);
+ }
+
+ if (step <= 0) {
+ reportError(
+ ctx, cnt->GetOriginalExpression(),
+ "sub-command TRANSFORM, selector FOR expects "
+ "positive numeric value for <step>.");
+ return std::string{};
+ }
+
+ selector = cmList::TransformSelector::New<
+ cmList::TransformSelector::FOR>({ start, stop, step });
+ continue;
+ }
+
+ reportError(ctx, cnt->GetOriginalExpression(),
+ cmStrCat("sub-command TRANSFORM, '",
+ cmJoin(args, ", "),
+ "': unexpected argument(s)."));
+ return std::string{};
+ }
+
+ return list
+ .transform(descriptor->Action, arguments,
+ std::move(selector))
+ .to_string();
+ } catch (cmList::transform_error& e) {
+ reportError(ctx, cnt->GetOriginalExpression(), e.what());
+ return std::string{};
+ }
+ }
+ }
+ return std::string{};
+ } },
+ { "REVERSE"_s,
+ [](cmGeneratorExpressionContext* ctx,
+ const GeneratorExpressionContent* cnt,
+ Arguments& args) -> std::string {
+ if (CheckListParameters(ctx, cnt, "REVERSE"_s, args)) {
+ return GetList(args.front()).reverse().to_string();
+ }
+ return std::string{};
+ } },
+ { "SORT"_s,
+ [](cmGeneratorExpressionContext* ctx,
+ const GeneratorExpressionContent* cnt,
+ Arguments& args) -> std::string {
+ if (CheckListParametersEx(ctx, cnt, "SORT"_s, args.size(), 1,
+ false)) {
+ auto list = GetList(args.front());
+ args.advance(1);
+ const auto COMPARE = "COMPARE:"_s;
+ const auto CASE = "CASE:"_s;
+ const auto ORDER = "ORDER:"_s;
+ using SortConfig = cmList::SortConfiguration;
+ SortConfig sortConfig;
+ for (auto const& arg : args) {
+ if (cmHasPrefix(arg, COMPARE)) {
+ if (sortConfig.Compare !=
+ SortConfig::CompareMethod::DEFAULT) {
+ reportError(ctx, cnt->GetOriginalExpression(),
+ "sub-command SORT, COMPARE option has been "
+ "specified multiple times.");
+ return std::string{};
+ }
+ 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(
+ ctx, 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(ctx, cnt->GetOriginalExpression(),
+ "sub-command SORT, CASE option has been "
+ "specified multiple times.");
+ 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(
+ ctx, 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(ctx, 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(
+ ctx, cnt->GetOriginalExpression(),
+ cmStrCat(
+ "sub-command SORT, an invalid ORDER option has been "
+ "specified: \"",
+ option, "\"."));
+ return std::string{};
+ }
+ reportError(ctx, cnt->GetOriginalExpression(),
+ cmStrCat("sub-command SORT, option \"", arg,
+ "\" is invalid."));
+ return std::string{};
+ }
+
+ return list.sort(sortConfig).to_string();
+ }
+ return std::string{};
+ } }
+ };
+
+ if (cm::contains(listCommands, parameters.front())) {
+ auto args = Arguments{ parameters }.advance(1);
+ return listCommands[parameters.front()](context, content, args);
+ }
+
+ reportError(context, content->GetOriginalExpression(),
+ cmStrCat(parameters.front(), ": invalid option."));
+ return std::string{};
+ }
+} listNode;
+
static const struct MakeCIdentifierNode : public cmGeneratorExpressionNode
{
MakeCIdentifierNode() {} // NOLINT(modernize-use-equals-default)
@@ -1559,7 +2250,8 @@ static const struct CompileLanguageAndIdNode : public cmGeneratorExpressionNode
// reportError(context, content->GetOriginalExpression(), "");
reportError(
context, content->GetOriginalExpression(),
- "$<COMPILE_LANG_AND_ID:lang,id> may only be used with binary targets "
+ "$<COMPILE_LANG_AND_ID:lang,id> may only be used with binary "
+ "targets "
"to specify include directories, compile definitions, and compile "
"options. It may not be used with the add_custom_command, "
"add_custom_target, or file(GENERATE) commands.");
@@ -1704,7 +2396,8 @@ static const struct LinkLanguageAndIdNode : public cmGeneratorExpressionNode
reportError(
context, content->GetOriginalExpression(),
"$<LINK_LANG_AND_ID:lang,id> may only be used with binary targets "
- "to specify link libraries, link directories, link options, and link "
+ "to specify link libraries, link directories, link options, and "
+ "link "
"depends.");
return std::string();
}
@@ -2086,7 +2779,8 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
reportError(
context, content->GetOriginalExpression(),
"$<TARGET_PROPERTY:prop> may only be used with binary targets. "
- "It may not be used with add_custom_command or add_custom_target. "
+ "It may not be used with add_custom_command or add_custom_target. "
+ " "
" "
"Specify the target to read a property from using the "
"$<TARGET_PROPERTY:tgt,prop> signature instead.");
@@ -3780,6 +4474,7 @@ const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode(
{ "IN_LIST", &inListNode },
{ "FILTER", &filterNode },
{ "REMOVE_DUPLICATES", &removeDuplicatesNode },
+ { "LIST", &listNode },
{ "LOWER_CASE", &lowerCaseNode },
{ "UPPER_CASE", &upperCaseNode },
{ "PATH", &pathNode },