cmCPackNSISGenerator.cxx 35 KB

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