|
|
@@ -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 },
|