diff options
author | Katja Marttila <katja.marttila@qt.io> | 2022-12-13 09:04:55 +0200 |
---|---|---|
committer | Katja Marttila <katja.marttila@qt.io> | 2022-12-13 09:08:15 +0200 |
commit | c02d33db5723b7605dc953de078c26d54f284d5e (patch) | |
tree | 2aa841ad0ac416bf9baed474e90c75abd6fc67f9 | |
parent | 662110e1594788ddc54ec379d61776e1bdd82670 (diff) | |
parent | b7929635dcdbd01135c04e0a7ffc8fa29d647d4d (diff) |
Merge remote-tracking branch 'origin/4.5' into master
Change-Id: Id8121e6b4cdda23c8c44cf779e35dce8d9a28090
50 files changed, 561 insertions, 409 deletions
@@ -1,3 +1,13 @@ +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) +- Fix errors occurring in full uninstall on macOS (QTIFW-2875) +- Fix updater view behavior for non-checkable components (QTIFW-836) +- Execute operation: fix overwritten error string for crashed processes (QTIFW-2875) +- MetadataJob: fix removing compressed repositories after extracting (QTIFW-2876) +- Fix replaced removal on update (QTIFW-2887) +- Fix user set binary marker not having any effect on maintenance tool (QTIFW-2884) + 4.5.0 - Fix required virtual components still uninstalled in some occasions - Update translations (QTIFW-2814) @@ -11,6 +21,12 @@ - Fix installer stalling when there's only one CPU core (QTIFW-2786) - Adjust the 'ready to install' message to avoid repeating the app name (SQUISH-9672) - CLI: add support for hiding values of printed options (QTIFW-2756) +- Replace .vbs hack to update maintenance tool binary on Windows (QTIFW-2625) +- Disable package manager and updater for offline maintenance tool (QTIFW-2627) +- Display progress for loading component install scripts (QTIFW-2701) +- Fix separators for localInstallerBinaryUsed() (QTIFW-2700) +- Allow searching components also in the updater view (QTIFW-2667) +- Add list of components to uninstall to installation log (QTIFW-2666) 4.4.2 - Fix uninstallation of needed virtual components diff --git a/coin/instructions/qmake_module_build.yaml b/coin/instructions/qmake_module_build.yaml index 023927567..4a475b600 100644 --- a/coin/instructions/qmake_module_build.yaml +++ b/coin/instructions/qmake_module_build.yaml @@ -9,7 +9,7 @@ instructions: maxTimeInSeconds: 600 maxTimeBetweenOutput: 600 project: qtsdk/qtsdk - ref: master + ref: 8d9c6bb366b0f3c1e46c045a6453776ae98a8051 directory: qtsdk/qtsdk userMessageOnFailure: "Could not install qtsdk/qtsdk source archive. Please investigate why." diff --git a/doc/installerfw.qdoc b/doc/installerfw.qdoc index a45ba0f9d..6e403dc6b 100644 --- a/doc/installerfw.qdoc +++ b/doc/installerfw.qdoc @@ -992,7 +992,8 @@ \row \li ForcedInstallation \li Determines that the package must always be installed. End users - cannot deselect it in the installer. + cannot deselect it in the installer. When updating components, the + component can still be deselected from an update. \row \li ForcedUpdate \li Marks the package as \c ForcedUpdate to force a restart of the @@ -1018,7 +1019,9 @@ \row \li Checkable \li Set to \c false if you want to hide the checkbox for an item. This is useful - when only a few subcomponents should be selected instead of all. Optional. + when only a few subcomponents should be selected instead of all. When updating + components, the checkbox is still visible to allow toggling the component for + update. Optional. \row \li ExpandedByDefault \li Set to \c true if you want this item to be expanded by default. Optional. diff --git a/src/libs/installer/component.cpp b/src/libs/installer/component.cpp index 2ce948e7e..8e32ad347 100644 --- a/src/libs/installer/component.cpp +++ b/src/libs/installer/component.cpp @@ -89,6 +89,8 @@ static const QLatin1String scUnstable("Unstable"); Component has dependencies to missing components. \value InvalidTreeName Component has an invalid tree name. + \value DescendantOfUnstable + Component is descendant of an unstable component. */ /*! @@ -461,12 +463,14 @@ void Component::setValue(const QString &key, const QString &value) if (key == scName) d->m_componentName = normalizedValue; - if (key == scCheckable) - this->setCheckable(normalizedValue.toLower() == scTrue); + if (key == scCheckable) // Non-checkable components can still be toggled in updater + this->setCheckable(normalizedValue.toLower() == scTrue || d->m_core->isUpdater()); if (key == scExpandedByDefault) this->setExpandedByDefault(normalizedValue.toLower() == scTrue); if (key == scForcedInstallation) { - if (value == scTrue && !PackageManagerCore::noForceInstallation()) { + if (value == scTrue && !d->m_core->isUpdater() && !PackageManagerCore::noForceInstallation()) { + // Forced installation components can still be toggled in updater or when + // core is set to ignore forced installations. setCheckable(false); setCheckState(Qt::Checked); } diff --git a/src/libs/installer/componentmodel.cpp b/src/libs/installer/componentmodel.cpp index 8407e5f2d..873175937 100644 --- a/src/libs/installer/componentmodel.cpp +++ b/src/libs/installer/componentmodel.cpp @@ -60,14 +60,7 @@ namespace QInstaller { */ /*! - \fn void QInstaller::ComponentModel::componentsCheckStateChanged(const QList<QModelIndex> &indexes) - - 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::modelCheckStateChanged(QInstaller::ComponentModel::ModelState state) + \fn void QInstaller::ComponentModel::checkStateChanged(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 @@ -240,7 +233,6 @@ 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 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) { @@ -262,13 +254,10 @@ bool ComponentModel::setData(const QModelIndex &index, const QVariant &value, in const Qt::CheckState oldValue = component->checkState(); newValue = (oldValue == Qt::Checked) ? Qt::Unchecked : Qt::Checked; } - const QList<QModelIndex> changed = updateCheckedState(nodes << component, newValue); - foreach (const QModelIndex &changedIndex, changed) { + const QSet<QModelIndex> changed = updateCheckedState(nodes << component, newValue); + foreach (const QModelIndex &changedIndex, changed) emit dataChanged(changedIndex, changedIndex); - } - updateModelState(); - if (changed.count() > 0) - emit componentsCheckStateChanged(changed); + updateAndEmitModelState(); // update the internal state } else { component->setData(value, role); emit dataChanged(index, index); @@ -448,8 +437,7 @@ void ComponentModel::setCheckedState(QInstaller::ComponentModel::ModelStateFlag default: break; } - updateModelState(); - emit modelCheckStateChanged(m_modelState); + updateAndEmitModelState(); // update the internal state } @@ -487,11 +475,10 @@ void ComponentModel::postModelReset() } m_currentCheckedState = m_initialCheckedState; - updateModelState(); // update the internal state - emit modelCheckStateChanged(m_modelState); + updateAndEmitModelState(); // update the internal state } -void ComponentModel::updateModelState() +void ComponentModel::updateAndEmitModelState() { if (m_rootComponentList.isEmpty()) { m_modelState = ComponentModel::Empty; @@ -510,6 +497,8 @@ void ComponentModel::updateModelState() m_modelState |= ComponentModel::AllChecked; m_modelState &= ~ComponentModel::PartiallyChecked; } + + emit checkStateChanged(m_modelState); } void ComponentModel::collectComponents(Component *const component, const QModelIndex &parent) const @@ -553,7 +542,7 @@ static Qt::CheckState verifyPartiallyChecked(Component *component) } // namespace ComponentModelPrivate -QList<QModelIndex> ComponentModel::updateCheckedState(const ComponentSet &components, const Qt::CheckState state) +QSet<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; @@ -564,7 +553,7 @@ QList<QModelIndex> ComponentModel::updateCheckedState(const ComponentSet &compon } } - QList<QModelIndex> changed; + QSet<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--) { @@ -586,10 +575,7 @@ QList<QModelIndex> ComponentModel::updateCheckedState(const ComponentSet &compon continue; node->setCheckState(newState); - 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())); + changed.insert(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 001753055..b6b489888 100644 --- a/src/libs/installer/componentmodel.h +++ b/src/libs/installer/componentmodel.h @@ -91,17 +91,16 @@ public Q_SLOTS: void setCheckedState(QInstaller::ComponentModel::ModelStateFlag state); Q_SIGNALS: - void componentsCheckStateChanged(const QList<QModelIndex> &indexes); - void modelCheckStateChanged(QInstaller::ComponentModel::ModelState state); + void checkStateChanged(QInstaller::ComponentModel::ModelState state); private Q_SLOTS: void onVirtualStateChanged(); private: void postModelReset(); - void updateModelState(); + void updateAndEmitModelState(); void collectComponents(Component *const component, const QModelIndex &parent) const; - QList<QModelIndex> updateCheckedState(const ComponentSet &components, const Qt::CheckState state); + QSet<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 42b9c2a86..83bcaae14 100644 --- a/src/libs/installer/componentselectionpage_p.cpp +++ b/src/libs/installer/componentselectionpage_p.cpp @@ -44,7 +44,6 @@ #include <QVBoxLayout> #include <QHBoxLayout> #include <QCheckBox> -#include <QHeaderView> #include <QStandardPaths> #include <QFileDialog> #include <QStackedLayout> @@ -73,8 +72,10 @@ ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionP , m_categoryWidget(Q_NULLPTR) , m_categoryLayoutVisible(false) , m_proxyModel(new ComponentSortFilterProxyModel(q)) + , m_headerStretchLastSection(false) { m_treeView->setObjectName(QLatin1String("ComponentsTreeView")); + m_treeView->setUniformRowHeights(true); m_proxyModel->setRecursiveFilteringEnabled(true); m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); @@ -185,16 +186,10 @@ ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionP m_stackedLayout->addWidget(progressStackedWidget); m_stackedLayout->setCurrentIndex(0); - 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_allModel, &ComponentModel::checkStateChanged, + this, &ComponentSelectionPagePrivate::onModelStateChanged); + connect(m_updaterModel, &ComponentModel::checkStateChanged, + this, &ComponentSelectionPagePrivate::onModelStateChanged); connect(m_core, SIGNAL(metaJobProgress(int)), this, SLOT(onProgressChanged(int))); connect(m_core, SIGNAL(metaJobInfoMessage(QString)), this, SLOT(setMessage(QString))); @@ -374,6 +369,9 @@ void ComponentSelectionPagePrivate::expandDefault() */ void ComponentSelectionPagePrivate::expandSearchResults() { + // Avoid resizing the sections after each expand of a node + storeHeaderResizeModes(); + // Expand parents of root indexes accepted by filter const QVector<QModelIndex> acceptedIndexes = m_proxyModel->directlyAcceptedIndexes(); for (auto proxyModelIndex : acceptedIndexes) { @@ -389,6 +387,7 @@ void ComponentSelectionPagePrivate::expandSearchResults() index = index.parent(); } } + restoreHeaderResizeModes(); } void ComponentSelectionPagePrivate::currentSelectedChanged(const QModelIndex ¤t) @@ -588,4 +587,29 @@ void ComponentSelectionPagePrivate::setSearchPattern(const QString &text) } } +/*! + Stores the current resize modes of the tree view header's columns, and sets + the new resize modes to \c QHeaderView::Fixed. +*/ +void ComponentSelectionPagePrivate::storeHeaderResizeModes() +{ + m_headerStretchLastSection = m_treeView->header()->stretchLastSection(); + for (int i = 0; i < ComponentModelHelper::LastColumn; ++i) + m_headerResizeModes.insert(i, m_treeView->header()->sectionResizeMode(i)); + + m_treeView->header()->setStretchLastSection(false); + m_treeView->header()->setSectionResizeMode(QHeaderView::Fixed); +} + +/*! + Restores the resize modes of the tree view header's columns, that were + stored when calling \l storeHeaderResizeModes(). +*/ +void ComponentSelectionPagePrivate::restoreHeaderResizeModes() +{ + m_treeView->header()->setStretchLastSection(m_headerStretchLastSection); + for (int i = 0; i < ComponentModelHelper::LastColumn; ++i) + m_treeView->header()->setSectionResizeMode(i, m_headerResizeModes.value(i)); +} + } // namespace QInstaller diff --git a/src/libs/installer/componentselectionpage_p.h b/src/libs/installer/componentselectionpage_p.h index fc37ebdaa..7c2f6a38e 100644 --- a/src/libs/installer/componentselectionpage_p.h +++ b/src/libs/installer/componentselectionpage_p.h @@ -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. @@ -31,6 +31,7 @@ #include <QObject> #include <QWidget> +#include <QHeaderView> #include "componentmodel.h" #include "packagemanagergui.h" @@ -90,6 +91,10 @@ public slots: void setSearchPattern(const QString &text); private: + void storeHeaderResizeModes(); + void restoreHeaderResizeModes(); + +private: ComponentSelectionPage *q; PackageManagerCore *m_core; QTreeView *m_treeView; @@ -113,6 +118,9 @@ private: QStackedLayout *m_stackedLayout; ComponentSortFilterProxyModel *m_proxyModel; QLineEdit *m_searchLineEdit; + + bool m_headerStretchLastSection; + QHash<int, QHeaderView::ResizeMode> m_headerResizeModes; }; } // namespace QInstaller diff --git a/src/libs/installer/elevatedexecuteoperation.cpp b/src/libs/installer/elevatedexecuteoperation.cpp index 092b0f0eb..6f2393799 100644 --- a/src/libs/installer/elevatedexecuteoperation.cpp +++ b/src/libs/installer/elevatedexecuteoperation.cpp @@ -221,30 +221,29 @@ int ElevatedExecuteOperation::Private::run(QStringList &arguments, const Operati q->setValue(QLatin1String("ExitCode"), process->exitCode()); - if (process->exitStatus() == QProcessWrapper::CrashExit) { - q->setError(UserDefinedError); - q->setErrorString(tr("Program crashed: \"%1\"").arg(callstr)); - returnValue = Error; - } + if (success) { + const QByteArray standardErrorOutput = process->readAllStandardError(); + // in error case it would be useful to see something in verbose output + if (!standardErrorOutput.isEmpty()) + qCWarning(QInstaller::lcInstallerInstallLog).noquote() << standardErrorOutput; - if (!allowedExitCodes.contains(process->exitCode()) && returnValue != NeedsRerun) { - if (!needsRerunWithReplacedVariables(arguments, type)) { + if (process->exitStatus() == QProcessWrapper::CrashExit) { q->setError(UserDefinedError); - if (customErrorMessage.isEmpty()) { - q->setErrorString(tr("Execution failed (Unexpected exit code: %1): \"%2\"") - .arg(QString::number(process->exitCode()), callstr)); + q->setErrorString(tr("Program crashed: \"%1\"").arg(callstr)); + returnValue = Error; + } else if (!allowedExitCodes.contains(process->exitCode()) && returnValue != NeedsRerun) { + if (!needsRerunWithReplacedVariables(arguments, type)) { + q->setError(UserDefinedError); + if (customErrorMessage.isEmpty()) { + q->setErrorString(tr("Execution failed (Unexpected exit code: %1): \"%2\"") + .arg(QString::number(process->exitCode()), callstr)); + } else { + q->setErrorString(customErrorMessage); + } + returnValue = Error; } else { - q->setErrorString(customErrorMessage); + returnValue = NeedsRerun; } - - QByteArray standardErrorOutput = process->readAllStandardError(); - // in error case it would be useful to see something in verbose output - if (!standardErrorOutput.isEmpty()) - qCWarning(QInstaller::lcInstallerInstallLog).noquote() << standardErrorOutput; - - returnValue = Error; - } else { - returnValue = NeedsRerun; } } Q_ASSERT(process); diff --git a/src/libs/installer/extractarchiveoperation.cpp b/src/libs/installer/extractarchiveoperation.cpp index 986b9d8c8..162bd1609 100644 --- a/src/libs/installer/extractarchiveoperation.cpp +++ b/src/libs/installer/extractarchiveoperation.cpp @@ -178,9 +178,13 @@ bool ExtractArchiveOperation::performOperation() QString installDir = targetDir; // If we have package manager in use (normal installer run) then use // TargetDir for saving filenames, otherwise those would be saved to - // extracted folder. - if (packageManager()) - installDir = packageManager()->value(scTargetDir); + // extracted folder. Also initialize installerbasebinary which we use later + // to check if the extracted file in question is the maintenancetool itself. + QString installerBaseBinary; + if (PackageManagerCore *core = packageManager()) { + installDir = core->value(scTargetDir); + installerBaseBinary = core->toNativeSeparators(core->replaceVariables(core->installerBaseBinary())); + } const QString resourcesPath = installDir + QLatin1Char('/') + QLatin1String("installerResources"); QString fileDirectory = resourcesPath + QLatin1Char('/') + archivePath.section(QLatin1Char('/'), 1, 1, @@ -202,6 +206,13 @@ bool ExtractArchiveOperation::performOperation() setDefaultFilePermissions(file.fileName(), DefaultFilePermissions::NonExecutable); QDataStream out (&file); for (int i = 0; i < files.count(); ++i) { + if (!installerBaseBinary.isEmpty() && files[i].startsWith(installerBaseBinary)) { + // Do not write installerbase binary filename to extracted files. Installer binary + // is maintenance tool program, the binary is removed elsewhere + // when we do full uninstall. + files.clear(); + break; + } files[i] = replacePath(files.at(i), installDir, QLatin1String(scRelocatable)); } out << files; diff --git a/src/libs/installer/fileutils.cpp b/src/libs/installer/fileutils.cpp index 16502383c..633d283bb 100644 --- a/src/libs/installer/fileutils.cpp +++ b/src/libs/installer/fileutils.cpp @@ -485,7 +485,7 @@ bool QInstaller::createDirectoryWithParents(const QString &path) return false; QDir dir(path); - if (dir.mkdir(path)) + if (dir.exists() || dir.mkdir(path)) return true; // mkdir failed, try to create the parent directory @@ -493,7 +493,7 @@ bool QInstaller::createDirectoryWithParents(const QString &path) return false; // now try again - if (dir.mkdir(path)) + if (dir.exists() || dir.mkdir(path)) return true; // directory may be have also been created elsewhere diff --git a/src/libs/installer/genericdatacache.cpp b/src/libs/installer/genericdatacache.cpp index b70cbcb0b..45f3fa3e8 100644 --- a/src/libs/installer/genericdatacache.cpp +++ b/src/libs/installer/genericdatacache.cpp @@ -75,7 +75,7 @@ static const QLatin1String scManifestFile("manifest.json"); */ /*! - \fn QInstaller::CacheableItem::isActive const + \fn QInstaller::CacheableItem::isActive() const Returns \c true if this item is an actively used cache item, \c false otherwise. This information is used as a hint for filtering obsolete entries, an active item @@ -532,11 +532,11 @@ void GenericDataCache<T>::invalidate() } /*! - \fn template <typename T> QInstaller::GenericDataCache<T>::setErrorString(const QString &error) + \fn template <typename T> QInstaller::GenericDataCache<T>::setErrorString(const QString &error) const Sets the current error string to \a error and prints it as a warning to the console. */ -template<typename T> +template <typename T> void GenericDataCache<T>::setErrorString(const QString &error) const { m_error = error; diff --git a/src/libs/installer/installercalculator.cpp b/src/libs/installer/installercalculator.cpp index f285158f8..77989e3f8 100644 --- a/src/libs/installer/installercalculator.cpp +++ b/src/libs/installer/installercalculator.cpp @@ -49,7 +49,8 @@ InstallerCalculator::InstallerCalculator(PackageManagerCore *core, const AutoDep InstallerCalculator::InstallReasonType InstallerCalculator::installReasonType(const Component *c) const { - return m_toInstallComponentIdReasonHash.value(c->name()).first; + return m_toInstallComponentIdReasonHash.value(c->name(), + qMakePair(InstallerCalculator::Selected, QString())).first; } QString InstallerCalculator::installReason(const Component *component) const @@ -82,103 +83,77 @@ QString InstallerCalculator::componentsToInstallError() const return m_componentsToInstallError; } -bool InstallerCalculator::appendComponentsToInstall(const QList<Component *> &components, bool modelReset, const bool revertFromInstall) +bool InstallerCalculator::appendComponentsToInstall(const QList<Component *> &components) { if (components.isEmpty()) return true; QList<Component*> notAppendedComponents; // for example components with unresolved dependencies - foreach (Component *component, components) { + for (Component *component : qAsConst(components)){ if (!component) continue; - // When model has been reseted, there should not be components with the same name if (m_toInstallComponentIds.contains(component->name())) { - if (modelReset) { - const QString errorMessage = recursionError(component); - qCWarning(QInstaller::lcInstallerInstallLog).noquote() << errorMessage; - m_componentsToInstallError.append(errorMessage); - Q_ASSERT_X(!m_toInstallComponentIds.contains(component->name()), Q_FUNC_INFO, - qPrintable(errorMessage)); - return false; - } - if (!revertFromInstall && !modelReset) { - // We can end up here if component is already added as dependency and - // user explicitly selects it to install. Increase the references to - // know when the component should be removed from install - const QStringList dependenciesList = component->currentDependencies(); - for (const QString &dependencyComponentName : dependenciesList) - calculateComponentDependencyReferences(dependencyComponentName, component); - continue; - } + 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 (component->currentDependencies().isEmpty()) - realAppendToInstallComponents(component, QString(), revertFromInstall); + realAppendToInstallComponents(component); else notAppendedComponents.append(component); } - foreach (Component *component, notAppendedComponents) { - if (!appendComponentToInstall(component, QString(), revertFromInstall)) + for (Component *component : qAsConst(notAppendedComponents)) { + if (!appendComponentToInstall(component)) return false; } // All regular dependencies are resolved. Now we are looking for auto depend on components. - QSet<Component *> foundAutoDependOnList = autodependencyComponents(revertFromInstall); + QSet<Component *> foundAutoDependOnList = autodependencyComponents(); if (!foundAutoDependOnList.isEmpty()) - return appendComponentsToInstall(foundAutoDependOnList.values(), modelReset, revertFromInstall); - + return appendComponentsToInstall(foundAutoDependOnList.values()); return true; } -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; + return m_toInstallComponentIdReasonHash.value(component->name(), + qMakePair(InstallerCalculator::Selected, QString())).second; } -void InstallerCalculator::insertInstallReason(const Component *component, - const InstallReasonType installReason, const QString &referencedComponentName, const bool revertFromInstall) +void InstallerCalculator::insertInstallReason(Component *component, + InstallReasonType installReason, const QString &referencedComponentName) { - 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)); - } + // keep the first reason + if (m_toInstallComponentIdReasonHash.contains(component->name())) + return; + m_toInstallComponentIdReasonHash.insert(component->name(), + qMakePair(installReason, referencedComponentName)); } -void InstallerCalculator::realAppendToInstallComponents(Component *component, const QString &version, const bool revertFromInstall) +void InstallerCalculator::realAppendToInstallComponents(Component *component, const QString &version) { if (!m_componentsForAutodepencencyCheck.contains(component)) m_componentsForAutodepencencyCheck.append(component); - if (revertFromInstall) { - if (m_toInstallComponentIds.contains(component->name())) { - // Component is no longer added as dependency and is not explicitly selected for install by user - if (m_referenceCount.value(component->name()).isEmpty() && !component->isSelected()) { - m_toInstallComponentIds.remove(component->name()); - m_orderedComponentsToInstall.removeAll(component); - } - } - } else { - if (!component->isInstalled(version) - || (m_core->isUpdater() && component->isUpdateAvailable())) { - m_toInstallComponentIds.insert(component->name()); - m_orderedComponentsToInstall.append(component); - } + + if (!component->isInstalled(version) || (m_core->isUpdater() && component->isUpdateAvailable())) { + m_orderedComponentsToInstall.append(component); + m_toInstallComponentIds.insert(component->name()); } } -bool InstallerCalculator::appendComponentToInstall(Component *component, const QString &version, bool revertFromInstall) +QString InstallerCalculator::recursionError(Component *component) const +{ + return QCoreApplication::translate("InstallerCalculator", "Recursion detected, component \"%1\" " + "already added with reason: \"%2\"").arg(component->name(), installReason(component)); +} + +bool InstallerCalculator::appendComponentToInstall(Component *component, const QString &version) { const QStringList dependenciesList = component->currentDependencies(); QString requiredDependencyVersion = version; @@ -199,8 +174,6 @@ bool InstallerCalculator::appendComponentToInstall(Component *component, const Q return false; } } - if (revertFromInstall && dependencyComponent->forcedInstallation()) - continue; //Check if component requires higher version than what might be already installed bool isUpdateRequired = false; QString requiredName; @@ -221,72 +194,39 @@ 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 dependency component, so we know when component - // needs to be removed from install. Do not increase the reference count when - // the dependency is also autodependency as the component is removed anyway only - // when there are no references to the dependency. - if (revertFromInstall) { - if (m_toInstallComponentIds.contains(dependencyComponentName) - && m_referenceCount.contains(dependencyComponentName)) { - if (!component->autoDependencies().contains(dependencyComponentName)) { - QStringList &value = m_referenceCount[dependencyComponentName]; - if (value.contains(component->name())) { - value.removeOne(component->name()); - if (value.isEmpty()) - m_referenceCount.remove(dependencyComponentName); - } - } - } - insertInstallReason(dependencyComponent, InstallerCalculator::Dependent, - component->name(), true); - if (!appendComponentToInstall(dependencyComponent, requiredDependencyVersion, revertFromInstall)) + //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()) + || isUpdateRequired) && (!m_toInstallComponentIds.contains(dependencyComponent->name()) + && !m_toInstallComponentIds.contains(component->name()))) { + if (m_visitedComponents.value(component).contains(dependencyComponent)) { + const QString errorMessage = recursionError(component); + qCWarning(QInstaller::lcInstallerInstallLog).noquote() << errorMessage; + m_componentsToInstallError = errorMessage; + Q_ASSERT_X(!m_visitedComponents.value(component).contains(dependencyComponent), + Q_FUNC_INFO, qPrintable(errorMessage)); return false; - m_visitedComponents.remove(component); - } else { - //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()) - || isUpdateRequired) && (!m_toInstallComponentIds.contains(dependencyComponent->name()) - && !m_toInstallComponentIds.contains(component->name()))) { - if (m_visitedComponents.value(component).contains(dependencyComponent)) { - const QString errorMessage = recursionError(component); - qCWarning(QInstaller::lcInstallerInstallLog).noquote() << errorMessage; - m_componentsToInstallError = errorMessage; - Q_ASSERT_X(!m_visitedComponents.value(component).contains(dependencyComponent), - Q_FUNC_INFO, qPrintable(errorMessage)); - return false; - } - m_visitedComponents[component].insert(dependencyComponent); } - if (!component->autoDependencies().contains(dependencyComponentName)) - m_referenceCount[dependencyComponentName] << component->name(); + m_visitedComponents[component].insert(dependencyComponent); + // add needed dependency components to the next run + insertInstallReason(dependencyComponent, InstallerCalculator::Dependent, + component->name()); - insertInstallReason(dependencyComponent, InstallerCalculator::Dependent, component->name()); - if (!appendComponentToInstall(dependencyComponent, requiredDependencyVersion, revertFromInstall)) + if (!appendComponentToInstall(dependencyComponent, requiredDependencyVersion)) return false; } } - if (!revertFromInstall && !m_toInstallComponentIds.contains(component->name())) { - realAppendToInstallComponents(component, requiredDependencyVersion, revertFromInstall); + + if (!m_toInstallComponentIds.contains(component->name())) { + realAppendToInstallComponents(component, requiredDependencyVersion); 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) +QSet<Component *> InstallerCalculator::autodependencyComponents() { // All regular dependencies are resolved. Now we are looking for auto depend on components. // m_componentsForAutodepencencyCheck is a list of recently calculated components to be installed @@ -295,13 +235,12 @@ QSet<Component *> InstallerCalculator::autodependencyComponents(const bool rever QSet<Component *> foundAutoDependOnList; for (const Component *component : qAsConst(m_componentsForAutodepencencyCheck)) { if (!m_autoDependencyComponentHash.contains(component->name()) - || (!revertFromInstall && m_core->isUpdater() && !component->updateRequested())) + || (m_core->isUpdater() && !component->updateRequested())) continue; for (const QString& autoDependency : m_autoDependencyComponentHash.value(component->name())) { // If a components is already installed or is scheduled for installation, no need to check // for auto depend installation. - if ((!revertFromInstall && m_toInstallComponentIds.contains(autoDependency)) - || (revertFromInstall && !m_toInstallComponentIds.contains(autoDependency))) { + if (m_toInstallComponentIds.contains(autoDependency)) { continue; } Component *autoDependComponent = m_core->componentByName(autoDependency); @@ -316,10 +255,6 @@ QSet<Component *> InstallerCalculator::autodependencyComponents(const bool rever foundAutoDependOnList.insert(autoDependComponent); insertInstallReason(autoDependComponent, InstallerCalculator::Automatic); } - } else if (revertFromInstall - && m_toInstallComponentIds.contains(autoDependComponent->name()) - && !m_toInstallComponentIds.contains(component->name())) { - foundAutoDependOnList.insert(autoDependComponent); } } } @@ -327,20 +262,4 @@ QSet<Component *> InstallerCalculator::autodependencyComponents(const bool rever return foundAutoDependOnList; } -void InstallerCalculator::calculateComponentDependencyReferences(const QString &dependencyComponentName, const Component *component) -{ - Component *dependencyComponent = m_core->componentByName(dependencyComponentName); - if (!dependencyComponent || component->autoDependencies().contains(dependencyComponentName)) - return; - QStringList value = m_referenceCount.value(dependencyComponentName, QStringList()); - value << component->name(); - m_referenceCount.insert(dependencyComponentName, value); - - const QStringList dependenciesList = dependencyComponent->currentDependencies(); - for (const QString &depComponentName : dependenciesList) { - Component *depComponent = m_core->componentByName(depComponentName); - calculateComponentDependencyReferences(depComponentName, depComponent); - } -} - } // namespace QInstaller diff --git a/src/libs/installer/installercalculator.h b/src/libs/installer/installercalculator.h index bdb8ac404..422de439c 100644 --- a/src/libs/installer/installercalculator.h +++ b/src/libs/installer/installercalculator.h @@ -39,6 +39,7 @@ namespace QInstaller { class Component; +class PackageManagerCore; class INSTALLER_EXPORT InstallerCalculator { @@ -53,33 +54,27 @@ public: Resolved // "Component(s) that have resolved Dependencies" }; - InstallReasonType installReasonType(const Component *c) const; + InstallReasonType installReasonType(const Component *component) const; QString installReason(const Component *component) const; QList<Component*> orderedComponentsToInstall() const; QString componentsToInstallError() const; - - bool appendComponentsToInstall(const QList<Component*> &components, bool modelReset = false, const bool revertFromInstall = false); - bool removeComponentsFromInstall(const QList<Component*> &components); + bool appendComponentsToInstall(const QList<Component*> &components); private: 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); - void calculateComponentDependencyReferences(const QString &dependencyComponentName, const Component *component); + 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()); + QSet<Component *> autodependencyComponents(); + QString recursionError(Component *component) const; private: PackageManagerCore *m_core; QHash<Component*, QSet<Component*> > m_visitedComponents; QList<const Component*> m_componentsForAutodepencencyCheck; - //for faster lookups. - QSet<QString> m_toInstallComponentIds; - QHash<QString, QStringList> m_referenceCount; + QSet<QString> m_toInstallComponentIds; //for faster lookups QString m_componentsToInstallError; //calculate installation order variables QList<Component*> m_orderedComponentsToInstall; diff --git a/src/libs/installer/metadatajob.cpp b/src/libs/installer/metadatajob.cpp index 5179896ac..5a7aa66f4 100644 --- a/src/libs/installer/metadatajob.cpp +++ b/src/libs/installer/metadatajob.cpp @@ -582,6 +582,7 @@ void MetadataJob::metadataTaskFinished() } UnzipArchiveTask *task = new UnzipArchiveTask(result.target(), item.value(TaskRole::UserRole).toString()); + task->setRemoveArchive(true); QFutureWatcher<void> *watcher = new QFutureWatcher<void>(); m_unzipTasks.insert(watcher, qobject_cast<QObject*> (task)); diff --git a/src/libs/installer/metadatajob_p.h b/src/libs/installer/metadatajob_p.h index 5a4fcad2d..7dcb0b34c 100644 --- a/src/libs/installer/metadatajob_p.h +++ b/src/libs/installer/metadatajob_p.h @@ -62,10 +62,12 @@ class UnzipArchiveTask : public AbstractTask<void> public: UnzipArchiveTask(const QString &arcive, const QString &target) - : m_archive(arcive), m_targetDir(target) + : m_archive(arcive), m_targetDir(target), m_removeArchive(false) {} QString target() { return m_targetDir; } QString archive() { return m_archive; } + void setRemoveArchive(bool remove) { m_removeArchive = remove; } + void doTask(QFutureInterface<void> &fi) override { fi.reportStarted(); @@ -87,9 +89,10 @@ public: fi.reportException(UnzipArchiveException(MetadataJob::tr("Error while extracting " "archive \"%1\": %2").arg(QDir::toNativeSeparators(m_archive), archive->errorString()))); } - // Don't need the archive anymore + archive->close(); - QFile::remove(m_archive); + if (m_removeArchive) + QFile::remove(m_archive); fi.reportFinished(); } @@ -97,6 +100,7 @@ public: private: QString m_archive; QString m_targetDir; + bool m_removeArchive; }; class CacheTaskException : public QException diff --git a/src/libs/installer/packagemanagercore.cpp b/src/libs/installer/packagemanagercore.cpp index 45f8864a7..5100c62ed 100644 --- a/src/libs/installer/packagemanagercore.cpp +++ b/src/libs/installer/packagemanagercore.cpp @@ -622,7 +622,7 @@ void PackageManagerCore::componentsToInstallNeedsRecalculation() QList<Component*> selectedComponentsToInstall = componentsMarkedForInstallation(); d->m_componentsToInstallCalculated = - d->installerCalculator()->appendComponentsToInstall(selectedComponentsToInstall, true); + d->installerCalculator()->appendComponentsToInstall(selectedComponentsToInstall); d->calculateUninstallComponents(); d->updateComponentCheckedState(); @@ -633,66 +633,6 @@ 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 reselected 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); - if (componentsToUnInstall.isEmpty() && !componentsToInstall.isEmpty()) { - // There are no new components to be uninstalled but there - // are new components to be installed which might have virtual - // dependences to components which are already selected for uninstall. - // We need to remove those components from uninstall. - d->uninstallerCalculator()->appendVirtualComponentsToUninstall(true); - } - - d->updateComponentCheckedState(); -} - - -/*! Forces a recalculation of components to install. \sa {installer::clearComponentsToInstallCalculated}{installer.clearComponentsToInstallCalculated} */ @@ -3505,6 +3445,17 @@ void PackageManagerCore::setInstallerBaseBinary(const QString &path) } /*! + Returns the value of \c installerbase binary which is used when writing + the maintenance tool. Value can be empty. + + \sa setInstallerBaseBinary() +*/ +QString PackageManagerCore::installerBaseBinary() const +{ + return d->m_installerBaseBinaryUnreplaced; +} + +/*! Sets the \c installerbase binary located at \a path to use when writing the offline installer. Setting this makes it possible to run the offline generator in cases where we are not running a real installer, i.e. when executing autotests. @@ -4577,10 +4528,8 @@ ComponentModel *PackageManagerCore::componentModel(PackageManagerCore *core, con ComponentModel::tr("Release Date")); model->setHeaderData(ComponentModelHelper::UncompressedSizeColumn, Qt::Horizontal, ComponentModel::tr("Size")); - connect(model, &ComponentModel::modelCheckStateChanged, + connect(model, &ComponentModel::checkStateChanged, 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 cd44c2ef1..8a0b09c02 100644 --- a/src/libs/installer/packagemanagercore.h +++ b/src/libs/installer/packagemanagercore.h @@ -166,6 +166,7 @@ public: Q_INVOKABLE static QString findPath(const QString &name, const QStringList &paths = QStringList()); Q_INVOKABLE void setInstallerBaseBinary(const QString &path); + QString installerBaseBinary() const; void setOfflineBaseBinary(const QString &path); void addResourcesForOfflineGeneration(const QString &rcPath); @@ -362,7 +363,6 @@ public Q_SLOTS: void setCompleteUninstallation(bool complete); void cancelMetaInfoJob(); void componentsToInstallNeedsRecalculation(); - void calculateUserSelectedComponentsToInstall(const QList<QModelIndex> &indexes); void clearComponentsToInstallCalculated(); Q_SIGNALS: diff --git a/src/libs/installer/packagemanagercore_p.cpp b/src/libs/installer/packagemanagercore_p.cpp index 9b77dd657..721e0d867 100644 --- a/src/libs/installer/packagemanagercore_p.cpp +++ b/src/libs/installer/packagemanagercore_p.cpp @@ -544,21 +544,6 @@ 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; @@ -593,7 +578,7 @@ UninstallerCalculator *PackageManagerCorePrivate::uninstallerCalculator() const } } - pmcp->m_uninstallerCalculator = new UninstallerCalculator(installedComponents, m_core, + pmcp->m_uninstallerCalculator = new UninstallerCalculator(m_core, pmcp->m_autoDependencyComponentHash, pmcp->m_localDependencyComponentHash, pmcp->m_localVirtualComponents); } return m_uninstallerCalculator; @@ -1950,7 +1935,7 @@ bool PackageManagerCorePrivate::runPackageUpdater() // There is a replacement, but the replacement is not scheduled for update, keep it as well. if (m_componentsToReplaceUpdaterMode.contains(name) - && !m_componentsToReplaceUpdaterMode.value(name).first->updateRequested()) { + && !m_installerCalculator->orderedComponentsToInstall().contains(m_componentsToReplaceUpdaterMode.value(name).first)) { nonRevertedOperations.append(operation); continue; } @@ -3114,12 +3099,10 @@ void PackageManagerCorePrivate::calculateUninstallComponents() 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) { - if (comp.first->isSelectedForInstallation() || comp.first->updateRequested()) { - uninstallerCalculator()->insertUninstallReason(component, - UninstallerCalculator::Replaced, comp.first->name()); - selectedComponentsToUninstall.append(comp.second); - } + 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)) { diff --git a/src/libs/installer/packagemanagercore_p.h b/src/libs/installer/packagemanagercore_p.h index d45e2f219..8d6ba0c89 100644 --- a/src/libs/installer/packagemanagercore_p.h +++ b/src/libs/installer/packagemanagercore_p.h @@ -119,7 +119,6 @@ public: QList<Component*> &replacementDependencyComponents(); QHash<QString, QPair<Component*, Component*> > &componentsToReplace(); QHash<QString, QStringList > &componentReplaces(); - QList<Component*> replacedComponentsByName(const QString &name); void clearInstallerCalculator(); InstallerCalculator *installerCalculator() const; diff --git a/src/libs/installer/packagemanagergui.cpp b/src/libs/installer/packagemanagergui.cpp index f7c500811..b093838a3 100644 --- a/src/libs/installer/packagemanagergui.cpp +++ b/src/libs/installer/packagemanagergui.cpp @@ -1740,6 +1740,17 @@ void IntroductionPage::setMaintainerToolsEnabled(bool enable) && ProductKeyCheck::instance()->hasValidKey()); } +/*! + Resets the internal page state, so that on clicking \uicontrol Next the metadata needs to be + fetched again. +*/ +void IntroductionPage::resetFetchedState() +{ + m_updatesFetched = false; + m_allPackagesFetched = false; + m_forceUpdate = false; +} + // -- public slots /*! @@ -1881,9 +1892,7 @@ void IntroductionPage::initializePage() */ void IntroductionPage::onCoreNetworkSettingsChanged() { - m_updatesFetched = false; - m_allPackagesFetched = false; - m_forceUpdate = false; + resetFetchedState(); PackageManagerCore *core = packageManagerCore(); if (core->isUninstaller() || core->isMaintainer()) { diff --git a/src/libs/installer/packagemanagergui.h b/src/libs/installer/packagemanagergui.h index cce36e4ae..a25329b9c 100644 --- a/src/libs/installer/packagemanagergui.h +++ b/src/libs/installer/packagemanagergui.h @@ -236,6 +236,8 @@ public: void setMaintenanceToolsEnabled(bool enable); void setMaintainerToolsEnabled(bool enable); + void resetFetchedState(); + public Q_SLOTS: void onCoreNetworkSettingsChanged(); void setMessage(const QString &msg); diff --git a/src/libs/installer/qinstallerglobal.cpp b/src/libs/installer/qinstallerglobal.cpp index 8b1b9c6ad..457690892 100644 --- a/src/libs/installer/qinstallerglobal.cpp +++ b/src/libs/installer/qinstallerglobal.cpp @@ -39,6 +39,7 @@ \value ExtractionError \value UserIgnoreError \value RepositoryUpdatesReceived + \value CacheError */ /*! @@ -66,9 +67,10 @@ */ /*! - \typedef QInstaller::LocalPackagesHash + \typedef QInstaller::LocalPackagesMap - Synonym for QHash<QString, KDUpdater::LocalPackage>. + Synonym for QMap<QString, KDUpdater::LocalPackage>. The map key is component name, + and value contains information about the local package. */ /*! @@ -85,7 +87,7 @@ */ /*! - \typedef QInstaller::DependencyHash + \typedef QInstaller::LocalDependencyHash Synonym for QHash<QString, QStringList>. The hash key is component name, which other components depend on. The value can contain several component diff --git a/src/libs/installer/settingsoperation.cpp b/src/libs/installer/settingsoperation.cpp index 4c983b17a..a74161178 100644 --- a/src/libs/installer/settingsoperation.cpp +++ b/src/libs/installer/settingsoperation.cpp @@ -46,6 +46,7 @@ SettingsOperation::SettingsOperation(PackageManagerCore *core) : UpdateOperation(core) { setName(QLatin1String("Settings")); + setRequiresUnreplacedVariables(true); } void SettingsOperation::backup() @@ -104,10 +105,17 @@ bool SettingsOperation::performOperation() if (!checkArguments()) return false; - const QString path = argumentKeyValue(QLatin1String("path")); + QString path = argumentKeyValue(QLatin1String("path")); const QString method = argumentKeyValue(QLatin1String("method")); const QString key = argumentKeyValue(QLatin1String("key")); - const QString aValue = argumentKeyValue(QLatin1String("value")); + QString aValue = argumentKeyValue(QLatin1String("value")); + + if (requiresUnreplacedVariables()) { + if (PackageManagerCore *const core = packageManager()) { + path = core->replaceVariables(path); + aValue = core->replaceVariables(aValue); + } + } // use MkdirOperation to get the path so it can remove it with MkdirOperation::undoOperation later KDUpdater::MkdirOperation mkDirOperation; @@ -152,14 +160,25 @@ bool SettingsOperation::undoOperation() { if (!checkArguments()) return false; - const QString path = argumentKeyValue(QLatin1String("path")); + QString path = argumentKeyValue(QLatin1String("path")); const QString method = argumentKeyValue(QLatin1String("method")); const QString key = argumentKeyValue(QLatin1String("key")); - const QString aValue = argumentKeyValue(QLatin1String("value")); + QString aValue = argumentKeyValue(QLatin1String("value")); if (method.startsWith(QLatin1String("remove"))) return true; + if (requiresUnreplacedVariables()) { + if (PackageManagerCore *const core = packageManager()) { + path = core->replaceVariables(path); + aValue = core->replaceVariables(aValue); + // Check is different settings file is wanted to be used. + // Old settings file name should be set to variable <variable_name>_OLD, + // and new path is then read from <variable_name> variable. + variableReplacement(&path); + } + } + bool cleanUp = false; { // kill the scope to kill settings object, else remove file will not work QSettingsWrapper settings(path, QSettings::IniFormat); diff --git a/src/libs/installer/uninstallercalculator.cpp b/src/libs/installer/uninstallercalculator.cpp index 36d769207..2f029630f 100644 --- a/src/libs/installer/uninstallercalculator.cpp +++ b/src/libs/installer/uninstallercalculator.cpp @@ -30,9 +30,6 @@ #include "component.h" #include "packagemanagercore.h" -#include "globals.h" - -#include <QDebug> namespace QInstaller { @@ -42,13 +39,11 @@ namespace QInstaller { \internal */ -UninstallerCalculator::UninstallerCalculator(const QList<Component *> &installedComponents - , PackageManagerCore *core +UninstallerCalculator::UninstallerCalculator(PackageManagerCore *core , const AutoDependencyHash &autoDependencyComponentHash , const LocalDependencyHash &localDependencyComponentHash , const QStringList &localVirtualComponents) - : m_installedComponents(installedComponents) - , m_core(core) + : m_core(core) , m_autoDependencyComponentHash(autoDependencyComponentHash) , m_localDependencyComponentHash(localDependencyComponentHash) , m_localVirtualComponents(localVirtualComponents) @@ -60,7 +55,7 @@ QSet<Component *> UninstallerCalculator::componentsToUninstall() const return m_componentsToUninstall; } -void UninstallerCalculator::appendComponentToUninstall(Component *component, const bool reverse) +void UninstallerCalculator::appendComponentToUninstall(Component *component) { if (!component) return; @@ -74,29 +69,22 @@ void UninstallerCalculator::appendComponentToUninstall(Component *component, con Component *depComponent = m_core->componentByName(dependencyComponent); if (!depComponent) continue; - if (depComponent->isInstalled() && !m_componentsToUninstall.contains(depComponent)) { - appendComponentToUninstall(depComponent, reverse); + if (depComponent->isInstalled() && !m_componentsToUninstall.contains(depComponent) + && !m_core->orderedComponentsToInstall().contains(depComponent)) { + appendComponentToUninstall(depComponent); insertUninstallReason(depComponent, UninstallerCalculator::Dependent, component->name()); - } else if (reverse && depComponent->isVirtual()) { - // Remove from uninstall only hidden components, user - // can select other dependencies manually - 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, const bool reverse) +void UninstallerCalculator::appendComponentsToUninstall(const QList<Component*> &components) { - if (components.isEmpty()) - return; foreach (Component *component, components) - appendComponentToUninstall(component, reverse); + appendComponentToUninstall(component); + QList<Component*> autoDependOnList; // All regular dependees are resolved. Now we are looking for auto depend on components. for (Component *component : components) { @@ -108,9 +96,7 @@ void UninstallerCalculator::appendComponentsToUninstall(const QList<Component*> 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)) { + if (!m_componentsToUninstall.contains(autoDepComponent)) { insertUninstallReason(autoDepComponent, UninstallerCalculator::AutoDependent, component->name()); autoDepComponent->setInstallAction(ComponentModelHelper::AutodependUninstallation); autoDependOnList.append(autoDepComponent); @@ -118,15 +104,11 @@ void UninstallerCalculator::appendComponentsToUninstall(const QList<Component*> } } } + if (!autoDependOnList.isEmpty()) - appendComponentsToUninstall(autoDependOnList, reverse); + appendComponentsToUninstall(autoDependOnList); else - appendVirtualComponentsToUninstall(reverse); -} - -void UninstallerCalculator::removeComponentsFromUnInstall(const QList<Component*> &components) -{ - appendComponentsToUninstall(components, true); + appendVirtualComponentsToUninstall(); } void UninstallerCalculator::insertUninstallReason(Component *component, const UninstallReasonType uninstallReason, @@ -172,38 +154,30 @@ QString UninstallerCalculator::uninstallReasonReferencedComponent(Component *com return m_toUninstallComponentIdReasonHash.value(component->name()).second; } -void UninstallerCalculator::appendVirtualComponentsToUninstall(const bool reverse) +void UninstallerCalculator::appendVirtualComponentsToUninstall() { QList<Component*> unneededVirtualList; - // Check for virtual components without dependees - if (reverse) { - for (Component *reverseFromUninstall : qAsConst(m_virtualComponentsForReverse)) { - if (m_componentsToUninstall.contains(reverseFromUninstall) && (isRequiredVirtualPackage(reverseFromUninstall))) - unneededVirtualList.append(reverseFromUninstall); - } - } else { - for (const QString &componentName : qAsConst(m_localVirtualComponents)) { - Component *virtualComponent = m_core->componentByName(componentName, m_core->components(PackageManagerCore::ComponentType::All)); - if (!virtualComponent) - continue; + for (const QString &componentName : qAsConst(m_localVirtualComponents)) { + Component *virtualComponent = m_core->componentByName(componentName, m_core->components(PackageManagerCore::ComponentType::All)); + if (!virtualComponent) + continue; - if (virtualComponent->isInstalled() && !m_componentsToUninstall.contains(virtualComponent)) { - // Components with auto dependencies were handled in the previous step - if (!virtualComponent->autoDependencies().isEmpty() || virtualComponent->forcedInstallation()) - continue; + if (virtualComponent->isInstalled() && !m_componentsToUninstall.contains(virtualComponent)) { + // Components with auto dependencies were handled in the previous step + if (!virtualComponent->autoDependencies().isEmpty() || virtualComponent->forcedInstallation()) + continue; - if (!isRequiredVirtualPackage(virtualComponent)) { - unneededVirtualList.append(virtualComponent); - m_virtualComponentsForReverse.append(virtualComponent); - insertUninstallReason(virtualComponent, UninstallerCalculator::VirtualDependent); - } - } + if (!isRequiredVirtualPackage(virtualComponent)) { + unneededVirtualList.append(virtualComponent); + m_virtualComponentsForReverse.append(virtualComponent); + insertUninstallReason(virtualComponent, UninstallerCalculator::VirtualDependent); + } } } if (!unneededVirtualList.isEmpty()) - appendComponentsToUninstall(unneededVirtualList, reverse); + appendComponentsToUninstall(unneededVirtualList); } bool UninstallerCalculator::isRequiredVirtualPackage(Component *component) diff --git a/src/libs/installer/uninstallercalculator.h b/src/libs/installer/uninstallercalculator.h index 57f2f926a..74207e005 100644 --- a/src/libs/installer/uninstallercalculator.h +++ b/src/libs/installer/uninstallercalculator.h @@ -53,28 +53,26 @@ public: AutoDependent // "Removed as autodependency component is removed" }; - UninstallerCalculator(const QList<Component *> &installedComponents, PackageManagerCore *core, + UninstallerCalculator(PackageManagerCore *core, const AutoDependencyHash &autoDependencyComponentHash, const LocalDependencyHash &localDependencyComponentHash, const QStringList &localVirtualComponents); QSet<Component*> componentsToUninstall() const; - void appendComponentsToUninstall(const QList<Component*> &components, const bool reverse = false); - void removeComponentsFromUnInstall(const QList<Component*> &components); + void appendComponentsToUninstall(const QList<Component*> &components); void insertUninstallReason(Component *component, const UninstallReasonType uninstallReason, const QString &referencedComponentName = QString()); QString uninstallReason(Component *component) const; UninstallerCalculator::UninstallReasonType uninstallReasonType(Component *c) const; - QString uninstallReasonReferencedComponent(Component *component) const; - bool isRequiredVirtualPackage(Component *component); - void appendVirtualComponentsToUninstall(const bool reverse); private: - void appendComponentToUninstall(Component *component, const bool reverse); + QString uninstallReasonReferencedComponent(Component *component) const; + bool isRequiredVirtualPackage(Component *component); + void appendComponentToUninstall(Component *component); + void appendVirtualComponentsToUninstall(); - QList<Component *> m_installedComponents; QSet<Component *> m_componentsToUninstall; PackageManagerCore *m_core; QHash<QString, QPair<UninstallReasonType, QString> > m_toUninstallComponentIdReasonHash; diff --git a/src/libs/kdtools/localpackagehub.cpp b/src/libs/kdtools/localpackagehub.cpp index bd6d879e3..c7b627c51 100644 --- a/src/libs/kdtools/localpackagehub.cpp +++ b/src/libs/kdtools/localpackagehub.cpp @@ -313,6 +313,7 @@ void LocalPackageHub::refresh() \a title, \a treeName, \a description, + \a sortingPriority, \a dependencies, \a autoDependencies, \a forcedInstallation, diff --git a/src/libs/kdtools/updateoperation.cpp b/src/libs/kdtools/updateoperation.cpp index 35629809e..b35751096 100644 --- a/src/libs/kdtools/updateoperation.cpp +++ b/src/libs/kdtools/updateoperation.cpp @@ -321,6 +321,39 @@ void UpdateOperation::setRequiresUnreplacedVariables(bool isRequired) m_requiresUnreplacedVariables = isRequired; } +/*! + Replaces installer \c value \a variableValue with predefined variable. + If \c key is found for the \a variableValue and the \c key ends with string _OLD, + the initial \a variableValue is replaced with the \c value having a key + without _OLD ending. This way we can replace the hard coded values defined for operations, + if the value has for some reason changed. For example if we set following variables + in install script: + \badcode + installer.setValue("MY_OWN_EXECUTABLE", "C:/Qt/NewLocation/Tools.exe") + installer.setValue("MY_OWN_EXECUTABLE_OLD", "C:/Qt/OldLocation/Tools.exe") + \endcode + and we have moved the Tools.exe from OldLocation to NewLocation, the operation + continues to work and use the Tools.exe from NewLocation although original + installation has been made with Tools.exe in OldLocation. + Returns \c true if \a variableValue is replaced. +*/ +bool UpdateOperation::variableReplacement(QString *variableValue) +{ + bool variableValueChanged = false; + const QString valueNormalized = QDir::cleanPath(*variableValue); + QString key = m_core->key(valueNormalized); + if (key.endsWith(QLatin1String("_OLD"))) { + key.chop(4); + if (m_core->containsValue(key)) { + key.prepend(QLatin1String("@")); + key.append(QLatin1String("@")); + *variableValue = m_core->replaceVariables(key); + variableValueChanged = true; + } + } + return variableValueChanged; +} + struct StartsWith { explicit StartsWith(const QString &searchTerm) diff --git a/src/libs/kdtools/updateoperation.h b/src/libs/kdtools/updateoperation.h index a39c25747..4a431f107 100644 --- a/src/libs/kdtools/updateoperation.h +++ b/src/libs/kdtools/updateoperation.h @@ -114,6 +114,7 @@ protected: QStringList parsePerformOperationArguments(); QStringList parseUndoOperationArguments(); void setRequiresUnreplacedVariables(bool isRequired); + bool variableReplacement(QString *variableValue); private: QString m_name; diff --git a/src/sdk/tabcontroller.cpp b/src/sdk/tabcontroller.cpp index c40ceed5f..089bd8468 100644 --- a/src/sdk/tabcontroller.cpp +++ b/src/sdk/tabcontroller.cpp @@ -130,7 +130,7 @@ int TabController::init() if (page) { page->setMessage(QString()); page->setErrorMessage(QString()); - page->onCoreNetworkSettingsChanged(); + page->resetFetchedState(); } d->m_gui->restart(); diff --git a/tests/auto/installer/commandlineupdate/data/installPackagesRepository/Updates.xml b/tests/auto/installer/commandlineupdate/data/installPackagesRepository/Updates.xml index 9e01f1800..de6e66525 100644 --- a/tests/auto/installer/commandlineupdate/data/installPackagesRepository/Updates.xml +++ b/tests/auto/installer/commandlineupdate/data/installPackagesRepository/Updates.xml @@ -160,4 +160,20 @@ <ForcedUpdate>true</ForcedUpdate> <DownloadableArchives>content.7z</DownloadableArchives> </PackageUpdate> + <PackageUpdate> + <Name>qt.tools.qtcreator</Name> + <DisplayName>Component qtcreator. Depends on virtual component</DisplayName> + <Version>1.0.0</Version> + <ReleaseDate>2014-08-25</ReleaseDate> + <Dependencies>qt.tools.qtcreator.enterprise.plugins</Dependencies> + <DownloadableArchives>content.7z</DownloadableArchives> + </PackageUpdate> + <PackageUpdate> + <Name>qt.tools.qtcreator.enterprise.plugins</Name> + <DisplayName>enterprise plugin component</DisplayName> + <Version>1.0.0</Version> + <ReleaseDate>2014-08-25</ReleaseDate> + <DownloadableArchives>content.7z</DownloadableArchives> + <Virtual>true</Virtual> + </PackageUpdate> </Updates> diff --git a/tests/auto/installer/commandlineupdate/data/installPackagesRepository/qt.tools.qtcreator.enterprise.plugins/1.0.0content.7z b/tests/auto/installer/commandlineupdate/data/installPackagesRepository/qt.tools.qtcreator.enterprise.plugins/1.0.0content.7z Binary files differnew file mode 100644 index 000000000..5c7c4f37d --- /dev/null +++ b/tests/auto/installer/commandlineupdate/data/installPackagesRepository/qt.tools.qtcreator.enterprise.plugins/1.0.0content.7z diff --git a/tests/auto/installer/commandlineupdate/data/installPackagesRepository/qt.tools.qtcreator/1.0.0content.7z b/tests/auto/installer/commandlineupdate/data/installPackagesRepository/qt.tools.qtcreator/1.0.0content.7z Binary files differnew file mode 100644 index 000000000..e5e4178a4 --- /dev/null +++ b/tests/auto/installer/commandlineupdate/data/installPackagesRepository/qt.tools.qtcreator/1.0.0content.7z diff --git a/tests/auto/installer/commandlineupdate/data/repositoryUpdateWithReplacements/Updates.xml b/tests/auto/installer/commandlineupdate/data/repositoryUpdateWithReplacements/Updates.xml new file mode 100644 index 000000000..f55998c48 --- /dev/null +++ b/tests/auto/installer/commandlineupdate/data/repositoryUpdateWithReplacements/Updates.xml @@ -0,0 +1,32 @@ +<Updates> + <ApplicationName>{AnyApplication}</ApplicationName> + <ApplicationVersion>1.0.0</ApplicationVersion> + <Checksum>false</Checksum> + <PackageUpdate> + <Name>qt.tools.qtcreator</Name> + <DisplayName>Component qtcreator. Dependency removed</DisplayName> + <Version>2.0.0</Version> + <ReleaseDate>2014-08-25</ReleaseDate> + <DownloadableArchives>content.7z</DownloadableArchives> + </PackageUpdate> + <PackageUpdate> + <Name>qt.tools.qtcreator_gui</Name> + <DisplayName>Component K. Autodepends on componentJ</DisplayName> + <Description>Component K. Autodepends on componentJ</Description> + <Version>2.0.0</Version> + <ReleaseDate>2014-08-25</ReleaseDate> + <AutoDependOn>qt.tools.qtcreator</AutoDependOn> + <DownloadableArchives>content.7z</DownloadableArchives> + </PackageUpdate> + <PackageUpdate> + <Name>qt.tools.qtcreator_gui.enterprise.plugins</Name> + <DisplayName>enterprise plugins, replaces another component</DisplayName> + <Version>2.0.0</Version> + <ReleaseDate>2018-03-14</ReleaseDate> + <Virtual>true</Virtual> + <Replaces>qt.tools.qtcreator.enterprise.plugins</Replaces> + <AutoDependOn>qt.tools.qtcreator</AutoDependOn> + <UpdateFile UncompressedSize="99" OS="Any" CompressedSize="305"/> + <DownloadableArchives>content.7z</DownloadableArchives> + </PackageUpdate> +</Updates> diff --git a/tests/auto/installer/commandlineupdate/data/repositoryUpdateWithReplacements/qt.tools.qtcreator/2.0.0content.7z b/tests/auto/installer/commandlineupdate/data/repositoryUpdateWithReplacements/qt.tools.qtcreator/2.0.0content.7z Binary files differnew file mode 100644 index 000000000..f2b69fc13 --- /dev/null +++ b/tests/auto/installer/commandlineupdate/data/repositoryUpdateWithReplacements/qt.tools.qtcreator/2.0.0content.7z diff --git a/tests/auto/installer/commandlineupdate/data/repositoryUpdateWithReplacements/qt.tools.qtcreator_gui.enterprise.plugins/2.0.0content.7z b/tests/auto/installer/commandlineupdate/data/repositoryUpdateWithReplacements/qt.tools.qtcreator_gui.enterprise.plugins/2.0.0content.7z Binary files differnew file mode 100644 index 000000000..03d191cb5 --- /dev/null +++ b/tests/auto/installer/commandlineupdate/data/repositoryUpdateWithReplacements/qt.tools.qtcreator_gui.enterprise.plugins/2.0.0content.7z diff --git a/tests/auto/installer/commandlineupdate/data/repositoryUpdateWithReplacements/qt.tools.qtcreator_gui/2.0.0content.7z b/tests/auto/installer/commandlineupdate/data/repositoryUpdateWithReplacements/qt.tools.qtcreator_gui/2.0.0content.7z Binary files differnew file mode 100644 index 000000000..515c3a5cf --- /dev/null +++ b/tests/auto/installer/commandlineupdate/data/repositoryUpdateWithReplacements/qt.tools.qtcreator_gui/2.0.0content.7z diff --git a/tests/auto/installer/commandlineupdate/settings.qrc b/tests/auto/installer/commandlineupdate/settings.qrc index d398abe30..c8f328d6b 100644 --- a/tests/auto/installer/commandlineupdate/settings.qrc +++ b/tests/auto/installer/commandlineupdate/settings.qrc @@ -17,6 +17,8 @@ <file>data/installPackagesRepository/componentF.subcomponent1.subsubcomponent2/1.0.0content.7z</file> <file>data/installPackagesRepository/componentF.subcomponent2.subsubcomponent1/1.0.0content.7z</file> <file>data/installPackagesRepository/componentF.subcomponent2.subsubcomponent2/1.0.0content.7z</file> + <file>data/installPackagesRepository/qt.tools.qtcreator/1.0.0content.7z</file> + <file>data/installPackagesRepository/qt.tools.qtcreator.enterprise.plugins/1.0.0content.7z</file> <file>data/installPackagesRepositoryUpdate/Updates.xml</file> <file>data/installPackagesRepositoryUpdate/componentA/1.0.0content.7z</file> <file>data/installPackagesRepositoryUpdate/componentB/2.0.0content.7z</file> @@ -52,5 +54,9 @@ <file>data/repositoryWithDependencyToEssential/Updates.xml</file> <file>data/repositoryWithDependencyToEssential/componentAutoDependOnA/1.0content.7z</file> <file>data/repositoryWithDependencyToEssential/componentA/3.0.0content.7z</file> + <file>data/repositoryUpdateWithReplacements/Updates.xml</file> + <file>data/repositoryUpdateWithReplacements/qt.tools.qtcreator/2.0.0content.7z</file> + <file>data/repositoryUpdateWithReplacements/qt.tools.qtcreator_gui/2.0.0content.7z</file> + <file>data/repositoryUpdateWithReplacements/qt.tools.qtcreator_gui.enterprise.plugins/2.0.0content.7z</file> </qresource> </RCC> diff --git a/tests/auto/installer/commandlineupdate/tst_commandlineupdate.cpp b/tests/auto/installer/commandlineupdate/tst_commandlineupdate.cpp index 338157cba..0faeecf74 100644 --- a/tests/auto/installer/commandlineupdate/tst_commandlineupdate.cpp +++ b/tests/auto/installer/commandlineupdate/tst_commandlineupdate.cpp @@ -295,7 +295,7 @@ private slots: componentResourcesAfterUpdate.append(ComponentResource("componentA", "1.0.0content.txt")); componentResourcesAfterUpdate.append(ComponentResource("componentB", "2.0.0content.txt")); componentResourcesAfterUpdate.append(ComponentResource("componentD", "2.0.0content.txt"));//AutodepenOn componentA,componentB - componentResourcesAfterUpdate.append(ComponentResource("componentE", "2.0.0content.txt"));//ForcedInstall + componentResourcesAfterUpdate.append(ComponentResource("componentE", "1.0.0content.txt"));//ForcedInstall, not updated without user selection componentResourcesAfterUpdate.append(ComponentResource("componentG", "1.0.0content.txt")); deletedComponentResources.clear(); @@ -316,8 +316,38 @@ private slots: << componentResourcesAfterUpdate << (QStringList() << "components.xml" << "installcontent.txt" << "installcontentA.txt" << "installcontentD_update.txt" << "installcontentB_update.txt" - << "installcontentE_update.txt" << "installcontentG.txt") + << "installcontentE.txt" << "installcontentG.txt") << deletedComponentResources; + + /*********** Update packages with replacements **********/ + componentResources.clear(); + componentResources.append(ComponentResource("qt.tools.qtcreator", "1.0.0content.txt")); + componentResources.append(ComponentResource("qt.tools.qtcreator.enterprise.plugins", "1.0.0content.txt")); + componentResources.append(ComponentResource("componentE", "1.0.0content.txt")); + + componentResourcesAfterUpdate.clear(); + componentResourcesAfterUpdate.append(ComponentResource("qt.tools.qtcreator", "2.0.0content.txt")); + componentResourcesAfterUpdate.append(ComponentResource("qt.tools.qtcreator_gui", "2.0.0content.txt")); + componentResourcesAfterUpdate.append(ComponentResource("qt.tools.qtcreator_gui.enterprise.plugins", "2.0.0content.txt")); + componentResourcesAfterUpdate.append(ComponentResource("componentE", "1.0.0content.txt")); + + deletedComponentResources.clear(); + deletedComponentResources.append(ComponentResource("qt.tools.qtcreator.enterprise.plugins", "1.0.0content.txt")); + + QTest::newRow("Update packages with replacements") + << ":///data/installPackagesRepository" + << (QStringList()<< "qt.tools.qtcreator") + << PackageManagerCore::Success + << componentResources + << (QStringList() << "components.xml" << "installcontentA.txt" << "installcontentE.txt" << "installcontentG.txt" + << "installcontent.txt" << "qtcreator.txt" << "plugins.txt") + << ":///data/repositoryUpdateWithReplacements" + << (QStringList() << "qt.tools.qtcreator") + << PackageManagerCore::Success + << componentResourcesAfterUpdate + << (QStringList() << "components.xml" << "installcontentA.txt" << "installcontentE.txt" << "installcontentG.txt" + << "installcontent.txt" << "gui.txt" << "qtcreator2.txt" << "gui_plugins.txt") + << deletedComponentResources; } void testUpdate() diff --git a/tests/auto/installer/extractarchiveoperationtest/data.qrc b/tests/auto/installer/extractarchiveoperationtest/data.qrc index a8cc1a892..87f648568 100644 --- a/tests/auto/installer/extractarchiveoperationtest/data.qrc +++ b/tests/auto/installer/extractarchiveoperationtest/data.qrc @@ -11,5 +11,8 @@ <file>data/xmloperationrepository/A/1.0.0content4.zip</file> <file>data/xmloperationrepository/A/1.0.0anothercontent.7z</file> <file>data/xmloperationrepository/A/1.0.0default.7z</file> + <file>data/installerbaserepository/Updates.xml</file> + <file>data/installerbaserepository/A/1.0.0content.7z</file> + <file>data/installerbaserepository/A/1.0.0installerbase.7z</file> </qresource> </RCC> diff --git a/tests/auto/installer/extractarchiveoperationtest/data/installerbaserepository/A/1.0.0content.7z b/tests/auto/installer/extractarchiveoperationtest/data/installerbaserepository/A/1.0.0content.7z Binary files differnew file mode 100644 index 000000000..585f58296 --- /dev/null +++ b/tests/auto/installer/extractarchiveoperationtest/data/installerbaserepository/A/1.0.0content.7z diff --git a/tests/auto/installer/extractarchiveoperationtest/data/installerbaserepository/A/1.0.0installerbase.7z b/tests/auto/installer/extractarchiveoperationtest/data/installerbaserepository/A/1.0.0installerbase.7z Binary files differnew file mode 100644 index 000000000..c3b6aec9c --- /dev/null +++ b/tests/auto/installer/extractarchiveoperationtest/data/installerbaserepository/A/1.0.0installerbase.7z diff --git a/tests/auto/installer/extractarchiveoperationtest/data/installerbaserepository/Updates.xml b/tests/auto/installer/extractarchiveoperationtest/data/installerbaserepository/Updates.xml new file mode 100644 index 000000000..887300c97 --- /dev/null +++ b/tests/auto/installer/extractarchiveoperationtest/data/installerbaserepository/Updates.xml @@ -0,0 +1,13 @@ +<Updates> + <ApplicationName>{AnyApplication}</ApplicationName> + <ApplicationVersion>1.0.0</ApplicationVersion> + <PackageUpdate> + <Name>A</Name> + <DisplayName>InstallerBase</DisplayName> + <Description>Example component InstallerBase</Description> + <Version>1.0.0</Version> + <ReleaseDate>2015-01-01</ReleaseDate> + <Default>true</Default> + <DownloadableArchives>installerbase.7z,content.7z</DownloadableArchives> + </PackageUpdate> +</Updates> diff --git a/tests/auto/installer/extractarchiveoperationtest/tst_extractarchiveoperationtest.cpp b/tests/auto/installer/extractarchiveoperationtest/tst_extractarchiveoperationtest.cpp index 89492bd94..a8a65e983 100644 --- a/tests/auto/installer/extractarchiveoperationtest/tst_extractarchiveoperationtest.cpp +++ b/tests/auto/installer/extractarchiveoperationtest/tst_extractarchiveoperationtest.cpp @@ -177,6 +177,39 @@ private slots: QVERIFY(dir.removeRecursively()); } + void testInstallerBaseBinaryExtraction() + { + m_testDirectory = QInstaller::generateTemporaryFileName(); + QVERIFY(QDir().mkpath(m_testDirectory)); + QVERIFY(QDir(m_testDirectory).exists()); + + QScopedPointer<PackageManagerCore> core(PackageManager::getPackageManagerWithInit + (m_testDirectory, ":///data/installerbaserepository")); + core->setInstallerBaseBinary(m_testDirectory + QDir::separator() + "testInstallerBase.txt"); + core->installDefaultComponentsSilently(); + + QFile contentResourceFile(m_testDirectory + QDir::separator() + "installerResources" + QDir::separator() + "A" + QDir::separator() + "1.0.0content.txt"); + QVERIFY2(contentResourceFile.open(QIODevice::ReadOnly | QIODevice::Text), "Could not open content resource file for reading."); + QTextStream fileStream(&contentResourceFile); + QString line = fileStream.readLine(); + QVERIFY2(!line.isEmpty(), "Content not written to resource file."); + contentResourceFile.close(); + + QFile installerBaseResourceFile(m_testDirectory + QDir::separator() + "installerResources" + QDir::separator() + "A" + QDir::separator() + "1.0.0installerbase.txt"); + QVERIFY2(installerBaseResourceFile.open(QIODevice::ReadOnly | QIODevice::Text), "Could not open installerbase resource file for reading."); + QTextStream fileStream2(&installerBaseResourceFile); + line = installerBaseResourceFile.readLine(); + QVERIFY2(line.isEmpty(), "Installerbase falsly written to resource file."); + installerBaseResourceFile.close(); + + core->setPackageManager(); + core->commitSessionOperations(); + + QCOMPARE(PackageManagerCore::Success, core->uninstallComponentsSilently(QStringList() << "A")); + QDir dir(m_testDirectory); + QVERIFY(dir.removeRecursively()); + } + private: QString m_testDirectory; }; diff --git a/tests/auto/installer/settingsoperation/data/repository/B/1.0.0meta.7z b/tests/auto/installer/settingsoperation/data/repository/B/1.0.0meta.7z Binary files differnew file mode 100644 index 000000000..211bc0e0a --- /dev/null +++ b/tests/auto/installer/settingsoperation/data/repository/B/1.0.0meta.7z diff --git a/tests/auto/installer/settingsoperation/data/repository/C/1.0.0meta.7z b/tests/auto/installer/settingsoperation/data/repository/C/1.0.0meta.7z Binary files differnew file mode 100644 index 000000000..a990fdbf1 --- /dev/null +++ b/tests/auto/installer/settingsoperation/data/repository/C/1.0.0meta.7z diff --git a/tests/auto/installer/settingsoperation/data/repository/Updates.xml b/tests/auto/installer/settingsoperation/data/repository/Updates.xml index eddb160c0..3e88f849d 100644 --- a/tests/auto/installer/settingsoperation/data/repository/Updates.xml +++ b/tests/auto/installer/settingsoperation/data/repository/Updates.xml @@ -8,8 +8,26 @@ <Description>Example component A</Description> <Version>1.0.2-1</Version> <ReleaseDate>2015-01-01</ReleaseDate> - <Default>true</Default> + <Default>false</Default> <Script>script.qs</Script> <SHA1>5dc8a98f591998de0c555e194e228fa740a15632</SHA1> </PackageUpdate> + <PackageUpdate> + <Name>B</Name> + <DisplayName>B</DisplayName> + <Description>Example component B</Description> + <Version>1.0.0</Version> + <ReleaseDate>2015-01-01</ReleaseDate> + <Default>false</Default> + <Script>script.qs</Script> + </PackageUpdate> + <PackageUpdate> + <Name>C</Name> + <DisplayName>C</DisplayName> + <Description>Example component C</Description> + <Version>1.0.0</Version> + <ReleaseDate>2015-01-01</ReleaseDate> + <Default>false</Default> + <Script>script.qs</Script> + </PackageUpdate> </Updates> diff --git a/tests/auto/installer/settingsoperation/settings.qrc b/tests/auto/installer/settingsoperation/settings.qrc index d030220ab..8da639ad6 100644 --- a/tests/auto/installer/settingsoperation/settings.qrc +++ b/tests/auto/installer/settingsoperation/settings.qrc @@ -2,5 +2,7 @@ <qresource prefix="/"> <file>data/repository/Updates.xml</file> <file>data/repository/A/1.0.2-1meta.7z</file> + <file>data/repository/B/1.0.0meta.7z</file> + <file>data/repository/C/1.0.0meta.7z</file> </qresource> </RCC> diff --git a/tests/auto/installer/settingsoperation/tst_settingsoperation.cpp b/tests/auto/installer/settingsoperation/tst_settingsoperation.cpp index 9b08d4f6f..2da0870c7 100644 --- a/tests/auto/installer/settingsoperation/tst_settingsoperation.cpp +++ b/tests/auto/installer/settingsoperation/tst_settingsoperation.cpp @@ -177,6 +177,7 @@ private slots: QFile testFile(testFilePath); QVERIFY2(testFile.open(QIODevice::WriteOnly | QIODevice::Text), contentFile.errorString().toLatin1()); + m_cleanupFilePaths << testFilePath; QTextStream out(&testFile); @@ -244,6 +245,7 @@ private slots: QFile testFile(testFilePath); QVERIFY2(testFile.open(QIODevice::WriteOnly | QIODevice::Text), contentFile.errorString() .toLatin1()); + m_cleanupFilePaths << testFilePath; QTextStream out(&testFile); @@ -293,7 +295,7 @@ private slots: QCOMPARE(testSettings.value("testcategory/categoryarrayvalue1").toStringList(), QStringList() << "value1" << "value2" << "value3"); - core->installDefaultComponentsSilently(); + core->installSelectedComponentsSilently(QStringList() << "A"); QCOMPARE(testSettings.value("testcategory/categoryarrayvalue1").toStringList(), QStringList() << "value1" << "value2" << "value3" << "valueFromScript"); @@ -309,6 +311,68 @@ private slots: core->deleteLater(); } + void testPerformingFromCLIWithPlaceholders() + { + QString installDir = QInstaller::generateTemporaryFileName(); + QVERIFY(QDir().mkpath(installDir)); + PackageManagerCore *core = PackageManager::getPackageManagerWithInit + (installDir, ":///data/repository"); + + core->installSelectedComponentsSilently(QStringList() << "B"); + // Path is set in component constructor in install script + const QString settingsFile = core->value("SettingsPathFromVariable"); + QSettings testSettings(QDir(m_testSettingsDirPath).filePath(settingsFile), + QSettings::IniFormat); + + QCOMPARE(testSettings.value("testcategory/categoryarrayvalue1").toStringList(), + QStringList() << "ValueFromPlaceholder"); + + core->commitSessionOperations(); + core->setPackageManager(); + core->uninstallComponentsSilently(QStringList() << "B"); + + // Settings file is removed as it is empty + QVERIFY(!QFile::exists(settingsFile)); + QDir dir(installDir); + QVERIFY(dir.removeRecursively()); + core->deleteLater(); + } + + void testPerformingFromCLIWithSettingsFileMoved() + { + QString installDir = QInstaller::generateTemporaryFileName(); + QVERIFY(QDir().mkpath(installDir)); + PackageManagerCore *core = PackageManager::getPackageManagerWithInit + (installDir, ":///data/repository"); + + + core->installSelectedComponentsSilently(QStringList() << "C"); + + const QString settingsFileName = qApp->applicationDirPath() + "/oldTestSettings.ini"; + QSettings testSettings(QDir(m_testSettingsDirPath).filePath(settingsFileName), + QSettings::IniFormat); + + QCOMPARE(testSettings.value("testcategory/categoryarrayvalue1").toStringList(), + QStringList() << "valueFromScript"); + + QFile settingsFile(settingsFileName); + // Move the settings path to new location. Script has set values + // ComponentCSettingsPath (@InstallerDirPath@/newTestSettings.ini) and + // ComponentCSettingsPath_OLD (@InstallerDirPath@/oldTestSettings.ini) so + // UNDO operation should delete the moved newTestSettings.ini file instead. + QVERIFY2(settingsFile.rename(qApp->applicationDirPath() + "/newTestSettings.ini"), "Could not move settings file."); + core->commitSessionOperations(); + core->setPackageManager(); + core->uninstallComponentsSilently(QStringList() << "C"); + + // Settings file is removed in uninstall as it is empty + QVERIFY2(!QFile::exists(qApp->applicationDirPath() + "/newTestSettings.ini"), "Settings file not deleted correctly"); + + QDir dir(installDir); + QVERIFY(dir.removeRecursively()); + core->deleteLater(); + } + // called after all tests void cleanupTestCase() { diff --git a/tests/auto/installer/solver/tst_solver.cpp b/tests/auto/installer/solver/tst_solver.cpp index bdfc4d3df..ee05cc7f4 100644 --- a/tests/auto/installer/solver/tst_solver.cpp +++ b/tests/auto/installer/solver/tst_solver.cpp @@ -248,7 +248,6 @@ private slots: { QTest::addColumn<PackageManagerCore *>("core"); QTest::addColumn<QList<Component *> >("selectedToUninstall"); - QTest::addColumn<QList<Component *> >("installedComponents"); QTest::addColumn<QSet<Component *> >("expectedResult"); QTest::addColumn<UninstallReasonList >("uninstallReasons"); QTest::addColumn<LocalDependencyHash >("dependencyHash"); @@ -277,7 +276,6 @@ private slots: uninstallReasonList.append(qMakePair(componentB, UninstallerCalculator::Dependent)); QTest::newRow("Uninstaller resolved") << core << (QList<Component *>() << componentAB) - << (QList<Component *>() << componentA << componentB) << (QSet<Component *>() << componentAB << componentB) << uninstallReasonList << dependencyComponentHash; @@ -303,7 +301,6 @@ private slots: uninstallReasonList.append(qMakePair(compB, UninstallerCalculator::Dependent)); QTest::newRow("Cascade dependencies") << core << (QList<Component *>() << compA) - << (QList<Component *>() << compB) << (QSet<Component *>() << compA << compB) << (uninstallReasonList) << dependencyComponentHash; @@ -313,12 +310,11 @@ private slots: { QFETCH(PackageManagerCore *, core); QFETCH(QList<Component *> , selectedToUninstall); - QFETCH(QList<Component *> , installedComponents); QFETCH(QSet<Component *> , expectedResult); QFETCH(UninstallReasonList, uninstallReasons); QFETCH(LocalDependencyHash, dependencyHash); - UninstallerCalculator calc(installedComponents, core, QHash<QString, QStringList>(), dependencyHash, QStringList()); + UninstallerCalculator calc(core, QHash<QString, QStringList>(), dependencyHash, QStringList()); calc.appendComponentsToUninstall(selectedToUninstall); QSet<Component *> result = calc.componentsToUninstall(); for (auto pair : uninstallReasons) { |