From ad16c42b20261c2b7d09802b92999217889d3bca Mon Sep 17 00:00:00 2001
From: Martin Ankerl <martin.ankerl@gmail.com>
Date: Fri, 20 May 2016 09:23:56 +0200
Subject: [PATCH] Support for many custom commands in Windows.

Windows has a limit of 8191 characters per command line, so this patch
creates a batch file instead if the commands get too long.
---
 Source/cmLocalNinjaGenerator.cxx         | 27 +++++++++++++++++++++++++--
 Source/cmLocalNinjaGenerator.h           |  2 +-
 Source/cmNinjaUtilityTargetGenerator.cxx |  2 +-
 3 files changed, 27 insertions(+), 4 deletions(-)

diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx
index c7e1a90f8..31fc90c 100644
--- a/Source/cmLocalNinjaGenerator.cxx
+++ b/Source/cmLocalNinjaGenerator.cxx
@@ -286,7 +286,7 @@ void cmLocalNinjaGenerator::AppendCustomCommandDeps(
 }
 
 std::string cmLocalNinjaGenerator::BuildCommandLine(
-  const std::vector<std::string>& cmdLines)
+  const std::vector<std::string>& cmdLines, const std::string& cmdFile)
 {
   // If we have no commands but we need to build a command anyway, use ":".
   // This happens when building a POST_BUILD value for link targets that
@@ -299,9 +299,9 @@ std::string cmLocalNinjaGenerator::BuildCommandLine(
 #endif
 
   std::ostringstream cmd;
+#ifdef _WIN32
   for (std::vector<std::string>::const_iterator li = cmdLines.begin();
        li != cmdLines.end(); ++li)
-#ifdef _WIN32
   {
     if (li != cmdLines.begin()) {
       cmd << " && ";
@@ -313,7 +313,30 @@ std::string cmLocalNinjaGenerator::BuildCommandLine(
   if (cmdLines.size() > 1) {
     cmd << "\"";
   }
+
+  // windows command line cannot be longer than 8191 https://support.microsoft.com/en-us/kb/830473
+  // TODO fail if command is too long and no file specified
+  if (cmd.str().size() > 8191 && !cmdFile.empty()) {
+    // cmd just calls the batch file
+    cmd.clear();
+    cmd.str("");
+    cmd << "cmd.exe /C " << cmdFile;
+
+    std::ofstream fout(cmdFile);
+    // TODO fail if can't open file
+    for (std::vector<std::string>::const_iterator li = cmdLines.begin();
+      li != cmdLines.end(); ++li)
+    {
+      if (li != cmdLines.begin()) {
+        fout << "@if %errorlevel% neq 0 exit /b %errorlevel%" << std::endl;
+      }
+      fout << *li << std::endl;
+    }
+  }
+
 #else
+  for (std::vector<std::string>::const_iterator li = cmdLines.begin();
+    li != cmdLines.end(); ++li)
   {
     if (li != cmdLines.begin()) {
       cmd << " && ";
diff --git a/Source/cmLocalNinjaGenerator.h b/Source/cmLocalNinjaGenerator.h
index afaa24c..ed67679 100644
--- a/Source/cmLocalNinjaGenerator.h
+++ b/Source/cmLocalNinjaGenerator.h
@@ -60,7 +60,7 @@ public:
     cmLocalGenerator::ExpandRuleVariables(string, replaceValues);
   }
 
-  std::string BuildCommandLine(const std::vector<std::string>& cmdLines);
+  std::string BuildCommandLine(const std::vector<std::string>& cmdLines, const std::string& cmdFile="");
 
   void AppendTargetOutputs(cmGeneratorTarget* target, cmNinjaDeps& outputs);
   void AppendTargetDepends(cmGeneratorTarget* target, cmNinjaDeps& outputs);
diff --git a/Source/cmNinjaUtilityTargetGenerator.cxx b/Source/cmNinjaUtilityTargetGenerator.cxx
index d07341c..a34299f 100644
--- a/Source/cmNinjaUtilityTargetGenerator.cxx
+++ b/Source/cmNinjaUtilityTargetGenerator.cxx
@@ -95,7 +95,7 @@ void cmNinjaUtilityTargetGenerator::Generate()
       "Utility command for " + this->GetTargetName(), outputs, deps);
   } else {
     std::string command =
-      this->GetLocalGenerator()->BuildCommandLine(commands);
+      this->GetLocalGenerator()->BuildCommandLine(commands, this->GetTargetName() + ".bat");
     const char* echoStr =
       this->GetGeneratorTarget()->GetProperty("EchoString");
     std::string desc;
-- 
2.6.2.windows.1

