cmCPackInnoSetupGenerator.cxx 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmCPackInnoSetupGenerator.h"
  4. #include <algorithm>
  5. #include <cctype>
  6. #include <cstdlib>
  7. #include <ostream>
  8. #include <utility>
  9. #include "cmsys/RegularExpression.hxx"
  10. #include "cmCPackComponentGroup.h"
  11. #include "cmCPackLog.h"
  12. #include "cmDuration.h"
  13. #include "cmGeneratedFileStream.h"
  14. #include "cmList.h"
  15. #include "cmStringAlgorithms.h"
  16. #include "cmSystemTools.h"
  17. cmCPackInnoSetupGenerator::cmCPackInnoSetupGenerator() = default;
  18. cmCPackInnoSetupGenerator::~cmCPackInnoSetupGenerator() = default;
  19. bool cmCPackInnoSetupGenerator::CanGenerate()
  20. {
  21. return true;
  22. }
  23. int cmCPackInnoSetupGenerator::InitializeInternal()
  24. {
  25. if (GetOption("CPACK_INCLUDE_TOPLEVEL_DIRECTORY").IsOn()) {
  26. cmCPackLogger(cmCPackLog::LOG_WARNING,
  27. "Inno Setup Generator cannot work with "
  28. "CPACK_INCLUDE_TOPLEVEL_DIRECTORY set. "
  29. "This option will be reset to 0 (for this generator only)."
  30. << std::endl);
  31. SetOption("CPACK_INCLUDE_TOPLEVEL_DIRECTORY", nullptr);
  32. }
  33. std::vector<std::string> path;
  34. #ifdef _WIN32
  35. path.push_back("C:\\Program Files (x86)\\Inno Setup 5");
  36. path.push_back("C:\\Program Files (x86)\\Inno Setup 6");
  37. #endif
  38. SetOptionIfNotSet("CPACK_INNOSETUP_EXECUTABLE", "ISCC");
  39. const std::string& isccPath = cmSystemTools::FindProgram(
  40. GetOption("CPACK_INNOSETUP_EXECUTABLE"), path, false);
  41. if (isccPath.empty()) {
  42. cmCPackLogger(cmCPackLog::LOG_ERROR,
  43. "Cannot find Inno Setup compiler ISCC: "
  44. "likely it is not installed, or not in your PATH"
  45. << std::endl);
  46. return 0;
  47. }
  48. const std::string isccCmd =
  49. cmStrCat(QuotePath(isccPath, PathType::Native), "/?");
  50. cmCPackLogger(cmCPackLog::LOG_VERBOSE,
  51. "Test Inno Setup version: " << isccCmd << std::endl);
  52. std::string output;
  53. cmSystemTools::RunSingleCommand(isccCmd, &output, &output, nullptr, nullptr,
  54. this->GeneratorVerbose, cmDuration::zero());
  55. cmsys::RegularExpression vRex("Inno Setup ([0-9]+)");
  56. if (!vRex.find(output)) {
  57. cmCPackLogger(cmCPackLog::LOG_ERROR,
  58. "Problem checking Inno Setup version with command: "
  59. << isccCmd << std::endl
  60. << "Have you downloaded Inno Setup from "
  61. "https://jrsoftware.org/isinfo.php?"
  62. << std::endl);
  63. return 0;
  64. }
  65. const int isccVersion = atoi(vRex.match(1).c_str());
  66. const int minIsccVersion = 6;
  67. cmCPackLogger(cmCPackLog::LOG_DEBUG,
  68. "Inno Setup Version: " << isccVersion << std::endl);
  69. if (isccVersion < minIsccVersion) {
  70. cmCPackLogger(cmCPackLog::LOG_ERROR,
  71. "CPack requires Inno Setup Version 6 or greater. "
  72. "Inno Setup found on the system was: "
  73. << isccVersion << std::endl);
  74. return 0;
  75. }
  76. SetOption("CPACK_INSTALLER_PROGRAM", isccPath);
  77. return this->Superclass::InitializeInternal();
  78. }
  79. int cmCPackInnoSetupGenerator::PackageFiles()
  80. {
  81. // Includes
  82. if (IsSet("CPACK_INNOSETUP_EXTRA_SCRIPTS")) {
  83. const cmList extraScripts(GetOption("CPACK_INNOSETUP_EXTRA_SCRIPTS"));
  84. for (const std::string& i : extraScripts) {
  85. includeDirectives.emplace_back(cmStrCat(
  86. "#include ", QuotePath(cmSystemTools::CollapseFullPath(i, toplevel))));
  87. }
  88. }
  89. // [Languages] section
  90. SetOptionIfNotSet("CPACK_INNOSETUP_LANGUAGES", "english");
  91. const cmList languages(GetOption("CPACK_INNOSETUP_LANGUAGES"));
  92. for (std::string i : languages) {
  93. cmCPackInnoSetupKeyValuePairs params;
  94. params["Name"] = Quote(i);
  95. if (cmSystemTools::LowerCase(i) == "english") {
  96. params["MessagesFile"] = "\"compiler:Default.isl\"";
  97. } else {
  98. i[0] = static_cast<char>(std::toupper(i[0]));
  99. params["MessagesFile"] = cmStrCat("\"compiler:Languages\\", i, ".isl\"");
  100. }
  101. languageInstructions.push_back(ISKeyValueLine(params));
  102. }
  103. if (!Components.empty() && !ProcessComponents()) {
  104. return false;
  105. }
  106. if (!(ProcessSetupSection() && ProcessFiles())) {
  107. return false;
  108. }
  109. // [Code] section
  110. if (IsSet("CPACK_INNOSETUP_CODE_FILES")) {
  111. const cmList codeFiles(GetOption("CPACK_INNOSETUP_CODE_FILES"));
  112. for (const std::string& i : codeFiles) {
  113. codeIncludes.emplace_back(cmStrCat(
  114. "#include ", QuotePath(cmSystemTools::CollapseFullPath(i, toplevel))));
  115. }
  116. }
  117. return ConfigureISScript() && Compile();
  118. }
  119. bool cmCPackInnoSetupGenerator::ProcessSetupSection()
  120. {
  121. if (!RequireOption("CPACK_PACKAGE_INSTALL_REGISTRY_KEY")) {
  122. return false;
  123. }
  124. setupDirectives["AppId"] = GetOption("CPACK_PACKAGE_INSTALL_REGISTRY_KEY");
  125. if (!RequireOption("CPACK_PACKAGE_NAME")) {
  126. return false;
  127. }
  128. setupDirectives["AppName"] = GetOption("CPACK_PACKAGE_NAME");
  129. setupDirectives["UninstallDisplayName"] = GetOption("CPACK_PACKAGE_NAME");
  130. if (!RequireOption("CPACK_PACKAGE_VERSION")) {
  131. return false;
  132. }
  133. setupDirectives["AppVersion"] = GetOption("CPACK_PACKAGE_VERSION");
  134. if (!RequireOption("CPACK_PACKAGE_VENDOR")) {
  135. return false;
  136. }
  137. setupDirectives["AppPublisher"] = GetOption("CPACK_PACKAGE_VENDOR");
  138. if (IsSet("CPACK_PACKAGE_HOMEPAGE_URL")) {
  139. setupDirectives["AppPublisherURL"] =
  140. GetOption("CPACK_PACKAGE_HOMEPAGE_URL");
  141. setupDirectives["AppSupportURL"] = GetOption("CPACK_PACKAGE_HOMEPAGE_URL");
  142. setupDirectives["AppUpdatesURL"] = GetOption("CPACK_PACKAGE_HOMEPAGE_URL");
  143. }
  144. SetOptionIfNotSet("CPACK_INNOSETUP_IGNORE_LICENSE_PAGE", "OFF");
  145. if (IsSet("CPACK_RESOURCE_FILE_LICENSE") &&
  146. !GetOption("CPACK_INNOSETUP_IGNORE_LICENSE_PAGE").IsOn()) {
  147. setupDirectives["LicenseFile"] = cmSystemTools::ConvertToWindowsOutputPath(
  148. GetOption("CPACK_RESOURCE_FILE_LICENSE"));
  149. }
  150. SetOptionIfNotSet("CPACK_INNOSETUP_IGNORE_README_PAGE", "ON");
  151. if (IsSet("CPACK_RESOURCE_FILE_README") &&
  152. !GetOption("CPACK_INNOSETUP_IGNORE_README_PAGE").IsOn()) {
  153. setupDirectives["InfoBeforeFile"] =
  154. cmSystemTools::ConvertToWindowsOutputPath(
  155. GetOption("CPACK_RESOURCE_FILE_README"));
  156. }
  157. SetOptionIfNotSet("CPACK_INNOSETUP_USE_MODERN_WIZARD", "OFF");
  158. if (GetOption("CPACK_INNOSETUP_USE_MODERN_WIZARD").IsOn()) {
  159. setupDirectives["WizardStyle"] = "modern";
  160. } else {
  161. setupDirectives["WizardStyle"] = "classic";
  162. setupDirectives["WizardSmallImageFile"] =
  163. "compiler:WizClassicSmallImage.bmp";
  164. setupDirectives["WizardImageFile"] = "compiler:WizClassicImage.bmp";
  165. setupDirectives["SetupIconFile"] = "compiler:SetupClassicIcon.ico";
  166. }
  167. if (IsSet("CPACK_INNOSETUP_ICON_FILE")) {
  168. setupDirectives["SetupIconFile"] =
  169. cmSystemTools::ConvertToWindowsOutputPath(
  170. GetOption("CPACK_INNOSETUP_ICON_FILE"));
  171. }
  172. if (IsSet("CPACK_PACKAGE_ICON")) {
  173. setupDirectives["WizardSmallImageFile"] =
  174. cmSystemTools::ConvertToWindowsOutputPath(
  175. GetOption("CPACK_PACKAGE_ICON"));
  176. }
  177. if (!RequireOption("CPACK_PACKAGE_INSTALL_DIRECTORY")) {
  178. return false;
  179. }
  180. SetOptionIfNotSet("CPACK_INNOSETUP_INSTALL_ROOT", "{autopf}");
  181. setupDirectives["DefaultDirName"] =
  182. cmSystemTools::ConvertToWindowsOutputPath(
  183. cmStrCat(GetOption("CPACK_INNOSETUP_INSTALL_ROOT"), '\\',
  184. GetOption("CPACK_PACKAGE_INSTALL_DIRECTORY")));
  185. SetOptionIfNotSet("CPACK_INNOSETUP_ALLOW_CUSTOM_DIRECTORY", "ON");
  186. if (GetOption("CPACK_INNOSETUP_ALLOW_CUSTOM_DIRECTORY").IsOff()) {
  187. setupDirectives["DisableDirPage"] = "yes";
  188. }
  189. SetOptionIfNotSet("CPACK_INNOSETUP_PROGRAM_MENU_FOLDER",
  190. GetOption("CPACK_PACKAGE_NAME"));
  191. if (GetOption("CPACK_INNOSETUP_PROGRAM_MENU_FOLDER") == ".") {
  192. setupDirectives["DisableProgramGroupPage"] = "yes";
  193. toplevelProgramFolder = true;
  194. } else {
  195. setupDirectives["DefaultGroupName"] =
  196. GetOption("CPACK_INNOSETUP_PROGRAM_MENU_FOLDER");
  197. toplevelProgramFolder = false;
  198. }
  199. if (IsSet("CPACK_INNOSETUP_PASSWORD")) {
  200. setupDirectives["Password"] = GetOption("CPACK_INNOSETUP_PASSWORD");
  201. setupDirectives["Encryption"] = "yes";
  202. }
  203. /*
  204. * These directives can only be modified using the
  205. * CPACK_INNOSETUP_SETUP_<directive> variables
  206. */
  207. setupDirectives["ShowLanguageDialog"] = "auto";
  208. setupDirectives["AllowNoIcons"] = "yes";
  209. setupDirectives["Compression"] = "lzma";
  210. setupDirectives["SolidCompression"] = "yes";
  211. // Output file and directory
  212. if (!RequireOption("CPACK_PACKAGE_FILE_NAME")) {
  213. return false;
  214. }
  215. setupDirectives["OutputBaseFilename"] = GetOption("CPACK_PACKAGE_FILE_NAME");
  216. if (!RequireOption("CPACK_TOPLEVEL_DIRECTORY")) {
  217. return false;
  218. }
  219. setupDirectives["OutputDir"] = cmSystemTools::ConvertToWindowsOutputPath(
  220. GetOption("CPACK_TOPLEVEL_DIRECTORY"));
  221. setupDirectives["SourceDir"] =
  222. cmSystemTools::ConvertToWindowsOutputPath(toplevel);
  223. // Target architecture
  224. if (!RequireOption("CPACK_INNOSETUP_ARCHITECTURE")) {
  225. return false;
  226. }
  227. cmValue const architecture = GetOption("CPACK_INNOSETUP_ARCHITECTURE");
  228. if (architecture != "x86" && architecture != "x64" &&
  229. architecture != "arm64" && architecture != "ia64") {
  230. cmCPackLogger(cmCPackLog::LOG_ERROR,
  231. "CPACK_INNOSETUP_ARCHITECTURE must be either x86, x64, "
  232. "arm64 or ia64"
  233. << std::endl);
  234. return false;
  235. }
  236. // The following directives must not be set to target x86
  237. if (architecture != "x86") {
  238. setupDirectives["ArchitecturesAllowed"] = architecture;
  239. setupDirectives["ArchitecturesInstallIn64BitMode"] = architecture;
  240. }
  241. /*
  242. * Handle custom directives (they have higher priority than other variables,
  243. * so they have to be processed after all other variables)
  244. */
  245. for (const std::string& i : GetOptions()) {
  246. if (cmHasPrefix(i, "CPACK_INNOSETUP_SETUP_")) {
  247. const std::string& directive =
  248. i.substr(cmStrLen("CPACK_INNOSETUP_SETUP_"));
  249. setupDirectives[directive] = GetOption(i);
  250. }
  251. }
  252. return true;
  253. }
  254. bool cmCPackInnoSetupGenerator::ProcessFiles()
  255. {
  256. std::map<std::string, std::string> customFileInstructions;
  257. if (IsSet("CPACK_INNOSETUP_CUSTOM_INSTALL_INSTRUCTIONS")) {
  258. const cmList instructions(
  259. GetOption("CPACK_INNOSETUP_CUSTOM_INSTALL_INSTRUCTIONS"));
  260. if (instructions.size() % 2 != 0) {
  261. cmCPackLogger(cmCPackLog::LOG_ERROR,
  262. "CPACK_INNOSETUP_CUSTOM_INSTALL_INSTRUCTIONS should "
  263. "contain pairs of <path> and <instruction>"
  264. << std::endl);
  265. return false;
  266. }
  267. for (auto it = instructions.begin(); it != instructions.end(); ++it) {
  268. const std::string& key =
  269. QuotePath(cmSystemTools::CollapseFullPath(*it, toplevel));
  270. customFileInstructions[key] = *(++it);
  271. }
  272. }
  273. const std::string& iconsPrefix =
  274. toplevelProgramFolder ? "{autoprograms}\\" : "{group}\\";
  275. std::map<std::string, std::string> icons;
  276. if (IsSet("CPACK_PACKAGE_EXECUTABLES")) {
  277. const cmList executables(GetOption("CPACK_PACKAGE_EXECUTABLES"));
  278. if (executables.size() % 2 != 0) {
  279. cmCPackLogger(cmCPackLog::LOG_ERROR,
  280. "CPACK_PACKAGE_EXECUTABLES should should contain pairs of "
  281. "<executable> and <text label>"
  282. << std::endl);
  283. return false;
  284. }
  285. for (auto it = executables.begin(); it != executables.end(); ++it) {
  286. const std::string& key = *it;
  287. icons[key] = *(++it);
  288. }
  289. }
  290. std::vector<std::string> desktopIcons;
  291. if (IsSet("CPACK_CREATE_DESKTOP_LINKS")) {
  292. cmExpandList(GetOption("CPACK_CREATE_DESKTOP_LINKS"), desktopIcons);
  293. }
  294. std::vector<std::string> runExecutables;
  295. if (IsSet("CPACK_INNOSETUP_RUN_EXECUTABLES")) {
  296. cmExpandList(GetOption("CPACK_INNOSETUP_RUN_EXECUTABLES"), runExecutables);
  297. }
  298. for (const std::string& i : files) {
  299. cmCPackInnoSetupKeyValuePairs params;
  300. std::string toplevelDirectory;
  301. std::string outputDir;
  302. cmCPackComponent* component = nullptr;
  303. std::string componentParam;
  304. if (!Components.empty()) {
  305. const std::string& fileName = cmSystemTools::RelativePath(toplevel, i);
  306. const std::string::size_type pos = fileName.find('/');
  307. // Use the custom component install directory if we have one
  308. if (pos != std::string::npos) {
  309. const std::string& componentName = fileName.substr(0, pos);
  310. component = &Components[componentName];
  311. toplevelDirectory =
  312. cmSystemTools::CollapseFullPath(componentName, toplevel);
  313. outputDir = CustomComponentInstallDirectory(component);
  314. componentParam =
  315. CreateRecursiveComponentPath(component->Group, component->Name);
  316. if (component->IsHidden && component->IsDisabledByDefault) {
  317. continue;
  318. }
  319. if (component->IsHidden) {
  320. componentParam.clear();
  321. }
  322. } else {
  323. // Don't install component directories
  324. continue;
  325. }
  326. } else {
  327. toplevelDirectory = toplevel;
  328. outputDir = "{app}";
  329. }
  330. if (!componentParam.empty()) {
  331. params["Components"] = componentParam;
  332. }
  333. if (cmSystemTools::FileIsDirectory(i)) {
  334. // Custom instructions replace the automatic generated instructions
  335. if (customFileInstructions.count(QuotePath(i))) {
  336. dirInstructions.push_back(customFileInstructions[QuotePath(i)]);
  337. } else {
  338. std::string destDir = cmSystemTools::ConvertToWindowsOutputPath(
  339. cmStrCat(outputDir, '\\',
  340. cmSystemTools::RelativePath(toplevelDirectory, i)));
  341. cmStripSuffixIfExists(destDir, '\\');
  342. params["Name"] = QuotePath(destDir);
  343. dirInstructions.push_back(ISKeyValueLine(params));
  344. }
  345. } else {
  346. // Custom instructions replace the automatic generated instructions
  347. if (customFileInstructions.count(QuotePath(i))) {
  348. fileInstructions.push_back(customFileInstructions[QuotePath(i)]);
  349. } else {
  350. std::string destDir = cmSystemTools::ConvertToWindowsOutputPath(
  351. cmStrCat(outputDir, '\\',
  352. cmSystemTools::GetParentDirectory(
  353. cmSystemTools::RelativePath(toplevelDirectory, i))));
  354. cmStripSuffixIfExists(destDir, '\\');
  355. params["DestDir"] = QuotePath(destDir);
  356. if (component && component->IsDownloaded) {
  357. const std::string& archiveName =
  358. cmSystemTools::GetFilenameWithoutLastExtension(
  359. component->ArchiveFile);
  360. const std::string& relativePath =
  361. cmSystemTools::RelativePath(toplevelDirectory, i);
  362. params["Source"] =
  363. QuotePath(cmStrCat("{tmp}\\", archiveName, '\\', relativePath));
  364. params["ExternalSize"] =
  365. std::to_string(cmSystemTools::FileLength(i));
  366. params["Flags"] = "external ignoreversion";
  367. params["BeforeInstall"] =
  368. cmStrCat("CPackExtractFile('", archiveName, "', '",
  369. cmRemoveQuotes(cmSystemTools::ConvertToWindowsOutputPath(
  370. relativePath)),
  371. "')");
  372. } else {
  373. params["Source"] = QuotePath(i);
  374. params["Flags"] = "ignoreversion";
  375. }
  376. fileInstructions.push_back(ISKeyValueLine(params));
  377. // Icon
  378. const std::string& name =
  379. cmSystemTools::GetFilenameWithoutLastExtension(i);
  380. const std::string& extension =
  381. cmSystemTools::GetFilenameLastExtension(i);
  382. if ((extension == ".exe" || extension == ".com") && // only .exe, .com
  383. icons.count(name)) {
  384. cmCPackInnoSetupKeyValuePairs iconParams;
  385. iconParams["Name"] = QuotePath(cmStrCat(iconsPrefix, icons[name]));
  386. iconParams["Filename"] =
  387. QuotePath(cmStrCat(destDir, '\\', name, extension));
  388. if (!componentParam.empty()) {
  389. iconParams["Components"] = componentParam;
  390. }
  391. iconInstructions.push_back(ISKeyValueLine(iconParams));
  392. // Desktop icon
  393. if (std::find(desktopIcons.begin(), desktopIcons.end(), name) !=
  394. desktopIcons.end()) {
  395. iconParams["Name"] =
  396. QuotePath(cmStrCat("{autodesktop}\\", icons[name]));
  397. iconParams["Tasks"] = "desktopicon";
  398. if (!componentParam.empty() &&
  399. std::find(desktopIconComponents.begin(),
  400. desktopIconComponents.end(),
  401. componentParam) == desktopIconComponents.end()) {
  402. desktopIconComponents.push_back(componentParam);
  403. }
  404. iconInstructions.push_back(ISKeyValueLine(iconParams));
  405. }
  406. // [Run] section
  407. if (std::find(runExecutables.begin(), runExecutables.end(), name) !=
  408. runExecutables.end()) {
  409. cmCPackInnoSetupKeyValuePairs runParams;
  410. runParams["Filename"] = iconParams["Filename"];
  411. runParams["Description"] = cmStrCat(
  412. "\"{cm:LaunchProgram,", PrepareForConstant(icons[name]), "}\"");
  413. runParams["Flags"] = "nowait postinstall skipifsilent";
  414. if (!componentParam.empty()) {
  415. runParams["Components"] = componentParam;
  416. }
  417. runInstructions.push_back(ISKeyValueLine(runParams));
  418. }
  419. }
  420. }
  421. }
  422. }
  423. // Additional icons
  424. static cmsys::RegularExpression urlRegex(
  425. "^(mailto:|(ftps?|https?|news)://).*$");
  426. if (IsSet("CPACK_INNOSETUP_MENU_LINKS")) {
  427. const cmList menuIcons(GetOption("CPACK_INNOSETUP_MENU_LINKS"));
  428. if (menuIcons.size() % 2 != 0) {
  429. cmCPackLogger(cmCPackLog::LOG_ERROR,
  430. "CPACK_INNOSETUP_MENU_LINKS should "
  431. "contain pairs of <shortcut target> and <shortcut label>"
  432. << std::endl);
  433. return false;
  434. }
  435. for (auto it = menuIcons.begin(); it != menuIcons.end(); ++it) {
  436. const std::string& target = *it;
  437. const std::string& label = *(++it);
  438. cmCPackInnoSetupKeyValuePairs params;
  439. params["Name"] = QuotePath(cmStrCat(iconsPrefix, label));
  440. if (urlRegex.find(target)) {
  441. params["Filename"] = Quote(target);
  442. } else {
  443. std::string dir = "{app}";
  444. std::string componentName;
  445. for (const auto& i : Components) {
  446. if (cmSystemTools::FileExists(cmSystemTools::CollapseFullPath(
  447. cmStrCat(i.second.Name, '\\', target), toplevel))) {
  448. dir = CustomComponentInstallDirectory(&i.second);
  449. componentName =
  450. CreateRecursiveComponentPath(i.second.Group, i.second.Name);
  451. if (i.second.IsHidden && i.second.IsDisabledByDefault) {
  452. goto continueOuterLoop;
  453. } else if (i.second.IsHidden) {
  454. componentName.clear();
  455. }
  456. break;
  457. }
  458. }
  459. params["Filename"] = QuotePath(cmStrCat(dir, '\\', target));
  460. if (!componentName.empty()) {
  461. params["Components"] = componentName;
  462. }
  463. }
  464. iconInstructions.push_back(ISKeyValueLine(params));
  465. continueOuterLoop:;
  466. }
  467. }
  468. SetOptionIfNotSet("CPACK_INNOSETUP_CREATE_UNINSTALL_LINK", "OFF");
  469. if (GetOption("CPACK_INNOSETUP_CREATE_UNINSTALL_LINK").IsOn()) {
  470. cmCPackInnoSetupKeyValuePairs params;
  471. params["Name"] = QuotePath(
  472. cmStrCat(iconsPrefix, "{cm:UninstallProgram,",
  473. PrepareForConstant(GetOption("CPACK_PACKAGE_NAME")), '}'));
  474. params["Filename"] = "\"{uninstallexe}\"";
  475. iconInstructions.push_back(ISKeyValueLine(params));
  476. }
  477. return true;
  478. }
  479. bool cmCPackInnoSetupGenerator::ProcessComponents()
  480. {
  481. codeIncludes.emplace_back(
  482. "{ The following lines are required by CPack because "
  483. "this script uses components }");
  484. // Installation types
  485. std::vector<cmCPackInstallationType*> types(InstallationTypes.size());
  486. for (auto& i : InstallationTypes) {
  487. types[i.second.Index - 1] = &i.second;
  488. }
  489. std::vector<std::string> allTypes; // For required components
  490. for (cmCPackInstallationType* i : types) {
  491. cmCPackInnoSetupKeyValuePairs params;
  492. params["Name"] = Quote(i->Name);
  493. params["Description"] = Quote(i->DisplayName);
  494. allTypes.push_back(i->Name);
  495. typeInstructions.push_back(ISKeyValueLine(params));
  496. }
  497. // Inno Setup requires the additional "custom" type
  498. cmCPackInnoSetupKeyValuePairs customTypeParams;
  499. customTypeParams["Name"] = "\"custom\"";
  500. customTypeParams["Description"] =
  501. "\"{code:CPackGetCustomInstallationMessage}\"";
  502. customTypeParams["Flags"] = "iscustom";
  503. allTypes.emplace_back("custom");
  504. typeInstructions.push_back(ISKeyValueLine(customTypeParams));
  505. // Components
  506. std::vector<cmCPackComponent*> downloadedComponents;
  507. for (auto& i : Components) {
  508. cmCPackInnoSetupKeyValuePairs params;
  509. cmCPackComponent* component = &i.second;
  510. if (component->IsHidden) {
  511. continue;
  512. }
  513. CreateRecursiveComponentGroups(component->Group);
  514. params["Name"] =
  515. Quote(CreateRecursiveComponentPath(component->Group, component->Name));
  516. params["Description"] = Quote(component->DisplayName);
  517. if (component->IsRequired) {
  518. params["Types"] = cmJoin(allTypes, " ");
  519. params["Flags"] = "fixed";
  520. } else if (!component->InstallationTypes.empty()) {
  521. std::vector<std::string> installationTypes;
  522. installationTypes.reserve(component->InstallationTypes.size());
  523. for (cmCPackInstallationType* j : component->InstallationTypes) {
  524. installationTypes.push_back(j->Name);
  525. }
  526. params["Types"] = cmJoin(installationTypes, " ");
  527. }
  528. componentInstructions.push_back(ISKeyValueLine(params));
  529. if (component->IsDownloaded) {
  530. downloadedComponents.push_back(component);
  531. if (component->ArchiveFile.empty()) {
  532. // Compute the name of the archive.
  533. if (!RequireOption("CPACK_TEMPORARY_DIRECTORY")) {
  534. return false;
  535. }
  536. std::string packagesDir =
  537. cmStrCat(GetOption("CPACK_TEMPORARY_DIRECTORY"), ".dummy");
  538. component->ArchiveFile =
  539. cmStrCat(cmSystemTools::GetFilenameWithoutLastExtension(packagesDir),
  540. '-', component->Name, ".zip");
  541. } else if (!cmHasSuffix(component->ArchiveFile, ".zip")) {
  542. component->ArchiveFile = cmStrCat(component->ArchiveFile, ".zip");
  543. }
  544. }
  545. }
  546. // Downloaded components
  547. if (!downloadedComponents.empty()) {
  548. // Create the directory for the upload area
  549. cmValue userUploadDirectory = GetOption("CPACK_UPLOAD_DIRECTORY");
  550. std::string uploadDirectory;
  551. if (cmNonempty(userUploadDirectory)) {
  552. uploadDirectory = *userUploadDirectory;
  553. } else {
  554. if (!RequireOption("CPACK_PACKAGE_DIRECTORY")) {
  555. return false;
  556. }
  557. uploadDirectory =
  558. cmStrCat(GetOption("CPACK_PACKAGE_DIRECTORY"), "/CPackUploads");
  559. }
  560. if (!cmSystemTools::FileExists(uploadDirectory)) {
  561. if (!cmSystemTools::MakeDirectory(uploadDirectory)) {
  562. cmCPackLogger(cmCPackLog::LOG_ERROR,
  563. "Unable to create Inno Setup upload directory "
  564. << uploadDirectory << std::endl);
  565. return false;
  566. }
  567. }
  568. if (!RequireOption("CPACK_DOWNLOAD_SITE")) {
  569. return false;
  570. }
  571. SetOptionIfNotSet("CPACK_INNOSETUP_VERIFY_DOWNLOADS", "ON");
  572. const bool verifyDownloads =
  573. GetOption("CPACK_INNOSETUP_VERIFY_DOWNLOADS").IsOn();
  574. const std::string& urlPrefix =
  575. cmHasSuffix(GetOption("CPACK_DOWNLOAD_SITE").GetCStr(), '/')
  576. ? GetOption("CPACK_DOWNLOAD_SITE")
  577. : cmStrCat(GetOption("CPACK_DOWNLOAD_SITE"), '/');
  578. std::vector<std::string> archiveUrls;
  579. std::vector<std::string> archiveFiles;
  580. std::vector<std::string> archiveHashes;
  581. std::vector<std::string> archiveComponents;
  582. for (cmCPackComponent* i : downloadedComponents) {
  583. std::string hash;
  584. if (!BuildDownloadedComponentArchive(
  585. i, uploadDirectory, (verifyDownloads ? &hash : nullptr))) {
  586. return false;
  587. }
  588. archiveUrls.push_back(Quote(cmStrCat(urlPrefix, i->ArchiveFile)));
  589. archiveFiles.push_back(
  590. Quote(cmSystemTools::GetFilenameWithoutLastExtension(i->ArchiveFile)));
  591. archiveHashes.push_back(Quote(hash));
  592. archiveComponents.push_back(
  593. Quote(CreateRecursiveComponentPath(i->Group, i->Name)));
  594. }
  595. SetOption("CPACK_INNOSETUP_DOWNLOAD_COUNT_INTERNAL",
  596. std::to_string(archiveFiles.size()));
  597. SetOption("CPACK_INNOSETUP_DOWNLOAD_URLS_INTERNAL",
  598. cmJoin(archiveUrls, ", "));
  599. SetOption("CPACK_INNOSETUP_DOWNLOAD_ARCHIVES_INTERNAL",
  600. cmJoin(archiveFiles, ", "));
  601. SetOption("CPACK_INNOSETUP_DOWNLOAD_HASHES_INTERNAL",
  602. cmJoin(archiveHashes, ", "));
  603. SetOption("CPACK_INNOSETUP_DOWNLOAD_COMPONENTS_INTERNAL",
  604. cmJoin(archiveComponents, ", "));
  605. static const std::string& downloadLines =
  606. "#define protected CPackDownloadCount "
  607. "@CPACK_INNOSETUP_DOWNLOAD_COUNT_INTERNAL@\n"
  608. "#dim protected CPackDownloadUrls[CPackDownloadCount] "
  609. "{@CPACK_INNOSETUP_DOWNLOAD_URLS_INTERNAL@}\n"
  610. "#dim protected CPackDownloadArchives[CPackDownloadCount] "
  611. "{@CPACK_INNOSETUP_DOWNLOAD_ARCHIVES_INTERNAL@}\n"
  612. "#dim protected CPackDownloadHashes[CPackDownloadCount] "
  613. "{@CPACK_INNOSETUP_DOWNLOAD_HASHES_INTERNAL@}\n"
  614. "#dim protected CPackDownloadComponents[CPackDownloadCount] "
  615. "{@CPACK_INNOSETUP_DOWNLOAD_COMPONENTS_INTERNAL@}";
  616. std::string output;
  617. if (!ConfigureString(downloadLines, output)) {
  618. return false;
  619. }
  620. codeIncludes.push_back(output);
  621. }
  622. // Add the required script
  623. const std::string& componentsScriptTemplate =
  624. FindTemplate("ISComponents.pas");
  625. if (componentsScriptTemplate.empty()) {
  626. cmCPackLogger(cmCPackLog::LOG_ERROR,
  627. "Could not find additional Inno Setup script file."
  628. << std::endl);
  629. return false;
  630. }
  631. codeIncludes.push_back("#include " + QuotePath(componentsScriptTemplate) +
  632. "\n");
  633. return true;
  634. }
  635. bool cmCPackInnoSetupGenerator::ConfigureISScript()
  636. {
  637. const std::string& isScriptTemplate = FindTemplate("ISScript.template.in");
  638. const std::string& isScriptFile =
  639. cmStrCat(GetOption("CPACK_TOPLEVEL_DIRECTORY"), "/ISScript.iss");
  640. if (isScriptTemplate.empty()) {
  641. cmCPackLogger(cmCPackLog::LOG_ERROR,
  642. "Could not find Inno Setup installer template file."
  643. << std::endl);
  644. return false;
  645. }
  646. // Create internal variables
  647. std::vector<std::string> setupSection;
  648. for (const auto& i : setupDirectives) {
  649. setupSection.emplace_back(cmStrCat(i.first, '=', TranslateBool(i.second)));
  650. }
  651. // Also create comments if the sections are empty
  652. const std::string& defaultMessage =
  653. "; CPack didn't find any entries for this section";
  654. if (IsSet("CPACK_CREATE_DESKTOP_LINKS") &&
  655. !GetOption("CPACK_CREATE_DESKTOP_LINKS").Get()->empty()) {
  656. cmCPackInnoSetupKeyValuePairs tasks;
  657. tasks["Name"] = "\"desktopicon\"";
  658. tasks["Description"] = "\"{cm:CreateDesktopIcon}\"";
  659. tasks["GroupDescription"] = "\"{cm:AdditionalIcons}\"";
  660. tasks["Flags"] = "unchecked";
  661. if (!desktopIconComponents.empty()) {
  662. tasks["Components"] = cmJoin(desktopIconComponents, " ");
  663. }
  664. SetOption("CPACK_INNOSETUP_TASKS_INTERNAL", ISKeyValueLine(tasks));
  665. } else {
  666. SetOption("CPACK_INNOSETUP_TASKS_INTERNAL", defaultMessage);
  667. }
  668. SetOption("CPACK_INNOSETUP_INCLUDES_INTERNAL",
  669. includeDirectives.empty() ? "; No extra script files specified"
  670. : cmJoin(includeDirectives, "\n"));
  671. SetOption("CPACK_INNOSETUP_SETUP_INTERNAL",
  672. setupSection.empty() ? defaultMessage
  673. : cmJoin(setupSection, "\n"));
  674. SetOption("CPACK_INNOSETUP_LANGUAGES_INTERNAL",
  675. languageInstructions.empty() ? defaultMessage
  676. : cmJoin(languageInstructions, "\n"));
  677. SetOption("CPACK_INNOSETUP_DIRS_INTERNAL",
  678. dirInstructions.empty() ? defaultMessage
  679. : cmJoin(dirInstructions, "\n"));
  680. SetOption("CPACK_INNOSETUP_FILES_INTERNAL",
  681. fileInstructions.empty() ? defaultMessage
  682. : cmJoin(fileInstructions, "\n"));
  683. SetOption("CPACK_INNOSETUP_TYPES_INTERNAL",
  684. typeInstructions.empty() ? defaultMessage
  685. : cmJoin(typeInstructions, "\n"));
  686. SetOption("CPACK_INNOSETUP_COMPONENTS_INTERNAL",
  687. componentInstructions.empty()
  688. ? defaultMessage
  689. : cmJoin(componentInstructions, "\n"));
  690. SetOption("CPACK_INNOSETUP_ICONS_INTERNAL",
  691. iconInstructions.empty() ? defaultMessage
  692. : cmJoin(iconInstructions, "\n"));
  693. SetOption("CPACK_INNOSETUP_RUN_INTERNAL",
  694. runInstructions.empty() ? defaultMessage
  695. : cmJoin(runInstructions, "\n"));
  696. SetOption("CPACK_INNOSETUP_CODE_INTERNAL",
  697. codeIncludes.empty() ? "{ No extra code files specified }"
  698. : cmJoin(codeIncludes, "\n"));
  699. cmCPackLogger(cmCPackLog::LOG_VERBOSE,
  700. "Configure file: " << isScriptTemplate << " to "
  701. << isScriptFile << std::endl);
  702. return ConfigureFile(isScriptTemplate, isScriptFile);
  703. }
  704. bool cmCPackInnoSetupGenerator::Compile()
  705. {
  706. const std::string& isScriptFile =
  707. cmStrCat(GetOption("CPACK_TOPLEVEL_DIRECTORY"), "/ISScript.iss");
  708. const std::string& isccLogFile =
  709. cmStrCat(GetOption("CPACK_TOPLEVEL_DIRECTORY"), "/ISCCOutput.log");
  710. std::vector<std::string> isccArgs;
  711. // Custom defines
  712. for (const std::string& i : GetOptions()) {
  713. if (cmHasPrefix(i, "CPACK_INNOSETUP_DEFINE_")) {
  714. const std::string& name = i.substr(cmStrLen("CPACK_INNOSETUP_DEFINE_"));
  715. isccArgs.push_back(
  716. cmStrCat("\"/D", name, '=', TranslateBool(GetOption(i)), '"'));
  717. }
  718. }
  719. if (IsSet("CPACK_INNOSETUP_EXECUTABLE_ARGUMENTS")) {
  720. const cmList args(GetOption("CPACK_INNOSETUP_EXECUTABLE_ARGUMENTS"));
  721. isccArgs.insert(isccArgs.end(), args.begin(), args.end());
  722. }
  723. const std::string& isccCmd =
  724. cmStrCat(QuotePath(GetOption("CPACK_INSTALLER_PROGRAM"), PathType::Native),
  725. ' ', cmJoin(isccArgs, " "), ' ', QuotePath(isScriptFile));
  726. cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << isccCmd << std::endl);
  727. std::string output;
  728. int retVal = 1;
  729. const bool res = cmSystemTools::RunSingleCommand(
  730. isccCmd, &output, &output, &retVal, nullptr, this->GeneratorVerbose,
  731. cmDuration::zero());
  732. if (!res || retVal) {
  733. cmGeneratedFileStream ofs(isccLogFile);
  734. ofs << "# Run command: " << isccCmd << std::endl
  735. << "# Output:" << std::endl
  736. << output << std::endl;
  737. cmCPackLogger(cmCPackLog::LOG_ERROR,
  738. "Problem running ISCC. Please check "
  739. << isccLogFile << " for errors." << std::endl);
  740. return false;
  741. }
  742. return true;
  743. }
  744. bool cmCPackInnoSetupGenerator::BuildDownloadedComponentArchive(
  745. cmCPackComponent* component, const std::string& uploadDirectory,
  746. std::string* hash)
  747. {
  748. // Remove the old archive, if one exists
  749. const std::string& archiveFile =
  750. uploadDirectory + '/' + component->ArchiveFile;
  751. cmCPackLogger(cmCPackLog::LOG_OUTPUT,
  752. "- Building downloaded component archive: " << archiveFile
  753. << std::endl);
  754. if (cmSystemTools::FileExists(archiveFile, true)) {
  755. if (!cmSystemTools::RemoveFile(archiveFile)) {
  756. cmCPackLogger(cmCPackLog::LOG_ERROR,
  757. "Unable to remove archive file " << archiveFile
  758. << std::endl);
  759. return false;
  760. }
  761. }
  762. // Find a ZIP program
  763. if (!IsSet("ZIP_EXECUTABLE")) {
  764. ReadListFile("Internal/CPack/CPackZIP.cmake");
  765. if (!IsSet("ZIP_EXECUTABLE")) {
  766. cmCPackLogger(cmCPackLog::LOG_ERROR,
  767. "Unable to find ZIP program" << std::endl);
  768. return false;
  769. }
  770. }
  771. if (!RequireOption("CPACK_TOPLEVEL_DIRECTORY") ||
  772. !RequireOption("CPACK_TEMPORARY_DIRECTORY") ||
  773. !RequireOption("CPACK_ZIP_NEED_QUOTES") ||
  774. !RequireOption("CPACK_ZIP_COMMAND")) {
  775. return false;
  776. }
  777. // The directory where this component's files reside
  778. const std::string& dirName =
  779. cmStrCat(GetOption("CPACK_TEMPORARY_DIRECTORY"), '/', component->Name);
  780. // Build the list of files to go into this archive
  781. const std::string& zipListFileName =
  782. cmStrCat(GetOption("CPACK_TEMPORARY_DIRECTORY"), "/winZip.filelist");
  783. const bool needQuotesInFile = GetOption("CPACK_ZIP_NEED_QUOTES").IsOn();
  784. { // the scope is needed for cmGeneratedFileStream
  785. cmGeneratedFileStream out(zipListFileName);
  786. for (const std::string& i : component->Files) {
  787. out << (needQuotesInFile ? Quote(i) : i) << std::endl;
  788. }
  789. }
  790. // Build the archive in the upload area
  791. std::string cmd = GetOption("CPACK_ZIP_COMMAND");
  792. cmsys::SystemTools::ReplaceString(cmd, "<ARCHIVE>", archiveFile.c_str());
  793. cmsys::SystemTools::ReplaceString(cmd, "<FILELIST>",
  794. zipListFileName.c_str());
  795. std::string output;
  796. int retVal = -1;
  797. const bool res = cmSystemTools::RunSingleCommand(
  798. cmd, &output, &output, &retVal, dirName.c_str(), this->GeneratorVerbose,
  799. cmDuration::zero());
  800. if (!res || retVal) {
  801. std::string tmpFile =
  802. cmStrCat(GetOption("CPACK_TOPLEVEL_DIRECTORY"), "/CompressZip.log");
  803. cmGeneratedFileStream ofs(tmpFile);
  804. ofs << "# Run command: " << cmd << std::endl
  805. << "# Output:" << std::endl
  806. << output << std::endl;
  807. cmCPackLogger(cmCPackLog::LOG_ERROR,
  808. "Problem running zip command: " << cmd << std::endl
  809. << "Please check " << tmpFile
  810. << " for errors"
  811. << std::endl);
  812. return false;
  813. }
  814. // Try to get the SHA256 hash of the archive file
  815. if (!hash) {
  816. return true;
  817. }
  818. #ifdef _WIN32
  819. const std::string& hashCmd =
  820. cmStrCat("certutil -hashfile ", QuotePath(archiveFile), " SHA256");
  821. std::string hashOutput;
  822. int hashRetVal = -1;
  823. const bool hashRes = cmSystemTools::RunSingleCommand(
  824. hashCmd, &hashOutput, &hashOutput, &hashRetVal, nullptr,
  825. this->GeneratorVerbose, cmDuration::zero());
  826. if (!hashRes || hashRetVal) {
  827. cmCPackLogger(cmCPackLog::LOG_WARNING,
  828. "Problem running certutil command: " << hashCmd
  829. << std::endl);
  830. }
  831. *hash = cmTrimWhitespace(cmTokenize(hashOutput, "\n").at(1));
  832. if (hash->length() != 64) {
  833. cmCPackLogger(cmCPackLog::LOG_WARNING,
  834. "Problem parsing certutil output of command: " << hashCmd
  835. << std::endl);
  836. hash->clear();
  837. }
  838. #endif
  839. return true;
  840. }
  841. cmValue cmCPackInnoSetupGenerator::RequireOption(const std::string& key)
  842. {
  843. cmValue value = GetOption(key);
  844. if (!value) {
  845. cmCPackLogger(cmCPackLog::LOG_ERROR,
  846. "Required variable " << key << " not set" << std::endl);
  847. }
  848. return value;
  849. }
  850. std::string cmCPackInnoSetupGenerator::CustomComponentInstallDirectory(
  851. const cmCPackComponent* component)
  852. {
  853. cmValue outputDir = GetOption(
  854. cmStrCat("CPACK_INNOSETUP_", component->Name, "_INSTALL_DIRECTORY"));
  855. if (outputDir) {
  856. std::string destDir = cmSystemTools::ConvertToWindowsOutputPath(outputDir);
  857. cmStripSuffixIfExists(destDir, '\\');
  858. /*
  859. * Add a dir instruction for the custom directory
  860. * (only once and not for Inno Setup constants ending with '}')
  861. */
  862. static std::vector<std::string> customDirectories;
  863. if (!cmHasSuffix(destDir, '}') &&
  864. std::find(customDirectories.begin(), customDirectories.end(),
  865. component->Name) == customDirectories.end()) {
  866. cmCPackInnoSetupKeyValuePairs params;
  867. params["Name"] = QuotePath(destDir);
  868. params["Components"] =
  869. CreateRecursiveComponentPath(component->Group, component->Name);
  870. dirInstructions.push_back(ISKeyValueLine(params));
  871. customDirectories.push_back(component->Name);
  872. }
  873. return destDir;
  874. }
  875. return "{app}";
  876. }
  877. std::string cmCPackInnoSetupGenerator::TranslateBool(const std::string& value)
  878. {
  879. if (value.empty()) {
  880. return value;
  881. }
  882. SetOptionIfNotSet("CPACK_INNOSETUP_USE_CMAKE_BOOL_FORMAT", "ON");
  883. if (GetOption("CPACK_INNOSETUP_USE_CMAKE_BOOL_FORMAT").IsOn()) {
  884. if (cmIsOn(value)) {
  885. return "yes";
  886. }
  887. if (cmIsOff(value)) {
  888. return "no";
  889. }
  890. }
  891. return value;
  892. }
  893. std::string cmCPackInnoSetupGenerator::ISKeyValueLine(
  894. const cmCPackInnoSetupKeyValuePairs& params)
  895. {
  896. /*
  897. * To simplify readability of the generated code, the keys are sorted.
  898. * Unknown keys are ignored to avoid errors during compilation.
  899. */
  900. static const char* const availableKeys[] = {
  901. "Source", "DestDir", "Name", "Filename",
  902. "Description", "GroupDescription", "MessagesFile", "Types",
  903. "ExternalSize", "BeforeInstall", "Flags", "Components",
  904. "Tasks"
  905. };
  906. std::vector<std::string> keys;
  907. for (const char* i : availableKeys) {
  908. if (params.count(i)) {
  909. keys.emplace_back(cmStrCat(i, ": ", params.at(i)));
  910. }
  911. }
  912. return cmJoin(keys, "; ");
  913. }
  914. std::string cmCPackInnoSetupGenerator::CreateRecursiveComponentPath(
  915. cmCPackComponentGroup* group, const std::string& path)
  916. {
  917. if (!group) {
  918. return path;
  919. }
  920. const std::string& newPath =
  921. path.empty() ? group->Name : cmStrCat(group->Name, '\\', path);
  922. return CreateRecursiveComponentPath(group->ParentGroup, newPath);
  923. }
  924. void cmCPackInnoSetupGenerator::CreateRecursiveComponentGroups(
  925. cmCPackComponentGroup* group)
  926. {
  927. if (!group) {
  928. return;
  929. }
  930. CreateRecursiveComponentGroups(group->ParentGroup);
  931. static std::vector<cmCPackComponentGroup*> processedGroups;
  932. if (std::find(processedGroups.begin(), processedGroups.end(), group) ==
  933. processedGroups.end()) {
  934. processedGroups.push_back(group);
  935. cmCPackInnoSetupKeyValuePairs params;
  936. params["Name"] = Quote(CreateRecursiveComponentPath(group));
  937. params["Description"] = Quote(group->DisplayName);
  938. componentInstructions.push_back(ISKeyValueLine(params));
  939. }
  940. }
  941. std::string cmCPackInnoSetupGenerator::Quote(const std::string& string)
  942. {
  943. if (cmHasPrefix(string, '"') && cmHasSuffix(string, '"')) {
  944. return Quote(string.substr(1, string.length() - 2));
  945. }
  946. // Double quote syntax
  947. std::string nString = string;
  948. cmSystemTools::ReplaceString(nString, "\"", "\"\"");
  949. return cmStrCat('"', nString, '"');
  950. }
  951. std::string cmCPackInnoSetupGenerator::QuotePath(const std::string& path,
  952. PathType type)
  953. {
  954. #ifdef _WIN32
  955. static_cast<void>(type);
  956. #else
  957. if (type == PathType::Native) {
  958. return Quote(cmSystemTools::ConvertToUnixOutputPath(path));
  959. }
  960. #endif
  961. return Quote(cmSystemTools::ConvertToWindowsOutputPath(path));
  962. }
  963. std::string cmCPackInnoSetupGenerator::PrepareForConstant(
  964. const std::string& string)
  965. {
  966. std::string nString = string;
  967. cmSystemTools::ReplaceString(nString, "%", "%25"); // First replacement!
  968. cmSystemTools::ReplaceString(nString, "\"", "%22");
  969. cmSystemTools::ReplaceString(nString, ",", "%2c");
  970. cmSystemTools::ReplaceString(nString, "|", "%7c");
  971. cmSystemTools::ReplaceString(nString, "}", "%7d");
  972. return nString;
  973. }