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>
|
cmake_parse_arguments(<prefix> <options> <one_value_keywords>
|
||||||
<multi_value_keywords> args...)
|
<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,
|
The ``<options>`` argument contains all options for the respective macro,
|
||||||
i.e. keywords which can be used when calling the macro without any value
|
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>)
|
// cmake_parse_arguments(prefix options single multi <ARGN>)
|
||||||
// 1 2 3 4
|
// 1 2 3 4
|
||||||
|
// or
|
||||||
|
// cmake_parse_arguments(PARSE_ARGV N prefix options single multi)
|
||||||
if (args.size() < 4) {
|
if (args.size() < 4) {
|
||||||
this->SetError("must be called with at least 4 arguments.");
|
this->SetError("must be called with at least 4 arguments.");
|
||||||
return false;
|
return false;
|
||||||
|
@ -27,6 +29,27 @@ bool cmParseArgumentsCommand::InitialPass(std::vector<std::string> const& args,
|
||||||
|
|
||||||
std::vector<std::string>::const_iterator argIter = args.begin(),
|
std::vector<std::string>::const_iterator argIter = args.begin(),
|
||||||
argEnd = args.end();
|
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
|
// the first argument is the prefix
|
||||||
const std::string prefix = (*argIter++) + "_";
|
const std::string prefix = (*argIter++) + "_";
|
||||||
|
|
||||||
|
@ -90,12 +113,38 @@ bool cmParseArgumentsCommand::InitialPass(std::vector<std::string> const& args,
|
||||||
} insideValues = NONE;
|
} insideValues = NONE;
|
||||||
std::string currentArgName;
|
std::string currentArgName;
|
||||||
|
|
||||||
|
list.clear();
|
||||||
|
if (!parseFromArgV) {
|
||||||
// Flatten ;-lists in the arguments into a single list as was done
|
// Flatten ;-lists in the arguments into a single list as was done
|
||||||
// by the original function(CMAKE_PARSE_ARGUMENTS).
|
// by the original function(CMAKE_PARSE_ARGUMENTS).
|
||||||
list.clear();
|
|
||||||
for (; argIter != argEnd; ++argIter) {
|
for (; argIter != argEnd; ++argIter) {
|
||||||
cmSystemTools::ExpandListArgument(*argIter, list);
|
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
|
// iterate over the arguments list and fill in the values where applicable
|
||||||
for (argIter = list.begin(), argEnd = list.end(); argIter != argEnd;
|
for (argIter = list.begin(), argEnd = list.end(); argIter != argEnd;
|
||||||
|
|
|
@ -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(Mix)
|
||||||
run_cmake(CornerCases)
|
run_cmake(CornerCases)
|
||||||
run_cmake(Errors)
|
run_cmake(Errors)
|
||||||
|
run_cmake(ArgvN)
|
||||||
|
run_cmake(BadArgvN1)
|
||||||
|
run_cmake(BadArgvN2)
|
||||||
|
run_cmake(BadArgvN3)
|
||||||
|
run_cmake(BadArgvN4)
|
||||||
|
|
Loading…
Reference in New Issue