summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Source/CMakeLists.txt2
-rw-r--r--Source/cmAlgorithms.h2
-rw-r--r--Source/cmGeneratorExpressionNode.cxx45
-rw-r--r--Source/cmList.cxx996
-rw-r--r--Source/cmList.h1198
-rw-r--r--Tests/CMakeLib/CMakeLists.txt1
-rw-r--r--Tests/CMakeLib/testList.cxx995
-rwxr-xr-xbootstrap1
8 files changed, 3211 insertions, 29 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 3ae0bc643b..b691d38d40 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -333,6 +333,8 @@ add_library(
cmLinkLineComputer.h
cmLinkLineDeviceComputer.cxx
cmLinkLineDeviceComputer.h
+ cmList.h
+ cmList.cxx
cmListFileCache.cxx
cmListFileCache.h
cmLocalCommonGenerator.cxx
diff --git a/Source/cmAlgorithms.h b/Source/cmAlgorithms.h
index a1830f9f8e..2c3ee9b5ef 100644
--- a/Source/cmAlgorithms.h
+++ b/Source/cmAlgorithms.h
@@ -95,7 +95,7 @@ typename Range::const_iterator cmRemoveIndices(Range& r, InputRange const& rem)
}
template <typename Range, typename MatchRange>
-typename Range::const_iterator cmRemoveMatching(Range& r, MatchRange const& m)
+auto cmRemoveMatching(Range& r, MatchRange const& m) -> decltype(r.begin())
{
return std::remove_if(r.begin(), r.end(),
ContainerAlgorithms::BinarySearcher<MatchRange>(m));
diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx
index 6e78f55776..a47366b4a1 100644
--- a/Source/cmGeneratorExpressionNode.cxx
+++ b/Source/cmGeneratorExpressionNode.cxx
@@ -12,6 +12,7 @@
#include <memory>
#include <set>
#include <sstream>
+#include <stdexcept>
#include <unordered_map>
#include <utility>
@@ -25,7 +26,6 @@
#include "cmsys/RegularExpression.hxx"
#include "cmsys/String.h"
-#include "cmAlgorithms.h"
#include "cmCMakePath.h"
#include "cmComputeLinkInformation.h"
#include "cmGeneratorExpression.h"
@@ -35,6 +35,7 @@
#include "cmGeneratorTarget.h"
#include "cmGlobalGenerator.h"
#include "cmLinkItem.h"
+#include "cmList.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
@@ -291,18 +292,18 @@ static const struct InListNode : public cmGeneratorExpressionNode
const GeneratorExpressionContent* /*content*/,
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
{
- std::vector<std::string> values;
- std::vector<std::string> checkValues;
+ cmList values;
+ cmList checkValues;
bool check = false;
switch (context->LG->GetPolicyStatus(cmPolicies::CMP0085)) {
case cmPolicies::WARN:
if (parameters.front().empty()) {
check = true;
- cmExpandList(parameters[1], checkValues, true);
+ checkValues.assign(parameters[1], cmList::EmptyElements::Yes);
}
CM_FALLTHROUGH;
case cmPolicies::OLD:
- cmExpandList(parameters[1], values);
+ values.assign(parameters[1]);
if (check && values != checkValues) {
std::ostringstream e;
e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0085)
@@ -319,11 +320,11 @@ static const struct InListNode : public cmGeneratorExpressionNode
case cmPolicies::REQUIRED_IF_USED:
case cmPolicies::REQUIRED_ALWAYS:
case cmPolicies::NEW:
- cmExpandList(parameters[1], values, true);
+ values.assign(parameters[1], cmList::EmptyElements::Yes);
break;
}
- return cm::contains(values, parameters.front()) ? "1" : "0";
+ return values.find(parameters.front()) != cmList::npos ? "1" : "0";
}
} inListNode;
@@ -352,24 +353,17 @@ static const struct FilterNode : public cmGeneratorExpressionNode
return {};
}
- const bool exclude = parameters[1] == "EXCLUDE";
-
- cmsys::RegularExpression re;
- if (!re.compile(parameters[2])) {
+ try {
+ return cmList{ parameters.front(), cmList::EmptyElements::Yes }
+ .filter(parameters[2],
+ parameters[1] == "EXCLUDE" ? cmList::FilterMode::EXCLUDE
+ : cmList::FilterMode::INCLUDE)
+ .to_string();
+ } catch (std::invalid_argument&) {
reportError(context, content->GetOriginalExpression(),
"$<FILTER:...> failed to compile regex");
return {};
}
-
- std::vector<std::string> values;
- std::vector<std::string> result;
- cmExpandList(parameters.front(), values, true);
-
- std::copy_if(values.cbegin(), values.cend(), std::back_inserter(result),
- [&re, exclude](std::string const& input) {
- return exclude ^ re.find(input);
- });
- return cmJoin(cmMakeRange(result.cbegin(), result.cend()), ";");
}
} filterNode;
@@ -391,11 +385,7 @@ static const struct RemoveDuplicatesNode : public cmGeneratorExpressionNode
"$<REMOVE_DUPLICATES:...> expression requires one parameter");
}
- std::vector<std::string> values = cmExpandedList(parameters.front(), true);
-
- auto valuesEnd = cmRemoveDuplicates(values);
- auto valuesBegin = values.cbegin();
- return cmJoin(cmMakeRange(valuesBegin, valuesEnd), ";");
+ return cmList{ parameters.front() }.remove_duplicates().to_string();
}
} removeDuplicatesNode;
@@ -1500,8 +1490,7 @@ static const struct JoinNode : public cmGeneratorExpressionNode
const GeneratorExpressionContent* /*content*/,
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
{
- std::vector<std::string> list = cmExpandedList(parameters.front());
- return cmJoin(list, parameters[1]);
+ return cmList{ parameters.front() }.join(parameters[1]);
}
} joinNode;
diff --git a/Source/cmList.cxx b/Source/cmList.cxx
new file mode 100644
index 0000000000..39d138a61a
--- /dev/null
+++ b/Source/cmList.cxx
@@ -0,0 +1,996 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include "cmList.h"
+
+#include <algorithm>
+#include <cstddef>
+#include <functional>
+#include <iterator>
+#include <set>
+#include <stdexcept>
+#include <utility>
+
+#include <cm/memory>
+
+#include "cmsys/RegularExpression.hxx"
+
+#include "cmAlgorithms.h"
+#include "cmGeneratorExpression.h"
+#include "cmRange.h"
+#include "cmStringAlgorithms.h"
+#include "cmStringReplaceHelper.h"
+#include "cmSystemTools.h"
+
+cm::string_view cmList::element_separator{ ";" };
+
+cmList cmList::sublist(size_type pos, size_type length) const
+{
+ if (pos >= this->Values.size()) {
+ throw std::out_of_range(cmStrCat(
+ "begin index: ", pos, " is out of range 0 - ", this->Values.size() - 1));
+ }
+
+ size_type count = (length == npos || pos + length > this->size())
+ ? this->size()
+ : pos + length;
+ return this->sublist(this->begin() + pos, this->begin() + count);
+}
+
+cmList::size_type cmList::find(cm::string_view value) const
+{
+ auto res = std::find(this->Values.begin(), this->Values.end(), value);
+ if (res == this->Values.end()) {
+ return npos;
+ }
+
+ return std::distance(this->Values.begin(), res);
+}
+
+cmList& cmList::remove_duplicates()
+{
+ auto newEnd = cmRemoveDuplicates(this->Values);
+ this->Values.erase(newEnd, this->Values.end());
+
+ return *this;
+}
+
+namespace {
+class MatchesRegex
+{
+public:
+ MatchesRegex(cmsys::RegularExpression& regex, cmList::FilterMode mode)
+ : Regex(regex)
+ , IncludeMatches(mode == cmList::FilterMode::INCLUDE)
+ {
+ }
+
+ bool operator()(const std::string& target)
+ {
+ return this->Regex.find(target) ^ this->IncludeMatches;
+ }
+
+private:
+ cmsys::RegularExpression& Regex;
+ const bool IncludeMatches;
+};
+}
+
+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));
+ }
+
+ auto it = std::remove_if(this->Values.begin(), this->Values.end(),
+ MatchesRegex{ regex, mode });
+ this->Values.erase(it, this->Values.end());
+
+ return *this;
+}
+
+namespace {
+class StringSorter
+{
+protected:
+ using StringFilter = std::function<std::string(const std::string&)>;
+
+ using OrderMode = cmList::SortConfiguration::OrderMode;
+ using CompareMethod = cmList::SortConfiguration::CompareMethod;
+ using CaseSensitivity = cmList::SortConfiguration::CaseSensitivity;
+
+ StringFilter GetCompareFilter(CompareMethod compare)
+ {
+ return (compare == CompareMethod::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(CompareMethod compare)
+ {
+ if (compare == CompareMethod::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:
+ StringSorter(cmList::SortConfiguration const& config)
+ : Filters{ this->GetCompareFilter(config.Compare),
+ this->GetCaseFilter(config.Case) }
+ , SortMethod(this->GetComparisonFunction(config.Compare))
+ , Descending(config.Order == OrderMode::DESCENDING)
+ {
+ }
+
+ std::string ApplyFilter(const std::string& argument)
+ {
+ std::string result = argument;
+ for (auto const& 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;
+ }
+
+private:
+ StringFilter Filters[2] = { nullptr, nullptr };
+ ComparisonFunction SortMethod;
+ bool Descending;
+};
+}
+
+cmList::SortConfiguration::SortConfiguration() = default;
+
+cmList& cmList::sort(const SortConfiguration& cfg)
+{
+ SortConfiguration config{ cfg };
+
+ if (config.Order == SortConfiguration::OrderMode::DEFAULT) {
+ config.Order = SortConfiguration::OrderMode::ASCENDING;
+ }
+ if (config.Compare == SortConfiguration::CompareMethod::DEFAULT) {
+ config.Compare = SortConfiguration::CompareMethod::STRING;
+ }
+ if (config.Case == SortConfiguration::CaseSensitivity::DEFAULT) {
+ config.Case = SortConfiguration::CaseSensitivity::SENSITIVE;
+ }
+
+ if ((config.Compare == SortConfiguration::CompareMethod::STRING) &&
+ (config.Case == SortConfiguration::CaseSensitivity::SENSITIVE) &&
+ (config.Order == SortConfiguration::OrderMode::ASCENDING)) {
+ std::sort(this->Values.begin(), this->Values.end());
+ } else {
+ StringSorter sorter(config);
+ std::sort(this->Values.begin(), this->Values.end(), sorter);
+ }
+
+ return *this;
+}
+
+namespace {
+using transform_type = std::function<std::string(const std::string&)>;
+using transform_error = cmList::transform_error;
+
+class TransformSelector : public cmList::TransformSelector
+{
+public:
+ ~TransformSelector() override = default;
+
+ std::string Tag;
+
+ const std::string& GetTag() override { return this->Tag; }
+
+ virtual bool Validate(std::size_t count = 0) = 0;
+
+ virtual bool InSelection(const std::string&) = 0;
+
+ virtual void Transform(cmList::container_type& 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)
+ {
+ }
+ TransformSelectorRegex(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<index_type> 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> const& indexes)
+ : TransformSelector(std::move(tag))
+ , Indexes(indexes)
+ {
+ }
+ TransformSelectorIndexes(std::string&& tag, std::vector<int>&& indexes)
+ : TransformSelector(std::move(tag))
+ , Indexes(indexes)
+ {
+ }
+
+ int NormalizeIndex(index_type index, std::size_t count)
+ {
+ if (index < 0) {
+ index = static_cast<index_type>(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<index_type> const& indexes)
+ : TransformSelectorIndexes("AT", indexes)
+ {
+ }
+ TransformSelectorAt(std::vector<index_type>&& 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:
+ index_type Start, Stop, Step;
+};
+
+class TransformAction
+{
+public:
+ virtual ~TransformAction() = default;
+
+ void Initialize(TransformSelector* selector) { this->Selector = selector; }
+ virtual void Initialize(TransformSelector*, const std::string&) {}
+ virtual void Initialize(TransformSelector*, const std::string&,
+ const std::string&)
+ {
+ }
+ virtual void Initialize(TransformSelector* selector,
+ const std::vector<std::string>&)
+ {
+ this->Initialize(selector);
+ }
+
+ virtual std::string operator()(const std::string& s) = 0;
+
+protected:
+ TransformSelector* Selector;
+};
+class TransformActionAppend : public TransformAction
+{
+public:
+ using TransformAction::Initialize;
+
+ void Initialize(TransformSelector* selector,
+ const std::string& append) override
+ {
+ TransformAction::Initialize(selector);
+ this->Append = append;
+ }
+ void Initialize(TransformSelector* selector,
+ const std::vector<std::string>& append) override
+ {
+ this->Initialize(selector, append.front());
+ }
+
+ std::string operator()(const std::string& s) override
+ {
+ if (this->Selector->InSelection(s)) {
+ return cmStrCat(s, this->Append);
+ }
+
+ return s;
+ }
+
+private:
+ std::string Append;
+};
+class TransformActionPrepend : public TransformAction
+{
+public:
+ using TransformAction::Initialize;
+
+ void Initialize(TransformSelector* selector,
+ const std::string& prepend) override
+ {
+ TransformAction::Initialize(selector);
+ this->Prepend = prepend;
+ }
+ void Initialize(TransformSelector* selector,
+ const std::vector<std::string>& prepend) override
+ {
+ this->Initialize(selector, prepend.front());
+ }
+
+ std::string operator()(const std::string& s) override
+ {
+ if (this->Selector->InSelection(s)) {
+ return cmStrCat(this->Prepend, s);
+ }
+
+ return s;
+ }
+
+private:
+ std::string Prepend;
+};
+class TransformActionToUpper : public TransformAction
+{
+public:
+ std::string operator()(const std::string& s) override
+ {
+ if (this->Selector->InSelection(s)) {
+ return cmSystemTools::UpperCase(s);
+ }
+
+ return s;
+ }
+};
+class TransformActionToLower : public TransformAction
+{
+public:
+ std::string operator()(const std::string& s) override
+ {
+ if (this->Selector->InSelection(s)) {
+ return cmSystemTools::LowerCase(s);
+ }
+
+ return s;
+ }
+};
+class TransformActionStrip : public TransformAction
+{
+public:
+ std::string operator()(const std::string& s) override
+ {
+ if (this->Selector->InSelection(s)) {
+ return cmTrimWhitespace(s);
+ }
+
+ return s;
+ }
+};
+class TransformActionGenexStrip : public TransformAction
+{
+public:
+ std::string operator()(const std::string& s) override
+ {
+ if (this->Selector->InSelection(s)) {
+ return cmGeneratorExpression::Preprocess(
+ s, cmGeneratorExpression::StripAllGeneratorExpressions);
+ }
+
+ return s;
+ }
+};
+class TransformActionReplace : public TransformAction
+{
+public:
+ using TransformAction::Initialize;
+
+ void Initialize(TransformSelector* selector, const std::string& regex,
+ const std::string& replace) override
+ {
+ TransformAction::Initialize(selector);
+ this->ReplaceHelper =
+ cm::make_unique<cmStringReplaceHelper>(regex, replace);
+
+ if (!this->ReplaceHelper->IsRegularExpressionValid()) {
+ throw transform_error(
+ cmStrCat("sub-command TRANSFORM, action REPLACE: Failed to compile "
+ "regex \"",
+ regex, "\"."));
+ }
+ if (!this->ReplaceHelper->IsReplaceExpressionValid()) {
+ throw transform_error(cmStrCat("sub-command TRANSFORM, action REPLACE: ",
+ this->ReplaceHelper->GetError(), "."));
+ }
+ }
+ void Initialize(TransformSelector* selector,
+ const std::vector<std::string>& args) override
+ {
+ this->Initialize(selector, args[0], args[1]);
+ }
+
+ std::string operator()(const std::string& s) override
+ {
+ if (this->Selector->InSelection(s)) {
+ // Scan through the input for all matches.
+ std::string output;
+
+ if (!this->ReplaceHelper->Replace(s, output)) {
+ throw transform_error(
+ cmStrCat("sub-command TRANSFORM, action REPLACE: ",
+ this->ReplaceHelper->GetError(), "."));
+ }
+
+ return output;
+ }
+
+ return s;
+ }
+
+private:
+ std::unique_ptr<cmStringReplaceHelper> ReplaceHelper;
+};
+
+// Descriptor of action
+// Arity: number of arguments required for the action
+// Transform: Object implementing the action
+struct ActionDescriptor
+{
+ ActionDescriptor(cmList::TransformAction action)
+ : Action(action)
+ {
+ }
+ ActionDescriptor(cmList::TransformAction action, std::string name,
+ std::size_t arity,
+ std::unique_ptr<TransformAction> transform)
+ : Action(action)
+ , Name(std::move(name))
+ , Arity(arity)
+ , Transform(std::move(transform))
+ {
+ }
+
+ operator cmList::TransformAction() const { return this->Action; }
+
+ cmList::TransformAction Action;
+ std::string Name;
+ std::size_t Arity = 0;
+ std::unique_ptr<TransformAction> Transform;
+};
+
+// Build a set of supported actions.
+using ActionDescriptorSet = std::set<
+ ActionDescriptor,
+ std::function<bool(cmList::TransformAction, cmList::TransformAction)>>;
+
+ActionDescriptorSet Descriptors([](cmList::TransformAction x,
+ cmList::TransformAction y) {
+ return x < y;
+});
+
+ActionDescriptorSet::iterator TransformConfigure(
+ cmList::TransformAction action,
+ std::unique_ptr<cmList::TransformSelector>& selector, std::size_t arity)
+{
+ if (Descriptors.empty()) {
+ Descriptors.emplace(cmList::TransformAction::APPEND, "APPEND", 1,
+ cm::make_unique<TransformActionAppend>());
+ Descriptors.emplace(cmList::TransformAction::PREPEND, "PREPEND", 1,
+ cm::make_unique<TransformActionPrepend>());
+ Descriptors.emplace(cmList::TransformAction::TOUPPER, "TOUPPER", 0,
+ cm::make_unique<TransformActionToUpper>());
+ Descriptors.emplace(cmList::TransformAction::TOLOWER, "TOLOWER", 0,
+ cm::make_unique<TransformActionToLower>());
+ Descriptors.emplace(cmList::TransformAction::STRIP, "STRIP", 0,
+ cm::make_unique<TransformActionStrip>());
+ Descriptors.emplace(cmList::TransformAction::GENEX_STRIP, "GENEX_STRIP", 0,
+ cm::make_unique<TransformActionGenexStrip>());
+ Descriptors.emplace(cmList::TransformAction::REPLACE, "REPLACE", 2,
+ cm::make_unique<TransformActionReplace>());
+ }
+
+ auto descriptor = Descriptors.find(action);
+ if (descriptor == Descriptors.end()) {
+ throw transform_error(cmStrCat(" sub-command TRANSFORM, ",
+ std::to_string(static_cast<int>(action)),
+ " invalid action."));
+ }
+
+ if (descriptor->Arity != arity) {
+ throw transform_error(cmStrCat("sub-command TRANSFORM, action ",
+ descriptor->Name, " expects ",
+ descriptor->Arity, " argument(s)."));
+ }
+ if (!selector) {
+ selector = cm::make_unique<TransformNoSelector>();
+ }
+
+ return descriptor;
+}
+}
+
+std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewAT(
+ std::initializer_list<index_type> indexes)
+{
+ return cm::make_unique<TransformSelectorAt>(
+ std::vector<index_type>{ indexes.begin(), indexes.end() });
+ ;
+}
+std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewAT(
+ std::vector<index_type> const& indexes)
+{
+ return cm::make_unique<TransformSelectorAt>(indexes);
+}
+std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewAT(
+ std::vector<index_type>&& indexes)
+{
+ return cm::make_unique<TransformSelectorAt>(std::move(indexes));
+}
+
+std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewFOR(
+ std::initializer_list<index_type> indexes)
+{
+ if (indexes.size() < 2 || indexes.size() > 3) {
+ throw transform_error("sub-command TRANSFORM, selector FOR "
+ "expects 2 or 3 arguments");
+ }
+ if (indexes.size() == 3 && *(indexes.begin() + 2) < 0) {
+ throw transform_error("sub-command TRANSFORM, selector FOR expects "
+ "positive numeric value for <step>.");
+ }
+
+ return cm::make_unique<TransformSelectorFor>(
+ *indexes.begin(), *(indexes.begin() + 1),
+ indexes.size() == 3 ? *(indexes.begin() + 2) : 1);
+}
+std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewFOR(
+ std::vector<index_type> const& indexes)
+{
+ if (indexes.size() < 2 || indexes.size() > 3) {
+ throw transform_error("sub-command TRANSFORM, selector FOR "
+ "expects 2 or 3 arguments");
+ }
+ if (indexes.size() == 3 && indexes[2] < 0) {
+ throw transform_error("sub-command TRANSFORM, selector FOR expects "
+ "positive numeric value for <step>.");
+ }
+
+ return cm::make_unique<TransformSelectorFor>(
+ indexes[0], indexes[1], indexes.size() == 3 ? indexes[2] : 1);
+}
+std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewFOR(
+ std::vector<index_type>&& indexes)
+{
+ if (indexes.size() < 2 || indexes.size() > 3) {
+ throw transform_error("sub-command TRANSFORM, selector FOR "
+ "expects 2 or 3 arguments");
+ }
+ if (indexes.size() == 3 && indexes[2] < 0) {
+ throw transform_error("sub-command TRANSFORM, selector FOR expects "
+ "positive numeric value for <step>.");
+ }
+
+ return cm::make_unique<TransformSelectorFor>(
+ indexes[0], indexes[1], indexes.size() == 3 ? indexes[2] : 1);
+}
+
+std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewREGEX(
+ std::string const& regex)
+{
+ std::unique_ptr<::TransformSelector> selector =
+ cm::make_unique<TransformSelectorRegex>(regex);
+ if (!selector->Validate()) {
+ throw transform_error(
+ cmStrCat("sub-command TRANSFORM, selector REGEX failed to compile "
+ "regex \"",
+ regex, "\"."));
+ }
+ // weird construct to please all compilers
+ return std::unique_ptr<cmList::TransformSelector>(selector.release());
+}
+std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewREGEX(
+ std::string&& regex)
+{
+ std::unique_ptr<::TransformSelector> selector =
+ cm::make_unique<TransformSelectorRegex>(std::move(regex));
+ if (!selector->Validate()) {
+ throw transform_error(
+ cmStrCat("sub-command TRANSFORM, selector REGEX failed to compile "
+ "regex \"",
+ regex, "\"."));
+ }
+ // weird construct to please all compilers
+ return std::unique_ptr<cmList::TransformSelector>(selector.release());
+}
+
+cmList& cmList::transform(TransformAction action,
+ std::unique_ptr<TransformSelector> selector)
+{
+ auto descriptor = TransformConfigure(action, selector, 0);
+
+ descriptor->Transform->Initialize(
+ static_cast<::TransformSelector*>(selector.get()));
+
+ static_cast<::TransformSelector&>(*selector).Transform(
+ this->Values, [&descriptor](const std::string& s) -> std::string {
+ return (*descriptor->Transform)(s);
+ });
+
+ return *this;
+}
+
+cmList& cmList::transform(TransformAction action, std::string const& arg,
+ std::unique_ptr<TransformSelector> selector)
+{
+ auto descriptor = TransformConfigure(action, selector, 1);
+
+ descriptor->Transform->Initialize(
+ static_cast<::TransformSelector*>(selector.get()), arg);
+
+ static_cast<::TransformSelector&>(*selector).Transform(
+ this->Values, [&descriptor](const std::string& s) -> std::string {
+ return (*descriptor->Transform)(s);
+ });
+
+ return *this;
+}
+
+cmList& cmList::transform(TransformAction action, std::string const& arg1,
+ std::string const& arg2,
+ std::unique_ptr<TransformSelector> selector)
+{
+ auto descriptor = TransformConfigure(action, selector, 2);
+
+ descriptor->Transform->Initialize(
+ static_cast<::TransformSelector*>(selector.get()), arg1, arg2);
+
+ static_cast<::TransformSelector&>(*selector).Transform(
+ this->Values, [&descriptor](const std::string& s) -> std::string {
+ return (*descriptor->Transform)(s);
+ });
+
+ return *this;
+}
+
+cmList& cmList::transform(TransformAction action,
+ std::vector<std::string> const& args,
+ std::unique_ptr<TransformSelector> selector)
+{
+ auto descriptor = TransformConfigure(action, selector, args.size());
+
+ descriptor->Transform->Initialize(
+ static_cast<::TransformSelector*>(selector.get()), args);
+
+ static_cast<::TransformSelector&>(*selector).Transform(
+ this->Values, [&descriptor](const std::string& s) -> std::string {
+ return (*descriptor->Transform)(s);
+ });
+
+ return *this;
+}
+
+std::string cmList::join(cm::string_view glue) const
+{
+ return cmJoin(this->Values, glue);
+}
+
+std::string& cmList::append(cm::string_view value, std::string& list)
+{
+ if (list.empty()) {
+ list = std::string(value);
+ } else {
+ list += cmStrCat(cmList::element_separator, value);
+ }
+
+ return list;
+}
+
+std::string& cmList::prepend(cm::string_view value, std::string& list)
+{
+ if (list.empty()) {
+ list = std::string(value);
+ } else {
+ list.insert(0, cmStrCat(value, cmList::element_separator));
+ }
+
+ return list;
+}
+
+cmList::size_type cmList::ComputeIndex(index_type pos, bool boundCheck) const
+{
+ if (boundCheck) {
+ if (this->Values.empty()) {
+ throw std::out_of_range(
+ cmStrCat("index: ", pos, " out of range (0, 0)"));
+ }
+
+ if (!this->Values.empty()) {
+ auto length = this->Values.size();
+ if (pos < 0) {
+ pos = static_cast<index_type>(length) + pos;
+ }
+ if (pos < 0 || length <= static_cast<size_type>(pos)) {
+ throw std::out_of_range(cmStrCat("index: ", pos, " out of range (-",
+ this->Values.size(), ", ",
+ this->Values.size() - 1, ")"));
+ }
+ }
+ return pos;
+ }
+
+ return pos < 0 ? this->Values.size() + pos : pos;
+}
+cmList::size_type cmList::ComputeInsertIndex(index_type pos,
+ bool boundCheck) const
+{
+ if (boundCheck) {
+ if (this->Values.empty() && pos != 0) {
+ throw std::out_of_range(
+ cmStrCat("index: ", pos, " out of range (0, 0)"));
+ }
+
+ if (!this->Values.empty()) {
+ auto length = this->Values.size();
+ if (pos < 0) {
+ pos = static_cast<index_type>(length) + pos;
+ }
+ if (pos < 0 || length < static_cast<size_type>(pos)) {
+ throw std::out_of_range(cmStrCat("index: ", pos, " out of range (-",
+ this->Values.size(), ", ",
+ this->Values.size(), ")"));
+ }
+ }
+ return pos;
+ }
+
+ return pos < 0 ? this->Values.size() + pos : pos;
+}
+
+cmList cmList::GetItems(std::vector<index_type>&& indexes) const
+{
+ cmList listItems;
+
+ for (auto index : indexes) {
+ listItems.emplace_back(this->at(index));
+ }
+
+ return listItems;
+}
+
+cmList& cmList::RemoveItems(std::vector<index_type>&& indexes)
+{
+ if (indexes.empty()) {
+ return *this;
+ }
+
+ // compute all indexes
+ std::vector<size_type> idx(indexes.size());
+ std::transform(
+ indexes.cbegin(), indexes.cend(), idx.begin(),
+ [this](const index_type& index) { return this->ComputeIndex(index); });
+
+ std::sort(idx.begin(), idx.end(),
+ [](size_type l, size_type r) { return l > r; });
+ auto newEnd = std::unique(idx.begin(), idx.end());
+ idx.erase(newEnd, idx.end());
+
+ for (auto index : idx) {
+ this->erase(this->begin() + index);
+ }
+
+ return *this;
+}
+
+cmList& cmList::RemoveItems(std::vector<std::string>&& items)
+{
+ std::sort(items.begin(), items.end());
+ auto last = std::unique(items.begin(), items.end());
+ auto first = items.begin();
+
+ auto newEnd = cmRemoveMatching(this->Values, cmMakeRange(first, last));
+ this->Values.erase(newEnd, this->Values.end());
+
+ return *this;
+}
+
+cmList::container_type::iterator cmList::Insert(
+ container_type::const_iterator pos, std::string&& value,
+ container_type& container, ExpandElements expandElements,
+ EmptyElements emptyElements)
+{
+ auto delta = std::distance(container.cbegin(), pos);
+ auto insertPos = container.begin() + delta;
+
+ if (expandElements == ExpandElements::Yes) {
+ // If argument is empty, it is an empty list.
+ if (emptyElements == EmptyElements::No && value.empty()) {
+ return insertPos;
+ }
+
+ // if there are no ; in the name then just copy the current string
+ if (value.find(';') == std::string::npos) {
+ return container.insert(insertPos, std::move(value));
+ }
+
+ std::string newValue;
+ // Break the string at non-escaped semicolons not nested in [].
+ int squareNesting = 0;
+ auto last = value.begin();
+ auto const cend = value.end();
+ for (auto c = last; c != cend; ++c) {
+ switch (*c) {
+ case '\\': {
+ // We only want to allow escaping of semicolons. Other
+ // escapes should not be processed here.
+ auto cnext = c + 1;
+ if ((cnext != cend) && *cnext == ';') {
+ newValue.append(last, c);
+ // Skip over the escape character
+ last = cnext;
+ c = cnext;
+ }
+ } break;
+ case '[': {
+ ++squareNesting;
+ } break;
+ case ']': {
+ --squareNesting;
+ } break;
+ case ';': {
+ // brackets.
+ if (squareNesting == 0) {
+ newValue.append(last, c);
+ // Skip over the semicolon
+ last = c + 1;
+ if (!newValue.empty() || emptyElements == EmptyElements::Yes) {
+ // Add the last argument.
+ insertPos = container.insert(insertPos, newValue);
+ insertPos++;
+ newValue.clear();
+ }
+ }
+ } break;
+ default: {
+ // Just append this character.
+ } break;
+ }
+ }
+ newValue.append(last, cend);
+ if (!newValue.empty() || emptyElements == EmptyElements::Yes) {
+ // Add the last argument.
+ container.insert(insertPos, std::move(newValue));
+ }
+ } else if (!value.empty() || emptyElements == EmptyElements::Yes) {
+ return container.insert(insertPos, std::move(value));
+ }
+ return container.begin() + delta;
+}
diff --git a/Source/cmList.h b/Source/cmList.h
new file mode 100644
index 0000000000..d994ad3890
--- /dev/null
+++ b/Source/cmList.h
@@ -0,0 +1,1198 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#pragma once
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <algorithm>
+#include <initializer_list>
+#include <iterator>
+#include <memory>
+#include <numeric>
+#include <stdexcept>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <cm/string_view>
+
+#include "cmValue.h"
+
+/**
+ * CMake lists management
+ *
+ * For all operations, input arguments (single value like cm::string_view or
+ * multiple values specified through pair of iterators) are, by default,
+ * expanded. The expansion can be controlled by the cmList::ExpandElements
+ * option.
+ *
+ * There is an exception to this rule. The following methods do not expand
+ * their argument: cmList::push_back, cmList::emplace and cmList::emplace_back.
+ */
+
+class cmList
+{
+public:
+ using container_type = std::vector<std::string>;
+
+ using value_type = container_type::value_type;
+ using allocator_type = container_type::allocator_type;
+ using index_type = int;
+ using size_type = container_type::size_type;
+ using difference_type = container_type::difference_type;
+ using reference = container_type::reference;
+ using const_reference = container_type::const_reference;
+ using iterator = container_type::iterator;
+ using const_iterator = container_type::const_iterator;
+ using reverse_iterator = container_type::reverse_iterator;
+ using const_reverse_iterator = container_type::const_reverse_iterator;
+
+ static const size_type npos = static_cast<size_type>(-1);
+
+ static cm::string_view element_separator;
+
+ enum class EmptyElements
+ {
+ No,
+ Yes,
+ };
+ enum class ExpandElements
+ {
+ No,
+ Yes,
+ };
+
+ cmList() = default;
+ cmList(const cmList&) = default;
+ cmList(cmList&&) = default;
+
+ cmList(cm::string_view value,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ this->assign(value, expandElements, emptyElements);
+ }
+ cmList(cm::string_view value, EmptyElements emptyElements)
+ {
+ this->assign(value, ExpandElements::Yes, emptyElements);
+ }
+ cmList(cmValue list, ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ if (list) {
+ this->assign(*list, expandElements, emptyElements);
+ }
+ }
+ cmList(cmValue list, EmptyElements emptyElements)
+ : cmList(list, ExpandElements::Yes, emptyElements)
+ {
+ }
+ template <typename InputIterator>
+ cmList(InputIterator first, InputIterator last,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ this->assign(first, last, expandElements, emptyElements);
+ }
+ template <typename InputIterator>
+ cmList(InputIterator first, InputIterator last, EmptyElements emptyElements)
+ : cmList(first, last, ExpandElements::Yes, emptyElements)
+ {
+ }
+ cmList(const container_type& init,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ : cmList(init.begin(), init.end(), expandElements, emptyElements)
+ {
+ }
+ cmList(const container_type& init, EmptyElements emptyElements)
+ : cmList(init, ExpandElements::Yes, emptyElements)
+ {
+ }
+ cmList(container_type&& init,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ : cmList(std::make_move_iterator(init.begin()),
+ std::make_move_iterator(init.end()), expandElements,
+ emptyElements)
+ {
+ init.clear();
+ }
+ cmList(container_type&& init, EmptyElements emptyElements)
+ : cmList(std::move(init), ExpandElements::Yes, emptyElements)
+ {
+ }
+ cmList(std::initializer_list<std::string> init) { this->assign(init); }
+
+ ~cmList() = default;
+
+ cmList& operator=(const cmList&) = default;
+ cmList& operator=(cmList&&) = default;
+
+ cmList& operator=(cm::string_view value)
+ {
+ this->assign(value);
+ return *this;
+ }
+ cmList& operator=(cmValue value)
+ {
+ if (value) {
+ this->operator=(*value);
+ } else {
+ this->clear();
+ }
+
+ return *this;
+ }
+
+ cmList& operator=(const container_type& init)
+ {
+ this->assign(init);
+ return *this;
+ }
+ cmList& operator=(container_type&& init)
+ {
+ this->assign(std::move(init));
+
+ return *this;
+ }
+
+ cmList& operator=(std::initializer_list<std::string> init)
+ {
+ this->assign(init);
+ return *this;
+ }
+
+ void assign(cm::string_view value,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ this->clear();
+ this->append(value, expandElements, emptyElements);
+ }
+ void assign(cm::string_view value, EmptyElements emptyElements)
+ {
+ this->assign(value, ExpandElements::Yes, emptyElements);
+ }
+ void assign(cmValue value,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ if (value) {
+ this->assign(*value, expandElements, emptyElements);
+ } else {
+ this->clear();
+ }
+ }
+ void assign(cmValue value, EmptyElements emptyElements)
+ {
+ this->assign(value, ExpandElements::Yes, emptyElements);
+ }
+ template <typename InputIterator>
+ void assign(InputIterator first, InputIterator last,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ this->clear();
+ this->append(first, last, expandElements, emptyElements);
+ }
+ template <typename InputIterator>
+ void assign(InputIterator first, InputIterator last,
+ EmptyElements emptyElements)
+ {
+ this->assign(first, last, ExpandElements::Yes, emptyElements);
+ }
+ void assign(const cmList& init,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ this->assign(init.begin(), init.end(), expandElements, emptyElements);
+ }
+ void assign(const cmList& init, EmptyElements emptyElements)
+ {
+ this->assign(init, ExpandElements::Yes, emptyElements);
+ }
+ void assign(cmList&& init,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ this->assign(std::make_move_iterator(init.begin()),
+ std::make_move_iterator(init.end()), expandElements,
+ emptyElements);
+ init.clear();
+ }
+ void assign(cmList&& init, EmptyElements emptyElements)
+ {
+ this->assign(std::move(init), ExpandElements::Yes, emptyElements);
+ }
+ void assign(const container_type& init,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ this->assign(init.begin(), init.end(), expandElements, emptyElements);
+ }
+ void assign(const container_type& init, EmptyElements emptyElements)
+ {
+ this->assign(init, ExpandElements::Yes, emptyElements);
+ }
+ void assign(container_type&& init,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ this->assign(std::make_move_iterator(init.begin()),
+ std::make_move_iterator(init.end()), expandElements,
+ emptyElements);
+ init.clear();
+ }
+ void assign(container_type&& init, EmptyElements emptyElements)
+ {
+ this->assign(std::move(init), ExpandElements::Yes, emptyElements);
+ }
+ void assign(std::initializer_list<std::string> init)
+ {
+ this->assign(init.begin(), init.end());
+ }
+
+ // Conversions
+ std::string to_string() const
+ {
+ return this->join(cmList::element_separator);
+ }
+
+ operator container_type&() & noexcept { return this->Values; }
+ operator const container_type&() const& noexcept { return this->Values; }
+ operator container_type&&() && noexcept { return std::move(this->Values); }
+
+ // Element access
+ reference at(index_type pos)
+ {
+ return this->Values.at(this->ComputeIndex(pos));
+ }
+ const_reference at(index_type pos) const
+ {
+ return this->Values.at(this->ComputeIndex(pos));
+ }
+
+ reference operator[](index_type pos)
+ {
+ return this->Values[this->ComputeIndex(pos, false)];
+ }
+ const_reference operator[](index_type pos) const
+ {
+ return this->Values[this->ComputeIndex(pos, false)];
+ }
+
+ reference front() { return this->Values.front(); }
+ const_reference front() const { return this->Values.front(); }
+
+ reference back() { return this->Values.back(); }
+ const_reference back() const { return this->Values.back(); }
+
+ // extract sublist in range [first, last)
+ cmList sublist(const_iterator first, const_iterator last) const
+ {
+ return cmList{ first, last };
+ }
+ // Extract sublist in range [first, last)
+ // Throw std::out_of_range if pos is invalid
+ cmList sublist(size_type pos = 0, size_type length = npos) const;
+
+ // Returns the list of elements
+ // Throw std::out_of_range if any index is invalid
+ cmList get_items(std::initializer_list<index_type> indexes) const
+ {
+ return this->GetItems(
+ std::vector<index_type>{ indexes.begin(), indexes.end() });
+ }
+ template <typename InputIterator>
+ cmList get_items(InputIterator first, InputIterator last) const
+ {
+ return this->GetItems(std::vector<index_type>{ first, last });
+ }
+
+ size_type find(cm::string_view value) const;
+ size_type find(cmValue value) const
+ {
+ if (value) {
+ return this->find(*value);
+ }
+
+ return npos;
+ }
+
+ container_type& data() noexcept { return this->Values; }
+ const container_type& data() const noexcept { return this->Values; }
+
+ // Iterators
+ iterator begin() noexcept { return this->Values.begin(); }
+ const_iterator begin() const noexcept { return this->Values.begin(); }
+ const_iterator cbegin() const noexcept { return this->Values.cbegin(); }
+
+ iterator end() noexcept { return this->Values.end(); }
+ const_iterator end() const noexcept { return this->Values.end(); }
+ const_iterator cend() const noexcept { return this->Values.cend(); }
+
+ reverse_iterator rbegin() noexcept { return this->Values.rbegin(); }
+ const_reverse_iterator rbegin() const noexcept
+ {
+ return this->Values.rbegin();
+ }
+ const_reverse_iterator crbegin() const noexcept
+ {
+ return this->Values.crbegin();
+ }
+
+ reverse_iterator rend() noexcept { return this->Values.rend(); }
+ const_reverse_iterator rend() const noexcept { return this->Values.rend(); }
+ const_reverse_iterator crend() const noexcept
+ {
+ return this->Values.crend();
+ }
+
+ // Capacity
+ bool empty() const noexcept { return this->Values.empty(); }
+ size_type size() const noexcept { return this->Values.size(); }
+
+ // Modifiers
+ void clear() noexcept { this->Values.clear(); }
+
+ iterator insert(const_iterator pos, cm::string_view value,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return cmList::Insert(pos, std::string(value), this->Values,
+ expandElements, emptyElements);
+ }
+ iterator insert(const_iterator pos, cm::string_view value,
+ EmptyElements emptyElements)
+ {
+ return this->insert(pos, value, ExpandElements::Yes, emptyElements);
+ }
+ iterator insert(const_iterator pos, cmValue value,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ if (value) {
+ return this->insert(pos, *value, expandElements, emptyElements);
+ }
+
+ auto delta = std::distance(this->cbegin(), pos);
+ return this->begin() + delta;
+ }
+ iterator insert(const_iterator pos, cmValue value,
+ EmptyElements emptyElements)
+ {
+ return this->insert(pos, value, ExpandElements::Yes, emptyElements);
+ }
+ template <typename InputIterator>
+ iterator insert(const_iterator pos, InputIterator first, InputIterator last,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return cmList::Insert(pos, first, last, this->Values, expandElements,
+ emptyElements);
+ }
+ template <typename InputIterator>
+ iterator insert(const_iterator pos, InputIterator first, InputIterator last,
+ EmptyElements emptyElements)
+ {
+ return this->insert(pos, first, last, ExpandElements::Yes, emptyElements);
+ }
+ iterator insert(const_iterator pos, const cmList& values,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return this->insert(pos, values.begin(), values.end(), expandElements,
+ emptyElements);
+ }
+ iterator insert(const_iterator pos, const cmList& values,
+ EmptyElements emptyElements)
+ {
+ return this->insert(pos, values, ExpandElements::Yes, emptyElements);
+ }
+ iterator insert(const_iterator pos, cmList&& values,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ auto result = this->insert(pos, std::make_move_iterator(values.begin()),
+ std::make_move_iterator(values.end()),
+ expandElements, emptyElements);
+ values.clear();
+
+ return result;
+ }
+ iterator insert(const_iterator pos, cmList&& values,
+ EmptyElements emptyElements)
+ {
+ return this->insert(pos, std::move(values), ExpandElements::Yes,
+ emptyElements);
+ }
+ iterator insert(const_iterator pos, const container_type& values,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return this->insert(pos, values.begin(), values.end(), expandElements,
+ emptyElements);
+ }
+ iterator insert(const_iterator pos, const container_type& values,
+ EmptyElements emptyElements)
+ {
+ return this->insert(pos, values, ExpandElements::Yes, emptyElements);
+ }
+ iterator insert(const_iterator pos, container_type&& values,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ auto result = this->insert(pos, std::make_move_iterator(values.begin()),
+ std::make_move_iterator(values.end()),
+ expandElements, emptyElements);
+ values.clear();
+
+ return result;
+ }
+ iterator insert(const_iterator pos, container_type&& values,
+ EmptyElements emptyElements)
+ {
+ return this->insert(pos, std::move(values), ExpandElements::Yes,
+ emptyElements);
+ }
+ iterator insert(const_iterator pos, std::initializer_list<std::string> ilist)
+ {
+ return this->insert(pos, ilist.begin(), ilist.end());
+ }
+
+ iterator append(cm::string_view value,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return this->insert(this->cend(), value, expandElements, emptyElements);
+ }
+ iterator append(cm::string_view value, EmptyElements emptyElements)
+ {
+ return this->append(value, ExpandElements::Yes, emptyElements);
+ }
+ iterator append(cmValue value,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ if (value) {
+ return this->append(*value, expandElements, emptyElements);
+ }
+
+ return this->end();
+ }
+ iterator append(cmValue value, EmptyElements emptyElements)
+ {
+ return this->append(value, ExpandElements::Yes, emptyElements);
+ }
+ template <typename InputIterator>
+ iterator append(InputIterator first, InputIterator last,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return this->insert(this->cend(), first, last, expandElements,
+ emptyElements);
+ }
+ template <typename InputIterator>
+ iterator append(InputIterator first, InputIterator last,
+ EmptyElements emptyElements)
+ {
+ return this->append(first, last, ExpandElements::Yes, emptyElements);
+ }
+ iterator append(const cmList& values,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return this->append(values.begin(), values.end(), expandElements,
+ emptyElements);
+ }
+ iterator append(const cmList& values, EmptyElements emptyElements)
+ {
+ return this->append(values, ExpandElements::Yes, emptyElements);
+ }
+ iterator append(cmList&& values,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ auto result = this->append(std::make_move_iterator(values.begin()),
+ std::make_move_iterator(values.end()),
+ expandElements, emptyElements);
+ values.clear();
+
+ return result;
+ }
+ iterator append(cmList&& values, EmptyElements emptyElements)
+ {
+ return this->append(std::move(values), ExpandElements::Yes, emptyElements);
+ }
+ iterator append(const container_type& values,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return this->append(values.begin(), values.end(), expandElements,
+ emptyElements);
+ }
+ iterator append(const container_type& values, EmptyElements emptyElements)
+ {
+ return this->append(values, ExpandElements::Yes, emptyElements);
+ }
+ iterator append(container_type&& values,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ auto result = this->append(std::make_move_iterator(values.begin()),
+ std::make_move_iterator(values.end()),
+ expandElements, emptyElements);
+ values.clear();
+
+ return result;
+ }
+ iterator append(container_type&& values, EmptyElements emptyElements)
+ {
+ return this->append(std::move(values), ExpandElements::Yes, emptyElements);
+ }
+ iterator append(std::initializer_list<std::string> ilist)
+ {
+ return this->insert(this->cend(), ilist);
+ }
+
+ iterator prepend(cm::string_view value,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return this->insert(this->cbegin(), value, expandElements, emptyElements);
+ }
+ iterator prepend(cm::string_view value, EmptyElements emptyElements)
+ {
+ return this->prepend(value, ExpandElements::Yes, emptyElements);
+ }
+ iterator prepend(cmValue value,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ if (value) {
+ return this->prepend(*value, expandElements, emptyElements);
+ }
+
+ return this->begin();
+ }
+ iterator prepend(cmValue value, EmptyElements emptyElements)
+ {
+ return this->prepend(value, ExpandElements::Yes, emptyElements);
+ }
+ template <typename InputIterator>
+ iterator prepend(InputIterator first, InputIterator last,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return this->insert(this->cbegin(), first, last, expandElements,
+ emptyElements);
+ }
+ template <typename InputIterator>
+ iterator prepend(InputIterator first, InputIterator last,
+ EmptyElements emptyElements)
+ {
+ return this->prepend(first, last, ExpandElements::Yes, emptyElements);
+ }
+ iterator prepend(const cmList& values,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return this->prepend(values.begin(), values.end(), expandElements,
+ emptyElements);
+ }
+ iterator prepend(const cmList& values, EmptyElements emptyElements)
+ {
+ return this->prepend(values, ExpandElements::Yes, emptyElements);
+ }
+ iterator prepend(cmList&& values,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ auto result = this->prepend(std::make_move_iterator(values.begin()),
+ std::make_move_iterator(values.end()),
+ expandElements, emptyElements);
+ values.clear();
+
+ return result;
+ }
+ iterator prepend(cmList&& values, EmptyElements emptyElements)
+ {
+ return this->prepend(std::move(values), ExpandElements::Yes,
+ emptyElements);
+ }
+ iterator prepend(const container_type& values,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return this->prepend(values.begin(), values.end(), expandElements,
+ emptyElements);
+ }
+ iterator prepend(const container_type& values, EmptyElements emptyElements)
+ {
+ return this->prepend(values, ExpandElements::Yes, emptyElements);
+ }
+ iterator prepend(container_type&& values,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ auto result = this->prepend(std::make_move_iterator(values.begin()),
+ std::make_move_iterator(values.end()),
+ expandElements, emptyElements);
+ values.clear();
+
+ return result;
+ }
+ iterator prepend(container_type&& values, EmptyElements emptyElements)
+ {
+ return this->prepend(std::move(values), ExpandElements::Yes,
+ emptyElements);
+ }
+ iterator prepend(std::initializer_list<std::string> ilist)
+ {
+ return this->insert(this->cbegin(), ilist);
+ }
+
+ void push_back(cm::string_view value)
+ {
+ this->Values.push_back(std::string{ value });
+ }
+ void push_back(std::string&& value)
+ {
+ this->Values.push_back(std::move(value));
+ }
+
+ template <typename... Args>
+ iterator emplace(const_iterator pos, Args&&... args)
+ {
+ return this->Values.emplace(pos, std::forward<Args>(args)...);
+ }
+
+ template <typename... Args>
+ void emplace_back(Args&&... args)
+ {
+ this->Values.emplace_back(std::forward<Args>(args)...);
+ }
+
+ // Inserts elements in the list
+ // Throw std::out_of_range if index is invalid
+ template <typename InputIterator>
+ cmList& insert_items(index_type index, InputIterator first,
+ InputIterator last)
+ {
+ this->insert(this->begin() + this->ComputeInsertIndex(index), first, last);
+ return *this;
+ }
+ cmList& insert_items(index_type index,
+ std::initializer_list<std::string> values)
+ {
+ return this->insert_items(index, values.begin(), values.end());
+ }
+
+ iterator erase(const_iterator pos)
+ {
+ // convert const_iterator in iterator to please non standard c++11
+ // compilers (gcc 4.8 for example)
+ auto pos2 =
+ this->Values.begin() + std::distance(this->Values.cbegin(), pos);
+ return this->Values.erase(pos2);
+ }
+ iterator erase(const_iterator first, const_iterator last)
+ {
+ // convert const_iterator in iterator to please non standard c++11
+ // compilers (gcc 4.8 for example)
+ auto first2 =
+ this->Values.begin() + std::distance(this->Values.cbegin(), first);
+ auto last2 =
+ this->Values.begin() + std::distance(this->Values.cbegin(), last);
+ return this->Values.erase(first2, last2);
+ }
+
+ void pop_back() { this->Values.pop_back(); }
+ void pop_front() { this->Values.erase(this->begin()); }
+
+ // Removes elements from the list
+ // Throw std::out_of_range if any index is invalid
+ cmList& remove_items(std::initializer_list<index_type> indexes)
+ {
+ return this->RemoveItems(
+ std::vector<index_type>{ indexes.begin(), indexes.end() });
+ }
+ cmList& remove_items(std::initializer_list<std::string> values)
+ {
+ return this->RemoveItems(
+ std::vector<std::string>{ values.begin(), values.end() });
+ }
+ template <typename InputIterator>
+ cmList& remove_items(InputIterator first, InputIterator last)
+ {
+ return this->RemoveItems(
+ std::vector<typename InputIterator::value_type>{ first, last });
+ }
+
+ cmList& remove_duplicates();
+
+ enum class FilterMode
+ {
+ INCLUDE,
+ EXCLUDE
+ };
+ // Includes or removes items from the list
+ // Throw std::invalid_argument if regular expression is invalid
+ cmList& filter(cm::string_view regex, FilterMode mode);
+
+ cmList& reverse()
+ {
+ std::reverse(this->Values.begin(), this->Values.end());
+ return *this;
+ }
+
+ struct SortConfiguration
+ {
+ enum class OrderMode
+ {
+ DEFAULT,
+ ASCENDING,
+ DESCENDING,
+ } Order = OrderMode::DEFAULT;
+
+ enum class CompareMethod
+ {
+ DEFAULT,
+ STRING,
+ FILE_BASENAME,
+ NATURAL,
+ } Compare = CompareMethod::DEFAULT;
+
+ enum class CaseSensitivity
+ {
+ DEFAULT,
+ SENSITIVE,
+ INSENSITIVE,
+ } Case = CaseSensitivity::DEFAULT;
+
+ // declare the default constructor to work-around clang bug
+ SortConfiguration();
+
+ SortConfiguration(OrderMode order, CompareMethod compare,
+ CaseSensitivity caseSensitivity)
+ : Order(order)
+ , Compare(compare)
+ , Case(caseSensitivity)
+ {
+ }
+ };
+ cmList& sort(const SortConfiguration& config = SortConfiguration{});
+
+ // exception raised on error during transform operations
+ class transform_error : public std::runtime_error
+ {
+ public:
+ transform_error(const std::string& error)
+ : std::runtime_error(error)
+ {
+ }
+ };
+
+ class TransformSelector
+ {
+ public:
+ using index_type = cmList::index_type;
+
+ // define some structs used as template selector
+ struct AT;
+ struct FOR;
+ struct REGEX;
+
+ virtual ~TransformSelector() = default;
+
+ virtual const std::string& GetTag() = 0;
+
+ // method NEW is used to allocate a selector of the needed type.
+ // For example:
+ // cmList::TransformSelector::New<AT>({1, 2, 5, 6});
+ // or
+ // cmList::TransformSelector::New<REGEX>("^XX.*");
+ template <typename Type>
+ static std::unique_ptr<TransformSelector> New(
+ std::initializer_list<index_type>);
+ template <typename Type>
+ static std::unique_ptr<TransformSelector> New(
+ std::vector<index_type> const&);
+ template <typename Type>
+ static std::unique_ptr<TransformSelector> New(std::vector<index_type>&&);
+
+ template <typename Type>
+ static std::unique_ptr<TransformSelector> New(std::string const&);
+ template <typename Type>
+ static std::unique_ptr<TransformSelector> New(std::string&&);
+
+ private:
+ static std::unique_ptr<TransformSelector> NewAT(
+ std::initializer_list<index_type> init);
+ static std::unique_ptr<TransformSelector> NewAT(
+ std::vector<index_type> const& init);
+ static std::unique_ptr<TransformSelector> NewAT(
+ std::vector<index_type>&& init);
+
+ static std::unique_ptr<TransformSelector> NewFOR(
+ std::initializer_list<index_type> init);
+ static std::unique_ptr<TransformSelector> NewFOR(
+ std::vector<index_type> const& init);
+ static std::unique_ptr<TransformSelector> NewFOR(
+ std::vector<index_type>&& init);
+
+ static std::unique_ptr<TransformSelector> NewREGEX(
+ std::string const& init);
+ static std::unique_ptr<TransformSelector> NewREGEX(std::string&& init);
+ };
+
+ enum class TransformAction
+ {
+ APPEND,
+ PREPEND,
+ TOLOWER,
+ TOUPPER,
+ STRIP,
+ GENEX_STRIP,
+ REPLACE
+ };
+
+ // Transforms the list by applying an action
+ // Throw std::transform_error is any of arguments specified are invalid
+ cmList& transform(TransformAction action,
+ std::unique_ptr<TransformSelector> = {});
+ cmList& transform(TransformAction action, std::string const& arg,
+ std::unique_ptr<TransformSelector> = {});
+ cmList& transform(TransformAction action, std::string const& arg1,
+ std::string const& arg2,
+ std::unique_ptr<TransformSelector> = {});
+ cmList& transform(TransformAction action,
+ std::vector<std::string> const& args,
+ std::unique_ptr<TransformSelector> = {});
+
+ std::string join(cm::string_view glue) const;
+
+ void swap(cmList& other) noexcept { this->Values.swap(other.Values); }
+
+ // static members
+ // ==============
+ // these methods can be used to store CMake list expansion directly in a
+ // std::vector.
+ static void assign(cm::string_view value,
+ std::vector<std::string>& container,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ container.clear();
+ cmList::append(value, container, emptyElements);
+ }
+ static void assign(cmValue value, std::vector<std::string>& container,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ if (value) {
+ cmList::assign(*value, container, emptyElements);
+ } else {
+ container.clear();
+ }
+ }
+ template <typename InputIterator>
+ static void assign(InputIterator first, InputIterator last,
+ std::vector<std::string>& container,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ container.clear();
+ cmList::append(first, last, container, emptyElements);
+ }
+
+ static std::vector<std::string>::iterator insert(
+ std::vector<std::string>::const_iterator pos, cm::string_view value,
+ std::vector<std::string>& container,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return cmList::Insert(pos, std::string(value), container,
+ ExpandElements::Yes, emptyElements);
+ }
+ static std::vector<std::string>::iterator insert(
+ std::vector<std::string>::const_iterator pos, cmValue value,
+ std::vector<std::string>& container,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ if (value) {
+ return cmList::insert(pos, *value, container, emptyElements);
+ }
+
+ auto delta = std::distance(container.cbegin(), pos);
+ return container.begin() + delta;
+ }
+ template <typename InputIterator>
+ static std::vector<std::string>::iterator insert(
+ std::vector<std::string>::const_iterator pos, InputIterator first,
+ InputIterator last, std::vector<std::string>& container,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return cmList::Insert(pos, first, last, container, ExpandElements::Yes,
+ emptyElements);
+ }
+
+ static std::vector<std::string>::iterator append(
+ cm::string_view value, std::vector<std::string>& container,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return cmList::insert(container.cend(), value, container, emptyElements);
+ }
+ static std::vector<std::string>::iterator append(
+ cmValue value, std::vector<std::string>& container,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ if (value) {
+ return cmList::append(*value, container, emptyElements);
+ }
+
+ return container.end();
+ }
+ template <typename InputIterator>
+ static std::vector<std::string>::iterator append(
+ InputIterator first, InputIterator last,
+ std::vector<std::string>& container,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return cmList::insert(container.cend(), first, last, container,
+ emptyElements);
+ }
+
+ static std::vector<std::string>::iterator prepend(
+ cm::string_view value, std::vector<std::string>& container,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return cmList::insert(container.cbegin(), value, container, emptyElements);
+ }
+ static std::vector<std::string>::iterator prepend(
+ cmValue value, std::vector<std::string>& container,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ if (value) {
+ return cmList::prepend(*value, container, emptyElements);
+ }
+
+ return container.begin();
+ }
+ template <typename InputIterator>
+ static std::vector<std::string>::iterator prepend(
+ InputIterator first, InputIterator last,
+ std::vector<std::string>& container,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return cmList::insert(container.cbegin(), first, last, container,
+ emptyElements);
+ }
+
+ // The following methods offer the possibility to extend a CMake list
+ // but without any intermediate expansion. So the operation is simply a
+ // string concatenation with special handling for the CMake list item
+ // separator
+ static std::string& append(cm::string_view value, std::string& list);
+ template <typename InputIterator>
+ static std::string& append(InputIterator first, InputIterator last,
+ std::string& list)
+ {
+ if (first == last) {
+ return list;
+ }
+
+ return cmList::append(cm::string_view{ std::accumulate(
+ std::next(first), last, *first,
+ [](std::string a, const std::string& b) {
+ return std::move(a) +
+ std::string(cmList::element_separator) + b;
+ }) },
+ list);
+ }
+
+ static std::string& prepend(cm::string_view value, std::string& list);
+ template <typename InputIterator>
+ static std::string& prepend(InputIterator first, InputIterator last,
+ std::string& list)
+ {
+ if (first == last) {
+ return list;
+ }
+
+ return cmList::prepend(cm::string_view{ std::accumulate(
+ std::next(first), last, *first,
+ [](std::string a, const std::string& b) {
+ return std::move(a) +
+ std::string(cmList::element_separator) + b;
+ }) },
+ list);
+ }
+
+ // Non-members
+ // ===========
+ friend inline bool operator==(const cmList& lhs, const cmList& rhs) noexcept
+ {
+ return lhs.Values == rhs.Values;
+ }
+ friend inline bool operator!=(const cmList& lhs, const cmList& rhs) noexcept
+ {
+ return lhs.Values != rhs.Values;
+ }
+
+private:
+ size_type ComputeIndex(index_type pos, bool boundCheck = true) const;
+ size_type ComputeInsertIndex(index_type pos, bool boundCheck = true) const;
+
+ cmList GetItems(std::vector<index_type>&& indexes) const;
+
+ cmList& RemoveItems(std::vector<index_type>&& indexes);
+ cmList& RemoveItems(std::vector<std::string>&& items);
+
+ static container_type::iterator Insert(container_type::const_iterator pos,
+ std::string&& value,
+ container_type& container,
+ ExpandElements expandElements,
+ EmptyElements emptyElements);
+ static container_type::iterator Insert(container_type::const_iterator pos,
+ const std::string& value,
+ container_type& container,
+ ExpandElements expandElements,
+ EmptyElements emptyElements)
+ {
+ auto tmp = value;
+ return cmList::Insert(pos, std::move(tmp), container, expandElements,
+ emptyElements);
+ }
+ template <typename InputIterator>
+ static container_type::iterator Insert(container_type::const_iterator pos,
+ InputIterator first,
+ InputIterator last,
+ container_type& container,
+ ExpandElements expandElements,
+ EmptyElements emptyElements)
+ {
+ auto delta = std::distance(container.cbegin(), pos);
+
+ if (first == last) {
+ return container.begin() + delta;
+ }
+
+ auto insertPos = container.begin() + delta;
+ if (expandElements == ExpandElements::Yes) {
+ for (; first != last; ++first) {
+ auto size = container.size();
+ insertPos = cmList::Insert(insertPos, *first, container,
+ expandElements, emptyElements);
+ insertPos += container.size() - size;
+ }
+ } else {
+ for (; first != last; ++first) {
+ if (!first->empty() || emptyElements == EmptyElements::Yes) {
+ insertPos = container.insert(insertPos, *first);
+ insertPos++;
+ }
+ }
+ }
+
+ return container.begin() + delta;
+ }
+
+ container_type Values;
+};
+
+// specializations for cmList::TransformSelector allocators
+// ========================================================
+template <>
+inline std::unique_ptr<cmList::TransformSelector>
+cmList::TransformSelector::New<cmList::TransformSelector::AT>(
+ std::initializer_list<index_type> init)
+{
+ return cmList::TransformSelector::NewAT(init);
+}
+template <>
+inline std::unique_ptr<cmList::TransformSelector>
+cmList::TransformSelector::New<cmList::TransformSelector::AT>(
+ std::vector<index_type> const& init)
+{
+ return cmList::TransformSelector::NewAT(init);
+}
+template <>
+inline std::unique_ptr<cmList::TransformSelector>
+cmList::TransformSelector::New<cmList::TransformSelector::AT>(
+ std::vector<index_type>&& init)
+{
+ return cmList::TransformSelector::NewAT(std::move(init));
+}
+
+template <>
+inline std::unique_ptr<cmList::TransformSelector>
+cmList::TransformSelector::New<cmList::TransformSelector::FOR>(
+ std::initializer_list<index_type> init)
+{
+ return cmList::TransformSelector::NewFOR(init);
+}
+template <>
+inline std::unique_ptr<cmList::TransformSelector>
+cmList::TransformSelector::New<cmList::TransformSelector::FOR>(
+ std::vector<index_type> const& init)
+{
+ return cmList::TransformSelector::NewFOR(init);
+}
+template <>
+inline std::unique_ptr<cmList::TransformSelector>
+cmList::TransformSelector::New<cmList::TransformSelector::FOR>(
+ std::vector<index_type>&& init)
+{
+ return cmList::TransformSelector::NewFOR(std::move(init));
+}
+
+template <>
+inline std::unique_ptr<cmList::TransformSelector>
+cmList::TransformSelector::New<cmList::TransformSelector::REGEX>(
+ std::string const& init)
+{
+ return cmList::TransformSelector::NewREGEX(init);
+}
+template <>
+inline std::unique_ptr<cmList::TransformSelector>
+cmList::TransformSelector::New<cmList::TransformSelector::REGEX>(
+ std::string&& init)
+{
+ return cmList::TransformSelector::NewREGEX(std::move(init));
+}
+
+// Non-member functions
+// ====================
+inline std::vector<std::string>& operator+=(std::vector<std::string>& l,
+ const cmList& r)
+{
+ l.insert(l.end(), r.begin(), r.end());
+ return l;
+}
+inline std::vector<std::string>& operator+=(std::vector<std::string>& l,
+ cmList&& r)
+{
+ std::move(r.begin(), r.end(), std::back_inserter(l));
+ r.clear();
+
+ return l;
+}
+
+namespace cm {
+inline void erase(cmList& list, const std::string& value)
+{
+ list.erase(std::remove(list.begin(), list.end(), value), list.end());
+}
+
+template <typename Predicate>
+inline void erase_if(cmList& list, Predicate pred)
+{
+ list.erase(std::remove_if(list.begin(), list.end(), pred), list.end());
+}
+}
+
+namespace srd {
+inline void swap(cmList& lhs, cmList& rhs) noexcept
+{
+ lhs.swap(rhs);
+}
+}
diff --git a/Tests/CMakeLib/CMakeLists.txt b/Tests/CMakeLib/CMakeLists.txt
index 612d4b4d82..0fc3debc9f 100644
--- a/Tests/CMakeLib/CMakeLists.txt
+++ b/Tests/CMakeLib/CMakeLists.txt
@@ -30,6 +30,7 @@ set(CMakeLib_TESTS
testCMExtMemory.cxx
testCMExtAlgorithm.cxx
testCMExtEnumSet.cxx
+ testList.cxx
)
if (CMake_TEST_FILESYSTEM_PATH OR NOT CMake_HAVE_CXX_FILESYSTEM)
list(APPEND CMakeLib_TESTS testCMFilesystemPath.cxx)
diff --git a/Tests/CMakeLib/testList.cxx b/Tests/CMakeLib/testList.cxx
new file mode 100644
index 0000000000..7294be0b31
--- /dev/null
+++ b/Tests/CMakeLib/testList.cxx
@@ -0,0 +1,995 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#include <iostream>
+#include <stdexcept>
+#include <string>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include <cm/string_view>
+
+#include "cmList.h"
+
+namespace {
+
+void checkResult(bool success)
+{
+ if (!success) {
+ std::cout << " => failed";
+ }
+ std::cout << std::endl;
+}
+
+bool testConstructors()
+{
+ std::cout << "testConstructors()";
+
+ bool result = true;
+
+ {
+ cmList list;
+ if (!list.empty() || list != cmList{}) {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "aa;bb" };
+ if (list.size() != 2 || list.to_string() != "aa;bb") {
+ result = false;
+ }
+ }
+ {
+ cmList list1{ "aa", "bb" };
+ cmList list2("aa;bb");
+
+ if (list1.size() != 2 || list2.size() != 2 || list1 != list2) {
+ result = false;
+ }
+ if (list1.to_string() != "aa;bb") {
+ result = false;
+ }
+ if (list1.to_string() != list2.to_string()) {
+ result = false;
+ }
+ }
+ {
+ std::vector<std::string> v{ "aa", "bb", "cc" };
+ cmList list(v.begin(), v.end());
+ if (list.size() != 3 || list.to_string() != "aa;bb;cc") {
+ result = false;
+ }
+ }
+ {
+ std::vector<std::string> values{ "aa;bb", "cc", "dd;ee" };
+ cmList list1(values.begin(), values.end());
+ cmList list2(values.begin(), values.end(), cmList::ExpandElements::No);
+
+ if (list1.size() != 5 || list1.to_string() != "aa;bb;cc;dd;ee") {
+ result = false;
+ }
+ if (list2.size() != 3 || list2.to_string() != "aa;bb;cc;dd;ee") {
+ result = false;
+ }
+ }
+ {
+ std::vector<std::string> values{ "aa;bb;;cc", "", "dd;ee" };
+ cmList list1(values.begin(), values.end(), cmList::ExpandElements::No,
+ cmList::EmptyElements::No);
+ cmList list2(values.begin(), values.end(), cmList::ExpandElements::No,
+ cmList::EmptyElements::Yes);
+ cmList list3(values.begin(), values.end(), cmList::ExpandElements::Yes,
+ cmList::EmptyElements::No);
+ cmList list4(values.begin(), values.end(), cmList::ExpandElements::Yes,
+ cmList::EmptyElements::Yes);
+
+ if (list1.size() != 2 || list1.to_string() != "aa;bb;;cc;dd;ee") {
+ result = false;
+ }
+ if (list2.size() != 3 || list2.to_string() != "aa;bb;;cc;;dd;ee") {
+ result = false;
+ }
+ if (list3.size() != 5 || list3.to_string() != "aa;bb;cc;dd;ee") {
+ result = false;
+ }
+ if (list4.size() != 7 || list4.to_string() != "aa;bb;;cc;;dd;ee") {
+ result = false;
+ }
+ }
+ {
+ std::vector<std::string> values{ "aa;bb", "cc", "dd;ee" };
+ cmList list1(values);
+ cmList list2(values, cmList::ExpandElements::No);
+
+ if (list1.size() != 5 || list1.to_string() != "aa;bb;cc;dd;ee") {
+ result = false;
+ }
+ if (list2.size() != 3 || list2.to_string() != "aa;bb;cc;dd;ee") {
+ result = false;
+ }
+ }
+ {
+ std::vector<std::string> values{ "aa", "bb", "cc", "dd", "ee" };
+ cmList list(std::move(values));
+
+ if (list.size() != 5 || list.to_string() != "aa;bb;cc;dd;ee") {
+ result = false;
+ }
+ if (!values.empty()) {
+ result = false;
+ }
+ }
+
+ checkResult(result);
+
+ return result;
+}
+
+bool testAssign()
+{
+ std::cout << "testAssign()";
+
+ bool result = true;
+
+ {
+ cmList list1{ "aa", "bb" };
+ cmList list2{ "cc", "dd" };
+
+ list2 = list1;
+ if (list1.size() != 2 || list2.size() != 2 || list1 != list2) {
+ result = false;
+ }
+ if (list1.to_string() != "aa;bb") {
+ result = false;
+ }
+ if (list1.to_string() != list2.to_string()) {
+ result = false;
+ }
+ }
+ {
+ cmList list1{ "aa", "bb" };
+ cmList list2{ "cc", "dd" };
+
+ list2 = std::move(list1);
+ if (!list1.empty() || list2.size() != 2) {
+ result = false;
+ }
+ if (list2.to_string() != "aa;bb") {
+ result = false;
+ }
+ }
+ {
+ std::vector<std::string> v{ "aa", "bb" };
+ cmList list{ "cc", "dd" };
+
+ list = std::move(v);
+ if (!v.empty() || list.size() != 2) {
+ result = false;
+ }
+ if (list.to_string() != "aa;bb") {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "cc", "dd" };
+
+ list = "aa;bb";
+ if (list.size() != 2) {
+ result = false;
+ }
+ if (list.to_string() != "aa;bb") {
+ result = false;
+ }
+ }
+
+ checkResult(result);
+
+ return result;
+}
+
+bool testConversions()
+{
+ std::cout << "testConversions()";
+
+ bool result = true;
+
+ {
+ cmList list("a;b;c");
+ std::string s = list.to_string();
+
+ if (s != "a;b;c") {
+ result = false;
+ }
+ }
+ {
+ cmList list("a;b;c");
+ std::vector<std::string> v = list;
+
+ if (list.size() != 3 || v.size() != 3) {
+ result = false;
+ }
+ }
+ {
+ cmList list("a;b;c");
+ std::vector<std::string> v = std::move(list);
+
+ // Microsoft compiler is not able to handle correctly the move semantics
+ // so the initial list is not moved, so do not check its size...
+ if (v.size() != 3) {
+ result = false;
+ }
+ }
+ {
+ cmList list("a;b;c");
+ std::vector<std::string> v;
+
+ // compiler is not able to select the cmList conversion operator
+ // and the std::vector assignment operator using the move semantics
+ // v = std::move(list);
+ v = std::move(list.data());
+
+ if (!list.empty() || v.size() != 3) {
+ result = false;
+ }
+ }
+
+ checkResult(result);
+
+ return result;
+}
+
+bool testAccess()
+{
+ std::cout << "testAccess()";
+
+ bool result = true;
+
+ {
+ cmList list{ "a", "b", "c" };
+ if (list.at(1) != "b") {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "a", "b", "c" };
+ if (list.at(-3) != "a") {
+ result = false;
+ }
+ }
+ {
+ try {
+ cmList list{ "a", "b", "c" };
+ if (list.at(4) != "a") {
+ result = false;
+ }
+ } catch (std::out_of_range&) {
+ }
+ }
+ {
+ try {
+ cmList list{ "a", "b", "c" };
+ if (list.at(-4) != "a") {
+ result = false;
+ }
+ } catch (std::out_of_range&) {
+ }
+ }
+ {
+ cmList list{ "a", "b", "c", "d", "e" };
+ auto sublist = list.sublist(list.begin() + 1, list.begin() + 3);
+ if (sublist.size() != 2 || sublist != cmList{ "b", "c" }) {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "a", "b", "c", "d", "e" };
+ auto sublist = list.sublist(1, 2);
+ if (sublist.size() != 2 || sublist != cmList{ "b", "c" }) {
+ result = false;
+ }
+
+ sublist = list.sublist(1, cmList::npos);
+ if (sublist.size() != 4 || sublist != cmList{ "b", "c", "d", "e" }) {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "a", "b", "c", "d", "e", "f" };
+ auto sublist = list.get_items({ 1, 3, 5 });
+ if (sublist.size() != 3 || sublist != cmList{ "b", "d", "f" }) {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "a", "b", "c", "d", "e", "f" };
+ auto sublist = list.get_items({ 1, -3, 5, -3 });
+ if (sublist.size() != 4 || sublist != cmList{ "b", "d", "f", "d" }) {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "a", "b", "c", "d", "e", "f" };
+ try {
+ if (list.get_items({ 1, -3, 5, -3, 10 }).size() != 5) {
+ result = false;
+ }
+ } catch (std::out_of_range&) {
+ }
+ }
+ {
+ cmList list{ "a", "b", "c", "d", "e", "f" };
+
+ if (list.find("b") != 1) {
+ result = false;
+ }
+ if (list.find("x") != cmList::npos) {
+ result = false;
+ }
+ }
+
+ checkResult(result);
+
+ return result;
+}
+
+bool testModifiers()
+{
+ std::cout << "testModifiers()";
+
+ bool result = true;
+
+ {
+ cmList list{ "1;2;3;4;5" };
+
+ auto it = list.insert(list.begin() + 2, "6;7;8");
+ if (list.size() != 8 || list.to_string() != "1;2;6;7;8;3;4;5") {
+ result = false;
+ }
+ if (*it != "6") {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "1;2;3;4;5" };
+
+ auto it =
+ list.insert(list.begin() + 2, "6;7;8", cmList::ExpandElements::No);
+ if (list.size() != 6 || list.to_string() != "1;2;6;7;8;3;4;5") {
+ result = false;
+ }
+ if (*it != "6;7;8") {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "1;2;3;4;5" };
+ cmList v{ "6", "7", "8" };
+
+ auto it = list.insert(list.begin() + 2, v);
+ if (list.size() != 8 || list.to_string() != "1;2;6;7;8;3;4;5") {
+ result = false;
+ }
+ if (*it != "6") {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "1;2;3;4;5" };
+ cmList v{ "6", "7", "8" };
+
+ auto it = list.insert(list.begin() + 2, std::move(v));
+ if (list.size() != 8 || list.to_string() != "1;2;6;7;8;3;4;5") {
+ result = false;
+ }
+ if (*it != "6") {
+ result = false;
+ }
+
+ if (!v.empty()) {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "1;2;3;4;5" };
+ std::vector<std::string> v{ "6", "7", "8" };
+
+ auto it = list.insert(list.begin() + 2, v);
+ if (list.size() != 8 || list.to_string() != "1;2;6;7;8;3;4;5") {
+ result = false;
+ }
+ if (*it != "6") {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "1;2;3;4;5" };
+ std::vector<std::string> v{ "6;7", "8" };
+
+ auto it = list.insert(list.begin() + 2, v);
+ if (list.size() != 8 || list.to_string() != "1;2;6;7;8;3;4;5") {
+ result = false;
+ }
+ if (*it != "6") {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "1;2;3;4;5" };
+ std::vector<std::string> v{ "6;7", "8" };
+
+ auto it = list.insert(list.begin() + 2, v, cmList::ExpandElements::No);
+ if (list.size() != 7 || list.to_string() != "1;2;6;7;8;3;4;5") {
+ result = false;
+ }
+ if (*it != "6;7") {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "1;2;3;4;5" };
+ std::vector<std::string> v{ "6;;7", "8" };
+
+ auto it = list.insert(list.begin() + 2, v);
+ if (list.size() != 8 || list.to_string() != "1;2;6;7;8;3;4;5") {
+ result = false;
+ }
+ if (*it != "6") {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "1;2;3;4;5" };
+ std::vector<std::string> v{ "6;;7", "8" };
+
+ auto it = list.insert(list.begin() + 2, v, cmList::EmptyElements::Yes);
+ if (list.size() != 9 || list.to_string() != "1;2;6;;7;8;3;4;5") {
+ result = false;
+ }
+ if (*it != "6") {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "1;2;3;4;5" };
+ std::vector<std::string> v{ "6", "7", "8" };
+
+ auto it = list.insert(list.begin() + 2, std::move(v));
+ if (list.size() != 8 || list.to_string() != "1;2;6;7;8;3;4;5") {
+ result = false;
+ }
+ if (*it != "6") {
+ result = false;
+ }
+
+ if (!v.empty()) {
+ result = false;
+ }
+ }
+
+ checkResult(result);
+
+ return result;
+}
+
+bool testRemoveItems()
+{
+ std::cout << "testRemoveItems()";
+
+ bool result = true;
+
+ {
+ cmList list("a;b;c;d;e;f;g;h");
+
+ list.remove_items({ 1, 3, 5 });
+
+ if (list.size() != 5 || list.to_string() != "a;c;e;g;h") {
+ result = false;
+ }
+ }
+ {
+ cmList list("a;b;c;b;a;d;e;f");
+
+ list.remove_items({ "a", "b", "h" });
+
+ if (list.size() != 4 || list.to_string() != "c;d;e;f") {
+ result = false;
+ }
+ }
+ {
+ cmList list("a;b;c;d;e;f;g;h");
+ std::vector<cmList::index_type> remove{ 1, 3, 5 };
+
+ list.remove_items(remove.begin(), remove.end());
+
+ if (list.size() != 5 || list.to_string() != "a;c;e;g;h") {
+ result = false;
+ }
+ }
+ {
+ cmList list("a;b;c;b;a;d;e;f");
+ std::vector<std::string> remove{ "b", "a", "h" };
+
+ list.remove_items(remove.begin(), remove.end());
+
+ if (list.size() != 4 || list.to_string() != "c;d;e;f") {
+ result = false;
+ }
+ }
+
+ checkResult(result);
+
+ return result;
+}
+
+bool testRemoveDuplicates()
+{
+ std::cout << "testRemoveDuplicates()";
+
+ bool result = true;
+
+ {
+ cmList list("b;c;b;a;a;c;b;a;c;b");
+
+ list.remove_duplicates();
+
+ if (list.size() != 3 || list.to_string() != "b;c;a") {
+ result = false;
+ }
+ }
+
+ checkResult(result);
+
+ return result;
+}
+
+bool testFilter()
+{
+ std::cout << "testFilter()";
+
+ bool result = true;
+
+ {
+ cmList list{ "AA", "Aa", "aA" };
+
+ list.filter("^A", cmList::FilterMode::INCLUDE);
+ if (list.size() != 2 || list.to_string() != "AA;Aa") {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "AA", "Aa", "aA" };
+
+ list.filter("^A", cmList::FilterMode::EXCLUDE);
+ if (list.size() != 1 || list.to_string() != "aA") {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "AA", "Aa", "aA" };
+
+ try {
+ list.filter("^(A", cmList::FilterMode::EXCLUDE);
+ if (list.size() != 1) {
+ result = false;
+ }
+ } catch (const std::invalid_argument&) {
+ }
+ }
+
+ checkResult(result);
+
+ return result;
+}
+
+bool testReverse()
+{
+ std::cout << "testReverse()";
+
+ bool result = true;
+
+ {
+ cmList list{ "a", "b", "c" };
+ if (list.reverse().to_string() != "c;b;a") {
+ result = false;
+ }
+ }
+
+ checkResult(result);
+
+ return result;
+}
+
+bool testSort()
+{
+ std::cout << "testSort()";
+
+ bool result = true;
+
+ using SortConfiguration = cmList::SortConfiguration;
+
+ {
+ cmList list{ "A", "D", "C", "B", "A" };
+
+ list.sort();
+ if (list.to_string() != "A;A;B;C;D") {
+ result = false;
+ }
+
+ list.sort({ SortConfiguration::OrderMode::DESCENDING,
+ SortConfiguration::CompareMethod::DEFAULT,
+ SortConfiguration::CaseSensitivity::DEFAULT });
+ if (list.to_string() != "D;C;B;A;A") {
+ result = false;
+ }
+ }
+ {
+ SortConfiguration sortCfg;
+ cmList list{ "1.0", "1.1", "2.5", "10.2" };
+
+ list.sort(sortCfg);
+ if (list.to_string() != "1.0;1.1;10.2;2.5") {
+ result = false;
+ }
+
+ sortCfg.Compare = SortConfiguration::CompareMethod::NATURAL;
+ list.sort(sortCfg);
+ if (list.to_string() != "1.0;1.1;2.5;10.2") {
+ result = false;
+ }
+
+ sortCfg.Order = SortConfiguration::OrderMode::DESCENDING;
+ list.sort(sortCfg);
+ if (list.to_string() != "10.2;2.5;1.1;1.0") {
+ result = false;
+ }
+ }
+ {
+ SortConfiguration sortCfg;
+ cmList list{ "/zz/bb.cc", "/xx/yy/dd.cc", "/aa/cc.aa" };
+
+ list.sort(sortCfg);
+ if (list.to_string() != "/aa/cc.aa;/xx/yy/dd.cc;/zz/bb.cc") {
+ result = false;
+ }
+
+ sortCfg.Compare = SortConfiguration::CompareMethod::FILE_BASENAME;
+ if (list.sort(sortCfg).to_string() != "/zz/bb.cc;/aa/cc.aa;/xx/yy/dd.cc") {
+ result = false;
+ }
+ }
+ {
+ SortConfiguration sortCfg;
+ cmList list{ "c/B", "a/c", "B/a" };
+
+ if (list.sort().to_string() != "B/a;a/c;c/B") {
+ result = false;
+ }
+
+ sortCfg.Case = SortConfiguration::CaseSensitivity::INSENSITIVE;
+ if (list.sort(sortCfg).to_string() != "a/c;B/a;c/B") {
+ result = false;
+ }
+ }
+
+ checkResult(result);
+
+ return result;
+}
+
+bool testTransform()
+{
+ std::cout << "testTransform()";
+
+ bool result = true;
+
+ using AT = cmList::TransformSelector::AT;
+ using FOR = cmList::TransformSelector::FOR;
+ using REGEX = cmList::TransformSelector::REGEX;
+
+ {
+ cmList list({ "AA", "BB", "CC", "DD", "EE" });
+
+ list.transform(cmList::TransformAction::APPEND, "-X");
+ if (list.to_string() != "AA-X;BB-X;CC-X;DD-X;EE-X") {
+ result = false;
+ }
+ }
+ {
+ cmList list({ "AA", "BB", "CC", "DD", "EE" });
+
+ list.transform(cmList::TransformAction::PREPEND, "X-");
+ if (list.to_string() != "X-AA;X-BB;X-CC;X-DD;X-EE") {
+ result = false;
+ }
+ }
+ {
+ cmList list({ "AA", "BB", "CC", "DD", "EE" });
+
+ list.transform(cmList::TransformAction::TOLOWER);
+ if (list.to_string() != "aa;bb;cc;dd;ee") {
+ result = false;
+ }
+ }
+ {
+ cmList list({ "aa", "bb", "cc", "dd", "ee" });
+
+ list.transform(cmList::TransformAction::TOUPPER);
+ if (list.to_string() != "AA;BB;CC;DD;EE") {
+ result = false;
+ }
+ }
+ {
+ cmList list({ " AA", "BB ", " CC ", "DD", "EE" });
+
+ list.transform(cmList::TransformAction::STRIP);
+ if (list.to_string() != "AA;BB;CC;DD;EE") {
+ result = false;
+ }
+ }
+ {
+ cmList list({ "$<CONFIG>AA", "BB$<OR>", "C$<AND>C", "$<OR>DD$<AND>",
+ "$<>E$<>E$<>" });
+
+ list.transform(cmList::TransformAction::GENEX_STRIP);
+ if (list.to_string() != "AA;BB;CC;DD;EE") {
+ result = false;
+ }
+ }
+ {
+ cmList list({ "ABC", "BBCB", "BCCCBC", "BCBCDD", "EBCBCEBC" });
+
+ list.transform(cmList::TransformAction::REPLACE, "^BC|BC$", "X");
+ if (list.to_string() != "AX;BBCB;XCCX;XXDD;EBCBCEX") {
+ result = false;
+ }
+ }
+ {
+ auto atSelector = cmList::TransformSelector::New<AT>({ 1, 2, 4 });
+ cmList list({ "AA", "BB", "CC", "DD", "EE" });
+
+ list.transform(cmList::TransformAction::TOLOWER, std::move(atSelector));
+ if (list.to_string() != "AA;bb;cc;DD;ee") {
+ result = false;
+ }
+ }
+ {
+ auto atSelector = cmList::TransformSelector::New<AT>({ 1, 2, -1 });
+ cmList list({ "AA", "BB", "CC", "DD", "EE" });
+
+ list.transform(cmList::TransformAction::TOLOWER, std::move(atSelector));
+ if (list.to_string() != "AA;bb;cc;DD;ee") {
+ result = false;
+ }
+ }
+ {
+ auto forSelector = cmList::TransformSelector::New<FOR>({ 1, 3 });
+ cmList list({ "AA", "BB", "CC", "DD", "EE" });
+
+ list.transform(cmList::TransformAction::TOLOWER, std::move(forSelector));
+ if (list.to_string() != "AA;bb;cc;dd;EE") {
+ result = false;
+ }
+ }
+ {
+ auto forSelector = cmList::TransformSelector::New<FOR>({ 0, 4, 2 });
+ cmList list({ "AA", "BB", "CC", "DD", "EE" });
+
+ list.transform(cmList::TransformAction::TOLOWER, std::move(forSelector));
+ if (list.to_string() != "aa;BB;cc;DD;ee") {
+ result = false;
+ }
+ }
+ {
+ auto regexSelector = cmList::TransformSelector::New<REGEX>("^(A|D|E)");
+ cmList list({ "AA", "BB", "CC", "DD", "EE" });
+
+ list.transform(cmList::TransformAction::TOLOWER, std::move(regexSelector));
+ if (list.to_string() != "aa;BB;CC;dd;ee") {
+ result = false;
+ }
+ }
+
+ checkResult(result);
+
+ return result;
+}
+
+bool testStaticModifiers()
+{
+ std::cout << "testStaticModifiers()";
+
+ bool result = true;
+
+ {
+ std::vector<std::string> v{ "a", "b", "c" };
+ cmList::assign("d;e", v);
+
+ if (v.size() != 2 || v[0] != "d" || v[1] != "e") {
+ result = false;
+ }
+ }
+ {
+ std::vector<std::string> v{ "a", "b", "c" };
+ cmList::append("d;;e", v);
+
+ if (v.size() != 5 || v[3] != "d" || v[4] != "e") {
+ result = false;
+ }
+ }
+ {
+ std::vector<std::string> v{ "a", "b", "c" };
+ cmList::append("d;;e", v, cmList::EmptyElements::Yes);
+
+ if (v.size() != 6 || v[3] != "d" || !v[4].empty() || v[5] != "e") {
+ result = false;
+ }
+ }
+ {
+ std::vector<std::string> v{ "a", "b", "c" };
+ cmList::prepend("d;e", v);
+
+ if (v.size() != 5 || v[0] != "d" || v[1] != "e") {
+ result = false;
+ }
+ }
+ {
+ std::vector<std::string> v{ "a", "b", "c" };
+ cmList::prepend("d;;e", v, cmList::EmptyElements::Yes);
+
+ if (v.size() != 6 || v[0] != "d" || !v[1].empty() || v[2] != "e") {
+ result = false;
+ }
+ }
+ {
+ std::string list{ "a;b;c" };
+ cmList::append("d;e", list);
+
+ if (list != "a;b;c;d;e") {
+ result = false;
+ }
+ }
+ {
+ std::string list;
+ cmList::append("d;e", list);
+
+ if (list != "d;e") {
+ result = false;
+ }
+ }
+ {
+ std::string list{ "a;b;c" };
+ cmList::append("", list);
+
+ if (list != "a;b;c;") {
+ result = false;
+ }
+ }
+ {
+ std::string list{ "a;b;c" };
+ std::vector<std::string> v{ "d", "e" };
+ cmList::append(v.begin(), v.end(), list);
+
+ if (list != "a;b;c;d;e") {
+ result = false;
+ }
+ }
+ {
+ std::string list{ "a;b;c" };
+ std::vector<std::string> v;
+ cmList::append(v.begin(), v.end(), list);
+
+ if (list != "a;b;c") {
+ result = false;
+ }
+ }
+ {
+ std::string list;
+ std::vector<std::string> v{ "d", "e" };
+ cmList::append(v.begin(), v.end(), list);
+
+ if (list != "d;e") {
+ result = false;
+ }
+ }
+ {
+ std::string list{ "a;b;c" };
+ cmList::prepend("d;e", list);
+
+ if (list != "d;e;a;b;c") {
+ result = false;
+ }
+ }
+ {
+ std::string list;
+ cmList::prepend("d;e", list);
+
+ if (list != "d;e") {
+ result = false;
+ }
+ }
+ {
+ std::string list{ "a;b;c" };
+ cmList::prepend("", list);
+
+ if (list != ";a;b;c") {
+ result = false;
+ }
+ }
+ {
+ std::string list{ "a;b;c" };
+ std::vector<std::string> v{ "d", "e" };
+ cmList::prepend(v.begin(), v.end(), list);
+
+ if (list != "d;e;a;b;c") {
+ result = false;
+ }
+ }
+ {
+ std::string list{ "a;b;c" };
+ std::vector<std::string> v;
+ cmList::prepend(v.begin(), v.end(), list);
+
+ if (list != "a;b;c") {
+ result = false;
+ }
+ }
+ {
+ std::string list;
+ std::vector<std::string> v{ "d", "e" };
+ cmList::prepend(v.begin(), v.end(), list);
+
+ if (list != "d;e") {
+ result = false;
+ }
+ }
+
+ checkResult(result);
+
+ return result;
+}
+}
+
+int testList(int /*unused*/, char* /*unused*/[])
+{
+ int result = 0;
+
+ if (!testConstructors()) {
+ result = 1;
+ }
+ if (!testAssign()) {
+ result = 1;
+ }
+ if (!testConversions()) {
+ result = 1;
+ }
+ if (!testAccess()) {
+ result = 1;
+ }
+ if (!testModifiers()) {
+ result = 1;
+ }
+ if (!testRemoveItems()) {
+ result = 1;
+ }
+ if (!testRemoveDuplicates()) {
+ result = 1;
+ }
+ if (!testFilter()) {
+ result = 1;
+ }
+ if (!testReverse()) {
+ result = 1;
+ }
+ if (!testSort()) {
+ result = 1;
+ }
+ if (!testTransform()) {
+ result = 1;
+ }
+ if (!testStaticModifiers()) {
+ result = 1;
+ }
+
+ return result;
+}
diff --git a/bootstrap b/bootstrap
index 83f4814b6e..3d5ef405cd 100755
--- a/bootstrap
+++ b/bootstrap
@@ -307,6 +307,7 @@ CMAKE_CXX_SOURCES="\
cmBuildCommand \
cmCMakeLanguageCommand \
cmCMakeMinimumRequired \
+ cmList \
cmCMakePath \
cmCMakePathCommand \
cmCMakePolicyCommand \