summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBill Hoffman <bill.hoffman@kitware.com>2016-09-07 16:47:23 -0400
committerBrad King <brad.king@kitware.com>2016-09-08 11:33:46 -0400
commitcb299acc27d5f80b2fc801f0f57358ec9f0303d1 (patch)
treedb2426c946edfc3cd760edfc28f66a8bbedf4844
parentf506489d1ed222761f9ce752144a458290020e55 (diff)
downloadcmake-cb299acc27d5f80b2fc801f0f57358ec9f0303d1.tar.gz
cmake_parse_arguments: Add option to read arguments from ARGC/ARGV#
The `ARGC`/`ARGV#` variables in function scope hold the original arguments with no ;-list flattening. Add a way for functions to cleanly parse arguments that may contain `;`. This also avoids extra copying of the arguments. Co-Author: Brad King <brad.king@kitware.com>
-rw-r--r--Help/command/cmake_parse_arguments.rst11
-rw-r--r--Help/release/dev/parse_arguments_argv_n.rst6
-rw-r--r--Source/cmParseArgumentsCommand.cxx57
-rw-r--r--Tests/RunCMake/cmake_parse_arguments/ArgvN.cmake30
-rw-r--r--Tests/RunCMake/cmake_parse_arguments/BadArgvN1-result.txt1
-rw-r--r--Tests/RunCMake/cmake_parse_arguments/BadArgvN1-stderr.txt5
-rw-r--r--Tests/RunCMake/cmake_parse_arguments/BadArgvN1.cmake4
-rw-r--r--Tests/RunCMake/cmake_parse_arguments/BadArgvN2-result.txt1
-rw-r--r--Tests/RunCMake/cmake_parse_arguments/BadArgvN2-stderr.txt5
-rw-r--r--Tests/RunCMake/cmake_parse_arguments/BadArgvN2.cmake4
-rw-r--r--Tests/RunCMake/cmake_parse_arguments/BadArgvN3-result.txt1
-rw-r--r--Tests/RunCMake/cmake_parse_arguments/BadArgvN3-stderr.txt4
-rw-r--r--Tests/RunCMake/cmake_parse_arguments/BadArgvN3.cmake1
-rw-r--r--Tests/RunCMake/cmake_parse_arguments/BadArgvN4-result.txt1
-rw-r--r--Tests/RunCMake/cmake_parse_arguments/BadArgvN4-stderr.txt4
-rw-r--r--Tests/RunCMake/cmake_parse_arguments/BadArgvN4.cmake3
-rw-r--r--Tests/RunCMake/cmake_parse_arguments/RunCMakeTest.cmake5
17 files changed, 139 insertions, 4 deletions
diff --git a/Help/command/cmake_parse_arguments.rst b/Help/command/cmake_parse_arguments.rst
index 6206611d28..ec4ffed3ad 100644
--- a/Help/command/cmake_parse_arguments.rst
+++ b/Help/command/cmake_parse_arguments.rst
@@ -11,6 +11,17 @@ respective options.
cmake_parse_arguments(<prefix> <options> <one_value_keywords>
<multi_value_keywords> args...)
+ cmake_parse_arguments(PARSE_ARGV N <prefix> <options> <one_value_keywords>
+ <multi_value_keywords>)
+
+The first signature reads processes arguments passed in the ``args...``.
+This may be used in either a :command:`macro` or a :command:`function`.
+
+The ``PARSE_ARGV`` signature is only for use in a :command:`function`
+body. In this case the arguments that are parsed come from the
+``ARGV#`` variables of the calling function. The parsing starts with
+the Nth argument, where ``N`` is an unsigned integer. This allows for
+the values to have special characters like ``;`` in them.
The ``<options>`` argument contains all options for the respective macro,
i.e. keywords which can be used when calling the macro without any value
diff --git a/Help/release/dev/parse_arguments_argv_n.rst b/Help/release/dev/parse_arguments_argv_n.rst
new file mode 100644
index 0000000000..758b72a1bf
--- /dev/null
+++ b/Help/release/dev/parse_arguments_argv_n.rst
@@ -0,0 +1,6 @@
+parse_arguments_argv_n
+----------------------
+
+* The :command:`cmake_parse_arguments` command gained a new
+ mode to read arguments directly from ``ARGC`` and ``ARGV#``
+ variables inside a :command:`function` body.
diff --git a/Source/cmParseArgumentsCommand.cxx b/Source/cmParseArgumentsCommand.cxx
index 8f524ec0d8..f9313c584e 100644
--- a/Source/cmParseArgumentsCommand.cxx
+++ b/Source/cmParseArgumentsCommand.cxx
@@ -20,6 +20,8 @@ bool cmParseArgumentsCommand::InitialPass(std::vector<std::string> const& args,
{
// cmake_parse_arguments(prefix options single multi <ARGN>)
// 1 2 3 4
+ // or
+ // cmake_parse_arguments(PARSE_ARGV N prefix options single multi)
if (args.size() < 4) {
this->SetError("must be called with at least 4 arguments.");
return false;
@@ -27,6 +29,27 @@ bool cmParseArgumentsCommand::InitialPass(std::vector<std::string> const& args,
std::vector<std::string>::const_iterator argIter = args.begin(),
argEnd = args.end();
+ bool parseFromArgV = false;
+ unsigned long argvStart = 0;
+ if (*argIter == "PARSE_ARGV") {
+ if (args.size() != 6) {
+ this->Makefile->IssueMessage(
+ cmake::FATAL_ERROR,
+ "PARSE_ARGV must be called with exactly 6 arguments.");
+ cmSystemTools::SetFatalErrorOccured();
+ return true;
+ }
+ parseFromArgV = true;
+ argIter++; // move past PARSE_ARGV
+ if (!cmSystemTools::StringToULong(argIter->c_str(), &argvStart)) {
+ this->Makefile->IssueMessage(cmake::FATAL_ERROR, "PARSE_ARGV index '" +
+ *argIter +
+ "' is not an unsigned integer");
+ cmSystemTools::SetFatalErrorOccured();
+ return true;
+ }
+ argIter++; // move past N
+ }
// the first argument is the prefix
const std::string prefix = (*argIter++) + "_";
@@ -90,11 +113,37 @@ bool cmParseArgumentsCommand::InitialPass(std::vector<std::string> const& args,
} insideValues = NONE;
std::string currentArgName;
- // Flatten ;-lists in the arguments into a single list as was done
- // by the original function(CMAKE_PARSE_ARGUMENTS).
list.clear();
- for (; argIter != argEnd; ++argIter) {
- cmSystemTools::ExpandListArgument(*argIter, list);
+ if (!parseFromArgV) {
+ // Flatten ;-lists in the arguments into a single list as was done
+ // by the original function(CMAKE_PARSE_ARGUMENTS).
+ for (; argIter != argEnd; ++argIter) {
+ cmSystemTools::ExpandListArgument(*argIter, list);
+ }
+ } else {
+ // in the PARSE_ARGV move read the arguments from ARGC and ARGV#
+ std::string argc = this->Makefile->GetSafeDefinition("ARGC");
+ unsigned long count;
+ if (!cmSystemTools::StringToULong(argc.c_str(), &count)) {
+ this->Makefile->IssueMessage(cmake::FATAL_ERROR,
+ "PARSE_ARGV called with ARGC='" + argc +
+ "' that is not an unsigned integer");
+ cmSystemTools::SetFatalErrorOccured();
+ return true;
+ }
+ for (unsigned long i = argvStart; i < count; ++i) {
+ std::ostringstream argName;
+ argName << "ARGV" << i;
+ const char* arg = this->Makefile->GetDefinition(argName.str());
+ if (!arg) {
+ this->Makefile->IssueMessage(cmake::FATAL_ERROR,
+ "PARSE_ARGV called with " +
+ argName.str() + " not set");
+ cmSystemTools::SetFatalErrorOccured();
+ return true;
+ }
+ list.push_back(arg);
+ }
}
// iterate over the arguments list and fill in the values where applicable
diff --git a/Tests/RunCMake/cmake_parse_arguments/ArgvN.cmake b/Tests/RunCMake/cmake_parse_arguments/ArgvN.cmake
new file mode 100644
index 0000000000..61bde03638
--- /dev/null
+++ b/Tests/RunCMake/cmake_parse_arguments/ArgvN.cmake
@@ -0,0 +1,30 @@
+include(${CMAKE_CURRENT_LIST_DIR}/test_utils.cmake)
+
+function(test1)
+ cmake_parse_arguments(PARSE_ARGV 0
+ pref "OPT1;OPT2" "SINGLE1;SINGLE2" "MULTI1;MULTI2")
+
+ TEST(pref_OPT1 TRUE)
+ TEST(pref_OPT2 FALSE)
+ TEST(pref_SINGLE1 "foo;bar")
+ TEST(pref_SINGLE2 UNDEFINED)
+ TEST(pref_MULTI1 bar foo bar)
+ TEST(pref_MULTI2 UNDEFINED)
+ TEST(pref_UNPARSED_ARGUMENTS UNDEFINED)
+endfunction()
+test1(OPT1 SINGLE1 "foo;bar" MULTI1 bar foo bar)
+
+function(test2 arg1)
+ cmake_parse_arguments(PARSE_ARGV 1
+ pref "OPT1;OPT2" "SINGLE1;SINGLE2" "MULTI1;MULTI2")
+
+ TEST(arg1 "first named")
+ TEST(pref_OPT1 TRUE)
+ TEST(pref_OPT2 FALSE)
+ TEST(pref_SINGLE1 "foo;bar")
+ TEST(pref_SINGLE2 UNDEFINED)
+ TEST(pref_MULTI1 bar foo bar)
+ TEST(pref_MULTI2 UNDEFINED)
+ TEST(pref_UNPARSED_ARGUMENTS UNDEFINED)
+endfunction()
+test2("first named" OPT1 SINGLE1 "foo;bar" MULTI1 bar foo bar)
diff --git a/Tests/RunCMake/cmake_parse_arguments/BadArgvN1-result.txt b/Tests/RunCMake/cmake_parse_arguments/BadArgvN1-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/cmake_parse_arguments/BadArgvN1-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/cmake_parse_arguments/BadArgvN1-stderr.txt b/Tests/RunCMake/cmake_parse_arguments/BadArgvN1-stderr.txt
new file mode 100644
index 0000000000..e44ab4ddf4
--- /dev/null
+++ b/Tests/RunCMake/cmake_parse_arguments/BadArgvN1-stderr.txt
@@ -0,0 +1,5 @@
+^CMake Error at BadArgvN1.cmake:[0-9]+ \(cmake_parse_arguments\):
+ PARSE_ARGV must be called with exactly 6 arguments.
+Call Stack \(most recent call first\):
+ BadArgvN1.cmake:[0-9]+ \(test1\)
+ CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/cmake_parse_arguments/BadArgvN1.cmake b/Tests/RunCMake/cmake_parse_arguments/BadArgvN1.cmake
new file mode 100644
index 0000000000..f8941304ab
--- /dev/null
+++ b/Tests/RunCMake/cmake_parse_arguments/BadArgvN1.cmake
@@ -0,0 +1,4 @@
+function(test1)
+ cmake_parse_arguments(PARSE_ARGV 0 pref "" "" "" extra)
+endfunction()
+test1()
diff --git a/Tests/RunCMake/cmake_parse_arguments/BadArgvN2-result.txt b/Tests/RunCMake/cmake_parse_arguments/BadArgvN2-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/cmake_parse_arguments/BadArgvN2-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/cmake_parse_arguments/BadArgvN2-stderr.txt b/Tests/RunCMake/cmake_parse_arguments/BadArgvN2-stderr.txt
new file mode 100644
index 0000000000..1dde86df6a
--- /dev/null
+++ b/Tests/RunCMake/cmake_parse_arguments/BadArgvN2-stderr.txt
@@ -0,0 +1,5 @@
+^CMake Error at BadArgvN2.cmake:[0-9]+ \(cmake_parse_arguments\):
+ PARSE_ARGV index 'pref' is not an unsigned integer
+Call Stack \(most recent call first\):
+ BadArgvN2.cmake:[0-9]+ \(test2\)
+ CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/cmake_parse_arguments/BadArgvN2.cmake b/Tests/RunCMake/cmake_parse_arguments/BadArgvN2.cmake
new file mode 100644
index 0000000000..d20c2c8519
--- /dev/null
+++ b/Tests/RunCMake/cmake_parse_arguments/BadArgvN2.cmake
@@ -0,0 +1,4 @@
+function(test2)
+ cmake_parse_arguments(PARSE_ARGV pref "" "" "" extra)
+endfunction()
+test2()
diff --git a/Tests/RunCMake/cmake_parse_arguments/BadArgvN3-result.txt b/Tests/RunCMake/cmake_parse_arguments/BadArgvN3-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/cmake_parse_arguments/BadArgvN3-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/cmake_parse_arguments/BadArgvN3-stderr.txt b/Tests/RunCMake/cmake_parse_arguments/BadArgvN3-stderr.txt
new file mode 100644
index 0000000000..5a30dc9807
--- /dev/null
+++ b/Tests/RunCMake/cmake_parse_arguments/BadArgvN3-stderr.txt
@@ -0,0 +1,4 @@
+^CMake Error at BadArgvN3.cmake:[0-9]+ \(cmake_parse_arguments\):
+ PARSE_ARGV called with ARGC='' that is not an unsigned integer
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/cmake_parse_arguments/BadArgvN3.cmake b/Tests/RunCMake/cmake_parse_arguments/BadArgvN3.cmake
new file mode 100644
index 0000000000..8fb3fdd1dc
--- /dev/null
+++ b/Tests/RunCMake/cmake_parse_arguments/BadArgvN3.cmake
@@ -0,0 +1 @@
+cmake_parse_arguments(PARSE_ARGV 0 pref "" "" "")
diff --git a/Tests/RunCMake/cmake_parse_arguments/BadArgvN4-result.txt b/Tests/RunCMake/cmake_parse_arguments/BadArgvN4-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/cmake_parse_arguments/BadArgvN4-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/cmake_parse_arguments/BadArgvN4-stderr.txt b/Tests/RunCMake/cmake_parse_arguments/BadArgvN4-stderr.txt
new file mode 100644
index 0000000000..f4bdac4117
--- /dev/null
+++ b/Tests/RunCMake/cmake_parse_arguments/BadArgvN4-stderr.txt
@@ -0,0 +1,4 @@
+^CMake Error at BadArgvN4.cmake:[0-9]+ \(cmake_parse_arguments\):
+ PARSE_ARGV called with ARGV0 not set
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/cmake_parse_arguments/BadArgvN4.cmake b/Tests/RunCMake/cmake_parse_arguments/BadArgvN4.cmake
new file mode 100644
index 0000000000..b887a28b57
--- /dev/null
+++ b/Tests/RunCMake/cmake_parse_arguments/BadArgvN4.cmake
@@ -0,0 +1,3 @@
+set(ARGC 1)
+cmake_parse_arguments(PARSE_ARGV 0 pref "" "" "")
+unset(ARGC)
diff --git a/Tests/RunCMake/cmake_parse_arguments/RunCMakeTest.cmake b/Tests/RunCMake/cmake_parse_arguments/RunCMakeTest.cmake
index b89f1a55e3..22d7ee42df 100644
--- a/Tests/RunCMake/cmake_parse_arguments/RunCMakeTest.cmake
+++ b/Tests/RunCMake/cmake_parse_arguments/RunCMakeTest.cmake
@@ -5,3 +5,8 @@ run_cmake(Initialization)
run_cmake(Mix)
run_cmake(CornerCases)
run_cmake(Errors)
+run_cmake(ArgvN)
+run_cmake(BadArgvN1)
+run_cmake(BadArgvN2)
+run_cmake(BadArgvN3)
+run_cmake(BadArgvN4)