diff options
author | Brad King <brad.king@kitware.com> | 2020-09-23 16:05:16 (GMT) |
---|---|---|
committer | Kitware Robot <kwrobot@kitware.com> | 2020-09-23 16:05:26 (GMT) |
commit | d827fdb6f99e58e4dfc6fdda3816ed2862e00a95 (patch) | |
tree | 4597f12193130eaa0311c2220a2d623ee71c36b0 | |
parent | a41f375e65ad764f7512ebf9edd1370adaec24f7 (diff) | |
parent | d832c1cc7d717623b2eb07a940c85ca427a8084e (diff) | |
download | CMake-d827fdb6f99e58e4dfc6fdda3816ed2862e00a95.zip CMake-d827fdb6f99e58e4dfc6fdda3816ed2862e00a95.tar.gz CMake-d827fdb6f99e58e4dfc6fdda3816ed2862e00a95.tar.bz2 |
Merge topic 'separate_arguments-program'
d832c1cc7d separate_arguments: add option PROGRAM
f4c21d4953 separate_arguments: refactoring
Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !5253
19 files changed, 297 insertions, 74 deletions
diff --git a/Help/command/get_filename_component.rst b/Help/command/get_filename_component.rst index e8c68b2..1d93709 100644 --- a/Help/command/get_filename_component.rst +++ b/Help/command/get_filename_component.rst @@ -44,12 +44,6 @@ Paths are returned with forward slashes and have no trailing slashes. If the optional ``CACHE`` argument is specified, the result variable is added to the cache. -.. note:: - - All previous sub-commands has been superseded by - :command:`cmake_path` command, except ``REALPATH`` now offered by - :ref:`file(REAL_PATH) <REAL_PATH>` command. - .. code-block:: cmake get_filename_component(<var> <FileName> PROGRAM [PROGRAM_ARGS <arg_var>] [CACHE]) @@ -59,3 +53,9 @@ left as a full path. If ``PROGRAM_ARGS`` is present with ``PROGRAM``, then any command-line arguments present in the ``<FileName>`` string are split from the program name and stored in ``<arg_var>``. This is used to separate a program name from its arguments in a command line string. + +.. note:: + + This command been superseded by :command:`cmake_path` command, except + ``REALPATH`` now offered by :ref:`file(REAL_PATH) <REAL_PATH>` command and + ``PROGRAM`` now available in :command:`separate_arguments(PROGRAM)` command. diff --git a/Help/command/separate_arguments.rst b/Help/command/separate_arguments.rst index ab3d5c1..3c094aa 100644 --- a/Help/command/separate_arguments.rst +++ b/Help/command/separate_arguments.rst @@ -5,7 +5,7 @@ Parse command-line arguments into a semicolon-separated list. .. code-block:: cmake - separate_arguments(<variable> <mode> <args>) + separate_arguments(<variable> <mode> [PROGRAM [SEPARATE_ARGS]] <args>) Parses a space-separated string ``<args>`` into a list of items, and stores this list in semicolon-separated standard form in ``<variable>``. @@ -35,6 +35,36 @@ be one of the following keywords: Proceeds as in ``WINDOWS_COMMAND`` mode if the host system is Windows. Otherwise proceeds as in ``UNIX_COMMAND`` mode. +``PROGRAM`` + The first item in ``<args>`` is assumed to be an executable and will be + search in the system search path or left as a full path. If not found, + ``<variable>`` will be empty. Otherwise, ``<variable>`` is a list of 2 + elements: + + 0. Absolute path of the program + 1. Any command-line arguments present in ``<args>`` as a string + + For example: + + .. code-block:: cmake + + separate_arguments (out UNIX_COMMAND PROGRAM "cc -c main.c") + + * First element of the list: ``/path/to/cc`` + * Second element of the list: ``" -c main.c"`` + +``SEPARATE_ARGS`` + When this sub-option of ``PROGRAM`` option is specified, command-line + arguments will be split as well and stored in ``<variable>``. + + For example: + + .. code-block:: cmake + + separate_arguments (out UNIX_COMMAND PROGRAM SEPARATE_ARGS "cc -c main.c") + + The contents of ``out`` will be: ``/path/to/cc;-c;main.c`` + .. _`Parsing C Command-Line Arguments`: https://msdn.microsoft.com/library/a1y7w461.aspx .. code-block:: cmake diff --git a/Help/release/dev/separate_arguments-PROGRAM.rst b/Help/release/dev/separate_arguments-PROGRAM.rst new file mode 100644 index 0000000..428bfda --- /dev/null +++ b/Help/release/dev/separate_arguments-PROGRAM.rst @@ -0,0 +1,5 @@ +separate_arguments-PROGRAM +-------------------------- + +* The :command:`separate_arguments` command gained new ``PROGRAM`` option to + search program. diff --git a/Source/cmSeparateArgumentsCommand.cxx b/Source/cmSeparateArgumentsCommand.cxx index e45f3b0..7e501a2 100644 --- a/Source/cmSeparateArgumentsCommand.cxx +++ b/Source/cmSeparateArgumentsCommand.cxx @@ -4,9 +4,13 @@ #include <algorithm> +#include <cmext/string_view> + +#include "cmArgumentParser.h" #include "cmExecutionStatus.h" #include "cmMakefile.h" #include "cmProperty.h" +#include "cmRange.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -19,86 +23,138 @@ bool cmSeparateArgumentsCommand(std::vector<std::string> const& args, return false; } - std::string var; - std::string command; - enum Mode - { - ModeOld, - ModeUnix, - ModeWindows - }; - Mode mode = ModeOld; - enum Doing - { - DoingNone, - DoingVariable, - DoingMode, - DoingCommand - }; - Doing doing = DoingVariable; - for (std::string const& arg : args) { - if (doing == DoingVariable) { - var = arg; - doing = DoingMode; - // This will always clone one of the other blocks. - // NOLINTNEXTLINE(bugprone-branch-clone) - } else if (doing == DoingMode && arg == "NATIVE_COMMAND") { -#ifdef _WIN32 - mode = ModeWindows; -#else - mode = ModeUnix; -#endif - doing = DoingCommand; - } else if (doing == DoingMode && arg == "UNIX_COMMAND") { - mode = ModeUnix; - doing = DoingCommand; - } else if (doing == DoingMode && arg == "WINDOWS_COMMAND") { - mode = ModeWindows; - doing = DoingCommand; - } else if (doing == DoingCommand) { - command = arg; - doing = DoingNone; - } else { - status.SetError(cmStrCat("given unknown argument ", arg)); - return false; - } - } + std::string const& var = args.front(); - if (mode == ModeOld) { + if (args.size() == 1) { // Original space-replacement version of command. if (cmProp def = status.GetMakefile().GetDefinition(var)) { std::string value = *def; std::replace(value.begin(), value.end(), ' ', ';'); status.GetMakefile().AddDefinition(var, value); } - } else { - // 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); + + return true; + } + + struct Arguments + { + bool UnixCommand = false; + bool WindowsCommand = false; + bool NativeCommand = false; + bool Program = false; + bool SeparateArgs = false; + }; + + static auto const parser = + cmArgumentParser<Arguments>{} + .Bind("UNIX_COMMAND"_s, &Arguments::UnixCommand) + .Bind("WINDOWS_COMMAND"_s, &Arguments::WindowsCommand) + .Bind("NATIVE_COMMAND"_s, &Arguments::NativeCommand) + .Bind("PROGRAM"_s, &Arguments::Program) + .Bind("SEPARATE_ARGS"_s, &Arguments::SeparateArgs); + + std::vector<std::string> unparsedArguments; + Arguments arguments = + parser.Parse(cmMakeRange(args).advance(1), &unparsedArguments); + + if (!arguments.UnixCommand && !arguments.WindowsCommand && + !arguments.NativeCommand) { + status.SetError("missing required option: 'UNIX_COMMAND' or " + "'WINDOWS_COMMAND' or 'NATIVE_COMMAND'"); + return false; + } + if ((arguments.UnixCommand && arguments.WindowsCommand) || + (arguments.UnixCommand && arguments.NativeCommand) || + (arguments.WindowsCommand && arguments.NativeCommand)) { + status.SetError("'UNIX_COMMAND', 'WINDOWS_COMMAND' and 'NATIVE_COMMAND' " + "are mutually exclusive"); + return false; + } + if (arguments.SeparateArgs && !arguments.Program) { + status.SetError("`SEPARATE_ARGS` option requires `PROGRAM' option"); + return false; + } + + if (unparsedArguments.size() > 1) { + status.SetError("given unexpected argument(s)"); + return false; + } + + std::string& command = unparsedArguments.front(); + + if (command.empty()) { + status.GetMakefile().AddDefinition(var, command); + return true; + } + + if (arguments.Program && !arguments.SeparateArgs) { + std::string program; + std::string programArgs; + + // First assume the path to the program was specified with no + // arguments and with no quoting or escaping for spaces. + // Only bother doing this if there is non-whitespace. + if (!cmTrimWhitespace(command).empty()) { + program = cmSystemTools::FindProgram(command); } - // Construct the result list value. - std::string value; - const char* sep = ""; - for (std::string const& vi : vec) { - // Separate from the previous argument. - value += sep; - sep = ";"; - - // Preserve semicolons. - for (char si : vi) { - if (si == ';') { - value += '\\'; + // If that failed then assume a command-line string was given + // and split the program part from the rest of the arguments. + if (program.empty()) { + if (cmSystemTools::SplitProgramFromArgs(command, program, programArgs)) { + if (!cmSystemTools::FileExists(program)) { + program = cmSystemTools::FindProgram(program); } - value += si; } } - status.GetMakefile().AddDefinition(var, value); + + if (!program.empty()) { + program += cmStrCat(';', programArgs); + } + + status.GetMakefile().AddDefinition(var, program); + return true; + } + + // split command given + std::vector<std::string> values; + + if (arguments.NativeCommand) { +#if defined(_WIN32) + arguments.WindowsCommand = true; +#else + arguments.UnixCommand = true; +#endif + } + + if (arguments.UnixCommand) { + cmSystemTools::ParseUnixCommandLine(command.c_str(), values); + } else { + cmSystemTools::ParseWindowsCommandLine(command.c_str(), values); + } + + if (arguments.Program) { + // check program exist + if (!cmSystemTools::FileExists(values.front())) { + auto result = cmSystemTools::FindProgram(values.front()); + if (result.empty()) { + values.clear(); + } else { + values.front() = result; + } + } } + // preserve semicolons in arguments + std::for_each(values.begin(), values.end(), [](std::string& value) { + std::string::size_type pos = 0; + while ((pos = value.find_first_of(';', pos)) != std::string::npos) { + value.insert(pos, 1, '\\'); + pos += 2; + } + }); + auto value = cmJoin(values, ";"); + status.GetMakefile().AddDefinition(var, value); + return true; } diff --git a/Tests/RunCMake/separate_arguments/MultipleArguments-result.txt b/Tests/RunCMake/separate_arguments/MultipleArguments-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/separate_arguments/MultipleArguments-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/separate_arguments/MultipleArguments-stderr.txt b/Tests/RunCMake/separate_arguments/MultipleArguments-stderr.txt new file mode 100644 index 0000000..bbf1cf2 --- /dev/null +++ b/Tests/RunCMake/separate_arguments/MultipleArguments-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at MultipleArguments.cmake:[0-9]+ \(separate_arguments\): + separate_arguments given unexpected argument\(s\) +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/separate_arguments/MultipleArguments.cmake b/Tests/RunCMake/separate_arguments/MultipleArguments.cmake new file mode 100644 index 0000000..d38a03b --- /dev/null +++ b/Tests/RunCMake/separate_arguments/MultipleArguments.cmake @@ -0,0 +1,2 @@ + +separate_arguments (var UNIX_COMMAND arg1 arg2) diff --git a/Tests/RunCMake/separate_arguments/MultipleCommands-result.txt b/Tests/RunCMake/separate_arguments/MultipleCommands-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/separate_arguments/MultipleCommands-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/separate_arguments/MultipleCommands-stderr.txt b/Tests/RunCMake/separate_arguments/MultipleCommands-stderr.txt new file mode 100644 index 0000000..9dca8f6 --- /dev/null +++ b/Tests/RunCMake/separate_arguments/MultipleCommands-stderr.txt @@ -0,0 +1,19 @@ +CMake Error at MultipleCommands.cmake:[0-9]+ \(separate_arguments\): + separate_arguments 'UNIX_COMMAND', 'WINDOWS_COMMAND' and 'NATIVE_COMMAND' + are mutually exclusive +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) + + +CMake Error at MultipleCommands.cmake:[0-9]+ \(separate_arguments\): + separate_arguments 'UNIX_COMMAND', 'WINDOWS_COMMAND' and 'NATIVE_COMMAND' + are mutually exclusive +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) + + +CMake Error at MultipleCommands.cmake:[0-9]+ \(separate_arguments\): + separate_arguments 'UNIX_COMMAND', 'WINDOWS_COMMAND' and 'NATIVE_COMMAND' + are mutually exclusive +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/separate_arguments/MultipleCommands.cmake b/Tests/RunCMake/separate_arguments/MultipleCommands.cmake new file mode 100644 index 0000000..cb71fcb --- /dev/null +++ b/Tests/RunCMake/separate_arguments/MultipleCommands.cmake @@ -0,0 +1,6 @@ + +separate_arguments(var UNIX_COMMAND WINDOWS_COMMAND) + +separate_arguments(var UNIX_COMMAND NATIVE_COMMAND) + +separate_arguments(var WINDOWS_COMMAND NATIVE_COMMAND) diff --git a/Tests/RunCMake/separate_arguments/ProgramCommand.cmake b/Tests/RunCMake/separate_arguments/ProgramCommand.cmake new file mode 100644 index 0000000..bdf5810 --- /dev/null +++ b/Tests/RunCMake/separate_arguments/ProgramCommand.cmake @@ -0,0 +1,48 @@ + +separate_arguments (out UNIX_COMMAND PROGRAM "xx a b c") +if (out) + message (SEND_ERROR "unexpected result with nonexistent program") +endif() + +set (TEST_EXE_DIR "${CMAKE_CURRENT_BINARY_DIR}/TestExe") +file(MAKE_DIRECTORY "${TEST_EXE_DIR}") +file(COPY "${CMAKE_COMMAND}" DESTINATION "${TEST_EXE_DIR}") +cmake_path (GET CMAKE_COMMAND FILENAME cmake_exe) + +set (ENV{PATH} "${TEST_EXE_DIR}") + + +separate_arguments (out UNIX_COMMAND PROGRAM "${cmake_exe}") +list (LENGTH out length) +if (length EQUAL 0) + message(FATAL_ERROR "existent program not found") +endif() +if (NOT length EQUAL 2) + message(FATAL_ERROR "unexpected arguments") +endif() +list(GET out 0 cmake) +list(GET out 1 args) +if (NOT cmake STREQUAL "${TEST_EXE_DIR}/${cmake_exe}") + message (SEND_ERROR "bad path for program: '${cmake}' instead of '${TEST_EXE_DIR}/${cmake_exe}'") +endif() +if (NOT args STREQUAL "") + message (SEND_ERROR "bad value for args: '${args}' instead of ''") +endif() + + +separate_arguments (out UNIX_COMMAND PROGRAM "${cmake_exe} a b c") +list (LENGTH out length) +if (length EQUAL 0) + message(FATAL_ERROR "existent program not found") +endif() +if (NOT length EQUAL 2) + message(FATAL_ERROR "unexpected arguments") +endif() +list(GET out 0 cmake) +list(GET out 1 args) +if (NOT cmake STREQUAL "${TEST_EXE_DIR}/${cmake_exe}") + message (SEND_ERROR "bad path for program: '${cmake}' instead of '${TEST_EXE_DIR}/${cmake_exe}'") +endif() +if (NOT args STREQUAL " a b c") + message (SEND_ERROR "bad value for args: '${args}' instead of ' a b c'") +endif() diff --git a/Tests/RunCMake/separate_arguments/ProgramCommandWithSeparateArgs.cmake b/Tests/RunCMake/separate_arguments/ProgramCommandWithSeparateArgs.cmake new file mode 100644 index 0000000..2826cc9 --- /dev/null +++ b/Tests/RunCMake/separate_arguments/ProgramCommandWithSeparateArgs.cmake @@ -0,0 +1,28 @@ + +separate_arguments (out UNIX_COMMAND PROGRAM SEPARATE_ARGS "xx a b c") +if (out) + message (SEND_ERROR "unexpected result with nonexistent program") +endif() + +set (TEST_EXE_DIR "${CMAKE_CURRENT_BINARY_DIR}/TestExe") +file(MAKE_DIRECTORY "${TEST_EXE_DIR}") +file(COPY "${CMAKE_COMMAND}" DESTINATION "${TEST_EXE_DIR}") +cmake_path (GET CMAKE_COMMAND FILENAME cmake_exe) + +set (ENV{PATH} "${TEST_EXE_DIR}") + +separate_arguments (out UNIX_COMMAND PROGRAM SEPARATE_ARGS "${cmake_exe} a b c") +list (LENGTH out length) +if (length EQUAL 0) + message(FATAL_ERROR "existent program not found") +endif() +if (NOT length EQUAL 4) + message(FATAL_ERROR "unexpected arguments") +endif() +list(POP_FRONT out cmake) +if (NOT cmake STREQUAL "${TEST_EXE_DIR}/${cmake_exe}") + message (SEND_ERROR "bad path for program: '${cmake}' instead of '${TEST_EXE_DIR}/${cmake_exe}'") +endif() +if (NOT out STREQUAL "a;b;c") + message (SEND_ERROR "bad path for args: '${out}' instead of 'a;b;c'") +endif() diff --git a/Tests/RunCMake/separate_arguments/ProgramOnly-result.txt b/Tests/RunCMake/separate_arguments/ProgramOnly-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/separate_arguments/ProgramOnly-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/separate_arguments/ProgramOnly-stderr.txt b/Tests/RunCMake/separate_arguments/ProgramOnly-stderr.txt new file mode 100644 index 0000000..e34ca4c --- /dev/null +++ b/Tests/RunCMake/separate_arguments/ProgramOnly-stderr.txt @@ -0,0 +1,5 @@ +CMake Error at ProgramOnly.cmake:[0-9]+ \(separate_arguments\): + separate_arguments missing required option: 'UNIX_COMMAND' or + 'WINDOWS_COMMAND' or 'NATIVE_COMMAND' +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/separate_arguments/ProgramOnly.cmake b/Tests/RunCMake/separate_arguments/ProgramOnly.cmake new file mode 100644 index 0000000..cc21d2b --- /dev/null +++ b/Tests/RunCMake/separate_arguments/ProgramOnly.cmake @@ -0,0 +1,2 @@ + +separate_arguments (var PROGRAM arg) diff --git a/Tests/RunCMake/separate_arguments/RunCMakeTest.cmake b/Tests/RunCMake/separate_arguments/RunCMakeTest.cmake index 07951bb..3c02c49 100644 --- a/Tests/RunCMake/separate_arguments/RunCMakeTest.cmake +++ b/Tests/RunCMake/separate_arguments/RunCMakeTest.cmake @@ -1,7 +1,15 @@ include(RunCMake) +run_cmake(MultipleCommands) +run_cmake(MultipleArguments) +run_cmake(ProgramOnly) +run_cmake(SeparateArgsOnly) + run_cmake(EmptyCommand) run_cmake(PlainCommand) run_cmake(UnixCommand) run_cmake(WindowsCommand) run_cmake(NativeCommand) + +run_cmake(ProgramCommand) +run_cmake(ProgramCommandWithSeparateArgs) diff --git a/Tests/RunCMake/separate_arguments/SeparateArgsOnly-result.txt b/Tests/RunCMake/separate_arguments/SeparateArgsOnly-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/separate_arguments/SeparateArgsOnly-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/separate_arguments/SeparateArgsOnly-stderr.txt b/Tests/RunCMake/separate_arguments/SeparateArgsOnly-stderr.txt new file mode 100644 index 0000000..e87b1cb --- /dev/null +++ b/Tests/RunCMake/separate_arguments/SeparateArgsOnly-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at SeparateArgsOnly.cmake:[0-9]+ \(separate_arguments\): + separate_arguments `SEPARATE_ARGS` option requires `PROGRAM' option +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/separate_arguments/SeparateArgsOnly.cmake b/Tests/RunCMake/separate_arguments/SeparateArgsOnly.cmake new file mode 100644 index 0000000..876fd7c --- /dev/null +++ b/Tests/RunCMake/separate_arguments/SeparateArgsOnly.cmake @@ -0,0 +1,2 @@ + +separate_arguments (var UNIX_COMMAND SEPARATE_ARGS arg) |