1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmCPackNSISGenerator.cxx,v $
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 */
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
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."
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."
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";
79 std::vector
<std::string
>::const_iterator it
;
80 for ( it
= files
.begin(); it
!= files
.end(); ++ it
)
82 std::string fileN
= cmSystemTools::RelativePath(toplevel
,
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
;
99 for ( sit
= dirs
.begin(); sit
!= dirs
.end(); ++ sit
)
101 std::string componentName
;
102 std::string fileN
= cmSystemTools::RelativePath(toplevel
, sit
->c_str());
107 if (!Components
.empty())
109 // If this is a component installation, strip off the component
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
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",
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", "");
171 std::string componentCode
;
172 std::string sectionList
;
173 std::string selectedVarsList
;
174 std::string componentDescriptions
;
175 std::string groupDescriptions
;
176 std::string installTypesCode
;
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();
190 installTypes
[installTypeIt
->second
.Index
-1] = &installTypeIt
->second
;
192 std::vector
<cmCPackInstallationType
*>::iterator installTypeIt2
;
193 for (installTypeIt2
= installTypes
.begin();
194 installTypeIt2
!= installTypes
.end();
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();
208 if (groupIt
->second
.ParentGroup
== 0)
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();
229 if (compIt
->second
.Files
.empty())
231 // NSIS cannot cope with components that have no files.
235 anyDownloadedComponents
=
236 anyDownloadedComponents
|| compIt
->second
.IsDownloaded
;
238 if (!compIt
->second
.Group
)
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");
270 componentDescriptions
=
271 "!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN\n"
272 + componentDescriptions
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()
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
);
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."
338 this->SetOption("CPACK_INCLUDE_TOPLEVEL_DIRECTORY", 0);
341 cmCPackLogger(cmCPackLog::LOG_DEBUG
, "cmCPackNSISGenerator::Initialize()"
343 std::vector
<std::string
> path
;
344 std::string nsisPath
;
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
) )
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"
363 path
.push_back(nsisPath
);
365 nsisPath
= cmSystemTools::FindProgram("makensis", path
, false);
366 if ( nsisPath
.empty() )
368 cmCPackLogger(cmCPackLog::LOG_ERROR
, "Cannot find NSIS compiler"
372 std::string nsisCmd
= "\"" + nsisPath
+ "\" " NSIS_OPT
"VERSION";
373 cmCPackLogger(cmCPackLog::LOG_VERBOSE
, "Test NSIS version: "
374 << nsisCmd
.c_str() << std::endl
);
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
);
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 "
404 << nsisVersion
<< std::endl
);
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
);
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: "
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
);
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
);
450 std::vector
<std::string
>::iterator it
;
451 for ( it
= cpackPackageExecutablesVector
.begin();
452 it
!= cpackPackageExecutablesVector
.end();
455 std::string execName
= *it
;
457 std::string linkName
= *it
;
458 str
<< " CreateShortCut \"$SMPROGRAMS\\$STARTMENU_FOLDER\\"
459 << linkName
<< ".lnk\" \"$INSTDIR\\bin\\" << execName
<< ".exe\""
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(),
469 != cpackPackageDesktopLinksVector
.end())
471 str
<< " StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n";
472 str
<< " CreateShortCut \"$DESKTOP\\"
473 << linkName
<< ".lnk\" \"$INSTDIR\\bin\\" << execName
<< ".exe\""
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");
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 )
508 cmCPackLog::LOG_ERROR
,
509 "CPACK_PACKAGE_EXECUTABLES should contain pairs of <executable> and "
510 "<icon name>." << std::endl
);
513 std::vector
<std::string
>::iterator it
;
514 for ( it
= cpackMenuLinksVector
.begin();
515 it
!= cpackMenuLinksVector
.end();
518 std::string sourceName
= *it
;
520 if(sourceName
.find("http:") == 0)
524 /* convert / to \\ */
527 cmSystemTools::ReplaceString(sourceName
, "/", "\\");
530 std::string linkName
= *it
;
533 str
<< " CreateShortCut \"$SMPROGRAMS\\$STARTMENU_FOLDER\\"
534 << linkName
<< ".lnk\" \"$INSTDIR\\" << sourceName
<< "\""
536 deleteStr
<< " Delete \"$SMPROGRAMS\\$MUI_TEMP\\" << linkName
537 << ".lnk\"" << std::endl
;
541 str
<< " WriteINIStr \"$SMPROGRAMS\\$STARTMENU_FOLDER\\"
542 << linkName
<< ".url\" \"InternetShortcut\" \"URL\" \""
543 << sourceName
<< "\""
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_";
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
<< "\""
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
;
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
;
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
))
590 dirs
.push_back(topdir
);
594 //----------------------------------------------------------------------
595 bool cmCPackNSISGenerator::SupportsComponentInstallation() const
600 //----------------------------------------------------------------------
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())
625 std::vector
<cmCPackInstallationType
*>::iterator installTypeIter
;
626 for (installTypeIter
= component
->InstallationTypes
.begin();
627 installTypeIter
!= component
->InstallationTypes
.end();
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";
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
;
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
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
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"
707 // The directory where this component's files reside
708 std::string dirName
= this->GetOption("CPACK_TEMPORARY_DIRECTORY");
710 dirName
+= component
->Name
;
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();
727 if ( needQuotesInFile
)
732 if ( needQuotesInFile
)
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());
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
);
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;
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();
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
;
797 for (pathIt
= component
->Files
.begin();
798 pathIt
!= component
->Files
.end();
802 cmSystemTools::ReplaceString(path
, "/", "\\");
803 macrosOut
<< " Delete \"$INSTDIR\\"
807 for (pathIt
= component
->Directories
.begin();
808 pathIt
!= component
->Directories
.end();
812 cmSystemTools::ReplaceString(path
, "/", "\\");
813 macrosOut
<< " RMDir \"$INSTDIR\\"
817 macrosOut
<< " noremove_" << component
->Name
<< ":\n";
818 macrosOut
<< "!macroend\n";
820 // Macro used to select each of the components that this component
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
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
);
849 std::vector
<cmCPackComponent
*>::iterator dependIt
;
850 for (dependIt
= component
->Dependencies
.begin();
851 dependIt
!= component
->Dependencies
.end();
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";
861 out
<< CreateSelectionDependenciesDescription(*dependIt
, visited
).c_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
);
881 std::vector
<cmCPackComponent
*>::iterator dependIt
;
882 for (dependIt
= component
->ReverseDependencies
.begin();
883 dependIt
!= component
->ReverseDependencies
.end();
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";
895 CreateDeselectionDependenciesDescription(*dependIt
, visited
).c_str();
901 //----------------------------------------------------------------------
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
)
920 code
+= "\"!" + group
->DisplayName
+ "\" " + group
->Name
+ "\n";
924 code
+= "\"" + group
->DisplayName
+ "\" " + group
->Name
+ "\n";
927 std::vector
<cmCPackComponentGroup
*>::iterator groupIt
;
928 for (groupIt
= group
->Subgroups
.begin(); groupIt
!= group
->Subgroups
.end();
931 code
+= this->CreateComponentGroupDescription(*groupIt
, macrosOut
);
934 std::vector
<cmCPackComponent
*>::iterator comp
;
935 for (comp
= group
->Components
.begin();
936 comp
!= group
->Components
.end();
939 if ((*comp
)->Files
.empty())
944 code
+= this->CreateComponentDescription(*comp
, macrosOut
);
946 code
+= "SectionGroupEnd\n";
950 std::string
cmCPackNSISGenerator::TranslateNewlines(std::string str
)
952 cmSystemTools::ReplaceString(str
, "\n", "$\\r$\\n");