summaryrefslogtreecommitdiffstats
path: root/Source/CPack/cmCPackNSISGenerator.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'Source/CPack/cmCPackNSISGenerator.cxx')
-rw-r--r--Source/CPack/cmCPackNSISGenerator.cxx920
1 files changed, 920 insertions, 0 deletions
diff --git a/Source/CPack/cmCPackNSISGenerator.cxx b/Source/CPack/cmCPackNSISGenerator.cxx
new file mode 100644
index 0000000..6afd7d5
--- /dev/null
+++ b/Source/CPack/cmCPackNSISGenerator.cxx
@@ -0,0 +1,920 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmCPackNSISGenerator.h"
+
+#include "cmCPackComponentGroup.h"
+#include "cmCPackGenerator.h"
+#include "cmCPackLog.h"
+#include "cmDuration.h"
+#include "cmGeneratedFileStream.h"
+#include "cmSystemTools.h"
+
+#include "cmsys/Directory.hxx"
+#include "cmsys/RegularExpression.hxx"
+#include <algorithm>
+#include <map>
+#include <sstream>
+#include <stdlib.h>
+#include <string.h>
+#include <utility>
+
+/* NSIS uses different command line syntax on Windows and others */
+#ifdef _WIN32
+# define NSIS_OPT "/"
+#else
+# define NSIS_OPT "-"
+#endif
+
+cmCPackNSISGenerator::cmCPackNSISGenerator(bool nsis64)
+{
+ Nsis64 = nsis64;
+}
+
+cmCPackNSISGenerator::~cmCPackNSISGenerator() = default;
+
+int cmCPackNSISGenerator::PackageFiles()
+{
+ // TODO: Fix nsis to force out file name
+
+ std::string nsisInFileName = this->FindTemplate("NSIS.template.in");
+ if (nsisInFileName.empty()) {
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "CPack error: Could not find NSIS installer template file."
+ << std::endl);
+ return false;
+ }
+ std::string nsisInInstallOptions =
+ this->FindTemplate("NSIS.InstallOptions.ini.in");
+ if (nsisInInstallOptions.empty()) {
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "CPack error: Could not find NSIS installer options file."
+ << std::endl);
+ return false;
+ }
+
+ std::string nsisFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
+ std::string tmpFile = nsisFileName;
+ tmpFile += "/NSISOutput.log";
+ std::string nsisInstallOptions = nsisFileName + "/NSIS.InstallOptions.ini";
+ nsisFileName += "/project.nsi";
+ std::ostringstream str;
+ for (std::string const& file : files) {
+ std::string outputDir = "$INSTDIR";
+ std::string fileN = cmSystemTools::RelativePath(toplevel, file);
+ if (!this->Components.empty()) {
+ const std::string::size_type pos = fileN.find('/');
+
+ // 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);
+ }
+
+ // Strip off the component part of the path.
+ fileN = fileN.substr(pos + 1);
+ }
+ std::replace(fileN.begin(), fileN.end(), '/', '\\');
+
+ str << " Delete \"" << outputDir << "\\" << fileN << "\"" << std::endl;
+ }
+ cmCPackLogger(cmCPackLog::LOG_DEBUG,
+ "Uninstall Files: " << str.str() << std::endl);
+ this->SetOptionIfNotSet("CPACK_NSIS_DELETE_FILES", str.str().c_str());
+ std::vector<std::string> dirs;
+ this->GetListOfSubdirectories(toplevel.c_str(), dirs);
+ std::ostringstream dstr;
+ for (std::string const& dir : dirs) {
+ std::string componentName;
+ std::string fileN = cmSystemTools::RelativePath(toplevel, dir);
+ if (fileN.empty()) {
+ continue;
+ }
+ if (!Components.empty()) {
+ // If this is a component installation, strip off the component
+ // part of the path.
+ std::string::size_type slash = fileN.find('/');
+ if (slash != std::string::npos) {
+ // If this is a component installation, determine which component it
+ // is.
+ componentName = fileN.substr(0, slash);
+
+ // Strip off the component part of the path.
+ fileN = fileN.substr(slash + 1);
+ }
+ }
+ std::replace(fileN.begin(), fileN.end(), '/', '\\');
+
+ const std::string componentOutputDir =
+ CustomComponentInstallDirectory(componentName);
+
+ dstr << " RMDir \"" << componentOutputDir << "\\" << fileN << "\""
+ << std::endl;
+ if (!componentName.empty()) {
+ this->Components[componentName].Directories.push_back(std::move(fileN));
+ }
+ }
+ cmCPackLogger(cmCPackLog::LOG_DEBUG,
+ "Uninstall Dirs: " << dstr.str() << std::endl);
+ this->SetOptionIfNotSet("CPACK_NSIS_DELETE_DIRECTORIES", dstr.str().c_str());
+
+ cmCPackLogger(cmCPackLog::LOG_VERBOSE,
+ "Configure file: " << nsisInFileName << " to " << nsisFileName
+ << std::endl);
+ if (this->IsSet("CPACK_NSIS_MUI_ICON") ||
+ this->IsSet("CPACK_NSIS_MUI_UNIICON")) {
+ std::string installerIconCode;
+ if (this->IsSet("CPACK_NSIS_MUI_ICON")) {
+ installerIconCode += "!define MUI_ICON \"";
+ installerIconCode += this->GetOption("CPACK_NSIS_MUI_ICON");
+ installerIconCode += "\"\n";
+ }
+ if (this->IsSet("CPACK_NSIS_MUI_UNIICON")) {
+ installerIconCode += "!define MUI_UNICON \"";
+ installerIconCode += this->GetOption("CPACK_NSIS_MUI_UNIICON");
+ installerIconCode += "\"\n";
+ }
+ this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_ICON_CODE",
+ installerIconCode.c_str());
+ }
+ if (this->IsSet("CPACK_PACKAGE_ICON")) {
+ std::string installerIconCode = "!define MUI_HEADERIMAGE_BITMAP \"";
+ installerIconCode += this->GetOption("CPACK_PACKAGE_ICON");
+ installerIconCode += "\"\n";
+ this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_ICON_CODE",
+ installerIconCode.c_str());
+ }
+
+ if (this->IsSet("CPACK_NSIS_MUI_WELCOMEFINISHPAGE_BITMAP")) {
+ std::string installerBitmapCode =
+ "!define MUI_WELCOMEFINISHPAGE_BITMAP \"";
+ installerBitmapCode +=
+ this->GetOption("CPACK_NSIS_MUI_WELCOMEFINISHPAGE_BITMAP");
+ installerBitmapCode += "\"\n";
+ this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_WELCOMEFINISH_CODE",
+ installerBitmapCode.c_str());
+ }
+
+ if (this->IsSet("CPACK_NSIS_MUI_UNWELCOMEFINISHPAGE_BITMAP")) {
+ std::string installerBitmapCode =
+ "!define MUI_UNWELCOMEFINISHPAGE_BITMAP \"";
+ installerBitmapCode +=
+ this->GetOption("CPACK_NSIS_MUI_UNWELCOMEFINISHPAGE_BITMAP");
+ installerBitmapCode += "\"\n";
+ this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_UNWELCOMEFINISH_CODE",
+ installerBitmapCode.c_str());
+ }
+
+ if (this->IsSet("CPACK_NSIS_MUI_FINISHPAGE_RUN")) {
+ std::string installerRunCode = "!define MUI_FINISHPAGE_RUN \"$INSTDIR\\";
+ installerRunCode += this->GetOption("CPACK_NSIS_EXECUTABLES_DIRECTORY");
+ installerRunCode += "\\";
+ installerRunCode += this->GetOption("CPACK_NSIS_MUI_FINISHPAGE_RUN");
+ installerRunCode += "\"\n";
+ this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_FINISHPAGE_RUN_CODE",
+ installerRunCode.c_str());
+ }
+
+ // Setup all of the component sections
+ if (this->Components.empty()) {
+ this->SetOptionIfNotSet("CPACK_NSIS_INSTALLATION_TYPES", "");
+ this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC", "");
+ this->SetOptionIfNotSet("CPACK_NSIS_PAGE_COMPONENTS", "");
+ this->SetOptionIfNotSet("CPACK_NSIS_FULL_INSTALL",
+ "File /r \"${INST_DIR}\\*.*\"");
+ this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTIONS", "");
+ this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTION_LIST", "");
+ this->SetOptionIfNotSet("CPACK_NSIS_SECTION_SELECTED_VARS", "");
+ } else {
+ std::string componentCode;
+ std::string sectionList;
+ std::string selectedVarsList;
+ std::string componentDescriptions;
+ std::string groupDescriptions;
+ std::string installTypesCode;
+ std::string defines;
+ std::ostringstream macrosOut;
+ bool anyDownloadedComponents = false;
+
+ // Create installation types. The order is significant, so we first fill
+ // in a vector based on the indices, and print them in that order.
+ std::vector<cmCPackInstallationType*> installTypes(
+ this->InstallationTypes.size());
+ for (auto& installType : this->InstallationTypes) {
+ installTypes[installType.second.Index - 1] = &installType.second;
+ }
+ for (cmCPackInstallationType* installType : installTypes) {
+ installTypesCode += "InstType \"";
+ installTypesCode += installType->DisplayName;
+ installTypesCode += "\"\n";
+ }
+
+ // Create installation groups first
+ for (auto& group : this->ComponentGroups) {
+ if (group.second.ParentGroup == nullptr) {
+ componentCode +=
+ this->CreateComponentGroupDescription(&group.second, macrosOut);
+ }
+
+ // Add the group description, if any.
+ if (!group.second.Description.empty()) {
+ groupDescriptions += " !insertmacro MUI_DESCRIPTION_TEXT ${" +
+ group.first + "} \"" +
+ cmCPackNSISGenerator::TranslateNewlines(group.second.Description) +
+ "\"\n";
+ }
+ }
+
+ // Create the remaining components, which aren't associated with groups.
+ for (auto& comp : this->Components) {
+ if (comp.second.Files.empty()) {
+ // NSIS cannot cope with components that have no files.
+ continue;
+ }
+
+ anyDownloadedComponents =
+ anyDownloadedComponents || comp.second.IsDownloaded;
+
+ if (!comp.second.Group) {
+ componentCode +=
+ this->CreateComponentDescription(&comp.second, macrosOut);
+ }
+
+ // Add this component to the various section lists.
+ sectionList += " !insertmacro \"${MacroName}\" \"";
+ sectionList += comp.first;
+ sectionList += "\"\n";
+ selectedVarsList += "Var " + comp.first + "_selected\n";
+ selectedVarsList += "Var " + comp.first + "_was_installed\n";
+
+ // Add the component description, if any.
+ if (!comp.second.Description.empty()) {
+ componentDescriptions += " !insertmacro MUI_DESCRIPTION_TEXT ${" +
+ comp.first + "} \"" +
+ cmCPackNSISGenerator::TranslateNewlines(comp.second.Description) +
+ "\"\n";
+ }
+ }
+
+ componentCode += macrosOut.str();
+
+ if (componentDescriptions.empty() && groupDescriptions.empty()) {
+ // Turn off the "Description" box
+ this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC",
+ "!define MUI_COMPONENTSPAGE_NODESC");
+ } else {
+ componentDescriptions = "!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN\n" +
+ componentDescriptions + groupDescriptions +
+ "!insertmacro MUI_FUNCTION_DESCRIPTION_END\n";
+ this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC",
+ componentDescriptions.c_str());
+ }
+
+ if (anyDownloadedComponents) {
+ defines += "!define CPACK_USES_DOWNLOAD\n";
+ if (cmSystemTools::IsOn(this->GetOption("CPACK_ADD_REMOVE"))) {
+ defines += "!define CPACK_NSIS_ADD_REMOVE\n";
+ }
+ }
+
+ this->SetOptionIfNotSet("CPACK_NSIS_INSTALLATION_TYPES",
+ installTypesCode.c_str());
+ this->SetOptionIfNotSet("CPACK_NSIS_PAGE_COMPONENTS",
+ "!insertmacro MUI_PAGE_COMPONENTS");
+ this->SetOptionIfNotSet("CPACK_NSIS_FULL_INSTALL", "");
+ this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTIONS",
+ componentCode.c_str());
+ this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTION_LIST",
+ sectionList.c_str());
+ this->SetOptionIfNotSet("CPACK_NSIS_SECTION_SELECTED_VARS",
+ selectedVarsList.c_str());
+ this->SetOption("CPACK_NSIS_DEFINES", defines.c_str());
+ }
+
+ this->ConfigureFile(nsisInInstallOptions, nsisInstallOptions);
+ this->ConfigureFile(nsisInFileName, nsisFileName);
+ std::string nsisCmd = "\"";
+ nsisCmd += this->GetOption("CPACK_INSTALLER_PROGRAM");
+ nsisCmd += "\" \"" + nsisFileName + "\"";
+ cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << nsisCmd << std::endl);
+ std::string output;
+ int retVal = 1;
+ bool res = cmSystemTools::RunSingleCommand(
+ nsisCmd, &output, &output, &retVal, nullptr, this->GeneratorVerbose,
+ cmDuration::zero());
+ if (!res || retVal) {
+ cmGeneratedFileStream ofs(tmpFile);
+ ofs << "# Run command: " << nsisCmd << std::endl
+ << "# Output:" << std::endl
+ << output << std::endl;
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "Problem running NSIS command: " << nsisCmd << std::endl
+ << "Please check "
+ << tmpFile << " for errors"
+ << std::endl);
+ return 0;
+ }
+ return 1;
+}
+
+int cmCPackNSISGenerator::InitializeInternal()
+{
+ if (cmSystemTools::IsOn(
+ this->GetOption("CPACK_INCLUDE_TOPLEVEL_DIRECTORY"))) {
+ cmCPackLogger(
+ cmCPackLog::LOG_WARNING,
+ "NSIS Generator cannot work with CPACK_INCLUDE_TOPLEVEL_DIRECTORY set. "
+ "This option will be reset to 0 (for this generator only)."
+ << std::endl);
+ this->SetOption("CPACK_INCLUDE_TOPLEVEL_DIRECTORY", nullptr);
+ }
+
+ cmCPackLogger(cmCPackLog::LOG_DEBUG,
+ "cmCPackNSISGenerator::Initialize()" << std::endl);
+ std::vector<std::string> path;
+ std::string nsisPath;
+ bool gotRegValue = false;
+
+#ifdef _WIN32
+ if (Nsis64) {
+ if (!gotRegValue &&
+ cmsys::SystemTools::ReadRegistryValue(
+ "HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS\\Unicode", nsisPath,
+ cmsys::SystemTools::KeyWOW64_64)) {
+ gotRegValue = true;
+ }
+ if (!gotRegValue &&
+ cmsys::SystemTools::ReadRegistryValue(
+ "HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS", nsisPath,
+ cmsys::SystemTools::KeyWOW64_64)) {
+ gotRegValue = true;
+ }
+ }
+ if (!gotRegValue &&
+ cmsys::SystemTools::ReadRegistryValue(
+ "HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS\\Unicode", nsisPath,
+ cmsys::SystemTools::KeyWOW64_32)) {
+ gotRegValue = true;
+ }
+ if (!gotRegValue &&
+ cmsys::SystemTools::ReadRegistryValue(
+ "HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS\\Unicode", nsisPath)) {
+ gotRegValue = true;
+ }
+ if (!gotRegValue &&
+ cmsys::SystemTools::ReadRegistryValue(
+ "HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS", nsisPath,
+ cmsys::SystemTools::KeyWOW64_32)) {
+ gotRegValue = true;
+ }
+ if (!gotRegValue &&
+ cmsys::SystemTools::ReadRegistryValue(
+ "HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS", nsisPath)) {
+ gotRegValue = true;
+ }
+
+ if (gotRegValue) {
+ path.push_back(nsisPath);
+ }
+#endif
+
+ nsisPath = cmSystemTools::FindProgram("makensis", path, false);
+
+ if (nsisPath.empty()) {
+ cmCPackLogger(
+ cmCPackLog::LOG_ERROR,
+ "Cannot find NSIS compiler makensis: likely it is not installed, "
+ "or not in your PATH"
+ << std::endl);
+
+ if (!gotRegValue) {
+ cmCPackLogger(
+ cmCPackLog::LOG_ERROR,
+ "Could not read NSIS registry value. This is usually caused by "
+ "NSIS not being installed. Please install NSIS from "
+ "http://nsis.sourceforge.net"
+ << std::endl);
+ }
+
+ return 0;
+ }
+
+ std::string nsisCmd = "\"" + nsisPath + "\" " NSIS_OPT "VERSION";
+ cmCPackLogger(cmCPackLog::LOG_VERBOSE,
+ "Test NSIS version: " << nsisCmd << std::endl);
+ std::string output;
+ int retVal = 1;
+ bool resS = cmSystemTools::RunSingleCommand(
+ nsisCmd, &output, &output, &retVal, nullptr, this->GeneratorVerbose,
+ cmDuration::zero());
+ cmsys::RegularExpression versionRex("v([0-9]+.[0-9]+)");
+ cmsys::RegularExpression versionRexCVS("v(.*)\\.cvs");
+ if (!resS || retVal ||
+ (!versionRex.find(output) && !versionRexCVS.find(output))) {
+ const char* topDir = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
+ std::string tmpFile = topDir ? topDir : ".";
+ tmpFile += "/NSISOutput.log";
+ cmGeneratedFileStream ofs(tmpFile);
+ ofs << "# Run command: " << nsisCmd << std::endl
+ << "# Output:" << std::endl
+ << output << std::endl;
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "Problem checking NSIS version with command: "
+ << nsisCmd << std::endl
+ << "Please check " << tmpFile << " for errors"
+ << std::endl);
+ return 0;
+ }
+ if (versionRex.find(output)) {
+ double nsisVersion = atof(versionRex.match(1).c_str());
+ double minNSISVersion = 2.09;
+ cmCPackLogger(cmCPackLog::LOG_DEBUG,
+ "NSIS Version: " << nsisVersion << std::endl);
+ if (nsisVersion < minNSISVersion) {
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "CPack requires NSIS Version 2.09 or greater. "
+ "NSIS found on the system was: "
+ << nsisVersion << std::endl);
+ return 0;
+ }
+ }
+ if (versionRexCVS.find(output)) {
+ // No version check for NSIS cvs build
+ cmCPackLogger(cmCPackLog::LOG_DEBUG,
+ "NSIS Version: CVS " << versionRexCVS.match(1) << std::endl);
+ }
+ this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM", nsisPath.c_str());
+ this->SetOptionIfNotSet("CPACK_NSIS_EXECUTABLES_DIRECTORY", "bin");
+ const char* cpackPackageExecutables =
+ this->GetOption("CPACK_PACKAGE_EXECUTABLES");
+ const char* cpackPackageDeskTopLinks =
+ this->GetOption("CPACK_CREATE_DESKTOP_LINKS");
+ const char* cpackNsisExecutablesDirectory =
+ this->GetOption("CPACK_NSIS_EXECUTABLES_DIRECTORY");
+ std::vector<std::string> cpackPackageDesktopLinksVector;
+ if (cpackPackageDeskTopLinks) {
+ cmCPackLogger(cmCPackLog::LOG_DEBUG,
+ "CPACK_CREATE_DESKTOP_LINKS: " << cpackPackageDeskTopLinks
+ << std::endl);
+
+ cmSystemTools::ExpandListArgument(cpackPackageDeskTopLinks,
+ cpackPackageDesktopLinksVector);
+ for (std::string const& cpdl : cpackPackageDesktopLinksVector) {
+ cmCPackLogger(cmCPackLog::LOG_DEBUG,
+ "CPACK_CREATE_DESKTOP_LINKS: " << cpdl << std::endl);
+ }
+ } else {
+ cmCPackLogger(cmCPackLog::LOG_DEBUG,
+ "CPACK_CREATE_DESKTOP_LINKS: "
+ << "not set" << std::endl);
+ }
+
+ std::ostringstream str;
+ std::ostringstream deleteStr;
+
+ if (cpackPackageExecutables) {
+ cmCPackLogger(cmCPackLog::LOG_DEBUG,
+ "The cpackPackageExecutables: " << cpackPackageExecutables
+ << "." << std::endl);
+ std::vector<std::string> cpackPackageExecutablesVector;
+ cmSystemTools::ExpandListArgument(cpackPackageExecutables,
+ cpackPackageExecutablesVector);
+ if (cpackPackageExecutablesVector.size() % 2 != 0) {
+ cmCPackLogger(
+ cmCPackLog::LOG_ERROR,
+ "CPACK_PACKAGE_EXECUTABLES should contain pairs of <executable> and "
+ "<icon name>."
+ << std::endl);
+ return 0;
+ }
+ std::vector<std::string>::iterator it;
+ for (it = cpackPackageExecutablesVector.begin();
+ it != cpackPackageExecutablesVector.end(); ++it) {
+ std::string execName = *it;
+ ++it;
+ std::string linkName = *it;
+ str << " CreateShortCut \"$SMPROGRAMS\\$STARTMENU_FOLDER\\" << linkName
+ << ".lnk\" \"$INSTDIR\\" << cpackNsisExecutablesDirectory << "\\"
+ << execName << ".exe\"" << std::endl;
+ deleteStr << " Delete \"$SMPROGRAMS\\$MUI_TEMP\\" << linkName
+ << ".lnk\"" << std::endl;
+ // see if CPACK_CREATE_DESKTOP_LINK_ExeName is on
+ // if so add a desktop link
+ if (!cpackPackageDesktopLinksVector.empty() &&
+ std::find(cpackPackageDesktopLinksVector.begin(),
+ cpackPackageDesktopLinksVector.end(),
+ execName) != cpackPackageDesktopLinksVector.end()) {
+ str << " StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n";
+ str << " CreateShortCut \"$DESKTOP\\" << linkName
+ << ".lnk\" \"$INSTDIR\\" << cpackNsisExecutablesDirectory << "\\"
+ << execName << ".exe\"" << std::endl;
+ deleteStr << " StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n";
+ deleteStr << " Delete \"$DESKTOP\\" << linkName << ".lnk\""
+ << std::endl;
+ }
+ }
+ }
+
+ this->CreateMenuLinks(str, deleteStr);
+ this->SetOptionIfNotSet("CPACK_NSIS_CREATE_ICONS", str.str().c_str());
+ this->SetOptionIfNotSet("CPACK_NSIS_DELETE_ICONS", deleteStr.str().c_str());
+
+ this->SetOptionIfNotSet("CPACK_NSIS_COMPRESSOR", "lzma");
+
+ return this->Superclass::InitializeInternal();
+}
+
+void cmCPackNSISGenerator::CreateMenuLinks(std::ostream& str,
+ std::ostream& deleteStr)
+{
+ const char* cpackMenuLinks = this->GetOption("CPACK_NSIS_MENU_LINKS");
+ if (!cpackMenuLinks) {
+ return;
+ }
+ cmCPackLogger(cmCPackLog::LOG_DEBUG,
+ "The cpackMenuLinks: " << cpackMenuLinks << "." << std::endl);
+ std::vector<std::string> cpackMenuLinksVector;
+ cmSystemTools::ExpandListArgument(cpackMenuLinks, cpackMenuLinksVector);
+ if (cpackMenuLinksVector.size() % 2 != 0) {
+ cmCPackLogger(
+ cmCPackLog::LOG_ERROR,
+ "CPACK_NSIS_MENU_LINKS should contain pairs of <shortcut target> and "
+ "<shortcut label>."
+ << std::endl);
+ return;
+ }
+
+ static cmsys::RegularExpression urlRegex(
+ "^(mailto:|(ftps?|https?|news)://).*$");
+
+ std::vector<std::string>::iterator it;
+ for (it = cpackMenuLinksVector.begin(); it != cpackMenuLinksVector.end();
+ ++it) {
+ std::string sourceName = *it;
+ const bool url = urlRegex.find(sourceName);
+
+ // Convert / to \ in filenames, but not in urls:
+ //
+ if (!url) {
+ std::replace(sourceName.begin(), sourceName.end(), '/', '\\');
+ }
+
+ ++it;
+ std::string linkName = *it;
+ if (!url) {
+ str << " CreateShortCut \"$SMPROGRAMS\\$STARTMENU_FOLDER\\" << linkName
+ << ".lnk\" \"$INSTDIR\\" << sourceName << "\"" << std::endl;
+ deleteStr << " Delete \"$SMPROGRAMS\\$MUI_TEMP\\" << linkName
+ << ".lnk\"" << std::endl;
+ } else {
+ str << " WriteINIStr \"$SMPROGRAMS\\$STARTMENU_FOLDER\\" << linkName
+ << ".url\" \"InternetShortcut\" \"URL\" \"" << sourceName << "\""
+ << std::endl;
+ deleteStr << " Delete \"$SMPROGRAMS\\$MUI_TEMP\\" << linkName
+ << ".url\"" << std::endl;
+ }
+ // see if CPACK_CREATE_DESKTOP_LINK_ExeName is on
+ // if so add a desktop link
+ std::string desktop = "CPACK_CREATE_DESKTOP_LINK_";
+ desktop += linkName;
+ if (this->IsSet(desktop)) {
+ str << " StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n";
+ str << " CreateShortCut \"$DESKTOP\\" << linkName
+ << ".lnk\" \"$INSTDIR\\" << sourceName << "\"" << std::endl;
+ deleteStr << " StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n";
+ deleteStr << " Delete \"$DESKTOP\\" << linkName << ".lnk\""
+ << std::endl;
+ }
+ }
+}
+
+bool cmCPackNSISGenerator::GetListOfSubdirectories(
+ const char* topdir, std::vector<std::string>& dirs)
+{
+ cmsys::Directory dir;
+ dir.Load(topdir);
+ for (unsigned long i = 0; i < dir.GetNumberOfFiles(); ++i) {
+ const char* fileName = dir.GetFile(i);
+ if (strcmp(fileName, ".") != 0 && strcmp(fileName, "..") != 0) {
+ std::string const fullPath =
+ std::string(topdir).append("/").append(fileName);
+ if (cmsys::SystemTools::FileIsDirectory(fullPath) &&
+ !cmsys::SystemTools::FileIsSymlink(fullPath)) {
+ if (!this->GetListOfSubdirectories(fullPath.c_str(), dirs)) {
+ return false;
+ }
+ }
+ }
+ }
+ dirs.emplace_back(topdir);
+ return true;
+}
+
+enum cmCPackGenerator::CPackSetDestdirSupport
+cmCPackNSISGenerator::SupportsSetDestdir() const
+{
+ return cmCPackGenerator::SETDESTDIR_SHOULD_NOT_BE_USED;
+}
+
+bool cmCPackNSISGenerator::SupportsAbsoluteDestination() const
+{
+ return false;
+}
+
+bool cmCPackNSISGenerator::SupportsComponentInstallation() const
+{
+ return true;
+}
+
+std::string cmCPackNSISGenerator::CreateComponentDescription(
+ cmCPackComponent* component, std::ostream& macrosOut)
+{
+ // Basic description of the component
+ std::string componentCode = "Section ";
+ if (component->IsDisabledByDefault) {
+ componentCode += "/o ";
+ }
+ componentCode += "\"";
+ if (component->IsHidden) {
+ componentCode += "-";
+ }
+ componentCode += component->DisplayName + "\" " + component->Name + "\n";
+ if (component->IsRequired) {
+ componentCode += " SectionIn RO\n";
+ } else if (!component->InstallationTypes.empty()) {
+ std::ostringstream out;
+ for (cmCPackInstallationType const* installType :
+ component->InstallationTypes) {
+ out << " " << installType->Index;
+ }
+ componentCode += " SectionIn" + out.str() + "\n";
+ }
+
+ const std::string componentOutputDir =
+ CustomComponentInstallDirectory(component->Name);
+ componentCode += " SetOutPath \"" + componentOutputDir + "\"\n";
+
+ // Create the actual installation commands
+ if (component->IsDownloaded) {
+ if (component->ArchiveFile.empty()) {
+ // Compute the name of the archive.
+ std::string packagesDir = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
+ packagesDir += ".dummy";
+ std::ostringstream out;
+ out << cmSystemTools::GetFilenameWithoutLastExtension(packagesDir) << "-"
+ << component->Name << ".zip";
+ component->ArchiveFile = out.str();
+ }
+
+ // Create the directory for the upload area
+ const char* userUploadDirectory =
+ this->GetOption("CPACK_UPLOAD_DIRECTORY");
+ std::string uploadDirectory;
+ if (userUploadDirectory && *userUploadDirectory) {
+ uploadDirectory = userUploadDirectory;
+ } else {
+ uploadDirectory = this->GetOption("CPACK_PACKAGE_DIRECTORY");
+ uploadDirectory += "/CPackUploads";
+ }
+ if (!cmSystemTools::FileExists(uploadDirectory)) {
+ if (!cmSystemTools::MakeDirectory(uploadDirectory)) {
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "Unable to create NSIS upload directory "
+ << uploadDirectory << std::endl);
+ return "";
+ }
+ }
+
+ // Remove the old archive, if one exists
+ std::string archiveFile = uploadDirectory + '/' + component->ArchiveFile;
+ cmCPackLogger(cmCPackLog::LOG_OUTPUT,
+ "- Building downloaded component archive: " << archiveFile
+ << std::endl);
+ if (cmSystemTools::FileExists(archiveFile, true)) {
+ if (!cmSystemTools::RemoveFile(archiveFile)) {
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "Unable to remove archive file " << archiveFile
+ << std::endl);
+ return "";
+ }
+ }
+
+ // Find a ZIP program
+ if (!this->IsSet("ZIP_EXECUTABLE")) {
+ this->ReadListFile("Internal/CPack/CPackZIP.cmake");
+
+ if (!this->IsSet("ZIP_EXECUTABLE")) {
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "Unable to find ZIP program" << std::endl);
+ return "";
+ }
+ }
+
+ // The directory where this component's files reside
+ std::string dirName = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
+ dirName += '/';
+ dirName += component->Name;
+ dirName += '/';
+
+ // Build the list of files to go into this archive, and determine the
+ // size of the installed component.
+ std::string zipListFileName = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
+ zipListFileName += "/winZip.filelist";
+ bool needQuotesInFile =
+ cmSystemTools::IsOn(this->GetOption("CPACK_ZIP_NEED_QUOTES"));
+ unsigned long totalSize = 0;
+ { // the scope is needed for cmGeneratedFileStream
+ cmGeneratedFileStream out(zipListFileName);
+ for (std::string const& file : component->Files) {
+ if (needQuotesInFile) {
+ out << "\"";
+ }
+ out << file;
+ if (needQuotesInFile) {
+ out << "\"";
+ }
+ out << std::endl;
+
+ totalSize += cmSystemTools::FileLength(dirName + file);
+ }
+ }
+
+ // Build the archive in the upload area
+ std::string cmd = this->GetOption("CPACK_ZIP_COMMAND");
+ cmsys::SystemTools::ReplaceString(cmd, "<ARCHIVE>", archiveFile.c_str());
+ cmsys::SystemTools::ReplaceString(cmd, "<FILELIST>",
+ zipListFileName.c_str());
+ std::string output;
+ int retVal = -1;
+ int res = cmSystemTools::RunSingleCommand(
+ cmd, &output, &output, &retVal, dirName.c_str(),
+ cmSystemTools::OUTPUT_NONE, cmDuration::zero());
+ if (!res || retVal) {
+ std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
+ tmpFile += "/CompressZip.log";
+ cmGeneratedFileStream ofs(tmpFile);
+ ofs << "# Run command: " << cmd << std::endl
+ << "# Output:" << std::endl
+ << output << std::endl;
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "Problem running zip command: " << cmd << std::endl
+ << "Please check "
+ << tmpFile << " for errors"
+ << std::endl);
+ return "";
+ }
+
+ // Create the NSIS code to download this file on-the-fly.
+ unsigned long totalSizeInKbytes = (totalSize + 512) / 1024;
+ if (totalSizeInKbytes == 0) {
+ totalSizeInKbytes = 1;
+ }
+ std::ostringstream out;
+ /* clang-format off */
+ out << " AddSize " << totalSizeInKbytes << "\n"
+ << " Push \"" << component->ArchiveFile << "\"\n"
+ << " Call DownloadFile\n"
+ << " ZipDLL::extractall \"$INSTDIR\\"
+ << component->ArchiveFile << "\" \"$INSTDIR\"\n"
+ << " Pop $2 ; error message\n"
+ " StrCmp $2 \"success\" +2 0\n"
+ " MessageBox MB_OK \"Failed to unzip $2\"\n"
+ " Delete $INSTDIR\\$0\n";
+ /* clang-format on */
+ componentCode += out.str();
+ } else {
+ componentCode +=
+ " File /r \"${INST_DIR}\\" + component->Name + "\\*.*\"\n";
+ }
+ componentCode += "SectionEnd\n";
+
+ // Macro used to remove the component
+ macrosOut << "!macro Remove_${" << component->Name << "}\n";
+ macrosOut << " IntCmp $" << component->Name << "_was_installed 0 noremove_"
+ << component->Name << "\n";
+ std::string path;
+ for (std::string const& pathIt : component->Files) {
+ path = pathIt;
+ std::replace(path.begin(), path.end(), '/', '\\');
+ macrosOut << " Delete \"" << componentOutputDir << "\\" << path << "\"\n";
+ }
+ for (std::string const& pathIt : component->Directories) {
+ path = pathIt;
+ std::replace(path.begin(), path.end(), '/', '\\');
+ macrosOut << " RMDir \"" << componentOutputDir << "\\" << path << "\"\n";
+ }
+ macrosOut << " noremove_" << component->Name << ":\n";
+ macrosOut << "!macroend\n";
+
+ // Macro used to select each of the components that this component
+ // depends on.
+ std::set<cmCPackComponent*> visited;
+ macrosOut << "!macro Select_" << component->Name << "_depends\n";
+ macrosOut << CreateSelectionDependenciesDescription(component, visited);
+ macrosOut << "!macroend\n";
+
+ // Macro used to deselect each of the components that depend on this
+ // component.
+ visited.clear();
+ macrosOut << "!macro Deselect_required_by_" << component->Name << "\n";
+ macrosOut << CreateDeselectionDependenciesDescription(component, visited);
+ macrosOut << "!macroend\n";
+ return componentCode;
+}
+
+std::string cmCPackNSISGenerator::CreateSelectionDependenciesDescription(
+ cmCPackComponent* component, std::set<cmCPackComponent*>& visited)
+{
+ // Don't visit a component twice
+ if (visited.count(component)) {
+ return std::string();
+ }
+ visited.insert(component);
+
+ std::ostringstream out;
+ for (cmCPackComponent* depend : component->Dependencies) {
+ // Write NSIS code to select this dependency
+ out << " SectionGetFlags ${" << depend->Name << "} $0\n";
+ out << " IntOp $0 $0 | ${SF_SELECTED}\n";
+ out << " SectionSetFlags ${" << depend->Name << "} $0\n";
+ out << " IntOp $" << depend->Name << "_selected 0 + ${SF_SELECTED}\n";
+ // Recurse
+ out << CreateSelectionDependenciesDescription(depend, visited).c_str();
+ }
+
+ return out.str();
+}
+
+std::string cmCPackNSISGenerator::CreateDeselectionDependenciesDescription(
+ cmCPackComponent* component, std::set<cmCPackComponent*>& visited)
+{
+ // Don't visit a component twice
+ if (visited.count(component)) {
+ return std::string();
+ }
+ visited.insert(component);
+
+ std::ostringstream out;
+ for (cmCPackComponent* depend : component->ReverseDependencies) {
+ // Write NSIS code to deselect this dependency
+ out << " SectionGetFlags ${" << depend->Name << "} $0\n";
+ out << " IntOp $1 ${SF_SELECTED} ~\n";
+ out << " IntOp $0 $0 & $1\n";
+ out << " SectionSetFlags ${" << depend->Name << "} $0\n";
+ out << " IntOp $" << depend->Name << "_selected 0 + 0\n";
+
+ // Recurse
+ out << CreateDeselectionDependenciesDescription(depend, visited).c_str();
+ }
+
+ return out.str();
+}
+
+std::string cmCPackNSISGenerator::CreateComponentGroupDescription(
+ cmCPackComponentGroup* group, std::ostream& macrosOut)
+{
+ if (group->Components.empty() && group->Subgroups.empty()) {
+ // Silently skip empty groups. NSIS doesn't support them.
+ return std::string();
+ }
+
+ std::string code = "SectionGroup ";
+ if (group->IsExpandedByDefault) {
+ code += "/e ";
+ }
+ if (group->IsBold) {
+ code += "\"!" + group->DisplayName + "\" " + group->Name + "\n";
+ } else {
+ code += "\"" + group->DisplayName + "\" " + group->Name + "\n";
+ }
+
+ for (cmCPackComponentGroup* g : group->Subgroups) {
+ code += this->CreateComponentGroupDescription(g, macrosOut);
+ }
+
+ for (cmCPackComponent* comp : group->Components) {
+ if (comp->Files.empty()) {
+ continue;
+ }
+
+ code += this->CreateComponentDescription(comp, macrosOut);
+ }
+ code += "SectionGroupEnd\n";
+ 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");
+ return str;
+}