From 999f24268d85d575ef30aea17ffbc19a4f832d2d Mon Sep 17 00:00:00 2001
From: Yves Frederix <yves.frederix@gmail.com>
Date: Mon, 12 Sep 2016 17:54:04 +0200
Subject: [PATCH] When building using "cmake --build" which natively uses
 MSBuild explicitly build the ZERO_CHECK target first in a separate process
 and only afterwards build the requested target.

If building ZERO_CHECK triggered reconfiguration, a running build was not interrupted, meaning that outdated project files would be used for that build. By having "cmake --build" run two separate build commands (one building ZERO_CHECK and one building the requested target), the second build will correctly use the newly generated solution/project files.
---
 Source/cmGlobalGenerator.cxx               | 41 +++++++++++++++---------
 Source/cmGlobalVisualStudio10Generator.cxx | 50 ++++++++++++++++++++++++------
 Source/cmSystemTools.cxx                   | 16 ++++++++--
 Source/cmSystemTools.h                     |  9 ++++++
 4 files changed, 90 insertions(+), 26 deletions(-)

diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx
index ef8266f..d885851 100644
--- a/Source/cmGlobalGenerator.cxx
+++ b/Source/cmGlobalGenerator.cxx
@@ -1729,23 +1729,34 @@ int cmGlobalGenerator::Build(const std::string& /*unused*/,
   output += makeCommandStr;
   output += "\n";
 
-  if (!cmSystemTools::RunSingleCommand(makeCommand, outputPtr, outputPtr,
-                                       &retVal, CM_NULLPTR, outputflag,
-                                       timeout)) {
-    cmSystemTools::SetRunCommandHideConsole(hideconsole);
-    cmSystemTools::Error(
-      "Generator: execution of make failed. Make command was: ",
-      makeCommandStr.c_str());
+  // Check if we need to split the make command (i.e., if the vector contains "&&").
+  std::vector<std::string>::iterator amp_it;
+  std::vector<std::string>::iterator start_it = makeCommand.begin();
+  do {
+    amp_it = std::find(start_it, makeCommand.end(), "&&");
+
+    if (!cmSystemTools::RunSingleCommand(start_it, amp_it, outputPtr, outputPtr,
+                                         &retVal, CM_NULLPTR, outputflag,
+                                         timeout)) {
+      cmSystemTools::SetRunCommandHideConsole(hideconsole);
+      cmSystemTools::Error(
+        "Generator: execution of make failed. Make command was: ",
+        makeCommandStr.c_str());
+      output += *outputPtr;
+      output += "\nGenerator: execution of make failed. Make command was: " +
+        makeCommandStr + "\n";
+
+      // return to the original directory
+      cmSystemTools::ChangeDirectory(cwd);
+      return 1;
+    }
     output += *outputPtr;
-    output += "\nGenerator: execution of make failed. Make command was: " +
-      makeCommandStr + "\n";
+    cmSystemTools::SetRunCommandHideConsole(hideconsole);
 
-    // return to the original directory
-    cmSystemTools::ChangeDirectory(cwd);
-    return 1;
-  }
-  output += *outputPtr;
-  cmSystemTools::SetRunCommandHideConsole(hideconsole);
+    if (amp_it != makeCommand.end()) {
+      start_it = amp_it + 1;
+    }
+  } while (amp_it != makeCommand.end());
 
   // The SGI MipsPro 7.3 compiler does not return an error code when
   // the source has a #error in it!  This is a work-around for such
diff --git a/Source/cmGlobalVisualStudio10Generator.cxx b/Source/cmGlobalVisualStudio10Generator.cxx
index ac9c8ef..4bbe42c 100644
--- a/Source/cmGlobalVisualStudio10Generator.cxx
+++ b/Source/cmGlobalVisualStudio10Generator.cxx
@@ -9,6 +9,7 @@
   implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
   See the License for more information.
 ============================================================================*/
+#define NOMINMAX
 #include "windows.h" // this must be first to define GetCurrentDirectory
 
 #include "cmGlobalVisualStudio10Generator.h"
@@ -22,6 +23,8 @@
 #include "cmVisualStudioSlnParser.h"
 #include "cmake.h"
 
+#include <array>
+
 static const char vs10generatorName[] = "Visual Studio 10 2010";
 
 // Map generator name without year to name with year.
@@ -447,26 +450,41 @@ void cmGlobalVisualStudio10Generator::GenerateBuildCommand(
   makeCommand.push_back(makeProgramSelected);
 
   std::string realTarget = targetName;
+  std::array<std::string, 2> realTargetProjects;
+  size_t zeroCheckIndex = std::numeric_limits<size_t>::max();
   // msbuild.exe CxxOnly.sln /t:Build /p:Configuration=Debug /target:ALL_BUILD
   if (realTarget.empty()) {
     realTarget = "ALL_BUILD";
   }
+
   if (realTarget == "clean") {
     makeCommand.push_back(std::string(projectName) + ".sln");
     makeCommand.push_back("/t:Clean");
   } else {
-    std::string targetProject(realTarget);
-    targetProject += ".vcxproj";
-    if (targetProject.find('/') == std::string::npos) {
-      // it might be in a subdir
-      if (cmSlnProjectEntry const* proj =
-            slnData.GetProjectByName(realTarget)) {
-        targetProject = proj->GetRelativePath();
-        cmSystemTools::ConvertToUnixSlashes(targetProject);
+    std::array<std::string, 2> targets = {"ZERO_CHECK", realTarget};
+
+    for (unsigned int i = 0; i < 2; ++i) {
+      std::string targetProject(targets[i]);
+      targetProject += ".vcxproj";
+      if (targetProject.find('/') == std::string::npos) {
+        // it might be in a subdir
+        if (cmSlnProjectEntry const* proj =
+              slnData.GetProjectByName(targets[i])) {
+          targetProject = proj->GetRelativePath();
+          cmSystemTools::ConvertToUnixSlashes(targetProject);
+        }
       }
+      realTargetProjects[i] = targetProject;
+    }
+
+    if (cmSystemTools::FileExists(realTargetProjects[0])) {
+      makeCommand.push_back(realTargetProjects[0]);
+      zeroCheckIndex = makeCommand.size() - 1;
+    } else {
+      makeCommand.push_back(realTargetProjects[1]);
     }
-    makeCommand.push_back(targetProject);
   }
+
   std::string configArg = "/p:Configuration=";
   if (!config.empty()) {
     configArg += config;
@@ -478,6 +496,20 @@ void cmGlobalVisualStudio10Generator::GenerateBuildCommand(
                         this->GetIDEVersion());
   makeCommand.insert(makeCommand.end(), makeOptions.begin(),
                      makeOptions.end());
+
+  // We now got a complete build command for building the ZERO_CHECK target. 
+  // Now generate the same command, replacing ZERO_CHECK with the requested
+  // target.
+  if (zeroCheckIndex != std::numeric_limits<size_t>::max()) {
+    std::vector<std::string> origMakeCommand = makeCommand;
+
+    makeCommand.push_back("&&");
+    makeCommand.insert(makeCommand.end(), origMakeCommand.begin(),
+      origMakeCommand.begin() + zeroCheckIndex);
+    makeCommand.push_back(realTargetProjects[1]);
+    makeCommand.insert(makeCommand.end(), 
+      origMakeCommand.begin() + zeroCheckIndex + 1, origMakeCommand.end());
+  }
 }
 
 bool cmGlobalVisualStudio10Generator::Find64BitTools(cmMakefile* mf)
diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx
index 7352217..5e6ec15 100644
--- a/Source/cmSystemTools.cxx
+++ b/Source/cmSystemTools.cxx
@@ -584,9 +584,21 @@ bool cmSystemTools::RunSingleCommand(std::vector<std::string> const& command,
                                      const char* dir, OutputOption outputflag,
                                      double timeout)
 {
+  return RunSingleCommand(command.begin(), command.end(), captureStdOut, 
+    captureStdErr, retVal, dir, outputflag, timeout);
+}
+
+
+bool cmSystemTools::RunSingleCommand(std::vector<std::string>::const_iterator const& command_it_from,
+                                     std::vector<std::string>::const_iterator const& command_it_to,
+                                     std::string* captureStdOut,
+                                     std::string* captureStdErr, int* retVal,
+                                     const char* dir, OutputOption outputflag,
+                                     double timeout)
+{
   std::vector<const char*> argv;
-  for (std::vector<std::string>::const_iterator a = command.begin();
-       a != command.end(); ++a) {
+  for (std::vector<std::string>::const_iterator a = command_it_from;
+       a != command_it_to; ++a) {
     argv.push_back(a->c_str());
   }
   argv.push_back(CM_NULLPTR);
diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h
index 3c1a9f4..fd08297 100644
--- a/Source/cmSystemTools.h
+++ b/Source/cmSystemTools.h
@@ -244,6 +244,15 @@ public:
                                OutputOption outputflag = OUTPUT_MERGE,
                                double timeout = 0.0);
 
+  static bool RunSingleCommand(std::vector<std::string>::const_iterator const& command_it_from,
+                               std::vector<std::string>::const_iterator const& command_it_to,
+                               std::string* captureStdOut = CM_NULLPTR,
+                               std::string* captureStdErr = CM_NULLPTR,
+                               int* retVal = CM_NULLPTR,
+                               const char* dir = CM_NULLPTR,
+                               OutputOption outputflag = OUTPUT_MERGE,
+                               double timeout = 0.0);
+
   static std::string PrintSingleCommand(std::vector<std::string> const&);
 
   /**
-- 
1.9.5.msysgit.0

