From ad08f93ee40a1cc855c403c1cc738237511224e3 Mon Sep 17 00:00:00 2001
From: Kyle Edwards <kyle.edwards@kitware.com>
Date: Fri, 30 Apr 2021 14:45:19 -0400
Subject: Ninja Multi-Config: Split long command lines by config

Fixes: #22123
---
 Source/cmLocalNinjaGenerator.cxx                   | 17 ++++++++----
 Source/cmLocalNinjaGenerator.h                     |  5 +++-
 Source/cmNinjaNormalTargetGenerator.cxx            | 30 ++++++++++++++--------
 Source/cmNinjaTargetGenerator.cxx                  | 25 ++++++++++--------
 Source/cmNinjaTargetGenerator.h                    |  3 ++-
 Source/cmNinjaUtilityTargetGenerator.cxx           |  4 +--
 ...LongCommandLine-release-config-build-stdout.txt |  2 ++
 .../NinjaMultiConfig/LongCommandLine.cmake         |  7 +++++
 Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake |  3 +++
 9 files changed, 66 insertions(+), 30 deletions(-)
 create mode 100644 Tests/RunCMake/NinjaMultiConfig/LongCommandLine-release-config-build-stdout.txt

diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx
index 51ad993..081cc41 100644
--- a/Source/cmLocalNinjaGenerator.cxx
+++ b/Source/cmLocalNinjaGenerator.cxx
@@ -407,7 +407,8 @@ void cmLocalNinjaGenerator::AppendCustomCommandDeps(
 }
 
 std::string cmLocalNinjaGenerator::WriteCommandScript(
-  std::vector<std::string> const& cmdLines, std::string const& customStep,
+  std::vector<std::string> const& cmdLines, std::string const& outputConfig,
+  std::string const& commandConfig, std::string const& customStep,
   cmGeneratorTarget const* target) const
 {
   std::string scriptPath;
@@ -416,9 +417,13 @@ std::string cmLocalNinjaGenerator::WriteCommandScript(
   } else {
     scriptPath = cmStrCat(this->GetCurrentBinaryDirectory(), "/CMakeFiles");
   }
+  scriptPath += this->GetGlobalNinjaGenerator()->ConfigDirectory(outputConfig);
   cmSystemTools::MakeDirectory(scriptPath);
   scriptPath += '/';
   scriptPath += customStep;
+  if (this->GlobalGenerator->IsMultiConfig()) {
+    scriptPath += cmStrCat('-', commandConfig);
+  }
 #ifdef _WIN32
   scriptPath += ".bat";
 #else
@@ -461,7 +466,8 @@ std::string cmLocalNinjaGenerator::WriteCommandScript(
 }
 
 std::string cmLocalNinjaGenerator::BuildCommandLine(
-  std::vector<std::string> const& cmdLines, std::string const& customStep,
+  std::vector<std::string> const& cmdLines, std::string const& outputConfig,
+  std::string const& commandConfig, std::string const& customStep,
   cmGeneratorTarget const* target) const
 {
   // If we have no commands but we need to build a command anyway, use noop.
@@ -480,8 +486,8 @@ std::string cmLocalNinjaGenerator::BuildCommandLine(
       cmdLinesTotal += cmd.length() + 6;
     }
     if (cmdLinesTotal > cmSystemTools::CalculateCommandLineLengthLimit() / 2) {
-      std::string const scriptPath =
-        this->WriteCommandScript(cmdLines, customStep, target);
+      std::string const scriptPath = this->WriteCommandScript(
+        cmdLines, outputConfig, commandConfig, customStep, target);
       std::string cmd
 #ifndef _WIN32
         = "/bin/sh "
@@ -694,7 +700,8 @@ void cmLocalNinjaGenerator::WriteCustomCommandBuildStatement(
       }
 
       gg->WriteCustomCommandBuild(
-        this->BuildCommandLine(cmdLines, customStep),
+        this->BuildCommandLine(cmdLines, ccg.GetOutputConfig(), fileConfig,
+                               customStep),
         this->ConstructComment(ccg), "Custom command for " + ninjaOutputs[0],
         depfile, cc->GetJobPool(), cc->GetUsesTerminal(),
         /*restat*/ !symbolic || !byproducts.empty(), ninjaOutputs, fileConfig,
diff --git a/Source/cmLocalNinjaGenerator.h b/Source/cmLocalNinjaGenerator.h
index 5b850f3..0734c5c 100644
--- a/Source/cmLocalNinjaGenerator.h
+++ b/Source/cmLocalNinjaGenerator.h
@@ -60,7 +60,8 @@ public:
   }
 
   std::string BuildCommandLine(
-    std::vector<std::string> const& cmdLines,
+    std::vector<std::string> const& cmdLines, std::string const& outputConfig,
+    std::string const& commandConfig,
     std::string const& customStep = std::string(),
     cmGeneratorTarget const* target = nullptr) const;
 
@@ -119,6 +120,8 @@ private:
   std::string MakeCustomLauncher(cmCustomCommandGenerator const& ccg);
 
   std::string WriteCommandScript(std::vector<std::string> const& cmdLines,
+                                 std::string const& outputConfig,
+                                 std::string const& commandConfig,
                                  std::string const& customStep,
                                  cmGeneratorTarget const* target) const;
 
diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx
index 49e5e4c..1597d2c 100644
--- a/Source/cmNinjaNormalTargetGenerator.cxx
+++ b/Source/cmNinjaNormalTargetGenerator.cxx
@@ -287,7 +287,8 @@ void cmNinjaNormalTargetGenerator::WriteNvidiaDeviceLinkRule(
     // If there is no ranlib the command will be ":".  Skip it.
     cm::erase_if(linkCmds, cmNinjaRemoveNoOpCommands());
 
-    rule.Command = this->GetLocalGenerator()->BuildCommandLine(linkCmds);
+    rule.Command =
+      this->GetLocalGenerator()->BuildCommandLine(linkCmds, config, config);
 
     // Write the linker rule with response file if needed.
     rule.Comment =
@@ -310,7 +311,8 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkRules(
   cmNinjaRule rule(this->LanguageLinkerCudaDeviceRule(config));
   rule.Command = this->GetLocalGenerator()->BuildCommandLine(
     { cmStrCat(mf->GetRequiredDefinition("CMAKE_CUDA_DEVICE_LINKER"),
-               " -arch=$ARCH $REGISTER -o=$out $in") });
+               " -arch=$ARCH $REGISTER -o=$out $in") },
+    config, config);
   rule.Comment = "Rule for CUDA device linking.";
   rule.Description = "Linking CUDA $out";
   this->GetGlobalGenerator()->AddRule(rule);
@@ -336,7 +338,8 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkRules(
                                                compileCmd, vars);
 
   rule.Name = this->LanguageLinkerCudaDeviceCompileRule(config);
-  rule.Command = this->GetLocalGenerator()->BuildCommandLine({ compileCmd });
+  rule.Command = this->GetLocalGenerator()->BuildCommandLine({ compileCmd },
+                                                             config, config);
   rule.Comment = "Rule for compiling CUDA device stubs.";
   rule.Description = "Compiling CUDA device stub $out";
   this->GetGlobalGenerator()->AddRule(rule);
@@ -345,7 +348,8 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkRules(
   rule.Command = this->GetLocalGenerator()->BuildCommandLine(
     { cmStrCat(mf->GetRequiredDefinition("CMAKE_CUDA_FATBINARY"),
                " -64 -cmdline=--compile-only -compress-all -link "
-               "--embedded-fatbin=$out $PROFILES") });
+               "--embedded-fatbin=$out $PROFILES") },
+    config, config);
   rule.Comment = "Rule for CUDA fatbinaries.";
   rule.Description = "Creating fatbinary $out";
   this->GetGlobalGenerator()->AddRule(rule);
@@ -475,7 +479,8 @@ void cmNinjaNormalTargetGenerator::WriteLinkRule(bool useResponseFile,
 
     linkCmds.insert(linkCmds.begin(), "$PRE_LINK");
     linkCmds.emplace_back("$POST_BUILD");
-    rule.Command = this->GetLocalGenerator()->BuildCommandLine(linkCmds);
+    rule.Command =
+      this->GetLocalGenerator()->BuildCommandLine(linkCmds, config, config);
 
     // Write the linker rule with response file if needed.
     rule.Comment =
@@ -500,7 +505,8 @@ void cmNinjaNormalTargetGenerator::WriteLinkRule(bool useResponseFile,
         std::vector<std::string> cmd;
         cmd.push_back(cmakeCommand + " -E cmake_symlink_executable $in $out");
         cmd.emplace_back("$POST_BUILD");
-        rule.Command = this->GetLocalGenerator()->BuildCommandLine(cmd);
+        rule.Command =
+          this->GetLocalGenerator()->BuildCommandLine(cmd, config, config);
       }
       rule.Description = "Creating executable symlink $out";
       rule.Comment = "Rule for creating executable symlink.";
@@ -512,7 +518,8 @@ void cmNinjaNormalTargetGenerator::WriteLinkRule(bool useResponseFile,
         cmd.push_back(cmakeCommand +
                       " -E cmake_symlink_library $in $SONAME $out");
         cmd.emplace_back("$POST_BUILD");
-        rule.Command = this->GetLocalGenerator()->BuildCommandLine(cmd);
+        rule.Command =
+          this->GetLocalGenerator()->BuildCommandLine(cmd, config, config);
       }
       rule.Description = "Creating library symlink $out";
       rule.Comment = "Rule for creating library symlink.";
@@ -1310,10 +1317,11 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement(
     preLinkCmdLines.push_back("cd " + homeOutDir);
   }
 
-  vars["PRE_LINK"] = localGen.BuildCommandLine(preLinkCmdLines, "pre-link",
-                                               this->GeneratorTarget);
-  std::string postBuildCmdLine = localGen.BuildCommandLine(
-    postBuildCmdLines, "post-build", this->GeneratorTarget);
+  vars["PRE_LINK"] = localGen.BuildCommandLine(
+    preLinkCmdLines, config, fileConfig, "pre-link", this->GeneratorTarget);
+  std::string postBuildCmdLine =
+    localGen.BuildCommandLine(postBuildCmdLines, config, fileConfig,
+                              "post-build", this->GeneratorTarget);
 
   cmNinjaVars symlinkVars;
   bool const symlinkNeeded =
diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx
index 672b579..b4838d6 100644
--- a/Source/cmNinjaTargetGenerator.cxx
+++ b/Source/cmNinjaTargetGenerator.cxx
@@ -537,7 +537,8 @@ cmNinjaRule GetScanRule(
   cmRulePlaceholderExpander::RuleVariables const& vars,
   const std::string& responseFlag, const std::string& flags,
   cmRulePlaceholderExpander* const rulePlaceholderExpander,
-  cmLocalNinjaGenerator* generator, std::vector<std::string> scanCmds)
+  cmLocalNinjaGenerator* generator, std::vector<std::string> scanCmds,
+  const std::string& outputConfig)
 {
   cmNinjaRule rule(ruleName);
   // Scanning always uses a depfile for preprocessor dependencies.
@@ -578,7 +579,8 @@ cmNinjaRule GetScanRule(
   for (std::string& scanCmd : scanCmds) {
     rulePlaceholderExpander->ExpandRuleVariables(generator, scanCmd, scanVars);
   }
-  rule.Command = generator->BuildCommandLine(scanCmds);
+  rule.Command =
+    generator->BuildCommandLine(scanCmds, outputConfig, outputConfig);
 
   return rule;
 }
@@ -670,7 +672,7 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang,
 
       auto scanRule = GetScanRule(
         scanRuleName, vars, responseFlag, flags, rulePlaceholderExpander.get(),
-        this->GetLocalGenerator(), std::move(scanCommands));
+        this->GetLocalGenerator(), std::move(scanCommands), config);
 
       scanRule.Comment =
         cmStrCat("Rule for generating ", lang, " dependencies.");
@@ -700,7 +702,7 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang,
 
       auto scanRule = GetScanRule(
         scanRuleName, vars, "", flags, rulePlaceholderExpander.get(),
-        this->GetLocalGenerator(), std::move(scanCommands));
+        this->GetLocalGenerator(), std::move(scanCommands), config);
 
       // Write the rule for generating dependencies for the given language.
       scanRule.Comment = cmStrCat("Rule for generating ", lang,
@@ -732,7 +734,8 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang,
           ddModmapArg, " --dd=$out @", rule.RspFile);
         ddCmds.emplace_back(std::move(ccmd));
       }
-      rule.Command = this->GetLocalGenerator()->BuildCommandLine(ddCmds);
+      rule.Command =
+        this->GetLocalGenerator()->BuildCommandLine(ddCmds, config, config);
     }
     rule.Comment =
       cmStrCat("Rule to generate ninja dyndep files for ", lang, '.');
@@ -928,7 +931,8 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang,
                                                  vars);
   }
 
-  rule.Command = this->GetLocalGenerator()->BuildCommandLine(compileCmds);
+  rule.Command =
+    this->GetLocalGenerator()->BuildCommandLine(compileCmds, config, config);
 
   // Write the rule for compiling file of the given language.
   rule.Comment = cmStrCat("Rule for compiling ", lang, " files.");
@@ -1246,7 +1250,7 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement(
 
   this->ExportObjectCompileCommand(
     language, sourceFileName, objectDir, objectFileName, objectFileDir,
-    vars["FLAGS"], vars["DEFINES"], vars["INCLUDES"]);
+    vars["FLAGS"], vars["DEFINES"], vars["INCLUDES"], config);
 
   objBuild.Outputs.push_back(objectFileName);
   if (firstForConfig) {
@@ -1617,7 +1621,8 @@ void cmNinjaTargetGenerator::ExportObjectCompileCommand(
   std::string const& language, std::string const& sourceFileName,
   std::string const& objectDir, std::string const& objectFileName,
   std::string const& objectFileDir, std::string const& flags,
-  std::string const& defines, std::string const& includes)
+  std::string const& defines, std::string const& includes,
+  std::string const& outputConfig)
 {
   if (!this->GeneratorTarget->GetPropertyAsBool("EXPORT_COMPILE_COMMANDS")) {
     return;
@@ -1679,8 +1684,8 @@ void cmNinjaTargetGenerator::ExportObjectCompileCommand(
                                                  compileObjectVars);
   }
 
-  std::string cmdLine =
-    this->GetLocalGenerator()->BuildCommandLine(compileCmds);
+  std::string cmdLine = this->GetLocalGenerator()->BuildCommandLine(
+    compileCmds, outputConfig, outputConfig);
 
   this->GetGlobalGenerator()->AddCXXCompileCommand(cmdLine, sourceFileName);
 }
diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h
index 79dc622..3a28cef 100644
--- a/Source/cmNinjaTargetGenerator.h
+++ b/Source/cmNinjaTargetGenerator.h
@@ -162,7 +162,8 @@ protected:
     std::string const& language, std::string const& sourceFileName,
     std::string const& objectDir, std::string const& objectFileName,
     std::string const& objectFileDir, std::string const& flags,
-    std::string const& defines, std::string const& includes);
+    std::string const& defines, std::string const& includes,
+    std::string const& outputConfig);
 
   void AdditionalCleanFiles(const std::string& config);
 
diff --git a/Source/cmNinjaUtilityTargetGenerator.cxx b/Source/cmNinjaUtilityTargetGenerator.cxx
index 92c5b52..7a04c47 100644
--- a/Source/cmNinjaUtilityTargetGenerator.cxx
+++ b/Source/cmNinjaUtilityTargetGenerator.cxx
@@ -139,8 +139,8 @@ void cmNinjaUtilityTargetGenerator::WriteUtilBuildStatements(
       gg->WriteBuild(this->GetCommonFileStream(), phonyBuild);
     }
   } else {
-    std::string command =
-      lg->BuildCommandLine(commands, "utility", this->GeneratorTarget);
+    std::string command = lg->BuildCommandLine(
+      commands, config, fileConfig, "utility", this->GeneratorTarget);
     std::string desc;
     cmProp echoStr = genTarget->GetProperty("EchoString");
     if (echoStr) {
diff --git a/Tests/RunCMake/NinjaMultiConfig/LongCommandLine-release-config-build-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/LongCommandLine-release-config-build-stdout.txt
new file mode 100644
index 0000000..628b6bc
--- /dev/null
+++ b/Tests/RunCMake/NinjaMultiConfig/LongCommandLine-release-config-build-stdout.txt
@@ -0,0 +1,2 @@
+
+Post-build Debug Release \.*$
diff --git a/Tests/RunCMake/NinjaMultiConfig/LongCommandLine.cmake b/Tests/RunCMake/NinjaMultiConfig/LongCommandLine.cmake
index 00aa896..de528be 100644
--- a/Tests/RunCMake/NinjaMultiConfig/LongCommandLine.cmake
+++ b/Tests/RunCMake/NinjaMultiConfig/LongCommandLine.cmake
@@ -14,3 +14,10 @@ add_custom_target(
   ALL
   DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/gen.txt"
   )
+
+add_executable(exe main.c)
+
+add_custom_command(
+  TARGET exe POST_BUILD
+  COMMAND ${CMAKE_COMMAND} -E echo "Post-build $<CONFIG> $<COMMAND_CONFIG:$<CONFIG>> ${very_long}"
+  )
diff --git a/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake b/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake
index aa42739..e7acbc2 100644
--- a/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake
+++ b/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake
@@ -198,8 +198,11 @@ run_cmake_build(PostBuild release Release Exe)
 run_cmake_build(PostBuild debug-in-release-graph Release Exe:Debug)
 
 set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/LongCommandLine-build)
+set(RunCMake_TEST_OPTIONS "-DCMAKE_CROSS_CONFIGS=all")
 run_cmake_configure(LongCommandLine)
+unset(RunCMake_TEST_OPTIONS)
 run_cmake_build(LongCommandLine release Release custom)
+run_cmake_build(LongCommandLine release-config Release exe:Debug)
 
 set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Framework-build)
 set(RunCMake_TEST_OPTIONS "-DCMAKE_CROSS_CONFIGS=all")
-- 
cgit v0.12