summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/libs/installer/componentmodel.cpp41
-rw-r--r--src/libs/installer/componentmodel.h8
-rw-r--r--src/libs/installer/componentselectionpage_p.cpp16
-rw-r--r--src/libs/installer/installercalculator.cpp232
-rw-r--r--src/libs/installer/installercalculator.h39
-rw-r--r--src/libs/installer/packagemanagercore.cpp87
-rw-r--r--src/libs/installer/packagemanagercore.h2
-rw-r--r--src/libs/installer/packagemanagercore_p.cpp73
-rw-r--r--src/libs/installer/packagemanagercore_p.h19
-rw-r--r--src/libs/installer/qinstallerglobal.cpp14
-rw-r--r--src/libs/installer/qinstallerglobal.h5
-rw-r--r--src/libs/installer/uninstallercalculator.cpp170
-rw-r--r--src/libs/installer/uninstallercalculator.h19
-rw-r--r--tests/auto/installer/solver/tst_solver.cpp31
14 files changed, 515 insertions, 241 deletions
diff --git a/src/libs/installer/componentmodel.cpp b/src/libs/installer/componentmodel.cpp
index 88e502164..3bb9bc72e 100644
--- a/src/libs/installer/componentmodel.cpp
+++ b/src/libs/installer/componentmodel.cpp
@@ -58,14 +58,14 @@ namespace QInstaller {
*/
/*!
- \fn void QInstaller::ComponentModel::checkStateChanged(const QModelIndex &index)
+ \fn void QInstaller::ComponentModel::componentsCheckStateChanged(const QList<QModelIndex> &indexes)
- This signal is emitted whenever the checked state of a component is changed. The \a index value
- indicates the QModelIndex representation of the component as seen from the model.
+ This signal is emitted whenever the checked state of components are changed. The \a indexes value
+ indicates the QModelIndexes representation of the components as seen from the model.
*/
/*!
- \fn void QInstaller::ComponentModel::checkStateChanged(QInstaller::ComponentModel::ModelState state)
+ \fn void QInstaller::ComponentModel::modelCheckStateChanged(QInstaller::ComponentModel::ModelState state)
This signal is emitted whenever the checked state of a model is changed after all state
calculations have taken place. The \a state is a combination of \c ModelStateFlag values
@@ -238,7 +238,7 @@ QVariant ComponentModel::data(const QModelIndex &index, int role) const
/*!
Sets the \a role data for the item at \a index to \a value. Returns true if successful;
otherwise returns false. The dataChanged() signal is emitted if the data was successfully set.
- The checkStateChanged() signals are emitted in addition if the checked state of the item is set.
+ The componentsCheckStateChanged() signal is emitted in addition if the checked state of the item is set.
*/
bool ComponentModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
@@ -260,12 +260,13 @@ bool ComponentModel::setData(const QModelIndex &index, const QVariant &value, in
const Qt::CheckState oldValue = component->checkState();
newValue = (oldValue == Qt::Checked) ? Qt::Unchecked : Qt::Checked;
}
- QSet<QModelIndex> changed = updateCheckedState(nodes << component, newValue);
+ const QList<QModelIndex> changed = updateCheckedState(nodes << component, newValue);
foreach (const QModelIndex &changedIndex, changed) {
emit dataChanged(changedIndex, changedIndex);
- emit checkStateChanged(changedIndex);
}
- updateAndEmitModelState(); // update the internal state
+ updateModelState();
+ if (changed.count() > 0)
+ emit componentsCheckStateChanged(changed);
} else {
component->setData(value, role);
emit dataChanged(index, index);
@@ -425,12 +426,12 @@ void ComponentModel::reset(QList<Component *> rootComponents)
Sets the checked state of every component in the model to be \a state.
The ComponentModel::PartiallyChecked flag is ignored by this function. Note that components
- are not changed if they are not checkable. The dataChanged() and checkStateChanged() signals
+ are not changed if they are not checkable. The dataChanged() and componentsCheckStateChanged() signals
are emitted.
*/
void ComponentModel::setCheckedState(QInstaller::ComponentModel::ModelStateFlag state)
{
- QSet<QModelIndex> changed;
+ QList<QModelIndex> changed;
switch (state) {
case AllChecked:
changed = updateCheckedState(m_currentCheckedState[Qt::Unchecked], Qt::Checked);
@@ -453,9 +454,9 @@ void ComponentModel::setCheckedState(QInstaller::ComponentModel::ModelStateFlag
// notify about changes done to the model
foreach (const QModelIndex &index, changed) {
emit dataChanged(index, index);
- emit checkStateChanged(index);
}
- updateAndEmitModelState(); // update the internal state
+ updateModelState();
+ emit modelCheckStateChanged(m_modelState);
}
@@ -493,10 +494,11 @@ void ComponentModel::postModelReset()
}
m_currentCheckedState = m_initialCheckedState;
- updateAndEmitModelState(); // update the internal state
+ updateModelState(); // update the internal state
+ emit modelCheckStateChanged(m_modelState);
}
-void ComponentModel::updateAndEmitModelState()
+void ComponentModel::updateModelState()
{
m_modelState = ComponentModel::DefaultChecked;
if (m_initialCheckedState != m_currentCheckedState)
@@ -511,8 +513,6 @@ void ComponentModel::updateAndEmitModelState()
m_modelState |= ComponentModel::AllChecked;
m_modelState &= ~ComponentModel::PartiallyChecked;
}
-
- emit checkStateChanged(m_modelState);
}
void ComponentModel::collectComponents(Component *const component, const QModelIndex &parent) const
@@ -556,7 +556,7 @@ static Qt::CheckState verifyPartiallyChecked(Component *component)
} // namespace ComponentModelPrivate
-QSet<QModelIndex> ComponentModel::updateCheckedState(const ComponentSet &components, Qt::CheckState state)
+QList<QModelIndex> ComponentModel::updateCheckedState(const ComponentSet &components, const Qt::CheckState state)
{
// get all parent nodes for the components we're going to update
QMultiMap<QString, Component *> sortedNodesMap;
@@ -567,7 +567,7 @@ QSet<QModelIndex> ComponentModel::updateCheckedState(const ComponentSet &compone
}
}
- QSet<QModelIndex> changed;
+ QList<QModelIndex> changed;
const ComponentList sortedNodes = sortedNodesMap.values();
// we can start in descending order to check node and tri-state nodes properly
for (int i = sortedNodes.count(); i > 0; i--) {
@@ -589,7 +589,10 @@ QSet<QModelIndex> ComponentModel::updateCheckedState(const ComponentSet &compone
continue;
node->setCheckState(newState);
- changed.insert(indexFromComponentName(node->treeName()));
+ QModelIndex index = indexFromComponentName(node->treeName());
+ //Prepend to the list so that install order is correct (parent first)
+ if (!changed.contains(index))
+ changed.prepend(indexFromComponentName(node->treeName()));
m_currentCheckedState[Qt::Checked].remove(node);
m_currentCheckedState[Qt::Unchecked].remove(node);
diff --git a/src/libs/installer/componentmodel.h b/src/libs/installer/componentmodel.h
index 78d7e6401..f9fbae47a 100644
--- a/src/libs/installer/componentmodel.h
+++ b/src/libs/installer/componentmodel.h
@@ -90,17 +90,17 @@ public Q_SLOTS:
void setCheckedState(QInstaller::ComponentModel::ModelStateFlag state);
Q_SIGNALS:
- void checkStateChanged(const QModelIndex &index);
- void checkStateChanged(QInstaller::ComponentModel::ModelState state);
+ void componentsCheckStateChanged(const QList<QModelIndex> &indexes);
+ void modelCheckStateChanged(QInstaller::ComponentModel::ModelState state);
private Q_SLOTS:
void onVirtualStateChanged();
private:
void postModelReset();
- void updateAndEmitModelState();
+ void updateModelState();
void collectComponents(Component *const component, const QModelIndex &parent) const;
- QSet<QModelIndex> updateCheckedState(const ComponentSet &components, Qt::CheckState state);
+ QList<QModelIndex> updateCheckedState(const ComponentSet &components, const Qt::CheckState state);
private:
PackageManagerCore *m_core;
diff --git a/src/libs/installer/componentselectionpage_p.cpp b/src/libs/installer/componentselectionpage_p.cpp
index e775c0cb4..5e44d153c 100644
--- a/src/libs/installer/componentselectionpage_p.cpp
+++ b/src/libs/installer/componentselectionpage_p.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -185,10 +185,16 @@ ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionP
m_stackedLayout->addWidget(progressStackedWidget);
m_stackedLayout->setCurrentIndex(0);
- connect(m_allModel, SIGNAL(checkStateChanged(QInstaller::ComponentModel::ModelState)), this,
- SLOT(onModelStateChanged(QInstaller::ComponentModel::ModelState)));
- connect(m_updaterModel, SIGNAL(checkStateChanged(QInstaller::ComponentModel::ModelState)),
- this, SLOT(onModelStateChanged(QInstaller::ComponentModel::ModelState)));
+ connect(m_allModel, &ComponentModel::modelCheckStateChanged,
+ this, &ComponentSelectionPagePrivate::onModelStateChanged);
+ connect(m_updaterModel, &ComponentModel::modelCheckStateChanged,
+ this, &ComponentSelectionPagePrivate::onModelStateChanged);
+
+ connect(m_allModel, &ComponentModel::componentsCheckStateChanged, this,
+ [=]() { onModelStateChanged(m_allModel->checkedState());});
+
+ connect(m_updaterModel, &ComponentModel::componentsCheckStateChanged, this,
+ [=]() { onModelStateChanged(m_allModel->checkedState());});
connect(m_core, SIGNAL(metaJobProgress(int)), this, SLOT(onProgressChanged(int)));
connect(m_core, SIGNAL(metaJobInfoMessage(QString)), this, SLOT(setMessage(QString)));
diff --git a/src/libs/installer/installercalculator.cpp b/src/libs/installer/installercalculator.cpp
index ba8bf9db3..307c0d2fb 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) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -33,8 +33,6 @@
#include "settings.h"
#include <globals.h>
-#include <QDebug>
-
namespace QInstaller {
/*!
@@ -43,34 +41,18 @@ namespace QInstaller {
\internal
*/
-InstallerCalculator::InstallerCalculator(const QList<Component *> &allComponents)
+InstallerCalculator::InstallerCalculator(const QList<Component *> &allComponents, const QHash<QString, QStringList> &autoDependencyComponentHash)
: m_allComponents(allComponents)
+ , m_autoDependencyComponentHash(autoDependencyComponentHash)
{
}
-void InstallerCalculator::insertInstallReason(Component *component,
- InstallReasonType installReason, const QString &referencedComponentName)
+InstallerCalculator::InstallReasonType InstallerCalculator::installReasonType(const Component *c) const
{
- // keep the first reason
- if (m_toInstallComponentIdReasonHash.contains(component->name()))
- return;
- m_toInstallComponentIdReasonHash.insert(component->name(),
- qMakePair(installReason, referencedComponentName));
+ return m_toInstallComponentIdReasonHash.value(c->name()).first;
}
-InstallerCalculator::InstallReasonType InstallerCalculator::installReasonType(Component *c) const
-{
- return m_toInstallComponentIdReasonHash.value(c->name(),
- qMakePair(InstallerCalculator::Selected, QString())).first;
-}
-
-QString InstallerCalculator::installReasonReferencedComponent(Component *component) const
-{
- return m_toInstallComponentIdReasonHash.value(component->name(),
- qMakePair(InstallerCalculator::Selected, QString())).second;
-}
-
-QString InstallerCalculator::installReason(Component *component) const
+QString InstallerCalculator::installReason(const Component *component) const
{
InstallerCalculator::InstallReasonType reason = installReasonType(component);
switch (reason) {
@@ -100,69 +82,98 @@ 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::appendComponentsToInstall(const QList<Component *> &components, bool modelReset, const bool revertFromInstall)
{
if (components.isEmpty())
return true;
QList<Component*> notAppendedComponents; // for example components with unresolved dependencies
- foreach (Component *component, components){
+ foreach (Component *component, components) {
+ if (!component)
+ continue;
+ // When model has been reseted, there should not be components with the same name
if (m_toInstallComponentIds.contains(component->name())) {
- 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 (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;
+ }
}
if (component->dependencies().isEmpty())
- realAppendToInstallComponents(component);
+ realAppendToInstallComponents(component, QString(), revertFromInstall);
else
notAppendedComponents.append(component);
}
foreach (Component *component, notAppendedComponents) {
- if (!appendComponentToInstall(component))
+ if (!appendComponentToInstall(component, QString(), revertFromInstall))
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(revertFromInstall);
if (!foundAutoDependOnList.isEmpty())
- return appendComponentsToInstall(foundAutoDependOnList);
+ return appendComponentsToInstall(foundAutoDependOnList.values(), modelReset, revertFromInstall);
+
return true;
}
-bool InstallerCalculator::appendComponentToInstall(Component *component, const QString &version)
+bool InstallerCalculator::removeComponentsFromInstall(const QList<Component *> &components)
+{
+ return appendComponentsToInstall(components, false, true);
+}
+
+QString InstallerCalculator::installReasonReferencedComponent(const Component *component) const
+{
+ 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;
+}
+
+void InstallerCalculator::insertInstallReason(const Component *component,
+ const InstallReasonType installReason, const QString &referencedComponentName, const bool revertFromInstall)
+{
+ 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));
+ }
+}
+
+void InstallerCalculator::realAppendToInstallComponents(Component *component, const QString &version, const bool revertFromInstall)
+{
+ 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) || component->updateRequested()) {
+ m_toInstallComponentIds.insert(component->name());
+ m_orderedComponentsToInstall.append(component);
+ }
+ }
+}
+
+bool InstallerCalculator::appendComponentToInstall(Component *component, const QString &version, bool revertFromInstall)
{
const QStringList dependenciesList = component->dependencies();
QSet<QString> allDependencies(dependenciesList.begin(), dependenciesList.end());
@@ -185,6 +196,8 @@ 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;
@@ -203,11 +216,38 @@ bool InstallerCalculator::appendComponentToInstall(Component *component, const Q
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 (((!dependencyComponent->isInstalled() || dependencyComponent->updateRequested())
+ if (!revertFromInstall && ((!dependencyComponent->isInstalled() || dependencyComponent->updateRequested())
|| isUpdateRequired) && (!m_toInstallComponentIds.contains(dependencyComponent->name())
&& !m_toInstallComponentIds.contains(component->name()))) {
if (m_visitedComponents.value(component).contains(dependencyComponent)) {
@@ -220,20 +260,66 @@ bool InstallerCalculator::appendComponentToInstall(Component *component, const Q
}
m_visitedComponents[component].insert(dependencyComponent);
- // add needed dependency components to the next run
- insertInstallReason(dependencyComponent, InstallerCalculator::Dependent,
- component->name());
-
- if (!appendComponentToInstall(dependencyComponent, requiredDependencyVersion))
+ 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))
return false;
}
}
-
- if (!m_toInstallComponentIds.contains(component->name())) {
- realAppendToInstallComponents(component, requiredDependencyVersion);
+ 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);
+ }
return true;
}
+QString InstallerCalculator::recursionError(const Component *component) const
+{
+ return QCoreApplication::translate("InstallerCalculator", "Recursion detected, component \"%1\" "
+ "already added with reason: \"%2\"").arg(component->name(), installReason(component));
+}
+
+QSet<Component *> InstallerCalculator::autodependencyComponents(const bool revertFromInstall)
+{
+ // 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()))
+ 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))) {
+ continue;
+ }
+ Component *autoDependComponent = PackageManagerCore::componentByName(autoDependency, m_allComponents);
+ if (!autoDependComponent)
+ continue;
+ if ((!autoDependComponent->isInstalled() || autoDependComponent->updateRequested())
+ && !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);
+ insertInstallReason(autoDependComponent, InstallerCalculator::Automatic);
+ }
+ } else if (revertFromInstall && m_toInstallComponentIds.contains(autoDependComponent->name())) {
+ foundAutoDependOnList.insert(autoDependComponent);
+ }
+ }
+ }
+ m_componentsForAutodepencencyCheck.clear();
+ return foundAutoDependOnList;
+}
+
} // namespace QInstaller
diff --git a/src/libs/installer/installercalculator.h b/src/libs/installer/installercalculator.h
index b2d05bdbe..2841d8671 100644
--- a/src/libs/installer/installercalculator.h
+++ b/src/libs/installer/installercalculator.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -42,41 +42,50 @@ class Component;
class INSTALLER_EXPORT InstallerCalculator
{
public:
- InstallerCalculator(const QList<Component *> &allComponents);
+ InstallerCalculator(const QList<Component *> &allComponents, const QHash<QString, QStringList> &autoDependencyComponentHash);
enum InstallReasonType
{
+ Selected, // "Selected Component(s) without Dependencies"
Automatic, // "Component(s) added as automatic dependencies"
Dependent, // "Added as dependency for %1."
- Resolved, // "Component(s) that have resolved Dependencies"
- Selected // "Selected Component(s) without Dependencies"
+ Resolved // "Component(s) that have resolved Dependencies"
};
- InstallReasonType installReasonType(Component *component) const;
- QString installReasonReferencedComponent(Component *component) const;
- QString installReason(Component *component) const;
+ InstallReasonType installReasonType(const Component *c) const;
+ QString installReason(const Component *component) const;
QList<Component*> orderedComponentsToInstall() const;
QString componentsToInstallError() const;
- bool appendComponentsToInstall(const QList<Component*> &components);
+ bool appendComponentsToInstall(const QList<Component*> &components, bool modelReset = false, const bool revertFromInstall = false);
+ bool removeComponentsFromInstall(const QList<Component*> &components);
private:
- void insertInstallReason(Component *component,
- InstallReasonType installReasonType,
- const QString &referencedComponentName = QString());
- void realAppendToInstallComponents(Component *component, const QString &version = QString());
- bool appendComponentToInstall(Component *components, const QString &version = QString());
- QString recursionError(Component *component);
+ QString installReasonReferencedComponent(const Component *component) const;
+ void insertInstallReason(const Component *component,
+ const InstallReasonType installReason,
+ const QString &referencedComponentName = QString(),
+ const bool revertFromInstall = false);
+ void realAppendToInstallComponents(Component *component, const QString &version, const bool revertFromInstall);
+ bool appendComponentToInstall(Component *component, const QString &version, const bool revertFromInstall);
+ QString recursionError(const Component *component) const;
+ QSet<Component *> autodependencyComponents(const bool revertFromInstall);
+private:
QList<Component*> m_allComponents;
QHash<Component*, QSet<Component*> > m_visitedComponents;
- QSet<QString> m_toInstallComponentIds; //for faster lookups
+ QList<const Component*> m_componentsForAutodepencencyCheck;
+ //for faster lookups.
+ QSet<QString> m_toInstallComponentIds;
+ QHash<QString, QStringList> m_referenceCount;
QString m_componentsToInstallError;
//calculate installation order variables
QList<Component*> m_orderedComponentsToInstall;
//we can't use this reason hash as component id hash, because some reasons are ready before
//the component is added
QHash<QString, QPair<InstallReasonType, QString> > m_toInstallComponentIdReasonHash;
+ //Helper hash for quicker search for autodependency components
+ QHash<QString, QStringList> m_autoDependencyComponentHash;
};
}
diff --git a/src/libs/installer/packagemanagercore.cpp b/src/libs/installer/packagemanagercore.cpp
index b6f79a92b..041c0fd31 100644
--- a/src/libs/installer/packagemanagercore.cpp
+++ b/src/libs/installer/packagemanagercore.cpp
@@ -583,22 +583,10 @@ void PackageManagerCore::componentsToInstallNeedsRecalculation()
QList<Component*> selectedComponentsToInstall = componentsMarkedForInstallation();
d->m_componentsToInstallCalculated =
- d->installerCalculator()->appendComponentsToInstall(selectedComponentsToInstall);
-
- QList<Component *> componentsToInstall = d->installerCalculator()->orderedComponentsToInstall();
+ d->installerCalculator()->appendComponentsToInstall(selectedComponentsToInstall, true);
d->calculateUninstallComponents();
-
- QSet<Component *> componentsToUninstall = d->uninstallerCalculator()->componentsToUninstall();
-
- foreach (Component *component, components(ComponentType::All))
- component->setInstallAction(component->isInstalled()
- ? ComponentModelHelper::KeepInstalled
- : ComponentModelHelper::KeepUninstalled);
- foreach (Component *component, componentsToUninstall)
- component->setInstallAction(ComponentModelHelper::Uninstall);
- foreach (Component *component, componentsToInstall)
- component->setInstallAction(ComponentModelHelper::Install);
+ d->updateComponentCheckedState();
// update all nodes uncompressed size
foreach (Component *const component, components(ComponentType::Root))
@@ -606,6 +594,59 @@ void PackageManagerCore::componentsToInstallNeedsRecalculation()
}
/*!
+ Calculates components to install based on user selection. \a indexes
+ contains list of model indexes user has selected for install, dependencies
+ and autodependencies are resolved later.
+ */
+void PackageManagerCore::calculateUserSelectedComponentsToInstall(const QList<QModelIndex> &indexes)
+{
+ QList<Component*> componentsToInstall;
+ QList<Component*> componentsToUnInstall;
+ ComponentModel *model = isUpdater() ? updaterComponentModel() : defaultComponentModel();
+ for (QModelIndex index : indexes) {
+ Component *installComponent = model->componentFromIndex(index);
+ // 1. Component is selected for install
+ if (installComponent->isSelected() && !installComponent->isInstalled()) {
+ componentsToInstall.append(installComponent);
+ // Check if component has replacements that needs to be removed
+ const QList<Component*> replacedComponents = d->replacedComponentsByName(installComponent->name());
+ for (Component *replacedComponent : replacedComponents) {
+ componentsToUnInstall.append(replacedComponent);
+ d->uninstallerCalculator()->insertUninstallReason(replacedComponent,
+ UninstallerCalculator::UninstallReasonType::Replaced);
+ }
+ }
+ // 2. Component is reseleted for install (tapping checkbox off/on)
+ else if (installComponent->isSelected() && installComponent->isInstalled()
+ && !d->installerCalculator()->orderedComponentsToInstall().contains(installComponent)) {
+ componentsToInstall.append(installComponent);
+ }
+ // 3. Component is selected for uninstall
+ else if (!isUpdater() && !installComponent->isSelected() && installComponent->isInstalled()) {
+ componentsToUnInstall.append(installComponent);
+ }
+ // 4. Component is reselected for uninstall (tapping checkbox on/off)
+ else if (!installComponent->isSelected()
+ && d->installerCalculator()->orderedComponentsToInstall().contains(installComponent)) {
+ componentsToUnInstall.append(installComponent);
+ // Check if component has replacements that needs to be readded
+ componentsToInstall.append(d->replacedComponentsByName(installComponent->name()));
+ }
+ }
+
+ d->installerCalculator()->removeComponentsFromInstall(componentsToUnInstall);
+ d->m_componentsToInstallCalculated
+ = d->installerCalculator()->appendComponentsToInstall(componentsToInstall, false);
+ if (!isUpdater()) {
+ d->uninstallerCalculator()->appendComponentsToUninstall(componentsToUnInstall, false);
+ }
+ d->uninstallerCalculator()->removeComponentsFromUnInstall(componentsToInstall);
+
+ d->updateComponentCheckedState();
+}
+
+
+/*!
Forces a recalculation of components to install.
\sa {installer::clearComponentsToInstallCalculated}{installer.clearComponentsToInstallCalculated}
*/
@@ -2141,8 +2182,7 @@ QList<Component*> PackageManagerCore::orderedComponentsToInstall() const
bool PackageManagerCore::calculateComponents(QString *displayString)
{
QString htmlOutput;
- if (!calculateComponentsToUninstall() ||
- !calculateComponentsToInstall()) {
+ if (!calculateComponentsToInstall()) {
htmlOutput.append(QString::fromLatin1("<h2><font color=\"red\">%1</font></h2><ul>")
.arg(tr("Cannot resolve all dependencies.")));
//if we have a missing dependency or a recursion we can display it
@@ -3942,8 +3982,12 @@ void PackageManagerCore::storeReplacedComponents(QHash<QString, Component *> &co
components.remove(key);
d->m_deletedReplacedComponents.append(componentToReplace);
}
- d->componentsToReplace().insert(componentName, qMakePair(it.key(), componentToReplace));
d->replacementDependencyComponents().append(componentToReplace);
+
+ //Following hashes are created for quicker search of components
+ d->componentsToReplace().insert(componentName, qMakePair(it.key(), componentToReplace));
+ QStringList oldValue = d->componentReplaces().value(it.key()->name());
+ d->componentReplaces().insert(it.key()->name(), oldValue << componentToReplace->name());
}
}
}
@@ -4205,8 +4249,8 @@ bool PackageManagerCore::fetchUpdaterPackages(const PackagesList &remotes, const
if (!component->isUnstable())
component->setCheckState(Qt::Checked);
}
+ d->createDependencyHashes(component);
}
-
if (foundEssentialUpdate()) {
foreach (QInstaller::Component *component, components) {
if (d->statusCanceledOrFailed())
@@ -4379,9 +4423,10 @@ ComponentModel *PackageManagerCore::componentModel(PackageManagerCore *core, con
ComponentModel::tr("Release Date"));
model->setHeaderData(ComponentModelHelper::UncompressedSizeColumn, Qt::Horizontal,
ComponentModel::tr("Size"));
- connect(model, SIGNAL(checkStateChanged(QInstaller::ComponentModel::ModelState)), this,
- SLOT(componentsToInstallNeedsRecalculation()));
-
+ connect(model, &ComponentModel::modelCheckStateChanged,
+ this, &PackageManagerCore::componentsToInstallNeedsRecalculation);
+ connect(model, &ComponentModel::componentsCheckStateChanged,
+ this, &PackageManagerCore::calculateUserSelectedComponentsToInstall);
return model;
}
diff --git a/src/libs/installer/packagemanagercore.h b/src/libs/installer/packagemanagercore.h
index 7cce3f7be..bebfc191d 100644
--- a/src/libs/installer/packagemanagercore.h
+++ b/src/libs/installer/packagemanagercore.h
@@ -352,6 +352,7 @@ public Q_SLOTS:
void setCompleteUninstallation(bool complete);
void cancelMetaInfoJob();
void componentsToInstallNeedsRecalculation();
+ void calculateUserSelectedComponentsToInstall(const QList<QModelIndex> &indexes);
void clearComponentsToInstallCalculated();
Q_SIGNALS:
@@ -437,6 +438,7 @@ private:
PackageManagerCorePrivate *const d;
friend class PackageManagerCorePrivate;
QHash<QString, QString> m_fileDialogAutomaticAnswers;
+ QHash<QString, QStringList> m_localVirtualWithDependants;
private:
// remove once we deprecate isSelected, setSelected etc...
diff --git a/src/libs/installer/packagemanagercore_p.cpp b/src/libs/installer/packagemanagercore_p.cpp
index 73738d1cb..fc7a8e2c0 100644
--- a/src/libs/installer/packagemanagercore_p.cpp
+++ b/src/libs/installer/packagemanagercore_p.cpp
@@ -430,12 +430,13 @@ bool PackageManagerCorePrivate::buildComponentTree(QHash<QString, Component*> &c
m_core->appendRootComponent(component);
}
- // after everything is set up, load the scripts if needed
- if (loadScript) {
- foreach (QInstaller::Component *component, components)
+ // after everything is set up, load the scripts if needed and create helper hashes
+ // for autodependency and dependency components for quicker search later
+ foreach (QInstaller::Component *component, components) {
+ if (loadScript)
component->loadComponentScript();
+ createDependencyHashes(component);
}
-
// now we can preselect components in the tree
foreach (QInstaller::Component *component, components) {
// set the checked state for all components without child (means without tristate)
@@ -490,6 +491,10 @@ bool PackageManagerCorePrivate::buildComponentTree(QHash<QString, Component*> &c
void PackageManagerCorePrivate::cleanUpComponentEnvironment()
{
+ m_componentReplaces.clear();
+ m_autoDependencyComponentHash.clear();
+ m_dependencyComponentHash.clear();
+ m_localVirtualComponents.clear();
// clean up registered (downloaded) data
if (m_core->isMaintainer())
BinaryFormatEngineHandler::instance()->clear();
@@ -565,6 +570,26 @@ QHash<QString, QPair<Component*, Component*> > &PackageManagerCorePrivate::compo
return (!isUpdater()) ? m_componentsToReplaceAllMode : m_componentsToReplaceUpdaterMode;
}
+QHash<QString, QStringList> &PackageManagerCorePrivate::componentReplaces()
+{
+ return m_componentReplaces;
+}
+
+QList<Component*> PackageManagerCorePrivate::replacedComponentsByName(const QString &name)
+{
+ // Creates a list of components which are replaced by component 'name'
+ QList<Component*> replacedComponents;
+ if (m_componentReplaces.contains(name)) {
+ for (const QString &replacedComponentName : m_componentReplaces.value(name)) {
+ Component *replacedComponent = m_core->componentByName(replacedComponentName,
+ m_core->components(PackageManagerCore::ComponentType::All));
+ if (replacedComponent)
+ replacedComponents.append(replacedComponent);
+ }
+ }
+ return replacedComponents;
+}
+
void PackageManagerCorePrivate::clearInstallerCalculator()
{
delete m_installerCalculator;
@@ -576,7 +601,7 @@ InstallerCalculator *PackageManagerCorePrivate::installerCalculator() const
if (!m_installerCalculator) {
PackageManagerCorePrivate *const pmcp = const_cast<PackageManagerCorePrivate *> (this);
pmcp->m_installerCalculator = new InstallerCalculator(
- m_core->components(PackageManagerCore::ComponentType::AllNoReplacements));
+ m_core->components(PackageManagerCore::ComponentType::AllNoReplacements), pmcp->m_autoDependencyComponentHash);
}
return m_installerCalculator;
}
@@ -600,7 +625,8 @@ UninstallerCalculator *PackageManagerCorePrivate::uninstallerCalculator() const
}
}
- pmcp->m_uninstallerCalculator = new UninstallerCalculator(installedComponents, m_core);
+ pmcp->m_uninstallerCalculator = new UninstallerCalculator(installedComponents, m_core,
+ pmcp->m_autoDependencyComponentHash, pmcp->m_dependencyComponentHash, pmcp->m_localVirtualComponents);
}
return m_uninstallerCalculator;
}
@@ -2685,8 +2711,11 @@ LocalPackagesHash PackageManagerCorePrivate::localInstalledPackages()
if (statusCanceledOrFailed())
break;
installedPackages.insert(package.name, package);
+ if (package.virtualComp && package.autoDependencies.isEmpty()) {
+ if (!m_localVirtualComponents.contains(package.name))
+ m_localVirtualComponents.append(package.name);
+ }
}
-
return installedPackages;
}
@@ -2823,6 +2852,19 @@ void PackageManagerCorePrivate::storeCheckState()
m_coreCheckedHash.insert(component, component->checkState());
}
+void PackageManagerCorePrivate::updateComponentCheckedState()
+{
+ for (Component *component : m_core->components(PackageManagerCore::ComponentType::All)) {
+ component->setInstallAction(component->isInstalled()
+ ? ComponentModelHelper::KeepInstalled
+ : ComponentModelHelper::KeepUninstalled);
+ }
+ for (Component *component : uninstallerCalculator()->componentsToUninstall())
+ component->setInstallAction(ComponentModelHelper::Uninstall);
+ for (Component *component : installerCalculator()->orderedComponentsToInstall())
+ component->setInstallAction(ComponentModelHelper::Install);
+}
+
void PackageManagerCorePrivate::connectOperationCallMethodRequest(Operation *const operation)
{
QObject *const operationObject = dynamic_cast<QObject *> (operation);
@@ -3093,4 +3135,21 @@ void PackageManagerCorePrivate::commitPendingUnstableComponents()
m_pendingUnstableComponents.clear();
}
+void PackageManagerCorePrivate::createDependencyHashes(const Component* component)
+{
+ for (const QString &autodepend : component->autoDependencies()) {
+ QStringList value = m_autoDependencyComponentHash.value(autodepend);
+ if (!value.contains(component->name()))
+ value.append(component->name());
+ m_autoDependencyComponentHash.insert(autodepend, value);
+ }
+
+ for (const QString &depend : component->dependencies()) {
+ QStringList value = m_dependencyComponentHash.value(depend);
+ if (!value.contains(component->name()))
+ value.append(component->name());
+ m_dependencyComponentHash.insert(depend, value);
+ }
+}
+
} // namespace QInstaller
diff --git a/src/libs/installer/packagemanagercore_p.h b/src/libs/installer/packagemanagercore_p.h
index 8c5191dea..8e1184c58 100644
--- a/src/libs/installer/packagemanagercore_p.h
+++ b/src/libs/installer/packagemanagercore_p.h
@@ -111,6 +111,8 @@ public:
void clearUpdaterComponentLists();
QList<Component*> &replacementDependencyComponents();
QHash<QString, QPair<Component*, Component*> > &componentsToReplace();
+ QHash<QString, QStringList > &componentReplaces();
+ QList<Component*> replacedComponentsByName(const QString &name);
void clearInstallerCalculator();
InstallerCalculator *installerCalculator() const;
@@ -262,6 +264,12 @@ private:
bool askUserConfirmCommand() const;
bool packageNeedsUpdate(const LocalPackage &localPackage, const Package *update) const;
void commitPendingUnstableComponents();
+ void createDependencyHashes(const Component* component);
+ void updateComponentCheckedState();
+
+ // remove once we deprecate isSelected, setSelected etc...
+ void restoreCheckState();
+ void storeCheckState();
private:
PackageManagerCore *m_core;
@@ -296,12 +304,15 @@ private:
QScopedPointer<RemoteFileEngineHandler> m_remoteFileEngineHandler;
QHash<QString, QVariantMap> m_licenseItems;
-private:
- // remove once we deprecate isSelected, setSelected etc...
- void restoreCheckState();
- void storeCheckState();
QHash<Component*, Qt::CheckState> m_coreCheckedHash;
QList<Component*> m_deletedReplacedComponents;
+ AutoDependencyHash m_autoDependencyComponentHash;
+ DependencyHash m_dependencyComponentHash;
+
+ QStringList m_localVirtualComponents;
+
+ // < name (component replacing others), components to replace>
+ QHash<QString, QStringList > m_componentReplaces;
};
} // namespace QInstaller
diff --git a/src/libs/installer/qinstallerglobal.cpp b/src/libs/installer/qinstallerglobal.cpp
index 177a2595d..6a67e9e10 100644
--- a/src/libs/installer/qinstallerglobal.cpp
+++ b/src/libs/installer/qinstallerglobal.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -70,3 +70,15 @@
Synonym for QHash<QString, KDUpdater::LocalPackage>.
*/
+
+/*!
+ \typedef QInstaller::AutoDependencyHash
+
+ Synonym for QHash<QString, QStringList>.
+*/
+
+/*!
+ \typedef QInstaller::DependencyHash
+
+ Synonym for QHash<QString, QStringList>.
+*/
diff --git a/src/libs/installer/qinstallerglobal.h b/src/libs/installer/qinstallerglobal.h
index 884044db9..98a81f092 100644
--- a/src/libs/installer/qinstallerglobal.h
+++ b/src/libs/installer/qinstallerglobal.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -57,6 +57,9 @@ typedef QList<QInstaller::Package*> PackagesList;
typedef QHash<QString, KDUpdater::LocalPackage> LocalPackagesHash;
+typedef QHash<QString, QStringList> AutoDependencyHash;
+typedef QHash<QString, QStringList> DependencyHash;
+
} // namespace QInstaller
#endif // QINSTALLER_GLOBAL_H
diff --git a/src/libs/installer/uninstallercalculator.cpp b/src/libs/installer/uninstallercalculator.cpp
index a8c47e523..9bcacb246 100644
--- a/src/libs/installer/uninstallercalculator.cpp
+++ b/src/libs/installer/uninstallercalculator.cpp
@@ -42,9 +42,16 @@ namespace QInstaller {
\internal
*/
-UninstallerCalculator::UninstallerCalculator(const QList<Component *> &installedComponents, PackageManagerCore *core)
+UninstallerCalculator::UninstallerCalculator(const QList<Component *> &installedComponents
+ , PackageManagerCore *core
+ , const QHash<QString, QStringList> &autoDependencyComponentHash
+ , const QHash<QString, QStringList> &dependencyComponentHash
+ , const QStringList &localVirtualComponents)
: m_installedComponents(installedComponents)
, m_core(core)
+ , m_autoDependencyComponentHash(autoDependencyComponentHash)
+ , m_dependencyComponentHash(dependencyComponentHash)
+ , m_localVirtualComponents(localVirtualComponents)
{
}
@@ -53,7 +60,7 @@ QSet<Component *> UninstallerCalculator::componentsToUninstall() const
return m_componentsToUninstall;
}
-void UninstallerCalculator::appendComponentToUninstall(Component *component)
+void UninstallerCalculator::appendComponentToUninstall(Component *component, const bool reverse)
{
if (!component)
return;
@@ -61,79 +68,64 @@ void UninstallerCalculator::appendComponentToUninstall(Component *component)
if (!component->isInstalled())
return;
- // remove all already resolved dependees
- const QList<Component *> dependeesList = m_core->dependees(component);
- QSet<Component *> dependees = QSet<Component *>(dependeesList.begin(),
- dependeesList.end()).subtract(m_componentsToUninstall);
-
- foreach (Component *dependee, dependees) {
- appendComponentToUninstall(dependee);
- insertUninstallReason(dependee, UninstallerCalculator::Dependent, component->name());
+ if (m_dependencyComponentHash.contains(component->name())) {
+ const QStringList &dependencies = PackageManagerCore::parseNames(m_dependencyComponentHash.value(component->name()));
+ for (const QString &dependencyComponent : dependencies) {
+ Component *depComponent = m_core->componentByName(dependencyComponent);
+ if (depComponent && depComponent->isInstalled() && !m_componentsToUninstall.contains(depComponent)) {
+ appendComponentToUninstall(depComponent, reverse);
+ insertUninstallReason(depComponent, UninstallerCalculator::Dependent, component->name());
+ } else if (reverse) {
+ appendComponentToUninstall(depComponent, true);
+ }
+ }
+ }
+ if (reverse) {
+ m_componentsToUninstall.remove(component);
+ } else {
+ m_componentsToUninstall.insert(component);
}
-
- m_componentsToUninstall.insert(component);
}
-void UninstallerCalculator::appendComponentsToUninstall(const QList<Component*> &components)
+void UninstallerCalculator::appendComponentsToUninstall(const QList<Component*> &components, const bool reverse)
{
+ if (components.isEmpty())
+ return;
foreach (Component *component, components)
- appendComponentToUninstall(component);
-
+ appendComponentToUninstall(component, reverse);
QList<Component*> autoDependOnList;
// All regular dependees are resolved. Now we are looking for auto depend on components.
- foreach (Component *component, m_installedComponents) {
+ for (Component *component : components) {
// If a components is installed and not yet scheduled for un-installation, check for auto depend.
- if (component->isInstalled() && !m_componentsToUninstall.contains(component)) {
- QStringList autoDependencies = PackageManagerCore::parseNames(component->autoDependencies());
- if (autoDependencies.isEmpty())
- continue;
-
- // This code needs to be enabled once the scripts use isInstalled, installationRequested and
- // uninstallationRequested...
- if (autoDependencies.first().compare(scScript, Qt::CaseInsensitive) == 0) {
- //QScriptValue valueFromScript;
- //try {
- // valueFromScript = callScriptMethod(QLatin1String("isAutoDependOn"));
- //} catch (const Error &error) {
- // // keep the component, should do no harm
- // continue;
- //}
-
- //if (valueFromScript.isValid() && !valueFromScript.toBool())
- // autoDependOnList.append(component);
- continue;
- }
-
- foreach (Component *c, m_installedComponents) {
- const QString replaces = c->value(scReplaces);
- const QStringList possibleNames = replaces.split(QInstaller::commaRegExp(),
- Qt::SkipEmptyParts) << c->name();
- foreach (const QString &possibleName, possibleNames) {
-
- Component *cc = PackageManagerCore::componentByName(possibleName, m_installedComponents);
- if (cc && (cc->installAction() != ComponentModelHelper::AutodependUninstallation)) {
- autoDependencies.removeAll(possibleName);
-
- }
- }
- }
-
- // A component requested auto uninstallation, keep it to resolve their dependencies as well.
- if (!autoDependencies.isEmpty()) {
- autoDependOnList.append(component);
- insertUninstallReason(component, UninstallerCalculator::AutoDependent, autoDependencies.join(QLatin1String(", ")));
- component->setInstallAction(ComponentModelHelper::AutodependUninstallation);
+ if (!m_autoDependencyComponentHash.contains(component->name()))
+ continue;
+ const QStringList autoDependencies = PackageManagerCore::parseNames(m_autoDependencyComponentHash.value(component->name()));
+ for (const QString &autoDependencyComponent : autoDependencies) {
+ Component *autoDepComponent = m_core->componentByName(autoDependencyComponent);
+ if (autoDepComponent && autoDepComponent->isInstalled()) {
+ // A component requested auto uninstallation, keep it to resolve their dependencies as well.
+ if (reverse) {
+ autoDependOnList.append(autoDepComponent);
+ } else if (!m_componentsToUninstall.contains(autoDepComponent)) {
+ insertUninstallReason(autoDepComponent, UninstallerCalculator::AutoDependent, component->name());
+ autoDepComponent->setInstallAction(ComponentModelHelper::AutodependUninstallation);
+ autoDependOnList.append(autoDepComponent);
+ }
}
}
}
-
if (!autoDependOnList.isEmpty())
- appendComponentsToUninstall(autoDependOnList);
+ appendComponentsToUninstall(autoDependOnList, reverse);
else
- appendVirtualComponentsToUninstall();
+ appendVirtualComponentsToUninstall(reverse);
+}
+
+void UninstallerCalculator::removeComponentsFromUnInstall(const QList<Component*> &components)
+{
+ appendComponentsToUninstall(components, true);
}
-void UninstallerCalculator::insertUninstallReason(Component *component, UninstallReasonType uninstallReason,
+void UninstallerCalculator::insertUninstallReason(Component *component, const UninstallReasonType uninstallReason,
const QString &referencedComponentName)
{
// keep the first reason
@@ -176,34 +168,56 @@ QString UninstallerCalculator::uninstallReasonReferencedComponent(Component *com
return m_toUninstallComponentIdReasonHash.value(component->name()).second;
}
-
-void UninstallerCalculator::appendVirtualComponentsToUninstall()
+void UninstallerCalculator::appendVirtualComponentsToUninstall(const bool reverse)
{
QList<Component*> unneededVirtualList;
+
// Check for virtual components without dependees
- for (Component *component : qAsConst(m_installedComponents)) {
- if (component->isInstalled() && component->isVirtual() && !m_componentsToUninstall.contains(component)) {
- // Components with auto dependencies were handled in the previous step
- if (!component->autoDependencies().isEmpty() || component->forcedInstallation())
- continue;
-
- bool required = false;
- // Check if installed or about to be updated -packages are dependant on the package
- for (Component *dependant : m_core->installDependants(component)) {
- if (dependant->isInstalled() && !m_componentsToUninstall.contains(dependant)) {
- required = true;
- break;
+ if (reverse) {
+ for (Component *reverseFromUninstall : qAsConst(m_virtualComponentsForReverse)) {
+ if (m_componentsToUninstall.contains(reverseFromUninstall)) {
+ bool required = false;
+ // Check if installed or about to be updated -packages are dependant on the package
+ const QList<Component*> installDependants = m_core->installDependants(reverseFromUninstall);
+ for (Component *dependant : installDependants) {
+ if (dependant->isInstalled() && !m_componentsToUninstall.contains(dependant)) {
+ required = true;
+ break;
+ }
+ }
+ if (required) {
+ unneededVirtualList.append(reverseFromUninstall);
}
}
- if (!required) {
- unneededVirtualList.append(component);
- insertUninstallReason(component, UninstallerCalculator::VirtualDependent);
+ }
+ } else {
+ for (const QString &componentName : qAsConst(m_localVirtualComponents)) {
+ Component *virtualComponent = m_core->componentByName(componentName);
+ if (virtualComponent->isInstalled() && !m_componentsToUninstall.contains(virtualComponent)) {
+ // Components with auto dependencies were handled in the previous step
+ if (!virtualComponent->autoDependencies().isEmpty() || virtualComponent->forcedInstallation())
+ continue;
+
+ bool required = false;
+ // Check if installed or about to be updated -packages are dependant on the package
+ const QList<Component*> installDependants = m_core->installDependants(virtualComponent);
+ for (Component *dependant : installDependants) {
+ if (dependant->isInstalled() && !m_componentsToUninstall.contains(dependant)) {
+ required = true;
+ break;
+ }
+ }
+ if (!required) {
+ unneededVirtualList.append(virtualComponent);
+ m_virtualComponentsForReverse.append(virtualComponent);
+ insertUninstallReason(virtualComponent, UninstallerCalculator::VirtualDependent);
+ }
}
}
}
if (!unneededVirtualList.isEmpty())
- appendComponentsToUninstall(unneededVirtualList);
+ appendComponentsToUninstall(unneededVirtualList, reverse);
}
} // namespace QInstaller
diff --git a/src/libs/installer/uninstallercalculator.h b/src/libs/installer/uninstallercalculator.h
index c20616e93..8bb9eb814 100644
--- a/src/libs/installer/uninstallercalculator.h
+++ b/src/libs/installer/uninstallercalculator.h
@@ -52,27 +52,34 @@ public:
AutoDependent // "Removed as autodependency component is removed"
};
- UninstallerCalculator(const QList<Component *> &installedComponents, PackageManagerCore *core);
+ UninstallerCalculator(const QList<Component *> &installedComponents, PackageManagerCore *core,
+ const QHash<QString, QStringList> &autoDependencyComponentHash,
+ const QHash<QString, QStringList> &dependencyComponentHash,
+ const QStringList &localVirtualComponents);
QSet<Component*> componentsToUninstall() const;
- void appendComponentsToUninstall(const QList<Component*> &components);
+ void appendComponentsToUninstall(const QList<Component*> &components, const bool reverse = false);
+ void removeComponentsFromUnInstall(const QList<Component*> &components);
void insertUninstallReason(Component *component,
- UninstallReasonType installReasonType,
+ const UninstallReasonType uninstallReason,
const QString &referencedComponentName = QString());
QString uninstallReason(Component *component) const;
UninstallerCalculator::UninstallReasonType uninstallReasonType(Component *c) const;
QString uninstallReasonReferencedComponent(Component *component) const;
private:
-
- void appendComponentToUninstall(Component *component);
- void appendVirtualComponentsToUninstall();
+ void appendComponentToUninstall(Component *component, const bool reverse);
+ void appendVirtualComponentsToUninstall(const bool reverse);
QList<Component *> m_installedComponents;
QSet<Component *> m_componentsToUninstall;
PackageManagerCore *m_core;
QHash<QString, QPair<UninstallReasonType, QString> > m_toUninstallComponentIdReasonHash;
+ QHash<QString, QStringList> m_autoDependencyComponentHash;
+ QHash<QString, QStringList> m_dependencyComponentHash;
+ QStringList m_localVirtualComponents;
+ QList<Component *> m_virtualComponentsForReverse;
};
}
diff --git a/tests/auto/installer/solver/tst_solver.cpp b/tests/auto/installer/solver/tst_solver.cpp
index 5e499da2d..f4e10f5e7 100644
--- a/tests/auto/installer/solver/tst_solver.cpp
+++ b/tests/auto/installer/solver/tst_solver.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -150,6 +150,7 @@ private slots:
QTest::addColumn<QList<Component *> >("selectedComponents");
QTest::addColumn<QList<Component *> >("expectedResult");
QTest::addColumn<QList<int> >("installReason");
+ QTest::addColumn<AutoDependencyHash >("autodependencyHash");
PackageManagerCore *core = new PackageManagerCore();
core->setPackageManager();
@@ -169,6 +170,9 @@ private slots:
core->appendRootComponent(componentB_NewVersion);
core->appendRootComponent(componentB_Auto);
+ QHash<QString, QStringList> autodependencyHash;
+ autodependencyHash.insert(QLatin1String("B_version"), QStringList() << QLatin1String("B_auto"));
+
QTest::newRow("Installer resolved") << core
<< (QList<Component *>() << componentB)
<< (QList<Component *>() << componentB_NewVersion << componentAB << componentB << componentB_Auto)
@@ -176,7 +180,8 @@ private slots:
<< InstallerCalculator::Dependent
<< InstallerCalculator::Dependent
<< InstallerCalculator::Resolved
- << InstallerCalculator::Automatic);
+ << InstallerCalculator::Automatic)
+ << autodependencyHash;
}
void resolveInstaller()
@@ -185,8 +190,9 @@ private slots:
QFETCH(QList<Component *> , selectedComponents);
QFETCH(QList<Component *> , expectedResult);
QFETCH(QList<int>, installReason);
+ QFETCH(AutoDependencyHash, autodependencyHash);
- InstallerCalculator calc(core->components(PackageManagerCore::ComponentType::AllNoReplacements));
+ InstallerCalculator calc(core->components(PackageManagerCore::ComponentType::AllNoReplacements), autodependencyHash);
calc.appendComponentsToInstall(selectedComponents);
QList<Component *> result = calc.orderedComponentsToInstall();
@@ -229,7 +235,7 @@ private slots:
QFETCH(QList<Component *> , selectedComponents);
QFETCH(QList<Component *> , expectedResult);
- InstallerCalculator calc(core->components(PackageManagerCore::ComponentType::AllNoReplacements));
+ InstallerCalculator calc(core->components(PackageManagerCore::ComponentType::AllNoReplacements), QHash<QString, QStringList>());
QTest::ignoreMessage(QtWarningMsg, "Cannot find missing dependency \"B->=2.0.0\" for \"A\".");
calc.appendComponentsToInstall(selectedComponents);
@@ -245,6 +251,7 @@ private slots:
QTest::addColumn<QList<Component *> >("installedComponents");
QTest::addColumn<QSet<Component *> >("expectedResult");
QTest::addColumn<UninstallReasonList >("uninstallReasons");
+ QTest::addColumn<DependencyHash >("dependencyHash");
UninstallReasonList uninstallReasonList;
PackageManagerCore *core = new PackageManagerCore();
@@ -263,14 +270,19 @@ private slots:
componentB->setInstalled();
componentAB->setInstalled();
+ QHash<QString, QStringList> dependencyComponentHash;
+ dependencyComponentHash.insert(QLatin1String("A.B"), QStringList() << QLatin1String("B"));
+
uninstallReasonList.append(qMakePair(componentAB, UninstallerCalculator::Selected));
uninstallReasonList.append(qMakePair(componentB, UninstallerCalculator::Dependent));
QTest::newRow("Uninstaller resolved") << core
<< (QList<Component *>() << componentAB)
<< (QList<Component *>() << componentA << componentB)
<< (QSet<Component *>() << componentAB << componentB)
- << (uninstallReasonList);
+ << uninstallReasonList
+ << dependencyComponentHash;
+ dependencyComponentHash.clear();
uninstallReasonList.clear();
core = new PackageManagerCore();
core->setPackageManager();
@@ -285,13 +297,16 @@ private slots:
compA->setInstalled();
compB->setInstalled();
+ dependencyComponentHash.insert(QLatin1String("A"), QStringList() << QLatin1String("B"));
+
uninstallReasonList.append(qMakePair(compA, UninstallerCalculator::Selected));
uninstallReasonList.append(qMakePair(compB, UninstallerCalculator::Dependent));
QTest::newRow("Cascade dependencies") << core
<< (QList<Component *>() << compA)
<< (QList<Component *>() << compB)
<< (QSet<Component *>() << compA << compB)
- << (uninstallReasonList);
+ << (uninstallReasonList)
+ << dependencyComponentHash;
}
void resolveUninstaller()
@@ -301,7 +316,9 @@ private slots:
QFETCH(QList<Component *> , installedComponents);
QFETCH(QSet<Component *> , expectedResult);
QFETCH(UninstallReasonList, uninstallReasons);
- UninstallerCalculator calc(installedComponents, core);
+ QFETCH(DependencyHash, dependencyHash);
+
+ UninstallerCalculator calc(installedComponents, core, QHash<QString, QStringList>(), dependencyHash, QStringList());
calc.appendComponentsToUninstall(selectedToUninstall);
QSet<Component *> result = calc.componentsToUninstall();
for (auto pair : uninstallReasons) {