mirror of
https://github.com/Kitware/CMake.git
synced 2025-10-19 19:43:23 +08:00
execute_process: Fix invocation of .cmd/.bat with spaces
The `CreateProcessW` documentation states "to run a batch file, you must start the command interpreter". Use `cmd /c the.bat` to run batch files. Also, use a "short path" to the `.bat` file if needed to avoid spaces. Previously this worked in some cases only due to undocumented behavior of `CreateProcessW` when given a `.bat` file. Fixes: #26655
This commit is contained in:
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -25,6 +25,7 @@ configure eol=lf
|
|||||||
|
|
||||||
*.bat eol=crlf
|
*.bat eol=crlf
|
||||||
*.bat.in eol=crlf
|
*.bat.in eol=crlf
|
||||||
|
*.cmd eol=crlf
|
||||||
*.sln eol=crlf
|
*.sln eol=crlf
|
||||||
*.vcproj eol=crlf
|
*.vcproj eol=crlf
|
||||||
|
|
||||||
|
@@ -48,8 +48,14 @@ Options:
|
|||||||
child process in an ``argv[]`` style array.
|
child process in an ``argv[]`` style array.
|
||||||
|
|
||||||
* On Windows platforms, the command line is encoded as a string such
|
* On Windows platforms, the command line is encoded as a string such
|
||||||
that child processes using ``CommandLineToArgvW`` will decode the
|
that child processes using `CommandLineToArgvW`_ will decode the
|
||||||
original arguments.
|
original arguments. If the command runs a ``.bat`` or ``.cmd``
|
||||||
|
script, it may receive arguments with extra quoting.
|
||||||
|
|
||||||
|
* .. versionchanged:: 4.0
|
||||||
|
On Windows platforms, if the command runs a ``.bat`` or ``.cmd`` script,
|
||||||
|
it is automatically executed through the command interpreter, ``cmd /c``.
|
||||||
|
However, paths with spaces may fail if a "short path" is not available.
|
||||||
|
|
||||||
No intermediate shell is used, so shell operators such as ``>``
|
No intermediate shell is used, so shell operators such as ``>``
|
||||||
are treated as normal arguments.
|
are treated as normal arguments.
|
||||||
@@ -197,3 +203,5 @@ Options:
|
|||||||
is checked. If the variable is not set, the default is ``NONE``.
|
is checked. If the variable is not set, the default is ``NONE``.
|
||||||
If ``RESULT_VARIABLE`` or ``RESULTS_VARIABLE`` is supplied,
|
If ``RESULT_VARIABLE`` or ``RESULTS_VARIABLE`` is supplied,
|
||||||
:variable:`CMAKE_EXECUTE_PROCESS_COMMAND_ERROR_IS_FATAL` is ignored.
|
:variable:`CMAKE_EXECUTE_PROCESS_COMMAND_ERROR_IS_FATAL` is ignored.
|
||||||
|
|
||||||
|
.. _`CommandLineToArgvW`: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-commandlinetoargvw
|
||||||
|
@@ -120,7 +120,7 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args,
|
|||||||
.Bind("COMMAND_ERROR_IS_FATAL"_s, &Arguments::CommandErrorIsFatal);
|
.Bind("COMMAND_ERROR_IS_FATAL"_s, &Arguments::CommandErrorIsFatal);
|
||||||
|
|
||||||
std::vector<std::string> unparsedArguments;
|
std::vector<std::string> unparsedArguments;
|
||||||
Arguments const arguments = parser.Parse(args, &unparsedArguments);
|
Arguments arguments = parser.Parse(args, &unparsedArguments);
|
||||||
|
|
||||||
if (arguments.MaybeReportError(status.GetMakefile())) {
|
if (arguments.MaybeReportError(status.GetMakefile())) {
|
||||||
return true;
|
return true;
|
||||||
@@ -161,11 +161,21 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args,
|
|||||||
status.SetError(" called with no COMMAND argument.");
|
status.SetError(" called with no COMMAND argument.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (std::vector<std::string> const& cmd : arguments.Commands) {
|
for (std::vector<std::string>& cmd : arguments.Commands) {
|
||||||
if (cmd.empty()) {
|
if (cmd.empty()) {
|
||||||
status.SetError(" given COMMAND argument with no value.");
|
status.SetError(" given COMMAND argument with no value.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#ifdef _WIN32
|
||||||
|
cmsys::Status shortPathRes = cmSystemTools::MaybePrependCmdExe(cmd);
|
||||||
|
if (!shortPathRes) {
|
||||||
|
status.GetMakefile().IssueMessage(
|
||||||
|
MessageType::WARNING,
|
||||||
|
cmStrCat("Conversion of COMMAND:\n ", cmd[2], '\n',
|
||||||
|
"to a short path without spaces failed:\n ",
|
||||||
|
shortPathRes.GetString()));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the timeout string.
|
// Parse the timeout string.
|
||||||
|
@@ -63,6 +63,14 @@ if(WIN32 OR CYGWIN)
|
|||||||
run_cmake_command(WindowsNoExtension-build ${CMAKE_COMMAND} --build . --config Debug --target RunScript)
|
run_cmake_command(WindowsNoExtension-build ${CMAKE_COMMAND} --build . --config Debug --target RunScript)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(CMAKE_HOST_WIN32
|
||||||
|
# By default, only C: has short paths enabled.
|
||||||
|
# Since querying with `fsutil 8dot3name query C:`
|
||||||
|
# requires admin, just test the drive letter.
|
||||||
|
AND RunCMake_SOURCE_DIR MATCHES "^[Cc]:")
|
||||||
|
run_cmake_command(WindowsBatch ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/WindowsBatch.cmake)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(TEST_STARTUPINFO_EXE)
|
if(TEST_STARTUPINFO_EXE)
|
||||||
run_cmake_script(StartupInfo -DTEST_STARTUPINFO_EXE=${TEST_STARTUPINFO_EXE})
|
run_cmake_script(StartupInfo -DTEST_STARTUPINFO_EXE=${TEST_STARTUPINFO_EXE})
|
||||||
endif()
|
endif()
|
||||||
|
8
Tests/RunCMake/execute_process/WindowsBatch-stdout.txt
Normal file
8
Tests/RunCMake/execute_process/WindowsBatch-stdout.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
^bat arg1 - arg2 -
|
||||||
|
bat "arg1 space" - arg2 -
|
||||||
|
bat arg1 - "arg2 space" -
|
||||||
|
bat "arg1 space" - "arg2 space" -
|
||||||
|
cmd arg1 - arg2 -
|
||||||
|
cmd "arg1 space" - arg2 -
|
||||||
|
cmd arg1 - "arg2 space" -
|
||||||
|
cmd "arg1 space" - "arg2 space" -$
|
8
Tests/RunCMake/execute_process/WindowsBatch.cmake
Normal file
8
Tests/RunCMake/execute_process/WindowsBatch.cmake
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
execute_process(COMMAND "${CMAKE_CURRENT_LIST_DIR}/with space.bat" arg1 arg2)
|
||||||
|
execute_process(COMMAND "${CMAKE_CURRENT_LIST_DIR}/with space.Bat" "arg1 space" arg2)
|
||||||
|
execute_process(COMMAND "${CMAKE_CURRENT_LIST_DIR}/with space.bAT" arg1 "arg2 space")
|
||||||
|
execute_process(COMMAND "${CMAKE_CURRENT_LIST_DIR}/with space.BAT" "arg1 space" "arg2 space")
|
||||||
|
execute_process(COMMAND "${CMAKE_CURRENT_LIST_DIR}/with space.cmd" arg1 arg2)
|
||||||
|
execute_process(COMMAND "${CMAKE_CURRENT_LIST_DIR}/with space.Cmd" "arg1 space" arg2)
|
||||||
|
execute_process(COMMAND "${CMAKE_CURRENT_LIST_DIR}/with space.cMD" arg1 "arg2 space")
|
||||||
|
execute_process(COMMAND "${CMAKE_CURRENT_LIST_DIR}/with space.CMD" "arg1 space" "arg2 space")
|
1
Tests/RunCMake/execute_process/with space.bat
Executable file
1
Tests/RunCMake/execute_process/with space.bat
Executable file
@@ -0,0 +1 @@
|
|||||||
|
@echo bat %1 - %2 -
|
1
Tests/RunCMake/execute_process/with space.cmd
Executable file
1
Tests/RunCMake/execute_process/with space.cmd
Executable file
@@ -0,0 +1 @@
|
|||||||
|
@echo cmd %1 - %2 -
|
Reference in New Issue
Block a user