1 /*============================================================================
2 CMake - Cross Platform Makefile Generator
3 Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
5 Distributed under the OSI-approved BSD License (the "License");
6 see accompanying file Copyright.txt for details.
8 This software is distributed WITHOUT ANY WARRANTY; without even the
9 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10 See the License for more information.
11 ============================================================================*/
13 #include "cmCPackNSISGenerator.h"
15 #include "cmGlobalGenerator.h"
16 #include "cmLocalGenerator.h"
17 #include "cmSystemTools.h"
18 #include "cmMakefile.h"
19 #include "cmGeneratedFileStream.h"
20 #include "cmCPackLog.h"
21 #include "cmCPackComponentGroup.h"
23 #include <cmsys/SystemTools.hxx>
24 #include <cmsys/Glob.hxx>
25 #include <cmsys/Directory.hxx>
26 #include <cmsys/RegularExpression.hxx>
28 /* NSIS uses different command line syntax on Windows and others */
35 //----------------------------------------------------------------------
36 cmCPackNSISGenerator::cmCPackNSISGenerator()
40 //----------------------------------------------------------------------
41 cmCPackNSISGenerator::~cmCPackNSISGenerator()
45 //----------------------------------------------------------------------
46 int cmCPackNSISGenerator::PackageFiles()
48 // TODO: Fix nsis to force out file name
50 std::string nsisInFileName = this->FindTemplate("NSIS.template.in");
51 if ( nsisInFileName.size() == 0 )
53 cmCPackLogger(cmCPackLog::LOG_ERROR,
54 "CPack error: Could not find NSIS installer template file."
58 std::string nsisInInstallOptions
59 = this->FindTemplate("NSIS.InstallOptions.ini.in");
60 if ( nsisInInstallOptions.size() == 0 )
62 cmCPackLogger(cmCPackLog::LOG_ERROR,
63 "CPack error: Could not find NSIS installer options file."
68 std::string nsisFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
69 std::string tmpFile = nsisFileName;
70 tmpFile += "/NSISOutput.log";
71 std::string nsisInstallOptions = nsisFileName + "/NSIS.InstallOptions.ini";
72 nsisFileName += "/project.nsi";
74 std::vector<std::string>::const_iterator it;
75 for ( it = files.begin(); it != files.end(); ++ it )
77 std::string fileN = cmSystemTools::RelativePath(toplevel.c_str(),
79 if (!this->Components.empty())
81 // Strip off the component part of the path.
82 fileN = fileN.substr(fileN.find('/')+1, std::string::npos);
84 cmSystemTools::ReplaceString(fileN, "/", "\\");
85 str << " Delete \"$INSTDIR\\" << fileN.c_str() << "\"" << std::endl;
87 cmCPackLogger(cmCPackLog::LOG_DEBUG, "Uninstall Files: "
88 << str.str().c_str() << std::endl);
89 this->SetOptionIfNotSet("CPACK_NSIS_DELETE_FILES", str.str().c_str());
90 std::vector<std::string> dirs;
91 this->GetListOfSubdirectories(toplevel.c_str(), dirs);
92 std::vector<std::string>::const_iterator sit;
94 for ( sit = dirs.begin(); sit != dirs.end(); ++ sit )
96 std::string componentName;
97 std::string fileN = cmSystemTools::RelativePath(toplevel.c_str(),
103 if (!Components.empty())
105 // If this is a component installation, strip off the component
107 std::string::size_type slash = fileN.find('/');
108 if (slash != std::string::npos)
110 // If this is a component installation, determine which component it
112 componentName = fileN.substr(0, slash);
114 // Strip off the component part of the path.
115 fileN = fileN.substr(slash+1, std::string::npos);
118 cmSystemTools::ReplaceString(fileN, "/", "\\");
119 dstr << " RMDir \"$INSTDIR\\" << fileN.c_str() << "\"" << std::endl;
120 if (!componentName.empty())
122 this->Components[componentName].Directories.push_back(fileN);
125 cmCPackLogger(cmCPackLog::LOG_DEBUG, "Uninstall Dirs: "
126 << dstr.str().c_str() << std::endl);
127 this->SetOptionIfNotSet("CPACK_NSIS_DELETE_DIRECTORIES",
130 cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: " << nsisInFileName
131 << " to " << nsisFileName << std::endl);
132 if(this->IsSet("CPACK_NSIS_MUI_ICON")
133 || this->IsSet("CPACK_NSIS_MUI_UNIICON"))
135 std::string installerIconCode;
136 if(this->IsSet("CPACK_NSIS_MUI_ICON"))
138 installerIconCode += "!define MUI_ICON \"";
139 installerIconCode += this->GetOption("CPACK_NSIS_MUI_ICON");
140 installerIconCode += "\"\n";
142 if(this->IsSet("CPACK_NSIS_MUI_UNIICON"))
144 installerIconCode += "!define MUI_UNICON \"";
145 installerIconCode += this->GetOption("CPACK_NSIS_MUI_UNIICON");
146 installerIconCode += "\"\n";
148 this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_ICON_CODE",
149 installerIconCode.c_str());
151 if(this->IsSet("CPACK_PACKAGE_ICON"))
153 std::string installerIconCode = "!define MUI_HEADERIMAGE_BITMAP \"";
154 installerIconCode += this->GetOption("CPACK_PACKAGE_ICON");
155 installerIconCode += "\"\n";
156 this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_ICON_CODE",
157 installerIconCode.c_str());
160 if(this->IsSet("CPACK_NSIS_MUI_FINISHPAGE_RUN"))
162 std::string installerRunCode = "!define MUI_FINISHPAGE_RUN \"$INSTDIR\\";
163 installerRunCode += this->GetOption("CPACK_NSIS_EXECUTABLES_DIRECTORY");
164 installerRunCode += "\\";
165 installerRunCode += this->GetOption("CPACK_NSIS_MUI_FINISHPAGE_RUN");
166 installerRunCode += "\"\n";
167 this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_FINISHPAGE_RUN_CODE",
168 installerRunCode.c_str());
171 // Setup all of the component sections
172 if (this->Components.empty())
174 this->SetOptionIfNotSet("CPACK_NSIS_INSTALLATION_TYPES", "");
175 this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC", "");
176 this->SetOptionIfNotSet("CPACK_NSIS_PAGE_COMPONENTS", "");
177 this->SetOptionIfNotSet("CPACK_NSIS_FULL_INSTALL",
178 "File /r \"${INST_DIR}\\*.*\"");
179 this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTIONS", "");
180 this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTION_LIST", "");
181 this->SetOptionIfNotSet("CPACK_NSIS_SECTION_SELECTED_VARS", "");
185 std::string componentCode;
186 std::string sectionList;
187 std::string selectedVarsList;
188 std::string componentDescriptions;
189 std::string groupDescriptions;
190 std::string installTypesCode;
192 cmOStringStream macrosOut;
193 bool anyDownloadedComponents = false;
195 // Create installation types. The order is significant, so we first fill
196 // in a vector based on the indices, and print them in that order.
197 std::vector<cmCPackInstallationType *>
198 installTypes(this->InstallationTypes.size());
199 std::map<std::string, cmCPackInstallationType>::iterator installTypeIt;
200 for (installTypeIt = this->InstallationTypes.begin();
201 installTypeIt != this->InstallationTypes.end();
204 installTypes[installTypeIt->second.Index-1] = &installTypeIt->second;
206 std::vector<cmCPackInstallationType *>::iterator installTypeIt2;
207 for (installTypeIt2 = installTypes.begin();
208 installTypeIt2 != installTypes.end();
211 installTypesCode += "InstType \"";
212 installTypesCode += (*installTypeIt2)->DisplayName;
213 installTypesCode += "\"\n";
216 // Create installation groups first
217 std::map<std::string, cmCPackComponentGroup>::iterator groupIt;
218 for (groupIt = this->ComponentGroups.begin();
219 groupIt != this->ComponentGroups.end();
222 if (groupIt->second.ParentGroup == 0)
225 this->CreateComponentGroupDescription(&groupIt->second, macrosOut);
228 // Add the group description, if any.
229 if (!groupIt->second.Description.empty())
231 groupDescriptions += " !insertmacro MUI_DESCRIPTION_TEXT ${"
232 + groupIt->first + "} \""
233 + this->TranslateNewlines(groupIt->second.Description) + "\"\n";
237 // Create the remaining components, which aren't associated with groups.
238 std::map<std::string, cmCPackComponent>::iterator compIt;
239 for (compIt = this->Components.begin();
240 compIt != this->Components.end();
243 if (compIt->second.Files.empty())
245 // NSIS cannot cope with components that have no files.
249 anyDownloadedComponents =
250 anyDownloadedComponents || compIt->second.IsDownloaded;
252 if (!compIt->second.Group)
255 += this->CreateComponentDescription(&compIt->second, macrosOut);
258 // Add this component to the various section lists.
259 sectionList += " !insertmacro \"${MacroName}\" \"";
260 sectionList += compIt->first;
261 sectionList += "\"\n";
262 selectedVarsList += "Var " + compIt->first + "_selected\n";
263 selectedVarsList += "Var " + compIt->first + "_was_installed\n";
265 // Add the component description, if any.
266 if (!compIt->second.Description.empty())
268 componentDescriptions += " !insertmacro MUI_DESCRIPTION_TEXT ${"
269 + compIt->first + "} \""
270 + this->TranslateNewlines(compIt->second.Description) + "\"\n";
274 componentCode += macrosOut.str();
276 if (componentDescriptions.empty() && groupDescriptions.empty())
278 // Turn off the "Description" box
279 this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC",
280 "!define MUI_COMPONENTSPAGE_NODESC");
284 componentDescriptions =
285 "!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN\n"
286 + componentDescriptions
288 + "!insertmacro MUI_FUNCTION_DESCRIPTION_END\n";
289 this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC",
290 componentDescriptions.c_str());
293 if (anyDownloadedComponents)
295 defines += "!define CPACK_USES_DOWNLOAD\n";
296 if (cmSystemTools::IsOn(this->GetOption("CPACK_ADD_REMOVE")))
298 defines += "!define CPACK_NSIS_ADD_REMOVE\n";
302 this->SetOptionIfNotSet("CPACK_NSIS_INSTALLATION_TYPES",
303 installTypesCode.c_str());
304 this->SetOptionIfNotSet("CPACK_NSIS_PAGE_COMPONENTS",
305 "!insertmacro MUI_PAGE_COMPONENTS");
306 this->SetOptionIfNotSet("CPACK_NSIS_FULL_INSTALL", "");
307 this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTIONS",
308 componentCode.c_str());
309 this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTION_LIST",
310 sectionList.c_str());
311 this->SetOptionIfNotSet("CPACK_NSIS_SECTION_SELECTED_VARS",
312 selectedVarsList.c_str());
313 this->SetOption("CPACK_NSIS_DEFINES", defines.c_str());
316 this->ConfigureFile(nsisInInstallOptions.c_str(),
317 nsisInstallOptions.c_str());
318 this->ConfigureFile(nsisInFileName.c_str(), nsisFileName.c_str());
319 std::string nsisCmd = "\"";
320 nsisCmd += this->GetOption("CPACK_INSTALLER_PROGRAM");
321 nsisCmd += "\" \"" + nsisFileName + "\"";
322 cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << nsisCmd.c_str()
326 bool res = cmSystemTools::RunSingleCommand(nsisCmd.c_str(), &output,
327 &retVal, 0, this->GeneratorVerbose, 0);
328 if ( !res || retVal )
330 cmGeneratedFileStream ofs(tmpFile.c_str());
331 ofs << "# Run command: " << nsisCmd.c_str() << std::endl
332 << "# Output:" << std::endl
333 << output.c_str() << std::endl;
334 cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running NSIS command: "
335 << nsisCmd.c_str() << std::endl
336 << "Please check " << tmpFile.c_str() << " for errors" << std::endl);
342 //----------------------------------------------------------------------
343 int cmCPackNSISGenerator::InitializeInternal()
345 if ( cmSystemTools::IsOn(this->GetOption(
346 "CPACK_INCLUDE_TOPLEVEL_DIRECTORY")) )
348 cmCPackLogger(cmCPackLog::LOG_WARNING,
349 "NSIS Generator cannot work with CPACK_INCLUDE_TOPLEVEL_DIRECTORY set. "
350 "This option will be reset to 0 (for this generator only)."
352 this->SetOption("CPACK_INCLUDE_TOPLEVEL_DIRECTORY", 0);
355 cmCPackLogger(cmCPackLog::LOG_DEBUG, "cmCPackNSISGenerator::Initialize()"
357 std::vector<std::string> path;
358 std::string nsisPath;
359 bool gotRegValue = true;
362 if ( !cmsys::SystemTools::ReadRegistryValue(
363 "HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS", nsisPath,
364 cmsys::SystemTools::KeyWOW64_32) )
366 if ( !cmsys::SystemTools::ReadRegistryValue(
367 "HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS", nsisPath) )
375 path.push_back(nsisPath);
379 nsisPath = cmSystemTools::FindProgram("makensis", path, false);
381 if ( nsisPath.empty() )
383 cmCPackLogger(cmCPackLog::LOG_ERROR,
384 "Cannot find NSIS compiler makensis: likely it is not installed, "
385 "or not in your PATH"
390 cmCPackLogger(cmCPackLog::LOG_ERROR,
391 "Could not read NSIS registry value. This is usually caused by "
392 "NSIS not being installed. Please install NSIS from "
393 "http://nsis.sourceforge.net"
400 std::string nsisCmd = "\"" + nsisPath + "\" " NSIS_OPT "VERSION";
401 cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Test NSIS version: "
402 << nsisCmd.c_str() << std::endl);
405 bool resS = cmSystemTools::RunSingleCommand(nsisCmd.c_str(),
406 &output, &retVal, 0, this->GeneratorVerbose, 0);
408 cmsys::RegularExpression versionRex("v([0-9]+.[0-9]+)");
409 if ( !resS || retVal || !versionRex.find(output))
411 std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
412 tmpFile += "/NSISOutput.log";
413 cmGeneratedFileStream ofs(tmpFile.c_str());
414 ofs << "# Run command: " << nsisCmd.c_str() << std::endl
415 << "# Output:" << std::endl
416 << output.c_str() << std::endl;
417 cmCPackLogger(cmCPackLog::LOG_ERROR,
418 "Problem checking NSIS version with command: "
419 << nsisCmd.c_str() << std::endl
420 << "Please check " << tmpFile.c_str() << " for errors" << std::endl);
423 double nsisVersion = atof(versionRex.match(1).c_str());
424 double minNSISVersion = 2.09;
425 cmCPackLogger(cmCPackLog::LOG_DEBUG, "NSIS Version: "
426 << nsisVersion << std::endl);
427 if ( nsisVersion < minNSISVersion )
429 cmCPackLogger(cmCPackLog::LOG_ERROR,
430 "CPack requires NSIS Version 2.09 or greater. NSIS found on the system "
432 << nsisVersion << std::endl);
435 this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM", nsisPath.c_str());
436 this->SetOptionIfNotSet("CPACK_NSIS_EXECUTABLES_DIRECTORY", "bin");
437 const char* cpackPackageExecutables
438 = this->GetOption("CPACK_PACKAGE_EXECUTABLES");
439 const char* cpackPackageDeskTopLinks
440 = this->GetOption("CPACK_CREATE_DESKTOP_LINKS");
441 const char* cpackNsisExecutablesDirectory
442 = this->GetOption("CPACK_NSIS_EXECUTABLES_DIRECTORY");
443 std::vector<std::string> cpackPackageDesktopLinksVector;
444 if(cpackPackageDeskTopLinks)
446 cmCPackLogger(cmCPackLog::LOG_DEBUG, "CPACK_CREATE_DESKTOP_LINKS: "
447 << cpackPackageDeskTopLinks << std::endl);
450 ExpandListArgument(cpackPackageDeskTopLinks,
451 cpackPackageDesktopLinksVector);
452 for(std::vector<std::string>::iterator i =
453 cpackPackageDesktopLinksVector.begin(); i !=
454 cpackPackageDesktopLinksVector.end(); ++i)
456 cmCPackLogger(cmCPackLog::LOG_DEBUG, "CPACK_CREATE_DESKTOP_LINKS: "
462 cmCPackLogger(cmCPackLog::LOG_DEBUG, "CPACK_CREATE_DESKTOP_LINKS: "
463 << "not set" << std::endl);
467 cmOStringStream deleteStr;
469 if ( cpackPackageExecutables )
471 cmCPackLogger(cmCPackLog::LOG_DEBUG, "The cpackPackageExecutables: "
472 << cpackPackageExecutables << "." << std::endl);
473 std::vector<std::string> cpackPackageExecutablesVector;
474 cmSystemTools::ExpandListArgument(cpackPackageExecutables,
475 cpackPackageExecutablesVector);
476 if ( cpackPackageExecutablesVector.size() % 2 != 0 )
478 cmCPackLogger(cmCPackLog::LOG_ERROR,
479 "CPACK_PACKAGE_EXECUTABLES should contain pairs of <executable> and "
480 "<icon name>." << std::endl);
483 std::vector<std::string>::iterator it;
484 for ( it = cpackPackageExecutablesVector.begin();
485 it != cpackPackageExecutablesVector.end();
488 std::string execName = *it;
490 std::string linkName = *it;
491 str << " CreateShortCut \"$SMPROGRAMS\\$STARTMENU_FOLDER\\"
492 << linkName << ".lnk\" \"$INSTDIR\\"
493 << cpackNsisExecutablesDirectory << "\\" << execName << ".exe\""
495 deleteStr << " Delete \"$SMPROGRAMS\\$MUI_TEMP\\" << linkName
496 << ".lnk\"" << std::endl;
497 // see if CPACK_CREATE_DESKTOP_LINK_ExeName is on
498 // if so add a desktop link
499 if(cpackPackageDesktopLinksVector.size() &&
500 std::find(cpackPackageDesktopLinksVector.begin(),
501 cpackPackageDesktopLinksVector.end(),
503 != cpackPackageDesktopLinksVector.end())
505 str << " StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n";
506 str << " CreateShortCut \"$DESKTOP\\"
507 << linkName << ".lnk\" \"$INSTDIR\\"
508 << cpackNsisExecutablesDirectory << "\\" << execName << ".exe\""
510 deleteStr << " StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n";
511 deleteStr << " Delete \"$DESKTOP\\" << linkName
512 << ".lnk\"" << std::endl;
517 this->CreateMenuLinks(str, deleteStr);
518 this->SetOptionIfNotSet("CPACK_NSIS_CREATE_ICONS", str.str().c_str());
519 this->SetOptionIfNotSet("CPACK_NSIS_DELETE_ICONS",
520 deleteStr.str().c_str());
522 this->SetOptionIfNotSet("CPACK_NSIS_COMPRESSOR", "lzma");
524 return this->Superclass::InitializeInternal();
527 //----------------------------------------------------------------------
528 void cmCPackNSISGenerator::CreateMenuLinks( cmOStringStream& str,
529 cmOStringStream& deleteStr)
531 const char* cpackMenuLinks
532 = this->GetOption("CPACK_NSIS_MENU_LINKS");
537 cmCPackLogger(cmCPackLog::LOG_DEBUG, "The cpackMenuLinks: "
538 << cpackMenuLinks << "." << std::endl);
539 std::vector<std::string> cpackMenuLinksVector;
540 cmSystemTools::ExpandListArgument(cpackMenuLinks,
541 cpackMenuLinksVector);
542 if ( cpackMenuLinksVector.size() % 2 != 0 )
545 cmCPackLog::LOG_ERROR,
546 "CPACK_NSIS_MENU_LINKS should contain pairs of <shortcut target> and "
547 "<shortcut label>." << std::endl);
551 cmsys::RegularExpression urlRegex;
552 urlRegex.compile("^(mailto:|(ftps?|https?|news)://).*$");
554 std::vector<std::string>::iterator it;
555 for ( it = cpackMenuLinksVector.begin();
556 it != cpackMenuLinksVector.end();
559 std::string sourceName = *it;
560 const bool url = urlRegex.find(sourceName);
562 // Convert / to \ in filenames, but not in urls:
566 cmSystemTools::ReplaceString(sourceName, "/", "\\");
570 std::string linkName = *it;
573 str << " CreateShortCut \"$SMPROGRAMS\\$STARTMENU_FOLDER\\"
574 << linkName << ".lnk\" \"$INSTDIR\\" << sourceName << "\""
576 deleteStr << " Delete \"$SMPROGRAMS\\$MUI_TEMP\\" << linkName
577 << ".lnk\"" << std::endl;
581 str << " WriteINIStr \"$SMPROGRAMS\\$STARTMENU_FOLDER\\"
582 << linkName << ".url\" \"InternetShortcut\" \"URL\" \""
583 << sourceName << "\""
585 deleteStr << " Delete \"$SMPROGRAMS\\$MUI_TEMP\\" << linkName
586 << ".url\"" << std::endl;
588 // see if CPACK_CREATE_DESKTOP_LINK_ExeName is on
589 // if so add a desktop link
590 std::string desktop = "CPACK_CREATE_DESKTOP_LINK_";
592 if(this->IsSet(desktop.c_str()))
594 str << " StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n";
595 str << " CreateShortCut \"$DESKTOP\\"
596 << linkName << ".lnk\" \"$INSTDIR\\" << sourceName << "\""
598 deleteStr << " StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n";
599 deleteStr << " Delete \"$DESKTOP\\" << linkName
600 << ".lnk\"" << std::endl;
605 //----------------------------------------------------------------------
606 bool cmCPackNSISGenerator::GetListOfSubdirectories(const char* topdir,
607 std::vector<std::string>& dirs)
609 cmsys::Directory dir;
612 for (fileNum = 0; fileNum < dir.GetNumberOfFiles(); ++fileNum)
614 if (strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)),".") &&
615 strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)),".."))
617 cmsys_stl::string fullPath = topdir;
619 fullPath += dir.GetFile(static_cast<unsigned long>(fileNum));
620 if(cmsys::SystemTools::FileIsDirectory(fullPath.c_str()) &&
621 !cmsys::SystemTools::FileIsSymlink(fullPath.c_str()))
623 if (!this->GetListOfSubdirectories(fullPath.c_str(), dirs))
630 dirs.push_back(topdir);
634 //----------------------------------------------------------------------
635 enum cmCPackGenerator::CPackSetDestdirSupport
636 cmCPackNSISGenerator::SupportsSetDestdir() const
638 return cmCPackGenerator::SETDESTDIR_SHOULD_NOT_BE_USED;
641 //----------------------------------------------------------------------
642 bool cmCPackNSISGenerator::SupportsAbsoluteDestination() const
647 //----------------------------------------------------------------------
648 bool cmCPackNSISGenerator::SupportsComponentInstallation() const
653 //----------------------------------------------------------------------
655 cmCPackNSISGenerator::
656 CreateComponentDescription(cmCPackComponent *component,
657 cmOStringStream& macrosOut)
659 // Basic description of the component
660 std::string componentCode = "Section ";
661 if (component->IsDisabledByDefault)
663 componentCode += "/o ";
665 componentCode += "\"";
666 if (component->IsHidden)
668 componentCode += "-";
670 componentCode += component->DisplayName + "\" " + component->Name + "\n";
671 if (component->IsRequired)
673 componentCode += " SectionIn RO\n";
675 else if (!component->InstallationTypes.empty())
678 std::vector<cmCPackInstallationType *>::iterator installTypeIter;
679 for (installTypeIter = component->InstallationTypes.begin();
680 installTypeIter != component->InstallationTypes.end();
683 out << " " << (*installTypeIter)->Index;
685 componentCode += " SectionIn" + out.str() + "\n";
687 componentCode += " SetOutPath \"$INSTDIR\"\n";
689 // Create the actual installation commands
690 if (component->IsDownloaded)
692 if (component->ArchiveFile.empty())
694 // Compute the name of the archive.
695 std::string packagesDir = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
696 packagesDir += ".dummy";
698 out << cmSystemTools::GetFilenameWithoutLastExtension(packagesDir)
699 << "-" << component->Name << ".zip";
700 component->ArchiveFile = out.str();
703 // Create the directory for the upload area
704 const char* userUploadDirectory =
705 this->GetOption("CPACK_UPLOAD_DIRECTORY");
706 std::string uploadDirectory;
707 if (userUploadDirectory && *userUploadDirectory)
709 uploadDirectory = userUploadDirectory;
713 uploadDirectory= this->GetOption("CPACK_PACKAGE_DIRECTORY");
714 uploadDirectory += "/CPackUploads";
716 if(!cmSystemTools::FileExists(uploadDirectory.c_str()))
718 if (!cmSystemTools::MakeDirectory(uploadDirectory.c_str()))
720 cmCPackLogger(cmCPackLog::LOG_ERROR,
721 "Unable to create NSIS upload directory " << uploadDirectory
727 // Remove the old archive, if one exists
728 std::string archiveFile = uploadDirectory + '/' + component->ArchiveFile;
729 cmCPackLogger(cmCPackLog::LOG_OUTPUT,
730 "- Building downloaded component archive: "
731 << archiveFile << std::endl);
732 if (cmSystemTools::FileExists(archiveFile.c_str(), true))
734 if (!cmSystemTools::RemoveFile(archiveFile.c_str()))
736 cmCPackLogger(cmCPackLog::LOG_ERROR,
737 "Unable to remove archive file " << archiveFile
743 // Find a ZIP program
744 if (!this->IsSet("ZIP_EXECUTABLE"))
746 this->ReadListFile("CPackZIP.cmake");
748 if (!this->IsSet("ZIP_EXECUTABLE"))
750 cmCPackLogger(cmCPackLog::LOG_ERROR,
751 "Unable to find ZIP program"
757 // The directory where this component's files reside
758 std::string dirName = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
760 dirName += component->Name;
763 // Build the list of files to go into this archive, and determine the
764 // size of the installed component.
765 std::string zipListFileName = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
766 zipListFileName += "/winZip.filelist";
767 bool needQuotesInFile
768 = cmSystemTools::IsOn(this->GetOption("CPACK_ZIP_NEED_QUOTES"));
769 unsigned long totalSize = 0;
770 { // the scope is needed for cmGeneratedFileStream
771 cmGeneratedFileStream out(zipListFileName.c_str());
772 std::vector<std::string>::iterator fileIt;
773 for (fileIt = component->Files.begin();
774 fileIt != component->Files.end();
777 if ( needQuotesInFile )
782 if ( needQuotesInFile )
788 totalSize += cmSystemTools::FileLength((dirName + *fileIt).c_str());
792 // Build the archive in the upload area
793 std::string cmd = this->GetOption("CPACK_ZIP_COMMAND");
794 cmsys::SystemTools::ReplaceString(cmd, "<ARCHIVE>", archiveFile.c_str());
795 cmsys::SystemTools::ReplaceString(cmd, "<FILELIST>",
796 zipListFileName.c_str());
799 int res = cmSystemTools::RunSingleCommand(cmd.c_str(), &output, &retVal,
801 cmSystemTools::OUTPUT_NONE, 0);
802 if ( !res || retVal )
804 std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
805 tmpFile += "/CompressZip.log";
806 cmGeneratedFileStream ofs(tmpFile.c_str());
807 ofs << "# Run command: " << cmd.c_str() << std::endl
808 << "# Output:" << std::endl
809 << output.c_str() << std::endl;
810 cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running zip command: "
811 << cmd.c_str() << std::endl
812 << "Please check " << tmpFile.c_str() << " for errors" << std::endl);
816 // Create the NSIS code to download this file on-the-fly.
817 unsigned long totalSizeInKbytes = (totalSize + 512) / 1024;
818 if (totalSizeInKbytes == 0)
820 totalSizeInKbytes = 1;
823 out << " AddSize " << totalSizeInKbytes << "\n"
824 << " Push \"" << component->ArchiveFile << "\"\n"
825 << " Call DownloadFile\n"
826 << " ZipDLL::extractall \"$INSTDIR\\"
827 << component->ArchiveFile << "\" \"$INSTDIR\"\n"
828 << " Pop $2 ; error message\n"
829 " StrCmp $2 \"success\" +2 0\n"
830 " MessageBox MB_OK \"Failed to unzip $2\"\n"
831 " Delete $INSTDIR\\$0\n";
832 componentCode += out.str();
836 componentCode += " File /r \"${INST_DIR}\\" +
837 component->Name + "\\*.*\"\n";
839 componentCode += "SectionEnd\n";
841 // Macro used to remove the component
842 macrosOut << "!macro Remove_${" << component->Name << "}\n";
843 macrosOut << " IntCmp $" << component->Name << "_was_installed 0 noremove_"
844 << component->Name << "\n";
845 std::vector<std::string>::iterator pathIt;
847 for (pathIt = component->Files.begin();
848 pathIt != component->Files.end();
852 cmSystemTools::ReplaceString(path, "/", "\\");
853 macrosOut << " Delete \"$INSTDIR\\"
857 for (pathIt = component->Directories.begin();
858 pathIt != component->Directories.end();
862 cmSystemTools::ReplaceString(path, "/", "\\");
863 macrosOut << " RMDir \"$INSTDIR\\"
867 macrosOut << " noremove_" << component->Name << ":\n";
868 macrosOut << "!macroend\n";
870 // Macro used to select each of the components that this component
872 std::set<cmCPackComponent *> visited;
873 macrosOut << "!macro Select_" << component->Name << "_depends\n";
874 macrosOut << CreateSelectionDependenciesDescription(component, visited);
875 macrosOut << "!macroend\n";
877 // Macro used to deselect each of the components that depend on this
880 macrosOut << "!macro Deselect_required_by_" << component->Name << "\n";
881 macrosOut << CreateDeselectionDependenciesDescription(component, visited);
882 macrosOut << "!macroend\n";
883 return componentCode;
886 //----------------------------------------------------------------------
887 std::string cmCPackNSISGenerator::CreateSelectionDependenciesDescription
888 (cmCPackComponent *component,
889 std::set<cmCPackComponent *>& visited)
891 // Don't visit a component twice
892 if (visited.count(component))
894 return std::string();
896 visited.insert(component);
899 std::vector<cmCPackComponent *>::iterator dependIt;
900 for (dependIt = component->Dependencies.begin();
901 dependIt != component->Dependencies.end();
904 // Write NSIS code to select this dependency
905 out << " SectionGetFlags ${" << (*dependIt)->Name << "} $0\n";
906 out << " IntOp $0 $0 | ${SF_SELECTED}\n";
907 out << " SectionSetFlags ${" << (*dependIt)->Name << "} $0\n";
908 out << " IntOp $" << (*dependIt)->Name
909 << "_selected 0 + ${SF_SELECTED}\n";
911 out << CreateSelectionDependenciesDescription(*dependIt, visited).c_str();
918 //----------------------------------------------------------------------
919 std::string cmCPackNSISGenerator::CreateDeselectionDependenciesDescription
920 (cmCPackComponent *component,
921 std::set<cmCPackComponent *>& visited)
923 // Don't visit a component twice
924 if (visited.count(component))
926 return std::string();
928 visited.insert(component);
931 std::vector<cmCPackComponent *>::iterator dependIt;
932 for (dependIt = component->ReverseDependencies.begin();
933 dependIt != component->ReverseDependencies.end();
936 // Write NSIS code to deselect this dependency
937 out << " SectionGetFlags ${" << (*dependIt)->Name << "} $0\n";
938 out << " IntOp $1 ${SF_SELECTED} ~\n";
939 out << " IntOp $0 $0 & $1\n";
940 out << " SectionSetFlags ${" << (*dependIt)->Name << "} $0\n";
941 out << " IntOp $" << (*dependIt)->Name << "_selected 0 + 0\n";
945 CreateDeselectionDependenciesDescription(*dependIt, visited).c_str();
951 //----------------------------------------------------------------------
953 cmCPackNSISGenerator::
954 CreateComponentGroupDescription(cmCPackComponentGroup *group,
955 cmOStringStream& macrosOut)
957 if (group->Components.empty() && group->Subgroups.empty())
959 // Silently skip empty groups. NSIS doesn't support them.
960 return std::string();
963 std::string code = "SectionGroup ";
964 if (group->IsExpandedByDefault)
970 code += "\"!" + group->DisplayName + "\" " + group->Name + "\n";
974 code += "\"" + group->DisplayName + "\" " + group->Name + "\n";
977 std::vector<cmCPackComponentGroup*>::iterator groupIt;
978 for (groupIt = group->Subgroups.begin(); groupIt != group->Subgroups.end();
981 code += this->CreateComponentGroupDescription(*groupIt, macrosOut);
984 std::vector<cmCPackComponent*>::iterator comp;
985 for (comp = group->Components.begin();
986 comp != group->Components.end();
989 if ((*comp)->Files.empty())
994 code += this->CreateComponentDescription(*comp, macrosOut);
996 code += "SectionGroupEnd\n";
1000 std::string cmCPackNSISGenerator::TranslateNewlines(std::string str)
1002 cmSystemTools::ReplaceString(str, "\n", "$\\r$\\n");