فهرست منبع

Ninja: Fix depfile binding with spaces in path

In build statements we use a single `DEP_FILE = ...` binding that is
shared between `depfile = $DEP_FILE` and `command = ... $DEP_FILE ...`
bindings in the corresponding rule.  In cases that the command's shell
argument needs quoting, add a separate `depfile = ...` binding to the
build statement to express the depfile path without quoting.  Otherwise
`ninja` tries to open a file path that contains literal quotes.

Fixes: #26287
Brad King 1 سال پیش
والد
کامیت
b11323f1e4

+ 10 - 4
Source/cmNinjaTargetGenerator.cxx

@@ -2495,16 +2495,22 @@ void cmNinjaTargetGenerator::MacOSXContentGeneratorType::operator()(
   this->Generator->Configs[config].ExtraFiles.push_back(std::move(output));
 }
 
-void cmNinjaTargetGenerator::AddDepfileBinding(
-  cmNinjaVars& vars, std::string const& depfile) const
+void cmNinjaTargetGenerator::AddDepfileBinding(cmNinjaVars& vars,
+                                               std::string depfile) const
 {
-  vars["DEP_FILE"] = this->GetLocalGenerator()->ConvertToOutputFormat(
-    depfile, cmOutputConverter::SHELL);
+  std::string depfileForShell =
+    this->GetLocalGenerator()->ConvertToOutputFormat(depfile,
+                                                     cmOutputConverter::SHELL);
+  if (depfile != depfileForShell) {
+    vars["depfile"] = std::move(depfile);
+  }
+  vars["DEP_FILE"] = std::move(depfileForShell);
 }
 
 void cmNinjaTargetGenerator::RemoveDepfileBinding(cmNinjaVars& vars) const
 {
   vars.erase("DEP_FILE");
+  vars.erase("depfile");
 }
 
 void cmNinjaTargetGenerator::addPoolNinjaVariable(

+ 1 - 1
Source/cmNinjaTargetGenerator.h

@@ -42,7 +42,7 @@ public:
 
   std::string GetTargetName() const;
 
-  void AddDepfileBinding(cmNinjaVars& vars, std::string const& depfile) const;
+  void AddDepfileBinding(cmNinjaVars& vars, std::string depfile) const;
   void RemoveDepfileBinding(cmNinjaVars& vars) const;
 
 protected:

+ 17 - 0
Tests/RunCMake/BuildDepends/CompileDepends.cmake

@@ -3,13 +3,29 @@ enable_language(C)
 add_executable(main CompileDepends.c)
 target_include_directories(main PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
 
+set(CODE_WITH_SPACE [[
+add_executable(main2 ../CompileDepends.c)
+target_include_directories(main2 PRIVATE ${CMAKE_BINARY_DIR})
+]])
+if(MAKE_SUPPORTS_SPACES)
+  add_subdirectory("With Space")
+  set(check_pairs_with_space "
+    \"$<TARGET_FILE:main2>|${CMAKE_CURRENT_BINARY_DIR}/CompileDepends.h\"
+  ")
+  set(check_exes_with_space "
+    \"$<TARGET_FILE:main2>\"
+  ")
+endif()
+
 file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/check-$<LOWER_CASE:$<CONFIG>>.cmake CONTENT "
 cmake_minimum_required(VERSION ${CMAKE_VERSION})
 set(check_pairs
   \"$<TARGET_FILE:main>|${CMAKE_CURRENT_BINARY_DIR}/CompileDepends.h\"
+  ${check_pairs_with_space}
   )
 set(check_exes
   \"$<TARGET_FILE:main>\"
+  ${check_exes_with_space}
   )
 
 if (RunCMake_GENERATOR MATCHES \"Make\" AND check_step EQUAL 2)
@@ -17,6 +33,7 @@ if (RunCMake_GENERATOR MATCHES \"Make\" AND check_step EQUAL 2)
   if (NOT CMAKE_DEPEND_INFO_FILES)
     set(RunCMake_TEST_FAILED \"Variable CMAKE_DEPEND_INFO_FILES not found.\")
   else()
+    list(FILTER CMAKE_DEPEND_INFO_FILES EXCLUDE REGEX main2)
     foreach(DEPEND_INFO_FILE IN LISTS CMAKE_DEPEND_INFO_FILES)
       include(\"${CMAKE_CURRENT_BINARY_DIR}/\${DEPEND_INFO_FILE}\")
       if (NOT CMAKE_DEPENDS_DEPENDENCY_FILES)

+ 26 - 0
Tests/RunCMake/BuildDepends/CustomCommandDepends.cmake

@@ -1,3 +1,4 @@
+cmake_policy(SET CMP0116 NEW)
 enable_language(C)
 
 add_custom_command(OUTPUT main.c
@@ -10,14 +11,38 @@ add_custom_target(mainc ALL DEPENDS main.c)
 
 add_executable(main ${CMAKE_CURRENT_BINARY_DIR}/main.c)
 
+set(CODE_WITH_SPACE [[
+add_custom_command(OUTPUT main2.c
+  DEPFILE main2.c.d
+  COMMAND "${CMAKE_COMMAND}" -DINFILE=../main.c.in -DOUTFILE=main2.c -DDEPFILE=main2.c.d
+  -P "${CMAKE_SOURCE_DIR}/GenerateDepFile.cmake"
+  WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
+
+add_custom_target(main2c ALL DEPENDS main2.c)
+
+add_executable(main2 ${CMAKE_CURRENT_BINARY_DIR}/main2.c)
+]])
+if(MAKE_SUPPORTS_SPACES)
+  add_subdirectory("With Space")
+  set(check_pairs_with_space "
+    \"$<TARGET_FILE:main2>|${CMAKE_CURRENT_BINARY_DIR}/main.c.in\"
+    \"$<TARGET_FILE:main2>|${CMAKE_CURRENT_BINARY_DIR}/With Space/main2.c\"
+  ")
+  set(check_exes_with_space "
+    \"$<TARGET_FILE:main2>\"
+  ")
+endif()
+
 file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/check-$<LOWER_CASE:$<CONFIG>>.cmake CONTENT "
 cmake_minimum_required(VERSION 3.19)
 set(check_pairs
   \"$<TARGET_FILE:main>|${CMAKE_CURRENT_BINARY_DIR}/main.c.in\"
   \"$<TARGET_FILE:main>|${CMAKE_CURRENT_BINARY_DIR}/main.c\"
+  ${check_pairs_with_space}
   )
 set(check_exes
   \"$<TARGET_FILE:main>\"
+  ${check_exes_with_space}
   )
 
 if (RunCMake_GENERATOR MATCHES \"Make\" AND check_step EQUAL 2)
@@ -25,6 +50,7 @@ if (RunCMake_GENERATOR MATCHES \"Make\" AND check_step EQUAL 2)
   if (NOT CMAKE_DEPEND_INFO_FILES)
     set(RunCMake_TEST_FAILED \"Variable CMAKE_DEPEND_INFO_FILES not found.\")
   else()
+    list(FILTER CMAKE_DEPEND_INFO_FILES EXCLUDE REGEX main2)
     foreach(DEPEND_INFO_FILE IN LISTS CMAKE_DEPEND_INFO_FILES)
       include(\"${CMAKE_CURRENT_BINARY_DIR}/\${DEPEND_INFO_FILE}\")
       if (NOT CMAKE_DEPENDS_DEPENDENCY_FILES)

+ 18 - 0
Tests/RunCMake/BuildDepends/LinkDepends.cmake

@@ -11,10 +11,28 @@ add_executable(LinkDependsExe LinkDependsExe.c)
 target_link_directories(LinkDependsExe PRIVATE "${EXTERNAL_DIR}")
 target_link_libraries(LinkDependsExe PRIVATE External)
 
+set(CODE_WITH_SPACE [[
+add_library(LinkDependsLib2 SHARED ../LinkDependsLib.c)
+target_link_directories(LinkDependsLib2 PRIVATE "${EXTERNAL_DIR}")
+target_link_libraries(LinkDependsLib2 PRIVATE External)
+
+add_executable(LinkDependsExe2 ../LinkDependsExe.c)
+target_link_directories(LinkDependsExe2 PRIVATE "${EXTERNAL_DIR}")
+target_link_libraries(LinkDependsExe2 PRIVATE External)
+]])
+if(MAKE_SUPPORTS_SPACES)
+  add_subdirectory("With Space")
+  set(check_pairs_with_space "
+    \"$<TARGET_FILE:LinkDependsLib2>|${EXTERNAL_LIBRARY}\"
+    \"$<TARGET_FILE:LinkDependsExe2>|${EXTERNAL_LIBRARY}\"
+  ")
+endif()
+
 file(GENERATE OUTPUT "${CMAKE_BINARY_DIR}/check-$<LOWER_CASE:$<CONFIG>>.cmake"
   CONTENT "
 set(check_pairs
   \"$<TARGET_FILE:LinkDependsLib>|${EXTERNAL_LIBRARY}\"
   \"$<TARGET_FILE:LinkDependsExe>|${EXTERNAL_LIBRARY}\"
+  ${check_pairs_with_space}
   )
 ")

+ 4 - 3
Tests/RunCMake/BuildDepends/RunCMakeTest.cmake

@@ -33,6 +33,7 @@ function(run_BuildDepends CASE)
   if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
     set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug)
   endif()
+  list(APPEND RunCMake_TEST_OPTIONS ${ARGN})
   file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
   file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
   include(${RunCMake_SOURCE_DIR}/${CASE}.step1.cmake OPTIONAL)
@@ -173,8 +174,8 @@ if ((RunCMake_GENERATOR STREQUAL "Unix Makefiles"
       AND CMAKE_C_COMPILER_ID STREQUAL "MSVC")
     OR RunCMake_GENERATOR MATCHES "Ninja"
     )
-  run_BuildDepends(CompileDepends)
-  run_BuildDepends(CustomCommandDepends)
+  run_BuildDepends(CompileDepends -DMAKE_SUPPORTS_SPACES=${MAKE_SUPPORTS_SPACES})
+  run_BuildDepends(CustomCommandDepends -DMAKE_SUPPORTS_SPACES=${MAKE_SUPPORTS_SPACES})
 endif()
 
 if (RunCMake_GENERATOR MATCHES "Makefiles")
@@ -210,6 +211,6 @@ if (RunCMake_GENERATOR MATCHES "Make|Ninja")
       AND CMAKE_C_LINK_DEPENDS_USE_LINKER)
     run_BuildDepends(LinkDependsExternalLibrary)
     unset(run_BuildDepends_skip_step_2)
-    run_BuildDepends(LinkDepends)
+    run_BuildDepends(LinkDepends -DMAKE_SUPPORTS_SPACES=${MAKE_SUPPORTS_SPACES})
   endif()
 endif()

+ 1 - 0
Tests/RunCMake/BuildDepends/With Space/CMakeLists.txt

@@ -0,0 +1 @@
+cmake_language(EVAL CODE "${CODE_WITH_SPACE}")

+ 1 - 0
Tests/RunCMake/CMakeLists.txt

@@ -340,6 +340,7 @@ if(CMAKE_Fortran_COMPILER)
 endif()
 
 add_RunCMake_test(BuildDepends
+  -DMAKE_SUPPORTS_SPACES=${MAKE_SUPPORTS_SPACES}
   -DMSVC_VERSION=${MSVC_VERSION}
   -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID}
   -DCMAKE_C_LINK_DEPENDS_USE_COMPILER=${CMAKE_C_LINK_DEPENDS_USE_COMPILER}