summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2015-12-18 15:12:35 (GMT)
committerCMake Topic Stage <kwrobot@kitware.com>2015-12-18 15:12:35 (GMT)
commit4395190cd474f3e83139961d1271c766604e4a1c (patch)
treec93532b66be27e32724705df3a57fb0101ac4e17
parent614c8a1c920083c3ae76ff035168f7096db6c510 (diff)
parentab8a280857da5cf8545bd2a6f69b7378f53449a5 (diff)
downloadCMake-4395190cd474f3e83139961d1271c766604e4a1c.zip
CMake-4395190cd474f3e83139961d1271c766604e4a1c.tar.gz
CMake-4395190cd474f3e83139961d1271c766604e4a1c.tar.bz2
Merge topic 'CMakeParseArguments-native-impl'
ab8a2808 cmake_parse_arguments: consider duplicate keyword as warning e8b14831 CMakeParseArguments: replace by native cmake_parse_arguments command cbbdfc2b CMakeParseArguments: add a RunCMake test suite
-rw-r--r--Help/command/cmake_parse_arguments.rst85
-rw-r--r--Help/manual/cmake-commands.7.rst1
-rw-r--r--Help/release/dev/CMakeParseArguments-native-impl.rst6
-rw-r--r--Modules/CMakeParseArguments.cmake148
-rw-r--r--Source/cmBootstrapCommands1.cxx2
-rw-r--r--Source/cmParseArgumentsCommand.cxx192
-rw-r--r--Source/cmParseArgumentsCommand.h54
-rw-r--r--Tests/RunCMake/CMakeLists.txt1
-rw-r--r--Tests/RunCMake/cmake_parse_arguments/CMakeLists.txt3
-rw-r--r--Tests/RunCMake/cmake_parse_arguments/CornerCases.cmake15
-rw-r--r--Tests/RunCMake/cmake_parse_arguments/Errors-result.txt1
-rw-r--r--Tests/RunCMake/cmake_parse_arguments/Errors-stderr.txt44
-rw-r--r--Tests/RunCMake/cmake_parse_arguments/Errors.cmake14
-rw-r--r--Tests/RunCMake/cmake_parse_arguments/Initialization.cmake68
-rw-r--r--Tests/RunCMake/cmake_parse_arguments/Mix.cmake24
-rw-r--r--Tests/RunCMake/cmake_parse_arguments/RunCMakeTest.cmake7
-rw-r--r--Tests/RunCMake/cmake_parse_arguments/Utils.cmake20
-rw-r--r--Tests/RunCMake/cmake_parse_arguments/test_utils.cmake20
18 files changed, 561 insertions, 144 deletions
diff --git a/Help/command/cmake_parse_arguments.rst b/Help/command/cmake_parse_arguments.rst
new file mode 100644
index 0000000..6206611
--- /dev/null
+++ b/Help/command/cmake_parse_arguments.rst
@@ -0,0 +1,85 @@
+cmake_parse_arguments
+---------------------
+
+``cmake_parse_arguments`` is intended to be used in macros or functions for
+parsing the arguments given to that macro or function. It processes the
+arguments and defines a set of variables which hold the values of the
+respective options.
+
+::
+
+ cmake_parse_arguments(<prefix> <options> <one_value_keywords>
+ <multi_value_keywords> args...)
+
+
+The ``<options>`` argument contains all options for the respective macro,
+i.e. keywords which can be used when calling the macro without any value
+following, like e.g. the ``OPTIONAL`` keyword of the :command:`install`
+command.
+
+The ``<one_value_keywords>`` argument contains all keywords for this macro
+which are followed by one value, like e.g. ``DESTINATION`` keyword of the
+:command:`install` command.
+
+The ``<multi_value_keywords>`` argument contains all keywords for this
+macro which can be followed by more than one value, like e.g. the
+``TARGETS`` or ``FILES`` keywords of the :command:`install` command.
+
+.. note::
+
+ All keywords shall be unique. I.e. every keyword shall only be specified
+ once in either ``<options>``, ``<one_value_keywords>`` or
+ ``<multi_value_keywords>``. A warning will be emitted if uniqueness is
+ violated.
+
+When done, ``cmake_parse_arguments`` will have defined for each of the
+keywords listed in ``<options>``, ``<one_value_keywords>`` and
+``<multi_value_keywords>`` a variable composed of the given ``<prefix>``
+followed by ``"_"`` and the name of the respective keyword. These
+variables will then hold the respective value from the argument list.
+For the ``<options>`` keywords this will be ``TRUE`` or ``FALSE``.
+
+All remaining arguments are collected in a variable
+``<prefix>_UNPARSED_ARGUMENTS``, this can be checked afterwards to see
+whether your macro was called with unrecognized parameters.
+
+As an example here a ``my_install()`` macro, which takes similar arguments
+as the real :command:`install` command:
+
+.. code-block:: cmake
+
+ function(MY_INSTALL)
+ set(options OPTIONAL FAST)
+ set(oneValueArgs DESTINATION RENAME)
+ set(multiValueArgs TARGETS CONFIGURATIONS)
+ cmake_parse_arguments(MY_INSTALL "${options}" "${oneValueArgs}"
+ "${multiValueArgs}" ${ARGN} )
+
+ # ...
+
+Assume ``my_install()`` has been called like this:
+
+.. code-block:: cmake
+
+ my_install(TARGETS foo bar DESTINATION bin OPTIONAL blub)
+
+After the ``cmake_parse_arguments`` call the macro will have set the
+following variables::
+
+ MY_INSTALL_OPTIONAL = TRUE
+ MY_INSTALL_FAST = FALSE (was not used in call to my_install)
+ MY_INSTALL_DESTINATION = "bin"
+ MY_INSTALL_RENAME = "" (was not used)
+ MY_INSTALL_TARGETS = "foo;bar"
+ MY_INSTALL_CONFIGURATIONS = "" (was not used)
+ MY_INSTALL_UNPARSED_ARGUMENTS = "blub" (nothing expected after "OPTIONAL")
+
+You can then continue and process these variables.
+
+Keywords terminate lists of values, e.g. if directly after a
+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``.
diff --git a/Help/manual/cmake-commands.7.rst b/Help/manual/cmake-commands.7.rst
index 5b92b51..d0c2986 100644
--- a/Help/manual/cmake-commands.7.rst
+++ b/Help/manual/cmake-commands.7.rst
@@ -29,6 +29,7 @@ These commands may be used freely in CMake projects.
/command/build_command
/command/cmake_host_system_information
/command/cmake_minimum_required
+ /command/cmake_parse_arguments
/command/cmake_policy
/command/configure_file
/command/continue
diff --git a/Help/release/dev/CMakeParseArguments-native-impl.rst b/Help/release/dev/CMakeParseArguments-native-impl.rst
new file mode 100644
index 0000000..114a099
--- /dev/null
+++ b/Help/release/dev/CMakeParseArguments-native-impl.rst
@@ -0,0 +1,6 @@
+CMakeParseArguments-native-impl
+-------------------------------
+
+* The :command:`cmake_parse_arguments` command is now implemented natively.
+ The :module:`CMakeParseArguments` module remains as an empty placeholder
+ for compatibility.
diff --git a/Modules/CMakeParseArguments.cmake b/Modules/CMakeParseArguments.cmake
index 8553f38..fc64ab9 100644
--- a/Modules/CMakeParseArguments.cmake
+++ b/Modules/CMakeParseArguments.cmake
@@ -2,86 +2,10 @@
# CMakeParseArguments
# -------------------
#
-#
-#
-# CMAKE_PARSE_ARGUMENTS(<prefix> <options> <one_value_keywords>
-# <multi_value_keywords> args...)
-#
-# CMAKE_PARSE_ARGUMENTS() is intended to be used in macros or functions
-# for parsing the arguments given to that macro or function. It
-# processes the arguments and defines a set of variables which hold the
-# values of the respective options.
-#
-# The <options> argument contains all options for the respective macro,
-# i.e. keywords which can be used when calling the macro without any
-# value following, like e.g. the OPTIONAL keyword of the install()
-# command.
-#
-# The <one_value_keywords> argument contains all keywords for this macro
-# which are followed by one value, like e.g. DESTINATION keyword of the
-# install() command.
-#
-# The <multi_value_keywords> argument contains all keywords for this
-# macro which can be followed by more than one value, like e.g. the
-# TARGETS or FILES keywords of the install() command.
-#
-# When done, CMAKE_PARSE_ARGUMENTS() will have defined for each of the
-# keywords listed in <options>, <one_value_keywords> and
-# <multi_value_keywords> a variable composed of the given <prefix>
-# followed by "_" and the name of the respective keyword. These
-# variables will then hold the respective value from the argument list.
-# For the <options> keywords this will be TRUE or FALSE.
-#
-# All remaining arguments are collected in a variable
-# <prefix>_UNPARSED_ARGUMENTS, this can be checked afterwards to see
-# whether your macro was called with unrecognized parameters.
-#
-# As an example here a my_install() macro, which takes similar arguments
-# as the real install() command:
-#
-# ::
-#
-# function(MY_INSTALL)
-# set(options OPTIONAL FAST)
-# set(oneValueArgs DESTINATION RENAME)
-# set(multiValueArgs TARGETS CONFIGURATIONS)
-# cmake_parse_arguments(MY_INSTALL "${options}" "${oneValueArgs}"
-# "${multiValueArgs}" ${ARGN} )
-# ...
-#
-#
-#
-# Assume my_install() has been called like this:
-#
-# ::
-#
-# my_install(TARGETS foo bar DESTINATION bin OPTIONAL blub)
-#
-#
-#
-# After the cmake_parse_arguments() call the macro will have set the
-# following variables:
-#
-# ::
-#
-# MY_INSTALL_OPTIONAL = TRUE
-# MY_INSTALL_FAST = FALSE (this option was not used when calling my_install()
-# MY_INSTALL_DESTINATION = "bin"
-# MY_INSTALL_RENAME = "" (was not used)
-# MY_INSTALL_TARGETS = "foo;bar"
-# MY_INSTALL_CONFIGURATIONS = "" (was not used)
-# MY_INSTALL_UNPARSED_ARGUMENTS = "blub" (no value expected after "OPTIONAL"
-#
-#
-#
-# You can then continue and process these variables.
-#
-# Keywords terminate lists of values, e.g. if directly after a
-# 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 MY_INSTALL_DESTINATION
-# would be empty and MY_INSTALL_OPTIONAL would be set to TRUE therefor.
+# This module once implemented the :command:`cmake_parse_arguments` command
+# that is now implemented natively by CMake. It is now an empty placeholder
+# for compatibility with projects that include it to get the command from
+# CMake 3.4 and lower.
#=============================================================================
# Copyright 2010 Alexander Neundorf <neundorf@kde.org>
@@ -95,67 +19,3 @@
#=============================================================================
# (To distribute this file outside of CMake, substitute the full
# License text for the above reference.)
-
-
-if(__CMAKE_PARSE_ARGUMENTS_INCLUDED)
- return()
-endif()
-set(__CMAKE_PARSE_ARGUMENTS_INCLUDED TRUE)
-
-
-function(CMAKE_PARSE_ARGUMENTS prefix _optionNames _singleArgNames _multiArgNames)
- # first set all result variables to empty/FALSE
- foreach(arg_name ${_singleArgNames} ${_multiArgNames})
- set(${prefix}_${arg_name})
- endforeach()
-
- foreach(option ${_optionNames})
- set(${prefix}_${option} FALSE)
- endforeach()
-
- set(${prefix}_UNPARSED_ARGUMENTS)
-
- set(insideValues FALSE)
- set(currentArgName)
-
- # now iterate over all arguments and fill the result variables
- foreach(currentArg ${ARGN})
- list(FIND _optionNames "${currentArg}" optionIndex) # ... then this marks the end of the arguments belonging to this keyword
- list(FIND _singleArgNames "${currentArg}" singleArgIndex) # ... then this marks the end of the arguments belonging to this keyword
- list(FIND _multiArgNames "${currentArg}" multiArgIndex) # ... then this marks the end of the arguments belonging to this keyword
-
- if(${optionIndex} EQUAL -1 AND ${singleArgIndex} EQUAL -1 AND ${multiArgIndex} EQUAL -1)
- if(insideValues)
- if("${insideValues}" STREQUAL "SINGLE")
- set(${prefix}_${currentArgName} ${currentArg})
- set(insideValues FALSE)
- elseif("${insideValues}" STREQUAL "MULTI")
- list(APPEND ${prefix}_${currentArgName} ${currentArg})
- endif()
- else()
- list(APPEND ${prefix}_UNPARSED_ARGUMENTS ${currentArg})
- endif()
- else()
- if(NOT ${optionIndex} EQUAL -1)
- set(${prefix}_${currentArg} TRUE)
- set(insideValues FALSE)
- elseif(NOT ${singleArgIndex} EQUAL -1)
- set(currentArgName ${currentArg})
- set(${prefix}_${currentArgName})
- set(insideValues "SINGLE")
- elseif(NOT ${multiArgIndex} EQUAL -1)
- set(currentArgName ${currentArg})
- set(${prefix}_${currentArgName})
- set(insideValues "MULTI")
- endif()
- endif()
-
- endforeach()
-
- # propagate the result variables to the caller:
- foreach(arg_name ${_singleArgNames} ${_multiArgNames} ${_optionNames})
- set(${prefix}_${arg_name} ${${prefix}_${arg_name}} PARENT_SCOPE)
- endforeach()
- set(${prefix}_UNPARSED_ARGUMENTS ${${prefix}_UNPARSED_ARGUMENTS} PARENT_SCOPE)
-
-endfunction()
diff --git a/Source/cmBootstrapCommands1.cxx b/Source/cmBootstrapCommands1.cxx
index 1184514..0782b3b 100644
--- a/Source/cmBootstrapCommands1.cxx
+++ b/Source/cmBootstrapCommands1.cxx
@@ -54,6 +54,7 @@
#include "cmFunctionCommand.cxx"
#include "cmPathLabel.cxx"
#include "cmSearchPath.cxx"
+#include "cmParseArgumentsCommand.cxx"
void GetBootstrapCommands1(std::vector<cmCommand*>& commands)
{
@@ -91,4 +92,5 @@ void GetBootstrapCommands1(std::vector<cmCommand*>& commands)
commands.push_back(new cmFindProgramCommand);
commands.push_back(new cmForEachCommand);
commands.push_back(new cmFunctionCommand);
+ commands.push_back(new cmParseArgumentsCommand);
}
diff --git a/Source/cmParseArgumentsCommand.cxx b/Source/cmParseArgumentsCommand.cxx
new file mode 100644
index 0000000..a861965
--- /dev/null
+++ b/Source/cmParseArgumentsCommand.cxx
@@ -0,0 +1,192 @@
+/*============================================================================
+ CMake - Cross Platform Makefile Generator
+ Copyright 2015 Matthias Maennich <matthias@maennich.net>
+ Copyright 2010 Alexander Neundorf <neundorf@kde.org>
+
+ Distributed under the OSI-approved BSD License (the "License");
+ see accompanying file Copyright.txt for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even the
+ implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the License for more information.
+============================================================================*/
+
+#include "cmParseArgumentsCommand.h"
+#include "cmAlgorithms.h"
+
+//----------------------------------------------------------------------------
+bool cmParseArgumentsCommand
+::InitialPass(std::vector<std::string> const& args, cmExecutionStatus &)
+{
+ // cmake_parse_arguments(prefix options single multi <ARGN>)
+ // 1 2 3 4
+ if (args.size() < 4)
+ {
+ this->SetError("must be called with at least 4 arguments.");
+ return false;
+ }
+
+ std::vector<std::string>::const_iterator argIter = args.begin(),
+ argEnd = args.end();
+ // the first argument is the prefix
+ const std::string prefix = (*argIter++) + "_";
+
+ // 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 single;
+ multi_map multi;
+
+ // anything else is put into a vector of unparsed strings
+ std::vector<std::string> unparsed;
+
+ // remember already defined keywords
+ std::set<std::string> used_keywords;
+ const std::string dup_warning = "keyword defined more than once: ";
+
+ // the second argument is a (cmake) list of options without argument
+ std::vector<std::string> list;
+ cmSystemTools::ExpandListArgument(*argIter++, list);
+ for (std::vector<std::string>::const_iterator iter = list.begin(),
+ end = list.end();
+ iter != end; ++iter)
+ {
+ if (!used_keywords.insert(*iter).second)
+ {
+ this->GetMakefile()->IssueMessage(cmake::WARNING, dup_warning + *iter);
+ }
+ options[*iter]; // default initialize
+ }
+
+ // the third argument is a (cmake) list of single argument options
+ list.clear();
+ cmSystemTools::ExpandListArgument(*argIter++, list);
+ for (std::vector<std::string>::const_iterator iter = list.begin(),
+ end = list.end();
+ iter != end; ++iter)
+ {
+ if (!used_keywords.insert(*iter).second)
+ {
+ this->GetMakefile()->IssueMessage(cmake::WARNING, dup_warning + *iter);
+ }
+ single[*iter]; // default initialize
+ }
+
+ // the fourth argument is a (cmake) list of multi argument options
+ list.clear();
+ cmSystemTools::ExpandListArgument(*argIter++, list);
+ for (std::vector<std::string>::const_iterator iter = list.begin(),
+ end = list.end();
+ iter != end; ++iter)
+ {
+ if (!used_keywords.insert(*iter).second)
+ {
+ this->GetMakefile()->IssueMessage(cmake::WARNING, dup_warning + *iter);
+ }
+ multi[*iter]; // default initialize
+ }
+
+ enum insideValues
+ {
+ NONE,
+ SINGLE,
+ MULTI
+ } insideValues = NONE;
+ std::string currentArgName;
+
+ // now iterate over the remaining arguments
+ // and fill in the values where applicable
+ for(; argIter != argEnd; ++argIter)
+ {
+ const options_map::iterator optIter = options.find(*argIter);
+ if (optIter != options.end())
+ {
+ insideValues = NONE;
+ optIter->second = true;
+ continue;
+ }
+
+ const single_map::iterator singleIter = single.find(*argIter);
+ if (singleIter != single.end())
+ {
+ insideValues = SINGLE;
+ currentArgName = *argIter;
+ continue;
+ }
+
+ const multi_map::iterator multiIter = multi.find(*argIter);
+ if (multiIter != multi.end())
+ {
+ insideValues = MULTI;
+ currentArgName = *argIter;
+ continue;
+ }
+
+ switch(insideValues)
+ {
+ case SINGLE:
+ single[currentArgName] = *argIter;
+ insideValues = NONE;
+ break;
+ case MULTI:
+ multi[currentArgName].push_back(*argIter);
+ break;
+ default:
+ unparsed.push_back(*argIter);
+ break;
+ }
+ }
+
+ // now iterate over the collected values and update their definition
+ // within the current scope. undefine if necessary.
+
+ for (options_map::const_iterator iter = options.begin(), end = options.end();
+ iter != end; ++iter)
+ {
+ this->Makefile->AddDefinition(prefix + iter->first,
+ iter->second? "TRUE": "FALSE");
+ }
+ for (single_map::const_iterator iter = single.begin(), end = single.end();
+ iter != end; ++iter)
+ {
+ if (!iter->second.empty())
+ {
+ this->Makefile->AddDefinition(prefix + iter->first,
+ iter->second.c_str());
+ }
+ else
+ {
+ this->Makefile->RemoveDefinition(prefix + iter->first);
+ }
+ }
+
+ for (multi_map::const_iterator iter = multi.begin(), end = multi.end();
+ iter != end; ++iter)
+ {
+ if (!iter->second.empty())
+ {
+ this->Makefile->AddDefinition(prefix + iter->first,
+ cmJoin(cmMakeRange(iter->second), ";")
+ .c_str());
+ }
+ else
+ {
+ this->Makefile->RemoveDefinition(prefix + iter->first);
+ }
+ }
+
+ if (!unparsed.empty())
+ {
+ this->Makefile->AddDefinition(prefix + "UNPARSED_ARGUMENTS",
+ cmJoin(cmMakeRange(unparsed), ";").c_str());
+ }
+ else
+ {
+ this->Makefile->RemoveDefinition(prefix + "UNPARSED_ARGUMENTS");
+ }
+
+ return true;
+}
diff --git a/Source/cmParseArgumentsCommand.h b/Source/cmParseArgumentsCommand.h
new file mode 100644
index 0000000..7fbf642
--- /dev/null
+++ b/Source/cmParseArgumentsCommand.h
@@ -0,0 +1,54 @@
+/*============================================================================
+ CMake - Cross Platform Makefile Generator
+ Copyright 2015 Matthias Maennich <matthias@maennich.net>
+
+ Distributed under the OSI-approved BSD License (the "License");
+ see accompanying file Copyright.txt for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even the
+ implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the License for more information.
+============================================================================*/
+
+#ifndef cmParseArgumentsCommand_h
+#define cmParseArgumentsCommand_h
+
+#include "cmCommand.h"
+
+/** \class cmParseArgumentsCommand
+ *
+ */
+class cmParseArgumentsCommand : public cmCommand
+{
+public:
+ /**
+ * This is a virtual constructor for the command.
+ */
+ virtual cmCommand* Clone()
+ {
+ return new cmParseArgumentsCommand;
+ }
+
+ /**
+ * This is called when the command is first encountered in
+ * the CMakeLists.txt file.
+ */
+ virtual bool InitialPass(std::vector<std::string> const& args,
+ cmExecutionStatus &status);
+
+ /**
+ * This determines if the command is invoked when in script mode.
+ */
+ virtual bool IsScriptable() const { return true; }
+
+ /**
+ * The name of the command as specified in CMakeList.txt.
+ */
+ virtual std::string GetName() const { return "cmake_parse_arguments";}
+
+ cmTypeMacro(cmParseArgumentsCommand, cmCommand);
+
+};
+
+
+#endif
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index a6cbf86..0a388c5 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -177,6 +177,7 @@ add_RunCMake_test(build_command)
add_RunCMake_test(execute_process)
add_RunCMake_test(export)
add_RunCMake_test(cmake_minimum_required)
+add_RunCMake_test(cmake_parse_arguments)
add_RunCMake_test(continue)
add_RunCMake_test(ctest_build)
add_RunCMake_test(ctest_configure)
diff --git a/Tests/RunCMake/cmake_parse_arguments/CMakeLists.txt b/Tests/RunCMake/cmake_parse_arguments/CMakeLists.txt
new file mode 100644
index 0000000..6dd8cdf
--- /dev/null
+++ b/Tests/RunCMake/cmake_parse_arguments/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.4)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/cmake_parse_arguments/CornerCases.cmake b/Tests/RunCMake/cmake_parse_arguments/CornerCases.cmake
new file mode 100644
index 0000000..9a727dd
--- /dev/null
+++ b/Tests/RunCMake/cmake_parse_arguments/CornerCases.cmake
@@ -0,0 +1,15 @@
+include(${CMAKE_CURRENT_LIST_DIR}/test_utils.cmake)
+
+# example from the documentation
+# OPTIONAL is a keyword and therefore terminates the definition of
+# the multi-value DEFINITION before even a single value has been added
+
+set(options OPTIONAL FAST)
+set(oneValueArgs DESTINATION RENAME)
+set(multiValueArgs TARGETS CONFIGURATIONS)
+cmake_parse_arguments(MY_INSTALL "${options}" "${oneValueArgs}"
+ "${multiValueArgs}"
+ TARGETS foo DESTINATION OPTIONAL)
+
+TEST(MY_INSTALL_DESTINATION UNDEFINED)
+TEST(MY_INSTALL_OPTIONAL TRUE)
diff --git a/Tests/RunCMake/cmake_parse_arguments/Errors-result.txt b/Tests/RunCMake/cmake_parse_arguments/Errors-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/cmake_parse_arguments/Errors-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/cmake_parse_arguments/Errors-stderr.txt b/Tests/RunCMake/cmake_parse_arguments/Errors-stderr.txt
new file mode 100644
index 0000000..f2ba9b8
--- /dev/null
+++ b/Tests/RunCMake/cmake_parse_arguments/Errors-stderr.txt
@@ -0,0 +1,44 @@
+CMake Error at Errors\.cmake:2 \(cmake_parse_arguments\):
+ cmake_parse_arguments must be called with at least 4 arguments\.
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:3 \(include\)
++
+CMake Error at Errors\.cmake:3 \(cmake_parse_arguments\):
+ cmake_parse_arguments must be called with at least 4 arguments\.
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:3 \(include\)
++
+CMake Error at Errors\.cmake:4 \(cmake_parse_arguments\):
+ cmake_parse_arguments must be called with at least 4 arguments\.
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:3 \(include\)
++
+CMake Warning at Errors\.cmake:8 \(cmake_parse_arguments\):
+ keyword defined more than once: OPT
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:3 \(include\)
++
+CMake Warning at Errors\.cmake:9 \(cmake_parse_arguments\):
+ keyword defined more than once: OPT
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:3 \(include\)
++
+CMake Warning at Errors\.cmake:10 \(cmake_parse_arguments\):
+ keyword defined more than once: OPT
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:3 \(include\)
++
+CMake Warning at Errors\.cmake:12 \(cmake_parse_arguments\):
+ keyword defined more than once: OPT
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:3 \(include\)
++
+CMake Warning at Errors\.cmake:13 \(cmake_parse_arguments\):
+ keyword defined more than once: OPT
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:3 \(include\)
++
+CMake Warning at Errors\.cmake:14 \(cmake_parse_arguments\):
+ keyword defined more than once: OPT
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:3 \(include\)
diff --git a/Tests/RunCMake/cmake_parse_arguments/Errors.cmake b/Tests/RunCMake/cmake_parse_arguments/Errors.cmake
new file mode 100644
index 0000000..6a38081
--- /dev/null
+++ b/Tests/RunCMake/cmake_parse_arguments/Errors.cmake
@@ -0,0 +1,14 @@
+# wrong argument count
+cmake_parse_arguments()
+cmake_parse_arguments(prefix OPT)
+cmake_parse_arguments(prefix OPT SINGLE)
+cmake_parse_arguments(prefix OPT SINGLE MULTI) # not an error
+
+# duplicate keywords
+cmake_parse_arguments(prefix "OPT;OPT" "" "")
+cmake_parse_arguments(prefix "" "OPT;OPT" "")
+cmake_parse_arguments(prefix "" "" "OPT;OPT")
+
+cmake_parse_arguments(prefix "OPT" "OPT" "")
+cmake_parse_arguments(prefix "" "OPT" "OPT")
+cmake_parse_arguments(prefix "OPT" "" "OPT")
diff --git a/Tests/RunCMake/cmake_parse_arguments/Initialization.cmake b/Tests/RunCMake/cmake_parse_arguments/Initialization.cmake
new file mode 100644
index 0000000..462f923
--- /dev/null
+++ b/Tests/RunCMake/cmake_parse_arguments/Initialization.cmake
@@ -0,0 +1,68 @@
+include(${CMAKE_CURRENT_LIST_DIR}/test_utils.cmake)
+
+# unparsed arguments
+cmake_parse_arguments(pref "" "" "")
+TEST(pref_UNPARSED_ARGUMENTS UNDEFINED)
+
+cmake_parse_arguments(pref "" "" "" FOO)
+TEST(pref_UNPARSED_ARGUMENTS "FOO")
+cmake_parse_arguments(pref "" "" "" FOO BAR)
+TEST(pref_UNPARSED_ARGUMENTS "FOO;BAR")
+cmake_parse_arguments(pref "" "" "")
+TEST(pref_UNPARSED_ARGUMENTS UNDEFINED)
+
+
+# options
+cmake_parse_arguments(pref "OPT1" "" "")
+TEST(pref_OPT1 FALSE)
+
+cmake_parse_arguments(pref "OPT1;OPT2" "" "")
+TEST(pref_OPT1 FALSE)
+TEST(pref_OPT2 FALSE)
+
+cmake_parse_arguments(pref "OPT1" "" "" OPT1)
+TEST(pref_OPT1 TRUE)
+cmake_parse_arguments(pref "OPT1;OPT2" "" "" OPT1 OPT2)
+TEST(pref_OPT1 TRUE)
+TEST(pref_OPT2 TRUE)
+cmake_parse_arguments(pref "OPT1;OPT2" "" "")
+TEST(pref_OPT1 FALSE)
+TEST(pref_OPT2 FALSE)
+
+
+# single arguments
+cmake_parse_arguments(pref "" "SINGLE1" "")
+TEST(pref_SINGLE1 UNDEFINED)
+
+cmake_parse_arguments(pref "" "SINGLE1;SINGLE2" "")
+TEST(pref_SINGLE1 UNDEFINED)
+TEST(pref_SINGLE2 UNDEFINED)
+
+
+cmake_parse_arguments(pref "" "SINGLE1" "" SINGLE1 foo)
+TEST(pref_SINGLE1 foo)
+cmake_parse_arguments(pref "" "SINGLE1;SINGLE2" "" SINGLE1 foo SINGLE2 bar)
+TEST(pref_SINGLE1 foo)
+TEST(pref_SINGLE2 bar)
+cmake_parse_arguments(pref "" "SINGLE1;SINGLE2" "")
+TEST(pref_SINGLE1 UNDEFINED)
+TEST(pref_SINGLE2 UNDEFINED)
+
+
+# multi arguments
+
+cmake_parse_arguments(pref "" "" "MULTI1")
+TEST(pref_MULTI1 UNDEFINED)
+
+cmake_parse_arguments(pref "" "" "MULTI1;MULTI2")
+TEST(pref_MULTI1 UNDEFINED)
+TEST(pref_MULTI2 UNDEFINED)
+
+cmake_parse_arguments(pref "" "" "MULTI1" MULTI1 foo)
+TEST(pref_MULTI1 foo)
+cmake_parse_arguments(pref "" "" "MULTI1;MULTI2" MULTI1 foo bar MULTI2 bar foo)
+TEST(pref_MULTI1 foo bar)
+TEST(pref_MULTI2 bar foo)
+cmake_parse_arguments(pref "" "" "MULTI1;MULTI2")
+TEST(pref_MULTI1 UNDEFINED)
+TEST(pref_MULTI2 UNDEFINED)
diff --git a/Tests/RunCMake/cmake_parse_arguments/Mix.cmake b/Tests/RunCMake/cmake_parse_arguments/Mix.cmake
new file mode 100644
index 0000000..b3eff39
--- /dev/null
+++ b/Tests/RunCMake/cmake_parse_arguments/Mix.cmake
@@ -0,0 +1,24 @@
+include(${CMAKE_CURRENT_LIST_DIR}/test_utils.cmake)
+
+# specify two keywords for each category and set the first keyword of each
+# within ARGN
+cmake_parse_arguments(pref "OPT1;OPT2" "SINGLE1;SINGLE2" "MULTI1;MULTI2"
+ OPT1 SINGLE1 foo MULTI1 bar foo bar)
+TEST(pref_OPT1 TRUE)
+TEST(pref_OPT2 FALSE)
+TEST(pref_SINGLE1 foo)
+TEST(pref_SINGLE2 UNDEFINED)
+TEST(pref_MULTI1 bar foo bar)
+TEST(pref_MULTI2 UNDEFINED)
+TEST(pref_UNPARSED_ARGUMENTS UNDEFINED)
+
+# same as above but reversed ARGN
+cmake_parse_arguments(pref "OPT1;OPT2" "SINGLE1;SINGLE2" "MULTI1;MULTI2"
+ MULTI1 bar foo bar SINGLE1 foo OPT1)
+TEST(pref_OPT1 TRUE)
+TEST(pref_OPT2 FALSE)
+TEST(pref_SINGLE1 foo)
+TEST(pref_SINGLE2 UNDEFINED)
+TEST(pref_MULTI1 bar foo bar)
+TEST(pref_MULTI2 UNDEFINED)
+TEST(pref_UNPARSED_ARGUMENTS UNDEFINED)
diff --git a/Tests/RunCMake/cmake_parse_arguments/RunCMakeTest.cmake b/Tests/RunCMake/cmake_parse_arguments/RunCMakeTest.cmake
new file mode 100644
index 0000000..b89f1a5
--- /dev/null
+++ b/Tests/RunCMake/cmake_parse_arguments/RunCMakeTest.cmake
@@ -0,0 +1,7 @@
+include(RunCMake)
+
+run_cmake(Utils)
+run_cmake(Initialization)
+run_cmake(Mix)
+run_cmake(CornerCases)
+run_cmake(Errors)
diff --git a/Tests/RunCMake/cmake_parse_arguments/Utils.cmake b/Tests/RunCMake/cmake_parse_arguments/Utils.cmake
new file mode 100644
index 0000000..3bbf115
--- /dev/null
+++ b/Tests/RunCMake/cmake_parse_arguments/Utils.cmake
@@ -0,0 +1,20 @@
+include(${CMAKE_CURRENT_LIST_DIR}/test_utils.cmake)
+
+# test the TEST macro itself
+
+TEST(asdf UNDEFINED)
+
+SET (asdf FALSE)
+TEST(asdf FALSE)
+
+SET (asdf TRUE)
+TEST(asdf TRUE)
+
+SET (asdf TRUE)
+TEST(asdf TRUE)
+
+SET (asdf "some value")
+TEST(asdf "some value")
+
+SET (asdf some list)
+TEST(asdf "some;list")
diff --git a/Tests/RunCMake/cmake_parse_arguments/test_utils.cmake b/Tests/RunCMake/cmake_parse_arguments/test_utils.cmake
new file mode 100644
index 0000000..f5425c2
--- /dev/null
+++ b/Tests/RunCMake/cmake_parse_arguments/test_utils.cmake
@@ -0,0 +1,20 @@
+macro(TEST variable)
+ SET(expected "${ARGN}")
+ if ( "${expected}" STREQUAL "UNDEFINED" )
+ if (DEFINED ${variable})
+ message(FATAL_ERROR "'${variable}' shall be undefined but has value '${${variable}}'")
+ endif()
+ elseif( "${expected}" STREQUAL "FALSE" )
+ if (NOT ${variable} STREQUAL "FALSE")
+ message(FATAL_ERROR "'${variable}' shall be FALSE")
+ endif()
+ elseif( "${expected}" STREQUAL "TRUE" )
+ if (NOT ${variable} STREQUAL "TRUE")
+ message(FATAL_ERROR "'${variable}' shall be TRUE")
+ endif()
+ else()
+ if (NOT ${variable} STREQUAL "${expected}")
+ message(FATAL_ERROR "'${variable}' shall be '${expected}'")
+ endif()
+ endif()
+endmacro()