KWSys Nightly Date Stamp
[cmake.git] / Source / CPack / cmCPackNSISGenerator.cxx
blob05da383da1aa7d97ced33af56ee9b12c16d46228
1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmCPackNSISGenerator.cxx,v $
5 Language: C++
6 Date: $Date: 2009-01-20 19:29:41 $
7 Version: $Revision: 1.39 $
9 Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
10 See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
12 This software is distributed WITHOUT ANY WARRANTY; without even
13 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 PURPOSE. See the above copyright notices for more information.
16 =========================================================================*/
18 #include "cmCPackNSISGenerator.h"
20 #include "cmGlobalGenerator.h"
21 #include "cmLocalGenerator.h"
22 #include "cmSystemTools.h"
23 #include "cmMakefile.h"
24 #include "cmGeneratedFileStream.h"
25 #include "cmCPackLog.h"
26 #include "cmCPackComponentGroup.h"
28 #include <cmsys/SystemTools.hxx>
29 #include <cmsys/Glob.hxx>
30 #include <cmsys/Directory.hxx>
31 #include <cmsys/RegularExpression.hxx>
33 /* NSIS uses different command line syntax on Windows and others */
34 #ifdef _WIN32
35 # define NSIS_OPT "/"
36 #else
37 # define NSIS_OPT "-"
38 #endif
40 //----------------------------------------------------------------------
41 cmCPackNSISGenerator::cmCPackNSISGenerator()
45 //----------------------------------------------------------------------
46 cmCPackNSISGenerator::~cmCPackNSISGenerator()
50 //----------------------------------------------------------------------
51 int cmCPackNSISGenerator::CompressFiles(const char* outFileName,
52 const char* toplevel, const std::vector<std::string>& files)
54 (void)outFileName; // TODO: Fix nsis to force out file name
55 (void)toplevel;
56 std::string nsisInFileName = this->FindTemplate("NSIS.template.in");
57 if ( nsisInFileName.size() == 0 )
59 cmCPackLogger(cmCPackLog::LOG_ERROR,
60 "CPack error: Could not find NSIS installer template file."
61 << std::endl);
62 return false;
64 std::string nsisInInstallOptions
65 = this->FindTemplate("NSIS.InstallOptions.ini.in");
66 if ( nsisInInstallOptions.size() == 0 )
68 cmCPackLogger(cmCPackLog::LOG_ERROR,
69 "CPack error: Could not find NSIS installer options file."
70 << std::endl);
71 return false;
73 std::string nsisFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
74 std::string tmpFile = nsisFileName;
75 tmpFile += "/NSISOutput.log";
76 std::string nsisInstallOptions = nsisFileName + "/NSIS.InstallOptions.ini";
77 nsisFileName += "/project.nsi";
78 cmOStringStream str;
79 std::vector<std::string>::const_iterator it;
80 for ( it = files.begin(); it != files.end(); ++ it )
82 std::string fileN = cmSystemTools::RelativePath(toplevel,
83 it->c_str());
84 if (!this->Components.empty())
86 // Strip off the component part of the path.
87 fileN = fileN.substr(fileN.find('/')+1, std::string::npos);
89 cmSystemTools::ReplaceString(fileN, "/", "\\");
90 str << " Delete \"$INSTDIR\\" << fileN.c_str() << "\"" << std::endl;
92 cmCPackLogger(cmCPackLog::LOG_DEBUG, "Uninstall Files: "
93 << str.str().c_str() << std::endl);
94 this->SetOptionIfNotSet("CPACK_NSIS_DELETE_FILES", str.str().c_str());
95 std::vector<std::string> dirs;
96 this->GetListOfSubdirectories(toplevel, dirs);
97 std::vector<std::string>::const_iterator sit;
98 cmOStringStream dstr;
99 for ( sit = dirs.begin(); sit != dirs.end(); ++ sit )
101 std::string componentName;
102 std::string fileN = cmSystemTools::RelativePath(toplevel, sit->c_str());
103 if ( fileN.empty() )
105 continue;
107 if (!Components.empty())
109 // If this is a component installation, strip off the component
110 // part of the path.
111 std::string::size_type slash = fileN.find('/');
112 if (slash != std::string::npos)
114 // If this is a component installation, determine which component it
115 // is.
116 componentName = fileN.substr(0, slash);
118 // Strip off the component part of the path.
119 fileN = fileN.substr(slash+1, std::string::npos);
122 cmSystemTools::ReplaceString(fileN, "/", "\\");
123 dstr << " RMDir \"$INSTDIR\\" << fileN.c_str() << "\"" << std::endl;
124 if (!componentName.empty())
126 this->Components[componentName].Directories.push_back(fileN);
129 cmCPackLogger(cmCPackLog::LOG_DEBUG, "Uninstall Dirs: "
130 << dstr.str().c_str() << std::endl);
131 this->SetOptionIfNotSet("CPACK_NSIS_DELETE_DIRECTORIES",
132 dstr.str().c_str());
134 cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: " << nsisInFileName
135 << " to " << nsisFileName << std::endl);
136 if(this->IsSet("CPACK_NSIS_MUI_ICON")
137 && this->IsSet("CPACK_NSIS_MUI_UNIICON"))
139 std::string installerIconCode="!define MUI_ICON \"";
140 installerIconCode += this->GetOption("CPACK_NSIS_MUI_ICON");
141 installerIconCode += "\"\n";
142 installerIconCode += "!define MUI_UNICON \"";
143 installerIconCode += this->GetOption("CPACK_NSIS_MUI_UNIICON");
144 installerIconCode += "\"\n";
145 this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_ICON_CODE",
146 installerIconCode.c_str());
148 if(this->IsSet("CPACK_PACKAGE_ICON"))
150 std::string installerIconCode = "!define MUI_HEADERIMAGE_BITMAP \"";
151 installerIconCode += this->GetOption("CPACK_PACKAGE_ICON");
152 installerIconCode += "\"\n";
153 this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_ICON_CODE",
154 installerIconCode.c_str());
157 // Setup all of the component sections
158 if (this->Components.empty())
160 this->SetOptionIfNotSet("CPACK_NSIS_INSTALLATION_TYPES", "");
161 this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC", "");
162 this->SetOptionIfNotSet("CPACK_NSIS_PAGE_COMPONENTS", "");
163 this->SetOptionIfNotSet("CPACK_NSIS_FULL_INSTALL",
164 "File /r \"${INST_DIR}\\*.*\"");
165 this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTIONS", "");
166 this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTION_LIST", "");
167 this->SetOptionIfNotSet("CPACK_NSIS_SECTION_SELECTED_VARS", "");
169 else
171 std::string componentCode;
172 std::string sectionList;
173 std::string selectedVarsList;
174 std::string componentDescriptions;
175 std::string groupDescriptions;
176 std::string installTypesCode;
177 std::string defines;
178 cmOStringStream macrosOut;
179 bool anyDownloadedComponents = false;
181 // Create installation types. The order is significant, so we first fill
182 // in a vector based on the indices, and print them in that order.
183 std::vector<cmCPackInstallationType *>
184 installTypes(this->InstallationTypes.size());
185 std::map<std::string, cmCPackInstallationType>::iterator installTypeIt;
186 for (installTypeIt = this->InstallationTypes.begin();
187 installTypeIt != this->InstallationTypes.end();
188 ++installTypeIt)
190 installTypes[installTypeIt->second.Index-1] = &installTypeIt->second;
192 std::vector<cmCPackInstallationType *>::iterator installTypeIt2;
193 for (installTypeIt2 = installTypes.begin();
194 installTypeIt2 != installTypes.end();
195 ++installTypeIt2)
197 installTypesCode += "InstType \"";
198 installTypesCode += (*installTypeIt2)->DisplayName;
199 installTypesCode += "\"\n";
202 // Create installation groups first
203 std::map<std::string, cmCPackComponentGroup>::iterator groupIt;
204 for (groupIt = this->ComponentGroups.begin();
205 groupIt != this->ComponentGroups.end();
206 ++groupIt)
208 if (groupIt->second.ParentGroup == 0)
210 componentCode +=
211 this->CreateComponentGroupDescription(&groupIt->second, macrosOut);
214 // Add the group description, if any.
215 if (!groupIt->second.Description.empty())
217 groupDescriptions += " !insertmacro MUI_DESCRIPTION_TEXT ${"
218 + groupIt->first + "} \""
219 + this->TranslateNewlines(groupIt->second.Description) + "\"\n";
223 // Create the remaining components, which aren't associated with groups.
224 std::map<std::string, cmCPackComponent>::iterator compIt;
225 for (compIt = this->Components.begin();
226 compIt != this->Components.end();
227 ++compIt)
229 if (compIt->second.Files.empty())
231 // NSIS cannot cope with components that have no files.
232 continue;
235 anyDownloadedComponents =
236 anyDownloadedComponents || compIt->second.IsDownloaded;
238 if (!compIt->second.Group)
240 componentCode
241 += this->CreateComponentDescription(&compIt->second, macrosOut);
244 // Add this component to the various section lists.
245 sectionList += " !insertmacro \"${MacroName}\" \"";
246 sectionList += compIt->first;
247 sectionList += "\"\n";
248 selectedVarsList += "Var " + compIt->first + "_selected\n";
249 selectedVarsList += "Var " + compIt->first + "_was_installed\n";
251 // Add the component description, if any.
252 if (!compIt->second.Description.empty())
254 componentDescriptions += " !insertmacro MUI_DESCRIPTION_TEXT ${"
255 + compIt->first + "} \""
256 + this->TranslateNewlines(compIt->second.Description) + "\"\n";
260 componentCode += macrosOut.str();
262 if (componentDescriptions.empty() && groupDescriptions.empty())
264 // Turn off the "Description" box
265 this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC",
266 "!define MUI_COMPONENTSPAGE_NODESC");
268 else
270 componentDescriptions =
271 "!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN\n"
272 + componentDescriptions
273 + groupDescriptions
274 + "!insertmacro MUI_FUNCTION_DESCRIPTION_END\n";
275 this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC",
276 componentDescriptions.c_str());
279 if (anyDownloadedComponents)
281 defines += "!define CPACK_USES_DOWNLOAD\n";
282 if (cmSystemTools::IsOn(this->GetOption("CPACK_ADD_REMOVE")))
284 defines += "!define CPACK_NSIS_ADD_REMOVE\n";
288 this->SetOptionIfNotSet("CPACK_NSIS_INSTALLATION_TYPES",
289 installTypesCode.c_str());
290 this->SetOptionIfNotSet("CPACK_NSIS_PAGE_COMPONENTS",
291 "!insertmacro MUI_PAGE_COMPONENTS");
292 this->SetOptionIfNotSet("CPACK_NSIS_FULL_INSTALL", "");
293 this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTIONS",
294 componentCode.c_str());
295 this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTION_LIST",
296 sectionList.c_str());
297 this->SetOptionIfNotSet("CPACK_NSIS_SECTION_SELECTED_VARS",
298 selectedVarsList.c_str());
299 this->SetOption("CPACK_NSIS_DEFINES", defines.c_str());
302 this->ConfigureFile(nsisInInstallOptions.c_str(),
303 nsisInstallOptions.c_str());
304 this->ConfigureFile(nsisInFileName.c_str(), nsisFileName.c_str());
305 std::string nsisCmd = "\"";
306 nsisCmd += this->GetOption("CPACK_INSTALLER_PROGRAM");
307 nsisCmd += "\" \"" + nsisFileName + "\"";
308 cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << nsisCmd.c_str()
309 << std::endl);
310 std::string output;
311 int retVal = 1;
312 bool res = cmSystemTools::RunSingleCommand(nsisCmd.c_str(), &output,
313 &retVal, 0, this->GeneratorVerbose, 0);
314 if ( !res || retVal )
316 cmGeneratedFileStream ofs(tmpFile.c_str());
317 ofs << "# Run command: " << nsisCmd.c_str() << std::endl
318 << "# Output:" << std::endl
319 << output.c_str() << std::endl;
320 cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running NSIS command: "
321 << nsisCmd.c_str() << std::endl
322 << "Please check " << tmpFile.c_str() << " for errors" << std::endl);
323 return 0;
325 return 1;
328 //----------------------------------------------------------------------
329 int cmCPackNSISGenerator::InitializeInternal()
331 if ( cmSystemTools::IsOn(this->GetOption(
332 "CPACK_INCLUDE_TOPLEVEL_DIRECTORY")) )
334 cmCPackLogger(cmCPackLog::LOG_ERROR,
335 "NSIS Generator cannot work with CPACK_INCLUDE_TOPLEVEL_DIRECTORY. "
336 "This option will be ignored."
337 << std::endl);
338 this->SetOption("CPACK_INCLUDE_TOPLEVEL_DIRECTORY", 0);
341 cmCPackLogger(cmCPackLog::LOG_DEBUG, "cmCPackNSISGenerator::Initialize()"
342 << std::endl);
343 std::vector<std::string> path;
344 std::string nsisPath;
346 #ifdef _WIN32
347 if ( !cmsys::SystemTools::ReadRegistryValue(
348 "HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS", nsisPath,
349 cmsys::SystemTools::KeyWOW64_32) )
351 if ( !cmsys::SystemTools::ReadRegistryValue(
352 "HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS", nsisPath) )
354 cmCPackLogger
355 (cmCPackLog::LOG_ERROR,
356 "Cannot find NSIS registry value. This is usually caused by NSIS "
357 "not being installed. Please install NSIS from "
358 "http://nsis.sourceforge.net"
359 << std::endl);
360 return 0;
363 path.push_back(nsisPath);
364 #endif
365 nsisPath = cmSystemTools::FindProgram("makensis", path, false);
366 if ( nsisPath.empty() )
368 cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find NSIS compiler"
369 << std::endl);
370 return 0;
372 std::string nsisCmd = "\"" + nsisPath + "\" " NSIS_OPT "VERSION";
373 cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Test NSIS version: "
374 << nsisCmd.c_str() << std::endl);
375 std::string output;
376 int retVal = 1;
377 bool resS = cmSystemTools::RunSingleCommand(nsisCmd.c_str(),
378 &output, &retVal, 0, this->GeneratorVerbose, 0);
380 cmsys::RegularExpression versionRex("v([0-9]+.[0-9]+)");
381 if ( !resS || retVal || !versionRex.find(output))
383 std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
384 tmpFile += "/NSISOutput.log";
385 cmGeneratedFileStream ofs(tmpFile.c_str());
386 ofs << "# Run command: " << nsisCmd.c_str() << std::endl
387 << "# Output:" << std::endl
388 << output.c_str() << std::endl;
389 cmCPackLogger(cmCPackLog::LOG_ERROR,
390 "Problem checking NSIS version with command: "
391 << nsisCmd.c_str() << std::endl
392 << "Please check " << tmpFile.c_str() << " for errors" << std::endl);
393 return 0;
395 double nsisVersion = atof(versionRex.match(1).c_str());
396 double minNSISVersion = 2.09;
397 cmCPackLogger(cmCPackLog::LOG_DEBUG, "NSIS Version: "
398 << nsisVersion << std::endl);
399 if ( nsisVersion < minNSISVersion )
401 cmCPackLogger(cmCPackLog::LOG_ERROR,
402 "CPack requires NSIS Version 2.09 or greater. NSIS found on the system "
403 "was: "
404 << nsisVersion << std::endl);
405 return 0;
407 this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM", nsisPath.c_str());
408 const char* cpackPackageExecutables
409 = this->GetOption("CPACK_PACKAGE_EXECUTABLES");
410 const char* cpackPackageDeskTopLinks
411 = this->GetOption("CPACK_CREATE_DESKTOP_LINKS");
412 std::vector<std::string> cpackPackageDesktopLinksVector;
413 if(cpackPackageDeskTopLinks)
415 cmCPackLogger(cmCPackLog::LOG_DEBUG, "CPACK_CREATE_DESKTOP_LINKS: "
416 << cpackPackageDeskTopLinks << std::endl);
418 cmSystemTools::
419 ExpandListArgument(cpackPackageDeskTopLinks,
420 cpackPackageDesktopLinksVector);
421 for(std::vector<std::string>::iterator i =
422 cpackPackageDesktopLinksVector.begin(); i !=
423 cpackPackageDesktopLinksVector.end(); ++i)
425 cmCPackLogger(cmCPackLog::LOG_DEBUG, "CPACK_CREATE_DESKTOP_LINKS: "
426 << *i << std::endl);
429 else
431 cmCPackLogger(cmCPackLog::LOG_DEBUG, "CPACK_CREATE_DESKTOP_LINKS: "
432 << "not set" << std::endl);
434 if ( cpackPackageExecutables )
436 cmCPackLogger(cmCPackLog::LOG_DEBUG, "The cpackPackageExecutables: "
437 << cpackPackageExecutables << "." << std::endl);
438 cmOStringStream str;
439 cmOStringStream deleteStr;
440 std::vector<std::string> cpackPackageExecutablesVector;
441 cmSystemTools::ExpandListArgument(cpackPackageExecutables,
442 cpackPackageExecutablesVector);
443 if ( cpackPackageExecutablesVector.size() % 2 != 0 )
445 cmCPackLogger(cmCPackLog::LOG_ERROR,
446 "CPACK_PACKAGE_EXECUTABLES should contain pairs of <executable> and "
447 "<icon name>." << std::endl);
448 return 0;
450 std::vector<std::string>::iterator it;
451 for ( it = cpackPackageExecutablesVector.begin();
452 it != cpackPackageExecutablesVector.end();
453 ++it )
455 std::string execName = *it;
456 ++ it;
457 std::string linkName = *it;
458 str << " CreateShortCut \"$SMPROGRAMS\\$STARTMENU_FOLDER\\"
459 << linkName << ".lnk\" \"$INSTDIR\\bin\\" << execName << ".exe\""
460 << std::endl;
461 deleteStr << " Delete \"$SMPROGRAMS\\$MUI_TEMP\\" << linkName
462 << ".lnk\"" << std::endl;
463 // see if CPACK_CREATE_DESKTOP_LINK_ExeName is on
464 // if so add a desktop link
465 if(cpackPackageDesktopLinksVector.size() &&
466 std::find(cpackPackageDesktopLinksVector.begin(),
467 cpackPackageDesktopLinksVector.end(),
468 execName)
469 != cpackPackageDesktopLinksVector.end())
471 str << " StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n";
472 str << " CreateShortCut \"$DESKTOP\\"
473 << linkName << ".lnk\" \"$INSTDIR\\bin\\" << execName << ".exe\""
474 << std::endl;
475 deleteStr << " StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n";
476 deleteStr << " Delete \"$DESKTOP\\" << linkName
477 << ".lnk\"" << std::endl;
480 this->CreateMenuLinks(str, deleteStr);
481 this->SetOptionIfNotSet("CPACK_NSIS_CREATE_ICONS", str.str().c_str());
482 this->SetOptionIfNotSet("CPACK_NSIS_DELETE_ICONS",
483 deleteStr.str().c_str());
485 this->SetOptionIfNotSet("CPACK_NSIS_COMPRESSOR", "lzma");
487 return this->Superclass::InitializeInternal();
490 //----------------------------------------------------------------------
491 void cmCPackNSISGenerator::CreateMenuLinks( cmOStringStream& str,
492 cmOStringStream& deleteStr)
494 const char* cpackMenuLinks
495 = this->GetOption("CPACK_NSIS_MENU_LINKS");
496 if(!cpackMenuLinks)
498 return;
500 cmCPackLogger(cmCPackLog::LOG_DEBUG, "The cpackMenuLinks: "
501 << cpackMenuLinks << "." << std::endl);
502 std::vector<std::string> cpackMenuLinksVector;
503 cmSystemTools::ExpandListArgument(cpackMenuLinks,
504 cpackMenuLinksVector);
505 if ( cpackMenuLinksVector.size() % 2 != 0 )
507 cmCPackLogger(
508 cmCPackLog::LOG_ERROR,
509 "CPACK_PACKAGE_EXECUTABLES should contain pairs of <executable> and "
510 "<icon name>." << std::endl);
511 return;
513 std::vector<std::string>::iterator it;
514 for ( it = cpackMenuLinksVector.begin();
515 it != cpackMenuLinksVector.end();
516 ++it )
518 std::string sourceName = *it;
519 bool url = false;
520 if(sourceName.find("http:") == 0)
522 url = true;
524 /* convert / to \\ */
525 if(!url)
527 cmSystemTools::ReplaceString(sourceName, "/", "\\");
529 ++ it;
530 std::string linkName = *it;
531 if(!url)
533 str << " CreateShortCut \"$SMPROGRAMS\\$STARTMENU_FOLDER\\"
534 << linkName << ".lnk\" \"$INSTDIR\\" << sourceName << "\""
535 << std::endl;
536 deleteStr << " Delete \"$SMPROGRAMS\\$MUI_TEMP\\" << linkName
537 << ".lnk\"" << std::endl;
539 else
541 str << " WriteINIStr \"$SMPROGRAMS\\$STARTMENU_FOLDER\\"
542 << linkName << ".url\" \"InternetShortcut\" \"URL\" \""
543 << sourceName << "\""
544 << std::endl;
545 deleteStr << " Delete \"$SMPROGRAMS\\$MUI_TEMP\\" << linkName
546 << ".url\"" << std::endl;
548 // see if CPACK_CREATE_DESKTOP_LINK_ExeName is on
549 // if so add a desktop link
550 std::string desktop = "CPACK_CREATE_DESKTOP_LINK_";
551 desktop += linkName;
552 if(this->IsSet(desktop.c_str()))
554 str << " StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n";
555 str << " CreateShortCut \"$DESKTOP\\"
556 << linkName << ".lnk\" \"$INSTDIR\\" << sourceName << "\""
557 << std::endl;
558 deleteStr << " StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n";
559 deleteStr << " Delete \"$DESKTOP\\" << linkName
560 << ".lnk\"" << std::endl;
565 //----------------------------------------------------------------------
566 bool cmCPackNSISGenerator::GetListOfSubdirectories(const char* topdir,
567 std::vector<std::string>& dirs)
569 cmsys::Directory dir;
570 dir.Load(topdir);
571 size_t fileNum;
572 for (fileNum = 0; fileNum < dir.GetNumberOfFiles(); ++fileNum)
574 if (strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)),".") &&
575 strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)),".."))
577 cmsys_stl::string fullPath = topdir;
578 fullPath += "/";
579 fullPath += dir.GetFile(static_cast<unsigned long>(fileNum));
580 if(cmsys::SystemTools::FileIsDirectory(fullPath.c_str()) &&
581 !cmsys::SystemTools::FileIsSymlink(fullPath.c_str()))
583 if (!this->GetListOfSubdirectories(fullPath.c_str(), dirs))
585 return false;
590 dirs.push_back(topdir);
591 return true;
594 //----------------------------------------------------------------------
595 bool cmCPackNSISGenerator::SupportsComponentInstallation() const
597 return true;
600 //----------------------------------------------------------------------
601 std::string
602 cmCPackNSISGenerator::
603 CreateComponentDescription(cmCPackComponent *component,
604 cmOStringStream& macrosOut)
606 // Basic description of the component
607 std::string componentCode = "Section ";
608 if (component->IsDisabledByDefault)
610 componentCode += "/o ";
612 componentCode += "\"";
613 if (component->IsHidden)
615 componentCode += "-";
617 componentCode += component->DisplayName + "\" " + component->Name + "\n";
618 if (component->IsRequired)
620 componentCode += " SectionIn RO\n";
622 else if (!component->InstallationTypes.empty())
624 cmOStringStream out;
625 std::vector<cmCPackInstallationType *>::iterator installTypeIter;
626 for (installTypeIter = component->InstallationTypes.begin();
627 installTypeIter != component->InstallationTypes.end();
628 ++installTypeIter)
630 out << " " << (*installTypeIter)->Index;
632 componentCode += " SectionIn" + out.str() + "\n";
634 componentCode += " SetOutPath \"$INSTDIR\"\n";
636 componentCode += " IntCmp $" + component->Name
637 + "_was_installed ${SF_SELECTED} noinstall_" + component->Name + "\n";
639 // Create the actual installation commands
640 if (component->IsDownloaded)
642 if (component->ArchiveFile.empty())
644 // Compute the name of the archive.
645 std::string packagesDir = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
646 packagesDir += ".dummy";
647 cmOStringStream out;
648 out << cmSystemTools::GetFilenameWithoutLastExtension(packagesDir)
649 << "-" << component->Name << ".zip";
650 component->ArchiveFile = out.str();
653 // Create the directory for the upload area
654 const char* userUploadDirectory =
655 this->GetOption("CPACK_UPLOAD_DIRECTORY");
656 std::string uploadDirectory;
657 if (userUploadDirectory && *userUploadDirectory)
659 uploadDirectory = userUploadDirectory;
661 else
663 uploadDirectory= this->GetOption("CPACK_PACKAGE_DIRECTORY");
664 uploadDirectory += "/CPackUploads";
666 if(!cmSystemTools::FileExists(uploadDirectory.c_str()))
668 if (!cmSystemTools::MakeDirectory(uploadDirectory.c_str()))
670 cmCPackLogger(cmCPackLog::LOG_ERROR,
671 "Unable to create NSIS upload directory " << uploadDirectory
672 << std::endl);
673 return "";
677 // Remove the old archive, if one exists
678 std::string archiveFile = uploadDirectory + '/' + component->ArchiveFile;
679 cmCPackLogger(cmCPackLog::LOG_OUTPUT,
680 "- Building downloaded component archive: "
681 << archiveFile << std::endl);
682 if (cmSystemTools::FileExists(archiveFile.c_str(), true))
684 if (!cmSystemTools::RemoveFile(archiveFile.c_str()))
686 cmCPackLogger(cmCPackLog::LOG_ERROR,
687 "Unable to remove archive file " << archiveFile
688 << std::endl);
689 return "";
693 // Find a ZIP program
694 if (!this->IsSet("ZIP_EXECUTABLE"))
696 this->ReadListFile("CPackZIP.cmake");
698 if (!this->IsSet("ZIP_EXECUTABLE"))
700 cmCPackLogger(cmCPackLog::LOG_ERROR,
701 "Unable to find ZIP program"
702 << std::endl);
703 return "";
707 // The directory where this component's files reside
708 std::string dirName = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
709 dirName += '/';
710 dirName += component->Name;
711 dirName += '/';
713 // Build the list of files to go into this archive, and determine the
714 // size of the installed component.
715 std::string zipListFileName = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
716 zipListFileName += "/winZip.filelist";
717 bool needQuotesInFile
718 = cmSystemTools::IsOn(this->GetOption("CPACK_ZIP_NEED_QUOTES"));
719 unsigned long totalSize = 0;
720 { // the scope is needed for cmGeneratedFileStream
721 cmGeneratedFileStream out(zipListFileName.c_str());
722 std::vector<std::string>::iterator fileIt;
723 for (fileIt = component->Files.begin();
724 fileIt != component->Files.end();
725 ++fileIt)
727 if ( needQuotesInFile )
729 out << "\"";
731 out << *fileIt;
732 if ( needQuotesInFile )
734 out << "\"";
736 out << std::endl;
738 totalSize += cmSystemTools::FileLength((dirName + *fileIt).c_str());
742 // Build the archive in the upload area
743 std::string cmd = this->GetOption("CPACK_ZIP_COMMAND");
744 cmsys::SystemTools::ReplaceString(cmd, "<ARCHIVE>", archiveFile.c_str());
745 cmsys::SystemTools::ReplaceString(cmd, "<FILELIST>",
746 zipListFileName.c_str());
747 std::string output;
748 int retVal = -1;
749 int res = cmSystemTools::RunSingleCommand(cmd.c_str(), &output, &retVal,
750 dirName.c_str(), false, 0);
751 if ( !res || retVal )
753 std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
754 tmpFile += "/CompressZip.log";
755 cmGeneratedFileStream ofs(tmpFile.c_str());
756 ofs << "# Run command: " << cmd.c_str() << std::endl
757 << "# Output:" << std::endl
758 << output.c_str() << std::endl;
759 cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running zip command: "
760 << cmd.c_str() << std::endl
761 << "Please check " << tmpFile.c_str() << " for errors" << std::endl);
762 return "";
765 // Create the NSIS code to download this file on-the-fly.
766 unsigned totalSizeInKbytes = (totalSize + 512) / 1024;
767 if (totalSizeInKbytes == 0)
769 totalSizeInKbytes = 1;
771 cmOStringStream out;
772 out << " AddSize " << totalSizeInKbytes << "\n"
773 << " Push \"" << component->ArchiveFile << "\"\n"
774 << " Call DownloadFile\n"
775 << " ZipDLL::extractall \"$INSTDIR\\"
776 << component->ArchiveFile << "\" \"$INSTDIR\"\n"
777 << " Pop $2 ; error message\n"
778 " StrCmp $2 \"success\" +2 0\n"
779 " MessageBox MB_OK \"Failed to unzip $2\"\n"
780 " Delete $INSTDIR\\$0\n";
781 componentCode += out.str();
783 else
785 componentCode += " File /r \"${INST_DIR}\\" +
786 component->Name + "\\*.*\"\n";
788 componentCode += " noinstall_" + component->Name + ":\n";
789 componentCode += "SectionEnd\n";
791 // Macro used to remove the component
792 macrosOut << "!macro Remove_${" << component->Name << "}\n";
793 macrosOut << " IntCmp $" << component->Name << "_was_installed 0 noremove_"
794 << component->Name << "\n";
795 std::vector<std::string>::iterator pathIt;
796 std::string path;
797 for (pathIt = component->Files.begin();
798 pathIt != component->Files.end();
799 ++pathIt)
801 path = *pathIt;
802 cmSystemTools::ReplaceString(path, "/", "\\");
803 macrosOut << " Delete \"$INSTDIR\\"
804 << path.c_str()
805 << "\"\n";
807 for (pathIt = component->Directories.begin();
808 pathIt != component->Directories.end();
809 ++pathIt)
811 path = *pathIt;
812 cmSystemTools::ReplaceString(path, "/", "\\");
813 macrosOut << " RMDir \"$INSTDIR\\"
814 << path.c_str()
815 << "\"\n";
817 macrosOut << " noremove_" << component->Name << ":\n";
818 macrosOut << "!macroend\n";
820 // Macro used to select each of the components that this component
821 // depends on.
822 std::set<cmCPackComponent *> visited;
823 macrosOut << "!macro Select_" << component->Name << "_depends\n";
824 macrosOut << CreateSelectionDependenciesDescription(component, visited);
825 macrosOut << "!macroend\n";
827 // Macro used to deselect each of the components that depend on this
828 // component.
829 visited.clear();
830 macrosOut << "!macro Deselect_required_by_" << component->Name << "\n";
831 macrosOut << CreateDeselectionDependenciesDescription(component, visited);
832 macrosOut << "!macroend\n";
833 return componentCode;
836 //----------------------------------------------------------------------
837 std::string cmCPackNSISGenerator::CreateSelectionDependenciesDescription
838 (cmCPackComponent *component,
839 std::set<cmCPackComponent *>& visited)
841 // Don't visit a component twice
842 if (visited.count(component))
844 return std::string();
846 visited.insert(component);
848 cmOStringStream out;
849 std::vector<cmCPackComponent *>::iterator dependIt;
850 for (dependIt = component->Dependencies.begin();
851 dependIt != component->Dependencies.end();
852 ++dependIt)
854 // Write NSIS code to select this dependency
855 out << " SectionGetFlags ${" << (*dependIt)->Name << "} $0\n";
856 out << " IntOp $0 $0 | ${SF_SELECTED}\n";
857 out << " SectionSetFlags ${" << (*dependIt)->Name << "} $0\n";
858 out << " IntOp $" << (*dependIt)->Name
859 << "_selected 0 + ${SF_SELECTED}\n";
860 // Recurse
861 out << CreateSelectionDependenciesDescription(*dependIt, visited).c_str();
864 return out.str();
868 //----------------------------------------------------------------------
869 std::string cmCPackNSISGenerator::CreateDeselectionDependenciesDescription
870 (cmCPackComponent *component,
871 std::set<cmCPackComponent *>& visited)
873 // Don't visit a component twice
874 if (visited.count(component))
876 return std::string();
878 visited.insert(component);
880 cmOStringStream out;
881 std::vector<cmCPackComponent *>::iterator dependIt;
882 for (dependIt = component->ReverseDependencies.begin();
883 dependIt != component->ReverseDependencies.end();
884 ++dependIt)
886 // Write NSIS code to deselect this dependency
887 out << " SectionGetFlags ${" << (*dependIt)->Name << "} $0\n";
888 out << " IntOp $1 ${SF_SELECTED} ~\n";
889 out << " IntOp $0 $0 & $1\n";
890 out << " SectionSetFlags ${" << (*dependIt)->Name << "} $0\n";
891 out << " IntOp $" << (*dependIt)->Name << "_selected 0 + 0\n";
893 // Recurse
894 out <<
895 CreateDeselectionDependenciesDescription(*dependIt, visited).c_str();
898 return out.str();
901 //----------------------------------------------------------------------
902 std::string
903 cmCPackNSISGenerator::
904 CreateComponentGroupDescription(cmCPackComponentGroup *group,
905 cmOStringStream& macrosOut)
907 if (group->Components.empty() && group->Subgroups.empty())
909 // Silently skip empty groups. NSIS doesn't support them.
910 return std::string();
913 std::string code = "SectionGroup ";
914 if (group->IsExpandedByDefault)
916 code += "/e ";
918 if (group->IsBold)
920 code += "\"!" + group->DisplayName + "\" " + group->Name + "\n";
922 else
924 code += "\"" + group->DisplayName + "\" " + group->Name + "\n";
927 std::vector<cmCPackComponentGroup*>::iterator groupIt;
928 for (groupIt = group->Subgroups.begin(); groupIt != group->Subgroups.end();
929 ++groupIt)
931 code += this->CreateComponentGroupDescription(*groupIt, macrosOut);
934 std::vector<cmCPackComponent*>::iterator comp;
935 for (comp = group->Components.begin();
936 comp != group->Components.end();
937 ++comp)
939 if ((*comp)->Files.empty())
941 continue;
944 code += this->CreateComponentDescription(*comp, macrosOut);
946 code += "SectionGroupEnd\n";
947 return code;
950 std::string cmCPackNSISGenerator::TranslateNewlines(std::string str)
952 cmSystemTools::ReplaceString(str, "\n", "$\\r$\\n");
953 return str;