diff options
Diffstat (limited to 'Source/CPack/cmCPackNSISGenerator.cxx')
-rw-r--r-- | Source/CPack/cmCPackNSISGenerator.cxx | 1044 |
1 files changed, 1044 insertions, 0 deletions
diff --git a/Source/CPack/cmCPackNSISGenerator.cxx b/Source/CPack/cmCPackNSISGenerator.cxx new file mode 100644 index 0000000..62bfa91 --- /dev/null +++ b/Source/CPack/cmCPackNSISGenerator.cxx @@ -0,0 +1,1044 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackNSISGenerator.h" + +#include "cmGlobalGenerator.h" +#include "cmLocalGenerator.h" +#include "cmSystemTools.h" +#include "cmMakefile.h" +#include "cmGeneratedFileStream.h" +#include "cmCPackLog.h" +#include "cmCPackComponentGroup.h" + +#include <cmsys/SystemTools.hxx> +#include <cmsys/Glob.hxx> +#include <cmsys/Directory.hxx> +#include <cmsys/RegularExpression.hxx> + +/* 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() +{ +} + +//---------------------------------------------------------------------- +int cmCPackNSISGenerator::PackageFiles() +{ + // TODO: Fix nsis to force out file name + + std::string nsisInFileName = this->FindTemplate("NSIS.template.in"); + if ( nsisInFileName.size() == 0 ) + { + 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.size() == 0 ) + { + 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"; + cmOStringStream str; + std::vector<std::string>::const_iterator it; + for ( it = files.begin(); it != files.end(); ++ it ) + { + std::string fileN = cmSystemTools::RelativePath(toplevel.c_str(), + it->c_str()); + if (!this->Components.empty()) + { + // Strip off the component part of the path. + fileN = fileN.substr(fileN.find('/')+1, std::string::npos); + } + cmSystemTools::ReplaceString(fileN, "/", "\\"); + str << " Delete \"$INSTDIR\\" << fileN.c_str() << "\"" << std::endl; + } + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Uninstall Files: " + << str.str().c_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::vector<std::string>::const_iterator sit; + cmOStringStream dstr; + for ( sit = dirs.begin(); sit != dirs.end(); ++ sit ) + { + std::string componentName; + std::string fileN = cmSystemTools::RelativePath(toplevel.c_str(), + sit->c_str()); + 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::string::npos); + } + } + cmSystemTools::ReplaceString(fileN, "/", "\\"); + dstr << " RMDir \"$INSTDIR\\" << fileN.c_str() << "\"" << std::endl; + if (!componentName.empty()) + { + this->Components[componentName].Directories.push_back(fileN); + } + } + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Uninstall Dirs: " + << dstr.str().c_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_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; + cmOStringStream 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()); + std::map<std::string, cmCPackInstallationType>::iterator installTypeIt; + for (installTypeIt = this->InstallationTypes.begin(); + installTypeIt != this->InstallationTypes.end(); + ++installTypeIt) + { + installTypes[installTypeIt->second.Index-1] = &installTypeIt->second; + } + std::vector<cmCPackInstallationType *>::iterator installTypeIt2; + for (installTypeIt2 = installTypes.begin(); + installTypeIt2 != installTypes.end(); + ++installTypeIt2) + { + installTypesCode += "InstType \""; + installTypesCode += (*installTypeIt2)->DisplayName; + installTypesCode += "\"\n"; + } + + // Create installation groups first + std::map<std::string, cmCPackComponentGroup>::iterator groupIt; + for (groupIt = this->ComponentGroups.begin(); + groupIt != this->ComponentGroups.end(); + ++groupIt) + { + if (groupIt->second.ParentGroup == 0) + { + componentCode += + this->CreateComponentGroupDescription(&groupIt->second, macrosOut); + } + + // Add the group description, if any. + if (!groupIt->second.Description.empty()) + { + groupDescriptions += " !insertmacro MUI_DESCRIPTION_TEXT ${" + + groupIt->first + "} \"" + + this->TranslateNewlines(groupIt->second.Description) + "\"\n"; + } + } + + // Create the remaining components, which aren't associated with groups. + std::map<std::string, cmCPackComponent>::iterator compIt; + for (compIt = this->Components.begin(); + compIt != this->Components.end(); + ++compIt) + { + if (compIt->second.Files.empty()) + { + // NSIS cannot cope with components that have no files. + continue; + } + + anyDownloadedComponents = + anyDownloadedComponents || compIt->second.IsDownloaded; + + if (!compIt->second.Group) + { + componentCode + += this->CreateComponentDescription(&compIt->second, macrosOut); + } + + // Add this component to the various section lists. + sectionList += " !insertmacro \"${MacroName}\" \""; + sectionList += compIt->first; + sectionList += "\"\n"; + selectedVarsList += "Var " + compIt->first + "_selected\n"; + selectedVarsList += "Var " + compIt->first + "_was_installed\n"; + + // Add the component description, if any. + if (!compIt->second.Description.empty()) + { + componentDescriptions += " !insertmacro MUI_DESCRIPTION_TEXT ${" + + compIt->first + "} \"" + + this->TranslateNewlines(compIt->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.c_str(), + nsisInstallOptions.c_str()); + this->ConfigureFile(nsisInFileName.c_str(), nsisFileName.c_str()); + std::string nsisCmd = "\""; + nsisCmd += this->GetOption("CPACK_INSTALLER_PROGRAM"); + nsisCmd += "\" \"" + nsisFileName + "\""; + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << nsisCmd.c_str() + << std::endl); + std::string output; + int retVal = 1; + bool res = cmSystemTools::RunSingleCommand(nsisCmd.c_str(), &output, + &retVal, 0, this->GeneratorVerbose, 0); + if ( !res || retVal ) + { + cmGeneratedFileStream ofs(tmpFile.c_str()); + ofs << "# Run command: " << nsisCmd.c_str() << std::endl + << "# Output:" << std::endl + << output.c_str() << std::endl; + cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running NSIS command: " + << nsisCmd.c_str() << std::endl + << "Please check " << tmpFile.c_str() << " 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", 0); + } + + 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.c_str() << std::endl); + std::string output; + int retVal = 1; + bool resS = cmSystemTools::RunSingleCommand(nsisCmd.c_str(), + &output, &retVal, 0, this->GeneratorVerbose, 0); + 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.c_str()); + ofs << "# Run command: " << nsisCmd.c_str() << std::endl + << "# Output:" << std::endl + << output.c_str() << std::endl; + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Problem checking NSIS version with command: " + << nsisCmd.c_str() << std::endl + << "Please check " << tmpFile.c_str() << " 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).c_str() << 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::vector<std::string>::iterator i = + cpackPackageDesktopLinksVector.begin(); i != + cpackPackageDesktopLinksVector.end(); ++i) + { + cmCPackLogger(cmCPackLog::LOG_DEBUG, "CPACK_CREATE_DESKTOP_LINKS: " + << *i << std::endl); + } + } + else + { + cmCPackLogger(cmCPackLog::LOG_DEBUG, "CPACK_CREATE_DESKTOP_LINKS: " + << "not set" << std::endl); + } + + cmOStringStream str; + cmOStringStream 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.size() && + 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( cmOStringStream& str, + cmOStringStream& 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; + } + + cmsys::RegularExpression urlRegex; + urlRegex.compile("^(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) + { + cmSystemTools::ReplaceString(sourceName, "/", "\\"); + } + + ++ 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.c_str())) + { + 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); + size_t fileNum; + for (fileNum = 0; fileNum < dir.GetNumberOfFiles(); ++fileNum) + { + if (strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)),".") && + strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)),"..")) + { + cmsys_stl::string fullPath = topdir; + fullPath += "/"; + fullPath += dir.GetFile(static_cast<unsigned long>(fileNum)); + if(cmsys::SystemTools::FileIsDirectory(fullPath.c_str()) && + !cmsys::SystemTools::FileIsSymlink(fullPath.c_str())) + { + if (!this->GetListOfSubdirectories(fullPath.c_str(), dirs)) + { + return false; + } + } + } + } + dirs.push_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, + cmOStringStream& 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()) + { + cmOStringStream out; + std::vector<cmCPackInstallationType *>::iterator installTypeIter; + for (installTypeIter = component->InstallationTypes.begin(); + installTypeIter != component->InstallationTypes.end(); + ++installTypeIter) + { + out << " " << (*installTypeIter)->Index; + } + componentCode += " SectionIn" + out.str() + "\n"; + } + componentCode += " SetOutPath \"$INSTDIR\"\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"; + cmOStringStream 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.c_str())) + { + if (!cmSystemTools::MakeDirectory(uploadDirectory.c_str())) + { + 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.c_str(), true)) + { + if (!cmSystemTools::RemoveFile(archiveFile.c_str())) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Unable to remove archive file " << archiveFile + << std::endl); + return ""; + } + } + + // Find a ZIP program + if (!this->IsSet("ZIP_EXECUTABLE")) + { + this->ReadListFile("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.c_str()); + std::vector<std::string>::iterator fileIt; + for (fileIt = component->Files.begin(); + fileIt != component->Files.end(); + ++fileIt) + { + if ( needQuotesInFile ) + { + out << "\""; + } + out << *fileIt; + if ( needQuotesInFile ) + { + out << "\""; + } + out << std::endl; + + totalSize += cmSystemTools::FileLength((dirName + *fileIt).c_str()); + } + } + + // 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.c_str(), &output, &retVal, + dirName.c_str(), + cmSystemTools::OUTPUT_NONE, 0); + if ( !res || retVal ) + { + std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + tmpFile += "/CompressZip.log"; + cmGeneratedFileStream ofs(tmpFile.c_str()); + ofs << "# Run command: " << cmd.c_str() << std::endl + << "# Output:" << std::endl + << output.c_str() << std::endl; + cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running zip command: " + << cmd.c_str() << std::endl + << "Please check " << tmpFile.c_str() << " 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; + } + cmOStringStream out; + 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"; + 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::vector<std::string>::iterator pathIt; + std::string path; + for (pathIt = component->Files.begin(); + pathIt != component->Files.end(); + ++pathIt) + { + path = *pathIt; + cmSystemTools::ReplaceString(path, "/", "\\"); + macrosOut << " Delete \"$INSTDIR\\" + << path.c_str() + << "\"\n"; + } + for (pathIt = component->Directories.begin(); + pathIt != component->Directories.end(); + ++pathIt) + { + path = *pathIt; + cmSystemTools::ReplaceString(path, "/", "\\"); + macrosOut << " RMDir \"$INSTDIR\\" + << path.c_str() + << "\"\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); + + cmOStringStream out; + std::vector<cmCPackComponent *>::iterator dependIt; + for (dependIt = component->Dependencies.begin(); + dependIt != component->Dependencies.end(); + ++dependIt) + { + // Write NSIS code to select this dependency + out << " SectionGetFlags ${" << (*dependIt)->Name << "} $0\n"; + out << " IntOp $0 $0 | ${SF_SELECTED}\n"; + out << " SectionSetFlags ${" << (*dependIt)->Name << "} $0\n"; + out << " IntOp $" << (*dependIt)->Name + << "_selected 0 + ${SF_SELECTED}\n"; + // Recurse + out << CreateSelectionDependenciesDescription(*dependIt, 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); + + cmOStringStream out; + std::vector<cmCPackComponent *>::iterator dependIt; + for (dependIt = component->ReverseDependencies.begin(); + dependIt != component->ReverseDependencies.end(); + ++dependIt) + { + // Write NSIS code to deselect this dependency + out << " SectionGetFlags ${" << (*dependIt)->Name << "} $0\n"; + out << " IntOp $1 ${SF_SELECTED} ~\n"; + out << " IntOp $0 $0 & $1\n"; + out << " SectionSetFlags ${" << (*dependIt)->Name << "} $0\n"; + out << " IntOp $" << (*dependIt)->Name << "_selected 0 + 0\n"; + + // Recurse + out << + CreateDeselectionDependenciesDescription(*dependIt, visited).c_str(); + } + + return out.str(); +} + +//---------------------------------------------------------------------- +std::string +cmCPackNSISGenerator:: +CreateComponentGroupDescription(cmCPackComponentGroup *group, + cmOStringStream& 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"; + } + + std::vector<cmCPackComponentGroup*>::iterator groupIt; + for (groupIt = group->Subgroups.begin(); groupIt != group->Subgroups.end(); + ++groupIt) + { + code += this->CreateComponentGroupDescription(*groupIt, macrosOut); + } + + std::vector<cmCPackComponent*>::iterator comp; + for (comp = group->Components.begin(); + comp != group->Components.end(); + ++comp) + { + if ((*comp)->Files.empty()) + { + continue; + } + + code += this->CreateComponentDescription(*comp, macrosOut); + } + code += "SectionGroupEnd\n"; + return code; +} + +std::string cmCPackNSISGenerator::TranslateNewlines(std::string str) +{ + cmSystemTools::ReplaceString(str, "\n", "$\\r$\\n"); + return str; +} |