diff options
author | Marc Chevrier <marc.chevrier@gmail.com> | 2023-03-31 15:34:11 (GMT) |
---|---|---|
committer | Marc Chevrier <marc.chevrier@gmail.com> | 2023-04-08 10:55:42 (GMT) |
commit | e8c581606dd2b1a7beb56cdb8117a9a2e547c602 (patch) | |
tree | 2bdddcf2b7122d376fc309db6c4774dd95247e33 /Source | |
parent | 0dcb5456029e98c41153bcf4377cd406781c4df4 (diff) | |
download | CMake-e8c581606dd2b1a7beb56cdb8117a9a2e547c602.zip CMake-e8c581606dd2b1a7beb56cdb8117a9a2e547c602.tar.gz CMake-e8c581606dd2b1a7beb56cdb8117a9a2e547c602.tar.bz2 |
list(): rely on cmList class
Fixes: #24549
Diffstat (limited to 'Source')
-rw-r--r-- | Source/cmList.cxx | 4 | ||||
-rw-r--r-- | Source/cmListCommand.cxx | 1152 |
2 files changed, 315 insertions, 841 deletions
diff --git a/Source/cmList.cxx b/Source/cmList.cxx index 39d138a..bf5a654 100644 --- a/Source/cmList.cxx +++ b/Source/cmList.cxx @@ -82,7 +82,9 @@ cmList& cmList::filter(cm::string_view pattern, FilterMode mode) { cmsys::RegularExpression regex(std::string{ pattern }); if (!regex.is_valid()) { - throw std::invalid_argument(cmStrCat("invalid regex: ", pattern)); + throw std::invalid_argument( + cmStrCat("sub-command FILTER, mode REGEX failed to compile regex \"", + pattern, "\".")); } auto it = std::remove_if(this->Values.begin(), this->Values.end(), diff --git a/Source/cmListCommand.cxx b/Source/cmListCommand.cxx index d412534..40be0ce 100644 --- a/Source/cmListCommand.cxx +++ b/Source/cmListCommand.cxx @@ -2,11 +2,9 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmListCommand.h" -#include <algorithm> #include <cassert> #include <cstdio> #include <functional> -#include <iterator> #include <set> #include <sstream> #include <stdexcept> @@ -14,22 +12,18 @@ #include <vector> #include <cm/memory> +#include <cm/optional> #include <cmext/algorithm> #include <cmext/string_view> -#include "cmsys/RegularExpression.hxx" - -#include "cmAlgorithms.h" #include "cmExecutionStatus.h" -#include "cmGeneratorExpression.h" +#include "cmList.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmPolicies.h" #include "cmRange.h" #include "cmStringAlgorithms.h" -#include "cmStringReplaceHelper.h" #include "cmSubcommandTable.h" -#include "cmSystemTools.h" #include "cmValue.h" namespace { @@ -70,11 +64,6 @@ bool GetIndexArg(const std::string& arg, int* idx, cmMakefile& mf) return true; } -bool FilterRegex(std::vector<std::string> const& args, bool includeMatches, - std::string const& listName, - std::vector<std::string>& varArgsExpanded, - cmExecutionStatus& status); - bool GetListString(std::string& listString, const std::string& var, const cmMakefile& makefile) { @@ -87,22 +76,25 @@ bool GetListString(std::string& listString, const std::string& var, return true; } -bool GetList(std::vector<std::string>& list, const std::string& var, - const cmMakefile& makefile) +cm::optional<cmList> GetList(const std::string& var, + const cmMakefile& makefile) { + cm::optional<cmList> list; + std::string listString; if (!GetListString(listString, var, makefile)) { - return false; + return list; } // if the size of the list if (listString.empty()) { - return true; + list.emplace(); + return list; } // expand the variable into a list - cmExpandList(listString, list, true); + list.emplace(listString, cmList::EmptyElements::Yes); // if no empty elements then just return - if (!cm::contains(list, std::string())) { - return true; + if (!cm::contains(*list, std::string())) { + return list; } // if we have empty elements we need to check policy CMP0007 switch (makefile.GetPolicyStatus(cmPolicies::CMP0007)) { @@ -111,31 +103,29 @@ bool GetList(std::vector<std::string>& list, const std::string& var, // OLD behavior is to allow compatibility, so recall // ExpandListArgument without the true which will remove // empty values - list.clear(); - cmExpandList(listString, list); + list->assign(listString); std::string warn = cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0007), " List has value = [", listString, "]."); makefile.IssueMessage(MessageType::AUTHOR_WARNING, warn); - return true; + return list; } case cmPolicies::OLD: // OLD behavior is to allow compatibility, so recall // ExpandListArgument without the true which will remove // empty values - list.clear(); - cmExpandList(listString, list); - return true; + list->assign(listString); + return list; case cmPolicies::NEW: - return true; + return list; case cmPolicies::REQUIRED_IF_USED: case cmPolicies::REQUIRED_ALWAYS: makefile.IssueMessage( MessageType::FATAL_ERROR, cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0007)); - return false; + return {}; } - return true; + return list; } bool HandleLengthCommand(std::vector<std::string> const& args, @@ -148,16 +138,11 @@ bool HandleLengthCommand(std::vector<std::string> const& args, const std::string& listName = args[1]; const std::string& variableName = args.back(); - std::vector<std::string> varArgsExpanded; - // do not check the return value here - // if the list var is not found varArgsExpanded will have size 0 - // and we will return 0 - GetList(varArgsExpanded, listName, status.GetMakefile()); - size_t length = varArgsExpanded.size(); - char buffer[1024]; - snprintf(buffer, sizeof(buffer), "%d", static_cast<int>(length)); - - status.GetMakefile().AddDefinition(variableName, buffer); + + auto list = GetList(listName, status.GetMakefile()); + status.GetMakefile().AddDefinition(variableName, + std::to_string(list ? list->size() : 0)); + return true; } @@ -172,42 +157,35 @@ bool HandleGetCommand(std::vector<std::string> const& args, const std::string& listName = args[1]; const std::string& variableName = args.back(); // expand the variable - std::vector<std::string> varArgsExpanded; - if (!GetList(varArgsExpanded, listName, status.GetMakefile())) { + auto list = GetList(listName, status.GetMakefile()); + if (!list) { status.GetMakefile().AddDefinition(variableName, "NOTFOUND"); return true; } // FIXME: Add policy to make non-existing lists an error like empty lists. - if (varArgsExpanded.empty()) { + if (list->empty()) { status.SetError("GET given empty list"); return false; } - std::string value; - size_t cc; - const char* sep = ""; - size_t nitem = varArgsExpanded.size(); - for (cc = 2; cc < args.size() - 1; cc++) { - int item; - if (!GetIndexArg(args[cc], &item, status.GetMakefile())) { + std::vector<int> indexes; + for (std::size_t cc = 2; cc < args.size() - 1; cc++) { + int index; + if (!GetIndexArg(args[cc], &index, status.GetMakefile())) { status.SetError(cmStrCat("index: ", args[cc], " is not a valid index")); return false; } - value += sep; - sep = ";"; - if (item < 0) { - item = static_cast<int>(nitem) + item; - } - if (item < 0 || nitem <= static_cast<size_t>(item)) { - status.SetError(cmStrCat("index: ", item, " out of range (-", nitem, - ", ", nitem - 1, ")")); - return false; - } - value += varArgsExpanded[item]; + indexes.push_back(index); } - status.GetMakefile().AddDefinition(variableName, value); - return true; + try { + auto values = list->get_items(indexes.begin(), indexes.end()); + status.GetMakefile().AddDefinition(variableName, values.to_string()); + return true; + } catch (std::out_of_range& e) { + status.SetError(e.what()); + return false; + } } bool HandleAppendCommand(std::vector<std::string> const& args, @@ -226,13 +204,8 @@ bool HandleAppendCommand(std::vector<std::string> const& args, std::string listString; GetListString(listString, listName, makefile); - // If `listString` or `args` is empty, no need to append `;`, - // then index is going to be `1` and points to the end-of-string ";" - auto const offset = - static_cast<std::string::size_type>(listString.empty() || args.empty()); - listString += &";"[offset] + cmJoin(cmMakeRange(args).advance(2), ";"); - - makefile.AddDefinition(listName, listString); + makefile.AddDefinition( + listName, cmList::append(args.begin() + 2, args.end(), listString)); return true; } @@ -252,14 +225,8 @@ bool HandlePrependCommand(std::vector<std::string> const& args, std::string listString; GetListString(listString, listName, makefile); - // If `listString` or `args` is empty, no need to append `;`, - // then `offset` is going to be `1` and points to the end-of-string ";" - auto const offset = - static_cast<std::string::size_type>(listString.empty() || args.empty()); - listString.insert(0, - cmJoin(cmMakeRange(args).advance(2), ";") + &";"[offset]); - - makefile.AddDefinition(listName, listString); + makefile.AddDefinition( + listName, cmList::prepend(args.begin() + 2, args.end(), listString)); return true; } @@ -272,8 +239,9 @@ bool HandlePopBackCommand(std::vector<std::string> const& args, auto ai = args.cbegin(); ++ai; // Skip subcommand name std::string const& listName = *ai++; - std::vector<std::string> varArgsExpanded; - if (!GetList(varArgsExpanded, listName, makefile)) { + auto list = GetList(listName, makefile); + + if (!list) { // Can't get the list definition... undefine any vars given after. for (; ai != args.cend(); ++ai) { makefile.RemoveDefinition(*ai); @@ -281,16 +249,16 @@ bool HandlePopBackCommand(std::vector<std::string> const& args, return true; } - if (!varArgsExpanded.empty()) { + if (!list->empty()) { if (ai == args.cend()) { // No variables are given... Just remove one element. - varArgsExpanded.pop_back(); + list->pop_back(); } else { // Ok, assign elements to be removed to the given variables - for (; !varArgsExpanded.empty() && ai != args.cend(); ++ai) { + for (; !list->empty() && ai != args.cend(); ++ai) { assert(!ai->empty()); - makefile.AddDefinition(*ai, varArgsExpanded.back()); - varArgsExpanded.pop_back(); + makefile.AddDefinition(*ai, list->back()); + list->pop_back(); } // Undefine the rest variables if the list gets empty earlier... for (; ai != args.cend(); ++ai) { @@ -298,7 +266,7 @@ bool HandlePopBackCommand(std::vector<std::string> const& args, } } - makefile.AddDefinition(listName, cmJoin(varArgsExpanded, ";")); + makefile.AddDefinition(listName, list->to_string()); } else if (ai != args.cend()) { // The list is empty, but some args were given @@ -320,8 +288,9 @@ bool HandlePopFrontCommand(std::vector<std::string> const& args, auto ai = args.cbegin(); ++ai; // Skip subcommand name std::string const& listName = *ai++; - std::vector<std::string> varArgsExpanded; - if (!GetList(varArgsExpanded, listName, makefile)) { + auto list = GetList(listName, makefile); + + if (!list) { // Can't get the list definition... undefine any vars given after. for (; ai != args.cend(); ++ai) { makefile.RemoveDefinition(*ai); @@ -329,25 +298,25 @@ bool HandlePopFrontCommand(std::vector<std::string> const& args, return true; } - if (!varArgsExpanded.empty()) { + if (!list->empty()) { if (ai == args.cend()) { // No variables are given... Just remove one element. - varArgsExpanded.erase(varArgsExpanded.begin()); + list->pop_front(); } else { // Ok, assign elements to be removed to the given variables - auto vi = varArgsExpanded.begin(); - for (; vi != varArgsExpanded.end() && ai != args.cend(); ++ai, ++vi) { + auto vi = list->begin(); + for (; vi != list->end() && ai != args.cend(); ++ai, ++vi) { assert(!ai->empty()); makefile.AddDefinition(*ai, *vi); } - varArgsExpanded.erase(varArgsExpanded.begin(), vi); + list->erase(list->begin(), vi); // Undefine the rest variables if the list gets empty earlier... for (; ai != args.cend(); ++ai) { makefile.RemoveDefinition(*ai); } } - makefile.AddDefinition(listName, cmJoin(varArgsExpanded, ";")); + makefile.AddDefinition(listName, list->to_string()); } else if (ai != args.cend()) { // The list is empty, but some args were given @@ -371,21 +340,16 @@ bool HandleFindCommand(std::vector<std::string> const& args, const std::string& listName = args[1]; const std::string& variableName = args.back(); // expand the variable - std::vector<std::string> varArgsExpanded; - if (!GetList(varArgsExpanded, listName, status.GetMakefile())) { - status.GetMakefile().AddDefinition(variableName, "-1"); - return true; - } + auto list = GetList(listName, status.GetMakefile()); - auto it = std::find(varArgsExpanded.begin(), varArgsExpanded.end(), args[2]); - if (it != varArgsExpanded.end()) { - status.GetMakefile().AddDefinition( - variableName, - std::to_string(std::distance(varArgsExpanded.begin(), it))); + if (!list) { + status.GetMakefile().AddDefinition(variableName, "-1"); return true; } - status.GetMakefile().AddDefinition(variableName, "-1"); + auto index = list->find(args[2]); + status.GetMakefile().AddDefinition( + variableName, index == cmList::npos ? "-1" : std::to_string(index)); return true; } @@ -400,38 +364,24 @@ bool HandleInsertCommand(std::vector<std::string> const& args, const std::string& listName = args[1]; // expand the variable - int item; - if (!GetIndexArg(args[2], &item, status.GetMakefile())) { + int index; + if (!GetIndexArg(args[2], &index, status.GetMakefile())) { status.SetError(cmStrCat("index: ", args[2], " is not a valid index")); return false; } - std::vector<std::string> varArgsExpanded; - if ((!GetList(varArgsExpanded, listName, status.GetMakefile()) || - varArgsExpanded.empty()) && - item != 0) { - status.SetError(cmStrCat("index: ", item, " out of range (0, 0)")); - return false; + auto list = GetList(listName, status.GetMakefile()); + if (!list) { + list = cmList{}; } - if (!varArgsExpanded.empty()) { - size_t nitem = varArgsExpanded.size(); - if (item < 0) { - item = static_cast<int>(nitem) + item; - } - if (item < 0 || nitem < static_cast<size_t>(item)) { - status.SetError(cmStrCat("index: ", item, " out of range (-", - varArgsExpanded.size(), ", ", - varArgsExpanded.size(), ")")); - return false; - } + try { + list->insert_items(index, args.begin() + 3, args.end()); + status.GetMakefile().AddDefinition(listName, list->to_string()); + return true; + } catch (std::out_of_range& e) { + status.SetError(e.what()); + return false; } - - varArgsExpanded.insert(varArgsExpanded.begin() + item, args.begin() + 3, - args.end()); - - std::string value = cmJoin(varArgsExpanded, ";"); - status.GetMakefile().AddDefinition(listName, value); - return true; } bool HandleJoinCommand(std::vector<std::string> const& args, @@ -448,16 +398,14 @@ bool HandleJoinCommand(std::vector<std::string> const& args, const std::string& variableName = args[3]; // expand the variable - std::vector<std::string> varArgsExpanded; - if (!GetList(varArgsExpanded, listName, status.GetMakefile())) { + auto list = GetList(listName, status.GetMakefile()); + + if (!list) { status.GetMakefile().AddDefinition(variableName, ""); return true; } - std::string value = - cmJoin(cmMakeRange(varArgsExpanded.begin(), varArgsExpanded.end()), glue); - - status.GetMakefile().AddDefinition(variableName, value); + status.GetMakefile().AddDefinition(variableName, list->join(glue)); return true; } @@ -472,21 +420,14 @@ bool HandleRemoveItemCommand(std::vector<std::string> const& args, const std::string& listName = args[1]; // expand the variable - std::vector<std::string> varArgsExpanded; - if (!GetList(varArgsExpanded, listName, status.GetMakefile())) { + auto list = GetList(listName, status.GetMakefile()); + + if (!list) { return true; } - std::vector<std::string> remove(args.begin() + 2, args.end()); - std::sort(remove.begin(), remove.end()); - auto remEnd = std::unique(remove.begin(), remove.end()); - auto remBegin = remove.begin(); - - auto argsEnd = - cmRemoveMatching(varArgsExpanded, cmMakeRange(remBegin, remEnd)); - auto argsBegin = varArgsExpanded.cbegin(); - std::string value = cmJoin(cmMakeRange(argsBegin, argsEnd), ";"); - status.GetMakefile().AddDefinition(listName, value); + status.GetMakefile().AddDefinition( + listName, list->remove_items(args.begin() + 2, args.end()).to_string()); return true; } @@ -501,14 +442,13 @@ bool HandleReverseCommand(std::vector<std::string> const& args, const std::string& listName = args[1]; // expand the variable - std::vector<std::string> varArgsExpanded; - if (!GetList(varArgsExpanded, listName, status.GetMakefile())) { + auto list = GetList(listName, status.GetMakefile()); + + if (!list) { return true; } - std::string value = cmJoin(cmReverseRange(varArgsExpanded), ";"); - - status.GetMakefile().AddDefinition(listName, value); + status.GetMakefile().AddDefinition(listName, list->reverse().to_string()); return true; } @@ -523,237 +463,17 @@ bool HandleRemoveDuplicatesCommand(std::vector<std::string> const& args, const std::string& listName = args[1]; // expand the variable - std::vector<std::string> varArgsExpanded; - if (!GetList(varArgsExpanded, listName, status.GetMakefile())) { + auto list = GetList(listName, status.GetMakefile()); + + if (!list) { return true; } - auto argsEnd = cmRemoveDuplicates(varArgsExpanded); - auto argsBegin = varArgsExpanded.cbegin(); - std::string value = cmJoin(cmMakeRange(argsBegin, argsEnd), ";"); - - status.GetMakefile().AddDefinition(listName, value); + status.GetMakefile().AddDefinition(listName, + list->remove_duplicates().to_string()); return true; } -// Helpers for list(TRANSFORM <list> ...) -using transform_type = std::function<std::string(const std::string&)>; - -class transform_error : public std::runtime_error -{ -public: - transform_error(const std::string& error) - : std::runtime_error(error) - { - } -}; - -class TransformSelector -{ -public: - virtual ~TransformSelector() = default; - - std::string Tag; - - virtual bool Validate(std::size_t count = 0) = 0; - - virtual bool InSelection(const std::string&) = 0; - - virtual void Transform(std::vector<std::string>& list, - const transform_type& transform) - { - std::transform(list.begin(), list.end(), list.begin(), transform); - } - -protected: - TransformSelector(std::string&& tag) - : Tag(std::move(tag)) - { - } -}; -class TransformNoSelector : public TransformSelector -{ -public: - TransformNoSelector() - : TransformSelector("NO SELECTOR") - { - } - - bool Validate(std::size_t) override { return true; } - - bool InSelection(const std::string&) override { return true; } -}; -class TransformSelectorRegex : public TransformSelector -{ -public: - TransformSelectorRegex(const std::string& regex) - : TransformSelector("REGEX") - , Regex(regex) - { - } - - bool Validate(std::size_t) override { return this->Regex.is_valid(); } - - bool InSelection(const std::string& value) override - { - return this->Regex.find(value); - } - - cmsys::RegularExpression Regex; -}; -class TransformSelectorIndexes : public TransformSelector -{ -public: - std::vector<int> Indexes; - - bool InSelection(const std::string&) override { return true; } - - void Transform(std::vector<std::string>& list, - const transform_type& transform) override - { - this->Validate(list.size()); - - for (auto index : this->Indexes) { - list[index] = transform(list[index]); - } - } - -protected: - TransformSelectorIndexes(std::string&& tag) - : TransformSelector(std::move(tag)) - { - } - TransformSelectorIndexes(std::string&& tag, std::vector<int>&& indexes) - : TransformSelector(std::move(tag)) - , Indexes(indexes) - { - } - - int NormalizeIndex(int index, std::size_t count) - { - if (index < 0) { - index = static_cast<int>(count) + index; - } - if (index < 0 || count <= static_cast<std::size_t>(index)) { - throw transform_error(cmStrCat( - "sub-command TRANSFORM, selector ", this->Tag, ", index: ", index, - " out of range (-", count, ", ", count - 1, ").")); - } - return index; - } -}; -class TransformSelectorAt : public TransformSelectorIndexes -{ -public: - TransformSelectorAt(std::vector<int>&& indexes) - : TransformSelectorIndexes("AT", std::move(indexes)) - { - } - - bool Validate(std::size_t count) override - { - decltype(this->Indexes) indexes; - - for (auto index : this->Indexes) { - indexes.push_back(this->NormalizeIndex(index, count)); - } - this->Indexes = std::move(indexes); - - return true; - } -}; -class TransformSelectorFor : public TransformSelectorIndexes -{ -public: - TransformSelectorFor(int start, int stop, int step) - : TransformSelectorIndexes("FOR") - , Start(start) - , Stop(stop) - , Step(step) - { - } - - bool Validate(std::size_t count) override - { - this->Start = this->NormalizeIndex(this->Start, count); - this->Stop = this->NormalizeIndex(this->Stop, count); - - // Does stepping move us further from the end? - if (this->Start > this->Stop) { - throw transform_error( - cmStrCat("sub-command TRANSFORM, selector FOR " - "expects <start> to be no greater than <stop> (", - this->Start, " > ", this->Stop, ")")); - } - - // compute indexes - auto size = (this->Stop - this->Start + 1) / this->Step; - if ((this->Stop - this->Start + 1) % this->Step != 0) { - size += 1; - } - - this->Indexes.resize(size); - auto start = this->Start; - auto step = this->Step; - std::generate(this->Indexes.begin(), this->Indexes.end(), - [&start, step]() -> int { - auto r = start; - start += step; - return r; - }); - - return true; - } - -private: - int Start, Stop, Step; -}; - -class TransformAction -{ -public: - virtual ~TransformAction() = default; - - virtual std::string Transform(const std::string& input) = 0; -}; -class TransformReplace : public TransformAction -{ -public: - TransformReplace(const std::vector<std::string>& arguments, - cmMakefile* makefile) - : ReplaceHelper(arguments[0], arguments[1], makefile) - { - makefile->ClearMatches(); - - if (!this->ReplaceHelper.IsRegularExpressionValid()) { - throw transform_error( - cmStrCat("sub-command TRANSFORM, action REPLACE: Failed to compile " - "regex \"", - arguments[0], "\".")); - } - if (!this->ReplaceHelper.IsReplaceExpressionValid()) { - throw transform_error(cmStrCat("sub-command TRANSFORM, action REPLACE: ", - this->ReplaceHelper.GetError(), ".")); - } - } - - std::string Transform(const std::string& input) override - { - // Scan through the input for all matches. - std::string output; - - if (!this->ReplaceHelper.Replace(input, output)) { - throw transform_error(cmStrCat("sub-command TRANSFORM, action REPLACE: ", - this->ReplaceHelper.GetError(), ".")); - } - - return output; - } - -private: - cmStringReplaceHelper ReplaceHelper; -}; - bool HandleTransformCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { @@ -763,119 +483,50 @@ bool HandleTransformCommand(std::vector<std::string> const& args, return false; } - // Structure collecting all elements of the command - struct Command - { - Command(const std::string& listName) - : ListName(listName) - , OutputName(listName) - { - } - - std::string Name; - std::string ListName; - std::vector<std::string> Arguments; - std::unique_ptr<TransformAction> Action; - std::unique_ptr<TransformSelector> Selector; - std::string OutputName; - } command(args[1]); - // Descriptor of action + // Action: enum value identifying action // Arity: number of arguments required for the action - // Transform: lambda function implementing the action struct ActionDescriptor { ActionDescriptor(std::string name) : Name(std::move(name)) { } - ActionDescriptor(std::string name, int arity, transform_type transform) + ActionDescriptor(std::string name, cmList::TransformAction action, + int arity) : Name(std::move(name)) + , Action(action) , Arity(arity) -#if defined(__GNUC__) && __GNUC__ == 6 && defined(__aarch64__) - // std::function move constructor miscompiles on this architecture - , Transform(transform) -#else - , Transform(std::move(transform)) -#endif { } operator const std::string&() const { return this->Name; } std::string Name; + cmList::TransformAction Action; int Arity = 0; - transform_type Transform; }; // Build a set of supported actions. std::set<ActionDescriptor, std::function<bool(const std::string&, const std::string&)>> - descriptors( - [](const std::string& x, const std::string& y) { return x < y; }); - descriptors = { { "APPEND", 1, - [&command](const std::string& s) -> std::string { - if (command.Selector->InSelection(s)) { - return s + command.Arguments[0]; - } - - return s; - } }, - { "PREPEND", 1, - [&command](const std::string& s) -> std::string { - if (command.Selector->InSelection(s)) { - return command.Arguments[0] + s; - } - - return s; - } }, - { "TOUPPER", 0, - [&command](const std::string& s) -> std::string { - if (command.Selector->InSelection(s)) { - return cmSystemTools::UpperCase(s); - } - - return s; - } }, - { "TOLOWER", 0, - [&command](const std::string& s) -> std::string { - if (command.Selector->InSelection(s)) { - return cmSystemTools::LowerCase(s); - } - - return s; - } }, - { "STRIP", 0, - [&command](const std::string& s) -> std::string { - if (command.Selector->InSelection(s)) { - return cmTrimWhitespace(s); - } - - return s; - } }, - { "GENEX_STRIP", 0, - [&command](const std::string& s) -> std::string { - if (command.Selector->InSelection(s)) { - return cmGeneratorExpression::Preprocess( - s, - cmGeneratorExpression::StripAllGeneratorExpressions); - } - - return s; - } }, - { "REPLACE", 2, - [&command](const std::string& s) -> std::string { - if (command.Selector->InSelection(s)) { - return command.Action->Transform(s); - } - - return s; - } } }; + 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 }, + { "GENEX_STRIP", cmList::TransformAction::GENEX_STRIP, 0 }, + { "REPLACE", cmList::TransformAction::REPLACE, 2 } }, + [](const std::string& x, const std::string& y) { + return x < y; + } }; + const std::string& listName = args[1]; + + // Parse all possible function parameters using size_type = std::vector<std::string>::size_type; size_type index = 2; - // Parse all possible function parameters auto descriptor = descriptors.find(args[index]); if (descriptor == descriptors.end()) { @@ -893,295 +544,184 @@ bool HandleTransformCommand(std::vector<std::string> const& args, return false; } - command.Name = descriptor->Name; + std::vector<std::string> arguments; index += descriptor->Arity; if (descriptor->Arity > 0) { - command.Arguments = + arguments = std::vector<std::string>(args.begin() + 3, args.begin() + index); } - if (command.Name == "REPLACE") { - try { - command.Action = cm::make_unique<TransformReplace>( - command.Arguments, &status.GetMakefile()); - } catch (const transform_error& e) { - status.SetError(e.what()); - return false; - } - } - const std::string REGEX{ "REGEX" }; const std::string AT{ "AT" }; const std::string FOR{ "FOR" }; const std::string OUTPUT_VARIABLE{ "OUTPUT_VARIABLE" }; + std::unique_ptr<cmList::TransformSelector> selector; + std::string outputName = listName; - // handle optional arguments - while (args.size() > index) { - if ((args[index] == REGEX || args[index] == AT || args[index] == FOR) && - command.Selector) { - status.SetError( - cmStrCat("sub-command TRANSFORM, selector already specified (", - command.Selector->Tag, ").")); - - return false; - } + try { + // handle optional arguments + while (args.size() > index) { + if ((args[index] == REGEX || args[index] == AT || args[index] == FOR) && + selector) { + status.SetError( + cmStrCat("sub-command TRANSFORM, selector already specified (", + selector->GetTag(), ").")); - // REGEX selector - if (args[index] == REGEX) { - if (args.size() == ++index) { - status.SetError("sub-command TRANSFORM, selector REGEX expects " - "'regular expression' argument."); return false; } - command.Selector = cm::make_unique<TransformSelectorRegex>(args[index]); - if (!command.Selector->Validate()) { - status.SetError( - cmStrCat("sub-command TRANSFORM, selector REGEX failed to compile " - "regex \"", - args[index], "\".")); - return false; - } + // REGEX selector + if (args[index] == REGEX) { + if (args.size() == ++index) { + status.SetError("sub-command TRANSFORM, selector REGEX expects " + "'regular expression' argument."); + return false; + } - index += 1; - continue; - } + selector = + cmList::TransformSelector::New<cmList::TransformSelector::REGEX>( + args[index]); - // AT selector - if (args[index] == AT) { - // get all specified indexes - std::vector<int> indexes; - while (args.size() > ++index) { - std::size_t pos; - int value; + index += 1; + continue; + } - try { - value = std::stoi(args[index], &pos); - if (pos != args[index].length()) { + // AT selector + if (args[index] == AT) { + // get all specified indexes + std::vector<cmList::index_type> indexes; + while (args.size() > ++index) { + std::size_t pos; + int value; + + try { + value = std::stoi(args[index], &pos); + if (pos != args[index].length()) { + // this is not a number, stop processing + break; + } + indexes.push_back(value); + } catch (const std::invalid_argument&) { // this is not a number, stop processing break; } - indexes.push_back(value); - } catch (const std::invalid_argument&) { - // this is not a number, stop processing - break; } - } - if (indexes.empty()) { - status.SetError( - "sub-command TRANSFORM, selector AT expects at least one " - "numeric value."); - return false; - } + if (indexes.empty()) { + status.SetError( + "sub-command TRANSFORM, selector AT expects at least one " + "numeric value."); + return false; + } - command.Selector = - cm::make_unique<TransformSelectorAt>(std::move(indexes)); + selector = + cmList::TransformSelector::New<cmList::TransformSelector::AT>( + std::move(indexes)); - continue; - } - - // FOR selector - if (args[index] == FOR) { - if (args.size() <= ++index + 1) { - status.SetError( - "sub-command TRANSFORM, selector FOR expects, at least," - " two arguments."); - return false; + continue; } - int start = 0; - int stop = 0; - int step = 1; - bool valid = true; - try { - std::size_t pos; - - start = std::stoi(args[index], &pos); - if (pos != args[index].length()) { - // this is not a number - valid = false; - } else { - stop = std::stoi(args[++index], &pos); - if (pos != args[index].length()) { - // this is not a number - valid = false; - } + // FOR selector + if (args[index] == FOR) { + if (args.size() <= ++index + 1) { + status.SetError( + "sub-command TRANSFORM, selector FOR expects, at least," + " two arguments."); + return false; } - } catch (const std::invalid_argument&) { - // this is not numbers - valid = false; - } - if (!valid) { - status.SetError("sub-command TRANSFORM, selector FOR expects, " - "at least, two numeric values."); - return false; - } - // try to read a third numeric value for step - if (args.size() > ++index) { + + cmList::index_type start = 0; + cmList::index_type stop = 0; + cmList::index_type step = 1; + bool valid = true; try { std::size_t pos; - step = std::stoi(args[index], &pos); + start = std::stoi(args[index], &pos); if (pos != args[index].length()) { // this is not a number - step = 1; + valid = false; } else { - index += 1; + stop = std::stoi(args[++index], &pos); + if (pos != args[index].length()) { + // this is not a number + valid = false; + } } } catch (const std::invalid_argument&) { - // this is not number, ignore exception + // this is not numbers + valid = false; + } + if (!valid) { + status.SetError("sub-command TRANSFORM, selector FOR expects, " + "at least, two numeric values."); + return false; + } + // try to read a third numeric value for step + if (args.size() > ++index) { + try { + std::size_t pos; + + step = std::stoi(args[index], &pos); + if (pos != args[index].length()) { + // this is not a number + step = 1; + } else { + index += 1; + } + } catch (const std::invalid_argument&) { + // this is not number, ignore exception + } } - } - if (step <= 0) { - status.SetError("sub-command TRANSFORM, selector FOR expects " - "positive numeric value for <step>."); - return false; - } + if (step <= 0) { + status.SetError("sub-command TRANSFORM, selector FOR expects " + "positive numeric value for <step>."); + return false; + } - command.Selector = - cm::make_unique<TransformSelectorFor>(start, stop, step); + selector = + cmList::TransformSelector::New<cmList::TransformSelector::FOR>( + { start, stop, step }); - continue; - } + continue; + } - // output variable - if (args[index] == OUTPUT_VARIABLE) { - if (args.size() == ++index) { - status.SetError("sub-command TRANSFORM, OUTPUT_VARIABLE " - "expects variable name argument."); - return false; + // output variable + if (args[index] == OUTPUT_VARIABLE) { + if (args.size() == ++index) { + status.SetError("sub-command TRANSFORM, OUTPUT_VARIABLE " + "expects variable name argument."); + return false; + } + + outputName = args[index++]; + continue; } - command.OutputName = args[index++]; - continue; + status.SetError(cmStrCat("sub-command TRANSFORM, '", + cmJoin(cmMakeRange(args).advance(index), " "), + "': unexpected argument(s).")); + return false; } - status.SetError(cmStrCat("sub-command TRANSFORM, '", - cmJoin(cmMakeRange(args).advance(index), " "), - "': unexpected argument(s).")); - return false; - } - - // expand the list variable - std::vector<std::string> varArgsExpanded; - if (!GetList(varArgsExpanded, command.ListName, status.GetMakefile())) { - status.GetMakefile().AddDefinition(command.OutputName, ""); - return true; - } + // expand the list variable + auto list = GetList(listName, status.GetMakefile()); - if (!command.Selector) { - // no selector specified, apply transformation to all elements - command.Selector = cm::make_unique<TransformNoSelector>(); - } + if (!list) { + status.GetMakefile().AddDefinition(outputName, ""); + return true; + } - try { - command.Selector->Transform(varArgsExpanded, descriptor->Transform); - } catch (const transform_error& e) { + list->transform(descriptor->Action, arguments, std::move(selector)); + status.GetMakefile().AddDefinition(outputName, list->to_string()); + return true; + } catch (cmList::transform_error& e) { status.SetError(e.what()); return false; } - - status.GetMakefile().AddDefinition(command.OutputName, - cmJoin(varArgsExpanded, ";")); - - return true; } -class cmStringSorter -{ -public: - enum class Order - { - UNINITIALIZED, - ASCENDING, - DESCENDING, - }; - - enum class Compare - { - UNINITIALIZED, - STRING, - FILE_BASENAME, - NATURAL, - }; - enum class CaseSensitivity - { - UNINITIALIZED, - SENSITIVE, - INSENSITIVE, - }; - -protected: - using StringFilter = std::string (*)(const std::string&); - StringFilter GetCompareFilter(Compare compare) - { - return (compare == Compare::FILE_BASENAME) ? cmSystemTools::GetFilenameName - : nullptr; - } - - StringFilter GetCaseFilter(CaseSensitivity sensitivity) - { - return (sensitivity == CaseSensitivity::INSENSITIVE) - ? cmSystemTools::LowerCase - : nullptr; - } - - using ComparisonFunction = - std::function<bool(const std::string&, const std::string&)>; - ComparisonFunction GetComparisonFunction(Compare compare) - { - if (compare == Compare::NATURAL) { - return std::function<bool(const std::string&, const std::string&)>( - [](const std::string& x, const std::string& y) { - return cmSystemTools::strverscmp(x, y) < 0; - }); - } - return std::function<bool(const std::string&, const std::string&)>( - [](const std::string& x, const std::string& y) { return x < y; }); - } - -public: - cmStringSorter(Compare compare, CaseSensitivity caseSensitivity, - Order desc = Order::ASCENDING) - : filters{ this->GetCompareFilter(compare), - this->GetCaseFilter(caseSensitivity) } - , sortMethod(this->GetComparisonFunction(compare)) - , descending(desc == Order::DESCENDING) - { - } - - std::string ApplyFilter(const std::string& argument) - { - std::string result = argument; - for (auto filter : this->filters) { - if (filter != nullptr) { - result = filter(result); - } - } - return result; - } - - bool operator()(const std::string& a, const std::string& b) - { - std::string af = this->ApplyFilter(a); - std::string bf = this->ApplyFilter(b); - bool result; - if (this->descending) { - result = this->sortMethod(bf, af); - } else { - result = this->sortMethod(af, bf); - } - return result; - } - -protected: - StringFilter filters[2] = { nullptr, nullptr }; - ComparisonFunction sortMethod; - bool descending; -}; - bool HandleSortCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { @@ -1191,9 +731,8 @@ bool HandleSortCommand(std::vector<std::string> const& args, return false; } - auto sortCompare = cmStringSorter::Compare::UNINITIALIZED; - auto sortCaseSensitivity = cmStringSorter::CaseSensitivity::UNINITIALIZED; - auto sortOrder = cmStringSorter::Order::UNINITIALIZED; + using SortConfig = cmList::SortConfiguration; + SortConfig sortConfig; size_t argumentIndex = 2; const std::string messageHint = "sub-command SORT "; @@ -1201,7 +740,7 @@ bool HandleSortCommand(std::vector<std::string> const& args, while (argumentIndex < args.size()) { std::string const& option = args[argumentIndex++]; if (option == "COMPARE") { - if (sortCompare != cmStringSorter::Compare::UNINITIALIZED) { + if (sortConfig.Compare != SortConfig::CompareMethod::DEFAULT) { std::string error = cmStrCat(messageHint, "option \"", option, "\" has been specified multiple times."); status.SetError(error); @@ -1210,11 +749,11 @@ bool HandleSortCommand(std::vector<std::string> const& args, if (argumentIndex < args.size()) { std::string const& argument = args[argumentIndex++]; if (argument == "STRING") { - sortCompare = cmStringSorter::Compare::STRING; + sortConfig.Compare = SortConfig::CompareMethod::STRING; } else if (argument == "FILE_BASENAME") { - sortCompare = cmStringSorter::Compare::FILE_BASENAME; + sortConfig.Compare = SortConfig::CompareMethod::FILE_BASENAME; } else if (argument == "NATURAL") { - sortCompare = cmStringSorter::Compare::NATURAL; + sortConfig.Compare = SortConfig::CompareMethod::NATURAL; } else { std::string error = cmStrCat(messageHint, "value \"", argument, "\" for option \"", @@ -1228,8 +767,7 @@ bool HandleSortCommand(std::vector<std::string> const& args, return false; } } else if (option == "CASE") { - if (sortCaseSensitivity != - cmStringSorter::CaseSensitivity::UNINITIALIZED) { + if (sortConfig.Case != SortConfig::CaseSensitivity::DEFAULT) { status.SetError(cmStrCat(messageHint, "option \"", option, "\" has been specified multiple times.")); return false; @@ -1237,9 +775,9 @@ bool HandleSortCommand(std::vector<std::string> const& args, if (argumentIndex < args.size()) { std::string const& argument = args[argumentIndex++]; if (argument == "SENSITIVE") { - sortCaseSensitivity = cmStringSorter::CaseSensitivity::SENSITIVE; + sortConfig.Case = SortConfig::CaseSensitivity::SENSITIVE; } else if (argument == "INSENSITIVE") { - sortCaseSensitivity = cmStringSorter::CaseSensitivity::INSENSITIVE; + sortConfig.Case = SortConfig::CaseSensitivity::INSENSITIVE; } else { status.SetError(cmStrCat(messageHint, "value \"", argument, "\" for option \"", option, @@ -1253,7 +791,7 @@ bool HandleSortCommand(std::vector<std::string> const& args, } } else if (option == "ORDER") { - if (sortOrder != cmStringSorter::Order::UNINITIALIZED) { + if (sortConfig.Order != SortConfig::OrderMode::DEFAULT) { status.SetError(cmStrCat(messageHint, "option \"", option, "\" has been specified multiple times.")); return false; @@ -1261,9 +799,9 @@ bool HandleSortCommand(std::vector<std::string> const& args, if (argumentIndex < args.size()) { std::string const& argument = args[argumentIndex++]; if (argument == "ASCENDING") { - sortOrder = cmStringSorter::Order::ASCENDING; + sortConfig.Order = SortConfig::OrderMode::ASCENDING; } else if (argument == "DESCENDING") { - sortOrder = cmStringSorter::Order::DESCENDING; + sortConfig.Order = SortConfig::OrderMode::DESCENDING; } else { status.SetError(cmStrCat(messageHint, "value \"", argument, "\" for option \"", option, @@ -1281,35 +819,17 @@ bool HandleSortCommand(std::vector<std::string> const& args, return false; } } - // set Default Values if Option is not given - if (sortCompare == cmStringSorter::Compare::UNINITIALIZED) { - sortCompare = cmStringSorter::Compare::STRING; - } - if (sortCaseSensitivity == cmStringSorter::CaseSensitivity::UNINITIALIZED) { - sortCaseSensitivity = cmStringSorter::CaseSensitivity::SENSITIVE; - } - if (sortOrder == cmStringSorter::Order::UNINITIALIZED) { - sortOrder = cmStringSorter::Order::ASCENDING; - } const std::string& listName = args[1]; // expand the variable - std::vector<std::string> varArgsExpanded; - if (!GetList(varArgsExpanded, listName, status.GetMakefile())) { - return true; - } + auto list = GetList(listName, status.GetMakefile()); - if ((sortCompare == cmStringSorter::Compare::STRING) && - (sortCaseSensitivity == cmStringSorter::CaseSensitivity::SENSITIVE) && - (sortOrder == cmStringSorter::Order::ASCENDING)) { - std::sort(varArgsExpanded.begin(), varArgsExpanded.end()); - } else { - cmStringSorter sorter(sortCompare, sortCaseSensitivity, sortOrder); - std::sort(varArgsExpanded.begin(), varArgsExpanded.end(), sorter); + if (!list) { + return true; } - std::string value = cmJoin(varArgsExpanded, ";"); - status.GetMakefile().AddDefinition(listName, value); + status.GetMakefile().AddDefinition(listName, + list->sort(sortConfig).to_string()); return true; } @@ -1326,9 +846,9 @@ bool HandleSublistCommand(std::vector<std::string> const& args, const std::string& variableName = args.back(); // expand the variable - std::vector<std::string> varArgsExpanded; - if (!GetList(varArgsExpanded, listName, status.GetMakefile()) || - varArgsExpanded.empty()) { + auto list = GetList(listName, status.GetMakefile()); + + if (!list || list->empty()) { status.GetMakefile().AddDefinition(variableName, ""); return true; } @@ -1344,11 +864,9 @@ bool HandleSublistCommand(std::vector<std::string> const& args, return false; } - using size_type = decltype(varArgsExpanded)::size_type; - - if (start < 0 || static_cast<size_type>(start) >= varArgsExpanded.size()) { + if (start < 0) { status.SetError(cmStrCat("begin index: ", start, " is out of range 0 - ", - varArgsExpanded.size() - 1)); + list->size() - 1)); return false; } if (length < -1) { @@ -1356,15 +874,17 @@ bool HandleSublistCommand(std::vector<std::string> const& args, return false; } - const size_type end = - (length == -1 || - static_cast<size_type>(start + length) > varArgsExpanded.size()) - ? varArgsExpanded.size() - : static_cast<size_type>(start + length); - std::vector<std::string> sublist(varArgsExpanded.begin() + start, - varArgsExpanded.begin() + end); - status.GetMakefile().AddDefinition(variableName, cmJoin(sublist, ";")); - return true; + using size_type = cmList::size_type; + + try { + auto sublist = list->sublist(static_cast<size_type>(start), + static_cast<size_type>(length)); + status.GetMakefile().AddDefinition(variableName, sublist.to_string()); + return true; + } catch (std::out_of_range& e) { + status.SetError(e.what()); + return false; + } } bool HandleRemoveAtCommand(std::vector<std::string> const& args, @@ -1378,9 +898,9 @@ bool HandleRemoveAtCommand(std::vector<std::string> const& args, const std::string& listName = args[1]; // expand the variable - std::vector<std::string> varArgsExpanded; - if (!GetList(varArgsExpanded, listName, status.GetMakefile()) || - varArgsExpanded.empty()) { + auto list = GetList(listName, status.GetMakefile()); + + if (!list || list->empty()) { std::ostringstream str; str << "index: "; for (size_t i = 1; i < args.size(); ++i) { @@ -1395,36 +915,25 @@ bool HandleRemoveAtCommand(std::vector<std::string> const& args, } size_t cc; - std::vector<size_t> removed; - size_t nitem = varArgsExpanded.size(); + std::vector<cmList::index_type> removed; for (cc = 2; cc < args.size(); ++cc) { - int item; - if (!GetIndexArg(args[cc], &item, status.GetMakefile())) { + int index; + if (!GetIndexArg(args[cc], &index, status.GetMakefile())) { status.SetError(cmStrCat("index: ", args[cc], " is not a valid index")); return false; } - if (item < 0) { - item = static_cast<int>(nitem) + item; - } - if (item < 0 || nitem <= static_cast<size_t>(item)) { - status.SetError(cmStrCat("index: ", item, " out of range (-", nitem, - ", ", nitem - 1, ")")); - return false; - } - removed.push_back(static_cast<size_t>(item)); + removed.push_back(index); } - std::sort(removed.begin(), removed.end()); - auto remEnd = std::unique(removed.begin(), removed.end()); - auto remBegin = removed.begin(); - - auto argsEnd = - cmRemoveIndices(varArgsExpanded, cmMakeRange(remBegin, remEnd)); - auto argsBegin = varArgsExpanded.cbegin(); - std::string value = cmJoin(cmMakeRange(argsBegin, argsEnd), ";"); - - status.GetMakefile().AddDefinition(listName, value); - return true; + try { + status.GetMakefile().AddDefinition( + listName, + list->remove_items(removed.begin(), removed.end()).to_string()); + return true; + } catch (std::out_of_range& e) { + status.SetError(e.what()); + return false; + } } bool HandleFilterCommand(std::vector<std::string> const& args, @@ -1447,11 +956,11 @@ bool HandleFilterCommand(std::vector<std::string> const& args, } const std::string& op = args[2]; - bool includeMatches; + cmList::FilterMode filterMode; if (op == "INCLUDE") { - includeMatches = true; + filterMode = cmList::FilterMode::INCLUDE; } else if (op == "EXCLUDE") { - includeMatches = false; + filterMode = cmList::FilterMode::EXCLUDE; } else { status.SetError("sub-command FILTER does not recognize operator " + op); return false; @@ -1459,70 +968,33 @@ bool HandleFilterCommand(std::vector<std::string> const& args, const std::string& listName = args[1]; // expand the variable - std::vector<std::string> varArgsExpanded; - if (!GetList(varArgsExpanded, listName, status.GetMakefile())) { + auto list = GetList(listName, status.GetMakefile()); + + if (!list) { return true; } const std::string& mode = args[3]; - if (mode == "REGEX") { - if (args.size() != 5) { - status.SetError("sub-command FILTER, mode REGEX " - "requires five arguments."); - return false; - } - return FilterRegex(args, includeMatches, listName, varArgsExpanded, - status); - } - - status.SetError("sub-command FILTER does not recognize mode " + mode); - return false; -} - -class MatchesRegex -{ -public: - MatchesRegex(cmsys::RegularExpression& in_regex, bool in_includeMatches) - : regex(in_regex) - , includeMatches(in_includeMatches) - { + if (mode != "REGEX") { + status.SetError("sub-command FILTER does not recognize mode " + mode); + return false; } - - bool operator()(const std::string& target) - { - return this->regex.find(target) ^ this->includeMatches; + if (args.size() != 5) { + status.SetError("sub-command FILTER, mode REGEX " + "requires five arguments."); + return false; } - -private: - cmsys::RegularExpression& regex; - const bool includeMatches; -}; - -bool FilterRegex(std::vector<std::string> const& args, bool includeMatches, - std::string const& listName, - std::vector<std::string>& varArgsExpanded, - cmExecutionStatus& status) -{ const std::string& pattern = args[4]; - cmsys::RegularExpression regex(pattern); - if (!regex.is_valid()) { - std::string error = - cmStrCat("sub-command FILTER, mode REGEX failed to compile regex \"", - pattern, "\"."); - status.SetError(error); + + try { + status.GetMakefile().AddDefinition( + listName, list->filter(pattern, filterMode).to_string()); + return true; + } catch (std::invalid_argument& e) { + status.SetError(e.what()); return false; } - - auto argsBegin = varArgsExpanded.begin(); - auto argsEnd = varArgsExpanded.end(); - auto newArgsEnd = - std::remove_if(argsBegin, argsEnd, MatchesRegex(regex, includeMatches)); - - std::string value = cmJoin(cmMakeRange(argsBegin, newArgsEnd), ";"); - status.GetMakefile().AddDefinition(listName, value); - return true; } - } // namespace bool cmListCommand(std::vector<std::string> const& args, |