diff options
author | Arttu Tarkiainen <arttu.tarkiainen@qt.io> | 2023-01-27 09:59:10 +0200 |
---|---|---|
committer | Arttu Tarkiainen <arttu.tarkiainen@qt.io> | 2023-01-27 10:38:55 +0000 |
commit | f5c48c86d3670240fc5c36c2f406196fc23527d5 (patch) | |
tree | 1823d302c330bf513908fdf68005d03ce5848f82 | |
parent | 476b6d6fffaf6841adaf68889e8b8ddae17dd382 (diff) | |
parent | 50575212455fbd3109adbe92d8509fca3c51850a (diff) |
Merge remote-tracking branch 'origin/4.5'
Change-Id: I07baba6a0e64b6022e6a933708e199551be7dd2f
-rw-r--r-- | Changelog | 3 | ||||
-rw-r--r-- | doc/installerfw.qdoc | 8 | ||||
-rw-r--r-- | doc/scripting-api/packagemanagercore.qdoc | 14 | ||||
-rw-r--r-- | src/libs/installer/component.cpp | 11 | ||||
-rw-r--r-- | src/libs/installer/component.h | 3 | ||||
-rw-r--r-- | src/libs/installer/componentmodel.cpp | 10 | ||||
-rw-r--r-- | src/libs/installer/componentselectionpage_p.cpp | 21 | ||||
-rw-r--r-- | src/libs/installer/componentselectionpage_p.h | 4 | ||||
-rw-r--r-- | src/libs/installer/packagemanagercore.cpp | 144 | ||||
-rw-r--r-- | src/libs/installer/packagemanagercore.h | 11 | ||||
-rw-r--r-- | src/libs/installer/packagemanagercore_p.cpp | 49 | ||||
-rw-r--r-- | src/libs/installer/packagemanagercore_p.h | 4 | ||||
-rw-r--r-- | src/libs/installer/packagemanagergui.cpp | 20 | ||||
-rw-r--r-- | src/libs/installer/uninstallercalculator.cpp | 52 | ||||
-rw-r--r-- | src/libs/installer/uninstallercalculator.h | 8 | ||||
-rw-r--r-- | src/libs/kdtools/updateoperation.cpp | 2 | ||||
-rw-r--r-- | tests/auto/installer/brokeninstaller/tst_brokeninstaller.cpp | 4 | ||||
-rw-r--r-- | tests/auto/installer/packagemanagercore/tst_packagemanagercore.cpp | 3 |
18 files changed, 233 insertions, 138 deletions
@@ -1,3 +1,6 @@ +4.5.2 +- Fix freezing UI while searching components (QTIFW-2886) + 4.5.1 - Make Settings operation to support _OLD and placeholders (QTIFW-2882) - ExtractOp: fix leftover empty directories when 'targetDir' arg is used (QTIFW-2764) diff --git a/doc/installerfw.qdoc b/doc/installerfw.qdoc index 476234f80..a0fb9d848 100644 --- a/doc/installerfw.qdoc +++ b/doc/installerfw.qdoc @@ -907,8 +907,12 @@ The component is installed if and only if all of the specified dependencies are fulfilled. If a component has an automatic dependency on other components, - the check box will not be visible next to the component in the component tree. - The selection will be performed automatically. + the check box will not be visible next to the component in the component tree, + but this does not change the visibility of the check box in the updater view + where the end user may still manually select the component for update. + + When running an installer or a maintenance tool in package manager + mode, the selection will be performed automatically. If the component was not installed before, it will be selected for installation only when all components from this list are also selected for installation. diff --git a/doc/scripting-api/packagemanagercore.qdoc b/doc/scripting-api/packagemanagercore.qdoc index b14fd957f..ac6830d33 100644 --- a/doc/scripting-api/packagemanagercore.qdoc +++ b/doc/scripting-api/packagemanagercore.qdoc @@ -325,15 +325,25 @@ */ /*! + \qmlmethod boolean installer::recalculateAllComponents() + + Recalculates all components to install and uninstall. Returns \c true + on success, \c false otherwise. +*/ + +/*! \qmlmethod void installer::componentsToInstallNeedsRecalculation() + \deprecated [4.5] Use recalculateAllComponents() instead. Ensures that component dependencies are re-calculated. */ /*! \qmlmethod void installer::clearComponentsToInstallCalculated() - - Forces a recalculation of components to install. + \deprecated [4.5] Installer framework recalculates components each time the calculation + of components to install is requested, so there is no need to call this anymore, and the + method does nothing. On previous versions calling this forced a recalculation of + components to install. */ /*! diff --git a/src/libs/installer/component.cpp b/src/libs/installer/component.cpp index ccf9dc8bb..32f3fefc6 100644 --- a/src/libs/installer/component.cpp +++ b/src/libs/installer/component.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. @@ -1367,6 +1367,15 @@ bool Component::forcedInstallation() const } /*! + Returns whether this component is essential. Essential components + are always installed, and updated before other components. +*/ +bool Component::isEssential() const +{ + return d->m_vars.value(scEssential, scFalse).toLower() == scTrue; +} + +/*! Sets the validator callback name to \a name. */ void Component::setValidatorCallbackName(const QString &name) diff --git a/src/libs/installer/component.h b/src/libs/installer/component.h index 0d8bf85ab..8f17b2d98 100644 --- a/src/libs/installer/component.h +++ b/src/libs/installer/component.h @@ -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. @@ -209,6 +209,7 @@ public: bool isVirtual() const; bool isSelected() const; bool forcedInstallation() const; + bool isEssential() const; void setValidatorCallbackName(const QString &name); diff --git a/src/libs/installer/componentmodel.cpp b/src/libs/installer/componentmodel.cpp index 873175937..dbb80f2f6 100644 --- a/src/libs/installer/componentmodel.cpp +++ b/src/libs/installer/componentmodel.cpp @@ -216,7 +216,10 @@ QVariant ComponentModel::data(const QModelIndex &index, int role) const return component->data(Qt::UserRole + index.column()); } if (role == Qt::CheckStateRole) { - if (!component->isCheckable() || !component->autoDependencies().isEmpty() || component->isUnstable()) + if (!component->isCheckable() || component->isUnstable()) + return QVariant(); + + if (!m_core->isUpdater() && !component->autoDependencies().isEmpty()) return QVariant(); } if (role == ComponentModelHelper::ExpandedByDefault) { @@ -564,9 +567,12 @@ QSet<QModelIndex> ComponentModel::updateCheckedState(const ComponentSet &compone checkable = false; } - if ((!node->isCheckable() && checkable) || !node->isEnabled() || !node->autoDependencies().isEmpty() || node->isUnstable()) + if ((!node->isCheckable() && checkable) || !node->isEnabled() || node->isUnstable()) continue; + if (!m_core->isUpdater() && !node->autoDependencies().isEmpty()) + continue; + Qt::CheckState newState = state; const Qt::CheckState recentState = node->checkState(); if (node->isTristate()) diff --git a/src/libs/installer/componentselectionpage_p.cpp b/src/libs/installer/componentselectionpage_p.cpp index 83bcaae14..84a8915a0 100644 --- a/src/libs/installer/componentselectionpage_p.cpp +++ b/src/libs/installer/componentselectionpage_p.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. @@ -72,6 +72,7 @@ ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionP , m_categoryWidget(Q_NULLPTR) , m_categoryLayoutVisible(false) , m_proxyModel(new ComponentSortFilterProxyModel(q)) + , m_componentsResolved(false) , m_headerStretchLastSection(false) { m_treeView->setObjectName(QLatin1String("ComponentsTreeView")); @@ -390,6 +391,15 @@ void ComponentSelectionPagePrivate::expandSearchResults() restoreHeaderResizeModes(); } +/*! + Returns \c true if the components to install and uninstall are calculated + successfully, \c false otherwise. +*/ +bool ComponentSelectionPagePrivate::componentsResolved() const +{ + return m_componentsResolved; +} + void ComponentSelectionPagePrivate::currentSelectedChanged(const QModelIndex ¤t) { if (!current.isValid()) @@ -547,6 +557,15 @@ void ComponentSelectionPagePrivate::onModelStateChanged(QInstaller::ComponentMod m_checkDefault->setEnabled(false); return; } + + m_componentsResolved = m_core->recalculateAllComponents(); + if (!m_componentsResolved) { + const QString error = !m_core->componentsToInstallError().isEmpty() + ? m_core->componentsToInstallError() : m_core->componentsToUninstallError(); + MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), + QLatin1String("CalculateComponentsError"), tr("Error"), error); + } + q->setModified(state.testFlag(ComponentModel::DefaultChecked) == false); // If all components in the checked list are only checkable when run without forced // installation, set ComponentModel::AllUnchecked as well, as we cannot uncheck anything. diff --git a/src/libs/installer/componentselectionpage_p.h b/src/libs/installer/componentselectionpage_p.h index 7c2f6a38e..f06d376f4 100644 --- a/src/libs/installer/componentselectionpage_p.h +++ b/src/libs/installer/componentselectionpage_p.h @@ -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. @@ -74,6 +74,7 @@ public: void updateTreeView(); void expandDefault(); void expandSearchResults(); + bool componentsResolved() const; public slots: void currentSelectedChanged(const QModelIndex ¤t); @@ -118,6 +119,7 @@ private: QStackedLayout *m_stackedLayout; ComponentSortFilterProxyModel *m_proxyModel; QLineEdit *m_searchLineEdit; + bool m_componentsResolved; bool m_headerStretchLastSection; QHash<int, QHeaderView::ResizeMode> m_headerResizeModes; diff --git a/src/libs/installer/packagemanagercore.cpp b/src/libs/installer/packagemanagercore.cpp index 8c70bdae6..2f69c20c8 100644 --- a/src/libs/installer/packagemanagercore.cpp +++ b/src/libs/installer/packagemanagercore.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. @@ -531,7 +531,6 @@ void PackageManagerCore::reset() d->m_status = PackageManagerCore::Unfinished; d->m_installerBaseBinaryUnreplaced.clear(); d->m_coreCheckedHash.clear(); - d->m_componentsToInstallCalculated = false; } /*! @@ -613,32 +612,45 @@ template bool PackageManagerCore::loadComponentScripts<QList<Component *>>(const template bool PackageManagerCore::loadComponentScripts<QHash<QString, Component *>>(const QHash<QString, Component *> &, const bool); /*! + \deprecated [4.5] Use recalculateAllComponents() instead. + \sa {installer::componentsToInstallNeedsRecalculation}{installer.componentsToInstallNeedsRecalculation} */ void PackageManagerCore::componentsToInstallNeedsRecalculation() { - d->clearInstallerCalculator(); + recalculateAllComponents(); +} - QList<Component*> selectedComponentsToInstall = componentsMarkedForInstallation(); +/*! + \fn QInstaller::PackageManagerCore::clearComponentsToInstallCalculated() - d->m_componentsToInstallCalculated = - d->installerCalculator()->appendComponentsToInstall(selectedComponentsToInstall); + \deprecated [4.5] Installer framework recalculates components each time the calculation + of components to install is requested, so there is no need to call this anymore, and the + method does nothing. On previous versions calling this forced a recalculation of + components to install. - d->calculateUninstallComponents(); - d->updateComponentCheckedState(); + \sa {installer::clearComponentsToInstallCalculated}{installer.clearComponentsToInstallCalculated} + */ +/*! + Recalculates all components to install and uninstall. Returns \c true + on success, \c false otherwise. Detailed error messages can be retrieved + with {installer::componentsToInstallError} and {installer::componentsToUninstallError}. + */ +bool PackageManagerCore::recalculateAllComponents() +{ + if (!calculateComponentsToInstall()) + return false; + if (!isInstaller() && !calculateComponentsToUninstall()) + return false; + + // update install actions + d->updateComponentCheckedState(); // update all nodes uncompressed size foreach (Component *const component, components(ComponentType::Root)) component->updateUncompressedSize(); // this is a recursive call -} -/*! - Forces a recalculation of components to install. - \sa {installer::clearComponentsToInstallCalculated}{installer.clearComponentsToInstallCalculated} - */ -void PackageManagerCore::clearComponentsToInstallCalculated() -{ - d->m_componentsToInstallCalculated = false; + return true; } /*! @@ -2169,8 +2181,9 @@ QList<Component *> PackageManagerCore::componentsMarkedForInstallation() const } /*! - Determines which components to install based on the current run mode and returns an - ordered list of components to install. Also auto installed dependencies are resolved. + Determines which components to install based on the current run mode, including dependencies + and automatic dependencies. Returns \c true on success, \c false otherwise. + The aboutCalculateComponentsToInstall() signal is emitted before the calculation starts, the finishedCalculateComponentsToInstall() signal once all calculations are done. @@ -2181,16 +2194,15 @@ QList<Component *> PackageManagerCore::componentsMarkedForInstallation() const bool PackageManagerCore::calculateComponentsToInstall() const { emit aboutCalculateComponentsToInstall(); - if (!d->m_componentsToInstallCalculated) { - d->clearInstallerCalculator(); - QList<Component*> selectedComponentsToInstall = componentsMarkedForInstallation(); - d->storeCheckState(); - d->m_componentsToInstallCalculated = - d->installerCalculator()->appendComponentsToInstall(selectedComponentsToInstall); - } + d->clearInstallerCalculator(); + const QList<Component*> selectedComponentsToInstall = componentsMarkedForInstallation(); + + const bool componentsToInstallCalculated = + d->installerCalculator()->appendComponentsToInstall(selectedComponentsToInstall); + emit finishedCalculateComponentsToInstall(); - return d->m_componentsToInstallCalculated; + return componentsToInstallCalculated; } /*! @@ -2202,25 +2214,30 @@ QList<Component*> PackageManagerCore::orderedComponentsToInstall() const } /*! - Calculates components to install and uninstall. In case of an error, returns \c false - and and sets the \a displayString for error detail. + Returns a HTML-formatted description of the reasons each component is about + to be installed or uninstalled, or a description of the error occurred while + calculating components to install and uninstall. */ - -bool PackageManagerCore::calculateComponents(QString *displayString) +QString PackageManagerCore::componentResolveReasons() const { QString htmlOutput; - if (!calculateComponentsToInstall()) { + if (!componentsToInstallError().isEmpty()) { htmlOutput.append(QString::fromLatin1("<h2><font color=\"red\">%1</font></h2><ul>") - .arg(tr("Cannot resolve all dependencies."))); + .arg(tr("Cannot resolve all dependencies."))); //if we have a missing dependency or a recursion we can display it - if (!componentsToInstallError().isEmpty()) { - htmlOutput.append(QString::fromLatin1("<li> %1 </li>").arg( - componentsToInstallError())); - } + htmlOutput.append(QString::fromLatin1("<li> %1 </li>").arg( + componentsToInstallError())); htmlOutput.append(QLatin1String("</ul>")); - if (displayString) - *displayString = htmlOutput; - return false; + return htmlOutput; + } + + if (!componentsToUninstallError().isEmpty()) { + htmlOutput.append(QString::fromLatin1("<h2><font color=\"red\">%1</font></h2><ul>") + .arg(tr("Cannot resolve components to uninstall."))); + htmlOutput.append(QString::fromLatin1("<li> %1 </li>").arg( + componentsToUninstallError())); + htmlOutput.append(QLatin1String("</ul>")); + return htmlOutput; } QList<Component*> componentsToRemove = componentsToUninstall(); @@ -2253,28 +2270,44 @@ bool PackageManagerCore::calculateComponents(QString *displayString) } htmlOutput.append(QString::fromLatin1("<li> %1 </li>").arg(component->name())); } - if (displayString) - *displayString = htmlOutput; - return true; + return htmlOutput; } /*! - Calculates a list of components to uninstall based on the current run mode. + Calculates a list of components to uninstall. + The aboutCalculateComponentsToUninstall() signal is emitted before the calculation starts, the finishedCalculateComponentsToUninstall() signal once all - calculations are done. Always returns \c true. + calculations are done. Returns \c true on success, \c false otherwise. \sa {installer::calculateComponentsToUninstall}{installer.calculateComponentsToUninstall} */ bool PackageManagerCore::calculateComponentsToUninstall() const { emit aboutCalculateComponentsToUninstall(); - if (!isUpdater()) { - d->calculateUninstallComponents(); - d->storeCheckState(); + + d->clearUninstallerCalculator(); + const QList<Component *> componentsToInstallList = d->installerCalculator()->orderedComponentsToInstall(); + + QList<Component *> selectedComponentsToUninstall; + foreach (Component* component, components(PackageManagerCore::ComponentType::Replacements)) { + // Uninstall the component if replacement is selected for install or update + QPair<Component*, Component*> comp = d->componentsToReplace().value(component->name()); + if (comp.first && d->m_installerCalculator->orderedComponentsToInstall().contains(comp.first)) { + d->uninstallerCalculator()->insertUninstallReason(component, + UninstallerCalculator::Replaced, comp.first->name()); + selectedComponentsToUninstall.append(comp.second); + } + } + foreach (Component *component, components(PackageManagerCore::ComponentType::AllNoReplacements)) { + if (component->uninstallationRequested() && !componentsToInstallList.contains(component)) + selectedComponentsToUninstall.append(component); } + const bool componentsToUninstallCalculated = + d->uninstallerCalculator()->appendComponentsToUninstall(selectedComponentsToUninstall); + emit finishedCalculateComponentsToUninstall(); - return true; + return componentsToUninstallCalculated; } /*! @@ -2298,6 +2331,14 @@ QString PackageManagerCore::componentsToInstallError() const } /*! + Returns errors found in the components that are marked for uninstallation. +*/ +QString PackageManagerCore::componentsToUninstallError() const +{ + return d->uninstallerCalculator()->componentsToUninstallError(); +} + +/*! Returns the reason why \a component needs to be installed: \list @@ -4336,7 +4377,7 @@ bool PackageManagerCore::fetchUpdaterPackages(const PackagesList &remotes, const if (d->statusCanceledOrFailed()) return false; - if (!component->isUnstable() && component->autoDependencies().isEmpty()) + if (!component->isUnstable()) component->setCheckState(Qt::Checked); } @@ -4348,7 +4389,7 @@ bool PackageManagerCore::fetchUpdaterPackages(const PackagesList &remotes, const foreach (QInstaller::Component *component, d->m_updaterComponentsDeps) { if (d->statusCanceledOrFailed()) return false; - if (component->isInstalled() && !component->autoDependencies().isEmpty()) { + if (component->isInstalled()) { // since we do not put them into the model, which would force a update of e.g. tri state // components, we have to check all installed components ourselves if (!component->isUnstable()) @@ -4527,8 +4568,7 @@ ComponentModel *PackageManagerCore::componentModel(PackageManagerCore *core, con ComponentModel::tr("Release Date")); model->setHeaderData(ComponentModelHelper::UncompressedSizeColumn, Qt::Horizontal, ComponentModel::tr("Size")); - connect(model, &ComponentModel::checkStateChanged, - this, &PackageManagerCore::componentsToInstallNeedsRecalculation); + return model; } diff --git a/src/libs/installer/packagemanagercore.h b/src/libs/installer/packagemanagercore.h index 936a377ab..c63447543 100644 --- a/src/libs/installer/packagemanagercore.h +++ b/src/libs/installer/packagemanagercore.h @@ -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. @@ -240,12 +240,15 @@ public: Q_INVOKABLE bool calculateComponentsToInstall() const; QList<Component*> orderedComponentsToInstall() const; - bool calculateComponents(QString *displayString); + + Q_INVOKABLE bool recalculateAllComponents(); + QString componentResolveReasons() const; Q_INVOKABLE bool calculateComponentsToUninstall() const; QList<Component*> componentsToUninstall() const; QString componentsToInstallError() const; + QString componentsToUninstallError() const; QString installReason(Component *component) const; QString uninstallReason(Component *component) const; @@ -363,8 +366,8 @@ public Q_SLOTS: void languageChanged(); void setCompleteUninstallation(bool complete); void cancelMetaInfoJob(); - void componentsToInstallNeedsRecalculation(); - void clearComponentsToInstallCalculated(); + void componentsToInstallNeedsRecalculation(); // TODO: deprecated, remove + void clearComponentsToInstallCalculated() {} // TODO: deprecated, remove Q_SIGNALS: void aboutCalculateComponentsToInstall() const; diff --git a/src/libs/installer/packagemanagercore_p.cpp b/src/libs/installer/packagemanagercore_p.cpp index 52d66ff6b..7313a2761 100644 --- a/src/libs/installer/packagemanagercore_p.cpp +++ b/src/libs/installer/packagemanagercore_p.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. @@ -170,7 +170,6 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core) , m_updateSourcesAdded(false) , m_magicBinaryMarker(0) // initialize with pseudo marker , m_magicMarkerSupplement(BinaryContent::Default) - , m_componentsToInstallCalculated(false) , m_componentScriptEngine(nullptr) , m_controlScriptEngine(nullptr) , m_installerCalculator(nullptr) @@ -209,7 +208,6 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, q , m_updateSourcesAdded(false) , m_magicBinaryMarker(magicInstallerMaker) , m_magicMarkerSupplement(BinaryContent::Default) - , m_componentsToInstallCalculated(false) , m_componentScriptEngine(nullptr) , m_controlScriptEngine(nullptr) , m_installerCalculator(nullptr) @@ -496,7 +494,6 @@ void PackageManagerCorePrivate::clearAllComponentLists() m_deletedReplacedComponents.clear(); m_componentsToReplaceAllMode.clear(); - m_componentsToInstallCalculated = false; m_foundEssentialUpdate = false; qDeleteAll(toDelete); @@ -524,7 +521,6 @@ void PackageManagerCorePrivate::clearUpdaterComponentLists() m_updaterDependencyReplacements.clear(); m_componentsToReplaceUpdaterMode.clear(); - m_componentsToInstallCalculated = false; m_foundEssentialUpdate = false; qDeleteAll(usedComponents); @@ -590,7 +586,6 @@ void PackageManagerCorePrivate::initialize(const QHash<QString, QString> ¶ms { m_coreCheckedHash.clear(); m_data = PackageManagerCoreData(params, isInstaller()); - m_componentsToInstallCalculated = false; #ifdef Q_OS_LINUX if (m_launchedAsRoot && isInstaller()) @@ -2921,7 +2916,6 @@ void PackageManagerCorePrivate::restoreCheckState() } m_coreCheckedHash.clear(); - m_componentsToInstallCalculated = false; } void PackageManagerCorePrivate::storeCheckState() @@ -3062,20 +3056,19 @@ QStringList PackageManagerCorePrivate::runningInstallerProcesses(const QStringLi bool PackageManagerCorePrivate::calculateComponentsAndRun() { - QString htmlOutput; - bool componentsOk = m_core->calculateComponents(&htmlOutput); - - try { - loadComponentScripts(installerCalculator()->orderedComponentsToInstall(), true); - } catch (const Error &error) { - qCWarning(QInstaller::lcInstallerInstallLog) << error.message(); - return false; - } + bool componentsOk = m_core->recalculateAllComponents(); if (statusCanceledOrFailed()) { qCDebug(QInstaller::lcInstallerInstallLog) << "Installation canceled."; } else if (componentsOk && acceptLicenseAgreements()) { - qCDebug(QInstaller::lcInstallerInstallLog).noquote() << htmlToString(htmlOutput); + try { + loadComponentScripts(installerCalculator()->orderedComponentsToInstall(), true); + } catch (const Error &error) { + qCWarning(QInstaller::lcInstallerInstallLog) << error.message(); + return false; + } + qCDebug(QInstaller::lcInstallerInstallLog).noquote() + << htmlToString(m_core->componentResolveReasons()); QString spaceInfo; const bool spaceOk = m_core->checkAvailableSpace(spaceInfo); @@ -3092,28 +3085,6 @@ bool PackageManagerCorePrivate::calculateComponentsAndRun() return false; } -void PackageManagerCorePrivate::calculateUninstallComponents() -{ - clearUninstallerCalculator(); - const QList<Component *> componentsToInstallList = installerCalculator()->orderedComponentsToInstall(); - - QList<Component *> selectedComponentsToUninstall; - foreach (Component* component, m_core->components(PackageManagerCore::ComponentType::Replacements)) { - // Uninstall the component if replacement is selected for install or update - QPair<Component*, Component*> comp = componentsToReplace().value(component->name()); - if (comp.first && m_installerCalculator->orderedComponentsToInstall().contains(comp.first)) { - uninstallerCalculator()->insertUninstallReason(component, - UninstallerCalculator::Replaced, comp.first->name()); - selectedComponentsToUninstall.append(comp.second); - } - } - foreach (Component *component, m_core->components(PackageManagerCore::ComponentType::AllNoReplacements)) { - if (component->uninstallationRequested() && !componentsToInstallList.contains(component)) - selectedComponentsToUninstall.append(component); - } - uninstallerCalculator()->appendComponentsToUninstall(selectedComponentsToUninstall); -} - bool PackageManagerCorePrivate::acceptLicenseAgreements() const { // Always skip for uninstaller diff --git a/src/libs/installer/packagemanagercore_p.h b/src/libs/installer/packagemanagercore_p.h index 8d6ba0c89..f6e2b34dd 100644 --- a/src/libs/installer/packagemanagercore_p.h +++ b/src/libs/installer/packagemanagercore_p.h @@ -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. @@ -267,7 +267,6 @@ private: void findExecutablesRecursive(const QString &path, const QStringList &excludeFiles, QStringList *result); QStringList runningInstallerProcesses(const QStringList &exludeFiles); bool calculateComponentsAndRun(); - void calculateUninstallComponents(); bool acceptLicenseAgreements() const; bool askUserAcceptLicense(const QString &name, const QString &content) const; bool askUserConfirmCommand() const; @@ -292,7 +291,6 @@ private: qint64 m_magicBinaryMarker; int m_magicMarkerSupplement; - bool m_componentsToInstallCalculated; bool m_foundEssentialUpdate; mutable ScriptEngine *m_componentScriptEngine; diff --git a/src/libs/installer/packagemanagergui.cpp b/src/libs/installer/packagemanagergui.cpp index 9db6e6eeb..b91526663 100644 --- a/src/libs/installer/packagemanagergui.cpp +++ b/src/libs/installer/packagemanagergui.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. @@ -1439,7 +1439,6 @@ int PackageManagerPage::nextId() const if (core->isUninstaller()) return nextNextId; // forcibly hide the license page if we run as uninstaller - core->calculateComponentsToInstall(); foreach (Component* component, core->orderedComponentsToInstall()) { if (core->isMaintainer() && component->isInstalled()) continue; // package manager or updater, hide as long as the component is installed @@ -1950,9 +1949,6 @@ void IntroductionPage::entering() */ void IntroductionPage::leaving() { - // force a recalculation of components to install to keep the state correct - if (!packageManagerCore()->isUninstaller()) - packageManagerCore()->componentsToInstallNeedsRecalculation(); m_progressBar->setValue(0); m_progressBar->setRange(0, 0); setButtonText(QWizard::CancelButton, gui()->defaultButtonText(QWizard::CancelButton)); @@ -2082,7 +2078,6 @@ void LicenseAgreementPage::entering() m_textBrowser->setHtml(QString()); m_licenseListWidget->setVisible(false); - packageManagerCore()->calculateComponentsToInstall(); foreach (QInstaller::Component *component, packageManagerCore()->orderedComponentsToInstall()) packageManagerCore()->addLicenseItem(component->licenses()); @@ -2213,8 +2208,7 @@ void ComponentSelectionPage::entering() d->updateTreeView(); // check component model state so we can enable needed component selection buttons - if (core->isUpdater()) - d->onModelStateChanged(d->m_currentModel->checkedState()); + d->onModelStateChanged(d->m_currentModel->checkedState()); setModified(isComplete()); if (core->settings().repositoryCategories().count() > 0 && !core->isOfflineOnly() @@ -2339,7 +2333,7 @@ bool ComponentSelectionPage::addVirtualComponentToUninstall(const QString &name) name, allComponents); if (component && component->isInstalled() && component->isVirtual()) { component->setCheckState(Qt::Unchecked); - core->componentsToInstallNeedsRecalculation(); + core->recalculateAllComponents(); qCDebug(QInstaller::lcDeveloperBuild) << "Virtual component " << name << " was selected for uninstall by script."; return true; } @@ -2356,6 +2350,9 @@ void ComponentSelectionPage::setModified(bool modified) */ bool ComponentSelectionPage::isComplete() const { + if (!d->componentsResolved()) + return false; + if (packageManagerCore()->isInstaller() || packageManagerCore()->isUpdater()) return d->m_currentModel->checked().count(); @@ -2713,8 +2710,9 @@ void ReadyForInstallationPage::entering() .arg(productName())); } - QString htmlOutput; - bool componentsOk = packageManagerCore()->calculateComponents(&htmlOutput); + bool componentsOk = packageManagerCore()->recalculateAllComponents(); + const QString htmlOutput = packageManagerCore()->componentResolveReasons(); + qCDebug(QInstaller::lcInstallerInstallLog).noquote() << htmlToString(htmlOutput); m_taskDetailsBrowser->setHtml(htmlOutput); m_taskDetailsBrowser->setVisible(!componentsOk || LoggingHandler::instance().isVerbose()); diff --git a/src/libs/installer/uninstallercalculator.cpp b/src/libs/installer/uninstallercalculator.cpp index 2f029630f..214e1124a 100644 --- a/src/libs/installer/uninstallercalculator.cpp +++ b/src/libs/installer/uninstallercalculator.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. @@ -30,6 +30,7 @@ #include "component.h" #include "packagemanagercore.h" +#include "globals.h" namespace QInstaller { @@ -55,35 +56,60 @@ QSet<Component *> UninstallerCalculator::componentsToUninstall() const return m_componentsToUninstall; } -void UninstallerCalculator::appendComponentToUninstall(Component *component) +QString UninstallerCalculator::componentsToUninstallError() const +{ + return m_componentsToUninstallError; +} + +bool UninstallerCalculator::appendComponentToUninstall(Component *component) { if (!component) - return; + return true; if (!component->isInstalled()) - return; + return true; if (m_localDependencyComponentHash.contains(component->name())) { const QStringList &dependencies = PackageManagerCore::parseNames(m_localDependencyComponentHash.value(component->name())); for (const QString &dependencyComponent : dependencies) { Component *depComponent = m_core->componentByName(dependencyComponent); - if (!depComponent) + if (!depComponent || !depComponent->isInstalled()) + continue; + + if (m_componentsToUninstall.contains(depComponent) + || m_core->orderedComponentsToInstall().contains(depComponent)) { + // Component is already selected for uninstall or update continue; - if (depComponent->isInstalled() && !m_componentsToUninstall.contains(depComponent) - && !m_core->orderedComponentsToInstall().contains(depComponent)) { - appendComponentToUninstall(depComponent); - insertUninstallReason(depComponent, UninstallerCalculator::Dependent, component->name()); } + + if (depComponent->isEssential() || depComponent->forcedInstallation()) { + const QString errorMessage = QCoreApplication::translate("InstallerCalculator", + "Impossible dependency resolution detected. Forced install component \"%1\" would be uninstalled " + "because its dependency \"%2\" is marked for uninstallation with reason: \"%3\".") + .arg(depComponent->name(), component->name(), uninstallReason(component)); + + qCWarning(QInstaller::lcInstallerInstallLog).noquote() << errorMessage; + m_componentsToUninstallError.append(errorMessage); + return false; + } + // Resolve also all cascading dependencies + if (!appendComponentToUninstall(depComponent)) + return false; + + insertUninstallReason(depComponent, UninstallerCalculator::Dependent, component->name()); } } m_componentsToUninstall.insert(component); + return true; } -void UninstallerCalculator::appendComponentsToUninstall(const QList<Component*> &components) +bool UninstallerCalculator::appendComponentsToUninstall(const QList<Component*> &components) { - foreach (Component *component, components) - appendComponentToUninstall(component); + foreach (Component *component, components) { + if (!appendComponentToUninstall(component)) + return false; + } QList<Component*> autoDependOnList; // All regular dependees are resolved. Now we are looking for auto depend on components. @@ -109,6 +135,8 @@ void UninstallerCalculator::appendComponentsToUninstall(const QList<Component*> appendComponentsToUninstall(autoDependOnList); else appendVirtualComponentsToUninstall(); + + return true; } void UninstallerCalculator::insertUninstallReason(Component *component, const UninstallReasonType uninstallReason, diff --git a/src/libs/installer/uninstallercalculator.h b/src/libs/installer/uninstallercalculator.h index 74207e005..6bdd342d2 100644 --- a/src/libs/installer/uninstallercalculator.h +++ b/src/libs/installer/uninstallercalculator.h @@ -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. @@ -59,8 +59,9 @@ public: const QStringList &localVirtualComponents); QSet<Component*> componentsToUninstall() const; + QString componentsToUninstallError() const; - void appendComponentsToUninstall(const QList<Component*> &components); + bool appendComponentsToUninstall(const QList<Component*> &components); void insertUninstallReason(Component *component, const UninstallReasonType uninstallReason, const QString &referencedComponentName = QString()); @@ -70,8 +71,9 @@ public: private: QString uninstallReasonReferencedComponent(Component *component) const; bool isRequiredVirtualPackage(Component *component); - void appendComponentToUninstall(Component *component); + bool appendComponentToUninstall(Component *component); void appendVirtualComponentsToUninstall(); + QString m_componentsToUninstallError; QSet<Component *> m_componentsToUninstall; PackageManagerCore *m_core; diff --git a/src/libs/kdtools/updateoperation.cpp b/src/libs/kdtools/updateoperation.cpp index 9d1bd8230..6a7e62f2b 100644 --- a/src/libs/kdtools/updateoperation.cpp +++ b/src/libs/kdtools/updateoperation.cpp @@ -348,6 +348,8 @@ bool UpdateOperation::variableReplacement(QString *variableValue) key.prepend(QLatin1String("@")); key.append(QLatin1String("@")); *variableValue = m_core->replaceVariables(key); + qCDebug(QInstaller::lcInstallerInstallLog) << "Running above operation with replaced value: " << valueNormalized + << "has been replaced with" << *variableValue; variableValueChanged = true; } } diff --git a/tests/auto/installer/brokeninstaller/tst_brokeninstaller.cpp b/tests/auto/installer/brokeninstaller/tst_brokeninstaller.cpp index b52e0e9f6..9fbc12c41 100644 --- a/tests/auto/installer/brokeninstaller/tst_brokeninstaller.cpp +++ b/tests/auto/installer/brokeninstaller/tst_brokeninstaller.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. @@ -145,7 +145,7 @@ private slots: Component *componentDependingOnMissingDependency = core.componentByName("componentd"); componentDependingOnMissingDependency->addDependency("componentmissingdependency"); - core.componentsToInstallNeedsRecalculation(); + core.recalculateAllComponents(); model->reset(components); testModelState(model, m_checkedComponentsWithMissingDependency, m_partiallyCheckedComponentsWithBrokenScript, m_uncheckedComponentsWithMissingDependency); diff --git a/tests/auto/installer/packagemanagercore/tst_packagemanagercore.cpp b/tests/auto/installer/packagemanagercore/tst_packagemanagercore.cpp index 44dc65960..cc2e069ea 100644 --- a/tests/auto/installer/packagemanagercore/tst_packagemanagercore.cpp +++ b/tests/auto/installer/packagemanagercore/tst_packagemanagercore.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. @@ -260,7 +260,6 @@ private slots: root->setInstalled(); child1->setInstalled(); child2->setUninstalled(); - core.componentsToInstallNeedsRecalculation(); core.calculateComponentsToInstall(); QCOMPARE(core.requiredDiskSpace(), 250ULL); } |