cmCPackNSISGenerator.cxx 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032
  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. cmOStringStream 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.c_str() << "\"" << std::endl;
  76. }
  77. cmCPackLogger(cmCPackLog::LOG_DEBUG, "Uninstall Files: "
  78. << str.str().c_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. cmOStringStream 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.c_str() << "\"" << 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().c_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. cmOStringStream 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.c_str()
  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.c_str() << std::endl
  305. << "# Output:" << std::endl
  306. << output.c_str() << std::endl;
  307. cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running NSIS command: "
  308. << nsisCmd.c_str() << std::endl
  309. << "Please check " << tmpFile.c_str() << " 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.c_str() << 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. if ( !resS || retVal || !versionRex.find(output))
  400. {
  401. std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
  402. tmpFile += "/NSISOutput.log";
  403. cmGeneratedFileStream ofs(tmpFile.c_str());
  404. ofs << "# Run command: " << nsisCmd.c_str() << std::endl
  405. << "# Output:" << std::endl
  406. << output.c_str() << std::endl;
  407. cmCPackLogger(cmCPackLog::LOG_ERROR,
  408. "Problem checking NSIS version with command: "
  409. << nsisCmd.c_str() << std::endl
  410. << "Please check " << tmpFile.c_str() << " for errors" << std::endl);
  411. return 0;
  412. }
  413. double nsisVersion = atof(versionRex.match(1).c_str());
  414. double minNSISVersion = 2.09;
  415. cmCPackLogger(cmCPackLog::LOG_DEBUG, "NSIS Version: "
  416. << nsisVersion << std::endl);
  417. if ( nsisVersion < minNSISVersion )
  418. {
  419. cmCPackLogger(cmCPackLog::LOG_ERROR,
  420. "CPack requires NSIS Version 2.09 or greater. NSIS found on the system "
  421. "was: "
  422. << nsisVersion << std::endl);
  423. return 0;
  424. }
  425. this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM", nsisPath.c_str());
  426. this->SetOptionIfNotSet("CPACK_NSIS_EXECUTABLES_DIRECTORY", "bin");
  427. const char* cpackPackageExecutables
  428. = this->GetOption("CPACK_PACKAGE_EXECUTABLES");
  429. const char* cpackPackageDeskTopLinks
  430. = this->GetOption("CPACK_CREATE_DESKTOP_LINKS");
  431. const char* cpackNsisExecutablesDirectory
  432. = this->GetOption("CPACK_NSIS_EXECUTABLES_DIRECTORY");
  433. std::vector<std::string> cpackPackageDesktopLinksVector;
  434. if(cpackPackageDeskTopLinks)
  435. {
  436. cmCPackLogger(cmCPackLog::LOG_DEBUG, "CPACK_CREATE_DESKTOP_LINKS: "
  437. << cpackPackageDeskTopLinks << std::endl);
  438. cmSystemTools::
  439. ExpandListArgument(cpackPackageDeskTopLinks,
  440. cpackPackageDesktopLinksVector);
  441. for(std::vector<std::string>::iterator i =
  442. cpackPackageDesktopLinksVector.begin(); i !=
  443. cpackPackageDesktopLinksVector.end(); ++i)
  444. {
  445. cmCPackLogger(cmCPackLog::LOG_DEBUG, "CPACK_CREATE_DESKTOP_LINKS: "
  446. << *i << std::endl);
  447. }
  448. }
  449. else
  450. {
  451. cmCPackLogger(cmCPackLog::LOG_DEBUG, "CPACK_CREATE_DESKTOP_LINKS: "
  452. << "not set" << std::endl);
  453. }
  454. cmOStringStream str;
  455. cmOStringStream deleteStr;
  456. if ( cpackPackageExecutables )
  457. {
  458. cmCPackLogger(cmCPackLog::LOG_DEBUG, "The cpackPackageExecutables: "
  459. << cpackPackageExecutables << "." << std::endl);
  460. std::vector<std::string> cpackPackageExecutablesVector;
  461. cmSystemTools::ExpandListArgument(cpackPackageExecutables,
  462. cpackPackageExecutablesVector);
  463. if ( cpackPackageExecutablesVector.size() % 2 != 0 )
  464. {
  465. cmCPackLogger(cmCPackLog::LOG_ERROR,
  466. "CPACK_PACKAGE_EXECUTABLES should contain pairs of <executable> and "
  467. "<icon name>." << std::endl);
  468. return 0;
  469. }
  470. std::vector<std::string>::iterator it;
  471. for ( it = cpackPackageExecutablesVector.begin();
  472. it != cpackPackageExecutablesVector.end();
  473. ++it )
  474. {
  475. std::string execName = *it;
  476. ++ it;
  477. std::string linkName = *it;
  478. str << " CreateShortCut \"$SMPROGRAMS\\$STARTMENU_FOLDER\\"
  479. << linkName << ".lnk\" \"$INSTDIR\\"
  480. << cpackNsisExecutablesDirectory << "\\" << execName << ".exe\""
  481. << std::endl;
  482. deleteStr << " Delete \"$SMPROGRAMS\\$MUI_TEMP\\" << linkName
  483. << ".lnk\"" << std::endl;
  484. // see if CPACK_CREATE_DESKTOP_LINK_ExeName is on
  485. // if so add a desktop link
  486. if(cpackPackageDesktopLinksVector.size() &&
  487. std::find(cpackPackageDesktopLinksVector.begin(),
  488. cpackPackageDesktopLinksVector.end(),
  489. execName)
  490. != cpackPackageDesktopLinksVector.end())
  491. {
  492. str << " StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n";
  493. str << " CreateShortCut \"$DESKTOP\\"
  494. << linkName << ".lnk\" \"$INSTDIR\\"
  495. << cpackNsisExecutablesDirectory << "\\" << execName << ".exe\""
  496. << std::endl;
  497. deleteStr << " StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n";
  498. deleteStr << " Delete \"$DESKTOP\\" << linkName
  499. << ".lnk\"" << std::endl;
  500. }
  501. }
  502. }
  503. this->CreateMenuLinks(str, deleteStr);
  504. this->SetOptionIfNotSet("CPACK_NSIS_CREATE_ICONS", str.str().c_str());
  505. this->SetOptionIfNotSet("CPACK_NSIS_DELETE_ICONS",
  506. deleteStr.str().c_str());
  507. this->SetOptionIfNotSet("CPACK_NSIS_COMPRESSOR", "lzma");
  508. return this->Superclass::InitializeInternal();
  509. }
  510. //----------------------------------------------------------------------
  511. void cmCPackNSISGenerator::CreateMenuLinks( cmOStringStream& str,
  512. cmOStringStream& deleteStr)
  513. {
  514. const char* cpackMenuLinks
  515. = this->GetOption("CPACK_NSIS_MENU_LINKS");
  516. if(!cpackMenuLinks)
  517. {
  518. return;
  519. }
  520. cmCPackLogger(cmCPackLog::LOG_DEBUG, "The cpackMenuLinks: "
  521. << cpackMenuLinks << "." << std::endl);
  522. std::vector<std::string> cpackMenuLinksVector;
  523. cmSystemTools::ExpandListArgument(cpackMenuLinks,
  524. cpackMenuLinksVector);
  525. if ( cpackMenuLinksVector.size() % 2 != 0 )
  526. {
  527. cmCPackLogger(
  528. cmCPackLog::LOG_ERROR,
  529. "CPACK_NSIS_MENU_LINKS should contain pairs of <shortcut target> and "
  530. "<shortcut label>." << std::endl);
  531. return;
  532. }
  533. cmsys::RegularExpression urlRegex;
  534. urlRegex.compile("^(mailto:|(ftps?|https?|news)://).*$");
  535. std::vector<std::string>::iterator it;
  536. for ( it = cpackMenuLinksVector.begin();
  537. it != cpackMenuLinksVector.end();
  538. ++it )
  539. {
  540. std::string sourceName = *it;
  541. const bool url = urlRegex.find(sourceName);
  542. // Convert / to \ in filenames, but not in urls:
  543. //
  544. if(!url)
  545. {
  546. cmSystemTools::ReplaceString(sourceName, "/", "\\");
  547. }
  548. ++ it;
  549. std::string linkName = *it;
  550. if(!url)
  551. {
  552. str << " CreateShortCut \"$SMPROGRAMS\\$STARTMENU_FOLDER\\"
  553. << linkName << ".lnk\" \"$INSTDIR\\" << sourceName << "\""
  554. << std::endl;
  555. deleteStr << " Delete \"$SMPROGRAMS\\$MUI_TEMP\\" << linkName
  556. << ".lnk\"" << std::endl;
  557. }
  558. else
  559. {
  560. str << " WriteINIStr \"$SMPROGRAMS\\$STARTMENU_FOLDER\\"
  561. << linkName << ".url\" \"InternetShortcut\" \"URL\" \""
  562. << sourceName << "\""
  563. << std::endl;
  564. deleteStr << " Delete \"$SMPROGRAMS\\$MUI_TEMP\\" << linkName
  565. << ".url\"" << std::endl;
  566. }
  567. // see if CPACK_CREATE_DESKTOP_LINK_ExeName is on
  568. // if so add a desktop link
  569. std::string desktop = "CPACK_CREATE_DESKTOP_LINK_";
  570. desktop += linkName;
  571. if(this->IsSet(desktop.c_str()))
  572. {
  573. str << " StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n";
  574. str << " CreateShortCut \"$DESKTOP\\"
  575. << linkName << ".lnk\" \"$INSTDIR\\" << sourceName << "\""
  576. << std::endl;
  577. deleteStr << " StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n";
  578. deleteStr << " Delete \"$DESKTOP\\" << linkName
  579. << ".lnk\"" << std::endl;
  580. }
  581. }
  582. }
  583. //----------------------------------------------------------------------
  584. bool cmCPackNSISGenerator::GetListOfSubdirectories(const char* topdir,
  585. std::vector<std::string>& dirs)
  586. {
  587. cmsys::Directory dir;
  588. dir.Load(topdir);
  589. size_t fileNum;
  590. for (fileNum = 0; fileNum < dir.GetNumberOfFiles(); ++fileNum)
  591. {
  592. if (strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)),".") &&
  593. strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)),".."))
  594. {
  595. cmsys_stl::string fullPath = topdir;
  596. fullPath += "/";
  597. fullPath += dir.GetFile(static_cast<unsigned long>(fileNum));
  598. if(cmsys::SystemTools::FileIsDirectory(fullPath.c_str()) &&
  599. !cmsys::SystemTools::FileIsSymlink(fullPath.c_str()))
  600. {
  601. if (!this->GetListOfSubdirectories(fullPath.c_str(), dirs))
  602. {
  603. return false;
  604. }
  605. }
  606. }
  607. }
  608. dirs.push_back(topdir);
  609. return true;
  610. }
  611. //----------------------------------------------------------------------
  612. enum cmCPackGenerator::CPackSetDestdirSupport
  613. cmCPackNSISGenerator::SupportsSetDestdir() const
  614. {
  615. return cmCPackGenerator::SETDESTDIR_SHOULD_NOT_BE_USED;
  616. }
  617. //----------------------------------------------------------------------
  618. bool cmCPackNSISGenerator::SupportsAbsoluteDestination() const
  619. {
  620. return false;
  621. }
  622. //----------------------------------------------------------------------
  623. bool cmCPackNSISGenerator::SupportsComponentInstallation() const
  624. {
  625. return true;
  626. }
  627. //----------------------------------------------------------------------
  628. std::string
  629. cmCPackNSISGenerator::
  630. CreateComponentDescription(cmCPackComponent *component,
  631. cmOStringStream& macrosOut)
  632. {
  633. // Basic description of the component
  634. std::string componentCode = "Section ";
  635. if (component->IsDisabledByDefault)
  636. {
  637. componentCode += "/o ";
  638. }
  639. componentCode += "\"";
  640. if (component->IsHidden)
  641. {
  642. componentCode += "-";
  643. }
  644. componentCode += component->DisplayName + "\" " + component->Name + "\n";
  645. if (component->IsRequired)
  646. {
  647. componentCode += " SectionIn RO\n";
  648. }
  649. else if (!component->InstallationTypes.empty())
  650. {
  651. cmOStringStream out;
  652. std::vector<cmCPackInstallationType *>::iterator installTypeIter;
  653. for (installTypeIter = component->InstallationTypes.begin();
  654. installTypeIter != component->InstallationTypes.end();
  655. ++installTypeIter)
  656. {
  657. out << " " << (*installTypeIter)->Index;
  658. }
  659. componentCode += " SectionIn" + out.str() + "\n";
  660. }
  661. componentCode += " SetOutPath \"$INSTDIR\"\n";
  662. // Create the actual installation commands
  663. if (component->IsDownloaded)
  664. {
  665. if (component->ArchiveFile.empty())
  666. {
  667. // Compute the name of the archive.
  668. std::string packagesDir = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
  669. packagesDir += ".dummy";
  670. cmOStringStream out;
  671. out << cmSystemTools::GetFilenameWithoutLastExtension(packagesDir)
  672. << "-" << component->Name << ".zip";
  673. component->ArchiveFile = out.str();
  674. }
  675. // Create the directory for the upload area
  676. const char* userUploadDirectory =
  677. this->GetOption("CPACK_UPLOAD_DIRECTORY");
  678. std::string uploadDirectory;
  679. if (userUploadDirectory && *userUploadDirectory)
  680. {
  681. uploadDirectory = userUploadDirectory;
  682. }
  683. else
  684. {
  685. uploadDirectory= this->GetOption("CPACK_PACKAGE_DIRECTORY");
  686. uploadDirectory += "/CPackUploads";
  687. }
  688. if(!cmSystemTools::FileExists(uploadDirectory.c_str()))
  689. {
  690. if (!cmSystemTools::MakeDirectory(uploadDirectory.c_str()))
  691. {
  692. cmCPackLogger(cmCPackLog::LOG_ERROR,
  693. "Unable to create NSIS upload directory " << uploadDirectory
  694. << std::endl);
  695. return "";
  696. }
  697. }
  698. // Remove the old archive, if one exists
  699. std::string archiveFile = uploadDirectory + '/' + component->ArchiveFile;
  700. cmCPackLogger(cmCPackLog::LOG_OUTPUT,
  701. "- Building downloaded component archive: "
  702. << archiveFile << std::endl);
  703. if (cmSystemTools::FileExists(archiveFile.c_str(), true))
  704. {
  705. if (!cmSystemTools::RemoveFile(archiveFile.c_str()))
  706. {
  707. cmCPackLogger(cmCPackLog::LOG_ERROR,
  708. "Unable to remove archive file " << archiveFile
  709. << std::endl);
  710. return "";
  711. }
  712. }
  713. // Find a ZIP program
  714. if (!this->IsSet("ZIP_EXECUTABLE"))
  715. {
  716. this->ReadListFile("CPackZIP.cmake");
  717. if (!this->IsSet("ZIP_EXECUTABLE"))
  718. {
  719. cmCPackLogger(cmCPackLog::LOG_ERROR,
  720. "Unable to find ZIP program"
  721. << std::endl);
  722. return "";
  723. }
  724. }
  725. // The directory where this component's files reside
  726. std::string dirName = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
  727. dirName += '/';
  728. dirName += component->Name;
  729. dirName += '/';
  730. // Build the list of files to go into this archive, and determine the
  731. // size of the installed component.
  732. std::string zipListFileName = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
  733. zipListFileName += "/winZip.filelist";
  734. bool needQuotesInFile
  735. = cmSystemTools::IsOn(this->GetOption("CPACK_ZIP_NEED_QUOTES"));
  736. unsigned long totalSize = 0;
  737. { // the scope is needed for cmGeneratedFileStream
  738. cmGeneratedFileStream out(zipListFileName.c_str());
  739. std::vector<std::string>::iterator fileIt;
  740. for (fileIt = component->Files.begin();
  741. fileIt != component->Files.end();
  742. ++fileIt)
  743. {
  744. if ( needQuotesInFile )
  745. {
  746. out << "\"";
  747. }
  748. out << *fileIt;
  749. if ( needQuotesInFile )
  750. {
  751. out << "\"";
  752. }
  753. out << std::endl;
  754. totalSize += cmSystemTools::FileLength((dirName + *fileIt).c_str());
  755. }
  756. }
  757. // Build the archive in the upload area
  758. std::string cmd = this->GetOption("CPACK_ZIP_COMMAND");
  759. cmsys::SystemTools::ReplaceString(cmd, "<ARCHIVE>", archiveFile.c_str());
  760. cmsys::SystemTools::ReplaceString(cmd, "<FILELIST>",
  761. zipListFileName.c_str());
  762. std::string output;
  763. int retVal = -1;
  764. int res = cmSystemTools::RunSingleCommand(cmd.c_str(), &output, &retVal,
  765. dirName.c_str(),
  766. cmSystemTools::OUTPUT_NONE, 0);
  767. if ( !res || retVal )
  768. {
  769. std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
  770. tmpFile += "/CompressZip.log";
  771. cmGeneratedFileStream ofs(tmpFile.c_str());
  772. ofs << "# Run command: " << cmd.c_str() << std::endl
  773. << "# Output:" << std::endl
  774. << output.c_str() << std::endl;
  775. cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running zip command: "
  776. << cmd.c_str() << std::endl
  777. << "Please check " << tmpFile.c_str() << " for errors" << std::endl);
  778. return "";
  779. }
  780. // Create the NSIS code to download this file on-the-fly.
  781. unsigned long totalSizeInKbytes = (totalSize + 512) / 1024;
  782. if (totalSizeInKbytes == 0)
  783. {
  784. totalSizeInKbytes = 1;
  785. }
  786. cmOStringStream out;
  787. out << " AddSize " << totalSizeInKbytes << "\n"
  788. << " Push \"" << component->ArchiveFile << "\"\n"
  789. << " Call DownloadFile\n"
  790. << " ZipDLL::extractall \"$INSTDIR\\"
  791. << component->ArchiveFile << "\" \"$INSTDIR\"\n"
  792. << " Pop $2 ; error message\n"
  793. " StrCmp $2 \"success\" +2 0\n"
  794. " MessageBox MB_OK \"Failed to unzip $2\"\n"
  795. " Delete $INSTDIR\\$0\n";
  796. componentCode += out.str();
  797. }
  798. else
  799. {
  800. componentCode += " File /r \"${INST_DIR}\\" +
  801. component->Name + "\\*.*\"\n";
  802. }
  803. componentCode += "SectionEnd\n";
  804. // Macro used to remove the component
  805. macrosOut << "!macro Remove_${" << component->Name << "}\n";
  806. macrosOut << " IntCmp $" << component->Name << "_was_installed 0 noremove_"
  807. << component->Name << "\n";
  808. std::vector<std::string>::iterator pathIt;
  809. std::string path;
  810. for (pathIt = component->Files.begin();
  811. pathIt != component->Files.end();
  812. ++pathIt)
  813. {
  814. path = *pathIt;
  815. cmSystemTools::ReplaceString(path, "/", "\\");
  816. macrosOut << " Delete \"$INSTDIR\\"
  817. << path.c_str()
  818. << "\"\n";
  819. }
  820. for (pathIt = component->Directories.begin();
  821. pathIt != component->Directories.end();
  822. ++pathIt)
  823. {
  824. path = *pathIt;
  825. cmSystemTools::ReplaceString(path, "/", "\\");
  826. macrosOut << " RMDir \"$INSTDIR\\"
  827. << path.c_str()
  828. << "\"\n";
  829. }
  830. macrosOut << " noremove_" << component->Name << ":\n";
  831. macrosOut << "!macroend\n";
  832. // Macro used to select each of the components that this component
  833. // depends on.
  834. std::set<cmCPackComponent *> visited;
  835. macrosOut << "!macro Select_" << component->Name << "_depends\n";
  836. macrosOut << CreateSelectionDependenciesDescription(component, visited);
  837. macrosOut << "!macroend\n";
  838. // Macro used to deselect each of the components that depend on this
  839. // component.
  840. visited.clear();
  841. macrosOut << "!macro Deselect_required_by_" << component->Name << "\n";
  842. macrosOut << CreateDeselectionDependenciesDescription(component, visited);
  843. macrosOut << "!macroend\n";
  844. return componentCode;
  845. }
  846. //----------------------------------------------------------------------
  847. std::string cmCPackNSISGenerator::CreateSelectionDependenciesDescription
  848. (cmCPackComponent *component,
  849. std::set<cmCPackComponent *>& visited)
  850. {
  851. // Don't visit a component twice
  852. if (visited.count(component))
  853. {
  854. return std::string();
  855. }
  856. visited.insert(component);
  857. cmOStringStream out;
  858. std::vector<cmCPackComponent *>::iterator dependIt;
  859. for (dependIt = component->Dependencies.begin();
  860. dependIt != component->Dependencies.end();
  861. ++dependIt)
  862. {
  863. // Write NSIS code to select this dependency
  864. out << " SectionGetFlags ${" << (*dependIt)->Name << "} $0\n";
  865. out << " IntOp $0 $0 | ${SF_SELECTED}\n";
  866. out << " SectionSetFlags ${" << (*dependIt)->Name << "} $0\n";
  867. out << " IntOp $" << (*dependIt)->Name
  868. << "_selected 0 + ${SF_SELECTED}\n";
  869. // Recurse
  870. out << CreateSelectionDependenciesDescription(*dependIt, visited).c_str();
  871. }
  872. return out.str();
  873. }
  874. //----------------------------------------------------------------------
  875. std::string cmCPackNSISGenerator::CreateDeselectionDependenciesDescription
  876. (cmCPackComponent *component,
  877. std::set<cmCPackComponent *>& visited)
  878. {
  879. // Don't visit a component twice
  880. if (visited.count(component))
  881. {
  882. return std::string();
  883. }
  884. visited.insert(component);
  885. cmOStringStream out;
  886. std::vector<cmCPackComponent *>::iterator dependIt;
  887. for (dependIt = component->ReverseDependencies.begin();
  888. dependIt != component->ReverseDependencies.end();
  889. ++dependIt)
  890. {
  891. // Write NSIS code to deselect this dependency
  892. out << " SectionGetFlags ${" << (*dependIt)->Name << "} $0\n";
  893. out << " IntOp $1 ${SF_SELECTED} ~\n";
  894. out << " IntOp $0 $0 & $1\n";
  895. out << " SectionSetFlags ${" << (*dependIt)->Name << "} $0\n";
  896. out << " IntOp $" << (*dependIt)->Name << "_selected 0 + 0\n";
  897. // Recurse
  898. out <<
  899. CreateDeselectionDependenciesDescription(*dependIt, visited).c_str();
  900. }
  901. return out.str();
  902. }
  903. //----------------------------------------------------------------------
  904. std::string
  905. cmCPackNSISGenerator::
  906. CreateComponentGroupDescription(cmCPackComponentGroup *group,
  907. cmOStringStream& macrosOut)
  908. {
  909. if (group->Components.empty() && group->Subgroups.empty())
  910. {
  911. // Silently skip empty groups. NSIS doesn't support them.
  912. return std::string();
  913. }
  914. std::string code = "SectionGroup ";
  915. if (group->IsExpandedByDefault)
  916. {
  917. code += "/e ";
  918. }
  919. if (group->IsBold)
  920. {
  921. code += "\"!" + group->DisplayName + "\" " + group->Name + "\n";
  922. }
  923. else
  924. {
  925. code += "\"" + group->DisplayName + "\" " + group->Name + "\n";
  926. }
  927. std::vector<cmCPackComponentGroup*>::iterator groupIt;
  928. for (groupIt = group->Subgroups.begin(); groupIt != group->Subgroups.end();
  929. ++groupIt)
  930. {
  931. code += this->CreateComponentGroupDescription(*groupIt, macrosOut);
  932. }
  933. std::vector<cmCPackComponent*>::iterator comp;
  934. for (comp = group->Components.begin();
  935. comp != group->Components.end();
  936. ++comp)
  937. {
  938. if ((*comp)->Files.empty())
  939. {
  940. continue;
  941. }
  942. code += this->CreateComponentDescription(*comp, macrosOut);
  943. }
  944. code += "SectionGroupEnd\n";
  945. return code;
  946. }
  947. std::string cmCPackNSISGenerator::TranslateNewlines(std::string str)
  948. {
  949. cmSystemTools::ReplaceString(str, "\n", "$\\r$\\n");
  950. return str;
  951. }