Imported Upstream version 2.8.9
[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()
37 {
38 }
39
40 //----------------------------------------------------------------------
41 cmCPackNSISGenerator::~cmCPackNSISGenerator()
42 {
43 }
44
45 //----------------------------------------------------------------------
46 int cmCPackNSISGenerator::PackageFiles()
47 {
48   // TODO: Fix nsis to force out file name
49
50   std::string nsisInFileName = this->FindTemplate("NSIS.template.in");
51   if ( nsisInFileName.size() == 0 )
52     {
53     cmCPackLogger(cmCPackLog::LOG_ERROR,
54       "CPack error: Could not find NSIS installer template file."
55       << std::endl);
56     return false;
57     }
58   std::string nsisInInstallOptions
59     = this->FindTemplate("NSIS.InstallOptions.ini.in");
60   if ( nsisInInstallOptions.size() == 0 )
61     {
62     cmCPackLogger(cmCPackLog::LOG_ERROR,
63       "CPack error: Could not find NSIS installer options file."
64       << std::endl);
65     return false;
66     }
67
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";
73   cmOStringStream str;
74   std::vector<std::string>::const_iterator it;
75   for ( it = files.begin(); it != files.end(); ++ it )
76     {
77     std::string fileN = cmSystemTools::RelativePath(toplevel.c_str(),
78                                                     it->c_str());
79     if (!this->Components.empty())
80       {
81       // Strip off the component part of the path.
82       fileN = fileN.substr(fileN.find('/')+1, std::string::npos);
83       }
84     cmSystemTools::ReplaceString(fileN, "/", "\\");
85     str << "  Delete \"$INSTDIR\\" << fileN.c_str() << "\"" << std::endl;
86     }
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;
93   cmOStringStream dstr;
94   for ( sit = dirs.begin(); sit != dirs.end(); ++ sit )
95     {
96     std::string componentName;
97     std::string fileN = cmSystemTools::RelativePath(toplevel.c_str(),
98                                                     sit->c_str());
99     if ( fileN.empty() )
100       {
101       continue;
102       }
103     if (!Components.empty())
104       {
105       // If this is a component installation, strip off the component 
106       // part of the path.
107       std::string::size_type slash = fileN.find('/');
108       if (slash != std::string::npos)
109         {
110         // If this is a component installation, determine which component it
111         // is.
112         componentName = fileN.substr(0, slash);
113
114         // Strip off the component part of the path.
115         fileN = fileN.substr(slash+1, std::string::npos);
116         }
117       }
118     cmSystemTools::ReplaceString(fileN, "/", "\\");
119     dstr << "  RMDir \"$INSTDIR\\" << fileN.c_str() << "\"" << std::endl;
120     if (!componentName.empty())
121       {
122       this->Components[componentName].Directories.push_back(fileN);
123       }
124     }
125   cmCPackLogger(cmCPackLog::LOG_DEBUG, "Uninstall Dirs: "
126     << dstr.str().c_str() << std::endl);
127   this->SetOptionIfNotSet("CPACK_NSIS_DELETE_DIRECTORIES", 
128                           dstr.str().c_str());
129
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"))
134     {
135     std::string installerIconCode;
136     if(this->IsSet("CPACK_NSIS_MUI_ICON"))
137       {
138       installerIconCode += "!define MUI_ICON \"";
139       installerIconCode += this->GetOption("CPACK_NSIS_MUI_ICON");
140       installerIconCode += "\"\n";
141       }
142     if(this->IsSet("CPACK_NSIS_MUI_UNIICON"))
143       {
144       installerIconCode += "!define MUI_UNICON \"";
145       installerIconCode += this->GetOption("CPACK_NSIS_MUI_UNIICON");
146       installerIconCode += "\"\n";
147       }
148     this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_ICON_CODE",
149                             installerIconCode.c_str());
150     }
151   if(this->IsSet("CPACK_PACKAGE_ICON"))
152     {
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());
158     }
159
160   if(this->IsSet("CPACK_NSIS_MUI_FINISHPAGE_RUN"))
161     {
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());
169     }
170
171   // Setup all of the component sections
172   if (this->Components.empty())
173     {
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", "");
182     }
183   else
184     {
185     std::string componentCode;
186     std::string sectionList;
187     std::string selectedVarsList;
188     std::string componentDescriptions;
189     std::string groupDescriptions;
190     std::string installTypesCode;
191     std::string defines;
192     cmOStringStream macrosOut;
193     bool anyDownloadedComponents = false;
194
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();
202          ++installTypeIt)
203       {
204       installTypes[installTypeIt->second.Index-1] = &installTypeIt->second;
205       }
206     std::vector<cmCPackInstallationType *>::iterator installTypeIt2;
207     for (installTypeIt2 = installTypes.begin();
208          installTypeIt2 != installTypes.end();
209          ++installTypeIt2)
210       {
211       installTypesCode += "InstType \"";
212       installTypesCode += (*installTypeIt2)->DisplayName;
213       installTypesCode += "\"\n";
214       }
215
216     // Create installation groups first
217     std::map<std::string, cmCPackComponentGroup>::iterator groupIt;
218     for (groupIt = this->ComponentGroups.begin();
219          groupIt != this->ComponentGroups.end();
220          ++groupIt)
221       {
222       if (groupIt->second.ParentGroup == 0)
223         {
224         componentCode += 
225           this->CreateComponentGroupDescription(&groupIt->second, macrosOut);
226         }
227
228       // Add the group description, if any.
229       if (!groupIt->second.Description.empty())
230         {
231         groupDescriptions += "  !insertmacro MUI_DESCRIPTION_TEXT ${" 
232           + groupIt->first + "} \"" 
233           + this->TranslateNewlines(groupIt->second.Description) + "\"\n";
234         }
235       }
236
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();
241          ++compIt)
242       {
243       if (compIt->second.Files.empty())
244         {
245         // NSIS cannot cope with components that have no files.
246         continue;
247         }
248
249       anyDownloadedComponents =
250         anyDownloadedComponents || compIt->second.IsDownloaded;
251
252       if (!compIt->second.Group)
253         {
254         componentCode 
255           += this->CreateComponentDescription(&compIt->second, macrosOut);
256         }
257
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";
264
265       // Add the component description, if any.
266       if (!compIt->second.Description.empty())
267         {
268         componentDescriptions += "  !insertmacro MUI_DESCRIPTION_TEXT ${" 
269           + compIt->first + "} \"" 
270           + this->TranslateNewlines(compIt->second.Description) + "\"\n";
271         }
272       }
273
274     componentCode += macrosOut.str();
275
276     if (componentDescriptions.empty() && groupDescriptions.empty())
277       {
278       // Turn off the "Description" box
279       this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC", 
280                               "!define MUI_COMPONENTSPAGE_NODESC");
281       }
282     else
283       {
284       componentDescriptions = 
285         "!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN\n"
286         + componentDescriptions
287         + groupDescriptions
288         + "!insertmacro MUI_FUNCTION_DESCRIPTION_END\n";
289       this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC", 
290                               componentDescriptions.c_str());
291       }
292
293     if (anyDownloadedComponents)
294       {
295       defines += "!define CPACK_USES_DOWNLOAD\n";
296       if (cmSystemTools::IsOn(this->GetOption("CPACK_ADD_REMOVE")))
297         {
298         defines += "!define CPACK_NSIS_ADD_REMOVE\n";
299         }
300       }
301
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());
314     }
315
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()
323     << std::endl);
324   std::string output;
325   int retVal = 1;
326   bool res = cmSystemTools::RunSingleCommand(nsisCmd.c_str(), &output,
327     &retVal, 0, this->GeneratorVerbose, 0);
328   if ( !res || retVal )
329     {
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);
337     return 0;
338     }
339   return 1;
340 }
341
342 //----------------------------------------------------------------------
343 int cmCPackNSISGenerator::InitializeInternal()
344 {
345   if ( cmSystemTools::IsOn(this->GetOption(
346         "CPACK_INCLUDE_TOPLEVEL_DIRECTORY")) )
347     {
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)."
351       << std::endl);
352     this->SetOption("CPACK_INCLUDE_TOPLEVEL_DIRECTORY", 0);
353     }
354
355   cmCPackLogger(cmCPackLog::LOG_DEBUG, "cmCPackNSISGenerator::Initialize()"
356     << std::endl);
357   std::vector<std::string> path;
358   std::string nsisPath;
359   bool gotRegValue = true;
360
361 #ifdef _WIN32
362   if ( !cmsys::SystemTools::ReadRegistryValue(
363       "HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS", nsisPath,
364       cmsys::SystemTools::KeyWOW64_32) )
365     {
366     if ( !cmsys::SystemTools::ReadRegistryValue(
367         "HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS", nsisPath) )
368       {
369       gotRegValue = false;
370       }
371     }
372
373   if (gotRegValue)
374     {
375     path.push_back(nsisPath);
376     }
377 #endif
378
379   nsisPath = cmSystemTools::FindProgram("makensis", path, false);
380
381   if ( nsisPath.empty() )
382     {
383     cmCPackLogger(cmCPackLog::LOG_ERROR,
384       "Cannot find NSIS compiler makensis: likely it is not installed, "
385       "or not in your PATH"
386       << std::endl);
387
388     if (!gotRegValue)
389       {
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"
394          << std::endl);
395       }
396
397     return 0;
398     }
399
400   std::string nsisCmd = "\"" + nsisPath + "\" " NSIS_OPT "VERSION";
401   cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Test NSIS version: "
402     << nsisCmd.c_str() << std::endl);
403   std::string output;
404   int retVal = 1;
405   bool resS = cmSystemTools::RunSingleCommand(nsisCmd.c_str(),
406     &output, &retVal, 0, this->GeneratorVerbose, 0);
407
408   cmsys::RegularExpression versionRex("v([0-9]+.[0-9]+)");
409   if ( !resS || retVal || !versionRex.find(output))
410     {
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);
421     return 0;
422     }
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 )
428     {
429     cmCPackLogger(cmCPackLog::LOG_ERROR,
430       "CPack requires NSIS Version 2.09 or greater. NSIS found on the system "
431       "was: "
432       << nsisVersion << std::endl);
433     return 0;
434     }
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)
445     {
446     cmCPackLogger(cmCPackLog::LOG_DEBUG, "CPACK_CREATE_DESKTOP_LINKS: "
447                 << cpackPackageDeskTopLinks << std::endl);
448     
449     cmSystemTools::
450       ExpandListArgument(cpackPackageDeskTopLinks,
451                          cpackPackageDesktopLinksVector);
452     for(std::vector<std::string>::iterator i = 
453           cpackPackageDesktopLinksVector.begin(); i !=
454           cpackPackageDesktopLinksVector.end(); ++i)
455       {
456        cmCPackLogger(cmCPackLog::LOG_DEBUG, "CPACK_CREATE_DESKTOP_LINKS: "
457                 << *i << std::endl);
458       }
459     }
460   else
461     {
462     cmCPackLogger(cmCPackLog::LOG_DEBUG, "CPACK_CREATE_DESKTOP_LINKS: "
463                   << "not set" << std::endl);
464     }
465
466   cmOStringStream str;
467   cmOStringStream deleteStr;
468
469   if ( cpackPackageExecutables )
470     {
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 )
477       {
478       cmCPackLogger(cmCPackLog::LOG_ERROR,
479         "CPACK_PACKAGE_EXECUTABLES should contain pairs of <executable> and "
480         "<icon name>." << std::endl);
481       return 0;
482       }
483     std::vector<std::string>::iterator it;
484     for ( it = cpackPackageExecutablesVector.begin();
485           it != cpackPackageExecutablesVector.end();
486           ++it )
487       {
488       std::string execName = *it;
489       ++ it;
490       std::string linkName = *it;
491       str << "  CreateShortCut \"$SMPROGRAMS\\$STARTMENU_FOLDER\\"
492         << linkName << ".lnk\" \"$INSTDIR\\"
493         << cpackNsisExecutablesDirectory << "\\" << execName << ".exe\""
494         << std::endl;
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(),
502                    execName) 
503          != cpackPackageDesktopLinksVector.end())
504         {
505         str << "  StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n";
506         str << "    CreateShortCut \"$DESKTOP\\"
507             << linkName << ".lnk\" \"$INSTDIR\\"
508             << cpackNsisExecutablesDirectory << "\\" << execName << ".exe\""
509             << std::endl;
510         deleteStr << "  StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n";
511         deleteStr << "    Delete \"$DESKTOP\\" << linkName
512                   << ".lnk\"" << std::endl;
513         }
514       }
515     }
516
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());
521
522   this->SetOptionIfNotSet("CPACK_NSIS_COMPRESSOR", "lzma");
523
524   return this->Superclass::InitializeInternal();
525 }
526
527 //----------------------------------------------------------------------
528 void cmCPackNSISGenerator::CreateMenuLinks( cmOStringStream& str,
529                                             cmOStringStream& deleteStr)
530 {
531   const char* cpackMenuLinks
532     = this->GetOption("CPACK_NSIS_MENU_LINKS");
533   if(!cpackMenuLinks)
534     {
535     return;
536     }
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 )
543     {
544     cmCPackLogger(
545       cmCPackLog::LOG_ERROR,
546       "CPACK_NSIS_MENU_LINKS should contain pairs of <shortcut target> and "
547       "<shortcut label>." << std::endl);
548     return;
549     }
550
551   cmsys::RegularExpression urlRegex;
552   urlRegex.compile("^(mailto:|(ftps?|https?|news)://).*$");
553
554   std::vector<std::string>::iterator it;
555   for ( it = cpackMenuLinksVector.begin();
556         it != cpackMenuLinksVector.end();
557         ++it )
558     {
559     std::string sourceName = *it;
560     const bool url = urlRegex.find(sourceName);
561
562     // Convert / to \ in filenames, but not in urls:
563     //
564     if(!url)
565       {
566       cmSystemTools::ReplaceString(sourceName, "/", "\\");
567       }
568
569     ++ it;
570     std::string linkName = *it;
571     if(!url)
572       {
573       str << "  CreateShortCut \"$SMPROGRAMS\\$STARTMENU_FOLDER\\"
574           << linkName << ".lnk\" \"$INSTDIR\\" << sourceName << "\""
575           << std::endl;
576       deleteStr << "  Delete \"$SMPROGRAMS\\$MUI_TEMP\\" << linkName
577                 << ".lnk\"" << std::endl;
578       }
579     else
580       {
581       str << "  WriteINIStr \"$SMPROGRAMS\\$STARTMENU_FOLDER\\"
582           << linkName << ".url\" \"InternetShortcut\" \"URL\" \"" 
583           << sourceName << "\""
584           << std::endl;
585       deleteStr << "  Delete \"$SMPROGRAMS\\$MUI_TEMP\\" << linkName
586                 << ".url\"" << std::endl;
587       }
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_";
591     desktop += linkName;
592     if(this->IsSet(desktop.c_str()))
593       {
594       str << "  StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n";
595       str << "    CreateShortCut \"$DESKTOP\\"
596           << linkName << ".lnk\" \"$INSTDIR\\" << sourceName << "\""
597           << std::endl;
598       deleteStr << "  StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n";
599       deleteStr << "    Delete \"$DESKTOP\\" << linkName
600                 << ".lnk\"" << std::endl;
601       }
602     }
603 }
604
605 //----------------------------------------------------------------------
606 bool cmCPackNSISGenerator::GetListOfSubdirectories(const char* topdir,
607   std::vector<std::string>& dirs)
608 {
609   cmsys::Directory dir;
610   dir.Load(topdir);
611   size_t fileNum;
612   for (fileNum = 0; fileNum <  dir.GetNumberOfFiles(); ++fileNum)
613     {
614     if (strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)),".") &&
615         strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)),".."))
616       {
617       cmsys_stl::string fullPath = topdir;
618       fullPath += "/";
619       fullPath += dir.GetFile(static_cast<unsigned long>(fileNum));
620       if(cmsys::SystemTools::FileIsDirectory(fullPath.c_str()) &&
621         !cmsys::SystemTools::FileIsSymlink(fullPath.c_str()))
622         {
623         if (!this->GetListOfSubdirectories(fullPath.c_str(), dirs))
624           {
625           return false;
626           }
627         }
628       }
629     }
630   dirs.push_back(topdir);
631   return true;
632 }
633
634 //----------------------------------------------------------------------
635 enum cmCPackGenerator::CPackSetDestdirSupport
636 cmCPackNSISGenerator::SupportsSetDestdir() const
637 {
638   return cmCPackGenerator::SETDESTDIR_SHOULD_NOT_BE_USED;
639 }
640
641 //----------------------------------------------------------------------
642 bool cmCPackNSISGenerator::SupportsAbsoluteDestination() const
643 {
644         return false;
645 }
646
647 //----------------------------------------------------------------------
648 bool cmCPackNSISGenerator::SupportsComponentInstallation() const
649 {
650         return true;
651 }
652
653 //----------------------------------------------------------------------
654 std::string 
655 cmCPackNSISGenerator::
656 CreateComponentDescription(cmCPackComponent *component, 
657                            cmOStringStream& macrosOut)
658 {
659   // Basic description of the component
660   std::string componentCode = "Section ";
661   if (component->IsDisabledByDefault)
662     {
663     componentCode += "/o ";
664     }
665   componentCode += "\"";
666   if (component->IsHidden)
667     {
668     componentCode += "-";
669     }
670   componentCode += component->DisplayName + "\" " + component->Name + "\n";
671   if (component->IsRequired) 
672     {
673     componentCode += "  SectionIn RO\n";
674     }
675   else if (!component->InstallationTypes.empty())
676     {
677     cmOStringStream out;
678     std::vector<cmCPackInstallationType *>::iterator installTypeIter;
679     for (installTypeIter = component->InstallationTypes.begin();
680          installTypeIter != component->InstallationTypes.end();
681          ++installTypeIter)
682       {
683       out << " " << (*installTypeIter)->Index;
684       }
685     componentCode += "  SectionIn" + out.str() + "\n";
686     }
687   componentCode += "  SetOutPath \"$INSTDIR\"\n";
688
689   // Create the actual installation commands
690   if (component->IsDownloaded)
691     {
692     if (component->ArchiveFile.empty())
693       {
694       // Compute the name of the archive.
695       std::string packagesDir = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
696       packagesDir += ".dummy";
697       cmOStringStream out;
698       out << cmSystemTools::GetFilenameWithoutLastExtension(packagesDir)
699         << "-" << component->Name << ".zip";
700       component->ArchiveFile = out.str();
701       }
702
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)
708       {
709       uploadDirectory = userUploadDirectory;
710       }
711     else
712       {
713       uploadDirectory= this->GetOption("CPACK_PACKAGE_DIRECTORY");
714       uploadDirectory += "/CPackUploads";
715       }
716     if(!cmSystemTools::FileExists(uploadDirectory.c_str()))
717       {
718       if (!cmSystemTools::MakeDirectory(uploadDirectory.c_str()))
719         {
720         cmCPackLogger(cmCPackLog::LOG_ERROR,
721           "Unable to create NSIS upload directory " << uploadDirectory
722           << std::endl);
723         return "";
724         }
725       }
726
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))
733       {
734         if (!cmSystemTools::RemoveFile(archiveFile.c_str()))
735         {
736         cmCPackLogger(cmCPackLog::LOG_ERROR,
737           "Unable to remove archive file " << archiveFile
738           << std::endl);
739         return "";
740         }
741       }
742
743     // Find a ZIP program
744     if (!this->IsSet("ZIP_EXECUTABLE"))
745       {
746       this->ReadListFile("CPackZIP.cmake");
747
748       if (!this->IsSet("ZIP_EXECUTABLE"))
749         {
750         cmCPackLogger(cmCPackLog::LOG_ERROR,
751           "Unable to find ZIP program"
752           << std::endl);
753         return "";
754         }
755       }
756
757     // The directory where this component's files reside
758     std::string dirName = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
759     dirName += '/';
760     dirName += component->Name;
761     dirName += '/';
762
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(); 
775            ++fileIt)
776         {
777         if ( needQuotesInFile )
778           {
779           out << "\"";
780           }
781         out << *fileIt;
782         if ( needQuotesInFile )
783           {
784           out << "\"";
785           }
786         out << std::endl;
787
788         totalSize += cmSystemTools::FileLength((dirName + *fileIt).c_str());
789         }
790     }
791
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());
797     std::string output;
798     int retVal = -1;
799     int res = cmSystemTools::RunSingleCommand(cmd.c_str(), &output, &retVal, 
800                                               dirName.c_str(),
801                                               cmSystemTools::OUTPUT_NONE, 0);
802     if ( !res || retVal )
803     {
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);
813       return "";
814     }
815     
816     // Create the NSIS code to download this file on-the-fly.
817     unsigned long totalSizeInKbytes = (totalSize + 512) / 1024;
818     if (totalSizeInKbytes == 0)
819       {
820       totalSizeInKbytes = 1;
821       }
822     cmOStringStream out;
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();
833     }
834   else
835     {
836     componentCode += "  File /r \"${INST_DIR}\\" +
837       component->Name + "\\*.*\"\n";
838     }
839   componentCode += "SectionEnd\n";
840
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;
846   std::string path;
847   for (pathIt = component->Files.begin();
848        pathIt != component->Files.end();
849        ++pathIt)
850     {
851     path = *pathIt;
852     cmSystemTools::ReplaceString(path, "/", "\\");
853     macrosOut << "  Delete \"$INSTDIR\\"
854               << path.c_str()
855               << "\"\n";
856     }
857   for (pathIt = component->Directories.begin();
858        pathIt != component->Directories.end();
859        ++pathIt)
860     {
861     path = *pathIt;
862     cmSystemTools::ReplaceString(path, "/", "\\");
863     macrosOut << "  RMDir \"$INSTDIR\\"
864               << path.c_str()
865               << "\"\n";
866     }
867   macrosOut << "  noremove_" << component->Name << ":\n";
868   macrosOut << "!macroend\n";
869
870   // Macro used to select each of the components that this component
871   // depends on.
872   std::set<cmCPackComponent *> visited;
873   macrosOut << "!macro Select_" << component->Name << "_depends\n";
874   macrosOut << CreateSelectionDependenciesDescription(component, visited);
875   macrosOut << "!macroend\n";
876
877   // Macro used to deselect each of the components that depend on this
878   // component.
879   visited.clear();
880   macrosOut << "!macro Deselect_required_by_" << component->Name << "\n";
881   macrosOut << CreateDeselectionDependenciesDescription(component, visited);
882   macrosOut << "!macroend\n";
883   return componentCode;
884 }
885
886 //----------------------------------------------------------------------
887 std::string cmCPackNSISGenerator::CreateSelectionDependenciesDescription
888               (cmCPackComponent *component,
889                std::set<cmCPackComponent *>& visited)
890 {
891   // Don't visit a component twice
892   if (visited.count(component)) 
893     {
894     return std::string();
895     }
896   visited.insert(component);
897
898   cmOStringStream out;
899   std::vector<cmCPackComponent *>::iterator dependIt;
900   for (dependIt = component->Dependencies.begin();
901        dependIt != component->Dependencies.end();
902        ++dependIt)
903     {
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";
910     // Recurse
911     out << CreateSelectionDependenciesDescription(*dependIt, visited).c_str();
912     }
913
914   return out.str();
915 }
916
917
918 //----------------------------------------------------------------------
919 std::string cmCPackNSISGenerator::CreateDeselectionDependenciesDescription
920               (cmCPackComponent *component,
921                    std::set<cmCPackComponent *>& visited)
922 {
923   // Don't visit a component twice
924   if (visited.count(component)) 
925     {
926     return std::string();
927     }
928   visited.insert(component);
929
930   cmOStringStream out;
931   std::vector<cmCPackComponent *>::iterator dependIt;
932   for (dependIt = component->ReverseDependencies.begin();
933        dependIt != component->ReverseDependencies.end();
934        ++dependIt)
935     {
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";
942     
943     // Recurse
944     out <<
945       CreateDeselectionDependenciesDescription(*dependIt, visited).c_str();
946     }
947
948   return out.str();
949 }
950
951 //----------------------------------------------------------------------
952 std::string 
953 cmCPackNSISGenerator::
954 CreateComponentGroupDescription(cmCPackComponentGroup *group, 
955                                 cmOStringStream& macrosOut)
956 {
957   if (group->Components.empty() && group->Subgroups.empty())
958     {
959     // Silently skip empty groups. NSIS doesn't support them.
960     return std::string();
961     }
962
963   std::string code = "SectionGroup ";
964   if (group->IsExpandedByDefault)
965     {
966     code += "/e ";
967     }
968   if (group->IsBold)
969     {
970     code += "\"!" + group->DisplayName + "\" " + group->Name + "\n";
971     }
972   else
973     {
974     code += "\"" + group->DisplayName + "\" " + group->Name + "\n";
975     }
976
977   std::vector<cmCPackComponentGroup*>::iterator groupIt;
978   for (groupIt = group->Subgroups.begin(); groupIt != group->Subgroups.end();
979        ++groupIt)
980     {
981     code += this->CreateComponentGroupDescription(*groupIt, macrosOut);
982     }
983
984   std::vector<cmCPackComponent*>::iterator comp;
985   for (comp = group->Components.begin(); 
986        comp != group->Components.end(); 
987        ++comp)
988     {
989     if ((*comp)->Files.empty())
990       {
991       continue;
992       }
993
994     code += this->CreateComponentDescription(*comp, macrosOut);
995     }
996   code += "SectionGroupEnd\n";
997   return code;
998 }
999
1000 std::string cmCPackNSISGenerator::TranslateNewlines(std::string str)
1001 {
1002   cmSystemTools::ReplaceString(str, "\n", "$\\r$\\n");
1003   return str;
1004 }