cmCPackInnoSetupGenerator.cxx 39 KB

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