diff options
Diffstat (limited to 'src/libs/installer/installercalculator.cpp')
-rw-r--r-- | src/libs/installer/installercalculator.cpp | 296 |
1 files changed, 152 insertions, 144 deletions
diff --git a/src/libs/installer/installercalculator.cpp b/src/libs/installer/installercalculator.cpp index 21e82fc9a..4c53824af 100644 --- a/src/libs/installer/installercalculator.cpp +++ b/src/libs/installer/installercalculator.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2022 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -29,6 +29,8 @@ #include "installercalculator.h" #include "component.h" +#include "componentalias.h" +#include "componentmodel.h" #include "packagemanagercore.h" #include "settings.h" #include <globals.h> @@ -41,156 +43,188 @@ namespace QInstaller { \internal */ -InstallerCalculator::InstallerCalculator(PackageManagerCore *core, const QList<Component *> &allComponents, const AutoDependencyHash &autoDependencyComponentHash) - : m_core(core) - , m_allComponents(allComponents) +InstallerCalculator::InstallerCalculator(PackageManagerCore *core, const AutoDependencyHash &autoDependencyComponentHash) + : CalculatorBase(core) , m_autoDependencyComponentHash(autoDependencyComponentHash) { } -InstallerCalculator::InstallReasonType InstallerCalculator::installReasonType(const Component *c) const +InstallerCalculator::~InstallerCalculator() { - return m_toInstallComponentIdReasonHash.value(c->name()).first; } -QString InstallerCalculator::installReason(const Component *component) const +bool InstallerCalculator::solve() { - InstallerCalculator::InstallReasonType reason = installReasonType(component); + if (!solve(m_core->aliasesMarkedForInstallation())) + return false; + + // Subtract components added by aliases + QList<Component *> components = m_core->componentsMarkedForInstallation(); + for (auto *component : qAsConst(m_resolvedComponents)) + components.removeAll(component); + + return solve(components); +} + +QString InstallerCalculator::resolutionText(Component *component) const +{ + const Resolution reason = resolutionType(component); switch (reason) { - case Automatic: + case Resolution::Automatic: return QCoreApplication::translate("InstallerCalculator", "Components added as automatic dependencies:"); - case Dependent: + case Resolution::Dependent: return QCoreApplication::translate("InstallerCalculator", "Components added as " - "dependency for \"%1\":").arg(installReasonReferencedComponent(component)); - case Resolved: + "dependency for \"%1\":").arg(referencedComponent(component)); + case Resolution::Resolved: return QCoreApplication::translate("InstallerCalculator", "Components that have resolved dependencies:"); - case Selected: + case Resolution::Selected: return QCoreApplication::translate("InstallerCalculator", "Selected components without dependencies:"); + case Resolution::Alias: + return QCoreApplication::translate("InstallerCalculator", + "Components selected by alias \"%1\":").arg(referencedComponent(component)); + default: + Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid install resolution detected!"); } return QString(); } -QList<Component*> InstallerCalculator::orderedComponentsToInstall() const -{ - return m_orderedComponentsToInstall; -} - -QString InstallerCalculator::componentsToInstallError() const -{ - return m_componentsToInstallError; -} - -bool InstallerCalculator::appendComponentsToInstall(const QList<Component *> &components, bool modelReset, const bool revertFromInstall) +bool InstallerCalculator::solve(const QList<Component *> &components) { if (components.isEmpty()) return true; QList<Component*> notAppendedComponents; // for example components with unresolved dependencies - foreach (Component *component, components) { + for (Component *component : qAsConst(components)){ if (!component) continue; - // When model has been reseted, there should not be components with the same name if (m_toInstallComponentIds.contains(component->name())) { - if (modelReset) { - const QString errorMessage = recursionError(component); - qCWarning(QInstaller::lcInstallerInstallLog).noquote() << errorMessage; - m_componentsToInstallError.append(errorMessage); - Q_ASSERT_X(!m_toInstallComponentIds.contains(component->name()), Q_FUNC_INFO, - qPrintable(errorMessage)); - return false; - } - if (!revertFromInstall) { - // We can end up here if component is already added as dependency and - // user explicitly selects it to install - continue; - } + const QString errorMessage = recursionError(component); + qCWarning(QInstaller::lcInstallerInstallLog).noquote() << errorMessage; + m_errorString.append(errorMessage); + Q_ASSERT_X(!m_toInstallComponentIds.contains(component->name()), Q_FUNC_INFO, + qPrintable(errorMessage)); + return false; } - if (component->dependencies().isEmpty()) - realAppendToInstallComponents(component, QString(), revertFromInstall); + if (component->currentDependencies().isEmpty()) + addComponentForInstall(component); else notAppendedComponents.append(component); } - foreach (Component *component, notAppendedComponents) { - if (!appendComponentToInstall(component, QString(), revertFromInstall)) + for (Component *component : qAsConst(notAppendedComponents)) { + if (!solveComponent(component)) return false; } // All regular dependencies are resolved. Now we are looking for auto depend on components. - QSet<Component *> foundAutoDependOnList = autodependencyComponents(revertFromInstall); - + QSet<Component *> foundAutoDependOnList = autodependencyComponents(); if (!foundAutoDependOnList.isEmpty()) - return appendComponentsToInstall(foundAutoDependOnList.values(), modelReset, revertFromInstall); + return solve(foundAutoDependOnList.values()); return true; } -bool InstallerCalculator::removeComponentsFromInstall(const QList<Component *> &components) +bool InstallerCalculator::solve(const QList<ComponentAlias *> &aliases) { - return appendComponentsToInstall(components, false, true); + if (aliases.isEmpty()) + return true; + + QList<ComponentAlias *> notAppendedAliases; // Aliases that require other aliases + for (auto *alias : aliases) { + if (!alias) + continue; + + if (m_toInstallComponentAliases.contains(alias->name())) { + const QString errorMessage = QCoreApplication::translate("InstallerCalculator", + "Recursion detected, component alias \"%1\" already added.").arg(alias->name()); + qCWarning(QInstaller::lcInstallerInstallLog).noquote() << errorMessage; + m_errorString.append(errorMessage); + + Q_ASSERT_X(!m_toInstallComponentAliases.contains(alias->name()), Q_FUNC_INFO, + qPrintable(errorMessage)); + + return false; + } + + if (alias->aliases().isEmpty()) { + if (!addComponentsFromAlias(alias)) + return false; + } else { + notAppendedAliases.append(alias); + } + } + + for (auto *alias : qAsConst(notAppendedAliases)) { + if (!solveAlias(alias)) + return false; + } + + return true; } -QString InstallerCalculator::installReasonReferencedComponent(const Component *component) const +void InstallerCalculator::addComponentForInstall(Component *component, const QString &version) { - const QString componentName = component->name(); - if (m_referenceCount.contains(componentName)) - return m_referenceCount.value(componentName).first(); - return m_toInstallComponentIdReasonHash.value(componentName, - qMakePair(InstallerCalculator::Selected, QString())).second; + if (!m_componentsForAutodepencencyCheck.contains(component)) + m_componentsForAutodepencencyCheck.append(component); + + if (!component->isInstalled(version) || (m_core->isUpdater() && component->isUpdateAvailable())) { + m_resolvedComponents.append(component); + m_toInstallComponentIds.insert(component->name()); + } } -void InstallerCalculator::insertInstallReason(const Component *component, - const InstallReasonType installReason, const QString &referencedComponentName, const bool revertFromInstall) +bool InstallerCalculator::addComponentsFromAlias(ComponentAlias *alias) { - if (revertFromInstall && m_toInstallComponentIdReasonHash.contains(component->name())) { - m_toInstallComponentIdReasonHash.remove(component->name()); - } else if (!m_toInstallComponentIdReasonHash.contains(component->name())) { - m_toInstallComponentIdReasonHash.insert(component->name(), - qMakePair(installReason, referencedComponentName)); + QList<Component *> componentsToAdd; + for (auto *component : alias->components()) { + if (m_toInstallComponentIds.contains(component->name())) + continue; // Already added + + componentsToAdd.append(component); + // Updates the model, so that we also check the descendant + // components when calculating components to install + updateCheckState(component, Qt::Checked); + insertResolution(component, Resolution::Alias, alias->name()); } + + m_toInstallComponentAliases.insert(alias->name()); + return solve(componentsToAdd); } -void InstallerCalculator::realAppendToInstallComponents(Component *component, const QString &version, const bool revertFromInstall) +QString InstallerCalculator::recursionError(Component *component) const { - if (!m_componentsForAutodepencencyCheck.contains(component)) - m_componentsForAutodepencencyCheck.append(component); - if (revertFromInstall) { - if (m_toInstallComponentIds.contains(component->name())) { - // Component is no longer added as dependency and is not explicitly selected for install by user - if (m_referenceCount.value(component->name()).isEmpty() && !component->isSelected()) { - m_toInstallComponentIds.remove(component->name()); - m_orderedComponentsToInstall.removeAll(component); - } - } - } else { - if (!component->isInstalled(version) - || (m_core->isUpdater() && component->isUpdateAvailable())) { - m_toInstallComponentIds.insert(component->name()); - m_orderedComponentsToInstall.append(component); - } - } + return QCoreApplication::translate("InstallerCalculator", "Recursion detected, component \"%1\" " + "already added with reason: \"%2\"").arg(component->name(), resolutionText(component)); +} + +bool InstallerCalculator::updateCheckState(Component *component, Qt::CheckState state) +{ + ComponentModel *currentModel = m_core->isUpdater() + ? m_core->updaterComponentModel() + : m_core->defaultComponentModel(); + + const QModelIndex &idx = currentModel->indexFromComponentName(component->treeName()); + return currentModel->setData(idx, state, Qt::CheckStateRole); } -bool InstallerCalculator::appendComponentToInstall(Component *component, const QString &version, bool revertFromInstall) +bool InstallerCalculator::solveComponent(Component *component, const QString &version) { - const QStringList dependenciesList = component->dependencies(); - QSet<QString> allDependencies(dependenciesList.begin(), dependenciesList.end()); + const QStringList dependenciesList = component->currentDependencies(); QString requiredDependencyVersion = version; - foreach (const QString &dependencyComponentName, allDependencies) { + for (const QString &dependencyComponentName : dependenciesList) { // PackageManagerCore::componentByName returns 0 if dependencyComponentName contains a // version which is not available - Component *dependencyComponent = - PackageManagerCore::componentByName(dependencyComponentName, m_allComponents); + Component *dependencyComponent = m_core->componentByName(dependencyComponentName); if (!dependencyComponent) { const QString errorMessage = QCoreApplication::translate("InstallerCalculator", "Cannot find missing dependency \"%1\" for \"%2\".").arg(dependencyComponentName, component->name()); qCWarning(QInstaller::lcInstallerInstallLog).noquote() << errorMessage; - m_componentsToInstallError.append(errorMessage); + m_errorString.append(errorMessage); if (component->packageManagerCore()->settings().allowUnstableComponents()) { component->setUnstable(Component::UnstableError::MissingDependency, errorMessage); continue; @@ -198,8 +232,6 @@ bool InstallerCalculator::appendComponentToInstall(Component *component, const Q return false; } } - if (revertFromInstall && dependencyComponent->forcedInstallation()) - continue; //Check if component requires higher version than what might be already installed bool isUpdateRequired = false; QString requiredName; @@ -207,87 +239,65 @@ bool InstallerCalculator::appendComponentToInstall(Component *component, const Q PackageManagerCore::parseNameAndVersion(dependencyComponentName, &requiredName, &requiredVersion); if (!requiredVersion.isEmpty() && !dependencyComponent->value(scInstalledVersion).isEmpty()) { - QRegExp compEx(QLatin1String("([<=>]+)(.*)")); - const QString installedVersion = compEx.exactMatch(dependencyComponent->value(scInstalledVersion)) ? - compEx.cap(2) : dependencyComponent->value(scInstalledVersion); + static const QRegularExpression compEx(QLatin1String("^([<=>]+)(.*)$")); + QRegularExpressionMatch match = compEx.match(dependencyComponent->value(scInstalledVersion)); + const QString installedVersion = match.hasMatch() + ? match.captured(2) : dependencyComponent->value(scInstalledVersion); - requiredVersion = compEx.exactMatch(requiredVersion) ? compEx.cap(2) : requiredVersion; + match = compEx.match(requiredVersion); + requiredVersion = match.hasMatch() ? match.captured(2) : requiredVersion; if (KDUpdater::compareVersion(requiredVersion, installedVersion) >= 1 ) { isUpdateRequired = true; requiredDependencyVersion = requiredVersion; } } - - // Component can be requested for install by several component (as a dependency). - // Keep the reference count to a component, so we know when component needs to be - // removed from install. - if (!revertFromInstall && m_toInstallComponentIds.contains(dependencyComponentName)) { - QStringList value = m_referenceCount.value(dependencyComponentName, QStringList()); - if (!value.contains(component->name())) - value << component->name(); - m_referenceCount.insert(dependencyComponentName, value); - } - if (revertFromInstall) { - if (m_toInstallComponentIds.contains(dependencyComponentName) - && m_referenceCount.contains(dependencyComponentName)) { - QStringList value = m_referenceCount.value(dependencyComponentName); - if (value.contains(component->name())) - value.removeOne(component->name()); - if (value.isEmpty()) - m_referenceCount.remove(dependencyComponentName); - else - m_referenceCount.insert(dependencyComponentName, value); - } - insertInstallReason(dependencyComponent, InstallerCalculator::Dependent, component->name(), true); - if (!appendComponentToInstall(dependencyComponent, requiredDependencyVersion, revertFromInstall)) - return false; - m_visitedComponents.remove(component); - } - //Check dependencies only if //- Dependency is not installed or update requested, nor newer version of dependency component required //- And dependency component is not already added for install //- And component is not already added for install, then dependencies are already resolved - if (!revertFromInstall && ((!dependencyComponent->isInstalled() || dependencyComponent->updateRequested()) + if (((!dependencyComponent->isInstalled() || dependencyComponent->updateRequested()) || isUpdateRequired) && (!m_toInstallComponentIds.contains(dependencyComponent->name()) && !m_toInstallComponentIds.contains(component->name()))) { if (m_visitedComponents.value(component).contains(dependencyComponent)) { const QString errorMessage = recursionError(component); qCWarning(QInstaller::lcInstallerInstallLog).noquote() << errorMessage; - m_componentsToInstallError = errorMessage; + m_errorString = errorMessage; Q_ASSERT_X(!m_visitedComponents.value(component).contains(dependencyComponent), Q_FUNC_INFO, qPrintable(errorMessage)); return false; } m_visitedComponents[component].insert(dependencyComponent); + // add needed dependency components to the next run + insertResolution(dependencyComponent, Resolution::Dependent, + component->name()); - QStringList value = m_referenceCount.value(dependencyComponentName, QStringList()); - if (!value.contains(component->name())) - value << component->name(); - m_referenceCount.insert(dependencyComponentName, value); - insertInstallReason(dependencyComponent, InstallerCalculator::Dependent, component->name()); - if (!appendComponentToInstall(dependencyComponent, requiredDependencyVersion, revertFromInstall)) + if (!solveComponent(dependencyComponent, requiredDependencyVersion)) return false; } } - if (!revertFromInstall && !m_toInstallComponentIds.contains(component->name())) { - realAppendToInstallComponents(component, requiredDependencyVersion, revertFromInstall); - insertInstallReason(component, InstallerCalculator::Resolved); - } - if (revertFromInstall && m_toInstallComponentIds.contains(component->name())) { - realAppendToInstallComponents(component, requiredDependencyVersion, revertFromInstall); + + if (!m_toInstallComponentIds.contains(component->name())) { + addComponentForInstall(component, requiredDependencyVersion); + insertResolution(component, Resolution::Resolved); } return true; } -QString InstallerCalculator::recursionError(const Component *component) const +bool InstallerCalculator::solveAlias(ComponentAlias *alias) { - return QCoreApplication::translate("InstallerCalculator", "Recursion detected, component \"%1\" " - "already added with reason: \"%2\"").arg(component->name(), installReason(component)); + for (auto *requiredAlias : alias->aliases()) { + if (!solveAlias(requiredAlias)) + return false; + } + + if (m_toInstallComponentAliases.contains(alias->name())) + return true; + + return addComponentsFromAlias(alias); } -QSet<Component *> InstallerCalculator::autodependencyComponents(const bool revertFromInstall) +QSet<Component *> InstallerCalculator::autodependencyComponents() { // All regular dependencies are resolved. Now we are looking for auto depend on components. // m_componentsForAutodepencencyCheck is a list of recently calculated components to be installed @@ -295,16 +305,16 @@ QSet<Component *> InstallerCalculator::autodependencyComponents(const bool rever // dependency components based on that list. QSet<Component *> foundAutoDependOnList; for (const Component *component : qAsConst(m_componentsForAutodepencencyCheck)) { - if (!m_autoDependencyComponentHash.contains(component->name())) + if (!m_autoDependencyComponentHash.contains(component->name()) + || (m_core->isUpdater() && !component->updateRequested())) continue; for (const QString& autoDependency : m_autoDependencyComponentHash.value(component->name())) { // If a components is already installed or is scheduled for installation, no need to check // for auto depend installation. - if ((!revertFromInstall && m_toInstallComponentIds.contains(autoDependency)) - || (revertFromInstall && !m_toInstallComponentIds.contains(autoDependency))) { + if (m_toInstallComponentIds.contains(autoDependency)) { continue; } - Component *autoDependComponent = PackageManagerCore::componentByName(autoDependency, m_allComponents); + Component *autoDependComponent = m_core->componentByName(autoDependency); if (!autoDependComponent) continue; if ((!autoDependComponent->isInstalled() @@ -314,10 +324,8 @@ QSet<Component *> InstallerCalculator::autodependencyComponents(const bool rever // are other autodependencies as well if (autoDependComponent->isAutoDependOn(m_toInstallComponentIds)) { foundAutoDependOnList.insert(autoDependComponent); - insertInstallReason(autoDependComponent, InstallerCalculator::Automatic); + insertResolution(autoDependComponent, Resolution::Automatic); } - } else if (revertFromInstall && m_toInstallComponentIds.contains(autoDependComponent->name())) { - foundAutoDependOnList.insert(autoDependComponent); } } } |