From cb299acc27d5f80b2fc801f0f57358ec9f0303d1 Mon Sep 17 00:00:00 2001 From: Bill Hoffman Date: Wed, 7 Sep 2016 16:47:23 -0400 Subject: [PATCH] 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 --- Help/command/cmake_parse_arguments.rst | 11 ++++ Help/release/dev/parse_arguments_argv_n.rst | 6 ++ Source/cmParseArgumentsCommand.cxx | 57 +++++++++++++++++-- .../cmake_parse_arguments/ArgvN.cmake | 30 ++++++++++ .../BadArgvN1-result.txt | 1 + .../BadArgvN1-stderr.txt | 5 ++ .../cmake_parse_arguments/BadArgvN1.cmake | 4 ++ .../BadArgvN2-result.txt | 1 + .../BadArgvN2-stderr.txt | 5 ++ .../cmake_parse_arguments/BadArgvN2.cmake | 4 ++ .../BadArgvN3-result.txt | 1 + .../BadArgvN3-stderr.txt | 4 ++ .../cmake_parse_arguments/BadArgvN3.cmake | 1 + .../BadArgvN4-result.txt | 1 + .../BadArgvN4-stderr.txt | 4 ++ .../cmake_parse_arguments/BadArgvN4.cmake | 3 + .../cmake_parse_arguments/RunCMakeTest.cmake | 5 ++ 17 files changed, 139 insertions(+), 4 deletions(-) create mode 100644 Help/release/dev/parse_arguments_argv_n.rst create mode 100644 Tests/RunCMake/cmake_parse_arguments/ArgvN.cmake create mode 100644 Tests/RunCMake/cmake_parse_arguments/BadArgvN1-result.txt create mode 100644 Tests/RunCMake/cmake_parse_arguments/BadArgvN1-stderr.txt create mode 100644 Tests/RunCMake/cmake_parse_arguments/BadArgvN1.cmake create mode 100644 Tests/RunCMake/cmake_parse_arguments/BadArgvN2-result.txt create mode 100644 Tests/RunCMake/cmake_parse_arguments/BadArgvN2-stderr.txt create mode 100644 Tests/RunCMake/cmake_parse_arguments/BadArgvN2.cmake create mode 100644 Tests/RunCMake/cmake_parse_arguments/BadArgvN3-result.txt create mode 100644 Tests/RunCMake/cmake_parse_arguments/BadArgvN3-stderr.txt create mode 100644 Tests/RunCMake/cmake_parse_arguments/BadArgvN3.cmake create mode 100644 Tests/RunCMake/cmake_parse_arguments/BadArgvN4-result.txt create mode 100644 Tests/RunCMake/cmake_parse_arguments/BadArgvN4-stderr.txt create mode 100644 Tests/RunCMake/cmake_parse_arguments/BadArgvN4.cmake diff --git a/Help/command/cmake_parse_arguments.rst b/Help/command/cmake_parse_arguments.rst index 6206611d2..ec4ffed3a 100644 --- a/Help/command/cmake_parse_arguments.rst +++ b/Help/command/cmake_parse_arguments.rst @@ -11,6 +11,17 @@ respective options. cmake_parse_arguments( args...) + cmake_parse_arguments(PARSE_ARGV N + ) + +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 ```` 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 000000000..758b72a1b --- /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 8f524ec0d..f9313c584 100644 --- a/Source/cmParseArgumentsCommand.cxx +++ b/Source/cmParseArgumentsCommand.cxx @@ -20,6 +20,8 @@ bool cmParseArgumentsCommand::InitialPass(std::vector const& args, { // cmake_parse_arguments(prefix options single multi ) // 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 const& args, std::vector::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 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 000000000..61bde0363 --- /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 000000000..d00491fd7 --- /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 000000000..e44ab4ddf --- /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 000000000..f8941304a --- /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 000000000..d00491fd7 --- /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 000000000..1dde86df6 --- /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 000000000..d20c2c851 --- /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 000000000..d00491fd7 --- /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 000000000..5a30dc980 --- /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 000000000..8fb3fdd1d --- /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 000000000..d00491fd7 --- /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 000000000..f4bdac411 --- /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 000000000..b887a28b5 --- /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 b89f1a55e..22d7ee42d 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)