1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmCPackNSISGenerator.cxx,v $
6 Date: $Date: 2008/02/15 15:40:55 $
7 Version: $Revision: 1.31 $
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"
27 #include <cmsys/SystemTools.hxx>
28 #include <cmsys/Glob.hxx>
29 #include <cmsys/Directory.hxx>
30 #include <cmsys/RegularExpression.hxx>
32 /* NSIS uses different command line syntax on Windows and others */
39 //----------------------------------------------------------------------
40 cmCPackNSISGenerator::cmCPackNSISGenerator()
44 //----------------------------------------------------------------------
45 cmCPackNSISGenerator::~cmCPackNSISGenerator()
49 //----------------------------------------------------------------------
50 int cmCPackNSISGenerator::CompressFiles(const char* outFileName
,
51 const char* toplevel
, const std::vector
<std::string
>& files
)
53 (void)outFileName
; // TODO: Fix nsis to force out file name
55 std::string nsisInFileName
= this->FindTemplate("NSIS.template.in");
56 if ( nsisInFileName
.size() == 0 )
58 cmCPackLogger(cmCPackLog::LOG_ERROR
,
59 "CPack error: Could not find NSIS installer template file."
63 std::string nsisInInstallOptions
64 = this->FindTemplate("NSIS.InstallOptions.ini.in");
65 if ( nsisInInstallOptions
.size() == 0 )
67 cmCPackLogger(cmCPackLog::LOG_ERROR
,
68 "CPack error: Could not find NSIS installer options file."
72 std::string nsisFileName
= this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
73 std::string tmpFile
= nsisFileName
;
74 tmpFile
+= "/NSISOutput.log";
75 std::string nsisInstallOptions
= nsisFileName
+ "/NSIS.InstallOptions.ini";
76 nsisFileName
+= "/project.nsi";
78 std::vector
<std::string
>::const_iterator it
;
79 for ( it
= files
.begin(); it
!= files
.end(); ++ it
)
81 std::string fileN
= cmSystemTools::RelativePath(toplevel
,
83 cmSystemTools::ReplaceString(fileN
, "/", "\\");
84 str
<< " Delete \"$INSTDIR\\" << fileN
.c_str() << "\"" << std::endl
;
86 cmCPackLogger(cmCPackLog::LOG_DEBUG
, "Uninstall Files: "
87 << str
.str().c_str() << std::endl
);
88 this->SetOptionIfNotSet("CPACK_NSIS_DELETE_FILES", str
.str().c_str());
89 std::vector
<std::string
> dirs
;
90 this->GetListOfSubdirectories(toplevel
, dirs
);
91 std::vector
<std::string
>::const_iterator sit
;
93 for ( sit
= dirs
.begin(); sit
!= dirs
.end(); ++ sit
)
95 std::string fileN
= cmSystemTools::RelativePath(toplevel
,
101 cmSystemTools::ReplaceString(fileN
, "/", "\\");
102 dstr
<< " RMDir \"$INSTDIR\\" << fileN
.c_str() << "\"" << std::endl
;
104 cmCPackLogger(cmCPackLog::LOG_DEBUG
, "Uninstall Dirs: "
105 << dstr
.str().c_str() << std::endl
);
106 this->SetOptionIfNotSet("CPACK_NSIS_DELETE_DIRECTORIES",
109 cmCPackLogger(cmCPackLog::LOG_VERBOSE
, "Configure file: " << nsisInFileName
110 << " to " << nsisFileName
<< std::endl
);
111 if(this->IsSet("CPACK_NSIS_MUI_ICON")
112 && this->IsSet("CPACK_NSIS_MUI_UNIICON"))
114 std::string installerIconCode
="!define MUI_ICON \"";
115 installerIconCode
+= this->GetOption("CPACK_NSIS_MUI_ICON");
116 installerIconCode
+= "\"\n";
117 installerIconCode
+= "!define MUI_UNICON \"";
118 installerIconCode
+= this->GetOption("CPACK_NSIS_MUI_UNIICON");
119 installerIconCode
+= "\"\n";
120 this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_ICON_CODE",
121 installerIconCode
.c_str());
123 if(this->IsSet("CPACK_PACKAGE_ICON"))
125 std::string installerIconCode
= "!define MUI_HEADERIMAGE_BITMAP \"";
126 installerIconCode
+= this->GetOption("CPACK_PACKAGE_ICON");
127 installerIconCode
+= "\"\n";
128 this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_ICON_CODE",
129 installerIconCode
.c_str());
131 this->ConfigureFile(nsisInInstallOptions
.c_str(),
132 nsisInstallOptions
.c_str());
133 this->ConfigureFile(nsisInFileName
.c_str(), nsisFileName
.c_str());
134 std::string nsisCmd
= "\"";
135 nsisCmd
+= this->GetOption("CPACK_INSTALLER_PROGRAM");
136 nsisCmd
+= "\" \"" + nsisFileName
+ "\"";
137 cmCPackLogger(cmCPackLog::LOG_VERBOSE
, "Execute: " << nsisCmd
.c_str()
141 bool res
= cmSystemTools::RunSingleCommand(nsisCmd
.c_str(), &output
,
142 &retVal
, 0, this->GeneratorVerbose
, 0);
143 if ( !res
|| retVal
)
145 cmGeneratedFileStream
ofs(tmpFile
.c_str());
146 ofs
<< "# Run command: " << nsisCmd
.c_str() << std::endl
147 << "# Output:" << std::endl
148 << output
.c_str() << std::endl
;
149 cmCPackLogger(cmCPackLog::LOG_ERROR
, "Problem running NSIS command: "
150 << nsisCmd
.c_str() << std::endl
151 << "Please check " << tmpFile
.c_str() << " for errors" << std::endl
);
157 //----------------------------------------------------------------------
158 int cmCPackNSISGenerator::InitializeInternal()
160 if ( cmSystemTools::IsOn(this->GetOption(
161 "CPACK_INCLUDE_TOPLEVEL_DIRECTORY")) )
163 cmCPackLogger(cmCPackLog::LOG_ERROR
,
164 "NSIS Generator cannot work with CPACK_INCLUDE_TOPLEVEL_DIRECTORY. "
165 "This option will be ignored."
167 this->SetOption("CPACK_INCLUDE_TOPLEVEL_DIRECTORY", 0);
170 cmCPackLogger(cmCPackLog::LOG_DEBUG
, "cmCPackNSISGenerator::Initialize()"
172 std::vector
<std::string
> path
;
173 std::string nsisPath
;
176 if ( !cmsys::SystemTools::ReadRegistryValue(
177 "HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS", nsisPath
) )
180 (cmCPackLog::LOG_ERROR
,
181 "Cannot find NSIS registry value. This is usually caused by NSIS "
182 "not being installed. Please install NSIS from "
183 "http://nsis.sourceforge.net"
187 path
.push_back(nsisPath
);
189 nsisPath
= cmSystemTools::FindProgram("makensis", path
, false);
190 if ( nsisPath
.empty() )
192 cmCPackLogger(cmCPackLog::LOG_ERROR
, "Cannot find NSIS compiler"
196 std::string nsisCmd
= "\"" + nsisPath
+ "\" " NSIS_OPT
"VERSION";
197 cmCPackLogger(cmCPackLog::LOG_VERBOSE
, "Test NSIS version: "
198 << nsisCmd
.c_str() << std::endl
);
201 bool resS
= cmSystemTools::RunSingleCommand(nsisCmd
.c_str(),
202 &output
, &retVal
, 0, this->GeneratorVerbose
, 0);
204 cmsys::RegularExpression
versionRex("v([0-9]+.[0-9]+)");
205 if ( !resS
|| retVal
|| !versionRex
.find(output
))
207 std::string tmpFile
= this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
208 tmpFile
+= "/NSISOutput.log";
209 cmGeneratedFileStream
ofs(tmpFile
.c_str());
210 ofs
<< "# Run command: " << nsisCmd
.c_str() << std::endl
211 << "# Output:" << std::endl
212 << output
.c_str() << std::endl
;
213 cmCPackLogger(cmCPackLog::LOG_ERROR
,
214 "Problem checking NSIS version with command: "
215 << nsisCmd
.c_str() << std::endl
216 << "Please check " << tmpFile
.c_str() << " for errors" << std::endl
);
219 double nsisVersion
= atof(versionRex
.match(1).c_str());
220 double minNSISVersion
= 2.09;
221 cmCPackLogger(cmCPackLog::LOG_DEBUG
, "NSIS Version: "
222 << nsisVersion
<< std::endl
);
223 if ( nsisVersion
< minNSISVersion
)
225 cmCPackLogger(cmCPackLog::LOG_ERROR
,
226 "CPack requires NSIS Version 2.09 or greater. NSIS found on the system "
228 << nsisVersion
<< std::endl
);
231 this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM", nsisPath
.c_str());
232 const char* cpackPackageExecutables
233 = this->GetOption("CPACK_PACKAGE_EXECUTABLES");
234 const char* cpackPackageDeskTopLinks
235 = this->GetOption("CPACK_CREATE_DESKTOP_LINKS");
236 std::vector
<std::string
> cpackPackageDesktopLinksVector
;
237 if(cpackPackageDeskTopLinks
)
239 cmCPackLogger(cmCPackLog::LOG_DEBUG
, "CPACK_CREATE_DESKTOP_LINKS: "
240 << cpackPackageDeskTopLinks
<< std::endl
);
243 ExpandListArgument(cpackPackageDeskTopLinks
,
244 cpackPackageDesktopLinksVector
);
245 for(std::vector
<std::string
>::iterator i
=
246 cpackPackageDesktopLinksVector
.begin(); i
!=
247 cpackPackageDesktopLinksVector
.end(); ++i
)
249 cmCPackLogger(cmCPackLog::LOG_DEBUG
, "CPACK_CREATE_DESKTOP_LINKS: "
255 cmCPackLogger(cmCPackLog::LOG_DEBUG
, "CPACK_CREATE_DESKTOP_LINKS: "
256 << "not set" << std::endl
);
259 if ( cpackPackageExecutables
)
261 cmCPackLogger(cmCPackLog::LOG_DEBUG
, "The cpackPackageExecutables: "
262 << cpackPackageExecutables
<< "." << std::endl
);
264 cmOStringStream deleteStr
;
265 std::vector
<std::string
> cpackPackageExecutablesVector
;
266 cmSystemTools::ExpandListArgument(cpackPackageExecutables
,
267 cpackPackageExecutablesVector
);
268 if ( cpackPackageExecutablesVector
.size() % 2 != 0 )
270 cmCPackLogger(cmCPackLog::LOG_ERROR
,
271 "CPACK_PACKAGE_EXECUTABLES should contain pairs of <executable> and "
272 "<icon name>." << std::endl
);
275 std::vector
<std::string
>::iterator it
;
276 for ( it
= cpackPackageExecutablesVector
.begin();
277 it
!= cpackPackageExecutablesVector
.end();
280 std::string execName
= *it
;
282 std::string linkName
= *it
;
283 str
<< " CreateShortCut \"$SMPROGRAMS\\$STARTMENU_FOLDER\\"
284 << linkName
<< ".lnk\" \"$INSTDIR\\bin\\" << execName
<< ".exe\""
286 deleteStr
<< " Delete \"$SMPROGRAMS\\$MUI_TEMP\\" << linkName
287 << ".lnk\"" << std::endl
;
288 // see if CPACK_CREATE_DESKTOP_LINK_ExeName is on
289 // if so add a desktop link
290 if(cpackPackageDesktopLinksVector
.size() &&
291 std::find(cpackPackageDesktopLinksVector
.begin(),
292 cpackPackageDesktopLinksVector
.end(),
294 != cpackPackageDesktopLinksVector
.end())
296 str
<< " StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n";
297 str
<< " CreateShortCut \"$DESKTOP\\"
298 << linkName
<< ".lnk\" \"$INSTDIR\\bin\\" << execName
<< ".exe\""
300 deleteStr
<< " StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n";
301 deleteStr
<< " Delete \"$DESKTOP\\" << linkName
302 << ".lnk\"" << std::endl
;
305 this->CreateMenuLinks(str
, deleteStr
);
306 this->SetOptionIfNotSet("CPACK_NSIS_CREATE_ICONS", str
.str().c_str());
307 this->SetOptionIfNotSet("CPACK_NSIS_DELETE_ICONS",
308 deleteStr
.str().c_str());
310 this->SetOptionIfNotSet("CPACK_NSIS_COMPRESSOR", "lzma");
312 return this->Superclass::InitializeInternal();
315 //----------------------------------------------------------------------
316 void cmCPackNSISGenerator::CreateMenuLinks( cmOStringStream
& str
,
317 cmOStringStream
& deleteStr
)
319 const char* cpackMenuLinks
320 = this->GetOption("CPACK_NSIS_MENU_LINKS");
325 cmCPackLogger(cmCPackLog::LOG_DEBUG
, "The cpackMenuLinks: "
326 << cpackMenuLinks
<< "." << std::endl
);
327 std::vector
<std::string
> cpackMenuLinksVector
;
328 cmSystemTools::ExpandListArgument(cpackMenuLinks
,
329 cpackMenuLinksVector
);
330 if ( cpackMenuLinksVector
.size() % 2 != 0 )
333 cmCPackLog::LOG_ERROR
,
334 "CPACK_PACKAGE_EXECUTABLES should contain pairs of <executable> and "
335 "<icon name>." << std::endl
);
338 std::vector
<std::string
>::iterator it
;
339 for ( it
= cpackMenuLinksVector
.begin();
340 it
!= cpackMenuLinksVector
.end();
343 std::string sourceName
= *it
;
345 if(sourceName
.find("http:") == 0)
349 /* convert / to \\ */
352 cmSystemTools::ReplaceString(sourceName
, "/", "\\");
355 std::string linkName
= *it
;
358 str
<< " CreateShortCut \"$SMPROGRAMS\\$STARTMENU_FOLDER\\"
359 << linkName
<< ".lnk\" \"$INSTDIR\\" << sourceName
<< "\""
361 deleteStr
<< " Delete \"$SMPROGRAMS\\$MUI_TEMP\\" << linkName
362 << ".lnk\"" << std::endl
;
366 str
<< " WriteINIStr \"$SMPROGRAMS\\$STARTMENU_FOLDER\\"
367 << linkName
<< ".url\" \"InternetShortcut\" \"URL\" \""
368 << sourceName
<< "\""
370 deleteStr
<< " Delete \"$SMPROGRAMS\\$MUI_TEMP\\" << linkName
371 << ".url\"" << std::endl
;
373 // see if CPACK_CREATE_DESKTOP_LINK_ExeName is on
374 // if so add a desktop link
375 std::string desktop
= "CPACK_CREATE_DESKTOP_LINK_";
377 if(this->IsSet(desktop
.c_str()))
379 str
<< " StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n";
380 str
<< " CreateShortCut \"$DESKTOP\\"
381 << linkName
<< ".lnk\" \"$INSTDIR\\" << sourceName
<< "\""
383 deleteStr
<< " StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n";
384 deleteStr
<< " Delete \"$DESKTOP\\" << linkName
385 << ".lnk\"" << std::endl
;
390 //----------------------------------------------------------------------
391 bool cmCPackNSISGenerator::GetListOfSubdirectories(const char* topdir
,
392 std::vector
<std::string
>& dirs
)
394 cmsys::Directory dir
;
397 for (fileNum
= 0; fileNum
< dir
.GetNumberOfFiles(); ++fileNum
)
399 if (strcmp(dir
.GetFile(static_cast<unsigned long>(fileNum
)),".") &&
400 strcmp(dir
.GetFile(static_cast<unsigned long>(fileNum
)),".."))
402 cmsys_stl::string fullPath
= topdir
;
404 fullPath
+= dir
.GetFile(static_cast<unsigned long>(fileNum
));
405 if(cmsys::SystemTools::FileIsDirectory(fullPath
.c_str()) &&
406 !cmsys::SystemTools::FileIsSymlink(fullPath
.c_str()))
408 if (!this->GetListOfSubdirectories(fullPath
.c_str(), dirs
))
415 dirs
.push_back(topdir
);