/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCPackIFWPackage.h" #include #include #include #include #include #include "cmCPackComponentGroup.h" #include "cmCPackIFWCommon.h" #include "cmCPackIFWGenerator.h" #include "cmCPackIFWInstaller.h" #include "cmCPackLog.h" // IWYU pragma: keep #include "cmGeneratedFileStream.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmTimestamp.h" #include "cmXMLWriter.h" //---------------------------------------------------------- CompareStruct --- cmCPackIFWPackage::CompareStruct::CompareStruct() : Type(cmCPackIFWPackage::CompareNone) { } //------------------------------------------------------- DependenceStruct --- cmCPackIFWPackage::DependenceStruct::DependenceStruct() = default; cmCPackIFWPackage::DependenceStruct::DependenceStruct( const std::string& dependence) { // Preferred format is name and version are separated by a colon (:), but // note that this is only supported with QtIFW 3.1 or later. Backward // compatibility allows a hyphen (-) as a separator instead, but names then // cannot contain a hyphen. size_t pos; if ((pos = dependence.find(':')) == std::string::npos) { pos = dependence.find('-'); } if (pos != std::string::npos) { this->Name = dependence.substr(0, pos); ++pos; if (pos == dependence.size()) { // Nothing after the separator. Treat this as no version constraint. return; } const auto versionPart = cm::string_view(dependence.data() + pos, dependence.size() - pos); if (cmHasLiteralPrefix(versionPart, "<=")) { this->Compare.Type = cmCPackIFWPackage::CompareLessOrEqual; this->Compare.Value = std::string(versionPart.substr(2)); } else if (cmHasLiteralPrefix(versionPart, ">=")) { this->Compare.Type = cmCPackIFWPackage::CompareGreaterOrEqual; this->Compare.Value = std::string(versionPart.substr(2)); } else if (cmHasPrefix(versionPart, '<')) { this->Compare.Type = cmCPackIFWPackage::CompareLess; this->Compare.Value = std::string(versionPart.substr(1)); } else if (cmHasPrefix(versionPart, '=')) { this->Compare.Type = cmCPackIFWPackage::CompareEqual; this->Compare.Value = std::string(versionPart.substr(1)); } else if (cmHasPrefix(versionPart, '>')) { this->Compare.Type = cmCPackIFWPackage::CompareGreater; this->Compare.Value = std::string(versionPart.substr(1)); } else { // We found no operator but a version specification is still expected to // follow. The default behavior is to treat this the same as =. We // explicitly record that as our type (it simplifies our logic a little // and is also clearer). this->Compare.Type = cmCPackIFWPackage::CompareEqual; this->Compare.Value = std::string(versionPart); } } else { this->Name = dependence; } } std::string cmCPackIFWPackage::DependenceStruct::NameWithCompare() const { std::string result = this->Name; if (this->Name.find('-') != std::string::npos) { // When a name contains a hyphen, we must use a colon after the name to // prevent the hyphen from being parsed by QtIFW as the separator between // the name and the version. Note that a colon is only supported with // QtIFW 3.1 or later. result += ":"; } else if (this->Compare.Type != cmCPackIFWPackage::CompareNone || !this->Compare.Value.empty()) { // No hyphen in the name and we know a version part will follow. Use a // hyphen as a separator since this works for all QtIFW versions. result += "-"; } if (this->Compare.Type == cmCPackIFWPackage::CompareLessOrEqual) { result += "<="; } else if (this->Compare.Type == cmCPackIFWPackage::CompareGreaterOrEqual) { result += ">="; } else if (this->Compare.Type == cmCPackIFWPackage::CompareLess) { result += "<"; } else if (this->Compare.Type == cmCPackIFWPackage::CompareEqual) { result += "="; } else if (this->Compare.Type == cmCPackIFWPackage::CompareGreater) { result += ">"; } result += this->Compare.Value; return result; } //------------------------------------------------------ cmCPackIFWPackage --- cmCPackIFWPackage::cmCPackIFWPackage() : Installer(nullptr) { } std::string cmCPackIFWPackage::GetComponentName(cmCPackComponent* component) { if (!component) { return ""; } const char* option = this->GetOption("CPACK_IFW_COMPONENT_" + cmsys::SystemTools::UpperCase(component->Name) + "_NAME"); return option ? option : component->Name; } void cmCPackIFWPackage::DefaultConfiguration() { this->DisplayName.clear(); this->Description.clear(); this->Version.clear(); this->ReleaseDate.clear(); this->Script.clear(); this->Licenses.clear(); this->UserInterfaces.clear(); this->Translations.clear(); this->SortingPriority.clear(); this->UpdateText.clear(); this->Default.clear(); this->Essential.clear(); this->Virtual.clear(); this->ForcedInstallation.clear(); this->RequiresAdminRights.clear(); } // Default configuration (all in one package) int cmCPackIFWPackage::ConfigureFromOptions() { // Restore default configuration this->DefaultConfiguration(); // Name this->Name = this->Generator->GetRootPackageName(); // Display name if (const char* option = this->GetOption("CPACK_PACKAGE_NAME")) { this->DisplayName[""] = option; } else { this->DisplayName[""] = "Your package"; } // Description if (const char* option = this->GetOption("CPACK_PACKAGE_DESCRIPTION_SUMMARY")) { this->Description[""] = option; } else { this->Description[""] = "Your package description"; } // Version if (const char* option = this->GetOption("CPACK_PACKAGE_VERSION")) { this->Version = option; } else { this->Version = "1.0.0"; } this->ForcedInstallation = "true"; return 1; } int cmCPackIFWPackage::ConfigureFromComponent(cmCPackComponent* component) { if (!component) { return 0; } // Restore default configuration this->DefaultConfiguration(); std::string prefix = "CPACK_IFW_COMPONENT_" + cmsys::SystemTools::UpperCase(component->Name) + "_"; // Display name this->DisplayName[""] = component->DisplayName; // Description this->Description[""] = component->Description; // Version if (const char* optVERSION = this->GetOption(prefix + "VERSION")) { this->Version = optVERSION; } else if (const char* optPACKAGE_VERSION = this->GetOption("CPACK_PACKAGE_VERSION")) { this->Version = optPACKAGE_VERSION; } else { this->Version = "1.0.0"; } // Script if (const char* option = this->GetOption(prefix + "SCRIPT")) { this->Script = option; } // User interfaces if (const char* option = this->GetOption(prefix + "USER_INTERFACES")) { this->UserInterfaces.clear(); cmExpandList(option, this->UserInterfaces); } // CMake dependencies if (!component->Dependencies.empty()) { for (cmCPackComponent* dep : component->Dependencies) { this->Dependencies.insert(this->Generator->ComponentPackages[dep]); } } // Licenses if (const char* option = this->GetOption(prefix + "LICENSES")) { this->Licenses.clear(); cmExpandList(option, this->Licenses); if (this->Licenses.size() % 2 != 0) { cmCPackIFWLogger( WARNING, prefix << "LICENSES" << " should contain pairs of and ." << std::endl); this->Licenses.clear(); } } // Priority if (const char* option = this->GetOption(prefix + "PRIORITY")) { this->SortingPriority = option; cmCPackIFWLogger( WARNING, "The \"PRIORITY\" option is set " << "for component \"" << component->Name << "\", but there option is " << "deprecated. Please use \"SORTING_PRIORITY\" option instead." << std::endl); } // Default this->Default = component->IsDisabledByDefault ? "false" : "true"; // Essential if (this->IsOn(prefix + "ESSENTIAL")) { this->Essential = "true"; } // Virtual this->Virtual = component->IsHidden ? "true" : ""; // ForcedInstallation this->ForcedInstallation = component->IsRequired ? "true" : "false"; return this->ConfigureFromPrefix(prefix); } int cmCPackIFWPackage::ConfigureFromGroup(cmCPackComponentGroup* group) { if (!group) { return 0; } // Restore default configuration this->DefaultConfiguration(); std::string prefix = "CPACK_IFW_COMPONENT_GROUP_" + cmsys::SystemTools::UpperCase(group->Name) + "_"; this->DisplayName[""] = group->DisplayName; this->Description[""] = group->Description; // Version if (const char* optVERSION = this->GetOption(prefix + "VERSION")) { this->Version = optVERSION; } else if (const char* optPACKAGE_VERSION = this->GetOption("CPACK_PACKAGE_VERSION")) { this->Version = optPACKAGE_VERSION; } else { this->Version = "1.0.0"; } // Script if (const char* option = this->GetOption(prefix + "SCRIPT")) { this->Script = option; } // User interfaces if (const char* option = this->GetOption(prefix + "USER_INTERFACES")) { this->UserInterfaces.clear(); cmExpandList(option, this->UserInterfaces); } // Licenses if (const char* option = this->GetOption(prefix + "LICENSES")) { this->Licenses.clear(); cmExpandList(option, this->Licenses); if (this->Licenses.size() % 2 != 0) { cmCPackIFWLogger( WARNING, prefix << "LICENSES" << " should contain pairs of and ." << std::endl); this->Licenses.clear(); } } // Priority if (const char* option = this->GetOption(prefix + "PRIORITY")) { this->SortingPriority = option; cmCPackIFWLogger( WARNING, "The \"PRIORITY\" option is set " << "for component group \"" << group->Name << "\", but there option is " << "deprecated. Please use \"SORTING_PRIORITY\" option instead." << std::endl); } return this->ConfigureFromPrefix(prefix); } int cmCPackIFWPackage::ConfigureFromGroup(const std::string& groupName) { // Group configuration cmCPackComponentGroup group; std::string prefix = "CPACK_COMPONENT_GROUP_" + cmsys::SystemTools::UpperCase(groupName) + "_"; if (const char* option = this->GetOption(prefix + "DISPLAY_NAME")) { group.DisplayName = option; } else { group.DisplayName = group.Name; } if (const char* option = this->GetOption(prefix + "DESCRIPTION")) { group.Description = option; } group.IsBold = this->IsOn(prefix + "BOLD_TITLE"); group.IsExpandedByDefault = this->IsOn(prefix + "EXPANDED"); // Package configuration group.Name = groupName; if (this->Generator) { this->Name = this->Generator->GetGroupPackageName(&group); } else { this->Name = group.Name; } return this->ConfigureFromGroup(&group); } // Common options for components and groups int cmCPackIFWPackage::ConfigureFromPrefix(const std::string& prefix) { // Temporary variable for full option name std::string option; // Display name option = prefix + "DISPLAY_NAME"; if (this->IsSetToEmpty(option)) { this->DisplayName.clear(); } else if (const char* value = this->GetOption(option)) { cmCPackIFWPackage::ExpandListArgument(value, this->DisplayName); } // Description option = prefix + "DESCRIPTION"; if (this->IsSetToEmpty(option)) { this->Description.clear(); } else if (const char* value = this->GetOption(option)) { cmCPackIFWPackage::ExpandListArgument(value, this->Description); } // Release date option = prefix + "RELEASE_DATE"; if (this->IsSetToEmpty(option)) { this->ReleaseDate.clear(); } else if (const char* value = this->GetOption(option)) { this->ReleaseDate = value; } // Sorting priority option = prefix + "SORTING_PRIORITY"; if (this->IsSetToEmpty(option)) { this->SortingPriority.clear(); } else if (const char* value = this->GetOption(option)) { this->SortingPriority = value; } // Update text option = prefix + "UPDATE_TEXT"; if (this->IsSetToEmpty(option)) { this->UpdateText.clear(); } else if (const char* value = this->GetOption(option)) { this->UpdateText = value; } // Translations option = prefix + "TRANSLATIONS"; if (this->IsSetToEmpty(option)) { this->Translations.clear(); } else if (const char* value = this->GetOption(option)) { this->Translations.clear(); cmExpandList(value, this->Translations); } // QtIFW dependencies std::vector deps; option = prefix + "DEPENDS"; if (const char* value = this->GetOption(option)) { cmExpandList(value, deps); } option = prefix + "DEPENDENCIES"; if (const char* value = this->GetOption(option)) { cmExpandList(value, deps); } for (std::string const& d : deps) { DependenceStruct dep(d); if (this->Generator->Packages.count(dep.Name)) { cmCPackIFWPackage& depPkg = this->Generator->Packages[dep.Name]; dep.Name = depPkg.Name; } bool hasDep = this->Generator->DependentPackages.count(dep.Name) > 0; DependenceStruct& depRef = this->Generator->DependentPackages[dep.Name]; if (!hasDep) { depRef = dep; } this->AlienDependencies.insert(&depRef); } // Automatic dependency on option = prefix + "AUTO_DEPEND_ON"; if (this->IsSetToEmpty(option)) { this->AlienAutoDependOn.clear(); } else if (const char* value = this->GetOption(option)) { std::vector depsOn = cmExpandedList(value); for (std::string const& d : depsOn) { DependenceStruct dep(d); if (this->Generator->Packages.count(dep.Name)) { cmCPackIFWPackage& depPkg = this->Generator->Packages[dep.Name]; dep.Name = depPkg.Name; } bool hasDep = this->Generator->DependentPackages.count(dep.Name) > 0; DependenceStruct& depRef = this->Generator->DependentPackages[dep.Name]; if (!hasDep) { depRef = dep; } this->AlienAutoDependOn.insert(&depRef); } } // Visibility option = prefix + "VIRTUAL"; if (this->IsSetToEmpty(option)) { this->Virtual.clear(); } else if (this->IsOn(option)) { this->Virtual = "true"; } // Default selection option = prefix + "DEFAULT"; if (this->IsSetToEmpty(option)) { this->Default.clear(); } else if (const char* value = this->GetOption(option)) { std::string lowerValue = cmsys::SystemTools::LowerCase(value); if (lowerValue == "true") { this->Default = "true"; } else if (lowerValue == "false") { this->Default = "false"; } else if (lowerValue == "script") { this->Default = "script"; } else { this->Default = value; } } // Forsed installation option = prefix + "FORCED_INSTALLATION"; if (this->IsSetToEmpty(option)) { this->ForcedInstallation.clear(); } else if (this->IsOn(option)) { this->ForcedInstallation = "true"; } else if (this->IsSetToOff(option)) { this->ForcedInstallation = "false"; } // Replaces option = prefix + "REPLACES"; if (this->IsSetToEmpty(option)) { this->Replaces.clear(); } else if (const char* value = this->GetOption(option)) { this->Replaces.clear(); cmExpandList(value, this->Replaces); } // Requires admin rights option = prefix + "REQUIRES_ADMIN_RIGHTS"; if (this->IsSetToEmpty(option)) { this->RequiresAdminRights.clear(); } else if (this->IsOn(option)) { this->RequiresAdminRights = "true"; } else if (this->IsSetToOff(option)) { this->RequiresAdminRights = "false"; } // Checkable option = prefix + "CHECKABLE"; if (this->IsSetToEmpty(option)) { this->Checkable.clear(); } else if (this->IsOn(option)) { this->Checkable = "true"; } else if (this->IsSetToOff(option)) { this->Checkable = "false"; } return 1; } void cmCPackIFWPackage::GeneratePackageFile() { // Lazy directory initialization if (this->Directory.empty()) { if (this->Installer) { this->Directory = this->Installer->Directory + "/packages/" + this->Name; } else if (this->Generator) { this->Directory = this->Generator->toplevel + "/packages/" + this->Name; } } // Output stream cmGeneratedFileStream fout(this->Directory + "/meta/package.xml"); cmXMLWriter xout(fout); xout.StartDocument(); this->WriteGeneratedByToStrim(xout); xout.StartElement("Package"); // DisplayName (with translations) for (auto const& dn : this->DisplayName) { xout.StartElement("DisplayName"); if (!dn.first.empty()) { xout.Attribute("xml:lang", dn.first); } xout.Content(dn.second); xout.EndElement(); } // Description (with translations) for (auto const& d : this->Description) { xout.StartElement("Description"); if (!d.first.empty()) { xout.Attribute("xml:lang", d.first); } xout.Content(d.second); xout.EndElement(); } // Update text if (!this->UpdateText.empty()) { xout.Element("UpdateText", this->UpdateText); } xout.Element("Name", this->Name); xout.Element("Version", this->Version); if (!this->ReleaseDate.empty()) { xout.Element("ReleaseDate", this->ReleaseDate); } else { xout.Element("ReleaseDate", cmTimestamp().CurrentTime("%Y-%m-%d", true)); } // Script (copy to meta dir) if (!this->Script.empty()) { std::string name = cmSystemTools::GetFilenameName(this->Script); std::string path = this->Directory + "/meta/" + name; cmsys::SystemTools::CopyFileIfDifferent(this->Script, path); xout.Element("Script", name); } // User Interfaces (copy to meta dir) std::vector userInterfaces = this->UserInterfaces; for (std::string& userInterface : userInterfaces) { std::string name = cmSystemTools::GetFilenameName(userInterface); std::string path = this->Directory + "/meta/" + name; cmsys::SystemTools::CopyFileIfDifferent(userInterface, path); userInterface = name; } if (!userInterfaces.empty()) { xout.StartElement("UserInterfaces"); for (std::string const& userInterface : userInterfaces) { xout.Element("UserInterface", userInterface); } xout.EndElement(); } // Translations (copy to meta dir) std::vector translations = this->Translations; for (std::string& translation : translations) { std::string name = cmSystemTools::GetFilenameName(translation); std::string path = this->Directory + "/meta/" + name; cmsys::SystemTools::CopyFileIfDifferent(translation, path); translation = name; } if (!translations.empty()) { xout.StartElement("Translations"); for (std::string const& translation : translations) { xout.Element("Translation", translation); } xout.EndElement(); } // Dependencies const bool hyphensInNamesUnsupported = this->Generator && !this->Generator->FrameworkVersion.empty() && this->IsVersionLess("3.1"); bool warnUnsupportedNames = false; std::set compDepSet; for (DependenceStruct* ad : this->AlienDependencies) { compDepSet.insert(*ad); } for (cmCPackIFWPackage* d : this->Dependencies) { compDepSet.insert(DependenceStruct(d->Name)); } // Write dependencies if (!compDepSet.empty()) { std::ostringstream dependencies; auto it = compDepSet.begin(); warnUnsupportedNames |= hyphensInNamesUnsupported && it->Name.find('-') != std::string::npos; dependencies << it->NameWithCompare(); ++it; while (it != compDepSet.end()) { warnUnsupportedNames |= hyphensInNamesUnsupported && it->Name.find('-') != std::string::npos; dependencies << "," << it->NameWithCompare(); ++it; } xout.Element("Dependencies", dependencies.str()); } // Automatic dependency on std::set compAutoDepSet; for (DependenceStruct* aad : this->AlienAutoDependOn) { compAutoDepSet.insert(*aad); } // Write automatic dependency on if (!compAutoDepSet.empty()) { std::ostringstream dependencies; auto it = compAutoDepSet.begin(); warnUnsupportedNames |= hyphensInNamesUnsupported && it->Name.find('-') != std::string::npos; dependencies << it->NameWithCompare(); ++it; while (it != compAutoDepSet.end()) { warnUnsupportedNames |= hyphensInNamesUnsupported && it->Name.find('-') != std::string::npos; dependencies << "," << it->NameWithCompare(); ++it; } xout.Element("AutoDependOn", dependencies.str()); } if (warnUnsupportedNames) { cmCPackIFWLogger( WARNING, "The dependencies for component \"" << this->Name << "\" specify names that contain hyphens. " << "This requires QtIFW 3.1 or later, but you are using version " << this->Generator->FrameworkVersion << std::endl); } // Licenses (copy to meta dir) std::vector licenses = this->Licenses; for (size_t i = 1; i < licenses.size(); i += 2) { std::string name = cmSystemTools::GetFilenameName(licenses[i]); std::string path = this->Directory + "/meta/" + name; cmsys::SystemTools::CopyFileIfDifferent(licenses[i], path); licenses[i] = name; } if (!licenses.empty()) { xout.StartElement("Licenses"); for (size_t i = 0; i < licenses.size(); i += 2) { xout.StartElement("License"); xout.Attribute("name", licenses[i]); xout.Attribute("file", licenses[i + 1]); xout.EndElement(); } xout.EndElement(); } if (!this->ForcedInstallation.empty()) { xout.Element("ForcedInstallation", this->ForcedInstallation); } // Replaces if (!this->Replaces.empty()) { std::ostringstream replaces; auto it = this->Replaces.begin(); replaces << *it; ++it; while (it != this->Replaces.end()) { replaces << "," << *it; ++it; } xout.Element("Replaces", replaces.str()); } if (!this->RequiresAdminRights.empty()) { xout.Element("RequiresAdminRights", this->RequiresAdminRights); } if (!this->Virtual.empty()) { xout.Element("Virtual", this->Virtual); } else if (!this->Default.empty()) { xout.Element("Default", this->Default); } // Essential if (!this->Essential.empty()) { xout.Element("Essential", this->Essential); } // Priority if (!this->SortingPriority.empty()) { xout.Element("SortingPriority", this->SortingPriority); } // Checkable if (!this->Checkable.empty()) { xout.Element("Checkable", this->Checkable); } xout.EndElement(); xout.EndDocument(); }