From 768225837db303e8b568e39ade730eea8ac6b573 Mon Sep 17 00:00:00 2001
From: Marc Chevrier <marc.chevrier@sap.com>
Date: Fri, 16 Mar 2018 14:12:25 +0100
Subject: list: Add SUBLIST sub-command

Issue: #17823
---
 Help/command/list.rst                              | 12 +++++
 Help/release/dev/list-sublist.rst                  |  5 +++
 Source/cmListCommand.cxx                           | 52 ++++++++++++++++++++++
 Source/cmListCommand.h                             |  1 +
 Tests/RunCMake/list/RunCMakeTest.cmake             |  7 +++
 .../RunCMake/list/SUBLIST-InvalidIndex-result.txt  |  1 +
 .../RunCMake/list/SUBLIST-InvalidIndex-stderr.txt  |  4 ++
 Tests/RunCMake/list/SUBLIST-InvalidIndex.cmake     |  2 +
 .../RunCMake/list/SUBLIST-InvalidLength-result.txt |  1 +
 .../RunCMake/list/SUBLIST-InvalidLength-stderr.txt |  4 ++
 Tests/RunCMake/list/SUBLIST-InvalidLength.cmake    |  2 +
 Tests/RunCMake/list/SUBLIST-NoArguments-result.txt |  1 +
 Tests/RunCMake/list/SUBLIST-NoArguments-stderr.txt |  4 ++
 Tests/RunCMake/list/SUBLIST-NoArguments.cmake      |  1 +
 Tests/RunCMake/list/SUBLIST-NoVariable-result.txt  |  1 +
 Tests/RunCMake/list/SUBLIST-NoVariable-stderr.txt  |  4 ++
 Tests/RunCMake/list/SUBLIST-NoVariable.cmake       |  2 +
 .../list/SUBLIST-TooManyArguments-result.txt       |  1 +
 .../list/SUBLIST-TooManyArguments-stderr.txt       |  4 ++
 Tests/RunCMake/list/SUBLIST-TooManyArguments.cmake |  1 +
 Tests/RunCMake/list/SUBLIST.cmake                  | 46 +++++++++++++++++++
 21 files changed, 156 insertions(+)
 create mode 100644 Help/release/dev/list-sublist.rst
 create mode 100644 Tests/RunCMake/list/SUBLIST-InvalidIndex-result.txt
 create mode 100644 Tests/RunCMake/list/SUBLIST-InvalidIndex-stderr.txt
 create mode 100644 Tests/RunCMake/list/SUBLIST-InvalidIndex.cmake
 create mode 100644 Tests/RunCMake/list/SUBLIST-InvalidLength-result.txt
 create mode 100644 Tests/RunCMake/list/SUBLIST-InvalidLength-stderr.txt
 create mode 100644 Tests/RunCMake/list/SUBLIST-InvalidLength.cmake
 create mode 100644 Tests/RunCMake/list/SUBLIST-NoArguments-result.txt
 create mode 100644 Tests/RunCMake/list/SUBLIST-NoArguments-stderr.txt
 create mode 100644 Tests/RunCMake/list/SUBLIST-NoArguments.cmake
 create mode 100644 Tests/RunCMake/list/SUBLIST-NoVariable-result.txt
 create mode 100644 Tests/RunCMake/list/SUBLIST-NoVariable-stderr.txt
 create mode 100644 Tests/RunCMake/list/SUBLIST-NoVariable.cmake
 create mode 100644 Tests/RunCMake/list/SUBLIST-TooManyArguments-result.txt
 create mode 100644 Tests/RunCMake/list/SUBLIST-TooManyArguments-stderr.txt
 create mode 100644 Tests/RunCMake/list/SUBLIST-TooManyArguments.cmake
 create mode 100644 Tests/RunCMake/list/SUBLIST.cmake

diff --git a/Help/command/list.rst b/Help/command/list.rst
index 3c092bd..6218a2a 100644
--- a/Help/command/list.rst
+++ b/Help/command/list.rst
@@ -65,6 +65,18 @@ Returns a string joining all list's elements using the glue string.
 To join multiple strings, which are not part of a list, use ``JOIN`` operator
 from :command:`string` command.
 
+SUBLIST
+"""""""
+
+::
+
+  list(SUBLIST <list> <begin> <length> <output variable>)
+
+Returns a sublist of the given list.
+If ``<length>`` is 0, an empty list will be returned.
+If ``<length>`` is -1 or the list is smaller than ``<begin>+<length>`` then
+the remaining elements of the list starting at ``<begin>`` will be returned.
+
 Search
 ^^^^^^
 
diff --git a/Help/release/dev/list-sublist.rst b/Help/release/dev/list-sublist.rst
new file mode 100644
index 0000000..7ad225b
--- /dev/null
+++ b/Help/release/dev/list-sublist.rst
@@ -0,0 +1,5 @@
+list-sublist
+------------
+
+* The :command:`list` command learned a ``SUBLIST`` sub-command
+  to get a sublist of the list.
diff --git a/Source/cmListCommand.cxx b/Source/cmListCommand.cxx
index 62d4ea7..821e6d1 100644
--- a/Source/cmListCommand.cxx
+++ b/Source/cmListCommand.cxx
@@ -57,6 +57,9 @@ bool cmListCommand::InitialPass(std::vector<std::string> const& args,
   if (subCommand == "SORT") {
     return this->HandleSortCommand(args);
   }
+  if (subCommand == "SUBLIST") {
+    return this->HandleSublistCommand(args);
+  }
   if (subCommand == "REVERSE") {
     return this->HandleReverseCommand(args);
   }
@@ -427,6 +430,55 @@ bool cmListCommand::HandleSortCommand(std::vector<std::string> const& args)
   return true;
 }
 
+bool cmListCommand::HandleSublistCommand(std::vector<std::string> const& args)
+{
+  if (args.size() != 5) {
+    std::ostringstream error;
+    error << "sub-command SUBLIST requires four arguments (" << args.size() - 1
+          << " found).";
+    this->SetError(error.str());
+    return false;
+  }
+
+  const std::string& listName = args[1];
+  const std::string& variableName = args[args.size() - 1];
+
+  // expand the variable
+  std::vector<std::string> varArgsExpanded;
+  if (!this->GetList(varArgsExpanded, listName) || varArgsExpanded.empty()) {
+    this->Makefile->AddDefinition(variableName, "");
+    return true;
+  }
+
+  const int start = atoi(args[2].c_str());
+  const int length = atoi(args[3].c_str());
+
+  using size_type = decltype(varArgsExpanded)::size_type;
+
+  if (start < 0 || size_type(start) >= varArgsExpanded.size()) {
+    std::ostringstream error;
+    error << "begin index: " << start << " is out of range 0 - "
+          << varArgsExpanded.size() - 1;
+    this->SetError(error.str());
+    return false;
+  }
+  if (length < -1) {
+    std::ostringstream error;
+    error << "length: " << length << " should be -1 or greater";
+    this->SetError(error.str());
+    return false;
+  }
+
+  const size_type end =
+    (length == -1 || size_type(start + length) > varArgsExpanded.size())
+    ? varArgsExpanded.size()
+    : size_type(start + length);
+  std::vector<std::string> sublist(varArgsExpanded.begin() + start,
+                                   varArgsExpanded.begin() + end);
+  this->Makefile->AddDefinition(variableName, cmJoin(sublist, ";").c_str());
+  return true;
+}
+
 bool cmListCommand::HandleRemoveAtCommand(std::vector<std::string> const& args)
 {
   if (args.size() < 3) {
diff --git a/Source/cmListCommand.h b/Source/cmListCommand.h
index d6870e6..d69d8f9 100644
--- a/Source/cmListCommand.h
+++ b/Source/cmListCommand.h
@@ -42,6 +42,7 @@ protected:
   bool HandleRemoveItemCommand(std::vector<std::string> const& args);
   bool HandleRemoveDuplicatesCommand(std::vector<std::string> const& args);
   bool HandleSortCommand(std::vector<std::string> const& args);
+  bool HandleSublistCommand(std::vector<std::string> const& args);
   bool HandleReverseCommand(std::vector<std::string> const& args);
   bool HandleFilterCommand(std::vector<std::string> const& args);
   bool FilterRegex(std::vector<std::string> const& args, bool includeMatches,
diff --git a/Tests/RunCMake/list/RunCMakeTest.cmake b/Tests/RunCMake/list/RunCMakeTest.cmake
index 1be4f85..3913783 100644
--- a/Tests/RunCMake/list/RunCMakeTest.cmake
+++ b/Tests/RunCMake/list/RunCMakeTest.cmake
@@ -13,6 +13,7 @@ run_cmake(FILTER-REGEX-InvalidRegex)
 run_cmake(GET-InvalidIndex)
 run_cmake(INSERT-InvalidIndex)
 run_cmake(REMOVE_AT-InvalidIndex)
+run_cmake(SUBLIST-InvalidIndex)
 
 run_cmake(FILTER-REGEX-TooManyArguments)
 run_cmake(JOIN-TooManyArguments)
@@ -20,6 +21,7 @@ run_cmake(LENGTH-TooManyArguments)
 run_cmake(REMOVE_DUPLICATES-TooManyArguments)
 run_cmake(REVERSE-TooManyArguments)
 run_cmake(SORT-TooManyArguments)
+run_cmake(SUBLIST-TooManyArguments)
 
 run_cmake(FILTER-NotList)
 run_cmake(REMOVE_AT-NotList)
@@ -36,3 +38,8 @@ run_cmake(FILTER-REGEX-Valid1)
 run_cmake(JOIN-NoArguments)
 run_cmake(JOIN-NoVariable)
 run_cmake(JOIN)
+
+run_cmake(SUBLIST-NoArguments)
+run_cmake(SUBLIST-NoVariable)
+run_cmake(SUBLIST-InvalidLength)
+run_cmake(SUBLIST)
diff --git a/Tests/RunCMake/list/SUBLIST-InvalidIndex-result.txt b/Tests/RunCMake/list/SUBLIST-InvalidIndex-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/list/SUBLIST-InvalidIndex-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/list/SUBLIST-InvalidIndex-stderr.txt b/Tests/RunCMake/list/SUBLIST-InvalidIndex-stderr.txt
new file mode 100644
index 0000000..5e67b27
--- /dev/null
+++ b/Tests/RunCMake/list/SUBLIST-InvalidIndex-stderr.txt
@@ -0,0 +1,4 @@
+^CMake Error at SUBLIST-InvalidIndex.cmake:2 \(list\):
+  list begin index: 3 is out of range 0 - 2
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/SUBLIST-InvalidIndex.cmake b/Tests/RunCMake/list/SUBLIST-InvalidIndex.cmake
new file mode 100644
index 0000000..565b9dc
--- /dev/null
+++ b/Tests/RunCMake/list/SUBLIST-InvalidIndex.cmake
@@ -0,0 +1,2 @@
+set(mylist alpha bravo charlie)
+list(SUBLIST mylist 3 -1 result)
diff --git a/Tests/RunCMake/list/SUBLIST-InvalidLength-result.txt b/Tests/RunCMake/list/SUBLIST-InvalidLength-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/list/SUBLIST-InvalidLength-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/list/SUBLIST-InvalidLength-stderr.txt b/Tests/RunCMake/list/SUBLIST-InvalidLength-stderr.txt
new file mode 100644
index 0000000..16c6ecb
--- /dev/null
+++ b/Tests/RunCMake/list/SUBLIST-InvalidLength-stderr.txt
@@ -0,0 +1,4 @@
+^CMake Error at SUBLIST-InvalidLength.cmake:2 \(list\):
+  list length: -2 should be -1 or greater
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/SUBLIST-InvalidLength.cmake b/Tests/RunCMake/list/SUBLIST-InvalidLength.cmake
new file mode 100644
index 0000000..1dab83f
--- /dev/null
+++ b/Tests/RunCMake/list/SUBLIST-InvalidLength.cmake
@@ -0,0 +1,2 @@
+set(mylist alpha bravo charlie)
+list(SUBLIST mylist 0 -2 result)
diff --git a/Tests/RunCMake/list/SUBLIST-NoArguments-result.txt b/Tests/RunCMake/list/SUBLIST-NoArguments-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/list/SUBLIST-NoArguments-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/list/SUBLIST-NoArguments-stderr.txt b/Tests/RunCMake/list/SUBLIST-NoArguments-stderr.txt
new file mode 100644
index 0000000..073173c
--- /dev/null
+++ b/Tests/RunCMake/list/SUBLIST-NoArguments-stderr.txt
@@ -0,0 +1,4 @@
+^CMake Error at SUBLIST-NoArguments.cmake:1 \(list\):
+  list sub-command SUBLIST requires four arguments \(1 found\).
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/SUBLIST-NoArguments.cmake b/Tests/RunCMake/list/SUBLIST-NoArguments.cmake
new file mode 100644
index 0000000..df38b87
--- /dev/null
+++ b/Tests/RunCMake/list/SUBLIST-NoArguments.cmake
@@ -0,0 +1 @@
+list(SUBLIST mylist)
diff --git a/Tests/RunCMake/list/SUBLIST-NoVariable-result.txt b/Tests/RunCMake/list/SUBLIST-NoVariable-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/list/SUBLIST-NoVariable-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/list/SUBLIST-NoVariable-stderr.txt b/Tests/RunCMake/list/SUBLIST-NoVariable-stderr.txt
new file mode 100644
index 0000000..6f80c1a
--- /dev/null
+++ b/Tests/RunCMake/list/SUBLIST-NoVariable-stderr.txt
@@ -0,0 +1,4 @@
+^CMake Error at SUBLIST-NoVariable.cmake:2 \(list\):
+  list sub-command SUBLIST requires four arguments \(3 found\).
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/SUBLIST-NoVariable.cmake b/Tests/RunCMake/list/SUBLIST-NoVariable.cmake
new file mode 100644
index 0000000..83edae9
--- /dev/null
+++ b/Tests/RunCMake/list/SUBLIST-NoVariable.cmake
@@ -0,0 +1,2 @@
+set(mylist alpha bravo charlie)
+list(SUBLIST mylist 0 -1)
diff --git a/Tests/RunCMake/list/SUBLIST-TooManyArguments-result.txt b/Tests/RunCMake/list/SUBLIST-TooManyArguments-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/list/SUBLIST-TooManyArguments-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/list/SUBLIST-TooManyArguments-stderr.txt b/Tests/RunCMake/list/SUBLIST-TooManyArguments-stderr.txt
new file mode 100644
index 0000000..eb46844
--- /dev/null
+++ b/Tests/RunCMake/list/SUBLIST-TooManyArguments-stderr.txt
@@ -0,0 +1,4 @@
+^CMake Error at SUBLIST-TooManyArguments.cmake:1 \(list\):
+  list sub-command SUBLIST requires four arguments \(5 found\).
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/SUBLIST-TooManyArguments.cmake b/Tests/RunCMake/list/SUBLIST-TooManyArguments.cmake
new file mode 100644
index 0000000..28f20c1
--- /dev/null
+++ b/Tests/RunCMake/list/SUBLIST-TooManyArguments.cmake
@@ -0,0 +1 @@
+list(SUBLIST mylist 0 -1 result one_too_many)
diff --git a/Tests/RunCMake/list/SUBLIST.cmake b/Tests/RunCMake/list/SUBLIST.cmake
new file mode 100644
index 0000000..fd15c28
--- /dev/null
+++ b/Tests/RunCMake/list/SUBLIST.cmake
@@ -0,0 +1,46 @@
+set(mylist alpha bravo charlie delta)
+list(SUBLIST mylist 1 2 result)
+
+if (NOT result STREQUAL "bravo;charlie")
+  message (FATAL_ERROR "SUBLIST is \"${result}\", expected is \"bravo;charlie\"")
+endif()
+
+
+unset(result)
+list(SUBLIST mylist 0 2 result)
+
+if (NOT result STREQUAL "alpha;bravo")
+  message (FATAL_ERROR "SUBLIST is \"${result}\", expected is \"alpha;bravo\"")
+endif()
+
+
+unset(result)
+list(SUBLIST mylist 3 2 result)
+
+if (NOT result STREQUAL "delta")
+  message (FATAL_ERROR "SUBLIST is \"${result}\", expected is \"delta\"")
+endif()
+
+
+unset(result)
+list(SUBLIST mylist 2 0 result)
+list(LENGTH result length)
+if (NOT length EQUAL 0)
+  message (FATAL_ERROR "SUBLIST is \"${result}\", expected is an empty list")
+endif()
+
+
+unset(result)
+list(SUBLIST mylist 1 5 result)
+
+if (NOT result STREQUAL "bravo;charlie;delta")
+  message (FATAL_ERROR "SUBLIST is \"${result}\", expected is \"bravo;charlie;delta\"")
+endif()
+
+
+unset(result)
+list(SUBLIST mylist 1 -1 result)
+
+if (NOT result STREQUAL "bravo;charlie;delta")
+  message (FATAL_ERROR "SUBLIST is \"${result}\", expected is \"bravo;charlie;delta\"")
+endif()
-- 
cgit v0.12