diff options
-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/componentselectionpage_p.cpp | 20 | ||||
-rw-r--r-- | src/libs/installer/componentselectionpage_p.h | 4 | ||||
-rw-r--r-- | src/libs/installer/packagemanagercore.cpp | 140 | ||||
-rw-r--r-- | src/libs/installer/packagemanagercore.h | 11 | ||||
-rw-r--r-- | src/libs/installer/packagemanagercore_p.cpp | 37 | ||||
-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-- | tests/auto/installer/brokeninstaller/tst_brokeninstaller.cpp | 4 | ||||
-rw-r--r-- | tests/auto/installer/packagemanagercore/tst_packagemanagercore.cpp | 3 |
14 files changed, 205 insertions, 126 deletions
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 36d207af7..77a4775c4 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. @@ -1332,6 +1332,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 e5f1b38da..35b0ea0b2 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. @@ -208,6 +208,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/componentselectionpage_p.cpp b/src/libs/installer/componentselectionpage_p.cpp index 5f58ae5ba..1cb10336c 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()) @@ -536,6 +546,14 @@ void ComponentSelectionPagePrivate::selectDefault() void ComponentSelectionPagePrivate::onModelStateChanged(QInstaller::ComponentModel::ModelState state) { + 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 c0902569d..db6c692e2 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. @@ -532,7 +532,6 @@ void PackageManagerCore::reset() d->m_status = PackageManagerCore::Unfinished; d->m_installerBaseBinaryUnreplaced.clear(); d->m_coreCheckedHash.clear(); - d->m_componentsToInstallCalculated = false; } /*! @@ -602,32 +601,45 @@ bool PackageManagerCore::clearLocalCache(QString *error) } /*! + \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; } /*! @@ -2154,8 +2166,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. @@ -2166,16 +2179,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; } /*! @@ -2187,25 +2199,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(); @@ -2238,28 +2255,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; } /*! @@ -2283,6 +2316,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 @@ -4516,8 +4557,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 26fa8e086..fb1eee160 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. @@ -239,12 +239,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; @@ -360,8 +363,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 d9a35bcb1..6d56791a0 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; qDeleteAll(toDelete); cleanUpComponentEnvironment(); @@ -521,7 +518,6 @@ void PackageManagerCorePrivate::clearUpdaterComponentLists() m_updaterDependencyReplacements.clear(); m_componentsToReplaceUpdaterMode.clear(); - m_componentsToInstallCalculated = false; qDeleteAll(usedComponents); cleanUpComponentEnvironment(); @@ -586,7 +582,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()) @@ -2915,7 +2910,6 @@ void PackageManagerCorePrivate::restoreCheckState() } m_coreCheckedHash.clear(); - m_componentsToInstallCalculated = false; } void PackageManagerCorePrivate::storeCheckState() @@ -3056,12 +3050,12 @@ QStringList PackageManagerCorePrivate::runningInstallerProcesses(const QStringLi bool PackageManagerCorePrivate::calculateComponentsAndRun() { - QString htmlOutput; - bool componentsOk = m_core->calculateComponents(&htmlOutput); + bool componentsOk = m_core->recalculateAllComponents(); if (statusCanceledOrFailed()) { qCDebug(QInstaller::lcInstallerInstallLog) << "Installation canceled."; } else if (componentsOk && acceptLicenseAgreements()) { - qCDebug(QInstaller::lcInstallerInstallLog).noquote() << htmlToString(htmlOutput); + qCDebug(QInstaller::lcInstallerInstallLog).noquote() + << htmlToString(m_core->componentResolveReasons()); QString spaceInfo; const bool spaceOk = m_core->checkAvailableSpace(spaceInfo); @@ -3078,29 +3072,6 @@ bool PackageManagerCorePrivate::calculateComponentsAndRun() return false; } -void PackageManagerCorePrivate::calculateUninstallComponents() -{ - clearUninstallerCalculator(); - const QList<Component *> componentsToInstallList = installerCalculator()->orderedComponentsToInstall(); - QSet<Component*> componentsToInstall(componentsToInstallList.begin(), componentsToInstallList.end()); - - 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 56cf48d25..aaf9e27be 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 bec77b886..9cdea0356 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. @@ -1432,7 +1432,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 @@ -1935,9 +1934,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)); @@ -2067,7 +2063,6 @@ void LicenseAgreementPage::entering() m_textBrowser->setHtml(QString()); m_licenseListWidget->setVisible(false); - packageManagerCore()->calculateComponentsToInstall(); foreach (QInstaller::Component *component, packageManagerCore()->orderedComponentsToInstall()) packageManagerCore()->addLicenseItem(component->licenses()); @@ -2198,8 +2193,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() @@ -2301,7 +2295,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; } @@ -2318,6 +2312,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(); @@ -2675,8 +2672,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/tests/auto/installer/brokeninstaller/tst_brokeninstaller.cpp b/tests/auto/installer/brokeninstaller/tst_brokeninstaller.cpp index 43bd15288..afa11d707 100644 --- a/tests/auto/installer/brokeninstaller/tst_brokeninstaller.cpp +++ b/tests/auto/installer/brokeninstaller/tst_brokeninstaller.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2018 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); } |