summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKyle Edwards <kyle.edwards@kitware.com>2019-02-08 17:14:36 (GMT)
committerKitware Robot <kwrobot@kitware.com>2019-02-08 17:14:46 (GMT)
commit18b0329a8165c92cf94b5b97c2425df090c877f0 (patch)
tree643f038699017234810725645ed61cc581464246
parent89ca5d7fdce7730f38a719683059bbe02e6fb19d (diff)
parent5228432b45b7ce740b93f2c80c8c7ce6987d4bf1 (diff)
downloadCMake-18b0329a8165c92cf94b5b97c2425df090c877f0.zip
CMake-18b0329a8165c92cf94b5b97c2425df090c877f0.tar.gz
CMake-18b0329a8165c92cf94b5b97c2425df090c877f0.tar.bz2
Merge topic 'cmake_parse_arguments-keywords_missing_values'
5228432b45 cmake_parse_arguments: add KEYWORDS_MISSING_VALUES 8e746db6e1 cmake_parse_arguments: Factor out part of implementation for re-use Acked-by: Kitware Robot <kwrobot@kitware.com> Merge-request: !2910
-rw-r--r--Help/command/cmake_parse_arguments.rst14
-rw-r--r--Help/release/dev/cmake_parse_arguments-keywords_missing_values.rst6
-rw-r--r--Source/cmParseArgumentsCommand.cxx136
-rw-r--r--Tests/RunCMake/cmake_parse_arguments/KeyWordsMissingValues.cmake133
-rw-r--r--Tests/RunCMake/cmake_parse_arguments/RunCMakeTest.cmake1
5 files changed, 246 insertions, 44 deletions
diff --git a/Help/command/cmake_parse_arguments.rst b/Help/command/cmake_parse_arguments.rst
index c8327e2..196d90f 100644
--- a/Help/command/cmake_parse_arguments.rst
+++ b/Help/command/cmake_parse_arguments.rst
@@ -59,6 +59,11 @@ All remaining arguments are collected in a variable
where recognized. This can be checked afterwards to see
whether your macro was called with unrecognized parameters.
+``<one_value_keywords>`` and ``<multi_value_keywords>`` that where given no
+values at all are collected in a variable ``<prefix>_KEYWORDS_MISSING_VALUES``
+that will be undefined if all keywords received values. This can be checked
+to see if there where keywords without any values given.
+
As an example here a ``my_install()`` macro, which takes similar arguments
as the real :command:`install` command:
@@ -77,7 +82,7 @@ Assume ``my_install()`` has been called like this:
.. code-block:: cmake
- my_install(TARGETS foo bar DESTINATION bin OPTIONAL blub)
+ my_install(TARGETS foo bar DESTINATION bin OPTIONAL blub CONFIGURATIONS)
After the ``cmake_parse_arguments`` call the macro will have set or undefined
the following variables::
@@ -89,6 +94,8 @@ the following variables::
MY_INSTALL_TARGETS = "foo;bar"
MY_INSTALL_CONFIGURATIONS <UNDEFINED> # was not used
MY_INSTALL_UNPARSED_ARGUMENTS = "blub" # nothing expected after "OPTIONAL"
+ MY_INSTALL_KEYWORDS_MISSING_VALUES = "CONFIGURATIONS"
+ # No value for "CONFIGURATIONS" given
You can then continue and process these variables.
@@ -97,5 +104,6 @@ one_value_keyword another recognized keyword follows, this is
interpreted as the beginning of the new option. E.g.
``my_install(TARGETS foo DESTINATION OPTIONAL)`` would result in
``MY_INSTALL_DESTINATION`` set to ``"OPTIONAL"``, but as ``OPTIONAL``
-is a keyword itself ``MY_INSTALL_DESTINATION`` will be empty and
-``MY_INSTALL_OPTIONAL`` will therefore be set to ``TRUE``.
+is a keyword itself ``MY_INSTALL_DESTINATION`` will be empty (but added
+to ``MY_INSTALL_KEYWORDS_MISSING_VALUES``) and ``MY_INSTALL_OPTIONAL`` will
+therefore be set to ``TRUE``.
diff --git a/Help/release/dev/cmake_parse_arguments-keywords_missing_values.rst b/Help/release/dev/cmake_parse_arguments-keywords_missing_values.rst
new file mode 100644
index 0000000..c7fe96b
--- /dev/null
+++ b/Help/release/dev/cmake_parse_arguments-keywords_missing_values.rst
@@ -0,0 +1,6 @@
+cmake_parse_arguments-keywords_missing_values
+---------------------------------------------
+
+* The :command:`cmake_parse_arguments` command gained an additional
+ ``<prefix>_KEYWORDS_MISSING_VALUES`` output variable to report
+ keyword arguments that were given by the caller with no values.
diff --git a/Source/cmParseArgumentsCommand.cxx b/Source/cmParseArgumentsCommand.cxx
index 796974c..6231aab 100644
--- a/Source/cmParseArgumentsCommand.cxx
+++ b/Source/cmParseArgumentsCommand.cxx
@@ -14,7 +14,7 @@
class cmExecutionStatus;
-static std::string escape_arg(const std::string& arg)
+static std::string EscapeArg(const std::string& arg)
{
// replace ";" with "\;" so output argument lists will split correctly
std::string escapedArg;
@@ -27,6 +27,81 @@ static std::string escape_arg(const std::string& arg)
return escapedArg;
}
+namespace {
+enum insideValues
+{
+ NONE,
+ SINGLE,
+ MULTI
+};
+
+typedef std::map<std::string, bool> options_map;
+typedef std::map<std::string, std::string> single_map;
+typedef std::map<std::string, std::vector<std::string>> multi_map;
+typedef std::set<std::string> options_set;
+}
+
+// function to be called every time, a new key word was parsed or all
+// parameters where parsed.
+static void DetectKeywordsMissingValues(insideValues currentState,
+ const std::string& currentArgName,
+ int& argumentsFound,
+ options_set& keywordsMissingValues)
+{
+ if (currentState == SINGLE ||
+ (currentState == MULTI && argumentsFound == 0)) {
+ keywordsMissingValues.insert(currentArgName);
+ }
+
+ argumentsFound = 0;
+}
+
+static void PassParsedArguments(const std::string& prefix,
+ cmMakefile& makefile,
+ const options_map& options,
+ const single_map& singleValArgs,
+ const multi_map& multiValArgs,
+ const std::vector<std::string>& unparsed,
+ const options_set& keywordsMissingValues)
+{
+ for (auto const& iter : options) {
+ makefile.AddDefinition(prefix + iter.first,
+ iter.second ? "TRUE" : "FALSE");
+ }
+
+ for (auto const& iter : singleValArgs) {
+ if (!iter.second.empty()) {
+ makefile.AddDefinition(prefix + iter.first, iter.second.c_str());
+ } else {
+ makefile.RemoveDefinition(prefix + iter.first);
+ }
+ }
+
+ for (auto const& iter : multiValArgs) {
+ if (!iter.second.empty()) {
+ makefile.AddDefinition(prefix + iter.first,
+ cmJoin(cmMakeRange(iter.second), ";").c_str());
+ } else {
+ makefile.RemoveDefinition(prefix + iter.first);
+ }
+ }
+
+ if (!unparsed.empty()) {
+ makefile.AddDefinition(prefix + "UNPARSED_ARGUMENTS",
+ cmJoin(cmMakeRange(unparsed), ";").c_str());
+ } else {
+ makefile.RemoveDefinition(prefix + "UNPARSED_ARGUMENTS");
+ }
+
+ if (!keywordsMissingValues.empty()) {
+ makefile.AddDefinition(
+ prefix + "KEYWORDS_MISSING_VALUES",
+ cmJoin(cmMakeRange(keywordsMissingValues), ";").c_str());
+ } else {
+ makefile.RemoveDefinition(prefix + "KEYWORDS_MISSING_VALUES");
+ }
+}
+
bool cmParseArgumentsCommand::InitialPass(std::vector<std::string> const& args,
cmExecutionStatus&)
{
@@ -67,9 +142,6 @@ bool cmParseArgumentsCommand::InitialPass(std::vector<std::string> const& args,
// define the result maps holding key/value pairs for
// options, single values and multi values
- typedef std::map<std::string, bool> options_map;
- typedef std::map<std::string, std::string> single_map;
- typedef std::map<std::string, std::vector<std::string>> multi_map;
options_map options;
single_map singleValArgs;
multi_map multiValArgs;
@@ -114,12 +186,7 @@ bool cmParseArgumentsCommand::InitialPass(std::vector<std::string> const& args,
multiValArgs[iter]; // default initialize
}
- enum insideValues
- {
- NONE,
- SINGLE,
- MULTI
- } insideValues = NONE;
+ insideValues insideValues = NONE;
std::string currentArgName;
list.clear();
@@ -155,10 +222,15 @@ bool cmParseArgumentsCommand::InitialPass(std::vector<std::string> const& args,
}
}
+ options_set keywordsMissingValues;
+ int multiArgumentsFound = 0;
+
// iterate over the arguments list and fill in the values where applicable
for (std::string const& arg : list) {
const options_map::iterator optIter = options.find(arg);
if (optIter != options.end()) {
+ DetectKeywordsMissingValues(insideValues, currentArgName,
+ multiArgumentsFound, keywordsMissingValues);
insideValues = NONE;
optIter->second = true;
continue;
@@ -166,6 +238,8 @@ bool cmParseArgumentsCommand::InitialPass(std::vector<std::string> const& args,
const single_map::iterator singleIter = singleValArgs.find(arg);
if (singleIter != singleValArgs.end()) {
+ DetectKeywordsMissingValues(insideValues, currentArgName,
+ multiArgumentsFound, keywordsMissingValues);
insideValues = SINGLE;
currentArgName = arg;
continue;
@@ -173,6 +247,8 @@ bool cmParseArgumentsCommand::InitialPass(std::vector<std::string> const& args,
const multi_map::iterator multiIter = multiValArgs.find(arg);
if (multiIter != multiValArgs.end()) {
+ DetectKeywordsMissingValues(insideValues, currentArgName,
+ multiArgumentsFound, keywordsMissingValues);
insideValues = MULTI;
currentArgName = arg;
continue;
@@ -184,15 +260,18 @@ bool cmParseArgumentsCommand::InitialPass(std::vector<std::string> const& args,
insideValues = NONE;
break;
case MULTI:
+ ++multiArgumentsFound;
if (parseFromArgV) {
- multiValArgs[currentArgName].push_back(escape_arg(arg));
+ multiValArgs[currentArgName].push_back(EscapeArg(arg));
} else {
multiValArgs[currentArgName].push_back(arg);
}
break;
default:
+ multiArgumentsFound = 0;
+
if (parseFromArgV) {
- unparsed.push_back(escape_arg(arg));
+ unparsed.push_back(EscapeArg(arg));
} else {
unparsed.push_back(arg);
}
@@ -200,36 +279,11 @@ bool cmParseArgumentsCommand::InitialPass(std::vector<std::string> const& args,
}
}
- // now iterate over the collected values and update their definition
- // within the current scope. undefine if necessary.
-
- for (auto const& iter : options) {
- this->Makefile->AddDefinition(prefix + iter.first,
- iter.second ? "TRUE" : "FALSE");
- }
- for (auto const& iter : singleValArgs) {
- if (!iter.second.empty()) {
- this->Makefile->AddDefinition(prefix + iter.first, iter.second.c_str());
- } else {
- this->Makefile->RemoveDefinition(prefix + iter.first);
- }
- }
-
- for (auto const& iter : multiValArgs) {
- if (!iter.second.empty()) {
- this->Makefile->AddDefinition(
- prefix + iter.first, cmJoin(cmMakeRange(iter.second), ";").c_str());
- } else {
- this->Makefile->RemoveDefinition(prefix + iter.first);
- }
- }
+ DetectKeywordsMissingValues(insideValues, currentArgName,
+ multiArgumentsFound, keywordsMissingValues);
- if (!unparsed.empty()) {
- this->Makefile->AddDefinition(prefix + "UNPARSED_ARGUMENTS",
- cmJoin(cmMakeRange(unparsed), ";").c_str());
- } else {
- this->Makefile->RemoveDefinition(prefix + "UNPARSED_ARGUMENTS");
- }
+ PassParsedArguments(prefix, *this->Makefile, options, singleValArgs,
+ multiValArgs, unparsed, keywordsMissingValues);
return true;
}
diff --git a/Tests/RunCMake/cmake_parse_arguments/KeyWordsMissingValues.cmake b/Tests/RunCMake/cmake_parse_arguments/KeyWordsMissingValues.cmake
new file mode 100644
index 0000000..561f9c0
--- /dev/null
+++ b/Tests/RunCMake/cmake_parse_arguments/KeyWordsMissingValues.cmake
@@ -0,0 +1,133 @@
+include(${CMAKE_CURRENT_LIST_DIR}/test_utils.cmake)
+
+# No keywords that miss any values, _KEYWORDS_MISSING_VALUES should not be defined
+cmake_parse_arguments(PREF "" "P1" "P2" P1 p1 P2 p2_a p2_b)
+
+TEST(PREF_KEYWORDS_MISSING_VALUES "UNDEFINED")
+
+# Keyword should even be deleted from the actual scope
+set(PREF_KEYWORDS_MISSING_VALUES "What ever")
+cmake_parse_arguments(PREF "" "" "")
+
+TEST(PREF_KEYWORDS_MISSING_VALUES "UNDEFINED")
+
+# Given missing keywords as only option
+cmake_parse_arguments(PREF "" "P1" "P2" P1)
+
+TEST(PREF_KEYWORDS_MISSING_VALUES "P1")
+TEST(PREF_P1 "UNDEFINED")
+TEST(PREF_UNPARSED_ARGUMENTS "UNDEFINED")
+
+# Mixed with unparsed arguments
+cmake_parse_arguments(UPREF "" "P1" "P2" A B P2 C P1)
+TEST(UPREF_KEYWORDS_MISSING_VALUES "P1")
+TEST(UPREF_UNPARSED_ARGUMENTS A B)
+
+# one_value_keyword followed by option
+cmake_parse_arguments(REF "OP" "P1" "" P1 OP)
+TEST(REF_KEYWORDS_MISSING_VALUES "P1")
+TEST(REF_UNPARSED_ARGUMENTS "UNDEFINED")
+TEST(REF_OP "TRUE")
+
+# Counter Test
+cmake_parse_arguments(REF "OP" "P1" "" P1 p1 OP)
+TEST(REF_KEYWORDS_MISSING_VALUES "UNDEFINED")
+TEST(REF_P1 "p1")
+TEST(REF_UNPARSED_ARGUMENTS "UNDEFINED")
+TEST(REF_OP "TRUE")
+
+# one_value_keyword followed by a one_value_keyword
+cmake_parse_arguments(REF "" "P1;P2" "" P1 P2 p2)
+TEST(REF_KEYWORDS_MISSING_VALUES "P1")
+TEST(REF_P1 "UNDEFINED")
+TEST(REF_UNPARSED_ARGUMENTS "UNDEFINED")
+TEST(REF_P2 "p2")
+
+# Counter Test
+cmake_parse_arguments(REF "" "P1;P2" "" P1 p1 P2 p2)
+TEST(REF_KEYWORDS_MISSING_VALUES "UNDEFINED")
+TEST(REF_P1 "p1")
+TEST(REF_UNPARSED_ARGUMENTS "UNDEFINED")
+TEST(REF_P2 "p2")
+
+# one_value_keyword followed by a multi_value_keywords
+cmake_parse_arguments(REF "" "P1" "P2" P1 P2 p1 p2)
+TEST(REF_KEYWORDS_MISSING_VALUES "P1")
+TEST(REF_P1 "UNDEFINED")
+TEST(REF_UNPARSED_ARGUMENTS "UNDEFINED")
+TEST(REF_P2 p1 p2)
+
+# Counter Examples
+cmake_parse_arguments(REF "" "P1" "P2" P1 p1 P2 p1 p2)
+TEST(REF_KEYWORDS_MISSING_VALUES "UNDEFINED")
+TEST(REF_P1 "p1")
+TEST(REF_UNPARSED_ARGUMENTS "UNDEFINED")
+TEST(REF_P2 p1 p2)
+
+# multi_value_keywords as only option
+cmake_parse_arguments(REF "" "P1" "P2" P2)
+TEST(REF_KEYWORDS_MISSING_VALUES "P2")
+TEST(REF_P1 "UNDEFINED")
+TEST(REF_UNPARSED_ARGUMENTS "UNDEFINED")
+TEST(REF_P2 "UNDEFINED")
+
+# multi_value_keywords followed by option
+cmake_parse_arguments(REF "O1" "" "P1" P1 O1)
+TEST(REF_KEYWORDS_MISSING_VALUES "P1")
+TEST(REF_P1 "UNDEFINED")
+TEST(REF_UNPARSED_ARGUMENTS "UNDEFINED")
+TEST(REF_O1 "TRUE")
+
+# counter test
+cmake_parse_arguments(REF "O1" "" "P1" P1 p1 p2 O1)
+TEST(REF_KEYWORDS_MISSING_VALUES "UNDEFINED")
+TEST(REF_P1 "p1;p2")
+TEST(REF_UNPARSED_ARGUMENTS "UNDEFINED")
+TEST(REF_O1 "TRUE")
+
+# multi_value_keywords followed by one_value_keyword
+cmake_parse_arguments(REF "" "P1" "P2" P2 P1 p1)
+TEST(REF_KEYWORDS_MISSING_VALUES "P2")
+TEST(REF_P1 "p1")
+TEST(REF_UNPARSED_ARGUMENTS "UNDEFINED")
+TEST(REF_P2 "UNDEFINED")
+
+# counter test
+cmake_parse_arguments(REF "" "P1" "P2" P2 p2 P1 p1)
+TEST(REF_KEYWORDS_MISSING_VALUES "UNDEFINED")
+TEST(REF_P1 "p1")
+TEST(REF_UNPARSED_ARGUMENTS "UNDEFINED")
+TEST(REF_P2 "p2")
+
+# one_value_keyword as last argument
+cmake_parse_arguments(REF "" "P1" "P2" A P2 p2 P1)
+TEST(REF_KEYWORDS_MISSING_VALUES "P1")
+TEST(REF_P1 "UNDEFINED")
+TEST(REF_UNPARSED_ARGUMENTS "A")
+TEST(REF_P2 "p2")
+
+# multi_value_keywords as last argument
+cmake_parse_arguments(REF "" "P1" "P2" P1 p1 P2)
+TEST(REF_KEYWORDS_MISSING_VALUES "P2")
+TEST(REF_P1 "p1")
+TEST(REF_P2 "UNDEFINED")
+
+# Multiple one_value_keyword and multi_value_keywords at different places
+cmake_parse_arguments(REF "O1;O2" "P1" "P2" P1 O1 P2 O2)
+TEST(REF_KEYWORDS_MISSING_VALUES P1 P2)
+TEST(REF_P1 "UNDEFINED")
+TEST(REF_P2 "UNDEFINED")
+
+# Duplicated missing keywords
+cmake_parse_arguments(REF "O1;O2" "P1" "P2" P1 O1 P2 O2 P1 P2)
+TEST(REF_KEYWORDS_MISSING_VALUES P1 P2)
+TEST(REF_P1 "UNDEFINED")
+TEST(REF_P2 "UNDEFINED")
+
+# make sure keywords that are never used, don't get added to KEYWORDS_MISSING_VALUES
+cmake_parse_arguments(REF "O1;O2" "P1" "P2")
+TEST(REF_KEYWORDS_MISSING_VALUES "UNDEFINED")
+TEST(REF_O1 FALSE)
+TEST(REF_O2 FALSE)
+TEST(REF_P1 UNDEFINED)
+TEST(REF_P2 UNDEFINED)
diff --git a/Tests/RunCMake/cmake_parse_arguments/RunCMakeTest.cmake b/Tests/RunCMake/cmake_parse_arguments/RunCMakeTest.cmake
index 1e15b3b..505840d 100644
--- a/Tests/RunCMake/cmake_parse_arguments/RunCMakeTest.cmake
+++ b/Tests/RunCMake/cmake_parse_arguments/RunCMakeTest.cmake
@@ -11,3 +11,4 @@ run_cmake(BadArgvN2)
run_cmake(BadArgvN3)
run_cmake(BadArgvN4)
run_cmake(CornerCasesArgvN)
+run_cmake(KeyWordsMissingValues)