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>
This commit is contained in:
Bill Hoffman 2016-09-07 16:47:23 -04:00 committed by Brad King
parent f506489d1e
commit cb299acc27
17 changed files with 139 additions and 4 deletions

View File

@ -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

View File

@ -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.

View File

@ -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,12 +113,38 @@ bool cmParseArgumentsCommand::InitialPass(std::vector<std::string> const& args,
} insideValues = NONE;
std::string currentArgName;
list.clear();
if (!parseFromArgV) {
// 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);
}
} 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
for (argIter = list.begin(), argEnd = list.end(); argIter != argEnd;

View File

@ -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)

View File

@ -0,0 +1 @@
1

View File

@ -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\)$

View File

@ -0,0 +1,4 @@
function(test1)
cmake_parse_arguments(PARSE_ARGV 0 pref "" "" "" extra)
endfunction()
test1()

View File

@ -0,0 +1 @@
1

View File

@ -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\)$

View File

@ -0,0 +1,4 @@
function(test2)
cmake_parse_arguments(PARSE_ARGV pref "" "" "" extra)
endfunction()
test2()

View File

@ -0,0 +1 @@
1

View File

@ -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\)$

View File

@ -0,0 +1 @@
cmake_parse_arguments(PARSE_ARGV 0 pref "" "" "")

View File

@ -0,0 +1 @@
1

View File

@ -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\)$

View File

@ -0,0 +1,3 @@
set(ARGC 1)
cmake_parse_arguments(PARSE_ARGV 0 pref "" "" "")
unset(ARGC)

View File

@ -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)