summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Source/cmSeparateArgumentsCommand.cxx94
-rw-r--r--Source/cmSeparateArgumentsCommand.h18
-rw-r--r--Tests/CMakeTests/CMakeLists.txt1
-rw-r--r--Tests/CMakeTests/SeparateArgumentsTest.cmake.in25
4 files changed, 128 insertions, 10 deletions
diff --git a/Source/cmSeparateArgumentsCommand.cxx b/Source/cmSeparateArgumentsCommand.cxx
index 6e015b7..54cba49 100644
--- a/Source/cmSeparateArgumentsCommand.cxx
+++ b/Source/cmSeparateArgumentsCommand.cxx
@@ -20,19 +20,95 @@
bool cmSeparateArgumentsCommand
::InitialPass(std::vector<std::string> const& args, cmExecutionStatus &)
{
- if(args.size() != 1 )
+ if(args.empty())
{
- this->SetError("called with incorrect number of arguments");
+ this->SetError("must be given at least one argument.");
return false;
}
- const char* cacheValue = this->Makefile->GetDefinition(args[0].c_str());
- if(!cacheValue)
+
+ std::string var;
+ std::string command;
+ enum Mode { ModeOld, ModeUnix, ModeWindows };
+ Mode mode = ModeOld;
+ enum Doing { DoingNone, DoingVariable, DoingMode, DoingCommand };
+ Doing doing = DoingVariable;
+ for(unsigned int i=0; i < args.size(); ++i)
+ {
+ if(doing == DoingVariable)
+ {
+ var = args[i];
+ doing = DoingMode;
+ }
+ else if(doing == DoingMode && args[i] == "UNIX_COMMAND")
+ {
+ mode = ModeUnix;
+ doing = DoingCommand;
+ }
+ else if(doing == DoingMode && args[i] == "WINDOWS_COMMAND")
+ {
+ mode = ModeWindows;
+ doing = DoingCommand;
+ }
+ else if(doing == DoingCommand)
+ {
+ command = args[i];
+ doing = DoingNone;
+ }
+ else
+ {
+ cmOStringStream e;
+ e << "given unknown argument " << args[i];
+ this->SetError(e.str().c_str());
+ return false;
+ }
+ }
+
+ if(mode == ModeOld)
+ {
+ // Original space-replacement version of command.
+ if(const char* def = this->Makefile->GetDefinition(var.c_str()))
+ {
+ std::string value = def;
+ cmSystemTools::ReplaceString(value, " ", ";");
+ this->Makefile->AddDefinition(var.c_str(), value.c_str());
+ }
+ }
+ else
{
- return true;
+ // Parse the command line.
+ std::vector<std::string> vec;
+ if(mode == ModeUnix)
+ {
+ cmSystemTools::ParseUnixCommandLine(command.c_str(), vec);
+ }
+ else // if(mode == ModeWindows)
+ {
+ cmSystemTools::ParseWindowsCommandLine(command.c_str(), vec);
+ }
+
+ // Construct the result list value.
+ std::string value;
+ const char* sep = "";
+ for(std::vector<std::string>::const_iterator vi = vec.begin();
+ vi != vec.end(); ++vi)
+ {
+ // Separate from the previous argument.
+ value += sep;
+ sep = ";";
+
+ // Preserve semicolons.
+ for(std::string::const_iterator si = vi->begin();
+ si != vi->end(); ++si)
+ {
+ if(*si == ';')
+ {
+ value += '\\';
+ }
+ value += *si;
+ }
+ }
+ this->Makefile->AddDefinition(var.c_str(), value.c_str());
}
- std::string value = cacheValue;
- cmSystemTools::ReplaceString(value," ", ";");
- this->Makefile->AddDefinition(args[0].c_str(), value.c_str());
+
return true;
}
-
diff --git a/Source/cmSeparateArgumentsCommand.h b/Source/cmSeparateArgumentsCommand.h
index 827f7f2..c31d10e 100644
--- a/Source/cmSeparateArgumentsCommand.h
+++ b/Source/cmSeparateArgumentsCommand.h
@@ -58,7 +58,7 @@ public:
virtual const char* GetTerseDocumentation()
{
return
- "Split space separated arguments into a semi-colon separated list.";
+ "Parse space-separated arguments into a semicolon-separated list.";
}
/**
@@ -67,6 +67,22 @@ public:
virtual const char* GetFullDocumentation()
{
return
+ " separate_arguments(<var> <UNIX|WINDOWS>_COMMAND \"<args>\")\n"
+ "Parses a unix- or windows-style command-line string \"<args>\" and "
+ "stores a semicolon-separated list of the arguments in <var>. "
+ "The entire command line must be given in one \"<args>\" argument."
+ "\n"
+ "The UNIX_COMMAND mode separates arguments by unquoted whitespace. "
+ "It recognizes both single-quote and double-quote pairs. "
+ "A backslash escapes the next literal character (\\\" is \"); "
+ "there are no special escapes (\\n is just n)."
+ "\n"
+ "The WINDOWS_COMMAND mode parses a windows command-line using the "
+ "same syntax the runtime library uses to construct argv at startup. "
+ "It separates arguments by whitespace that is not double-quoted. "
+ "Backslashes are literal unless they precede double-quotes. "
+ "See the MSDN article \"Parsing C Command-Line Arguments\" for details."
+ "\n"
" separate_arguments(VARIABLE)\n"
"Convert the value of VARIABLE to a semi-colon separated list. "
"All spaces are replaced with ';'. This helps with generating "
diff --git a/Tests/CMakeTests/CMakeLists.txt b/Tests/CMakeTests/CMakeLists.txt
index 62e6133..b83518b 100644
--- a/Tests/CMakeTests/CMakeLists.txt
+++ b/Tests/CMakeTests/CMakeLists.txt
@@ -18,6 +18,7 @@ AddCMakeTest(GetFilenameComponentRealpath "")
AddCMakeTest(Version "")
AddCMakeTest(Message "")
AddCMakeTest(File "")
+AddCMakeTest(SeparateArguments "")
SET(GetPrerequisites_PreArgs
"-DCTEST_CONFIGURATION_TYPE:STRING=\\\${CTEST_CONFIGURATION_TYPE}"
diff --git a/Tests/CMakeTests/SeparateArgumentsTest.cmake.in b/Tests/CMakeTests/SeparateArgumentsTest.cmake.in
new file mode 100644
index 0000000..48964b8
--- /dev/null
+++ b/Tests/CMakeTests/SeparateArgumentsTest.cmake.in
@@ -0,0 +1,25 @@
+set(old_out "a b c")
+separate_arguments(old_out)
+set(old_exp "a;b;;c")
+
+set(unix_cmd "a \"b c\" 'd e' \";\" \\ \\'\\\" '\\'' \"\\\"\"")
+set(unix_exp "a;b c;d e;\;; '\";';\"")
+separate_arguments(unix_out UNIX_COMMAND "${unix_cmd}")
+
+set(windows_cmd "a \"b c\" 'd e' \";\" \\ \"c:\\windows\\path\\\\\" \\\"")
+set(windows_exp "a;b c;'d;e';\;;\\;c:\\windows\\path\\;\"")
+separate_arguments(windows_out WINDOWS_COMMAND "${windows_cmd}")
+
+foreach(mode old unix windows)
+ if(NOT "${${mode}_out}" STREQUAL "${${mode}_exp}")
+ message(FATAL_ERROR "separate_arguments ${mode}-style failed. "
+ "Expected\n [${${mode}_exp}]\nbut got\n [${${mode}_out}]\n")
+ endif()
+endforeach()
+
+set(nothing)
+separate_arguments(nothing)
+if(DEFINED nothing)
+ message(FATAL_ERROR "separate_arguments null-case failed: "
+ "nothing=[${nothing}]")
+endif()