| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
| file Copyright.txt or https://cmake.org/licensing for details. */ |
| |
| #include "cmCPackInnoSetupGenerator.h" |
| |
| #include <algorithm> |
| #include <cctype> |
| #include <cstdlib> |
| #include <ostream> |
| #include <utility> |
| |
| #include "cmsys/RegularExpression.hxx" |
| |
| #include "cmCPackComponentGroup.h" |
| #include "cmCPackLog.h" |
| #include "cmDuration.h" |
| #include "cmGeneratedFileStream.h" |
| #include "cmList.h" |
| #include "cmStringAlgorithms.h" |
| #include "cmSystemTools.h" |
| |
| cmCPackInnoSetupGenerator::cmCPackInnoSetupGenerator() = default; |
| cmCPackInnoSetupGenerator::~cmCPackInnoSetupGenerator() = default; |
| |
| bool cmCPackInnoSetupGenerator::CanGenerate() |
| { |
| return true; |
| } |
| |
| int cmCPackInnoSetupGenerator::InitializeInternal() |
| { |
| if (GetOption("CPACK_INCLUDE_TOPLEVEL_DIRECTORY").IsOn()) { |
| cmCPackLogger(cmCPackLog::LOG_WARNING, |
| "Inno Setup Generator cannot work with " |
| "CPACK_INCLUDE_TOPLEVEL_DIRECTORY set. " |
| "This option will be reset to 0 (for this generator only)." |
| << std::endl); |
| SetOption("CPACK_INCLUDE_TOPLEVEL_DIRECTORY", nullptr); |
| } |
| |
| std::vector<std::string> path; |
| |
| #ifdef _WIN32 |
| path.push_back("C:\\Program Files (x86)\\Inno Setup 5"); |
| path.push_back("C:\\Program Files (x86)\\Inno Setup 6"); |
| #endif |
| |
| SetOptionIfNotSet("CPACK_INNOSETUP_EXECUTABLE", "ISCC"); |
| const std::string& isccPath = cmSystemTools::FindProgram( |
| GetOption("CPACK_INNOSETUP_EXECUTABLE"), path, false); |
| |
| if (isccPath.empty()) { |
| cmCPackLogger(cmCPackLog::LOG_ERROR, |
| "Cannot find Inno Setup compiler ISCC: " |
| "likely it is not installed, or not in your PATH" |
| << std::endl); |
| |
| return 0; |
| } |
| |
| const std::string isccCmd = |
| cmStrCat(QuotePath(isccPath, PathType::Native), "/?"); |
| cmCPackLogger(cmCPackLog::LOG_VERBOSE, |
| "Test Inno Setup version: " << isccCmd << std::endl); |
| std::string output; |
| cmSystemTools::RunSingleCommand(isccCmd, &output, &output, nullptr, nullptr, |
| this->GeneratorVerbose, cmDuration::zero()); |
| cmsys::RegularExpression vRex("Inno Setup ([0-9]+)"); |
| if (!vRex.find(output)) { |
| cmCPackLogger(cmCPackLog::LOG_ERROR, |
| "Problem checking Inno Setup version with command: " |
| << isccCmd << std::endl |
| << "Have you downloaded Inno Setup from " |
| "https://jrsoftware.org/isinfo.php?" |
| << std::endl); |
| return 0; |
| } |
| |
| const int isccVersion = atoi(vRex.match(1).c_str()); |
| const int minIsccVersion = 6; |
| cmCPackLogger(cmCPackLog::LOG_DEBUG, |
| "Inno Setup Version: " << isccVersion << std::endl); |
| |
| if (isccVersion < minIsccVersion) { |
| cmCPackLogger(cmCPackLog::LOG_ERROR, |
| "CPack requires Inno Setup Version 6 or greater. " |
| "Inno Setup found on the system was: " |
| << isccVersion << std::endl); |
| return 0; |
| } |
| |
| SetOption("CPACK_INSTALLER_PROGRAM", isccPath); |
| |
| return this->Superclass::InitializeInternal(); |
| } |
| |
| int cmCPackInnoSetupGenerator::PackageFiles() |
| { |
| // Includes |
| if (IsSet("CPACK_INNOSETUP_EXTRA_SCRIPTS")) { |
| const cmList extraScripts(GetOption("CPACK_INNOSETUP_EXTRA_SCRIPTS")); |
| |
| for (const std::string& i : extraScripts) { |
| includeDirectives.emplace_back(cmStrCat( |
| "#include ", QuotePath(cmSystemTools::CollapseFullPath(i, toplevel)))); |
| } |
| } |
| |
| // [Languages] section |
| SetOptionIfNotSet("CPACK_INNOSETUP_LANGUAGES", "english"); |
| const cmList languages(GetOption("CPACK_INNOSETUP_LANGUAGES")); |
| for (std::string i : languages) { |
| cmCPackInnoSetupKeyValuePairs params; |
| |
| params["Name"] = Quote(i); |
| |
| if (cmSystemTools::LowerCase(i) == "english") { |
| params["MessagesFile"] = "\"compiler:Default.isl\""; |
| } else { |
| i[0] = static_cast<char>(std::toupper(i[0])); |
| params["MessagesFile"] = cmStrCat("\"compiler:Languages\\", i, ".isl\""); |
| } |
| |
| languageInstructions.push_back(ISKeyValueLine(params)); |
| } |
| |
| if (!Components.empty() && !ProcessComponents()) { |
| return false; |
| } |
| |
| if (!(ProcessSetupSection() && ProcessFiles())) { |
| return false; |
| } |
| |
| // [Code] section |
| if (IsSet("CPACK_INNOSETUP_CODE_FILES")) { |
| const cmList codeFiles(GetOption("CPACK_INNOSETUP_CODE_FILES")); |
| |
| for (const std::string& i : codeFiles) { |
| codeIncludes.emplace_back(cmStrCat( |
| "#include ", QuotePath(cmSystemTools::CollapseFullPath(i, toplevel)))); |
| } |
| } |
| |
| return ConfigureISScript() && Compile(); |
| } |
| |
| bool cmCPackInnoSetupGenerator::ProcessSetupSection() |
| { |
| if (!RequireOption("CPACK_PACKAGE_INSTALL_REGISTRY_KEY")) { |
| return false; |
| } |
| setupDirectives["AppId"] = GetOption("CPACK_PACKAGE_INSTALL_REGISTRY_KEY"); |
| |
| if (!RequireOption("CPACK_PACKAGE_NAME")) { |
| return false; |
| } |
| setupDirectives["AppName"] = GetOption("CPACK_PACKAGE_NAME"); |
| setupDirectives["UninstallDisplayName"] = GetOption("CPACK_PACKAGE_NAME"); |
| |
| if (!RequireOption("CPACK_PACKAGE_VERSION")) { |
| return false; |
| } |
| setupDirectives["AppVersion"] = GetOption("CPACK_PACKAGE_VERSION"); |
| |
| if (!RequireOption("CPACK_PACKAGE_VENDOR")) { |
| return false; |
| } |
| setupDirectives["AppPublisher"] = GetOption("CPACK_PACKAGE_VENDOR"); |
| |
| if (IsSet("CPACK_PACKAGE_HOMEPAGE_URL")) { |
| setupDirectives["AppPublisherURL"] = |
| GetOption("CPACK_PACKAGE_HOMEPAGE_URL"); |
| setupDirectives["AppSupportURL"] = GetOption("CPACK_PACKAGE_HOMEPAGE_URL"); |
| setupDirectives["AppUpdatesURL"] = GetOption("CPACK_PACKAGE_HOMEPAGE_URL"); |
| } |
| |
| SetOptionIfNotSet("CPACK_INNOSETUP_IGNORE_LICENSE_PAGE", "OFF"); |
| if (IsSet("CPACK_RESOURCE_FILE_LICENSE") && |
| !GetOption("CPACK_INNOSETUP_IGNORE_LICENSE_PAGE").IsOn()) { |
| setupDirectives["LicenseFile"] = cmSystemTools::ConvertToWindowsOutputPath( |
| GetOption("CPACK_RESOURCE_FILE_LICENSE")); |
| } |
| |
| SetOptionIfNotSet("CPACK_INNOSETUP_IGNORE_README_PAGE", "ON"); |
| if (IsSet("CPACK_RESOURCE_FILE_README") && |
| !GetOption("CPACK_INNOSETUP_IGNORE_README_PAGE").IsOn()) { |
| setupDirectives["InfoBeforeFile"] = |
| cmSystemTools::ConvertToWindowsOutputPath( |
| GetOption("CPACK_RESOURCE_FILE_README")); |
| } |
| |
| SetOptionIfNotSet("CPACK_INNOSETUP_USE_MODERN_WIZARD", "OFF"); |
| if (GetOption("CPACK_INNOSETUP_USE_MODERN_WIZARD").IsOn()) { |
| setupDirectives["WizardStyle"] = "modern"; |
| } else { |
| setupDirectives["WizardStyle"] = "classic"; |
| setupDirectives["WizardSmallImageFile"] = |
| "compiler:WizClassicSmallImage.bmp"; |
| setupDirectives["WizardImageFile"] = "compiler:WizClassicImage.bmp"; |
| setupDirectives["SetupIconFile"] = "compiler:SetupClassicIcon.ico"; |
| } |
| |
| if (IsSet("CPACK_INNOSETUP_ICON_FILE")) { |
| setupDirectives["SetupIconFile"] = |
| cmSystemTools::ConvertToWindowsOutputPath( |
| GetOption("CPACK_INNOSETUP_ICON_FILE")); |
| } |
| |
| if (IsSet("CPACK_PACKAGE_ICON")) { |
| setupDirectives["WizardSmallImageFile"] = |
| cmSystemTools::ConvertToWindowsOutputPath( |
| GetOption("CPACK_PACKAGE_ICON")); |
| } |
| |
| if (!RequireOption("CPACK_PACKAGE_INSTALL_DIRECTORY")) { |
| return false; |
| } |
| SetOptionIfNotSet("CPACK_INNOSETUP_INSTALL_ROOT", "{autopf}"); |
| setupDirectives["DefaultDirName"] = |
| cmSystemTools::ConvertToWindowsOutputPath( |
| cmStrCat(GetOption("CPACK_INNOSETUP_INSTALL_ROOT"), '\\', |
| GetOption("CPACK_PACKAGE_INSTALL_DIRECTORY"))); |
| |
| SetOptionIfNotSet("CPACK_INNOSETUP_ALLOW_CUSTOM_DIRECTORY", "ON"); |
| if (GetOption("CPACK_INNOSETUP_ALLOW_CUSTOM_DIRECTORY").IsOff()) { |
| setupDirectives["DisableDirPage"] = "yes"; |
| } |
| |
| SetOptionIfNotSet("CPACK_INNOSETUP_PROGRAM_MENU_FOLDER", |
| GetOption("CPACK_PACKAGE_NAME")); |
| if (GetOption("CPACK_INNOSETUP_PROGRAM_MENU_FOLDER") == ".") { |
| setupDirectives["DisableProgramGroupPage"] = "yes"; |
| toplevelProgramFolder = true; |
| } else { |
| setupDirectives["DefaultGroupName"] = |
| GetOption("CPACK_INNOSETUP_PROGRAM_MENU_FOLDER"); |
| toplevelProgramFolder = false; |
| } |
| |
| if (IsSet("CPACK_INNOSETUP_PASSWORD")) { |
| setupDirectives["Password"] = GetOption("CPACK_INNOSETUP_PASSWORD"); |
| setupDirectives["Encryption"] = "yes"; |
| } |
| |
| /* |
| * These directives can only be modified using the |
| * CPACK_INNOSETUP_SETUP_<directive> variables |
| */ |
| setupDirectives["ShowLanguageDialog"] = "auto"; |
| setupDirectives["AllowNoIcons"] = "yes"; |
| setupDirectives["Compression"] = "lzma"; |
| setupDirectives["SolidCompression"] = "yes"; |
| |
| // Output file and directory |
| if (!RequireOption("CPACK_PACKAGE_FILE_NAME")) { |
| return false; |
| } |
| setupDirectives["OutputBaseFilename"] = GetOption("CPACK_PACKAGE_FILE_NAME"); |
| |
| if (!RequireOption("CPACK_TOPLEVEL_DIRECTORY")) { |
| return false; |
| } |
| setupDirectives["OutputDir"] = cmSystemTools::ConvertToWindowsOutputPath( |
| GetOption("CPACK_TOPLEVEL_DIRECTORY")); |
| |
| setupDirectives["SourceDir"] = |
| cmSystemTools::ConvertToWindowsOutputPath(toplevel); |
| |
| // Target architecture |
| if (!RequireOption("CPACK_INNOSETUP_ARCHITECTURE")) { |
| return false; |
| } |
| |
| cmValue const architecture = GetOption("CPACK_INNOSETUP_ARCHITECTURE"); |
| if (architecture != "x86" && architecture != "x64" && |
| architecture != "arm64" && architecture != "ia64") { |
| cmCPackLogger(cmCPackLog::LOG_ERROR, |
| "CPACK_INNOSETUP_ARCHITECTURE must be either x86, x64, " |
| "arm64 or ia64" |
| << std::endl); |
| return false; |
| } |
| |
| // The following directives must not be set to target x86 |
| if (architecture != "x86") { |
| setupDirectives["ArchitecturesAllowed"] = architecture; |
| setupDirectives["ArchitecturesInstallIn64BitMode"] = architecture; |
| } |
| |
| /* |
| * Handle custom directives (they have higher priority than other variables, |
| * so they have to be processed after all other variables) |
| */ |
| for (const std::string& i : GetOptions()) { |
| if (cmHasPrefix(i, "CPACK_INNOSETUP_SETUP_")) { |
| const std::string& directive = |
| i.substr(cmStrLen("CPACK_INNOSETUP_SETUP_")); |
| setupDirectives[directive] = GetOption(i); |
| } |
| } |
| |
| return true; |
| } |
| |
| bool cmCPackInnoSetupGenerator::ProcessFiles() |
| { |
| std::map<std::string, std::string> customFileInstructions; |
| if (IsSet("CPACK_INNOSETUP_CUSTOM_INSTALL_INSTRUCTIONS")) { |
| const cmList instructions( |
| GetOption("CPACK_INNOSETUP_CUSTOM_INSTALL_INSTRUCTIONS")); |
| if (instructions.size() % 2 != 0) { |
| cmCPackLogger(cmCPackLog::LOG_ERROR, |
| "CPACK_INNOSETUP_CUSTOM_INSTALL_INSTRUCTIONS should " |
| "contain pairs of <path> and <instruction>" |
| << std::endl); |
| return false; |
| } |
| |
| for (auto it = instructions.begin(); it != instructions.end(); ++it) { |
| const std::string& key = |
| QuotePath(cmSystemTools::CollapseFullPath(*it, toplevel)); |
| customFileInstructions[key] = *(++it); |
| } |
| } |
| |
| const std::string& iconsPrefix = |
| toplevelProgramFolder ? "{autoprograms}\\" : "{group}\\"; |
| |
| std::map<std::string, std::string> icons; |
| if (IsSet("CPACK_PACKAGE_EXECUTABLES")) { |
| const cmList executables(GetOption("CPACK_PACKAGE_EXECUTABLES")); |
| if (executables.size() % 2 != 0) { |
| cmCPackLogger(cmCPackLog::LOG_ERROR, |
| "CPACK_PACKAGE_EXECUTABLES should should contain pairs of " |
| "<executable> and <text label>" |
| << std::endl); |
| return false; |
| } |
| |
| for (auto it = executables.begin(); it != executables.end(); ++it) { |
| const std::string& key = *it; |
| icons[key] = *(++it); |
| } |
| } |
| |
| std::vector<std::string> desktopIcons; |
| if (IsSet("CPACK_CREATE_DESKTOP_LINKS")) { |
| cmExpandList(GetOption("CPACK_CREATE_DESKTOP_LINKS"), desktopIcons); |
| } |
| |
| std::vector<std::string> runExecutables; |
| if (IsSet("CPACK_INNOSETUP_RUN_EXECUTABLES")) { |
| cmExpandList(GetOption("CPACK_INNOSETUP_RUN_EXECUTABLES"), runExecutables); |
| } |
| |
| for (const std::string& i : files) { |
| cmCPackInnoSetupKeyValuePairs params; |
| |
| std::string toplevelDirectory; |
| std::string outputDir; |
| cmCPackComponent* component = nullptr; |
| std::string componentParam; |
| if (!Components.empty()) { |
| const std::string& fileName = cmSystemTools::RelativePath(toplevel, i); |
| const std::string::size_type pos = fileName.find('/'); |
| |
| // Use the custom component install directory if we have one |
| if (pos != std::string::npos) { |
| const std::string& componentName = fileName.substr(0, pos); |
| component = &Components[componentName]; |
| |
| toplevelDirectory = |
| cmSystemTools::CollapseFullPath(componentName, toplevel); |
| outputDir = CustomComponentInstallDirectory(component); |
| componentParam = |
| CreateRecursiveComponentPath(component->Group, component->Name); |
| |
| if (component->IsHidden && component->IsDisabledByDefault) { |
| continue; |
| } |
| |
| if (component->IsHidden) { |
| componentParam.clear(); |
| } |
| } else { |
| // Don't install component directories |
| continue; |
| } |
| } else { |
| toplevelDirectory = toplevel; |
| outputDir = "{app}"; |
| } |
| |
| if (!componentParam.empty()) { |
| params["Components"] = componentParam; |
| } |
| |
| if (cmSystemTools::FileIsDirectory(i)) { |
| // Custom instructions replace the automatic generated instructions |
| if (customFileInstructions.count(QuotePath(i))) { |
| dirInstructions.push_back(customFileInstructions[QuotePath(i)]); |
| } else { |
| std::string destDir = cmSystemTools::ConvertToWindowsOutputPath( |
| cmStrCat(outputDir, '\\', |
| cmSystemTools::RelativePath(toplevelDirectory, i))); |
| cmStripSuffixIfExists(destDir, '\\'); |
| |
| params["Name"] = QuotePath(destDir); |
| |
| dirInstructions.push_back(ISKeyValueLine(params)); |
| } |
| } else { |
| // Custom instructions replace the automatic generated instructions |
| if (customFileInstructions.count(QuotePath(i))) { |
| fileInstructions.push_back(customFileInstructions[QuotePath(i)]); |
| } else { |
| std::string destDir = cmSystemTools::ConvertToWindowsOutputPath( |
| cmStrCat(outputDir, '\\', |
| cmSystemTools::GetParentDirectory( |
| cmSystemTools::RelativePath(toplevelDirectory, i)))); |
| cmStripSuffixIfExists(destDir, '\\'); |
| |
| params["DestDir"] = QuotePath(destDir); |
| |
| if (component != nullptr && component->IsDownloaded) { |
| const std::string& archiveName = |
| cmSystemTools::GetFilenameWithoutLastExtension( |
| component->ArchiveFile); |
| const std::string& relativePath = |
| cmSystemTools::RelativePath(toplevelDirectory, i); |
| |
| params["Source"] = |
| QuotePath(cmStrCat("{tmp}\\", archiveName, '\\', relativePath)); |
| params["ExternalSize"] = |
| std::to_string(cmSystemTools::FileLength(i)); |
| params["Flags"] = "external ignoreversion"; |
| params["BeforeInstall"] = |
| cmStrCat("CPackExtractFile('", archiveName, "', '", |
| cmRemoveQuotes(cmSystemTools::ConvertToWindowsOutputPath( |
| relativePath)), |
| "')"); |
| } else { |
| params["Source"] = QuotePath(i); |
| params["Flags"] = "ignoreversion"; |
| } |
| |
| fileInstructions.push_back(ISKeyValueLine(params)); |
| |
| // Icon |
| const std::string& name = |
| cmSystemTools::GetFilenameWithoutLastExtension(i); |
| const std::string& extension = |
| cmSystemTools::GetFilenameLastExtension(i); |
| if ((extension == ".exe" || extension == ".com") && // only .exe, .com |
| icons.count(name)) { |
| cmCPackInnoSetupKeyValuePairs iconParams; |
| |
| iconParams["Name"] = QuotePath(cmStrCat(iconsPrefix, icons[name])); |
| iconParams["Filename"] = |
| QuotePath(cmStrCat(destDir, '\\', name, extension)); |
| |
| if (!componentParam.empty()) { |
| iconParams["Components"] = componentParam; |
| } |
| |
| iconInstructions.push_back(ISKeyValueLine(iconParams)); |
| |
| // Desktop icon |
| if (std::find(desktopIcons.begin(), desktopIcons.end(), name) != |
| desktopIcons.end()) { |
| iconParams["Name"] = |
| QuotePath(cmStrCat("{autodesktop}\\", icons[name])); |
| iconParams["Tasks"] = "desktopicon"; |
| |
| if (!componentParam.empty() && |
| std::find(desktopIconComponents.begin(), |
| desktopIconComponents.end(), |
| componentParam) == desktopIconComponents.end()) { |
| desktopIconComponents.push_back(componentParam); |
| } |
| iconInstructions.push_back(ISKeyValueLine(iconParams)); |
| } |
| |
| // [Run] section |
| if (std::find(runExecutables.begin(), runExecutables.end(), name) != |
| runExecutables.end()) { |
| cmCPackInnoSetupKeyValuePairs runParams; |
| |
| runParams["Filename"] = iconParams["Filename"]; |
| runParams["Description"] = cmStrCat( |
| "\"{cm:LaunchProgram,", PrepareForConstant(icons[name]), "}\""); |
| runParams["Flags"] = "nowait postinstall skipifsilent"; |
| |
| if (!componentParam.empty()) { |
| runParams["Components"] = componentParam; |
| } |
| |
| runInstructions.push_back(ISKeyValueLine(runParams)); |
| } |
| } |
| } |
| } |
| } |
| |
| // Additional icons |
| static cmsys::RegularExpression urlRegex( |
| "^(mailto:|(ftps?|https?|news)://).*$"); |
| |
| if (IsSet("CPACK_INNOSETUP_MENU_LINKS")) { |
| const cmList menuIcons(GetOption("CPACK_INNOSETUP_MENU_LINKS")); |
| if (menuIcons.size() % 2 != 0) { |
| cmCPackLogger(cmCPackLog::LOG_ERROR, |
| "CPACK_INNOSETUP_MENU_LINKS should " |
| "contain pairs of <shortcut target> and <shortcut label>" |
| << std::endl); |
| return false; |
| } |
| |
| for (auto it = menuIcons.begin(); it != menuIcons.end(); ++it) { |
| const std::string& target = *it; |
| const std::string& label = *(++it); |
| cmCPackInnoSetupKeyValuePairs params; |
| |
| params["Name"] = QuotePath(cmStrCat(iconsPrefix, label)); |
| if (urlRegex.find(target)) { |
| params["Filename"] = Quote(target); |
| } else { |
| std::string dir = "{app}"; |
| std::string componentName; |
| for (const auto& i : Components) { |
| if (cmSystemTools::FileExists(cmSystemTools::CollapseFullPath( |
| cmStrCat(i.second.Name, '\\', target), toplevel))) { |
| dir = CustomComponentInstallDirectory(&i.second); |
| componentName = |
| CreateRecursiveComponentPath(i.second.Group, i.second.Name); |
| |
| if (i.second.IsHidden && i.second.IsDisabledByDefault) { |
| goto continueOuterLoop; |
| } else if (i.second.IsHidden) { |
| componentName.clear(); |
| } |
| |
| break; |
| } |
| } |
| |
| params["Filename"] = QuotePath(cmStrCat(dir, '\\', target)); |
| |
| if (!componentName.empty()) { |
| params["Components"] = componentName; |
| } |
| } |
| |
| iconInstructions.push_back(ISKeyValueLine(params)); |
| continueOuterLoop:; |
| } |
| } |
| |
| SetOptionIfNotSet("CPACK_INNOSETUP_CREATE_UNINSTALL_LINK", "OFF"); |
| if (GetOption("CPACK_INNOSETUP_CREATE_UNINSTALL_LINK").IsOn()) { |
| cmCPackInnoSetupKeyValuePairs params; |
| |
| params["Name"] = QuotePath( |
| cmStrCat(iconsPrefix, "{cm:UninstallProgram,", |
| PrepareForConstant(GetOption("CPACK_PACKAGE_NAME")), '}')); |
| params["Filename"] = "\"{uninstallexe}\""; |
| |
| iconInstructions.push_back(ISKeyValueLine(params)); |
| } |
| |
| return true; |
| } |
| |
| bool cmCPackInnoSetupGenerator::ProcessComponents() |
| { |
| codeIncludes.emplace_back( |
| "{ The following lines are required by CPack because " |
| "this script uses components }"); |
| |
| // Installation types |
| std::vector<cmCPackInstallationType*> types(InstallationTypes.size()); |
| for (auto& i : InstallationTypes) { |
| types[i.second.Index - 1] = &i.second; |
| } |
| |
| std::vector<std::string> allTypes; // For required components |
| for (cmCPackInstallationType* i : types) { |
| cmCPackInnoSetupKeyValuePairs params; |
| |
| params["Name"] = Quote(i->Name); |
| params["Description"] = Quote(i->DisplayName); |
| |
| allTypes.push_back(i->Name); |
| typeInstructions.push_back(ISKeyValueLine(params)); |
| } |
| |
| // Inno Setup requires the additional "custom" type |
| cmCPackInnoSetupKeyValuePairs customTypeParams; |
| |
| customTypeParams["Name"] = "\"custom\""; |
| customTypeParams["Description"] = |
| "\"{code:CPackGetCustomInstallationMessage}\""; |
| customTypeParams["Flags"] = "iscustom"; |
| |
| allTypes.emplace_back("custom"); |
| typeInstructions.push_back(ISKeyValueLine(customTypeParams)); |
| |
| // Components |
| std::vector<cmCPackComponent*> downloadedComponents; |
| for (auto& i : Components) { |
| cmCPackInnoSetupKeyValuePairs params; |
| cmCPackComponent* component = &i.second; |
| |
| if (component->IsHidden) { |
| continue; |
| } |
| |
| CreateRecursiveComponentGroups(component->Group); |
| |
| params["Name"] = |
| Quote(CreateRecursiveComponentPath(component->Group, component->Name)); |
| params["Description"] = Quote(component->DisplayName); |
| |
| if (component->IsRequired) { |
| params["Types"] = cmJoin(allTypes, " "); |
| params["Flags"] = "fixed"; |
| } else if (!component->InstallationTypes.empty()) { |
| std::vector<std::string> installationTypes; |
| |
| installationTypes.reserve(component->InstallationTypes.size()); |
| for (cmCPackInstallationType* j : component->InstallationTypes) { |
| installationTypes.push_back(j->Name); |
| } |
| |
| params["Types"] = cmJoin(installationTypes, " "); |
| } |
| |
| componentInstructions.push_back(ISKeyValueLine(params)); |
| |
| if (component->IsDownloaded) { |
| downloadedComponents.push_back(component); |
| |
| if (component->ArchiveFile.empty()) { |
| // Compute the name of the archive. |
| if (!RequireOption("CPACK_TEMPORARY_DIRECTORY")) { |
| return false; |
| } |
| |
| std::string packagesDir = |
| cmStrCat(GetOption("CPACK_TEMPORARY_DIRECTORY"), ".dummy"); |
| component->ArchiveFile = |
| cmStrCat(cmSystemTools::GetFilenameWithoutLastExtension(packagesDir), |
| '-', component->Name, ".zip"); |
| } else if (!cmHasSuffix(component->ArchiveFile, ".zip")) { |
| component->ArchiveFile = cmStrCat(component->ArchiveFile, ".zip"); |
| } |
| } |
| } |
| |
| // Downloaded components |
| if (!downloadedComponents.empty()) { |
| // Create the directory for the upload area |
| cmValue userUploadDirectory = GetOption("CPACK_UPLOAD_DIRECTORY"); |
| std::string uploadDirectory; |
| if (cmNonempty(userUploadDirectory)) { |
| uploadDirectory = *userUploadDirectory; |
| } else { |
| if (!RequireOption("CPACK_PACKAGE_DIRECTORY")) { |
| return false; |
| } |
| |
| uploadDirectory = |
| cmStrCat(GetOption("CPACK_PACKAGE_DIRECTORY"), "/CPackUploads"); |
| } |
| |
| if (!cmSystemTools::FileExists(uploadDirectory)) { |
| if (!cmSystemTools::MakeDirectory(uploadDirectory)) { |
| cmCPackLogger(cmCPackLog::LOG_ERROR, |
| "Unable to create Inno Setup upload directory " |
| << uploadDirectory << std::endl); |
| return false; |
| } |
| } |
| |
| if (!RequireOption("CPACK_DOWNLOAD_SITE")) { |
| return false; |
| } |
| |
| SetOptionIfNotSet("CPACK_INNOSETUP_VERIFY_DOWNLOADS", "ON"); |
| const bool verifyDownloads = |
| GetOption("CPACK_INNOSETUP_VERIFY_DOWNLOADS").IsOn(); |
| |
| const std::string& urlPrefix = |
| cmHasSuffix(GetOption("CPACK_DOWNLOAD_SITE").GetCStr(), '/') |
| ? GetOption("CPACK_DOWNLOAD_SITE") |
| : cmStrCat(GetOption("CPACK_DOWNLOAD_SITE"), '/'); |
| |
| std::vector<std::string> archiveUrls; |
| std::vector<std::string> archiveFiles; |
| std::vector<std::string> archiveHashes; |
| std::vector<std::string> archiveComponents; |
| for (cmCPackComponent* i : downloadedComponents) { |
| std::string hash; |
| if (!BuildDownloadedComponentArchive( |
| i, uploadDirectory, (verifyDownloads ? &hash : nullptr))) { |
| return false; |
| } |
| |
| archiveUrls.push_back(Quote(cmStrCat(urlPrefix, i->ArchiveFile))); |
| archiveFiles.push_back( |
| Quote(cmSystemTools::GetFilenameWithoutLastExtension(i->ArchiveFile))); |
| archiveHashes.push_back(Quote(hash)); |
| archiveComponents.push_back( |
| Quote(CreateRecursiveComponentPath(i->Group, i->Name))); |
| } |
| |
| SetOption("CPACK_INNOSETUP_DOWNLOAD_COUNT_INTERNAL", |
| std::to_string(archiveFiles.size())); |
| SetOption("CPACK_INNOSETUP_DOWNLOAD_URLS_INTERNAL", |
| cmJoin(archiveUrls, ", ")); |
| SetOption("CPACK_INNOSETUP_DOWNLOAD_ARCHIVES_INTERNAL", |
| cmJoin(archiveFiles, ", ")); |
| SetOption("CPACK_INNOSETUP_DOWNLOAD_HASHES_INTERNAL", |
| cmJoin(archiveHashes, ", ")); |
| SetOption("CPACK_INNOSETUP_DOWNLOAD_COMPONENTS_INTERNAL", |
| cmJoin(archiveComponents, ", ")); |
| |
| static const std::string& downloadLines = |
| "#define protected CPackDownloadCount " |
| "@CPACK_INNOSETUP_DOWNLOAD_COUNT_INTERNAL@\n" |
| "#dim protected CPackDownloadUrls[CPackDownloadCount] " |
| "{@CPACK_INNOSETUP_DOWNLOAD_URLS_INTERNAL@}\n" |
| "#dim protected CPackDownloadArchives[CPackDownloadCount] " |
| "{@CPACK_INNOSETUP_DOWNLOAD_ARCHIVES_INTERNAL@}\n" |
| "#dim protected CPackDownloadHashes[CPackDownloadCount] " |
| "{@CPACK_INNOSETUP_DOWNLOAD_HASHES_INTERNAL@}\n" |
| "#dim protected CPackDownloadComponents[CPackDownloadCount] " |
| "{@CPACK_INNOSETUP_DOWNLOAD_COMPONENTS_INTERNAL@}"; |
| |
| std::string output; |
| if (!ConfigureString(downloadLines, output)) { |
| return false; |
| } |
| codeIncludes.push_back(output); |
| } |
| |
| // Add the required script |
| const std::string& componentsScriptTemplate = |
| FindTemplate("ISComponents.pas"); |
| if (componentsScriptTemplate.empty()) { |
| cmCPackLogger(cmCPackLog::LOG_ERROR, |
| "Could not find additional Inno Setup script file." |
| << std::endl); |
| return false; |
| } |
| |
| codeIncludes.push_back("#include " + QuotePath(componentsScriptTemplate) + |
| "\n"); |
| |
| return true; |
| } |
| |
| bool cmCPackInnoSetupGenerator::ConfigureISScript() |
| { |
| const std::string& isScriptTemplate = FindTemplate("ISScript.template.in"); |
| const std::string& isScriptFile = |
| cmStrCat(GetOption("CPACK_TOPLEVEL_DIRECTORY"), "/ISScript.iss"); |
| |
| if (isScriptTemplate.empty()) { |
| cmCPackLogger(cmCPackLog::LOG_ERROR, |
| "Could not find Inno Setup installer template file." |
| << std::endl); |
| return false; |
| } |
| |
| // Create internal variables |
| std::vector<std::string> setupSection; |
| for (const auto& i : setupDirectives) { |
| setupSection.emplace_back(cmStrCat(i.first, '=', TranslateBool(i.second))); |
| } |
| |
| // Also create comments if the sections are empty |
| const std::string& defaultMessage = |
| "; CPack didn't find any entries for this section"; |
| |
| if (IsSet("CPACK_CREATE_DESKTOP_LINKS") && |
| !GetOption("CPACK_CREATE_DESKTOP_LINKS").Get()->empty()) { |
| cmCPackInnoSetupKeyValuePairs tasks; |
| tasks["Name"] = "\"desktopicon\""; |
| tasks["Description"] = "\"{cm:CreateDesktopIcon}\""; |
| tasks["GroupDescription"] = "\"{cm:AdditionalIcons}\""; |
| tasks["Flags"] = "unchecked"; |
| |
| if (!desktopIconComponents.empty()) { |
| tasks["Components"] = cmJoin(desktopIconComponents, " "); |
| } |
| |
| SetOption("CPACK_INNOSETUP_TASKS_INTERNAL", ISKeyValueLine(tasks)); |
| } else { |
| SetOption("CPACK_INNOSETUP_TASKS_INTERNAL", defaultMessage); |
| } |
| |
| SetOption("CPACK_INNOSETUP_INCLUDES_INTERNAL", |
| includeDirectives.empty() ? "; No extra script files specified" |
| : cmJoin(includeDirectives, "\n")); |
| SetOption("CPACK_INNOSETUP_SETUP_INTERNAL", |
| setupSection.empty() ? defaultMessage |
| : cmJoin(setupSection, "\n")); |
| SetOption("CPACK_INNOSETUP_LANGUAGES_INTERNAL", |
| languageInstructions.empty() ? defaultMessage |
| : cmJoin(languageInstructions, "\n")); |
| SetOption("CPACK_INNOSETUP_DIRS_INTERNAL", |
| dirInstructions.empty() ? defaultMessage |
| : cmJoin(dirInstructions, "\n")); |
| SetOption("CPACK_INNOSETUP_FILES_INTERNAL", |
| fileInstructions.empty() ? defaultMessage |
| : cmJoin(fileInstructions, "\n")); |
| SetOption("CPACK_INNOSETUP_TYPES_INTERNAL", |
| typeInstructions.empty() ? defaultMessage |
| : cmJoin(typeInstructions, "\n")); |
| SetOption("CPACK_INNOSETUP_COMPONENTS_INTERNAL", |
| componentInstructions.empty() |
| ? defaultMessage |
| : cmJoin(componentInstructions, "\n")); |
| SetOption("CPACK_INNOSETUP_ICONS_INTERNAL", |
| iconInstructions.empty() ? defaultMessage |
| : cmJoin(iconInstructions, "\n")); |
| SetOption("CPACK_INNOSETUP_RUN_INTERNAL", |
| runInstructions.empty() ? defaultMessage |
| : cmJoin(runInstructions, "\n")); |
| SetOption("CPACK_INNOSETUP_CODE_INTERNAL", |
| codeIncludes.empty() ? "{ No extra code files specified }" |
| : cmJoin(codeIncludes, "\n")); |
| |
| cmCPackLogger(cmCPackLog::LOG_VERBOSE, |
| "Configure file: " << isScriptTemplate << " to " |
| << isScriptFile << std::endl); |
| |
| return ConfigureFile(isScriptTemplate, isScriptFile); |
| } |
| |
| bool cmCPackInnoSetupGenerator::Compile() |
| { |
| const std::string& isScriptFile = |
| cmStrCat(GetOption("CPACK_TOPLEVEL_DIRECTORY"), "/ISScript.iss"); |
| const std::string& isccLogFile = |
| cmStrCat(GetOption("CPACK_TOPLEVEL_DIRECTORY"), "/ISCCOutput.log"); |
| |
| std::vector<std::string> isccArgs; |
| |
| // Custom defines |
| for (const std::string& i : GetOptions()) { |
| if (cmHasPrefix(i, "CPACK_INNOSETUP_DEFINE_")) { |
| const std::string& name = i.substr(cmStrLen("CPACK_INNOSETUP_DEFINE_")); |
| isccArgs.push_back( |
| cmStrCat("\"/D", name, '=', TranslateBool(GetOption(i)), '"')); |
| } |
| } |
| |
| if (IsSet("CPACK_INNOSETUP_EXECUTABLE_ARGUMENTS")) { |
| const cmList args(GetOption("CPACK_INNOSETUP_EXECUTABLE_ARGUMENTS")); |
| |
| isccArgs.insert(isccArgs.end(), args.begin(), args.end()); |
| } |
| |
| const std::string& isccCmd = |
| cmStrCat(QuotePath(GetOption("CPACK_INSTALLER_PROGRAM"), PathType::Native), |
| ' ', cmJoin(isccArgs, " "), ' ', QuotePath(isScriptFile)); |
| |
| cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << isccCmd << std::endl); |
| |
| std::string output; |
| int retVal = 1; |
| const bool res = cmSystemTools::RunSingleCommand( |
| isccCmd, &output, &output, &retVal, nullptr, this->GeneratorVerbose, |
| cmDuration::zero()); |
| |
| if (!res || retVal) { |
| cmGeneratedFileStream ofs(isccLogFile); |
| ofs << "# Run command: " << isccCmd << std::endl |
| << "# Output:" << std::endl |
| << output << std::endl; |
| cmCPackLogger(cmCPackLog::LOG_ERROR, |
| "Problem running ISCC. Please check " |
| << isccLogFile << " for errors." << std::endl); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool cmCPackInnoSetupGenerator::BuildDownloadedComponentArchive( |
| cmCPackComponent* component, const std::string& uploadDirectory, |
| std::string* hash) |
| { |
| // Remove the old archive, if one exists |
| const std::string& archiveFile = |
| uploadDirectory + '/' + component->ArchiveFile; |
| cmCPackLogger(cmCPackLog::LOG_OUTPUT, |
| "- Building downloaded component archive: " << archiveFile |
| << std::endl); |
| if (cmSystemTools::FileExists(archiveFile, true)) { |
| if (!cmSystemTools::RemoveFile(archiveFile)) { |
| cmCPackLogger(cmCPackLog::LOG_ERROR, |
| "Unable to remove archive file " << archiveFile |
| << std::endl); |
| return false; |
| } |
| } |
| |
| // Find a ZIP program |
| if (!IsSet("ZIP_EXECUTABLE")) { |
| ReadListFile("Internal/CPack/CPackZIP.cmake"); |
| |
| if (!IsSet("ZIP_EXECUTABLE")) { |
| cmCPackLogger(cmCPackLog::LOG_ERROR, |
| "Unable to find ZIP program" << std::endl); |
| return false; |
| } |
| } |
| |
| if (!RequireOption("CPACK_TOPLEVEL_DIRECTORY") || |
| !RequireOption("CPACK_TEMPORARY_DIRECTORY") || |
| !RequireOption("CPACK_ZIP_NEED_QUOTES") || |
| !RequireOption("CPACK_ZIP_COMMAND")) { |
| return false; |
| } |
| |
| // The directory where this component's files reside |
| const std::string& dirName = |
| cmStrCat(GetOption("CPACK_TEMPORARY_DIRECTORY"), '/', component->Name); |
| |
| // Build the list of files to go into this archive |
| const std::string& zipListFileName = |
| cmStrCat(GetOption("CPACK_TEMPORARY_DIRECTORY"), "/winZip.filelist"); |
| const bool needQuotesInFile = GetOption("CPACK_ZIP_NEED_QUOTES").IsOn(); |
| { // the scope is needed for cmGeneratedFileStream |
| cmGeneratedFileStream out(zipListFileName); |
| for (const std::string& i : component->Files) { |
| out << (needQuotesInFile ? Quote(i) : i) << std::endl; |
| } |
| } |
| |
| // Build the archive in the upload area |
| std::string cmd = GetOption("CPACK_ZIP_COMMAND"); |
| cmsys::SystemTools::ReplaceString(cmd, "<ARCHIVE>", archiveFile.c_str()); |
| cmsys::SystemTools::ReplaceString(cmd, "<FILELIST>", |
| zipListFileName.c_str()); |
| std::string output; |
| int retVal = -1; |
| const bool res = cmSystemTools::RunSingleCommand( |
| cmd, &output, &output, &retVal, dirName.c_str(), this->GeneratorVerbose, |
| cmDuration::zero()); |
| if (!res || retVal) { |
| std::string tmpFile = |
| cmStrCat(GetOption("CPACK_TOPLEVEL_DIRECTORY"), "/CompressZip.log"); |
| cmGeneratedFileStream ofs(tmpFile); |
| ofs << "# Run command: " << cmd << std::endl |
| << "# Output:" << std::endl |
| << output << std::endl; |
| cmCPackLogger(cmCPackLog::LOG_ERROR, |
| "Problem running zip command: " << cmd << std::endl |
| << "Please check " << tmpFile |
| << " for errors" |
| << std::endl); |
| return false; |
| } |
| |
| // Try to get the SHA256 hash of the archive file |
| if (hash == nullptr) { |
| return true; |
| } |
| |
| #ifdef _WIN32 |
| const std::string& hashCmd = |
| cmStrCat("certutil -hashfile ", QuotePath(archiveFile), " SHA256"); |
| |
| std::string hashOutput; |
| int hashRetVal = -1; |
| const bool hashRes = cmSystemTools::RunSingleCommand( |
| hashCmd, &hashOutput, &hashOutput, &hashRetVal, nullptr, |
| this->GeneratorVerbose, cmDuration::zero()); |
| if (!hashRes || hashRetVal) { |
| cmCPackLogger(cmCPackLog::LOG_WARNING, |
| "Problem running certutil command: " << hashCmd |
| << std::endl); |
| } |
| *hash = cmTrimWhitespace(cmTokenize(hashOutput, "\n").at(1)); |
| |
| if (hash->length() != 64) { |
| cmCPackLogger(cmCPackLog::LOG_WARNING, |
| "Problem parsing certutil output of command: " << hashCmd |
| << std::endl); |
| hash->clear(); |
| } |
| #endif |
| |
| return true; |
| } |
| |
| cmValue cmCPackInnoSetupGenerator::RequireOption(const std::string& key) |
| { |
| cmValue value = GetOption(key); |
| |
| if (!value) { |
| cmCPackLogger(cmCPackLog::LOG_ERROR, |
| "Required variable " << key << " not set" << std::endl); |
| } |
| |
| return value; |
| } |
| |
| std::string cmCPackInnoSetupGenerator::CustomComponentInstallDirectory( |
| const cmCPackComponent* component) |
| { |
| cmValue outputDir = GetOption( |
| cmStrCat("CPACK_INNOSETUP_", component->Name, "_INSTALL_DIRECTORY")); |
| if (outputDir) { |
| std::string destDir = cmSystemTools::ConvertToWindowsOutputPath(outputDir); |
| cmStripSuffixIfExists(destDir, '\\'); |
| |
| /* |
| * Add a dir instruction for the custom directory |
| * (only once and not for Inno Setup constants ending with '}') |
| */ |
| static std::vector<std::string> customDirectories; |
| if (!cmHasSuffix(destDir, '}') && |
| std::find(customDirectories.begin(), customDirectories.end(), |
| component->Name) == customDirectories.end()) { |
| cmCPackInnoSetupKeyValuePairs params; |
| params["Name"] = QuotePath(destDir); |
| params["Components"] = |
| CreateRecursiveComponentPath(component->Group, component->Name); |
| |
| dirInstructions.push_back(ISKeyValueLine(params)); |
| customDirectories.push_back(component->Name); |
| } |
| return destDir; |
| } |
| |
| return "{app}"; |
| } |
| |
| std::string cmCPackInnoSetupGenerator::TranslateBool(const std::string& value) |
| { |
| if (value.empty()) { |
| return value; |
| } |
| |
| SetOptionIfNotSet("CPACK_INNOSETUP_USE_CMAKE_BOOL_FORMAT", "ON"); |
| if (GetOption("CPACK_INNOSETUP_USE_CMAKE_BOOL_FORMAT").IsOn()) { |
| if (cmIsOn(value)) { |
| return "yes"; |
| } |
| if (cmIsOff(value)) { |
| return "no"; |
| } |
| } |
| |
| return value; |
| } |
| |
| std::string cmCPackInnoSetupGenerator::ISKeyValueLine( |
| const cmCPackInnoSetupKeyValuePairs& params) |
| { |
| /* |
| * To simplify readability of the generated code, the keys are sorted. |
| * Unknown keys are ignored to avoid errors during compilation. |
| */ |
| static const char* const availableKeys[] = { |
| "Source", "DestDir", "Name", "Filename", |
| "Description", "GroupDescription", "MessagesFile", "Types", |
| "ExternalSize", "BeforeInstall", "Flags", "Components", |
| "Tasks" |
| }; |
| |
| std::vector<std::string> keys; |
| for (const char* i : availableKeys) { |
| if (params.count(i)) { |
| keys.emplace_back(cmStrCat(i, ": ", params.at(i))); |
| } |
| } |
| |
| return cmJoin(keys, "; "); |
| } |
| |
| std::string cmCPackInnoSetupGenerator::CreateRecursiveComponentPath( |
| cmCPackComponentGroup* group, const std::string& path) |
| { |
| if (group == nullptr) { |
| return path; |
| } |
| |
| const std::string& newPath = |
| path.empty() ? group->Name : cmStrCat(group->Name, '\\', path); |
| return CreateRecursiveComponentPath(group->ParentGroup, newPath); |
| } |
| |
| void cmCPackInnoSetupGenerator::CreateRecursiveComponentGroups( |
| cmCPackComponentGroup* group) |
| { |
| if (group == nullptr) { |
| return; |
| } |
| |
| CreateRecursiveComponentGroups(group->ParentGroup); |
| |
| static std::vector<cmCPackComponentGroup*> processedGroups; |
| if (std::find(processedGroups.begin(), processedGroups.end(), group) == |
| processedGroups.end()) { |
| processedGroups.push_back(group); |
| |
| cmCPackInnoSetupKeyValuePairs params; |
| |
| params["Name"] = Quote(CreateRecursiveComponentPath(group)); |
| params["Description"] = Quote(group->DisplayName); |
| |
| componentInstructions.push_back(ISKeyValueLine(params)); |
| } |
| } |
| |
| std::string cmCPackInnoSetupGenerator::Quote(const std::string& string) |
| { |
| if (cmHasPrefix(string, '"') && cmHasSuffix(string, '"')) { |
| return Quote(string.substr(1, string.length() - 2)); |
| } |
| |
| // Double quote syntax |
| std::string nString = string; |
| cmSystemTools::ReplaceString(nString, "\"", "\"\""); |
| return cmStrCat('"', nString, '"'); |
| } |
| |
| std::string cmCPackInnoSetupGenerator::QuotePath(const std::string& path, |
| PathType type) |
| { |
| #ifdef _WIN32 |
| static_cast<void>(type); |
| #else |
| if (type == PathType::Native) { |
| return Quote(cmSystemTools::ConvertToUnixOutputPath(path)); |
| } |
| #endif |
| return Quote(cmSystemTools::ConvertToWindowsOutputPath(path)); |
| } |
| |
| std::string cmCPackInnoSetupGenerator::PrepareForConstant( |
| const std::string& string) |
| { |
| std::string nString = string; |
| |
| cmSystemTools::ReplaceString(nString, "%", "%25"); // First replacement! |
| cmSystemTools::ReplaceString(nString, "\"", "%22"); |
| cmSystemTools::ReplaceString(nString, ",", "%2c"); |
| cmSystemTools::ReplaceString(nString, "|", "%7c"); |
| cmSystemTools::ReplaceString(nString, "}", "%7d"); |
| |
| return nString; |
| } |