Browse Source

Ninja: Fix command concatenation on Windows

Put commands that contain `||` into brackets to avoid early abort of
execution by `cmd.exe` because `||` has higher precedence than `&&` in
`cmd.exe`.

Add test to check for command execution after `||` as part of a
parameter and as command separator.

Fixes: #16850
Bernhard Burgermeister 8 years ago
parent
commit
5e0e03d953

+ 7 - 1
Source/cmLocalNinjaGenerator.cxx

@@ -322,7 +322,13 @@ std::string cmLocalNinjaGenerator::BuildCommandLine(
     } else if (cmdLines.size() > 1) {
       cmd << "cmd.exe /C \"";
     }
-    cmd << *li;
+    // Put current cmdLine in brackets if it contains "||" because it has
+    // higher precedence than "&&" in cmd.exe
+    if (li->find("||") != std::string::npos) {
+      cmd << "( " << *li << " )";
+    } else {
+      cmd << *li;
+    }
   }
   if (cmdLines.size() > 1) {
     cmd << "\"";

+ 14 - 0
Tests/RunCMake/Ninja/CommandConcat.cmake

@@ -0,0 +1,14 @@
+cmake_minimum_required(VERSION 3.7)
+project(concat_cmd NONE)
+set(output1 ${CMAKE_BINARY_DIR}/out1.txt)
+set(output2 ${CMAKE_BINARY_DIR}/out2.txt)
+file(REMOVE ${output1} ${output2})
+# Check that second command runs if first command contains "||" which has higher precedence than "&&" on Windows
+add_custom_target(concat_cmd ALL
+  COMMAND ${CMAKE_COMMAND} -E echo "Hello || pipe world" && ${CMAKE_COMMAND} -E touch ${output1} || exit 1
+  COMMAND ${CMAKE_COMMAND} -E touch ${output2})
+# Check output
+add_custom_target(check_output ALL
+  COMMAND ${CMAKE_COMMAND} -E copy ${output1} ${output1}.copy
+  COMMAND ${CMAKE_COMMAND} -E copy ${output2} ${output2}.copy)
+add_dependencies(check_output concat_cmd)

+ 10 - 0
Tests/RunCMake/Ninja/RunCMakeTest.cmake

@@ -40,6 +40,16 @@ run_CMP0058(NEW-by)
 
 run_cmake(CustomCommandDepfile)
 
+function(run_CommandConcat)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CommandConcat-build)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
+  file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
+  run_cmake(CommandConcat)
+  run_cmake_command(CommandConcat-build ${CMAKE_COMMAND} --build .)
+endfunction()
+run_CommandConcat()
+
 function(run_SubDir)
   # Use a single build tree for a few tests without cleaning.
   set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/SubDir-build)