1
0
Эх сурвалжийг харах

Ninja: Fix regression with a large number of subdirectories

Since commit f50fb77a4f (Ninja: Regenerate when test or install scripts
are missing, 2024-10-29, v4.0.0-rc1~516^2) the list of paths we pass to
`ninja -t restat` scales with the number of project subdirectories.
Run it in blocks to avoid "command line too long" errors, particularly
on Windows.

Fixes: #26738
Ben Boeckel 8 сар өмнө
parent
commit
5a36d0c9e7

+ 20 - 11
Source/cmGlobalNinjaGenerator.cxx

@@ -7,11 +7,11 @@
 #include <cctype>
 #include <cctype>
 #include <cstdio>
 #include <cstdio>
 #include <functional>
 #include <functional>
+#include <iterator>
 #include <sstream>
 #include <sstream>
 #include <type_traits>
 #include <type_traits>
 #include <utility>
 #include <utility>
 
 
-#include <cm/iterator>
 #include <cm/memory>
 #include <cm/memory>
 #include <cm/optional>
 #include <cm/optional>
 #include <cm/string_view>
 #include <cm/string_view>
@@ -663,6 +663,7 @@ void cmGlobalNinjaGenerator::Generate()
 
 
 void cmGlobalNinjaGenerator::CleanMetaData()
 void cmGlobalNinjaGenerator::CleanMetaData()
 {
 {
+  constexpr size_t ninja_tool_arg_size = 8; // 2 `-_` flags and 4 separators
   auto run_ninja_tool = [this](std::vector<char const*> const& args) {
   auto run_ninja_tool = [this](std::vector<char const*> const& args) {
     std::vector<std::string> command;
     std::vector<std::string> command;
     command.push_back(this->NinjaCommand);
     command.push_back(this->NinjaCommand);
@@ -705,19 +706,27 @@ void cmGlobalNinjaGenerator::CleanMetaData()
     run_ninja_tool({ "recompact" });
     run_ninja_tool({ "recompact" });
   }
   }
   if (this->NinjaSupportsRestatTool && this->OutputPathPrefix.empty()) {
   if (this->NinjaSupportsRestatTool && this->OutputPathPrefix.empty()) {
-    // XXX(ninja): We only list `build.ninja` entry files here because CMake
-    // *always* rewrites these files on a reconfigure. If CMake ever gets
-    // smarter about this, all CMake-time created/edited files listed as
-    // outputs for the reconfigure build statement will need to be listed here.
     cmNinjaDeps outputs;
     cmNinjaDeps outputs;
     this->AddRebuildManifestOutputs(outputs);
     this->AddRebuildManifestOutputs(outputs);
-    std::vector<char const*> args;
-    args.reserve(outputs.size() + 1);
-    args.push_back("restat");
-    for (auto const& output : outputs) {
-      args.push_back(output.c_str());
+    auto output_it = outputs.begin();
+    size_t static_arg_size = ninja_tool_arg_size + this->NinjaCommand.size() +
+      this->GetCMakeInstance()->GetHomeOutputDirectory().size();
+    // The Windows command-line length limit is 32768.  Leave plenty.
+    constexpr size_t maximum_arg_size = 30000;
+    while (output_it != outputs.end()) {
+      size_t total_arg_size = static_arg_size;
+      std::vector<char const*> args;
+      args.reserve(std::distance(output_it, outputs.end()) + 1);
+      args.push_back("restat");
+      total_arg_size += 7; // restat + 1
+      while (output_it != outputs.end() &&
+             total_arg_size + output_it->size() + 1 < maximum_arg_size) {
+        args.push_back(output_it->c_str());
+        total_arg_size += output_it->size() + 1;
+        ++output_it;
+      }
+      run_ninja_tool(args);
     }
     }
-    run_ninja_tool(args);
   }
   }
 }
 }
 
 

+ 12 - 0
Tests/RunCMake/Configure/RerunCMakeNinja.cmake

@@ -2,3 +2,15 @@ set(input  ${CMAKE_CURRENT_BINARY_DIR}/input.txt)
 set(stamp  ${CMAKE_CURRENT_BINARY_DIR}/stamp.txt)
 set(stamp  ${CMAKE_CURRENT_BINARY_DIR}/stamp.txt)
 file(READ ${input} content)
 file(READ ${input} content)
 file(WRITE ${stamp} "${content}")
 file(WRITE ${stamp} "${content}")
+
+# Add enough subdirectories to make the total list of paths to 'cmake_install.cmake'
+# files exceed the Windows command-line length limit.
+set(length 0)
+foreach(i RANGE 1 1000)
+  if(length GREATER_EQUAL 32678)
+    break()
+  endif()
+  add_subdirectory(RerunCMakeNinja RerunCMakeNinja${i})
+  string(LENGTH "${CMAKE_CURRENT_BINARY_DIR}/RerunCMakeNinja${i}/cmake_install.cmake" subdir_length)
+  math(EXPR length "${length} + ${subdir_length}")
+endforeach()

+ 1 - 0
Tests/RunCMake/Configure/RerunCMakeNinja/CMakeLists.txt

@@ -0,0 +1 @@
+# Empty subdirectory, but it has a 'cmake_install.cmake'.