1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
4 #include "cmCPackInnoSetupGenerator.h"
12 #include "cmsys/RegularExpression.hxx"
14 #include "cmCPackComponentGroup.h"
15 #include "cmCPackLog.h"
16 #include "cmDuration.h"
17 #include "cmGeneratedFileStream.h"
19 #include "cmStringAlgorithms.h"
20 #include "cmSystemTools.h"
22 cmCPackInnoSetupGenerator::cmCPackInnoSetupGenerator() = default;
23 cmCPackInnoSetupGenerator::~cmCPackInnoSetupGenerator() = default;
25 bool cmCPackInnoSetupGenerator::CanGenerate()
30 int cmCPackInnoSetupGenerator::InitializeInternal()
32 if (GetOption("CPACK_INCLUDE_TOPLEVEL_DIRECTORY").IsOn()) {
33 cmCPackLogger(cmCPackLog::LOG_WARNING
,
34 "Inno Setup Generator cannot work with "
35 "CPACK_INCLUDE_TOPLEVEL_DIRECTORY set. "
36 "This option will be reset to 0 (for this generator only)."
38 SetOption("CPACK_INCLUDE_TOPLEVEL_DIRECTORY", nullptr);
41 std::vector
<std::string
> path
;
44 path
.push_back("C:\\Program Files (x86)\\Inno Setup 5");
45 path
.push_back("C:\\Program Files (x86)\\Inno Setup 6");
48 SetOptionIfNotSet("CPACK_INNOSETUP_EXECUTABLE", "ISCC");
49 const std::string
& isccPath
= cmSystemTools::FindProgram(
50 GetOption("CPACK_INNOSETUP_EXECUTABLE"), path
, false);
52 if (isccPath
.empty()) {
53 cmCPackLogger(cmCPackLog::LOG_ERROR
,
54 "Cannot find Inno Setup compiler ISCC: "
55 "likely it is not installed, or not in your PATH"
61 const std::string isccCmd
=
62 cmStrCat(QuotePath(isccPath
, PathType::Native
), "/?");
63 cmCPackLogger(cmCPackLog::LOG_VERBOSE
,
64 "Test Inno Setup version: " << isccCmd
<< std::endl
);
66 cmSystemTools::RunSingleCommand(isccCmd
, &output
, &output
, nullptr, nullptr,
67 this->GeneratorVerbose
, cmDuration::zero());
68 cmsys::RegularExpression
vRex("Inno Setup ([0-9]+)");
69 if (!vRex
.find(output
)) {
70 cmCPackLogger(cmCPackLog::LOG_ERROR
,
71 "Problem checking Inno Setup version with command: "
72 << isccCmd
<< std::endl
73 << "Have you downloaded Inno Setup from "
74 "https://jrsoftware.org/isinfo.php?"
79 const int isccVersion
= atoi(vRex
.match(1).c_str());
80 const int minIsccVersion
= 6;
81 cmCPackLogger(cmCPackLog::LOG_DEBUG
,
82 "Inno Setup Version: " << isccVersion
<< std::endl
);
84 if (isccVersion
< minIsccVersion
) {
85 cmCPackLogger(cmCPackLog::LOG_ERROR
,
86 "CPack requires Inno Setup Version 6 or greater. "
87 "Inno Setup found on the system was: "
88 << isccVersion
<< std::endl
);
92 SetOption("CPACK_INSTALLER_PROGRAM", isccPath
);
94 return this->Superclass::InitializeInternal();
97 int cmCPackInnoSetupGenerator::PackageFiles()
100 if (IsSet("CPACK_INNOSETUP_EXTRA_SCRIPTS")) {
101 const cmList
extraScripts(GetOption("CPACK_INNOSETUP_EXTRA_SCRIPTS"));
103 for (const std::string
& i
: extraScripts
) {
104 includeDirectives
.emplace_back(cmStrCat(
105 "#include ", QuotePath(cmSystemTools::CollapseFullPath(i
, toplevel
))));
109 // [Languages] section
110 SetOptionIfNotSet("CPACK_INNOSETUP_LANGUAGES", "english");
111 const cmList
languages(GetOption("CPACK_INNOSETUP_LANGUAGES"));
112 for (std::string i
: languages
) {
113 cmCPackInnoSetupKeyValuePairs params
;
115 params
["Name"] = Quote(i
);
117 if (cmSystemTools::LowerCase(i
) == "english") {
118 params
["MessagesFile"] = "\"compiler:Default.isl\"";
120 i
[0] = static_cast<char>(std::toupper(i
[0]));
121 params
["MessagesFile"] = cmStrCat("\"compiler:Languages\\", i
, ".isl\"");
124 languageInstructions
.push_back(ISKeyValueLine(params
));
127 if (!Components
.empty() && !ProcessComponents()) {
131 if (!(ProcessSetupSection() && ProcessFiles())) {
136 if (IsSet("CPACK_INNOSETUP_CODE_FILES")) {
137 const cmList
codeFiles(GetOption("CPACK_INNOSETUP_CODE_FILES"));
139 for (const std::string
& i
: codeFiles
) {
140 codeIncludes
.emplace_back(cmStrCat(
141 "#include ", QuotePath(cmSystemTools::CollapseFullPath(i
, toplevel
))));
145 return ConfigureISScript() && Compile();
148 bool cmCPackInnoSetupGenerator::ProcessSetupSection()
150 if (!RequireOption("CPACK_PACKAGE_INSTALL_REGISTRY_KEY")) {
153 setupDirectives
["AppId"] = GetOption("CPACK_PACKAGE_INSTALL_REGISTRY_KEY");
155 if (!RequireOption("CPACK_PACKAGE_NAME")) {
158 setupDirectives
["AppName"] = GetOption("CPACK_PACKAGE_NAME");
159 setupDirectives
["UninstallDisplayName"] = GetOption("CPACK_PACKAGE_NAME");
161 if (!RequireOption("CPACK_PACKAGE_VERSION")) {
164 setupDirectives
["AppVersion"] = GetOption("CPACK_PACKAGE_VERSION");
166 if (!RequireOption("CPACK_PACKAGE_VENDOR")) {
169 setupDirectives
["AppPublisher"] = GetOption("CPACK_PACKAGE_VENDOR");
171 if (IsSet("CPACK_PACKAGE_HOMEPAGE_URL")) {
172 setupDirectives
["AppPublisherURL"] =
173 GetOption("CPACK_PACKAGE_HOMEPAGE_URL");
174 setupDirectives
["AppSupportURL"] = GetOption("CPACK_PACKAGE_HOMEPAGE_URL");
175 setupDirectives
["AppUpdatesURL"] = GetOption("CPACK_PACKAGE_HOMEPAGE_URL");
178 SetOptionIfNotSet("CPACK_INNOSETUP_IGNORE_LICENSE_PAGE", "OFF");
179 if (IsSet("CPACK_RESOURCE_FILE_LICENSE") &&
180 !GetOption("CPACK_INNOSETUP_IGNORE_LICENSE_PAGE").IsOn()) {
181 setupDirectives
["LicenseFile"] = cmSystemTools::ConvertToWindowsOutputPath(
182 GetOption("CPACK_RESOURCE_FILE_LICENSE"));
185 SetOptionIfNotSet("CPACK_INNOSETUP_IGNORE_README_PAGE", "ON");
186 if (IsSet("CPACK_RESOURCE_FILE_README") &&
187 !GetOption("CPACK_INNOSETUP_IGNORE_README_PAGE").IsOn()) {
188 setupDirectives
["InfoBeforeFile"] =
189 cmSystemTools::ConvertToWindowsOutputPath(
190 GetOption("CPACK_RESOURCE_FILE_README"));
193 SetOptionIfNotSet("CPACK_INNOSETUP_USE_MODERN_WIZARD", "OFF");
194 if (GetOption("CPACK_INNOSETUP_USE_MODERN_WIZARD").IsOn()) {
195 setupDirectives
["WizardStyle"] = "modern";
197 setupDirectives
["WizardStyle"] = "classic";
198 setupDirectives
["WizardSmallImageFile"] =
199 "compiler:WizClassicSmallImage.bmp";
200 setupDirectives
["WizardImageFile"] = "compiler:WizClassicImage.bmp";
201 setupDirectives
["SetupIconFile"] = "compiler:SetupClassicIcon.ico";
204 if (IsSet("CPACK_INNOSETUP_ICON_FILE")) {
205 setupDirectives
["SetupIconFile"] =
206 cmSystemTools::ConvertToWindowsOutputPath(
207 GetOption("CPACK_INNOSETUP_ICON_FILE"));
210 if (IsSet("CPACK_PACKAGE_ICON")) {
211 setupDirectives
["WizardSmallImageFile"] =
212 cmSystemTools::ConvertToWindowsOutputPath(
213 GetOption("CPACK_PACKAGE_ICON"));
216 if (!RequireOption("CPACK_PACKAGE_INSTALL_DIRECTORY")) {
219 SetOptionIfNotSet("CPACK_INNOSETUP_INSTALL_ROOT", "{autopf}");
220 setupDirectives
["DefaultDirName"] =
221 cmSystemTools::ConvertToWindowsOutputPath(
222 cmStrCat(GetOption("CPACK_INNOSETUP_INSTALL_ROOT"), '\\',
223 GetOption("CPACK_PACKAGE_INSTALL_DIRECTORY")));
225 SetOptionIfNotSet("CPACK_INNOSETUP_ALLOW_CUSTOM_DIRECTORY", "ON");
226 if (GetOption("CPACK_INNOSETUP_ALLOW_CUSTOM_DIRECTORY").IsOff()) {
227 setupDirectives
["DisableDirPage"] = "yes";
230 SetOptionIfNotSet("CPACK_INNOSETUP_PROGRAM_MENU_FOLDER",
231 GetOption("CPACK_PACKAGE_NAME"));
232 if (GetOption("CPACK_INNOSETUP_PROGRAM_MENU_FOLDER") == ".") {
233 setupDirectives
["DisableProgramGroupPage"] = "yes";
234 toplevelProgramFolder
= true;
236 setupDirectives
["DefaultGroupName"] =
237 GetOption("CPACK_INNOSETUP_PROGRAM_MENU_FOLDER");
238 toplevelProgramFolder
= false;
241 if (IsSet("CPACK_INNOSETUP_PASSWORD")) {
242 setupDirectives
["Password"] = GetOption("CPACK_INNOSETUP_PASSWORD");
243 setupDirectives
["Encryption"] = "yes";
247 * These directives can only be modified using the
248 * CPACK_INNOSETUP_SETUP_<directive> variables
250 setupDirectives
["ShowLanguageDialog"] = "auto";
251 setupDirectives
["AllowNoIcons"] = "yes";
252 setupDirectives
["Compression"] = "lzma";
253 setupDirectives
["SolidCompression"] = "yes";
255 // Output file and directory
256 if (!RequireOption("CPACK_PACKAGE_FILE_NAME")) {
259 setupDirectives
["OutputBaseFilename"] = GetOption("CPACK_PACKAGE_FILE_NAME");
261 if (!RequireOption("CPACK_TOPLEVEL_DIRECTORY")) {
264 setupDirectives
["OutputDir"] = cmSystemTools::ConvertToWindowsOutputPath(
265 GetOption("CPACK_TOPLEVEL_DIRECTORY"));
267 setupDirectives
["SourceDir"] =
268 cmSystemTools::ConvertToWindowsOutputPath(toplevel
);
270 // Target architecture
271 if (!RequireOption("CPACK_INNOSETUP_ARCHITECTURE")) {
275 cmValue
const architecture
= GetOption("CPACK_INNOSETUP_ARCHITECTURE");
276 if (architecture
!= "x86" && architecture
!= "x64" &&
277 architecture
!= "arm64" && architecture
!= "ia64") {
278 cmCPackLogger(cmCPackLog::LOG_ERROR
,
279 "CPACK_INNOSETUP_ARCHITECTURE must be either x86, x64, "
285 // The following directives must not be set to target x86
286 if (architecture
!= "x86") {
287 setupDirectives
["ArchitecturesAllowed"] = architecture
;
288 setupDirectives
["ArchitecturesInstallIn64BitMode"] = architecture
;
292 * Handle custom directives (they have higher priority than other variables,
293 * so they have to be processed after all other variables)
295 for (const std::string
& i
: GetOptions()) {
296 if (cmHasPrefix(i
, "CPACK_INNOSETUP_SETUP_")) {
297 const std::string
& directive
=
298 i
.substr(cmStrLen("CPACK_INNOSETUP_SETUP_"));
299 setupDirectives
[directive
] = GetOption(i
);
306 bool cmCPackInnoSetupGenerator::ProcessFiles()
308 std::map
<std::string
, std::string
> customFileInstructions
;
309 if (IsSet("CPACK_INNOSETUP_CUSTOM_INSTALL_INSTRUCTIONS")) {
310 const cmList
instructions(
311 GetOption("CPACK_INNOSETUP_CUSTOM_INSTALL_INSTRUCTIONS"));
312 if (instructions
.size() % 2 != 0) {
313 cmCPackLogger(cmCPackLog::LOG_ERROR
,
314 "CPACK_INNOSETUP_CUSTOM_INSTALL_INSTRUCTIONS should "
315 "contain pairs of <path> and <instruction>"
320 for (auto it
= instructions
.begin(); it
!= instructions
.end(); ++it
) {
321 const std::string
& key
=
322 QuotePath(cmSystemTools::CollapseFullPath(*it
, toplevel
));
323 customFileInstructions
[key
] = *(++it
);
327 const std::string
& iconsPrefix
=
328 toplevelProgramFolder
? "{autoprograms}\\" : "{group}\\";
330 std::map
<std::string
, std::string
> icons
;
331 if (IsSet("CPACK_PACKAGE_EXECUTABLES")) {
332 const cmList
executables(GetOption("CPACK_PACKAGE_EXECUTABLES"));
333 if (executables
.size() % 2 != 0) {
334 cmCPackLogger(cmCPackLog::LOG_ERROR
,
335 "CPACK_PACKAGE_EXECUTABLES should should contain pairs of "
336 "<executable> and <text label>"
341 for (auto it
= executables
.begin(); it
!= executables
.end(); ++it
) {
342 const std::string
& key
= *it
;
343 icons
[key
] = *(++it
);
347 std::vector
<std::string
> desktopIcons
;
348 if (IsSet("CPACK_CREATE_DESKTOP_LINKS")) {
349 cmExpandList(GetOption("CPACK_CREATE_DESKTOP_LINKS"), desktopIcons
);
352 std::vector
<std::string
> runExecutables
;
353 if (IsSet("CPACK_INNOSETUP_RUN_EXECUTABLES")) {
354 cmExpandList(GetOption("CPACK_INNOSETUP_RUN_EXECUTABLES"), runExecutables
);
357 for (const std::string
& i
: files
) {
358 cmCPackInnoSetupKeyValuePairs params
;
360 std::string toplevelDirectory
;
361 std::string outputDir
;
362 cmCPackComponent
* component
= nullptr;
363 std::string componentParam
;
364 if (!Components
.empty()) {
365 const std::string
& fileName
= cmSystemTools::RelativePath(toplevel
, i
);
366 const std::string::size_type pos
= fileName
.find('/');
368 // Use the custom component install directory if we have one
369 if (pos
!= std::string::npos
) {
370 const std::string
& componentName
= fileName
.substr(0, pos
);
371 component
= &Components
[componentName
];
374 cmSystemTools::CollapseFullPath(componentName
, toplevel
);
375 outputDir
= CustomComponentInstallDirectory(component
);
377 CreateRecursiveComponentPath(component
->Group
, component
->Name
);
379 if (component
->IsHidden
&& component
->IsDisabledByDefault
) {
383 if (component
->IsHidden
) {
384 componentParam
.clear();
387 // Don't install component directories
391 toplevelDirectory
= toplevel
;
395 if (!componentParam
.empty()) {
396 params
["Components"] = componentParam
;
399 if (cmSystemTools::FileIsDirectory(i
)) {
400 // Custom instructions replace the automatic generated instructions
401 if (customFileInstructions
.count(QuotePath(i
))) {
402 dirInstructions
.push_back(customFileInstructions
[QuotePath(i
)]);
404 std::string destDir
= cmSystemTools::ConvertToWindowsOutputPath(
405 cmStrCat(outputDir
, '\\',
406 cmSystemTools::RelativePath(toplevelDirectory
, i
)));
407 cmStripSuffixIfExists(destDir
, '\\');
409 params
["Name"] = QuotePath(destDir
);
411 dirInstructions
.push_back(ISKeyValueLine(params
));
414 // Custom instructions replace the automatic generated instructions
415 if (customFileInstructions
.count(QuotePath(i
))) {
416 fileInstructions
.push_back(customFileInstructions
[QuotePath(i
)]);
418 std::string destDir
= cmSystemTools::ConvertToWindowsOutputPath(
419 cmStrCat(outputDir
, '\\',
420 cmSystemTools::GetParentDirectory(
421 cmSystemTools::RelativePath(toplevelDirectory
, i
))));
422 cmStripSuffixIfExists(destDir
, '\\');
424 params
["DestDir"] = QuotePath(destDir
);
426 if (component
!= nullptr && component
->IsDownloaded
) {
427 const std::string
& archiveName
=
428 cmSystemTools::GetFilenameWithoutLastExtension(
429 component
->ArchiveFile
);
430 const std::string
& relativePath
=
431 cmSystemTools::RelativePath(toplevelDirectory
, i
);
434 QuotePath(cmStrCat("{tmp}\\", archiveName
, '\\', relativePath
));
435 params
["ExternalSize"] =
436 std::to_string(cmSystemTools::FileLength(i
));
437 params
["Flags"] = "external ignoreversion";
438 params
["BeforeInstall"] =
439 cmStrCat("CPackExtractFile('", archiveName
, "', '",
440 cmRemoveQuotes(cmSystemTools::ConvertToWindowsOutputPath(
444 params
["Source"] = QuotePath(i
);
445 params
["Flags"] = "ignoreversion";
448 fileInstructions
.push_back(ISKeyValueLine(params
));
451 const std::string
& name
=
452 cmSystemTools::GetFilenameWithoutLastExtension(i
);
453 const std::string
& extension
=
454 cmSystemTools::GetFilenameLastExtension(i
);
455 if ((extension
== ".exe" || extension
== ".com") && // only .exe, .com
457 cmCPackInnoSetupKeyValuePairs iconParams
;
459 iconParams
["Name"] = QuotePath(cmStrCat(iconsPrefix
, icons
[name
]));
460 iconParams
["Filename"] =
461 QuotePath(cmStrCat(destDir
, '\\', name
, extension
));
463 if (!componentParam
.empty()) {
464 iconParams
["Components"] = componentParam
;
467 iconInstructions
.push_back(ISKeyValueLine(iconParams
));
470 if (std::find(desktopIcons
.begin(), desktopIcons
.end(), name
) !=
471 desktopIcons
.end()) {
473 QuotePath(cmStrCat("{autodesktop}\\", icons
[name
]));
474 iconParams
["Tasks"] = "desktopicon";
476 if (!componentParam
.empty() &&
477 std::find(desktopIconComponents
.begin(),
478 desktopIconComponents
.end(),
479 componentParam
) == desktopIconComponents
.end()) {
480 desktopIconComponents
.push_back(componentParam
);
482 iconInstructions
.push_back(ISKeyValueLine(iconParams
));
486 if (std::find(runExecutables
.begin(), runExecutables
.end(), name
) !=
487 runExecutables
.end()) {
488 cmCPackInnoSetupKeyValuePairs runParams
;
490 runParams
["Filename"] = iconParams
["Filename"];
491 runParams
["Description"] = cmStrCat(
492 "\"{cm:LaunchProgram,", PrepareForConstant(icons
[name
]), "}\"");
493 runParams
["Flags"] = "nowait postinstall skipifsilent";
495 if (!componentParam
.empty()) {
496 runParams
["Components"] = componentParam
;
499 runInstructions
.push_back(ISKeyValueLine(runParams
));
507 static cmsys::RegularExpression
urlRegex(
508 "^(mailto:|(ftps?|https?|news)://).*$");
510 if (IsSet("CPACK_INNOSETUP_MENU_LINKS")) {
511 const cmList
menuIcons(GetOption("CPACK_INNOSETUP_MENU_LINKS"));
512 if (menuIcons
.size() % 2 != 0) {
513 cmCPackLogger(cmCPackLog::LOG_ERROR
,
514 "CPACK_INNOSETUP_MENU_LINKS should "
515 "contain pairs of <shortcut target> and <shortcut label>"
520 for (auto it
= menuIcons
.begin(); it
!= menuIcons
.end(); ++it
) {
521 const std::string
& target
= *it
;
522 const std::string
& label
= *(++it
);
523 cmCPackInnoSetupKeyValuePairs params
;
525 params
["Name"] = QuotePath(cmStrCat(iconsPrefix
, label
));
526 if (urlRegex
.find(target
)) {
527 params
["Filename"] = Quote(target
);
529 std::string dir
= "{app}";
530 std::string componentName
;
531 for (const auto& i
: Components
) {
532 if (cmSystemTools::FileExists(cmSystemTools::CollapseFullPath(
533 cmStrCat(i
.second
.Name
, '\\', target
), toplevel
))) {
534 dir
= CustomComponentInstallDirectory(&i
.second
);
536 CreateRecursiveComponentPath(i
.second
.Group
, i
.second
.Name
);
538 if (i
.second
.IsHidden
&& i
.second
.IsDisabledByDefault
) {
539 goto continueOuterLoop
;
540 } else if (i
.second
.IsHidden
) {
541 componentName
.clear();
548 params
["Filename"] = QuotePath(cmStrCat(dir
, '\\', target
));
550 if (!componentName
.empty()) {
551 params
["Components"] = componentName
;
555 iconInstructions
.push_back(ISKeyValueLine(params
));
560 SetOptionIfNotSet("CPACK_INNOSETUP_CREATE_UNINSTALL_LINK", "OFF");
561 if (GetOption("CPACK_INNOSETUP_CREATE_UNINSTALL_LINK").IsOn()) {
562 cmCPackInnoSetupKeyValuePairs params
;
564 params
["Name"] = QuotePath(
565 cmStrCat(iconsPrefix
, "{cm:UninstallProgram,",
566 PrepareForConstant(GetOption("CPACK_PACKAGE_NAME")), '}'));
567 params
["Filename"] = "\"{uninstallexe}\"";
569 iconInstructions
.push_back(ISKeyValueLine(params
));
575 bool cmCPackInnoSetupGenerator::ProcessComponents()
577 codeIncludes
.emplace_back(
578 "{ The following lines are required by CPack because "
579 "this script uses components }");
581 // Installation types
582 std::vector
<cmCPackInstallationType
*> types(InstallationTypes
.size());
583 for (auto& i
: InstallationTypes
) {
584 types
[i
.second
.Index
- 1] = &i
.second
;
587 std::vector
<std::string
> allTypes
; // For required components
588 for (cmCPackInstallationType
* i
: types
) {
589 cmCPackInnoSetupKeyValuePairs params
;
591 params
["Name"] = Quote(i
->Name
);
592 params
["Description"] = Quote(i
->DisplayName
);
594 allTypes
.push_back(i
->Name
);
595 typeInstructions
.push_back(ISKeyValueLine(params
));
598 // Inno Setup requires the additional "custom" type
599 cmCPackInnoSetupKeyValuePairs customTypeParams
;
601 customTypeParams
["Name"] = "\"custom\"";
602 customTypeParams
["Description"] =
603 "\"{code:CPackGetCustomInstallationMessage}\"";
604 customTypeParams
["Flags"] = "iscustom";
606 allTypes
.emplace_back("custom");
607 typeInstructions
.push_back(ISKeyValueLine(customTypeParams
));
610 std::vector
<cmCPackComponent
*> downloadedComponents
;
611 for (auto& i
: Components
) {
612 cmCPackInnoSetupKeyValuePairs params
;
613 cmCPackComponent
* component
= &i
.second
;
615 if (component
->IsHidden
) {
619 CreateRecursiveComponentGroups(component
->Group
);
622 Quote(CreateRecursiveComponentPath(component
->Group
, component
->Name
));
623 params
["Description"] = Quote(component
->DisplayName
);
625 if (component
->IsRequired
) {
626 params
["Types"] = cmJoin(allTypes
, " ");
627 params
["Flags"] = "fixed";
628 } else if (!component
->InstallationTypes
.empty()) {
629 std::vector
<std::string
> installationTypes
;
631 installationTypes
.reserve(component
->InstallationTypes
.size());
632 for (cmCPackInstallationType
* j
: component
->InstallationTypes
) {
633 installationTypes
.push_back(j
->Name
);
636 params
["Types"] = cmJoin(installationTypes
, " ");
639 componentInstructions
.push_back(ISKeyValueLine(params
));
641 if (component
->IsDownloaded
) {
642 downloadedComponents
.push_back(component
);
644 if (component
->ArchiveFile
.empty()) {
645 // Compute the name of the archive.
646 if (!RequireOption("CPACK_TEMPORARY_DIRECTORY")) {
650 std::string packagesDir
=
651 cmStrCat(GetOption("CPACK_TEMPORARY_DIRECTORY"), ".dummy");
652 component
->ArchiveFile
=
653 cmStrCat(cmSystemTools::GetFilenameWithoutLastExtension(packagesDir
),
654 '-', component
->Name
, ".zip");
655 } else if (!cmHasSuffix(component
->ArchiveFile
, ".zip")) {
656 component
->ArchiveFile
= cmStrCat(component
->ArchiveFile
, ".zip");
661 // Downloaded components
662 if (!downloadedComponents
.empty()) {
663 // Create the directory for the upload area
664 cmValue userUploadDirectory
= GetOption("CPACK_UPLOAD_DIRECTORY");
665 std::string uploadDirectory
;
666 if (cmNonempty(userUploadDirectory
)) {
667 uploadDirectory
= *userUploadDirectory
;
669 if (!RequireOption("CPACK_PACKAGE_DIRECTORY")) {
674 cmStrCat(GetOption("CPACK_PACKAGE_DIRECTORY"), "/CPackUploads");
677 if (!cmSystemTools::FileExists(uploadDirectory
)) {
678 if (!cmSystemTools::MakeDirectory(uploadDirectory
)) {
679 cmCPackLogger(cmCPackLog::LOG_ERROR
,
680 "Unable to create Inno Setup upload directory "
681 << uploadDirectory
<< std::endl
);
686 if (!RequireOption("CPACK_DOWNLOAD_SITE")) {
690 SetOptionIfNotSet("CPACK_INNOSETUP_VERIFY_DOWNLOADS", "ON");
691 const bool verifyDownloads
=
692 GetOption("CPACK_INNOSETUP_VERIFY_DOWNLOADS").IsOn();
694 const std::string
& urlPrefix
=
695 cmHasSuffix(GetOption("CPACK_DOWNLOAD_SITE").GetCStr(), '/')
696 ? GetOption("CPACK_DOWNLOAD_SITE")
697 : cmStrCat(GetOption("CPACK_DOWNLOAD_SITE"), '/');
699 std::vector
<std::string
> archiveUrls
;
700 std::vector
<std::string
> archiveFiles
;
701 std::vector
<std::string
> archiveHashes
;
702 std::vector
<std::string
> archiveComponents
;
703 for (cmCPackComponent
* i
: downloadedComponents
) {
705 if (!BuildDownloadedComponentArchive(
706 i
, uploadDirectory
, (verifyDownloads
? &hash
: nullptr))) {
710 archiveUrls
.push_back(Quote(cmStrCat(urlPrefix
, i
->ArchiveFile
)));
711 archiveFiles
.push_back(
712 Quote(cmSystemTools::GetFilenameWithoutLastExtension(i
->ArchiveFile
)));
713 archiveHashes
.push_back(Quote(hash
));
714 archiveComponents
.push_back(
715 Quote(CreateRecursiveComponentPath(i
->Group
, i
->Name
)));
718 SetOption("CPACK_INNOSETUP_DOWNLOAD_COUNT_INTERNAL",
719 std::to_string(archiveFiles
.size()));
720 SetOption("CPACK_INNOSETUP_DOWNLOAD_URLS_INTERNAL",
721 cmJoin(archiveUrls
, ", "));
722 SetOption("CPACK_INNOSETUP_DOWNLOAD_ARCHIVES_INTERNAL",
723 cmJoin(archiveFiles
, ", "));
724 SetOption("CPACK_INNOSETUP_DOWNLOAD_HASHES_INTERNAL",
725 cmJoin(archiveHashes
, ", "));
726 SetOption("CPACK_INNOSETUP_DOWNLOAD_COMPONENTS_INTERNAL",
727 cmJoin(archiveComponents
, ", "));
729 static const std::string
& downloadLines
=
730 "#define protected CPackDownloadCount "
731 "@CPACK_INNOSETUP_DOWNLOAD_COUNT_INTERNAL@\n"
732 "#dim protected CPackDownloadUrls[CPackDownloadCount] "
733 "{@CPACK_INNOSETUP_DOWNLOAD_URLS_INTERNAL@}\n"
734 "#dim protected CPackDownloadArchives[CPackDownloadCount] "
735 "{@CPACK_INNOSETUP_DOWNLOAD_ARCHIVES_INTERNAL@}\n"
736 "#dim protected CPackDownloadHashes[CPackDownloadCount] "
737 "{@CPACK_INNOSETUP_DOWNLOAD_HASHES_INTERNAL@}\n"
738 "#dim protected CPackDownloadComponents[CPackDownloadCount] "
739 "{@CPACK_INNOSETUP_DOWNLOAD_COMPONENTS_INTERNAL@}";
742 if (!ConfigureString(downloadLines
, output
)) {
745 codeIncludes
.push_back(output
);
748 // Add the required script
749 const std::string
& componentsScriptTemplate
=
750 FindTemplate("ISComponents.pas");
751 if (componentsScriptTemplate
.empty()) {
752 cmCPackLogger(cmCPackLog::LOG_ERROR
,
753 "Could not find additional Inno Setup script file."
758 codeIncludes
.push_back("#include " + QuotePath(componentsScriptTemplate
) +
764 bool cmCPackInnoSetupGenerator::ConfigureISScript()
766 const std::string
& isScriptTemplate
= FindTemplate("ISScript.template.in");
767 const std::string
& isScriptFile
=
768 cmStrCat(GetOption("CPACK_TOPLEVEL_DIRECTORY"), "/ISScript.iss");
770 if (isScriptTemplate
.empty()) {
771 cmCPackLogger(cmCPackLog::LOG_ERROR
,
772 "Could not find Inno Setup installer template file."
777 // Create internal variables
778 std::vector
<std::string
> setupSection
;
779 for (const auto& i
: setupDirectives
) {
780 setupSection
.emplace_back(cmStrCat(i
.first
, '=', TranslateBool(i
.second
)));
783 // Also create comments if the sections are empty
784 const std::string
& defaultMessage
=
785 "; CPack didn't find any entries for this section";
787 if (IsSet("CPACK_CREATE_DESKTOP_LINKS") &&
788 !GetOption("CPACK_CREATE_DESKTOP_LINKS").Get()->empty()) {
789 cmCPackInnoSetupKeyValuePairs tasks
;
790 tasks
["Name"] = "\"desktopicon\"";
791 tasks
["Description"] = "\"{cm:CreateDesktopIcon}\"";
792 tasks
["GroupDescription"] = "\"{cm:AdditionalIcons}\"";
793 tasks
["Flags"] = "unchecked";
795 if (!desktopIconComponents
.empty()) {
796 tasks
["Components"] = cmJoin(desktopIconComponents
, " ");
799 SetOption("CPACK_INNOSETUP_TASKS_INTERNAL", ISKeyValueLine(tasks
));
801 SetOption("CPACK_INNOSETUP_TASKS_INTERNAL", defaultMessage
);
804 SetOption("CPACK_INNOSETUP_INCLUDES_INTERNAL",
805 includeDirectives
.empty() ? "; No extra script files specified"
806 : cmJoin(includeDirectives
, "\n"));
807 SetOption("CPACK_INNOSETUP_SETUP_INTERNAL",
808 setupSection
.empty() ? defaultMessage
809 : cmJoin(setupSection
, "\n"));
810 SetOption("CPACK_INNOSETUP_LANGUAGES_INTERNAL",
811 languageInstructions
.empty() ? defaultMessage
812 : cmJoin(languageInstructions
, "\n"));
813 SetOption("CPACK_INNOSETUP_DIRS_INTERNAL",
814 dirInstructions
.empty() ? defaultMessage
815 : cmJoin(dirInstructions
, "\n"));
816 SetOption("CPACK_INNOSETUP_FILES_INTERNAL",
817 fileInstructions
.empty() ? defaultMessage
818 : cmJoin(fileInstructions
, "\n"));
819 SetOption("CPACK_INNOSETUP_TYPES_INTERNAL",
820 typeInstructions
.empty() ? defaultMessage
821 : cmJoin(typeInstructions
, "\n"));
822 SetOption("CPACK_INNOSETUP_COMPONENTS_INTERNAL",
823 componentInstructions
.empty()
825 : cmJoin(componentInstructions
, "\n"));
826 SetOption("CPACK_INNOSETUP_ICONS_INTERNAL",
827 iconInstructions
.empty() ? defaultMessage
828 : cmJoin(iconInstructions
, "\n"));
829 SetOption("CPACK_INNOSETUP_RUN_INTERNAL",
830 runInstructions
.empty() ? defaultMessage
831 : cmJoin(runInstructions
, "\n"));
832 SetOption("CPACK_INNOSETUP_CODE_INTERNAL",
833 codeIncludes
.empty() ? "{ No extra code files specified }"
834 : cmJoin(codeIncludes
, "\n"));
836 cmCPackLogger(cmCPackLog::LOG_VERBOSE
,
837 "Configure file: " << isScriptTemplate
<< " to "
838 << isScriptFile
<< std::endl
);
840 return ConfigureFile(isScriptTemplate
, isScriptFile
);
843 bool cmCPackInnoSetupGenerator::Compile()
845 const std::string
& isScriptFile
=
846 cmStrCat(GetOption("CPACK_TOPLEVEL_DIRECTORY"), "/ISScript.iss");
847 const std::string
& isccLogFile
=
848 cmStrCat(GetOption("CPACK_TOPLEVEL_DIRECTORY"), "/ISCCOutput.log");
850 std::vector
<std::string
> isccArgs
;
853 for (const std::string
& i
: GetOptions()) {
854 if (cmHasPrefix(i
, "CPACK_INNOSETUP_DEFINE_")) {
855 const std::string
& name
= i
.substr(cmStrLen("CPACK_INNOSETUP_DEFINE_"));
857 cmStrCat("\"/D", name
, '=', TranslateBool(GetOption(i
)), '"'));
861 if (IsSet("CPACK_INNOSETUP_EXECUTABLE_ARGUMENTS")) {
862 const cmList
args(GetOption("CPACK_INNOSETUP_EXECUTABLE_ARGUMENTS"));
864 isccArgs
.insert(isccArgs
.end(), args
.begin(), args
.end());
867 const std::string
& isccCmd
=
868 cmStrCat(QuotePath(GetOption("CPACK_INSTALLER_PROGRAM"), PathType::Native
),
869 ' ', cmJoin(isccArgs
, " "), ' ', QuotePath(isScriptFile
));
871 cmCPackLogger(cmCPackLog::LOG_VERBOSE
, "Execute: " << isccCmd
<< std::endl
);
875 const bool res
= cmSystemTools::RunSingleCommand(
876 isccCmd
, &output
, &output
, &retVal
, nullptr, this->GeneratorVerbose
,
879 if (!res
|| retVal
) {
880 cmGeneratedFileStream
ofs(isccLogFile
);
881 ofs
<< "# Run command: " << isccCmd
<< std::endl
882 << "# Output:" << std::endl
883 << output
<< std::endl
;
884 cmCPackLogger(cmCPackLog::LOG_ERROR
,
885 "Problem running ISCC. Please check "
886 << isccLogFile
<< " for errors." << std::endl
);
893 bool cmCPackInnoSetupGenerator::BuildDownloadedComponentArchive(
894 cmCPackComponent
* component
, const std::string
& uploadDirectory
,
897 // Remove the old archive, if one exists
898 const std::string
& archiveFile
=
899 uploadDirectory
+ '/' + component
->ArchiveFile
;
900 cmCPackLogger(cmCPackLog::LOG_OUTPUT
,
901 "- Building downloaded component archive: " << archiveFile
903 if (cmSystemTools::FileExists(archiveFile
, true)) {
904 if (!cmSystemTools::RemoveFile(archiveFile
)) {
905 cmCPackLogger(cmCPackLog::LOG_ERROR
,
906 "Unable to remove archive file " << archiveFile
912 // Find a ZIP program
913 if (!IsSet("ZIP_EXECUTABLE")) {
914 ReadListFile("Internal/CPack/CPackZIP.cmake");
916 if (!IsSet("ZIP_EXECUTABLE")) {
917 cmCPackLogger(cmCPackLog::LOG_ERROR
,
918 "Unable to find ZIP program" << std::endl
);
923 if (!RequireOption("CPACK_TOPLEVEL_DIRECTORY") ||
924 !RequireOption("CPACK_TEMPORARY_DIRECTORY") ||
925 !RequireOption("CPACK_ZIP_NEED_QUOTES") ||
926 !RequireOption("CPACK_ZIP_COMMAND")) {
930 // The directory where this component's files reside
931 const std::string
& dirName
=
932 cmStrCat(GetOption("CPACK_TEMPORARY_DIRECTORY"), '/', component
->Name
);
934 // Build the list of files to go into this archive
935 const std::string
& zipListFileName
=
936 cmStrCat(GetOption("CPACK_TEMPORARY_DIRECTORY"), "/winZip.filelist");
937 const bool needQuotesInFile
= GetOption("CPACK_ZIP_NEED_QUOTES").IsOn();
938 { // the scope is needed for cmGeneratedFileStream
939 cmGeneratedFileStream
out(zipListFileName
);
940 for (const std::string
& i
: component
->Files
) {
941 out
<< (needQuotesInFile
? Quote(i
) : i
) << std::endl
;
945 // Build the archive in the upload area
946 std::string cmd
= GetOption("CPACK_ZIP_COMMAND");
947 cmsys::SystemTools::ReplaceString(cmd
, "<ARCHIVE>", archiveFile
.c_str());
948 cmsys::SystemTools::ReplaceString(cmd
, "<FILELIST>",
949 zipListFileName
.c_str());
952 const bool res
= cmSystemTools::RunSingleCommand(
953 cmd
, &output
, &output
, &retVal
, dirName
.c_str(), this->GeneratorVerbose
,
955 if (!res
|| retVal
) {
956 std::string tmpFile
=
957 cmStrCat(GetOption("CPACK_TOPLEVEL_DIRECTORY"), "/CompressZip.log");
958 cmGeneratedFileStream
ofs(tmpFile
);
959 ofs
<< "# Run command: " << cmd
<< std::endl
960 << "# Output:" << std::endl
961 << output
<< std::endl
;
962 cmCPackLogger(cmCPackLog::LOG_ERROR
,
963 "Problem running zip command: " << cmd
<< std::endl
964 << "Please check " << tmpFile
970 // Try to get the SHA256 hash of the archive file
971 if (hash
== nullptr) {
976 const std::string
& hashCmd
=
977 cmStrCat("certutil -hashfile ", QuotePath(archiveFile
), " SHA256");
979 std::string hashOutput
;
981 const bool hashRes
= cmSystemTools::RunSingleCommand(
982 hashCmd
, &hashOutput
, &hashOutput
, &hashRetVal
, nullptr,
983 this->GeneratorVerbose
, cmDuration::zero());
984 if (!hashRes
|| hashRetVal
) {
985 cmCPackLogger(cmCPackLog::LOG_WARNING
,
986 "Problem running certutil command: " << hashCmd
989 *hash
= cmTrimWhitespace(cmTokenize(hashOutput
, "\n").at(1));
991 if (hash
->length() != 64) {
992 cmCPackLogger(cmCPackLog::LOG_WARNING
,
993 "Problem parsing certutil output of command: " << hashCmd
1002 cmValue
cmCPackInnoSetupGenerator::RequireOption(const std::string
& key
)
1004 cmValue value
= GetOption(key
);
1007 cmCPackLogger(cmCPackLog::LOG_ERROR
,
1008 "Required variable " << key
<< " not set" << std::endl
);
1014 std::string
cmCPackInnoSetupGenerator::CustomComponentInstallDirectory(
1015 const cmCPackComponent
* component
)
1017 cmValue outputDir
= GetOption(
1018 cmStrCat("CPACK_INNOSETUP_", component
->Name
, "_INSTALL_DIRECTORY"));
1020 std::string destDir
= cmSystemTools::ConvertToWindowsOutputPath(outputDir
);
1021 cmStripSuffixIfExists(destDir
, '\\');
1024 * Add a dir instruction for the custom directory
1025 * (only once and not for Inno Setup constants ending with '}')
1027 static std::vector
<std::string
> customDirectories
;
1028 if (!cmHasSuffix(destDir
, '}') &&
1029 std::find(customDirectories
.begin(), customDirectories
.end(),
1030 component
->Name
) == customDirectories
.end()) {
1031 cmCPackInnoSetupKeyValuePairs params
;
1032 params
["Name"] = QuotePath(destDir
);
1033 params
["Components"] =
1034 CreateRecursiveComponentPath(component
->Group
, component
->Name
);
1036 dirInstructions
.push_back(ISKeyValueLine(params
));
1037 customDirectories
.push_back(component
->Name
);
1045 std::string
cmCPackInnoSetupGenerator::TranslateBool(const std::string
& value
)
1047 if (value
.empty()) {
1051 SetOptionIfNotSet("CPACK_INNOSETUP_USE_CMAKE_BOOL_FORMAT", "ON");
1052 if (GetOption("CPACK_INNOSETUP_USE_CMAKE_BOOL_FORMAT").IsOn()) {
1053 if (cmIsOn(value
)) {
1056 if (cmIsOff(value
)) {
1064 std::string
cmCPackInnoSetupGenerator::ISKeyValueLine(
1065 const cmCPackInnoSetupKeyValuePairs
& params
)
1068 * To simplify readability of the generated code, the keys are sorted.
1069 * Unknown keys are ignored to avoid errors during compilation.
1071 static const char* const availableKeys
[] = {
1072 "Source", "DestDir", "Name", "Filename",
1073 "Description", "GroupDescription", "MessagesFile", "Types",
1074 "ExternalSize", "BeforeInstall", "Flags", "Components",
1078 std::vector
<std::string
> keys
;
1079 for (const char* i
: availableKeys
) {
1080 if (params
.count(i
)) {
1081 keys
.emplace_back(cmStrCat(i
, ": ", params
.at(i
)));
1085 return cmJoin(keys
, "; ");
1088 std::string
cmCPackInnoSetupGenerator::CreateRecursiveComponentPath(
1089 cmCPackComponentGroup
* group
, const std::string
& path
)
1091 if (group
== nullptr) {
1095 const std::string
& newPath
=
1096 path
.empty() ? group
->Name
: cmStrCat(group
->Name
, '\\', path
);
1097 return CreateRecursiveComponentPath(group
->ParentGroup
, newPath
);
1100 void cmCPackInnoSetupGenerator::CreateRecursiveComponentGroups(
1101 cmCPackComponentGroup
* group
)
1103 if (group
== nullptr) {
1107 CreateRecursiveComponentGroups(group
->ParentGroup
);
1109 static std::vector
<cmCPackComponentGroup
*> processedGroups
;
1110 if (std::find(processedGroups
.begin(), processedGroups
.end(), group
) ==
1111 processedGroups
.end()) {
1112 processedGroups
.push_back(group
);
1114 cmCPackInnoSetupKeyValuePairs params
;
1116 params
["Name"] = Quote(CreateRecursiveComponentPath(group
));
1117 params
["Description"] = Quote(group
->DisplayName
);
1119 componentInstructions
.push_back(ISKeyValueLine(params
));
1123 std::string
cmCPackInnoSetupGenerator::Quote(const std::string
& string
)
1125 if (cmHasPrefix(string
, '"') && cmHasSuffix(string
, '"')) {
1126 return Quote(string
.substr(1, string
.length() - 2));
1129 // Double quote syntax
1130 std::string nString
= string
;
1131 cmSystemTools::ReplaceString(nString
, "\"", "\"\"");
1132 return cmStrCat('"', nString
, '"');
1135 std::string
cmCPackInnoSetupGenerator::QuotePath(const std::string
& path
,
1139 static_cast<void>(type
);
1141 if (type
== PathType::Native
) {
1142 return Quote(cmSystemTools::ConvertToUnixOutputPath(path
));
1145 return Quote(cmSystemTools::ConvertToWindowsOutputPath(path
));
1148 std::string
cmCPackInnoSetupGenerator::PrepareForConstant(
1149 const std::string
& string
)
1151 std::string nString
= string
;
1153 cmSystemTools::ReplaceString(nString
, "%", "%25"); // First replacement!
1154 cmSystemTools::ReplaceString(nString
, "\"", "%22");
1155 cmSystemTools::ReplaceString(nString
, ",", "%2c");
1156 cmSystemTools::ReplaceString(nString
, "|", "%7c");
1157 cmSystemTools::ReplaceString(nString
, "}", "%7d");