Merge topic 'list_sort'

49a51a61d7 list: Add options to control the SORT comparison operation

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !2065
This commit is contained in:
Brad King 2018-06-14 17:55:15 +00:00 committed by Kitware Robot
commit 575f97763f
30 changed files with 391 additions and 11 deletions

View File

@ -28,7 +28,7 @@ Synopsis
`Ordering`_
list(`REVERSE`_ <list>)
list(`SORT`_ <list>)
list(`SORT`_ <list> [...])
Introduction
^^^^^^^^^^^^
@ -253,7 +253,23 @@ Reverses the contents of the list in-place.
::
list(SORT <list>)
list(SORT <list> [COMPARE <compare>] [CASE <case>] [ORDER <order>])
Sorts the list in-place alphabetically.
Use the option ``<compare>`` to select the compare type for sorting.
The ``<compare>`` option may be one of:
* ``STRING``: Sorts a list of strings alphabetically.
* ``FILE_BASENAME``: Sort a list of pathnames of files by their basenames.
Use the option ``<case>`` to select a case sensitive or case insensitive sort mode.
The ``<case>`` option may be one of:
* ``SENSITIVE``: Sorts the list alphabetically.
* ``INSENSITIVE``: Sorts the list alphabetically in descending order.
Use the option ``<order>`` to select a case sensitive or case insensitive sort mode.
The ``<order>`` option may be one of:
* ``ASCENDING``: Sorts the list in ascending order.
* ``DESCENDING``: Sorts the list in descending order.

View File

@ -0,0 +1,5 @@
list_sort
---------
* The :command:`list(SORT)` command gained options to control the
comparison operation used to order the entries.

View File

@ -963,14 +963,193 @@ bool cmListCommand::HandleTransformCommand(
return true;
}
class cmStringSorter
{
public:
enum class Order
{
UNINITIALIZED,
ASCENDING,
DESCENDING,
};
enum class Compare
{
UNINITIALIZED,
STRING,
FILE_BASENAME,
};
enum class CaseSensitivity
{
UNINITIALIZED,
SENSITIVE,
INSENSITIVE,
};
protected:
typedef std::string (*StringFilter)(const std::string& in);
StringFilter GetCompareFilter(Compare compare)
{
return (compare == Compare::FILE_BASENAME) ? cmSystemTools::GetFilenameName
: nullptr;
}
StringFilter GetCaseFilter(CaseSensitivity sensitivity)
{
return (sensitivity == CaseSensitivity::INSENSITIVE)
? cmSystemTools::LowerCase
: nullptr;
}
public:
cmStringSorter(Compare compare, CaseSensitivity caseSensitivity,
Order desc = Order::ASCENDING)
: filters{ GetCompareFilter(compare), GetCaseFilter(caseSensitivity) }
, descending(desc == Order::DESCENDING)
{
}
std::string ApplyFilter(const std::string& argument)
{
std::string result = argument;
for (auto filter : filters) {
if (filter != nullptr) {
result = filter(result);
}
}
return result;
}
bool operator()(const std::string& a, const std::string& b)
{
std::string af = ApplyFilter(a);
std::string bf = ApplyFilter(b);
bool result;
if (descending) {
result = bf < af;
} else {
result = af < bf;
}
return result;
}
protected:
StringFilter filters[2] = { nullptr, nullptr };
bool descending;
};
bool cmListCommand::HandleSortCommand(std::vector<std::string> const& args)
{
assert(args.size() >= 2);
if (args.size() > 2) {
this->SetError("sub-command SORT only takes one argument.");
if (args.size() > 8) {
this->SetError("sub-command SORT only takes up to six arguments.");
return false;
}
auto sortCompare = cmStringSorter::Compare::UNINITIALIZED;
auto sortCaseSensitivity = cmStringSorter::CaseSensitivity::UNINITIALIZED;
auto sortOrder = cmStringSorter::Order::UNINITIALIZED;
size_t argumentIndex = 2;
const std::string messageHint = "sub-command SORT ";
while (argumentIndex < args.size()) {
const std::string option = args[argumentIndex++];
if (option == "COMPARE") {
if (sortCompare != cmStringSorter::Compare::UNINITIALIZED) {
std::string error = messageHint + "option \"" + option +
"\" has been specified multiple times.";
this->SetError(error);
return false;
}
if (argumentIndex < args.size()) {
const std::string argument = args[argumentIndex++];
if (argument == "STRING") {
sortCompare = cmStringSorter::Compare::STRING;
} else if (argument == "FILE_BASENAME") {
sortCompare = cmStringSorter::Compare::FILE_BASENAME;
} else {
std::string error = messageHint + "value \"" + argument +
"\" for option \"" + option + "\" is invalid.";
this->SetError(error);
return false;
}
} else {
std::string error =
messageHint + "missing argument for option \"" + option + "\".";
this->SetError(error);
return false;
}
} else if (option == "CASE") {
if (sortCaseSensitivity !=
cmStringSorter::CaseSensitivity::UNINITIALIZED) {
std::string error = messageHint + "option \"" + option +
"\" has been specified multiple times.";
this->SetError(error);
return false;
}
if (argumentIndex < args.size()) {
const std::string argument = args[argumentIndex++];
if (argument == "SENSITIVE") {
sortCaseSensitivity = cmStringSorter::CaseSensitivity::SENSITIVE;
} else if (argument == "INSENSITIVE") {
sortCaseSensitivity = cmStringSorter::CaseSensitivity::INSENSITIVE;
} else {
std::string error = messageHint + "value \"" + argument +
"\" for option \"" + option + "\" is invalid.";
this->SetError(error);
return false;
}
} else {
std::string error =
messageHint + "missing argument for option \"" + option + "\".";
this->SetError(error);
return false;
}
} else if (option == "ORDER") {
if (sortOrder != cmStringSorter::Order::UNINITIALIZED) {
std::string error = messageHint + "option \"" + option +
"\" has been specified multiple times.";
this->SetError(error);
return false;
}
if (argumentIndex < args.size()) {
const std::string argument = args[argumentIndex++];
if (argument == "ASCENDING") {
sortOrder = cmStringSorter::Order::ASCENDING;
} else if (argument == "DESCENDING") {
sortOrder = cmStringSorter::Order::DESCENDING;
} else {
std::string error = messageHint + "value \"" + argument +
"\" for option \"" + option + "\" is invalid.";
this->SetError(error);
return false;
}
} else {
std::string error =
messageHint + "missing argument for option \"" + option + "\".";
this->SetError(error);
return false;
}
} else {
std::string error =
messageHint + "option \"" + option + "\" is unknown.";
this->SetError(error);
return false;
}
}
// set Default Values if Option is not given
if (sortCompare == cmStringSorter::Compare::UNINITIALIZED) {
sortCompare = cmStringSorter::Compare::STRING;
}
if (sortCaseSensitivity == cmStringSorter::CaseSensitivity::UNINITIALIZED) {
sortCaseSensitivity = cmStringSorter::CaseSensitivity::SENSITIVE;
}
if (sortOrder == cmStringSorter::Order::UNINITIALIZED) {
sortOrder = cmStringSorter::Order::ASCENDING;
}
const std::string& listName = args[1];
// expand the variable
std::vector<std::string> varArgsExpanded;
@ -979,7 +1158,14 @@ bool cmListCommand::HandleSortCommand(std::vector<std::string> const& args)
return false;
}
std::sort(varArgsExpanded.begin(), varArgsExpanded.end());
if ((sortCompare == cmStringSorter::Compare::STRING) &&
(sortCaseSensitivity == cmStringSorter::CaseSensitivity::SENSITIVE) &&
(sortOrder == cmStringSorter::Order::ASCENDING)) {
std::sort(varArgsExpanded.begin(), varArgsExpanded.end());
} else {
cmStringSorter sorter(sortCompare, sortCaseSensitivity, sortOrder);
std::sort(varArgsExpanded.begin(), varArgsExpanded.end(), sorter);
}
std::string value = cmJoin(varArgsExpanded, ";");
this->Makefile->AddDefinition(listName, value.c_str());

View File

@ -20,7 +20,6 @@ run_cmake(JOIN-TooManyArguments)
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)
@ -84,3 +83,16 @@ run_cmake(TRANSFORM-GENEX_STRIP)
run_cmake(TRANSFORM-APPEND)
run_cmake(TRANSFORM-PREPEND)
run_cmake(TRANSFORM-REPLACE)
# argument tests
run_cmake(SORT-WrongOption)
run_cmake(SORT-BadCaseOption)
run_cmake(SORT-BadCompareOption)
run_cmake(SORT-BadOrderOption)
run_cmake(SORT-DuplicateOrderOption)
run_cmake(SORT-DuplicateCompareOption)
run_cmake(SORT-DuplicateCaseOption)
run_cmake(SORT-NoCaseOption)
# Successful tests
run_cmake(SORT)

View File

@ -0,0 +1,4 @@
^CMake Error at SORT-BadCaseOption.cmake:1 \(list\):
list sub-command SORT value "BAD_CASE_OPTION" for option "CASE" is invalid.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)$

View File

@ -0,0 +1 @@
list(SORT mylist CASE BAD_CASE_OPTION)

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,5 @@
^CMake Error at SORT-BadCompareOption.cmake:1 \(list\):
list sub-command SORT value "BAD_COMPARE_OPTION" for option "COMPARE" is
invalid.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)$

View File

@ -0,0 +1 @@
list(SORT mylist COMPARE BAD_COMPARE_OPTION)

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,5 @@
^CMake Error at SORT-BadOrderOption.cmake:1 \(list\):
list sub-command SORT value "BAD_ODER_OPTION" for option "ORDER" is
invalid.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)$

View File

@ -0,0 +1 @@
list(SORT mylist ORDER BAD_ODER_OPTION)

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,4 @@
^CMake Error at SORT-DuplicateCaseOption.cmake:2 \(list\):
list sub-command SORT option "CASE" has been specified multiple times.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)$

View File

@ -0,0 +1,2 @@
set (mylist a b c)
list(SORT mylist CASE INSENSITIVE CASE INSENSITIVE )

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,4 @@
^CMake Error at SORT-DuplicateCompareOption.cmake:2 \(list\):
list sub-command SORT option "COMPARE" has been specified multiple times.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)$

View File

@ -0,0 +1,2 @@
set (mylist a b c)
list(SORT mylist COMPARE STRING COMPARE STRING)

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,4 @@
^CMake Error at SORT-DuplicateOrderOption.cmake:2 \(list\):
list sub-command SORT option "ORDER" has been specified multiple times.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)$

View File

@ -0,0 +1,2 @@
set (mylist a b c)
list(SORT mylist ORDER ASCENDING ORDER ASCENDING)

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,4 @@
^CMake Error at SORT-NoCaseOption.cmake:1 \(list\):
list sub-command SORT missing argument for option "CASE".
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)$

View File

@ -0,0 +1 @@
list(SORT mylist CASE)

View File

@ -1,4 +0,0 @@
^CMake Error at SORT-TooManyArguments.cmake:1 \(list\):
list sub-command SORT only takes one argument.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)$

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,4 @@
^CMake Error at SORT-WrongOption.cmake:1 \(list\):
list sub-command SORT option "one_too_many" is unknown.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)$

View File

@ -0,0 +1,114 @@
set(source_unsorted
c/B.h
a/c.h
B/a.h
)
## Test with default options
set(expected
B/a.h
a/c.h
c/B.h
)
set(list ${source_unsorted})
list(SORT list)
if (NOT expected STREQUAL list)
message(FATAL_ERROR "wrong sort result with command list(SORT list CASE SENSITIVE ORDER ASCENDING COMPARE STRING)")
endif ()
## Test CASE INSENSITIVE ORDER ASCENDING COMPARE STRING
set(expected
a/c.h
B/a.h
c/B.h
)
set(list ${source_unsorted})
list(SORT list CASE INSENSITIVE ORDER ASCENDING COMPARE STRING)
if (NOT expected STREQUAL list)
message(FATAL_ERROR "wrong sort result with command list(SORT list CASE INSENSITIVE ORDER ASCENDING COMPARE STRING)")
endif ()
## Test CASE INSENSITIVE ORDER DESCENDING COMPARE STRING
set(expected
c/B.h
B/a.h
a/c.h
)
set(list ${source_unsorted})
list(SORT list CASE INSENSITIVE ORDER DESCENDING COMPARE STRING)
if (NOT expected STREQUAL list)
message(FATAL_ERROR "wrong sort result with command list(SORT list CASE INSENSITIVE ORDER DESCENDING COMPARE STRING)")
endif ()
## Test CASE SENSITIVE ORDER ASCENDING COMPARE STRING
set(expected
B/a.h
a/c.h
c/B.h
)
set(list ${source_unsorted})
list(SORT list CASE SENSITIVE ORDER ASCENDING COMPARE STRING)
if (NOT expected STREQUAL list)
message(FATAL_ERROR "wrong sort result with command list(SORT list CASE SENSITIVE ORDER ASCENDING COMPARE STRING)")
endif ()
## Test CASE SENSITIVE ORDER DESCENDING COMPARE STRING
set(expected
c/B.h
a/c.h
B/a.h
)
set(list ${source_unsorted})
list(SORT list CASE SENSITIVE ORDER DESCENDING COMPARE STRING)
if (NOT expected STREQUAL list)
message(FATAL_ERROR "wrong sort result with command list(SORT list CASE SENSITIVE ORDER DESCENDING COMPARE STRING)")
endif ()
## Test CASE INSENSITIVE ORDER ASCENDING COMPARE FILE_BASENAME
set(expected
B/a.h
c/B.h
a/c.h
)
set(list ${source_unsorted})
list(SORT list CASE INSENSITIVE ORDER ASCENDING COMPARE FILE_BASENAME)
if (NOT expected STREQUAL list)
message(FATAL_ERROR "wrong sort result with command list(SORT list CASE INSENSITIVE ORDER ASCENDING COMPARE FILE_BASENAME)")
endif ()
## Test CASE INSENSITIVE ORDER DESCENDING COMPARE FILE_BASENAME
set(expected
a/c.h
c/B.h
B/a.h
)
set(list ${source_unsorted})
list(SORT list CASE INSENSITIVE ORDER DESCENDING COMPARE FILE_BASENAME)
if (NOT expected STREQUAL list)
message(FATAL_ERROR "wrong sort result with command list(SORT list CASE INSENSITIVE ORDER DESCENDING COMPARE FILE_BASENAME)")
endif ()
## Test CASE SENSITIVE ORDER ASCENDING COMPARE FILE_BASENAME
set(expected
c/B.h
B/a.h
a/c.h
)
set(list ${source_unsorted})
list(SORT list CASE SENSITIVE ORDER ASCENDING COMPARE FILE_BASENAME)
if (NOT expected STREQUAL list)
message(FATAL_ERROR "wrong sort result with command list(SORT list CASE SENSITIVE ORDER ASCENDING COMPARE FILE_BASENAME)")
endif ()
## Test CASE SENSITIVE ORDER DESCENDING COMPARE FILE_BASENAME
set(expected
a/c.h
B/a.h
c/B.h
)
set(list ${source_unsorted})
list(SORT list CASE SENSITIVE ORDER DESCENDING COMPARE FILE_BASENAME)
if (NOT expected STREQUAL list)
message(FATAL_ERROR "wrong sort result with command list(SORT list CASE SENSITIVE ORDER DESCENDING COMPARE FILE_BASENAME)")
endif ()