Imported Upstream version 2.8.11.2
[platform/upstream/cmake.git] / Source / CPack / cmCPackNSISGenerator.cxx
1 /*============================================================================
2   CMake - Cross Platform Makefile Generator
3   Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
4
5   Distributed under the OSI-approved BSD License (the "License");
6   see accompanying file Copyright.txt for details.
7
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 ============================================================================*/
12
13 #include "cmCPackNSISGenerator.h"
14
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"
22
23 #include <cmsys/SystemTools.hxx>
24 #include <cmsys/Glob.hxx>
25 #include <cmsys/Directory.hxx>
26 #include <cmsys/RegularExpression.hxx>
27
28 /* NSIS uses different command line syntax on Windows and others */
29 #ifdef _WIN32
30 # define NSIS_OPT "/"
31 #else
32 # define NSIS_OPT "-"
33 #endif
34
35 //----------------------------------------------------------------------
36 cmCPackNSISGenerator::cmCPackNSISGenerator(bool nsis64)
37 {
38   Nsis64 = nsis64;
39 }
40
41 //----------------------------------------------------------------------
42 cmCPackNSISGenerator::~cmCPackNSISGenerator()
43 {
44 }
45
46 //----------------------------------------------------------------------
47 int cmCPackNSISGenerator::PackageFiles()
48 {
49   // TODO: Fix nsis to force out file name
50
51   std::string nsisInFileName = this->FindTemplate("NSIS.template.in");
52   if ( nsisInFileName.size() == 0 )
53     {
54     cmCPackLogger(cmCPackLog::LOG_ERROR,
55       "CPack error: Could not find NSIS installer template file."
56       << std::endl);
57     return false;
58     }
59   std::string nsisInInstallOptions
60     = this->FindTemplate("NSIS.InstallOptions.ini.in");
61   if ( nsisInInstallOptions.size() == 0 )
62     {
63     cmCPackLogger(cmCPackLog::LOG_ERROR,
64       "CPack error: Could not find NSIS installer options file."
65       << std::endl);
66     return false;
67     }
68
69   std::string nsisFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
70   std::string tmpFile = nsisFileName;
71   tmpFile += "/NSISOutput.log";
72   std::string nsisInstallOptions = nsisFileName + "/NSIS.InstallOptions.ini";
73   nsisFileName += "/project.nsi";
74   cmOStringStream str;
75   std::vector<std::string>::const_iterator it;
76   for ( it = files.begin(); it != files.end(); ++ it )
77     {
78     std::string fileN = cmSystemTools::RelativePath(toplevel.c_str(),
79                                                     it->c_str());
80     if (!this->Components.empty())
81       {
82       // Strip off the component part of the path.
83       fileN = fileN.substr(fileN.find('/')+1, std::string::npos);
84       }
85     cmSystemTools::ReplaceString(fileN, "/", "\\");
86     str << "  Delete \"$INSTDIR\\" << fileN.c_str() << "\"" << std::endl;
87     }
88   cmCPackLogger(cmCPackLog::LOG_DEBUG, "Uninstall Files: "
89     << str.str().c_str() << std::endl);
90   this->SetOptionIfNotSet("CPACK_NSIS_DELETE_FILES", str.str().c_str());
91   std::vector<std::string> dirs;
92   this->GetListOfSubdirectories(toplevel.c_str(), dirs);
93   std::vector<std::string>::const_iterator sit;
94   cmOStringStream dstr;
95   for ( sit = dirs.begin(); sit != dirs.end(); ++ sit )
96     {
97     std::string componentName;
98     std::string fileN = cmSystemTools::RelativePath(toplevel.c_str(),
99                                                     sit->c_str());
100     if ( fileN.empty() )
101       {
102       continue;
103       }
104     if (!Components.empty())
105       {
106       // If this is a component installation, strip off the component
107       // part of the path.
108       std::string::size_type slash = fileN.find('/');
109       if (slash != std::string::npos)
110         {
111         // If this is a component installation, determine which component it
112         // is.
113         componentName = fileN.substr(0, slash);
114
115         // Strip off the component part of the path.
116         fileN = fileN.substr(slash+1, std::string::npos);
117         }
118       }
119     cmSystemTools::ReplaceString(fileN, "/", "\\");
120     dstr << "  RMDir \"$INSTDIR\\" << fileN.c_str() << "\"" << std::endl;
121     if (!componentName.empty())
122       {
123       this->Components[componentName].Directories.push_back(fileN);
124       }
125     }
126   cmCPackLogger(cmCPackLog::LOG_DEBUG, "Uninstall Dirs: "
127     << dstr.str().c_str() << std::endl);
128   this->SetOptionIfNotSet("CPACK_NSIS_DELETE_DIRECTORIES",
129                           dstr.str().c_str());
130
131   cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: " << nsisInFileName
132     << " to " << nsisFileName << std::endl);
133   if(this->IsSet("CPACK_NSIS_MUI_ICON")
134      || this->IsSet("CPACK_NSIS_MUI_UNIICON"))
135     {
136     std::string installerIconCode;
137     if(this->IsSet("CPACK_NSIS_MUI_ICON"))
138       {
139       installerIconCode += "!define MUI_ICON \"";
140       installerIconCode += this->GetOption("CPACK_NSIS_MUI_ICON");
141       installerIconCode += "\"\n";
142       }
143     if(this->IsSet("CPACK_NSIS_MUI_UNIICON"))
144       {
145       installerIconCode += "!define MUI_UNICON \"";
146       installerIconCode += this->GetOption("CPACK_NSIS_MUI_UNIICON");
147       installerIconCode += "\"\n";
148       }
149     this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_ICON_CODE",
150                             installerIconCode.c_str());
151     }
152   if(this->IsSet("CPACK_PACKAGE_ICON"))
153     {
154     std::string installerIconCode = "!define MUI_HEADERIMAGE_BITMAP \"";
155     installerIconCode += this->GetOption("CPACK_PACKAGE_ICON");
156     installerIconCode += "\"\n";
157     this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_ICON_CODE",
158                             installerIconCode.c_str());
159     }
160
161   if(this->IsSet("CPACK_NSIS_MUI_FINISHPAGE_RUN"))
162     {
163     std::string installerRunCode = "!define MUI_FINISHPAGE_RUN \"$INSTDIR\\";
164     installerRunCode += this->GetOption("CPACK_NSIS_EXECUTABLES_DIRECTORY");
165     installerRunCode += "\\";
166     installerRunCode += this->GetOption("CPACK_NSIS_MUI_FINISHPAGE_RUN");
167     installerRunCode += "\"\n";
168     this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_FINISHPAGE_RUN_CODE",
169                             installerRunCode.c_str());
170     }
171
172   // Setup all of the component sections
173   if (this->Components.empty())
174     {
175     this->SetOptionIfNotSet("CPACK_NSIS_INSTALLATION_TYPES", "");
176     this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC", "");
177     this->SetOptionIfNotSet("CPACK_NSIS_PAGE_COMPONENTS", "");
178     this->SetOptionIfNotSet("CPACK_NSIS_FULL_INSTALL",
179                             "File /r \"${INST_DIR}\\*.*\"");
180     this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTIONS", "");
181     this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTION_LIST", "");
182     this->SetOptionIfNotSet("CPACK_NSIS_SECTION_SELECTED_VARS", "");
183     }
184   else
185     {
186     std::string componentCode;
187     std::string sectionList;
188     std::string selectedVarsList;
189     std::string componentDescriptions;
190     std::string groupDescriptions;
191     std::string installTypesCode;
192     std::string defines;
193     cmOStringStream macrosOut;
194     bool anyDownloadedComponents = false;
195
196     // Create installation types. The order is significant, so we first fill
197     // in a vector based on the indices, and print them in that order.
198     std::vector<cmCPackInstallationType *>
199       installTypes(this->InstallationTypes.size());
200     std::map<std::string, cmCPackInstallationType>::iterator installTypeIt;
201     for (installTypeIt = this->InstallationTypes.begin();
202          installTypeIt != this->InstallationTypes.end();
203          ++installTypeIt)
204       {
205       installTypes[installTypeIt->second.Index-1] = &installTypeIt->second;
206       }
207     std::vector<cmCPackInstallationType *>::iterator installTypeIt2;
208     for (installTypeIt2 = installTypes.begin();
209          installTypeIt2 != installTypes.end();
210          ++installTypeIt2)
211       {
212       installTypesCode += "InstType \"";
213       installTypesCode += (*installTypeIt2)->DisplayName;
214       installTypesCode += "\"\n";
215       }
216
217     // Create installation groups first
218     std::map<std::string, cmCPackComponentGroup>::iterator groupIt;
219     for (groupIt = this->ComponentGroups.begin();
220          groupIt != this->ComponentGroups.end();
221          ++groupIt)
222       {
223       if (groupIt->second.ParentGroup == 0)
224         {
225         componentCode +=
226           this->CreateComponentGroupDescription(&groupIt->second, macrosOut);
227         }
228
229       // Add the group description, if any.
230       if (!groupIt->second.Description.empty())
231         {
232         groupDescriptions += "  !insertmacro MUI_DESCRIPTION_TEXT ${"
233           + groupIt->first + "} \""
234           + this->TranslateNewlines(groupIt->second.Description) + "\"\n";
235         }
236       }
237
238     // Create the remaining components, which aren't associated with groups.
239     std::map<std::string, cmCPackComponent>::iterator compIt;
240     for (compIt = this->Components.begin();
241          compIt != this->Components.end();
242          ++compIt)
243       {
244       if (compIt->second.Files.empty())
245         {
246         // NSIS cannot cope with components that have no files.
247         continue;
248         }
249
250       anyDownloadedComponents =
251         anyDownloadedComponents || compIt->second.IsDownloaded;
252
253       if (!compIt->second.Group)
254         {
255         componentCode
256           += this->CreateComponentDescription(&compIt->second, macrosOut);
257         }
258
259       // Add this component to the various section lists.
260       sectionList += "  !insertmacro \"${MacroName}\" \"";
261       sectionList += compIt->first;
262       sectionList += "\"\n";
263       selectedVarsList += "Var " + compIt->first + "_selected\n";
264       selectedVarsList += "Var " + compIt->first + "_was_installed\n";
265
266       // Add the component description, if any.
267       if (!compIt->second.Description.empty())
268         {
269         componentDescriptions += "  !insertmacro MUI_DESCRIPTION_TEXT ${"
270           + compIt->first + "} \""
271           + this->TranslateNewlines(compIt->second.Description) + "\"\n";
272         }
273       }
274
275     componentCode += macrosOut.str();
276
277     if (componentDescriptions.empty() && groupDescriptions.empty())
278       {
279       // Turn off the "Description" box
280       this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC",
281                               "!define MUI_COMPONENTSPAGE_NODESC");
282       }
283     else
284       {
285       componentDescriptions =
286         "!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN\n"
287         + componentDescriptions
288         + groupDescriptions
289         + "!insertmacro MUI_FUNCTION_DESCRIPTION_END\n";
290       this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC",
291                               componentDescriptions.c_str());
292       }
293
294     if (anyDownloadedComponents)
295       {
296       defines += "!define CPACK_USES_DOWNLOAD\n";
297       if (cmSystemTools::IsOn(this->GetOption("CPACK_ADD_REMOVE")))
298         {
299         defines += "!define CPACK_NSIS_ADD_REMOVE\n";
300         }
301       }
302
303     this->SetOptionIfNotSet("CPACK_NSIS_INSTALLATION_TYPES",
304                             installTypesCode.c_str());
305     this->SetOptionIfNotSet("CPACK_NSIS_PAGE_COMPONENTS",
306                             "!insertmacro MUI_PAGE_COMPONENTS");
307     this->SetOptionIfNotSet("CPACK_NSIS_FULL_INSTALL", "");
308     this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTIONS",
309                             componentCode.c_str());
310     this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTION_LIST",
311                             sectionList.c_str());
312     this->SetOptionIfNotSet("CPACK_NSIS_SECTION_SELECTED_VARS",
313                             selectedVarsList.c_str());
314     this->SetOption("CPACK_NSIS_DEFINES", defines.c_str());
315     }
316
317   this->ConfigureFile(nsisInInstallOptions.c_str(),
318                       nsisInstallOptions.c_str());
319   this->ConfigureFile(nsisInFileName.c_str(), nsisFileName.c_str());
320   std::string nsisCmd = "\"";
321   nsisCmd += this->GetOption("CPACK_INSTALLER_PROGRAM");
322   nsisCmd += "\" \"" + nsisFileName + "\"";
323   cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << nsisCmd.c_str()
324     << std::endl);
325   std::string output;
326   int retVal = 1;
327   bool res = cmSystemTools::RunSingleCommand(nsisCmd.c_str(), &output,
328     &retVal, 0, this->GeneratorVerbose, 0);
329   if ( !res || retVal )
330     {
331     cmGeneratedFileStream ofs(tmpFile.c_str());
332     ofs << "# Run command: " << nsisCmd.c_str() << std::endl
333       << "# Output:" << std::endl
334       << output.c_str() << std::endl;
335     cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running NSIS command: "
336       << nsisCmd.c_str() << std::endl
337       << "Please check " << tmpFile.c_str() << " for errors" << std::endl);
338     return 0;
339     }
340   return 1;
341 }
342
343 //----------------------------------------------------------------------
344 int cmCPackNSISGenerator::InitializeInternal()
345 {
346   if ( cmSystemTools::IsOn(this->GetOption(
347         "CPACK_INCLUDE_TOPLEVEL_DIRECTORY")) )
348     {
349     cmCPackLogger(cmCPackLog::LOG_WARNING,
350       "NSIS Generator cannot work with CPACK_INCLUDE_TOPLEVEL_DIRECTORY set. "
351       "This option will be reset to 0 (for this generator only)."
352       << std::endl);
353     this->SetOption("CPACK_INCLUDE_TOPLEVEL_DIRECTORY", 0);
354     }
355
356   cmCPackLogger(cmCPackLog::LOG_DEBUG, "cmCPackNSISGenerator::Initialize()"
357     << std::endl);
358   std::vector<std::string> path;
359   std::string nsisPath;
360   bool gotRegValue = false;
361
362 #ifdef _WIN32
363   if (Nsis64)
364     {
365     if ( !gotRegValue && cmsys::SystemTools::ReadRegistryValue(
366       "HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS\\Unicode", nsisPath,
367       cmsys::SystemTools::KeyWOW64_64) )
368       {
369       gotRegValue = true;
370       }
371     if ( !gotRegValue && cmsys::SystemTools::ReadRegistryValue(
372       "HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS", nsisPath,
373       cmsys::SystemTools::KeyWOW64_64) )
374       {
375       gotRegValue = true;
376       }
377     }
378   if ( !gotRegValue && cmsys::SystemTools::ReadRegistryValue(
379       "HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS\\Unicode", nsisPath,
380       cmsys::SystemTools::KeyWOW64_32) )
381     {
382     gotRegValue = true;
383     }
384   if ( !gotRegValue && cmsys::SystemTools::ReadRegistryValue(
385       "HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS\\Unicode", nsisPath) )
386     {
387     gotRegValue = true;
388     }
389   if ( !gotRegValue && cmsys::SystemTools::ReadRegistryValue(
390       "HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS", nsisPath,
391       cmsys::SystemTools::KeyWOW64_32) )
392     {
393     gotRegValue = true;
394     }
395   if ( !gotRegValue && cmsys::SystemTools::ReadRegistryValue(
396       "HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS", nsisPath) )
397     {
398     gotRegValue = true;
399     }
400
401   if (gotRegValue)
402     {
403     path.push_back(nsisPath);
404     }
405 #endif
406
407   nsisPath = cmSystemTools::FindProgram("makensis", path, false);
408
409   if ( nsisPath.empty() )
410     {
411     cmCPackLogger(cmCPackLog::LOG_ERROR,
412       "Cannot find NSIS compiler makensis: likely it is not installed, "
413       "or not in your PATH"
414       << std::endl);
415
416     if (!gotRegValue)
417       {
418       cmCPackLogger(cmCPackLog::LOG_ERROR,
419          "Could not read NSIS registry value. This is usually caused by "
420          "NSIS not being installed. Please install NSIS from "
421          "http://nsis.sourceforge.net"
422          << std::endl);
423       }
424
425     return 0;
426     }
427
428   std::string nsisCmd = "\"" + nsisPath + "\" " NSIS_OPT "VERSION";
429   cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Test NSIS version: "
430     << nsisCmd.c_str() << std::endl);
431   std::string output;
432   int retVal = 1;
433   bool resS = cmSystemTools::RunSingleCommand(nsisCmd.c_str(),
434     &output, &retVal, 0, this->GeneratorVerbose, 0);
435   cmsys::RegularExpression versionRex("v([0-9]+.[0-9]+)");
436   cmsys::RegularExpression versionRexCVS("v(.*)\\.cvs");
437   if ( !resS || retVal ||
438       (!versionRex.find(output) && !versionRexCVS.find(output))
439      )
440     {
441     const char* topDir = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
442     std::string tmpFile = topDir ? topDir : ".";
443     tmpFile += "/NSISOutput.log";
444     cmGeneratedFileStream ofs(tmpFile.c_str());
445     ofs << "# Run command: " << nsisCmd.c_str() << std::endl
446       << "# Output:" << std::endl
447       << output.c_str() << std::endl;
448     cmCPackLogger(cmCPackLog::LOG_ERROR,
449       "Problem checking NSIS version with command: "
450       << nsisCmd.c_str() << std::endl
451       << "Please check " << tmpFile.c_str() << " for errors" << std::endl);
452     return 0;
453     }
454   if ( versionRex.find(output))
455     {
456     double nsisVersion = atof(versionRex.match(1).c_str());
457     double minNSISVersion = 2.09;
458     cmCPackLogger(cmCPackLog::LOG_DEBUG, "NSIS Version: "
459       << nsisVersion << std::endl);
460     if ( nsisVersion < minNSISVersion )
461       {
462       cmCPackLogger(cmCPackLog::LOG_ERROR,
463         "CPack requires NSIS Version 2.09 or greater.  "
464         "NSIS found on the system was: "
465         << nsisVersion << std::endl);
466       return 0;
467       }
468     }
469   if ( versionRexCVS.find(output))
470     {
471     // No version check for NSIS cvs build
472     cmCPackLogger(cmCPackLog::LOG_DEBUG, "NSIS Version: CVS "
473       << versionRexCVS.match(1).c_str() << std::endl);
474     }
475   this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM", nsisPath.c_str());
476   this->SetOptionIfNotSet("CPACK_NSIS_EXECUTABLES_DIRECTORY", "bin");
477   const char* cpackPackageExecutables
478     = this->GetOption("CPACK_PACKAGE_EXECUTABLES");
479   const char* cpackPackageDeskTopLinks
480     = this->GetOption("CPACK_CREATE_DESKTOP_LINKS");
481   const char* cpackNsisExecutablesDirectory
482     = this->GetOption("CPACK_NSIS_EXECUTABLES_DIRECTORY");
483   std::vector<std::string> cpackPackageDesktopLinksVector;
484   if(cpackPackageDeskTopLinks)
485     {
486     cmCPackLogger(cmCPackLog::LOG_DEBUG, "CPACK_CREATE_DESKTOP_LINKS: "
487                 << cpackPackageDeskTopLinks << std::endl);
488
489     cmSystemTools::
490       ExpandListArgument(cpackPackageDeskTopLinks,
491                          cpackPackageDesktopLinksVector);
492     for(std::vector<std::string>::iterator i =
493           cpackPackageDesktopLinksVector.begin(); i !=
494           cpackPackageDesktopLinksVector.end(); ++i)
495       {
496        cmCPackLogger(cmCPackLog::LOG_DEBUG, "CPACK_CREATE_DESKTOP_LINKS: "
497                 << *i << std::endl);
498       }
499     }
500   else
501     {
502     cmCPackLogger(cmCPackLog::LOG_DEBUG, "CPACK_CREATE_DESKTOP_LINKS: "
503                   << "not set" << std::endl);
504     }
505
506   cmOStringStream str;
507   cmOStringStream deleteStr;
508
509   if ( cpackPackageExecutables )
510     {
511     cmCPackLogger(cmCPackLog::LOG_DEBUG, "The cpackPackageExecutables: "
512       << cpackPackageExecutables << "." << std::endl);
513     std::vector<std::string> cpackPackageExecutablesVector;
514     cmSystemTools::ExpandListArgument(cpackPackageExecutables,
515       cpackPackageExecutablesVector);
516     if ( cpackPackageExecutablesVector.size() % 2 != 0 )
517       {
518       cmCPackLogger(cmCPackLog::LOG_ERROR,
519         "CPACK_PACKAGE_EXECUTABLES should contain pairs of <executable> and "
520         "<icon name>." << std::endl);
521       return 0;
522       }
523     std::vector<std::string>::iterator it;
524     for ( it = cpackPackageExecutablesVector.begin();
525           it != cpackPackageExecutablesVector.end();
526           ++it )
527       {
528       std::string execName = *it;
529       ++ it;
530       std::string linkName = *it;
531       str << "  CreateShortCut \"$SMPROGRAMS\\$STARTMENU_FOLDER\\"
532         << linkName << ".lnk\" \"$INSTDIR\\"
533         << cpackNsisExecutablesDirectory << "\\" << execName << ".exe\""
534         << std::endl;
535       deleteStr << "  Delete \"$SMPROGRAMS\\$MUI_TEMP\\" << linkName
536         << ".lnk\"" << std::endl;
537       // see if CPACK_CREATE_DESKTOP_LINK_ExeName is on
538       // if so add a desktop link
539       if(cpackPackageDesktopLinksVector.size() &&
540          std::find(cpackPackageDesktopLinksVector.begin(),
541                    cpackPackageDesktopLinksVector.end(),
542                    execName)
543          != cpackPackageDesktopLinksVector.end())
544         {
545         str << "  StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n";
546         str << "    CreateShortCut \"$DESKTOP\\"
547             << linkName << ".lnk\" \"$INSTDIR\\"
548             << cpackNsisExecutablesDirectory << "\\" << execName << ".exe\""
549             << std::endl;
550         deleteStr << "  StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n";
551         deleteStr << "    Delete \"$DESKTOP\\" << linkName
552                   << ".lnk\"" << std::endl;
553         }
554       }
555     }
556
557   this->CreateMenuLinks(str, deleteStr);
558   this->SetOptionIfNotSet("CPACK_NSIS_CREATE_ICONS", str.str().c_str());
559   this->SetOptionIfNotSet("CPACK_NSIS_DELETE_ICONS",
560                           deleteStr.str().c_str());
561
562   this->SetOptionIfNotSet("CPACK_NSIS_COMPRESSOR", "lzma");
563
564   return this->Superclass::InitializeInternal();
565 }
566
567 //----------------------------------------------------------------------
568 void cmCPackNSISGenerator::CreateMenuLinks( cmOStringStream& str,
569                                             cmOStringStream& deleteStr)
570 {
571   const char* cpackMenuLinks
572     = this->GetOption("CPACK_NSIS_MENU_LINKS");
573   if(!cpackMenuLinks)
574     {
575     return;
576     }
577   cmCPackLogger(cmCPackLog::LOG_DEBUG, "The cpackMenuLinks: "
578                 << cpackMenuLinks << "." << std::endl);
579   std::vector<std::string> cpackMenuLinksVector;
580   cmSystemTools::ExpandListArgument(cpackMenuLinks,
581                                     cpackMenuLinksVector);
582   if ( cpackMenuLinksVector.size() % 2 != 0 )
583     {
584     cmCPackLogger(
585       cmCPackLog::LOG_ERROR,
586       "CPACK_NSIS_MENU_LINKS should contain pairs of <shortcut target> and "
587       "<shortcut label>." << std::endl);
588     return;
589     }
590
591   cmsys::RegularExpression urlRegex;
592   urlRegex.compile("^(mailto:|(ftps?|https?|news)://).*$");
593
594   std::vector<std::string>::iterator it;
595   for ( it = cpackMenuLinksVector.begin();
596         it != cpackMenuLinksVector.end();
597         ++it )
598     {
599     std::string sourceName = *it;
600     const bool url = urlRegex.find(sourceName);
601
602     // Convert / to \ in filenames, but not in urls:
603     //
604     if(!url)
605       {
606       cmSystemTools::ReplaceString(sourceName, "/", "\\");
607       }
608
609     ++ it;
610     std::string linkName = *it;
611     if(!url)
612       {
613       str << "  CreateShortCut \"$SMPROGRAMS\\$STARTMENU_FOLDER\\"
614           << linkName << ".lnk\" \"$INSTDIR\\" << sourceName << "\""
615           << std::endl;
616       deleteStr << "  Delete \"$SMPROGRAMS\\$MUI_TEMP\\" << linkName
617                 << ".lnk\"" << std::endl;
618       }
619     else
620       {
621       str << "  WriteINIStr \"$SMPROGRAMS\\$STARTMENU_FOLDER\\"
622           << linkName << ".url\" \"InternetShortcut\" \"URL\" \""
623           << sourceName << "\""
624           << std::endl;
625       deleteStr << "  Delete \"$SMPROGRAMS\\$MUI_TEMP\\" << linkName
626                 << ".url\"" << std::endl;
627       }
628     // see if CPACK_CREATE_DESKTOP_LINK_ExeName is on
629     // if so add a desktop link
630     std::string desktop = "CPACK_CREATE_DESKTOP_LINK_";
631     desktop += linkName;
632     if(this->IsSet(desktop.c_str()))
633       {
634       str << "  StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n";
635       str << "    CreateShortCut \"$DESKTOP\\"
636           << linkName << ".lnk\" \"$INSTDIR\\" << sourceName << "\""
637           << std::endl;
638       deleteStr << "  StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n";
639       deleteStr << "    Delete \"$DESKTOP\\" << linkName
640                 << ".lnk\"" << std::endl;
641       }
642     }
643 }
644
645 //----------------------------------------------------------------------
646 bool cmCPackNSISGenerator::GetListOfSubdirectories(const char* topdir,
647   std::vector<std::string>& dirs)
648 {
649   cmsys::Directory dir;
650   dir.Load(topdir);
651   size_t fileNum;
652   for (fileNum = 0; fileNum <  dir.GetNumberOfFiles(); ++fileNum)
653     {
654     if (strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)),".") &&
655         strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)),".."))
656       {
657       cmsys_stl::string fullPath = topdir;
658       fullPath += "/";
659       fullPath += dir.GetFile(static_cast<unsigned long>(fileNum));
660       if(cmsys::SystemTools::FileIsDirectory(fullPath.c_str()) &&
661         !cmsys::SystemTools::FileIsSymlink(fullPath.c_str()))
662         {
663         if (!this->GetListOfSubdirectories(fullPath.c_str(), dirs))
664           {
665           return false;
666           }
667         }
668       }
669     }
670   dirs.push_back(topdir);
671   return true;
672 }
673
674 //----------------------------------------------------------------------
675 enum cmCPackGenerator::CPackSetDestdirSupport
676 cmCPackNSISGenerator::SupportsSetDestdir() const
677 {
678   return cmCPackGenerator::SETDESTDIR_SHOULD_NOT_BE_USED;
679 }
680
681 //----------------------------------------------------------------------
682 bool cmCPackNSISGenerator::SupportsAbsoluteDestination() const
683 {
684         return false;
685 }
686
687 //----------------------------------------------------------------------
688 bool cmCPackNSISGenerator::SupportsComponentInstallation() const
689 {
690         return true;
691 }
692
693 //----------------------------------------------------------------------
694 std::string
695 cmCPackNSISGenerator::
696 CreateComponentDescription(cmCPackComponent *component,
697                            cmOStringStream& macrosOut)
698 {
699   // Basic description of the component
700   std::string componentCode = "Section ";
701   if (component->IsDisabledByDefault)
702     {
703     componentCode += "/o ";
704     }
705   componentCode += "\"";
706   if (component->IsHidden)
707     {
708     componentCode += "-";
709     }
710   componentCode += component->DisplayName + "\" " + component->Name + "\n";
711   if (component->IsRequired)
712     {
713     componentCode += "  SectionIn RO\n";
714     }
715   else if (!component->InstallationTypes.empty())
716     {
717     cmOStringStream out;
718     std::vector<cmCPackInstallationType *>::iterator installTypeIter;
719     for (installTypeIter = component->InstallationTypes.begin();
720          installTypeIter != component->InstallationTypes.end();
721          ++installTypeIter)
722       {
723       out << " " << (*installTypeIter)->Index;
724       }
725     componentCode += "  SectionIn" + out.str() + "\n";
726     }
727   componentCode += "  SetOutPath \"$INSTDIR\"\n";
728
729   // Create the actual installation commands
730   if (component->IsDownloaded)
731     {
732     if (component->ArchiveFile.empty())
733       {
734       // Compute the name of the archive.
735       std::string packagesDir = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
736       packagesDir += ".dummy";
737       cmOStringStream out;
738       out << cmSystemTools::GetFilenameWithoutLastExtension(packagesDir)
739         << "-" << component->Name << ".zip";
740       component->ArchiveFile = out.str();
741       }
742
743     // Create the directory for the upload area
744     const char* userUploadDirectory =
745       this->GetOption("CPACK_UPLOAD_DIRECTORY");
746     std::string uploadDirectory;
747     if (userUploadDirectory && *userUploadDirectory)
748       {
749       uploadDirectory = userUploadDirectory;
750       }
751     else
752       {
753       uploadDirectory= this->GetOption("CPACK_PACKAGE_DIRECTORY");
754       uploadDirectory += "/CPackUploads";
755       }
756     if(!cmSystemTools::FileExists(uploadDirectory.c_str()))
757       {
758       if (!cmSystemTools::MakeDirectory(uploadDirectory.c_str()))
759         {
760         cmCPackLogger(cmCPackLog::LOG_ERROR,
761           "Unable to create NSIS upload directory " << uploadDirectory
762           << std::endl);
763         return "";
764         }
765       }
766
767     // Remove the old archive, if one exists
768     std::string archiveFile = uploadDirectory + '/' + component->ArchiveFile;
769     cmCPackLogger(cmCPackLog::LOG_OUTPUT,
770                   "-   Building downloaded component archive: "
771                   << archiveFile << std::endl);
772     if (cmSystemTools::FileExists(archiveFile.c_str(), true))
773       {
774         if (!cmSystemTools::RemoveFile(archiveFile.c_str()))
775         {
776         cmCPackLogger(cmCPackLog::LOG_ERROR,
777           "Unable to remove archive file " << archiveFile
778           << std::endl);
779         return "";
780         }
781       }
782
783     // Find a ZIP program
784     if (!this->IsSet("ZIP_EXECUTABLE"))
785       {
786       this->ReadListFile("CPackZIP.cmake");
787
788       if (!this->IsSet("ZIP_EXECUTABLE"))
789         {
790         cmCPackLogger(cmCPackLog::LOG_ERROR,
791           "Unable to find ZIP program"
792           << std::endl);
793         return "";
794         }
795       }
796
797     // The directory where this component's files reside
798     std::string dirName = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
799     dirName += '/';
800     dirName += component->Name;
801     dirName += '/';
802
803     // Build the list of files to go into this archive, and determine the
804     // size of the installed component.
805     std::string zipListFileName = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
806     zipListFileName += "/winZip.filelist";
807     bool needQuotesInFile
808       = cmSystemTools::IsOn(this->GetOption("CPACK_ZIP_NEED_QUOTES"));
809     unsigned long totalSize = 0;
810     { // the scope is needed for cmGeneratedFileStream
811       cmGeneratedFileStream out(zipListFileName.c_str());
812       std::vector<std::string>::iterator fileIt;
813       for (fileIt = component->Files.begin();
814            fileIt != component->Files.end();
815            ++fileIt)
816         {
817         if ( needQuotesInFile )
818           {
819           out << "\"";
820           }
821         out << *fileIt;
822         if ( needQuotesInFile )
823           {
824           out << "\"";
825           }
826         out << std::endl;
827
828         totalSize += cmSystemTools::FileLength((dirName + *fileIt).c_str());
829         }
830     }
831
832     // Build the archive in the upload area
833     std::string cmd = this->GetOption("CPACK_ZIP_COMMAND");
834     cmsys::SystemTools::ReplaceString(cmd, "<ARCHIVE>", archiveFile.c_str());
835     cmsys::SystemTools::ReplaceString(cmd, "<FILELIST>",
836                                       zipListFileName.c_str());
837     std::string output;
838     int retVal = -1;
839     int res = cmSystemTools::RunSingleCommand(cmd.c_str(), &output, &retVal,
840                                               dirName.c_str(),
841                                               cmSystemTools::OUTPUT_NONE, 0);
842     if ( !res || retVal )
843     {
844       std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
845       tmpFile += "/CompressZip.log";
846       cmGeneratedFileStream ofs(tmpFile.c_str());
847       ofs << "# Run command: " << cmd.c_str() << std::endl
848         << "# Output:" << std::endl
849         << output.c_str() << std::endl;
850       cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running zip command: "
851         << cmd.c_str() << std::endl
852         << "Please check " << tmpFile.c_str() << " for errors" << std::endl);
853       return "";
854     }
855
856     // Create the NSIS code to download this file on-the-fly.
857     unsigned long totalSizeInKbytes = (totalSize + 512) / 1024;
858     if (totalSizeInKbytes == 0)
859       {
860       totalSizeInKbytes = 1;
861       }
862     cmOStringStream out;
863     out << "  AddSize " << totalSizeInKbytes << "\n"
864         << "  Push \"" << component->ArchiveFile << "\"\n"
865         << "  Call DownloadFile\n"
866         << "  ZipDLL::extractall \"$INSTDIR\\"
867         << component->ArchiveFile << "\" \"$INSTDIR\"\n"
868         <<  "  Pop $2 ; error message\n"
869                      "  StrCmp $2 \"success\" +2 0\n"
870                      "  MessageBox MB_OK \"Failed to unzip $2\"\n"
871                      "  Delete $INSTDIR\\$0\n";
872     componentCode += out.str();
873     }
874   else
875     {
876     componentCode += "  File /r \"${INST_DIR}\\" +
877       component->Name + "\\*.*\"\n";
878     }
879   componentCode += "SectionEnd\n";
880
881   // Macro used to remove the component
882   macrosOut << "!macro Remove_${" << component->Name << "}\n";
883   macrosOut << "  IntCmp $" << component->Name << "_was_installed 0 noremove_"
884             << component->Name << "\n";
885   std::vector<std::string>::iterator pathIt;
886   std::string path;
887   for (pathIt = component->Files.begin();
888        pathIt != component->Files.end();
889        ++pathIt)
890     {
891     path = *pathIt;
892     cmSystemTools::ReplaceString(path, "/", "\\");
893     macrosOut << "  Delete \"$INSTDIR\\"
894               << path.c_str()
895               << "\"\n";
896     }
897   for (pathIt = component->Directories.begin();
898        pathIt != component->Directories.end();
899        ++pathIt)
900     {
901     path = *pathIt;
902     cmSystemTools::ReplaceString(path, "/", "\\");
903     macrosOut << "  RMDir \"$INSTDIR\\"
904               << path.c_str()
905               << "\"\n";
906     }
907   macrosOut << "  noremove_" << component->Name << ":\n";
908   macrosOut << "!macroend\n";
909
910   // Macro used to select each of the components that this component
911   // depends on.
912   std::set<cmCPackComponent *> visited;
913   macrosOut << "!macro Select_" << component->Name << "_depends\n";
914   macrosOut << CreateSelectionDependenciesDescription(component, visited);
915   macrosOut << "!macroend\n";
916
917   // Macro used to deselect each of the components that depend on this
918   // component.
919   visited.clear();
920   macrosOut << "!macro Deselect_required_by_" << component->Name << "\n";
921   macrosOut << CreateDeselectionDependenciesDescription(component, visited);
922   macrosOut << "!macroend\n";
923   return componentCode;
924 }
925
926 //----------------------------------------------------------------------
927 std::string cmCPackNSISGenerator::CreateSelectionDependenciesDescription
928               (cmCPackComponent *component,
929                std::set<cmCPackComponent *>& visited)
930 {
931   // Don't visit a component twice
932   if (visited.count(component))
933     {
934     return std::string();
935     }
936   visited.insert(component);
937
938   cmOStringStream out;
939   std::vector<cmCPackComponent *>::iterator dependIt;
940   for (dependIt = component->Dependencies.begin();
941        dependIt != component->Dependencies.end();
942        ++dependIt)
943     {
944     // Write NSIS code to select this dependency
945     out << "  SectionGetFlags ${" << (*dependIt)->Name << "} $0\n";
946     out << "  IntOp $0 $0 | ${SF_SELECTED}\n";
947     out << "  SectionSetFlags ${" << (*dependIt)->Name << "} $0\n";
948     out << "  IntOp $" << (*dependIt)->Name
949         << "_selected 0 + ${SF_SELECTED}\n";
950     // Recurse
951     out << CreateSelectionDependenciesDescription(*dependIt, visited).c_str();
952     }
953
954   return out.str();
955 }
956
957
958 //----------------------------------------------------------------------
959 std::string cmCPackNSISGenerator::CreateDeselectionDependenciesDescription
960               (cmCPackComponent *component,
961                    std::set<cmCPackComponent *>& visited)
962 {
963   // Don't visit a component twice
964   if (visited.count(component))
965     {
966     return std::string();
967     }
968   visited.insert(component);
969
970   cmOStringStream out;
971   std::vector<cmCPackComponent *>::iterator dependIt;
972   for (dependIt = component->ReverseDependencies.begin();
973        dependIt != component->ReverseDependencies.end();
974        ++dependIt)
975     {
976     // Write NSIS code to deselect this dependency
977     out << "  SectionGetFlags ${" << (*dependIt)->Name << "} $0\n";
978     out << "  IntOp $1 ${SF_SELECTED} ~\n";
979     out << "  IntOp $0 $0 & $1\n";
980     out << "  SectionSetFlags ${" << (*dependIt)->Name << "} $0\n";
981     out << "  IntOp $" << (*dependIt)->Name << "_selected 0 + 0\n";
982
983     // Recurse
984     out <<
985       CreateDeselectionDependenciesDescription(*dependIt, visited).c_str();
986     }
987
988   return out.str();
989 }
990
991 //----------------------------------------------------------------------
992 std::string
993 cmCPackNSISGenerator::
994 CreateComponentGroupDescription(cmCPackComponentGroup *group,
995                                 cmOStringStream& macrosOut)
996 {
997   if (group->Components.empty() && group->Subgroups.empty())
998     {
999     // Silently skip empty groups. NSIS doesn't support them.
1000     return std::string();
1001     }
1002
1003   std::string code = "SectionGroup ";
1004   if (group->IsExpandedByDefault)
1005     {
1006     code += "/e ";
1007     }
1008   if (group->IsBold)
1009     {
1010     code += "\"!" + group->DisplayName + "\" " + group->Name + "\n";
1011     }
1012   else
1013     {
1014     code += "\"" + group->DisplayName + "\" " + group->Name + "\n";
1015     }
1016
1017   std::vector<cmCPackComponentGroup*>::iterator groupIt;
1018   for (groupIt = group->Subgroups.begin(); groupIt != group->Subgroups.end();
1019        ++groupIt)
1020     {
1021     code += this->CreateComponentGroupDescription(*groupIt, macrosOut);
1022     }
1023
1024   std::vector<cmCPackComponent*>::iterator comp;
1025   for (comp = group->Components.begin();
1026        comp != group->Components.end();
1027        ++comp)
1028     {
1029     if ((*comp)->Files.empty())
1030       {
1031       continue;
1032       }
1033
1034     code += this->CreateComponentDescription(*comp, macrosOut);
1035     }
1036   code += "SectionGroupEnd\n";
1037   return code;
1038 }
1039
1040 std::string cmCPackNSISGenerator::TranslateNewlines(std::string str)
1041 {
1042   cmSystemTools::ReplaceString(str, "\n", "$\\r$\\n");
1043   return str;
1044 }