From 880e7163fc8fa07f234fb6f8342539b64d324f9a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Roman=20W=C3=BCger?= <roman.wueger@gmx.at>
Date: Fri, 23 Sep 2016 08:57:11 +0200
Subject: [PATCH] CPack/NSIS custom component install directory

---
 Modules/CPackNSIS.cmake               |  5 +++++
 Source/CPack/cmCPackNSISGenerator.cxx | 41 ++++++++++++++++++++++++++++++-----
 Source/CPack/cmCPackNSISGenerator.h   |  5 +++++
 3 files changed, 45 insertions(+), 6 deletions(-)

diff --git a/Modules/CPackNSIS.cmake b/Modules/CPackNSIS.cmake
index db5984a..4693ce5 100644
--- a/Modules/CPackNSIS.cmake
+++ b/Modules/CPackNSIS.cmake
@@ -96,6 +96,11 @@
 #  Contact information for questions and comments about the installation
 #  process.
 #
+# .. variable:: CPACK_NSIS_<compName>_INSTALL_DIRECTORY
+#
+#  Custom install directory for the specified component <compName> instead
+#  of $INSTDIR.
+#
 # .. variable:: CPACK_NSIS_CREATE_ICONS_EXTRA
 #
 #  Additional NSIS commands for creating start menu shortcuts.
diff --git a/Source/CPack/cmCPackNSISGenerator.cxx b/Source/CPack/cmCPackNSISGenerator.cxx
index 2db94f1..168a437 100644
--- a/Source/CPack/cmCPackNSISGenerator.cxx
+++ b/Source/CPack/cmCPackNSISGenerator.cxx
@@ -71,14 +71,26 @@ int cmCPackNSISGenerator::PackageFiles()
   std::ostringstream str;
   std::vector<std::string>::const_iterator it;
   for (it = files.begin(); it != files.end(); ++it) {
+    std::string outputDir = "$INSTDIR";
     std::string fileN =
       cmSystemTools::RelativePath(toplevel.c_str(), it->c_str());
     if (!this->Components.empty()) {
+      const size_t pos = fileN.find('/');
+
       // Strip off the component part of the path.
-      fileN = fileN.substr(fileN.find('/') + 1, std::string::npos);
+      fileN = fileN.substr(pos + 1, std::string::npos);
+
+      // Use the custom component install directory if we have one
+      if (pos != std::string::npos) {
+        const std::string componentName = fileN.substr(0, pos);
+        outputDir = CustomComponentInstallDirectory(componentName);
+      } else {
+        outputDir = CustomComponentInstallDirectory(fileN);
+      }
     }
     std::replace(fileN.begin(), fileN.end(), '/', '\\');
-    str << "  Delete \"$INSTDIR\\" << fileN << "\"" << std::endl;
+
+    str << "  Delete \"" << outputDir << "\\" << fileN << "\"" << std::endl;
   }
   cmCPackLogger(cmCPackLog::LOG_DEBUG, "Uninstall Files: " << str.str()
                                                            << std::endl);
@@ -108,7 +120,12 @@ int cmCPackNSISGenerator::PackageFiles()
       }
     }
     std::replace(fileN.begin(), fileN.end(), '/', '\\');
-    dstr << "  RMDir \"$INSTDIR\\" << fileN << "\"" << std::endl;
+
+    const std::string componentOutputDir =
+      CustomComponentInstallDirectory(fileN);
+
+    dstr << "  RMDir \"" << componentOutputDir << "\\" << fileN << "\""
+         << std::endl;
     if (!componentName.empty()) {
       this->Components[componentName].Directories.push_back(fileN);
     }
@@ -650,7 +667,10 @@ std::string cmCPackNSISGenerator::CreateComponentDescription(
     }
     componentCode += "  SectionIn" + out.str() + "\n";
   }
-  componentCode += "  SetOutPath \"$INSTDIR\"\n";
+
+  const std::string componentOutputDir =
+    CustomComponentInstallDirectory(component->Name);
+  componentCode += "  SetOutPath \"" + componentOutputDir + "\"\n";
 
   // Create the actual installation commands
   if (component->IsDownloaded) {
@@ -796,13 +816,13 @@ std::string cmCPackNSISGenerator::CreateComponentDescription(
        ++pathIt) {
     path = *pathIt;
     std::replace(path.begin(), path.end(), '/', '\\');
-    macrosOut << "  Delete \"$INSTDIR\\" << path << "\"\n";
+    macrosOut << "  Delete \"" << componentOutputDir << "\\" << path << "\"\n";
   }
   for (pathIt = component->Directories.begin();
        pathIt != component->Directories.end(); ++pathIt) {
     path = *pathIt;
     std::replace(path.begin(), path.end(), '/', '\\');
-    macrosOut << "  RMDir \"$INSTDIR\\" << path << "\"\n";
+    macrosOut << "  RMDir \"" << componentOutputDir << "\\" << path << "\"\n";
   }
   macrosOut << "  noremove_" << component->Name << ":\n";
   macrosOut << "!macroend\n";
@@ -914,6 +934,15 @@ std::string cmCPackNSISGenerator::CreateComponentGroupDescription(
   return code;
 }
 
+std::string cmCPackNSISGenerator::CustomComponentInstallDirectory(
+  const std::string& componentName)
+{
+  const char* outputDir =
+    this->GetOption("CPACK_NSIS_" + componentName + "_INSTALL_DIRECTORY");
+  const std::string componentOutputDir = (outputDir ? outputDir : "$INSTDIR");
+  return componentOutputDir;
+}
+
 std::string cmCPackNSISGenerator::TranslateNewlines(std::string str)
 {
   cmSystemTools::ReplaceString(str, "\n", "$\\r$\\n");
diff --git a/Source/CPack/cmCPackNSISGenerator.h b/Source/CPack/cmCPackNSISGenerator.h
index ae03e6b..bd7d752 100644
--- a/Source/CPack/cmCPackNSISGenerator.h
+++ b/Source/CPack/cmCPackNSISGenerator.h
@@ -84,6 +84,11 @@ protected:
   std::string CreateComponentGroupDescription(cmCPackComponentGroup* group,
                                               std::ostream& macrosOut);
 
+  /// Returns the custom install directory if available for the specified
+  /// component, otherwise $INSTDIR is returned.
+  std::string CustomComponentInstallDirectory(
+    const std::string& componentName);
+
   /// Translations any newlines found in the string into \\r\\n, so that the
   /// resulting string can be used within NSIS.
   static std::string TranslateNewlines(std::string str);
-- 
2.7.4.windows.1

