cmCPackNSISGenerator.cxx 32 KB

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