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:
parent
f506489d1e
commit
cb299acc27
|
@ -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
|
||||
|
|
|
@ -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.
|
|
@ -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
|
||||
|
|
|
@ -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)
|
|
@ -0,0 +1 @@
|
|||
1
|
|
@ -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\)$
|
|
@ -0,0 +1,4 @@
|
|||
function(test1)
|
||||
cmake_parse_arguments(PARSE_ARGV 0 pref "" "" "" extra)
|
||||
endfunction()
|
||||
test1()
|
|
@ -0,0 +1 @@
|
|||
1
|
|
@ -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\)$
|
|
@ -0,0 +1,4 @@
|
|||
function(test2)
|
||||
cmake_parse_arguments(PARSE_ARGV pref "" "" "" extra)
|
||||
endfunction()
|
||||
test2()
|
|
@ -0,0 +1 @@
|
|||
1
|
|
@ -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\)$
|
|
@ -0,0 +1 @@
|
|||
cmake_parse_arguments(PARSE_ARGV 0 pref "" "" "")
|
|
@ -0,0 +1 @@
|
|||
1
|
|
@ -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\)$
|
|
@ -0,0 +1,3 @@
|
|||
set(ARGC 1)
|
||||
cmake_parse_arguments(PARSE_ARGV 0 pref "" "" "")
|
||||
unset(ARGC)
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue