From 5affe65ebb32d1a8c6cba30930ebe2c612bd0f2f Mon Sep 17 00:00:00 2001
From: Daniel Levin <dendy.ua@gmail.com>
Date: Thu, 24 Apr 2014 20:32:11 -0400
Subject: [PATCH] Ninja: Add 'Unix Ninja' generator

'Unix Ninja' generator serves same purpose as 'Unix Makefiles'
generator.

It is intended to create *.ninja files compatible with Unix
environment launched in non-Unix OS, like Windows.
---
 Source/cmGlobalNinjaGenerator.cxx       | 14 +++++++-
 Source/cmGlobalNinjaGenerator.h         | 21 +++++++++++
 Source/cmLocalGenerator.cxx             | 36 ++++++++++++++++++-
 Source/cmLocalGenerator.h               |  1 +
 Source/cmLocalNinjaGenerator.cxx        | 64 ++++++++++++++++++++++++++++++---
 Source/cmLocalNinjaGenerator.h          | 10 ++++++
 Source/cmNinjaNormalTargetGenerator.cxx | 31 ++++++++++++++++
 Source/cmake.cxx                        |  2 ++
 8 files changed, 173 insertions(+), 6 deletions(-)

diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx
index 6e7b06b..d8e6664 100644
--- a/Source/cmGlobalNinjaGenerator.cxx
+++ b/Source/cmGlobalNinjaGenerator.cxx
@@ -95,7 +95,7 @@ std::string cmGlobalNinjaGenerator::EncodePath(const std::string &path)
 {
   std::string result = path;
 #ifdef _WIN32
-  if(UsingMinGW)
+  if(UsingMinGW || UsingUnix)
     cmSystemTools::ReplaceString(result, "\\", "/");
   else
     cmSystemTools::ReplaceString(result, "/", "\\");
@@ -478,6 +478,10 @@ cmGlobalNinjaGenerator::cmGlobalNinjaGenerator()
   this->FindMakeProgramFile = "CMakeNinjaFindMake.cmake";
 }
 
+cmGlobalUnixNinjaGenerator::cmGlobalUnixNinjaGenerator()
+{
+}
+
 
 //----------------------------------------------------------------------------
 // Virtual public methods.
@@ -489,6 +493,13 @@ cmLocalGenerator* cmGlobalNinjaGenerator::CreateLocalGenerator()
   return lg;
 }
 
+cmLocalGenerator* cmGlobalUnixNinjaGenerator::CreateLocalGenerator()
+{
+  cmLocalGenerator* lg = new cmLocalUnixNinjaGenerator;
+  lg->SetGlobalGenerator(this);
+  return lg;
+}
+
 void cmGlobalNinjaGenerator
 ::GetDocumentation(cmDocumentationEntry& entry)
 {
@@ -552,6 +563,7 @@ void cmGlobalNinjaGenerator
 }
 
 bool cmGlobalNinjaGenerator::UsingMinGW = false;
+bool cmGlobalNinjaGenerator::UsingUnix = false;
 
 // Implemented by:
 //   cmGlobalUnixMakefileGenerator3
diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h
index f666ee3..ce32530 100644
--- a/Source/cmGlobalNinjaGenerator.h
+++ b/Source/cmGlobalNinjaGenerator.h
@@ -159,6 +159,8 @@ public:
 
 
 public:
+  static bool UsingUnix;
+
   /// Default constructor.
   cmGlobalNinjaGenerator();
 
@@ -383,4 +385,23 @@ private:
 
 };
 
+class cmGlobalUnixNinjaGenerator : public cmGlobalNinjaGenerator
+{
+public:
+  cmGlobalUnixNinjaGenerator();
+
+  /// Convenience method for creating an instance of this class.
+  static cmGlobalGeneratorFactory* NewFactory() {
+    return new cmGlobalGeneratorSimpleFactory<cmGlobalUnixNinjaGenerator>(); }
+
+  /// Overloaded methods. @see cmGlobalGenerator::GetName().
+  virtual std::string GetName() const {
+    return cmGlobalUnixNinjaGenerator::GetActualName(); }
+
+  /// @return the name of this generator.
+  static std::string GetActualName() { return "Unix Ninja"; }
+
+  virtual cmLocalGenerator* CreateLocalGenerator();
+};
+
 #endif // ! cmGlobalNinjaGenerator_h
diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx
index 50e279b..faa234a 100644
--- a/Source/cmLocalGenerator.cxx
+++ b/Source/cmLocalGenerator.cxx
@@ -61,6 +61,7 @@ cmLocalGenerator::cmLocalGenerator()
   this->PathConversionsSetup = false;
   this->BackwardsCompatibility = 0;
   this->BackwardsCompatibilityFinal = false;
+  this->UnixNinja = false;
 }
 
 cmLocalGenerator::~cmLocalGenerator()
@@ -2562,6 +2563,29 @@ void cmLocalGenerator::AppendDefines(std::set<std::string>& defines,
     }
 }
 
+static std::string escapeToUnixNinja(const char * str)
+{
+  std::string dstr;
+  bool haveQuote = false;
+  for(const char* c = str; *c; ++c)
+    {
+    if(*c == '"')
+      {
+      dstr += "\\\\\\\"";
+      haveQuote = !haveQuote;
+      }
+    else if(*c == ' ')
+      {
+        dstr += "\\ ";
+      }
+    else
+      {
+      dstr += *c;
+      }
+    }
+  return dstr;
+}
+
 //----------------------------------------------------------------------------
 void cmLocalGenerator::JoinDefines(const std::set<std::string>& defines,
                                    std::string &definesString,
@@ -2618,7 +2642,14 @@ void cmLocalGenerator::JoinDefines(const std::set<std::string>& defines,
       if(eq != defineIt->npos)
         {
         def += "=";
-        def += this->EscapeForShell(defineIt->c_str() + eq + 1, true);
+        if(!this->UnixNinja)
+          {
+          def += this->EscapeForShell(defineIt->c_str() + eq + 1, true);
+          }
+        else
+          {
+          def += escapeToUnixNinja(defineIt->c_str() + eq + 1);
+          }
         }
       }
     definesString += itemSeparator;
@@ -3381,6 +3412,9 @@ std::string cmLocalGenerator::EscapeForShellOldStyle(const std::string& str)
 {
   std::string result;
 #if defined(_WIN32) && !defined(__CYGWIN__)
+  if(this->UnixNinja)
+    return escapeToUnixNinja(str.c_str());
+
   // if there are spaces
   std::string temp = str;
   if (temp.find(" ") != std::string::npos &&
diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h
index 3a9d5be..513c27b 100644
--- a/Source/cmLocalGenerator.h
+++ b/Source/cmLocalGenerator.h
@@ -472,6 +472,7 @@ protected:
   bool IgnoreLibPrefix;
   bool Configured;
   bool EmitUniversalBinaryFlags;
+  bool UnixNinja;
   // Hack for ExpandRuleVariable until object-oriented version is
   // committed.
   std::string TargetImplib;
diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx
index 398b55a..8a5fe6c 100644
--- a/Source/cmLocalNinjaGenerator.cxx
+++ b/Source/cmLocalNinjaGenerator.cxx
@@ -32,6 +32,16 @@ cmLocalNinjaGenerator::cmLocalNinjaGenerator()
   this->TargetImplib = "$TARGET_IMPLIB";
 }
 
+cmLocalUnixNinjaGenerator::cmLocalUnixNinjaGenerator()
+  : cmLocalNinjaGenerator()
+{
+  cmGlobalNinjaGenerator::UsingUnix = true;
+
+  this->WindowsShell = false;
+  this->ForceUnixPath = true;
+  this->UnixNinja = true;
+}
+
 //----------------------------------------------------------------------------
 // Virtual public methods.
 
@@ -41,6 +51,30 @@ cmLocalNinjaGenerator::~cmLocalNinjaGenerator()
 
 void cmLocalNinjaGenerator::Generate()
 {
+  const std::string shellPath = std::string(this->GetCMakeInstance()->GetHomeOutputDirectory()) + "/.unix-ninja-shell";
+    {
+    std::ifstream shellPathFile(shellPath.c_str());
+    if(shellPathFile)
+      {
+      shellPathFile.seekg(0, shellPathFile.end);
+      this->ShellExecutable.resize(shellPathFile.tellg(), ' ');
+      shellPathFile.seekg(0, shellPathFile.beg);
+      shellPathFile.read(&*this->ShellExecutable.begin(), this->ShellExecutable.length());
+      shellPathFile.close();
+      }
+  }
+
+  if(this->ShellExecutable.empty())
+    {
+    this->ShellExecutable = cmSystemTools::FindProgram("sh.exe");
+    std::ofstream shellPathFile(shellPath.c_str());
+    if(shellPathFile)
+      {
+      shellPathFile.write(this->ShellExecutable.c_str(), this->ShellExecutable.length());
+      shellPathFile.close();
+      }
+    }
+
   this->SetConfigName();
 
   this->WriteProcessedMakefile(this->GetBuildFileStream());
@@ -324,7 +358,8 @@ std::string cmLocalNinjaGenerator::ConvertToNinjaPath(const std::string& path)
 {
   std::string convPath = this->Convert(path, cmLocalGenerator::HOME_OUTPUT);
 #ifdef _WIN32
-  cmSystemTools::ReplaceString(convPath, "/", "\\");
+  if (!this->ForceUnixPath)
+    cmSystemTools::ReplaceString(convPath, "/", "\\");
 #endif
   return convPath;
 }
@@ -356,6 +391,20 @@ void cmLocalNinjaGenerator::AppendCustomCommandDeps(
   }
 }
 
+#ifdef _WIN32
+static std::string convertToUnixNinjaCommand(const std::string &cmd)
+{
+  const size_t colonSlashPos = cmd.find(":/");
+  if(colonSlashPos == std::string::npos || colonSlashPos == 0)
+    return cmd;
+
+  for(int i = 0; i < static_cast<int>(colonSlashPos) - 1; ++i)
+    if(cmd.at(i) != ' ') return cmd;
+
+  return std::string("/") + cmd.at(static_cast<int>(colonSlashPos) - 1) + cmd.substr(colonSlashPos + 1);
+}
+#endif
+
 std::string cmLocalNinjaGenerator::BuildCommandLine(
                                     const std::vector<std::string> &cmdLines)
 {
@@ -378,13 +427,20 @@ std::string cmLocalNinjaGenerator::BuildCommandLine(
       {
       cmd << " && ";
       }
+    else if (!this->WindowsShell)
+      {
+      cmd << this->ShellExecutable << " -c \"";
+      }
     else if (cmdLines.size() > 1)
       {
       cmd << "cmd.exe /C \"";
       }
-    cmd << *li;
+    if(UnixNinja)
+      cmd << convertToUnixNinjaCommand(*li);
+    else
+      cmd << *li;
     }
-  if (cmdLines.size() > 1)
+  if (cmdLines.size() > 1 || !this->WindowsShell)
     {
     cmd << "\"";
     }
@@ -411,7 +467,7 @@ void cmLocalNinjaGenerator::AppendCustomCommandLines(
 
     cmOStringStream cdCmd;
 #ifdef _WIN32
-        std::string cdStr = "cd /D ";
+        std::string cdStr = this->WindowsShell ? "cd /D " : "cd ";
 #else
         std::string cdStr = "cd ";
 #endif
diff --git a/Source/cmLocalNinjaGenerator.h b/Source/cmLocalNinjaGenerator.h
index 1d27224..82c7aa6 100644
--- a/Source/cmLocalNinjaGenerator.h
+++ b/Source/cmLocalNinjaGenerator.h
@@ -139,6 +139,16 @@ private:
   typedef std::map<cmCustomCommand const*, std::set<cmTarget*> >
     CustomCommandTargetMap;
   CustomCommandTargetMap CustomCommandTargets;
+
+protected:
+  std::string ShellExecutable;
+};
+
+class cmLocalUnixNinjaGenerator : public cmLocalNinjaGenerator
+{
+public:
+  /// Default constructor.
+  cmLocalUnixNinjaGenerator();
 };
 
 #endif // ! cmLocalNinjaGenerator_h
diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx
index e344df4..e5e6292 100644
--- a/Source/cmNinjaNormalTargetGenerator.cxx
+++ b/Source/cmNinjaNormalTargetGenerator.cxx
@@ -271,6 +271,36 @@ cmNinjaNormalTargetGenerator
       this->GetLocalGenerator()->ConvertToOutputFormat(
         this->GetMakefile()->GetRequiredDefinition("CMAKE_COMMAND"),
         cmLocalGenerator::SHELL);
+#ifdef _WIN32
+    if (targetType == cmTarget::EXECUTABLE)
+      this->GetGlobalGenerator()->AddRule("CMAKE_SYMLINK_EXECUTABLE",
+                                          "cmd.exe /C \"" + cmakeCommand +
+                                          " -E copy"
+                                          " $in $out && $POST_BUILD\"",
+                                          "Creating executable symlink $out",
+                                          "Rule for creating "
+                                          "executable symlink.",
+                                          /*depfile*/ "",
+                                          /*deptype*/ "",
+                                          /*rspfile*/ "",
+                                          /*rspcontent*/ "",
+                                          /*restat*/ false,
+                                          /*generator*/ false);
+    else
+      this->GetGlobalGenerator()->AddRule("CMAKE_SYMLINK_LIBRARY",
+                                          "cmd.exe /C \"" + cmakeCommand +
+                                          " -E copy"
+                                          " $in $out && $POST_BUILD\"",
+                                          "Creating library symlink $out",
+                                          "Rule for creating "
+                                          "library symlink.",
+                                          /*depfile*/ "",
+                                          /*deptype*/ "",
+                                          /*rspfile*/ "",
+                                          /*rspcontent*/ "",
+                                          /*restat*/ false,
+                                          /*generator*/ false);
+#else
     if (targetType == cmTarget::EXECUTABLE)
       this->GetGlobalGenerator()->AddRule("CMAKE_SYMLINK_EXECUTABLE",
                                           cmakeCommand +
@@ -299,6 +329,7 @@ cmNinjaNormalTargetGenerator
                                           /*rspcontent*/ "",
                                           /*restat*/ false,
                                           /*generator*/ false);
+#endif
   }
 }
 
diff --git a/Source/cmake.cxx b/Source/cmake.cxx
index 36a0645..cb40b76 100644
--- a/Source/cmake.cxx
+++ b/Source/cmake.cxx
@@ -1854,6 +1854,8 @@ void cmake::AddDefaultGenerators()
     cmGlobalUnixMakefileGenerator3::NewFactory());
   this->Generators.push_back(
     cmGlobalNinjaGenerator::NewFactory());
+  this->Generators.push_back(
+    cmGlobalUnixNinjaGenerator::NewFactory());
 #if defined(CMAKE_USE_WMAKE)
   this->Generators.push_back(
     cmGlobalWatcomWMakeGenerator::NewFactory());
-- 
1.9.2.msysgit.0

