diff options
Diffstat (limited to 'src/libs/installer/installercalculator.cpp')
-rw-r--r-- | src/libs/installer/installercalculator.cpp | 274 |
1 files changed, 186 insertions, 88 deletions
diff --git a/src/libs/installer/installercalculator.cpp b/src/libs/installer/installercalculator.cpp index 363837dd1..4c53824af 100644 --- a/src/libs/installer/installercalculator.cpp +++ b/src/libs/installer/installercalculator.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2017 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,12 +29,12 @@ #include "installercalculator.h" #include "component.h" +#include "componentalias.h" +#include "componentmodel.h" #include "packagemanagercore.h" #include "settings.h" #include <globals.h> -#include <QDebug> - namespace QInstaller { /*! @@ -43,140 +43,188 @@ namespace QInstaller { \internal */ -InstallerCalculator::InstallerCalculator(const QList<Component *> &allComponents) - : m_allComponents(allComponents) +InstallerCalculator::InstallerCalculator(PackageManagerCore *core, const AutoDependencyHash &autoDependencyComponentHash) + : CalculatorBase(core) + , m_autoDependencyComponentHash(autoDependencyComponentHash) { } -void InstallerCalculator::insertInstallReason(Component *component, - InstallReasonType installReason, const QString &referencedComponentName) +InstallerCalculator::~InstallerCalculator() { - // keep the first reason - if (m_toInstallComponentIdReasonHash.contains(component->name())) - return; - m_toInstallComponentIdReasonHash.insert(component->name(), - qMakePair(installReason, referencedComponentName)); } -InstallerCalculator::InstallReasonType InstallerCalculator::installReasonType(Component *c) const +bool InstallerCalculator::solve() { - return m_toInstallComponentIdReasonHash.value(c->name(), - qMakePair(InstallerCalculator::Selected, QString())).first; -} + if (!solve(m_core->aliasesMarkedForInstallation())) + return false; -QString InstallerCalculator::installReasonReferencedComponent(Component *component) const -{ - return m_toInstallComponentIdReasonHash.value(component->name(), - qMakePair(InstallerCalculator::Selected, QString())).second; + // 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::installReason(Component *component) const +QString InstallerCalculator::resolutionText(Component *component) const { - InstallerCalculator::InstallReasonType reason = installReasonType(component); + 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; -} - -void InstallerCalculator::realAppendToInstallComponents(Component *component, const QString &version) -{ - if (!component->isInstalled(version) || component->updateRequested()) { - m_orderedComponentsToInstall.append(component); - m_toInstallComponentIds.insert(component->name()); - } -} - -QString InstallerCalculator::recursionError(Component *component) -{ - return QCoreApplication::translate("InstallerCalculator", "Recursion detected, component \"%1\" " - "already added with reason: \"%2\"").arg(component->name(), installReason(component)); -} - -bool InstallerCalculator::appendComponentsToInstall(const QList<Component *> &components) +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; if (m_toInstallComponentIds.contains(component->name())) { const QString errorMessage = recursionError(component); qCWarning(QInstaller::lcInstallerInstallLog).noquote() << errorMessage; - m_componentsToInstallError.append(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); + if (component->currentDependencies().isEmpty()) + addComponentForInstall(component); else notAppendedComponents.append(component); } - foreach (Component *component, notAppendedComponents) { - if (!appendComponentToInstall(component)) + for (Component *component : qAsConst(notAppendedComponents)) { + if (!solveComponent(component)) return false; } - QList<Component *> foundAutoDependOnList; // All regular dependencies are resolved. Now we are looking for auto depend on components. - foreach (Component *component, m_allComponents) { - // If a components is already installed or is scheduled for installation, no need to check - // for auto depend installation. - if ((!component->isInstalled() || component->updateRequested()) - && !m_toInstallComponentIds.contains(component->name())) { - // If we figure out a component requests auto installation, keep it to resolve - // their dependencies as well. - if (component->isAutoDependOn(m_toInstallComponentIds)) { - foundAutoDependOnList.append(component); - insertInstallReason(component, InstallerCalculator::Automatic); - } + QSet<Component *> foundAutoDependOnList = autodependencyComponents(); + if (!foundAutoDependOnList.isEmpty()) + return solve(foundAutoDependOnList.values()); + + return true; +} + +bool InstallerCalculator::solve(const QList<ComponentAlias *> &aliases) +{ + 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); } } - if (!foundAutoDependOnList.isEmpty()) - return appendComponentsToInstall(foundAutoDependOnList); + for (auto *alias : qAsConst(notAppendedAliases)) { + if (!solveAlias(alias)) + return false; + } + return true; } -bool InstallerCalculator::appendComponentToInstall(Component *component, const QString &version) +void InstallerCalculator::addComponentForInstall(Component *component, const QString &version) +{ + 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()); + } +} + +bool InstallerCalculator::addComponentsFromAlias(ComponentAlias *alias) +{ + 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); +} + +QString InstallerCalculator::recursionError(Component *component) const +{ + 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::solveComponent(Component *component, const QString &version) { - QSet<QString> allDependencies = component->dependencies().toSet(); + 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; @@ -191,11 +239,13 @@ 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; @@ -212,27 +262,75 @@ bool InstallerCalculator::appendComponentToInstall(Component *component, const Q 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 - insertInstallReason(dependencyComponent, InstallerCalculator::Dependent, + insertResolution(dependencyComponent, Resolution::Dependent, component->name()); - if (!appendComponentToInstall(dependencyComponent, requiredDependencyVersion)) + if (!solveComponent(dependencyComponent, requiredDependencyVersion)) return false; } } if (!m_toInstallComponentIds.contains(component->name())) { - realAppendToInstallComponents(component, requiredDependencyVersion); - insertInstallReason(component, InstallerCalculator::Resolved); + addComponentForInstall(component, requiredDependencyVersion); + insertResolution(component, Resolution::Resolved); } return true; } +bool InstallerCalculator::solveAlias(ComponentAlias *alias) +{ + 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() +{ + // 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 + // (normal components and regular dependencies components), and we check possible installable auto + // dependency components based on that list. + QSet<Component *> foundAutoDependOnList; + for (const Component *component : qAsConst(m_componentsForAutodepencencyCheck)) { + 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 (m_toInstallComponentIds.contains(autoDependency)) { + continue; + } + Component *autoDependComponent = m_core->componentByName(autoDependency); + if (!autoDependComponent) + continue; + if ((!autoDependComponent->isInstalled() + || (m_core->isUpdater() && autoDependComponent->isUpdateAvailable())) + && !m_toInstallComponentIds.contains(autoDependComponent->name())) { + // One of the components autodependons is requested for install, check if there + // are other autodependencies as well + if (autoDependComponent->isAutoDependOn(m_toInstallComponentIds)) { + foundAutoDependOnList.insert(autoDependComponent); + insertResolution(autoDependComponent, Resolution::Automatic); + } + } + } + } + m_componentsForAutodepencencyCheck.clear(); + return foundAutoDependOnList; +} + } // namespace QInstaller |