summaryrefslogtreecommitdiffstats
path: root/src/libs/installer/packagemanagercore.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/installer/packagemanagercore.cpp')
-rw-r--r--src/libs/installer/packagemanagercore.cpp1451
1 files changed, 939 insertions, 512 deletions
diff --git a/src/libs/installer/packagemanagercore.cpp b/src/libs/installer/packagemanagercore.cpp
index c96083f5d..5c6f481f9 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) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -31,6 +31,7 @@
#include "adminauthorization.h"
#include "binarycontent.h"
#include "component.h"
+#include "componentalias.h"
#include "componentmodel.h"
#include "downloadarchivesjob.h"
#include "errors.h"
@@ -46,6 +47,7 @@
#include "installercalculator.h"
#include "uninstallercalculator.h"
#include "loggingutils.h"
+#include "componentsortfilterproxymodel.h"
#include <productkeycheck.h>
@@ -54,12 +56,17 @@
#include <QtConcurrentRun>
#include <QtCore/QMutex>
-#include <QtCore/QRegExp>
#include <QtCore/QSettings>
#include <QtCore/QTemporaryFile>
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+#include <QtCore5Compat/QTextCodec>
+#include <QtCore5Compat/QTextDecoder>
+#include <QtCore5Compat/QTextEncoder>
+#else
#include <QtCore/QTextCodec>
#include <QtCore/QTextDecoder>
#include <QtCore/QTextEncoder>
+#endif
#include <QtCore/QTextStream>
#include <QDesktopServices>
@@ -176,25 +183,6 @@ using namespace QInstaller;
Emitted when the new root component \a comp is added.
\sa {installer::componentAdded}{installer.componentAdded}
- \sa rootComponentsAdded(), updaterComponentsAdded()
-*/
-
-/*!
- \fn QInstaller::PackageManagerCore::rootComponentsAdded(QList<QInstaller::Component*> components)
-
- Emitted when the list of root components specified by \a components is added.
-
- \sa {installer::rootComponentsAdded}{installer.rootComponentsAdded}
- \sa componentAdded(), updaterComponentsAdded()
-*/
-
-/*!
- \fn QInstaller::PackageManagerCore::updaterComponentsAdded(QList<QInstaller::Component*> components)
-
- Emitted when a new list of updater components specified by \a components is added.
-
- \sa {installer::updaterComponentsAdded}{installer.updaterComponentsAdded}
- \sa componentAdded(), rootComponentsAdded()
*/
/*!
@@ -214,7 +202,7 @@ using namespace QInstaller;
*/
/*!
- \fn QInstaller::PackageManagerCore::defaultTranslationsLoadedForLanguage(QLocale::Language lang)
+ \fn QInstaller::PackageManagerCore::defaultTranslationsLoadedForLanguage(QLocale lang)
Emitted when the language \a lang has changed.
@@ -532,7 +520,6 @@ void PackageManagerCore::reset()
d->m_status = PackageManagerCore::Unfinished;
d->m_installerBaseBinaryUnreplaced.clear();
d->m_coreCheckedHash.clear();
- d->m_componentsToInstallCalculated = false;
}
/*!
@@ -574,85 +561,113 @@ void PackageManagerCore::cancelMetaInfoJob()
}
/*!
- \sa {installer::componentsToInstallNeedsRecalculation}{installer.componentsToInstallNeedsRecalculation}
- */
-void PackageManagerCore::componentsToInstallNeedsRecalculation()
+ Resets the cache used to store downloaded metadata, if one was previously
+ initialized. If \a init is set to \c true, the cache is reinitialized for
+ the path configured in installer's settings.
+
+ Returns \c true on success, \c false otherwise.
+*/
+bool PackageManagerCore::resetLocalCache(bool init)
{
- d->clearInstallerCalculator();
+ return d->m_metadataJob.resetCache(init);
+}
- QList<Component*> selectedComponentsToInstall = componentsMarkedForInstallation();
+/*!
+ Clears the contents of the cache used to store downloaded metadata.
+ Returns \c true on success, \c false otherwise. An error string can
+ be retrieved with \a error.
+*/
+bool PackageManagerCore::clearLocalCache(QString *error)
+{
+ if (d->m_metadataJob.clearCache())
+ return true;
- d->m_componentsToInstallCalculated =
- d->installerCalculator()->appendComponentsToInstall(selectedComponentsToInstall, true);
+ if (error)
+ *error = d->m_metadataJob.errorString();
- d->calculateUninstallComponents();
- d->updateComponentCheckedState();
+ return false;
+}
- // update all nodes uncompressed size
- foreach (Component *const component, components(ComponentType::Root))
- component->updateUncompressedSize(); // this is a recursive call
+/*!
+ Returns \c true if the metadata cache is initialized and valid, \c false otherwise.
+*/
+bool PackageManagerCore::isValidCache() const
+{
+ return d->m_metadataJob.isValidCache();
}
/*!
- 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.
+ \internal
*/
-void PackageManagerCore::calculateUserSelectedComponentsToInstall(const QList<QModelIndex> &indexes)
-{
- QList<Component*> componentsToInstall;
- QList<Component*> componentsToUnInstall;
- ComponentModel *model = isUpdater() ? updaterComponentModel() : defaultComponentModel();
- for (QModelIndex index : indexes) {
- Component *installComponent = model->componentFromIndex(index);
- // 1. Component is selected for install
- if (installComponent->isSelected() && !installComponent->isInstalled()) {
- componentsToInstall.append(installComponent);
- // Check if component has replacements that needs to be removed
- const QList<Component*> replacedComponents = d->replacedComponentsByName(installComponent->name());
- for (Component *replacedComponent : replacedComponents) {
- componentsToUnInstall.append(replacedComponent);
- d->uninstallerCalculator()->insertUninstallReason(replacedComponent,
- UninstallerCalculator::UninstallReasonType::Replaced);
- }
- }
- // 2. Component is reseleted for install (tapping checkbox off/on)
- else if (installComponent->isSelected() && installComponent->isInstalled()
- && !d->installerCalculator()->orderedComponentsToInstall().contains(installComponent)) {
- componentsToInstall.append(installComponent);
- }
- // 3. Component is selected for uninstall
- else if (!isUpdater() && !installComponent->isSelected() && installComponent->isInstalled()) {
- componentsToUnInstall.append(installComponent);
- }
- // 4. Component is reselected for uninstall (tapping checkbox on/off)
- else if (!installComponent->isSelected()
- && d->installerCalculator()->orderedComponentsToInstall().contains(installComponent)) {
- componentsToUnInstall.append(installComponent);
- // Check if component has replacements that needs to be readded
- componentsToInstall.append(d->replacedComponentsByName(installComponent->name()));
- }
- }
+template <typename T>
+bool PackageManagerCore::loadComponentScripts(const T &components, const bool postScript)
+{
+ return d->loadComponentScripts(components, postScript);
+}
- d->installerCalculator()->removeComponentsFromInstall(componentsToUnInstall);
- d->m_componentsToInstallCalculated
- = d->installerCalculator()->appendComponentsToInstall(componentsToInstall, false);
- if (!isUpdater()) {
- d->uninstallerCalculator()->appendComponentsToUninstall(componentsToUnInstall, false);
- }
- d->uninstallerCalculator()->removeComponentsFromUnInstall(componentsToInstall);
+template bool PackageManagerCore::loadComponentScripts<QList<Component *>>(const QList<Component *> &, const bool);
+template bool PackageManagerCore::loadComponentScripts<QHash<QString, Component *>>(const QHash<QString, Component *> &, const bool);
- d->updateComponentCheckedState();
+/*!
+ Saves the installer \a args user has given when running installer. Command and option arguments
+ are not saved.
+*/
+void PackageManagerCore::saveGivenArguments(const QStringList &args)
+{
+ m_arguments = args;
}
+/*!
+ Returns the commands and options user has given when running installer.
+*/
+QStringList PackageManagerCore::givenArguments() const
+{
+ return m_arguments;
+}
+/*!
+ \deprecated [4.5] Use recalculateAllComponents() instead.
+
+ \sa {installer::componentsToInstallNeedsRecalculation}{installer.componentsToInstallNeedsRecalculation}
+ */
+void PackageManagerCore::componentsToInstallNeedsRecalculation()
+{
+ recalculateAllComponents();
+}
/*!
- Forces a recalculation of components to install.
+ \fn QInstaller::PackageManagerCore::clearComponentsToInstallCalculated()
+
+ \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.
+
\sa {installer::clearComponentsToInstallCalculated}{installer.clearComponentsToInstallCalculated}
*/
-void PackageManagerCore::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()
{
- d->m_componentsToInstallCalculated = false;
+ // Clear previous results first, as the check states are updated
+ // at the end of both calculate methods, which refer to the results
+ // from both calculators. Needed to keep the state correct.
+ d->clearInstallerCalculator();
+ d->clearUninstallerCalculator();
+
+ if (!calculateComponentsToInstall())
+ return false;
+ if (!isInstaller() && !calculateComponentsToUninstall())
+ return false;
+
+ // update all nodes uncompressed size
+ foreach (Component *const component, components(ComponentType::Root))
+ component->updateUncompressedSize(); // this is a recursive call
+
+ return true;
}
/*!
@@ -796,12 +811,13 @@ quint64 PackageManagerCore::requiredDiskSpace() const
*/
quint64 PackageManagerCore::requiredTemporaryDiskSpace() const
{
- if (isOfflineOnly())
- return 0;
-
quint64 result = 0;
- foreach (QInstaller::Component *component, orderedComponentsToInstall())
+ foreach (QInstaller::Component *component, orderedComponentsToInstall()) {
+ if (!component->isFromOnlineRepository())
+ continue;
+
result += size(component, scCompressedSize);
+ }
return result;
}
@@ -814,16 +830,19 @@ int PackageManagerCore::downloadNeededArchives(double partProgressSize)
{
Q_ASSERT(partProgressSize >= 0 && partProgressSize <= 1);
- QList<QPair<QString, QString> > archivesToDownload;
+ QList<DownloadItem> archivesToDownload;
quint64 archivesToDownloadTotalSize = 0;
QList<Component*> neededComponents = orderedComponentsToInstall();
foreach (Component *component, neededComponents) {
// collect all archives to be downloaded
const QStringList toDownload = component->downloadableArchives();
+ bool checkSha1CheckSum = (component->value(scCheckSha1CheckSum).toLower() == scTrue);
foreach (const QString &versionFreeString, toDownload) {
- archivesToDownload.push_back(qMakePair(QString::fromLatin1("installer://%1/%2")
- .arg(component->name(), versionFreeString), QString::fromLatin1("%1/%2/%3")
- .arg(component->repositoryUrl().toString(), component->name(), versionFreeString)));
+ DownloadItem item;
+ item.checkSha1CheckSum = checkSha1CheckSum;
+ item.fileName = scInstallerPrefixWithTwoArgs.arg(component->name(), versionFreeString);
+ item.sourceUrl = scThreeArgs.arg(component->repositoryUrl().toString(), component->name(), versionFreeString);
+ archivesToDownload.push_back(item);
}
archivesToDownloadTotalSize += component->value(scCompressedSize).toULongLong();
}
@@ -834,7 +853,7 @@ int PackageManagerCore::downloadNeededArchives(double partProgressSize)
ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(QLatin1Char('\n')
+ tr("Downloading packages..."));
- DownloadArchivesJob archivesJob(this);
+ DownloadArchivesJob archivesJob(this, QLatin1String("downloadArchiveJob"));
archivesJob.setAutoDelete(false);
archivesJob.setArchivesToDownload(archivesToDownload);
archivesJob.setExpectedTotalSize(archivesToDownloadTotalSize);
@@ -844,6 +863,11 @@ int PackageManagerCore::downloadNeededArchives(double partProgressSize)
connect(&archivesJob, &DownloadArchivesJob::downloadStatusChanged,
ProgressCoordinator::instance(), &ProgressCoordinator::additionalProgressStatusChanged);
+ connect(&archivesJob, &DownloadArchivesJob::fileDownloadReady,
+ d, &PackageManagerCorePrivate::addPathForDeletion);
+ connect(&archivesJob, &DownloadArchivesJob::hashDownloadReady,
+ d, &PackageManagerCorePrivate::addPathForDeletion);
+
ProgressCoordinator::instance()->registerPartProgress(&archivesJob,
SIGNAL(progressChanged(double)), partProgressSize);
@@ -997,7 +1021,7 @@ bool PackageManagerCore::isFileExtensionRegistered(const QString &extension) con
*/
bool PackageManagerCore::fileExists(const QString &filePath) const
{
- return QFileInfo(filePath).exists();
+ return QFileInfo::exists(filePath);
}
/*!
@@ -1022,8 +1046,7 @@ QString PackageManagerCore::readFile(const QString &filePath, const QString &cod
return QString();
QTextStream stream(&f);
- stream.setCodec(codec);
- return stream.readAll();
+ return QString::fromUtf8(codec->fromUnicode(stream.readAll()));
}
/*!
@@ -1264,11 +1287,16 @@ PackageManagerCore::PackageManagerCore()
Creates and initializes a remote client. Requests administrator's rights for
QFile, QSettings, and QProcess operations. Calls \c init() with \a socketName, \a key,
and \a mode to set the server side authorization key.
+
+ The \a datFileName contains the corresponding .dat file name for the running
+ \c maintenance tool binary. \a datFileName can be empty if \c maintenance tool
+ fails to find it or if \c installer is run instead of \c maintenance tool.
*/
PackageManagerCore::PackageManagerCore(qint64 magicmaker, const QList<OperationBlob> &operations,
+ const QString &datFileName,
const QString &socketName, const QString &key, Protocol::Mode mode,
const QHash<QString, QString> &params, const bool commandLineInstance)
- : d(new PackageManagerCorePrivate(this, magicmaker, operations))
+ : d(new PackageManagerCorePrivate(this, magicmaker, operations, datFileName))
{
setCommandLineInstance(commandLineInstance);
Repository::registerMetaType(); // register, cause we stream the type as QVariant
@@ -1299,8 +1327,8 @@ PackageManagerCore::PackageManagerCore(qint64 magicmaker, const QList<OperationB
QSet<QString> packagesWithoutOperation = installedPackages - operationPackages;
QSet<QString> orphanedOperations = operationPackages - installedPackages;
if (!packagesWithoutOperation.isEmpty() || !orphanedOperations.isEmpty()) {
- qCritical() << "Operations missing for installed packages" << packagesWithoutOperation.toList();
- qCritical() << "Orphaned operations" << orphanedOperations.toList();
+ qCritical() << "Operations missing for installed packages" << packagesWithoutOperation.values();
+ qCritical() << "Orphaned operations" << orphanedOperations.values();
qCritical() << "Your installation seems to be corrupted. Please consider re-installing from scratch, "
"remove the packages from components.xml which operations are missing, "
"or reinstall the packages.";
@@ -1507,7 +1535,7 @@ bool PackageManagerCore::fetchLocalPackagesTree()
continue;
}
- QScopedPointer<QInstaller::Component> component(new QInstaller::Component(this));
+ std::unique_ptr<QInstaller::Component> component(new QInstaller::Component(this));
component->loadDataFromPackage(package);
QString name = component->treeName();
if (components.contains(name)) {
@@ -1533,7 +1561,7 @@ bool PackageManagerCore::fetchLocalPackagesTree()
if (!treeName.isEmpty())
treeNameComponents.insert(component->name(), treeName);
- components.insert(name, component.take());
+ components.insert(name, component.release());
}
// Second pass with leftover packages
if (firstRun)
@@ -1578,10 +1606,11 @@ void PackageManagerCore::networkSettingsChanged()
cancelMetaInfoJob();
d->m_updates = false;
+ d->m_aliases = false;
d->m_repoFetched = false;
d->m_updateSourcesAdded = false;
- if (isMaintainer() ) {
+ if (!isInstaller()) {
bool gainedAdminRights = false;
if (!directoryWritable(d->targetDir())) {
gainAdminRights();
@@ -1643,7 +1672,7 @@ bool PackageManagerCore::fetchCompressedPackagesTree()
if (!d->fetchMetaInformationFromRepositories(DownloadType::CompressedPackage))
return false;
- if (!d->addUpdateResourcesFromRepositories(true, true)) {
+ if (!d->addUpdateResourcesFromRepositories(true)) {
return false;
}
@@ -1654,11 +1683,42 @@ bool PackageManagerCore::fetchCompressedPackagesTree()
return fetchPackagesTree(packages, installedPackages);
}
+bool PackageManagerCore::fetchPackagesWithFallbackRepositories(const QStringList& components, bool &fallBackReposFetched)
+{
+ auto checkComponents = [&]() {
+ if (!fetchRemotePackagesTree(components))
+ return false;
+ return true;
+ };
+
+ if (!checkComponents()) {
+ // error when fetching packages tree
+ if (status() != NoPackagesFound)
+ return false;
+ //retry fetching packages with all categories enabled
+ fallBackReposFetched = true;
+ if (!d->enableAllCategories())
+ return false;
+
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote()
+ << "Components not found with the current selection."
+ << "Searching from additional repositories";
+ if (!ProductKeyCheck::instance()->securityWarning().isEmpty()) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << ProductKeyCheck::instance()->securityWarning();
+ }
+ if (!checkComponents()) {
+ return false;
+ }
+ }
+ return true;
+}
+
/*!
Checks for packages to install. Returns \c true if newer versions exist
- and they can be installed.
+ and they can be installed. Returns \c false if not \a components are found
+ for install, or if error occurred when fetching and generating package tree.
*/
-bool PackageManagerCore::fetchRemotePackagesTree()
+bool PackageManagerCore::fetchRemotePackagesTree(const QStringList& components)
{
d->setStatus(Running);
@@ -1682,12 +1742,18 @@ bool PackageManagerCore::fetchRemotePackagesTree()
if (!d->fetchMetaInformationFromRepositories(DownloadType::CompressedPackage))
return false;
- if (!d->addUpdateResourcesFromRepositories(true))
+ if (!d->addUpdateResourcesFromRepositories())
return false;
const PackagesList &packages = d->remotePackages();
- if (packages.isEmpty())
+ if (packages.isEmpty()) {
+ d->setStatus(PackageManagerCore::NoPackagesFound);
return false;
+ }
+
+ if (!d->installablePackagesFound(components))
+ return false;
+
return fetchPackagesTree(packages, installedPackages);
}
@@ -1936,24 +2002,82 @@ void PackageManagerCore::setTemporaryRepositories(const QStringList &repositorie
settings().setTemporaryRepositories(repositorySet, replace);
}
-/*!
- Checks whether the downloader should try to download SHA-1 checksums for
- archives and returns the checksums.
-*/
-bool PackageManagerCore::testChecksum() const
+bool PackageManagerCore::addQBspRepositories(const QStringList &repositories)
{
- return d->m_testChecksum;
+ QSet<Repository> set;
+ foreach (QString fileName, repositories) {
+ Repository repository = Repository::fromUserInput(fileName, true);
+ repository.setEnabled(true);
+ set.insert(repository);
+ }
+ if (set.count() > 0) {
+ settings().addTemporaryRepositories(set, false);
+ return true;
+ }
+ return false;
}
-/*!
- The \a test argument determines whether the downloader should try to
- download SHA-1 checksums for archives.
-*/
-void PackageManagerCore::setTestChecksum(bool test)
+bool PackageManagerCore::validRepositoriesAvailable() const
+{
+ foreach (const Repository &repo, settings().repositories()) {
+ if (repo.isEnabled() && repo.isValid()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void PackageManagerCore::setAllowCompressedRepositoryInstall(bool allow)
{
- d->m_testChecksum = test;
+ d->m_allowCompressedRepositoryInstall = allow;
}
+bool PackageManagerCore::allowCompressedRepositoryInstall() const
+{
+ return d->m_allowCompressedRepositoryInstall;
+}
+
+bool PackageManagerCore::showRepositoryCategories() const
+{
+ bool showCagetories = settings().repositoryCategories().count() > 0 && !isOfflineOnly() && !isUpdater();
+ if (showCagetories)
+ settings().setAllowUnstableComponents(true);
+ return showCagetories;
+}
+
+QVariantMap PackageManagerCore::organizedRepositoryCategories() const
+{
+ QVariantMap map;
+ QSet<RepositoryCategory> categories = settings().repositoryCategories();
+ foreach (const RepositoryCategory &category, categories)
+ map.insert(category.displayname(), QVariant::fromValue(category));
+ return map;
+}
+
+void PackageManagerCore::enableRepositoryCategory(const QString &repositoryName, bool enable)
+{
+ QMap<QString, RepositoryCategory> organizedRepositoryCategories = settings().organizedRepositoryCategories();
+
+ QMap<QString, RepositoryCategory>::iterator i = organizedRepositoryCategories.find(repositoryName);
+ while (i != organizedRepositoryCategories.end() && i.key() == repositoryName) {
+ d->enableRepositoryCategory(i.value(), enable);
+ i++;
+ }
+}
+
+void PackageManagerCore::runProgram()
+{
+ const QString program = replaceVariables(value(scRunProgram));
+
+ const QStringList args = replaceVariables(values(scRunProgramArguments));
+ if (program.isEmpty())
+ return;
+
+ qCDebug(QInstaller::lcInstallerInstallLog) << "starting" << program << args;
+ QProcess::startDetached(program, args);
+}
+
+
/*!
Returns the script engine that prepares and runs the component scripts.
@@ -1982,6 +2106,10 @@ ScriptEngine *PackageManagerCore::controlScriptEngine() const
*/
void PackageManagerCore::appendRootComponent(Component *component)
{
+ // For normal installer runs components aren't appended after model reset
+ if (Q_UNLIKELY(!d->m_componentByNameHash.isEmpty()))
+ d->m_componentByNameHash.clear();
+
d->m_rootComponents.append(component);
emit componentAdded(component);
}
@@ -2047,7 +2175,7 @@ QList<Component *> PackageManagerCore::components(ComponentTypes mask, const QSt
QRegularExpression re(regexp);
QList<Component*>::iterator iter = components.begin();
while (iter != components.end()) {
- if (!re.match(iter.i->t()->name()).hasMatch())
+ if (!re.match((*iter)->name()).hasMatch())
iter = components.erase(iter);
else
iter++;
@@ -2063,6 +2191,10 @@ QList<Component *> PackageManagerCore::components(ComponentTypes mask, const QSt
*/
void PackageManagerCore::appendUpdaterComponent(Component *component)
{
+ // For normal installer runs components aren't appended after model reset
+ if (Q_UNLIKELY(!d->m_componentByNameHash.isEmpty()))
+ d->m_componentByNameHash.clear();
+
component->setUpdateAvailable(true);
d->m_updaterComponents.append(component);
emit componentAdded(component);
@@ -2076,7 +2208,39 @@ void PackageManagerCore::appendUpdaterComponent(Component *component)
*/
Component *PackageManagerCore::componentByName(const QString &name) const
{
- return componentByName(name, components(ComponentType::AllNoReplacements));
+ if (name.isEmpty())
+ return nullptr;
+
+ if (d->m_componentByNameHash.isEmpty()) {
+ // We can avoid the linear lookups from the component list by creating
+ // a <name,component> hash once, and reusing it on subsequent calls.
+ const QList<Component *> componentsList = components(ComponentType::AllNoReplacements);
+ for (Component *component : componentsList)
+ d->m_componentByNameHash.insert(component->name(), component);
+ }
+
+ QString fixedVersion;
+ QString fixedName;
+
+ parseNameAndVersion(name, &fixedName, &fixedVersion);
+
+ Component *component = d->m_componentByNameHash.value(fixedName);
+ if (!component)
+ return nullptr;
+
+ if (componentMatches(component, fixedName, fixedVersion))
+ return component;
+
+ return nullptr;
+}
+
+/*!
+ Searches for a component alias matching \a name and returns it.
+ If no alias matches the name, \c nullptr is returned.
+*/
+ComponentAlias *PackageManagerCore::aliasByName(const QString &name) const
+{
+ return d->m_componentAliases.value(name);
}
/*!
@@ -2105,6 +2269,18 @@ Component *PackageManagerCore::componentByName(const QString &name, const QList<
}
/*!
+ Returns an array of all components currently available. If the repository
+ metadata have not been fetched yet, the array will be empty. Optionally, a
+ \a regexp expression can be used to further filter the listed packages.
+
+ \sa {installer::components}{installer.components}
+ */
+QList<Component *> PackageManagerCore::components(const QString &regexp) const
+{
+ return components(PackageManagerCore::ComponentType::All, regexp);
+}
+
+/*!
Returns \c true if directory specified by \a path is writable by
the current user.
*/
@@ -2142,8 +2318,27 @@ 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.
+ Returns a list of component aliases that are marked for installation.
+ The list can be empty.
+*/
+QList<ComponentAlias *> PackageManagerCore::aliasesMarkedForInstallation() const
+{
+ if (isUpdater()) // Aliases not supported on update at the moment
+ return QList<ComponentAlias *>();
+
+ QList<ComponentAlias *> markedForInstallation;
+ for (auto *alias : qAsConst(d->m_componentAliases)) {
+ if (alias && alias->isSelected())
+ markedForInstallation.append(alias);
+ }
+
+ return markedForInstallation;
+}
+
+/*!
+ Determines which components to install based on the current run mode, including component aliases,
+ 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.
@@ -2154,16 +2349,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 bool calculated = d->installerCalculator()->solve();
+
+ d->updateComponentInstallActions();
+
emit finishedCalculateComponentsToInstall();
- return d->m_componentsToInstallCalculated;
+ return calculated;
}
/*!
@@ -2171,29 +2365,34 @@ bool PackageManagerCore::calculateComponentsToInstall() const
*/
QList<Component*> PackageManagerCore::orderedComponentsToInstall() const
{
- return d->installerCalculator()->orderedComponentsToInstall();
+ return d->installerCalculator()->resolvedComponents();
}
/*!
- 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();
@@ -2226,28 +2425,46 @@ 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()->resolvedComponents();
+
+ 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->resolvedComponents().contains(comp.first)) {
+ d->uninstallerCalculator()->insertResolution(component,
+ CalculatorBase::Resolution::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()->solve(selectedComponentsToUninstall);
+
+ d->updateComponentInstallActions();
+
emit finishedCalculateComponentsToUninstall();
- return true;
+ return componentsToUninstallCalculated;
}
/*!
@@ -2259,7 +2476,7 @@ bool PackageManagerCore::calculateComponentsToUninstall() const
*/
QList<Component *> PackageManagerCore::componentsToUninstall() const
{
- return d->uninstallerCalculator()->componentsToUninstall().toList();
+ return d->uninstallerCalculator()->resolvedComponents();
}
/*!
@@ -2267,7 +2484,15 @@ QList<Component *> PackageManagerCore::componentsToUninstall() const
*/
QString PackageManagerCore::componentsToInstallError() const
{
- return d->installerCalculator()->componentsToInstallError();
+ return d->installerCalculator()->error();
+}
+
+/*!
+ Returns errors found in the components that are marked for uninstallation.
+*/
+QString PackageManagerCore::componentsToUninstallError() const
+{
+ return d->uninstallerCalculator()->error();
}
/*!
@@ -2281,7 +2506,7 @@ QString PackageManagerCore::componentsToInstallError() const
*/
QString PackageManagerCore::installReason(Component *component) const
{
- return d->installerCalculator()->installReason(component);
+ return d->installerCalculator()->resolutionText(component);
}
/*!
@@ -2297,7 +2522,7 @@ QString PackageManagerCore::installReason(Component *component) const
*/
QString PackageManagerCore::uninstallReason(Component *component) const
{
- return d->uninstallerCalculator()->uninstallReason(component);
+ return d->uninstallerCalculator()->resolutionText(component);
}
/*!
@@ -2330,44 +2555,65 @@ QList<Component*> PackageManagerCore::dependees(const Component *_component) con
}
/*!
- Returns a list of components that depend on \a component. The list can be
- empty. Dependendants are calculated from components which are about to be updated,
- if no update is requested then the dependant is calculated from installed packages.
-
- \note Automatic dependencies are not resolved.
+ Returns true if components which are about to be installed or updated
+ are dependent on \a component.
*/
-QList<Component*> PackageManagerCore::installDependants(const Component *component) const
+bool PackageManagerCore::isDependencyForRequestedComponent(const Component *component) const
{
if (!component)
- return QList<Component *>();
+ return false;
const QList<QInstaller::Component *> availableComponents = components(ComponentType::All);
if (availableComponents.isEmpty())
- return QList<Component *>();
+ return false;
- QList<Component *> dependants;
QString name;
QString version;
- foreach (Component *availableComponent, availableComponents) {
- if (isUpdater() && availableComponent->updateRequested()) {
+ for (Component *availableComponent : availableComponents) {
+ if (!availableComponent) {
+ continue;
+ }
+ // 1. In updater mode, component to be updated might have new dependencies
+ // Check if the dependency is still needed
+ // 2. If component is selected and not installed, check if the dependency is needed
+ if (availableComponent->isSelected()
+ && ((isUpdater() && availableComponent->isInstalled())
+ || (isPackageManager() && !availableComponent->isInstalled()))) {
const QStringList &dependencies = availableComponent->dependencies();
foreach (const QString &dependency, dependencies) {
parseNameAndVersion(dependency, &name, &version);
if (componentMatches(component, name, version)) {
- dependants.append(availableComponent);
+ return true;
}
}
- } else {
- KDUpdater::LocalPackage localPackage = d->m_localPackageHub->packageInfo(availableComponent->name());
- foreach (const QString &dependency, localPackage.dependencies) {
- parseNameAndVersion(dependency, &name, &version);
- if (componentMatches(component, name, version)) {
- dependants.append(availableComponent);
- }
+ }
+ }
+ return false;
+}
+
+
+/*!
+ Returns a list of local components which are dependent on \a component.
+*/
+QStringList PackageManagerCore::localDependenciesToComponent(const Component *component) const
+{
+ if (!component)
+ return QStringList();
+
+ QStringList dependents;
+ QString name;
+ QString version;
+
+ QMap<QString, LocalPackage> localPackages = d->m_localPackageHub->localPackages();
+ for (const KDUpdater::LocalPackage &localPackage : qAsConst(localPackages)) {
+ for (const QString &dependency : localPackage.dependencies) {
+ parseNameAndVersion(dependency, &name, &version);
+ if (componentMatches(component, name, version)) {
+ dependents.append(localPackage.name);
}
}
}
- return dependants;
+ return dependents;
}
/*!
@@ -2379,12 +2625,13 @@ ComponentModel *PackageManagerCore::defaultComponentModel() const
if (!d->m_defaultModel) {
d->m_defaultModel = componentModel(const_cast<PackageManagerCore*> (this),
QLatin1String("AllComponentsModel"));
+
+ connect(this, &PackageManagerCore::startAllComponentsReset, [&] {
+ d->m_defaultModel->reset();
+ });
+ connect(this, &PackageManagerCore::finishAllComponentsReset, d->m_defaultModel,
+ &ComponentModel::reset);
}
- connect(this, &PackageManagerCore::startAllComponentsReset, [&] {
- d->m_defaultModel->reset();
- });
- connect(this, &PackageManagerCore::finishAllComponentsReset, d->m_defaultModel,
- &ComponentModel::reset);
return d->m_defaultModel;
}
@@ -2397,44 +2644,70 @@ ComponentModel *PackageManagerCore::updaterComponentModel() const
if (!d->m_updaterModel) {
d->m_updaterModel = componentModel(const_cast<PackageManagerCore*> (this),
QLatin1String("UpdaterComponentsModel"));
+
+ connect(this, &PackageManagerCore::startUpdaterComponentsReset, [&] {
+ d->m_updaterModel->reset();
+ });
+ connect(this, &PackageManagerCore::finishUpdaterComponentsReset, d->m_updaterModel,
+ &ComponentModel::reset);
}
- connect(this, &PackageManagerCore::startUpdaterComponentsReset, [&] {
- d->m_updaterModel->reset();
- });
- connect(this, &PackageManagerCore::finishUpdaterComponentsReset, d->m_updaterModel,
- &ComponentModel::reset);
return d->m_updaterModel;
}
/*!
+ Returns the proxy model
+*/
+
+ComponentSortFilterProxyModel *PackageManagerCore::componentSortFilterProxyModel()
+{
+ if (!d->m_componentSortFilterProxyModel) {
+ d->m_componentSortFilterProxyModel = new ComponentSortFilterProxyModel(this);
+ d->m_componentSortFilterProxyModel->setRecursiveFilteringEnabled(true);
+ d->m_componentSortFilterProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
+ }
+ return d->m_componentSortFilterProxyModel;
+}
+
+/*!
Lists available packages filtered with \a regexp without GUI. Virtual
components are not listed unless set visible. Optionally, a \a filters
hash containing package information elements and regular expressions
can be used to further filter listed packages.
+ Returns \c true if matching packages were found, \c false otherwise.
+
\sa setVirtualComponentsVisible()
*/
-void PackageManagerCore::listAvailablePackages(const QString &regexp, const QHash<QString, QString> &filters)
+bool PackageManagerCore::listAvailablePackages(const QString &regexp, const QHash<QString, QString> &filters)
{
setPackageViewer();
+ d->enableAllCategories();
qCDebug(QInstaller::lcInstallerInstallLog)
<< "Searching packages with regular expression:" << regexp;
ComponentModel *model = defaultComponentModel();
- d->fetchMetaInformationFromRepositories(DownloadType::UpdatesXML);
+ PackagesList packages;
+
+ if (!d->m_updates) {
+ d->fetchMetaInformationFromRepositories();
+ d->addUpdateResourcesFromRepositories();
+
+ packages = d->remotePackages();
+ if (!fetchAllPackages(packages, LocalPackagesMap())) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "There was a problem with loading the package data.";
+ return false;
+ }
+ } else {
+ // No need to fetch metadata again
+ packages = d->remotePackages();
+ }
- d->addUpdateResourcesFromRepositories(true);
QRegularExpression re(regexp);
re.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
- const PackagesList &packages = d->remotePackages();
- if (!fetchAllPackages(packages, LocalPackagesMap())) {
- qCWarning(QInstaller::lcInstallerInstallLog)
- << "There was a problem with loading the package data.";
- return;
- }
PackagesList matchedPackages;
- foreach (Package *package, packages) {
+ foreach (Package *package, qAsConst(packages)) {
const QString name = package->data(scName).toString();
Component *component = componentByName(name);
if (!component)
@@ -2456,10 +2729,68 @@ void PackageManagerCore::listAvailablePackages(const QString &regexp, const QHas
matchedPackages.append(package);
}
}
- if (matchedPackages.count() == 0)
+ if (matchedPackages.count() == 0) {
qCDebug(QInstaller::lcInstallerInstallLog) << "No matching packages found.";
- else
- LoggingHandler::instance().printPackageInformation(matchedPackages, localInstalledPackages());
+ return false;
+ }
+
+ LoggingHandler::instance().printPackageInformation(matchedPackages, localInstalledPackages());
+ return true;
+}
+
+/*!
+ Lists available component aliases filtered with \a regexp without GUI. Virtual
+ aliases are not listed unless set visible.
+
+ Returns \c true if matching package aliases were found, \c false otherwise.
+
+ \sa setVirtualComponentsVisible()
+*/
+bool PackageManagerCore::listAvailableAliases(const QString &regexp)
+{
+ setPackageViewer();
+ d->enableAllCategories();
+ qCDebug(QInstaller::lcInstallerInstallLog)
+ << "Searching aliases with regular expression:" << regexp;
+
+ ComponentModel *model = defaultComponentModel();
+ Q_UNUSED(model);
+
+ if (!d->m_updates) {
+ d->fetchMetaInformationFromRepositories();
+ d->addUpdateResourcesFromRepositories();
+
+ const PackagesList &packages = d->remotePackages();
+ if (!fetchAllPackages(packages, LocalPackagesMap())) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "There was a problem with loading the package data.";
+ return false;
+ }
+ }
+
+ QRegularExpression re(regexp);
+ re.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
+
+ QList<ComponentAlias *> matchedAliases;
+ for (auto *alias : qAsConst(d->m_componentAliases)) {
+ if (!alias)
+ continue;
+
+ if (re.match(alias->name()).hasMatch() && !alias->isUnstable()) {
+ if (alias->isVirtual() && !virtualComponentsVisible())
+ continue;
+
+ matchedAliases.append(alias);
+ }
+ }
+
+ if (matchedAliases.isEmpty()) {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "No matching package aliases found.";
+ return false;
+ }
+
+ LoggingHandler::instance().printAliasInformation(matchedAliases);
+ return true;
}
bool PackageManagerCore::componentUninstallableFromCommandLine(const QString &componentName)
@@ -2473,7 +2804,7 @@ bool PackageManagerCore::componentUninstallableFromCommandLine(const QString &co
}
ComponentModel *model = defaultComponentModel();
const QModelIndex &idx = model->indexFromComponentName(component->treeName());
- if (model->data(idx, Qt::CheckStateRole) == QVariant::Invalid) {
+ if (model->data(idx, Qt::CheckStateRole) == QVariant()) {
// Component cannot be unselected, check why
if (component->forcedInstallation()) {
qCWarning(QInstaller::lcInstallerInstallLog).noquote().nospace()
@@ -2497,25 +2828,45 @@ bool PackageManagerCore::componentUninstallableFromCommandLine(const QString &co
/*!
\internal
- Tries to set \c Qt::CheckStateRole to \c Qt::Checked for given \a components in the
- default component model. Returns \c true if \a components contains at least one component
+ Tries to set \c Qt::CheckStateRole to \c Qt::Checked for given component \a names in the
+ default component model, and select given aliases in the \c names list.
+
+ Returns \c true if \a names contains at least one component or component alias
eligible for installation, otherwise returns \c false. An error message can be retrieved
with \a errorMessage.
*/
-bool PackageManagerCore::checkComponentsForInstallation(const QStringList &components, QString &errorMessage)
+bool PackageManagerCore::checkComponentsForInstallation(const QStringList &names, QString &errorMessage, bool &unstableAliasFound)
{
bool installComponentsFound = false;
ComponentModel *model = defaultComponentModel();
- foreach (const QString &name, components) {
+ foreach (const QString &name, names) {
Component *component = componentByName(name);
if (!component) {
- errorMessage.append(tr("Cannot install %1. Component not found.").arg(name) + QLatin1Char('\n'));
+ // No such component, check if we have an alias by the name
+ if (ComponentAlias *alias = aliasByName(name)) {
+ if (alias->isUnstable()) {
+ errorMessage.append(tr("Cannot select alias %1. There was a problem loading this alias, "
+ "so it is marked unstable and cannot be selected.").arg(name) + QLatin1Char('\n'));
+ unstableAliasFound = true;
+ continue;
+ } else if (alias->isVirtual()) {
+ errorMessage.append(tr("Cannot select %1. Alias is marked virtual, meaning it cannot "
+ "be selected manually.").arg(name) + QLatin1Char('\n'));
+ continue;
+ }
+
+ alias->setSelected(true);
+ installComponentsFound = true;
+ } else {
+ errorMessage.append(tr("Cannot install %1. Component not found.").arg(name) + QLatin1Char('\n'));
+ }
+
continue;
}
const QModelIndex &idx = model->indexFromComponentName(component->treeName());
if (idx.isValid()) {
- if ((model->data(idx, Qt::CheckStateRole) == QVariant::Invalid) && !component->forcedInstallation()) {
+ if ((model->data(idx, Qt::CheckStateRole) == QVariant()) && !component->forcedInstallation()) {
// User cannot select the component, check why
if (component->autoDependencies().count() > 0) {
errorMessage.append(tr("Cannot install component %1. Component is installed only as automatic "
@@ -2585,6 +2936,25 @@ void PackageManagerCore::listInstalledPackages(const QString &regexp)
LoggingHandler::instance().printLocalPackageInformation(packages);
}
+PackageManagerCore::Status PackageManagerCore::searchAvailableUpdates()
+{
+ setUpdater();
+ d->enableAllCategories();
+ if (!fetchRemotePackagesTree()) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << error();
+ return status();
+ }
+
+ const QList<QInstaller::Component *> availableUpdates =
+ components(QInstaller::PackageManagerCore::ComponentType::Root);
+ if (availableUpdates.isEmpty()) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << "There are currently no updates available.";
+ return status();
+ }
+ QInstaller::LoggingHandler::instance().printUpdateInformation(availableUpdates);
+ return status();
+}
+
/*!
Updates the selected components \a componentsToUpdate without GUI.
If essential components are found, then only those will be updated.
@@ -2592,13 +2962,25 @@ void PackageManagerCore::listInstalledPackages(const QString &regexp)
*/
PackageManagerCore::Status PackageManagerCore::updateComponentsSilently(const QStringList &componentsToUpdate)
{
- if (d->runningProcessesFound())
- throw Error(tr("Running processes found."));
setUpdater();
ComponentModel *model = updaterComponentModel();
- fetchRemotePackagesTree();
+ if (componentsToUpdate.isEmpty()) {
+ d->enableAllCategories();
+ fetchRemotePackagesTree();
+ } else {
+ bool fallbackReposFetched = false;
+ bool packagesFound = fetchPackagesWithFallbackRepositories(componentsToUpdate, fallbackReposFetched);
+
+ if (!packagesFound) {
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote().nospace()
+ << "No components available for update with the current selection.";
+ d->setStatus(Canceled);
+ return status();
+ }
+ }
+
// List contains components containing update, if essential found contains only essential component
const QList<QInstaller::Component*> componentList = componentsMarkedForInstallation();
@@ -2704,6 +3086,20 @@ void PackageManagerCore::addLicenseItem(const QHash<QString, QVariantMap> &licen
}
}
+bool PackageManagerCore::hasLicenses() const
+{
+ foreach (Component* component, orderedComponentsToInstall()) {
+ if (isMaintainer() && component->isInstalled())
+ continue; // package manager or updater, hide as long as the component is installed
+
+ // The component is about to be installed and provides a license, so the page needs to
+ // be shown.
+ if (!component->licenses().isEmpty())
+ return true;
+ }
+ return false;
+}
+
/*!
* Adds \a component local \a dependencies to a hash table for quicker search for
* uninstall dependency components.
@@ -2728,9 +3124,6 @@ void PackageManagerCore::createAutoDependencyHash(const QString &component, cons
*/
PackageManagerCore::Status PackageManagerCore::uninstallComponentsSilently(const QStringList& components)
{
- if (d->runningProcessesFound())
- throw Error(tr("Running processes found."));
-
if (components.isEmpty()) {
qCDebug(QInstaller::lcInstallerInstallLog) << "No components selected for uninstallation.";
return PackageManagerCore::Canceled;
@@ -2769,8 +3162,6 @@ PackageManagerCore::Status PackageManagerCore::uninstallComponentsSilently(const
PackageManagerCore::Status PackageManagerCore::removeInstallationSilently()
{
setCompleteUninstallation(true);
- if (d->runningProcessesFound())
- throw Error(tr("Running processes found."));
qCDebug(QInstaller::lcInstallerInstallLog) << "Complete uninstallation was chosen.";
if (!(d->m_autoConfirmCommand || d->askUserConfirmCommand())) {
@@ -2792,23 +3183,7 @@ PackageManagerCore::Status PackageManagerCore::removeInstallationSilently()
PackageManagerCore::Status PackageManagerCore::createOfflineInstaller(const QStringList &componentsToAdd)
{
setOfflineGenerator();
- // init default model before fetching remote packages tree
- ComponentModel *model = defaultComponentModel();
- Q_UNUSED(model);
- if (!fetchRemotePackagesTree())
- return status();
-
- QString errorMessage;
- if (checkComponentsForInstallation(componentsToAdd, errorMessage)) {
- if (d->calculateComponentsAndRun()) {
- qCDebug(QInstaller::lcInstallerInstallLog)
- << "Created installer to:" << offlineBinaryName();
- }
- } else {
- qCDebug(QInstaller::lcInstallerInstallLog).noquote().nospace() << errorMessage
- << "\nNo components available with the current selection.";
- }
- return status();
+ return d->fetchComponentsAndInstall(componentsToAdd);
}
/*!
@@ -2820,9 +3195,6 @@ PackageManagerCore::Status PackageManagerCore::createOfflineInstaller(const QStr
PackageManagerCore::Status PackageManagerCore::installSelectedComponentsSilently(const QStringList& components)
{
if (!isInstaller()) {
- // Check if there are processes running in the install if maintenancetool is used.
- if (d->runningProcessesFound())
- throw Error(tr("Running processes found."));
setPackageManager();
//Check that packages are not already installed
@@ -2835,24 +3207,7 @@ PackageManagerCore::Status PackageManagerCore::installSelectedComponentsSilently
return PackageManagerCore::Canceled;
}
}
-
- // init default model before fetching remote packages tree
- ComponentModel *model = defaultComponentModel();
- Q_UNUSED(model);
- if (!fetchRemotePackagesTree())
- return status();
-
- QString errorMessage;
- if (checkComponentsForInstallation(components, errorMessage)) {
- if (!errorMessage.isEmpty())
- qCDebug(QInstaller::lcInstallerInstallLog).noquote().nospace() << errorMessage;
- if (d->calculateComponentsAndRun())
- qCDebug(QInstaller::lcInstallerInstallLog) << "Components installed successfully";
- } else {
- qCDebug(QInstaller::lcInstallerInstallLog).noquote().nospace() << errorMessage
- << "\nNo components available for installation with the current selection.";
- }
- return status();
+ return d->fetchComponentsAndInstall(components);
}
/*!
@@ -2918,6 +3273,21 @@ void PackageManagerCore::dropAdminRights()
}
/*!
+ Returns \c true if the installer has admin rights. For example, if the installer
+ was started with a root account, or the internal admin rights elevation is active.
+
+ Returns \c false otherwise.
+
+ \sa {installer::hasAdminRights}{installer.hasAdminRights}
+ \sa gainAdminRights()
+ \sa dropAdminRights()
+*/
+bool PackageManagerCore::hasAdminRights() const
+{
+ return AdminAuthorization::hasAdminRights() || RemoteClient::instance().isActive();
+}
+
+/*!
Sets checkAvailableSpace based on value of \a check.
*/
void PackageManagerCore::setCheckAvailableSpace(bool check)
@@ -2926,12 +3296,22 @@ void PackageManagerCore::setCheckAvailableSpace(bool check)
}
/*!
- Checks available disk space if the feature is not explicitly disabled. Informative
- text about space status can be retrieved by passing \a message parameter. Returns
+ * Returns informative text about disk space status
+ */
+QString PackageManagerCore::availableSpaceMessage() const
+{
+ return m_availableSpaceMessage;
+}
+
+/*!
+ Checks available disk space if the feature is not explicitly disabled. Returns
\c true if there is sufficient free space on installation and temporary volumes.
+
+ \sa availableSpaceMessage()
*/
-bool PackageManagerCore::checkAvailableSpace(QString &message) const
+bool PackageManagerCore::checkAvailableSpace()
{
+ m_availableSpaceMessage.clear();
const quint64 extraSpace = 256 * 1024 * 1024LL;
quint64 required(requiredDiskSpace());
quint64 tempRequired(requiredTemporaryDiskSpace());
@@ -2959,10 +3339,10 @@ bool PackageManagerCore::checkAvailableSpace(QString &message) const
<< humanReadableSize(repositorySize);
if (d->m_checkAvailableSpace) {
- const VolumeInfo tempVolume = VolumeInfo::fromPath(QDir::tempPath());
+ const VolumeInfo cacheVolume = VolumeInfo::fromPath(settings().localCachePath());
const VolumeInfo targetVolume = VolumeInfo::fromPath(value(scTargetDir));
- const quint64 tempVolumeAvailableSize = tempVolume.availableSize();
+ const quint64 cacheVolumeAvailableSize = cacheVolume.availableSize();
const quint64 installVolumeAvailableSize = targetVolume.availableSize();
// at the moment there is no better way to check this
@@ -2973,64 +3353,62 @@ bool PackageManagerCore::checkAvailableSpace(QString &message) const
return true;
}
- const bool tempOnSameVolume = (targetVolume == tempVolume);
- if (tempOnSameVolume) {
- qDebug() << "Tmp and install directories are on the same volume. Volume mount point:"
+ const bool cacheOnSameVolume = (targetVolume == cacheVolume);
+ if (cacheOnSameVolume) {
+ qDebug() << "Cache and install directories are on the same volume. Volume mount point:"
<< targetVolume.mountPath() << "Free space available:"
<< humanReadableSize(installVolumeAvailableSize);
} else {
- qDebug() << "Tmp is on a different volume than the installation directory. Tmp volume mount point:"
- << tempVolume.mountPath() << "Free space available:"
- << humanReadableSize(tempVolumeAvailableSize) << "Install volume mount point:"
+ qDebug() << "Cache is on a different volume than the installation directory. Cache volume mount point:"
+ << cacheVolume.mountPath() << "Free space available:"
+ << humanReadableSize(cacheVolumeAvailableSize) << "Install volume mount point:"
<< targetVolume.mountPath() << "Free space available:"
<< humanReadableSize(installVolumeAvailableSize);
}
- if (tempOnSameVolume && (installVolumeAvailableSize <= (required + tempRequired))) {
- message = tr("Not enough disk space to store temporary files and the "
+ if (cacheOnSameVolume && (installVolumeAvailableSize <= (required + tempRequired))) {
+ m_availableSpaceMessage = tr("Not enough disk space to store temporary files and the "
"installation. %1 are available, while the minimum required is %2.").arg(
humanReadableSize(installVolumeAvailableSize), humanReadableSize(required + tempRequired));
return false;
}
if (installVolumeAvailableSize < required) {
- message = tr("Not enough disk space to store all selected components! %1 are "
+ m_availableSpaceMessage = tr("Not enough disk space to store all selected components! %1 are "
"available, while the minimum required is %2.").arg(humanReadableSize(installVolumeAvailableSize),
humanReadableSize(required));
return false;
}
- if (tempVolumeAvailableSize < tempRequired) {
-#ifdef Q_OS_WIN
- static const QLatin1String scTmpVariable("\"TEMP\" or \"TMP\"");
-#elif defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
- static const QLatin1String scTmpVariable("\"TMPDIR\"");
-#endif
- message = tr("Not enough disk space to store temporary files! %1 are available, "
+ if (cacheVolumeAvailableSize < tempRequired) {
+ m_availableSpaceMessage = tr("Not enough disk space to store temporary files! %1 are available, "
"while the minimum required is %2. You may select another location for the "
- "temporary files by modifying the %3 environment variable and restarting the application.")
- .arg(humanReadableSize(tempVolumeAvailableSize), humanReadableSize(tempRequired), scTmpVariable);
+ "temporary files by modifying the local cache path from the installer settings.")
+ .arg(humanReadableSize(cacheVolumeAvailableSize), humanReadableSize(tempRequired));
return false;
}
if (installVolumeAvailableSize - required < 0.01 * targetVolume.size()) {
// warn for less than 1% of the volume's space being free
- message = tr("The volume you selected for installation seems to have sufficient space for "
+ m_availableSpaceMessage = tr("The volume you selected for installation seems to have sufficient space for "
"installation, but there will be less than 1% of the volume's space available afterwards.");
} else if (installVolumeAvailableSize - required < 100 * 1024 * 1024LL) {
// warn for less than 100MB being free
- message = tr("The volume you selected for installation seems to have sufficient "
+ m_availableSpaceMessage = tr("The volume you selected for installation seems to have sufficient "
"space for installation, but there will be less than 100 MB available afterwards.");
}
#ifdef Q_OS_WIN
if (isOfflineGenerator() && (required > UINT_MAX)) {
- message = tr("The estimated installer size %1 would exceed the supported executable "
+ m_availableSpaceMessage = tr("The estimated installer size %1 would exceed the supported executable "
"size limit of %2. The application may not be able to run.")
.arg(humanReadableSize(required), humanReadableSize(UINT_MAX));
}
#endif
}
- message = QString::fromLatin1("%1 %2").arg(message, tr("Installation will use %1 of disk space.")
+ m_availableSpaceMessage = QString::fromLatin1("%1 %2").arg(m_availableSpaceMessage,
+ (isOfflineGenerator()
+ ? tr("Created installer will use %1 of disk space.")
+ : tr("Installation will use %1 of disk space."))
.arg(humanReadableSize(requiredDiskSpace()))).simplified();
return true;
@@ -3089,6 +3467,10 @@ bool PackageManagerCore::killProcess(const QString &absoluteFilePath) const
}
/*!
+ \deprecated [4.6] Maintenance tool no longer automatically checks for all running processes
+ in the installation directory for CLI runs. To manually check for a process to stop, use
+ \l {component::addStopProcessForUpdateRequest}{component.addStopProcessForUpdateRequest} instead.
+
Sets additional \a processes that can run when
updating with the maintenance tool.
@@ -3100,6 +3482,10 @@ void PackageManagerCore::setAllowedRunningProcesses(const QStringList &processes
}
/*!
+ \deprecated [4.6] Maintenance tool no longer automatically checks for all running processes
+ in the installation directory for CLI runs. To manually check for a process to stop, use
+ \l {component::addStopProcessForUpdateRequest}{component.addStopProcessForUpdateRequest} instead.
+
Returns processes that are allowed to run when
updating with the maintenance tool.
@@ -3307,9 +3693,10 @@ bool PackageManagerCore::performOperation(const QString &name, const QStringList
*/
bool PackageManagerCore::versionMatches(const QString &version, const QString &requirement)
{
- QRegExp compEx(QLatin1String("([<=>]+)(.*)"));
- const QString comparator = compEx.exactMatch(requirement) ? compEx.cap(1) : QLatin1String("=");
- const QString ver = compEx.exactMatch(requirement) ? compEx.cap(2) : requirement;
+ static const QRegularExpression compEx(QLatin1String("^([<=>]+)(.*)$"));
+ const QRegularExpressionMatch match = compEx.match(requirement);
+ const QString comparator = match.hasMatch() ? match.captured(1) : QLatin1String("=");
+ const QString ver = match.hasMatch() ? match.captured(2) : requirement;
const bool allowEqual = comparator.contains(QLatin1Char('='));
const bool allowLess = comparator.contains(QLatin1Char('<'));
@@ -3405,6 +3792,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.
@@ -3603,6 +4001,14 @@ QString PackageManagerCore::offlineBinaryName() const
}
/*!
+ Add new \a source for looking component aliases.
+*/
+void PackageManagerCore::addAliasSource(const AliasSource &source)
+{
+ d->m_aliasSources.insert(source);
+}
+
+/*!
\sa {installer::setInstaller}{installer.setInstaller}
\sa isInstaller(), setUpdater(), setPackageManager()
*/
@@ -3661,6 +4067,7 @@ bool PackageManagerCore::isUninstaller() const
void PackageManagerCore::setUpdater()
{
d->m_magicBinaryMarker = BinaryContent::MagicUpdaterMarker;
+ d->m_componentByNameHash.clear();
emit installerBinaryMarkerChanged(d->m_magicBinaryMarker);
}
@@ -3681,6 +4088,7 @@ bool PackageManagerCore::isUpdater() const
void PackageManagerCore::setPackageManager()
{
d->m_magicBinaryMarker = BinaryContent::MagicPackageManagerMarker;
+ d->m_componentByNameHash.clear();
emit installerBinaryMarkerChanged(d->m_magicBinaryMarker);
}
@@ -3702,6 +4110,7 @@ bool PackageManagerCore::isPackageManager() const
void PackageManagerCore::setOfflineGenerator()
{
d->m_magicMarkerSupplement = BinaryContent::OfflineGenerator;
+ emit installerBinaryMarkerChanged(d->m_magicBinaryMarker);
}
/*!
@@ -3720,6 +4129,7 @@ bool PackageManagerCore::isOfflineGenerator() const
void PackageManagerCore::setPackageViewer()
{
d->m_magicMarkerSupplement = BinaryContent::PackageViewer;
+ emit installerBinaryMarkerChanged(d->m_magicBinaryMarker);
}
/*!
@@ -3733,6 +4143,17 @@ bool PackageManagerCore::isPackageViewer() const
}
/*!
+ Resets the binary marker supplement of the installer to \c Default.
+ The supplement enables or disables additional features on top of the binary
+ marker state (\c Installer, \c Updater, \c PackageManager, \c Uninstaller).
+*/
+void PackageManagerCore::resetBinaryMarkerSupplement()
+{
+ d->m_magicMarkerSupplement = BinaryContent::Default;
+ emit installerBinaryMarkerChanged(d->m_magicBinaryMarker);
+}
+
+/*!
Sets the installer magic binary marker based on \a magicMarker and
userSetBinaryMarker to \c true.
*/
@@ -3896,9 +4317,9 @@ bool PackageManagerCore::updateComponentData(struct Data &data, Component *compo
if (settings().allowUnstableComponents()) {
// Check if there are sha checksum mismatch. Component will still show in install tree
// but is unselectable.
- foreach (const QString packageName, d->m_metadataJob.shaMismatchPackages()) {
- if (packageName == component->name()) {
- const QString errorString = QLatin1String("SHA mismatch detected for component ") + packageName;
+ foreach (const QString pkgName, d->m_metadataJob.shaMismatchPackages()) {
+ if (pkgName == component->name()) {
+ const QString errorString = QLatin1String("SHA mismatch detected for component ") + pkgName;
d->m_pendingUnstableComponents.insert(component->name(),
QPair<Component::UnstableError, QString>(Component::ShaMismatch, errorString));
}
@@ -3907,33 +4328,18 @@ bool PackageManagerCore::updateComponentData(struct Data &data, Component *compo
component->setUninstalled();
const QString localPath = component->localTempPath();
- if (LoggingHandler::instance().verboseLevel() == LoggingHandler::Detailed) {
- static QString lastLocalPath;
- if (lastLocalPath != localPath)
- qCDebug(QInstaller::lcDeveloperBuild()) << "Url is:" << localPath;
- lastLocalPath = localPath;
- }
-
- const Repository repo = d->m_metadataJob.repositoryForDirectory(localPath);
+ const Repository repo = d->m_metadataJob.repositoryForCacheDirectory(localPath);
if (repo.isValid()) {
component->setRepositoryUrl(repo.url());
component->setValue(QLatin1String("username"), repo.username());
component->setValue(QLatin1String("password"), repo.password());
}
- // add downloadable archive from xml
- const QStringList downloadableArchives = data.package->data(scDownloadableArchives).toString()
- .split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
-
- if (component->isFromOnlineRepository()) {
- foreach (const QString downloadableArchive, downloadableArchives)
- component->addDownloadableArchive(downloadableArchive);
- }
-
- const QStringList componentsToReplace = data.package->data(scReplaces).toString()
- .split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
+ if (component->isFromOnlineRepository())
+ component->addDownloadableArchives(data.package->data(scDownloadableArchives).toString());
+ const QStringList componentsToReplace = QInstaller::splitStringWithComma(data.package->data(scReplaces).toString());
if (!componentsToReplace.isEmpty()) {
// Store the component (this is a component that replaces others) and all components that
// this one will replace.
@@ -4026,134 +4432,148 @@ bool PackageManagerCore::fetchAllPackages(const PackagesList &remotes, const Loc
{
emit startAllComponentsReset();
- d->clearAllComponentLists();
- QHash<QString, QInstaller::Component*> allComponents;
+ try {
+ d->clearAllComponentLists();
+ QHash<QString, QInstaller::Component*> allComponents;
- Data data;
- data.components = &allComponents;
- data.installedPackages = &locals;
+ Data data;
+ data.components = &allComponents;
+ data.installedPackages = &locals;
- QMap<QString, QString> remoteTreeNameComponents;
- QMap<QString, QString> allTreeNameComponents;
+ QMap<QString, QString> remoteTreeNameComponents;
+ QMap<QString, QString> allTreeNameComponents;
- std::function<bool(PackagesList *, bool)> loadRemotePackages;
- loadRemotePackages = [&](PackagesList *treeNamePackages, bool firstRun) -> bool {
- foreach (Package *const package, (firstRun ? remotes : *treeNamePackages)) {
- if (d->statusCanceledOrFailed())
- return false;
+ std::function<bool(PackagesList *, bool)> loadRemotePackages;
+ loadRemotePackages = [&](PackagesList *treeNamePackages, bool firstRun) -> bool {
+ foreach (Package *const package, (firstRun ? remotes : *treeNamePackages)) {
+ if (d->statusCanceledOrFailed())
+ return false;
- if (!ProductKeyCheck::instance()->isValidPackage(package->data(scName).toString()))
- continue;
+ if (!ProductKeyCheck::instance()->isValidPackage(package->data(scName).toString()))
+ continue;
- if (firstRun && !package->data(scTreeName)
- .value<QPair<QString, bool>>().first.isEmpty()) {
- // Package has a tree name, leave for later
- treeNamePackages->append(package);
- continue;
- }
+ if (firstRun && !package->data(scTreeName)
+ .value<QPair<QString, bool>>().first.isEmpty()) {
+ // Package has a tree name, leave for later
+ treeNamePackages->append(package);
+ continue;
+ }
- QScopedPointer<QInstaller::Component> remoteComponent(new QInstaller::Component(this));
- data.package = package;
- remoteComponent->loadDataFromPackage(*package);
- if (updateComponentData(data, remoteComponent.data())) {
- // Create a list where is name and treename. Repo can contain a package with
- // a different treename of component which is already installed. We don't want
- // to move already installed local packages.
- const QString treeName = remoteComponent->value(scTreeName);
- if (!treeName.isEmpty())
- remoteTreeNameComponents.insert(remoteComponent->name(), treeName);
- const QString name = remoteComponent->treeName();
- allComponents.insert(name, remoteComponent.take());
+ std::unique_ptr<QInstaller::Component> remoteComponent(new QInstaller::Component(this));
+ data.package = package;
+ remoteComponent->loadDataFromPackage(*package);
+ if (updateComponentData(data, remoteComponent.get())) {
+ // Create a list where is name and treename. Repo can contain a package with
+ // a different treename of component which is already installed. We don't want
+ // to move already installed local packages.
+ const QString treeName = remoteComponent->value(scTreeName);
+ if (!treeName.isEmpty())
+ remoteTreeNameComponents.insert(remoteComponent->name(), treeName);
+ const QString name = remoteComponent->treeName();
+ allComponents.insert(name, remoteComponent.release());
+ }
}
+ // Second pass with leftover packages
+ return firstRun ? loadRemotePackages(treeNamePackages, false) : true;
+ };
+
+ {
+ // Loading remote package data is performed in two steps: first, components without
+ // - and then components with tree names. This is to ensure components with tree
+ // names do not replace other components when registering fails to a name conflict.
+ PackagesList treeNamePackagesTmp;
+ if (!loadRemotePackages(&treeNamePackagesTmp, true))
+ return false;
}
- // Second pass with leftover packages
- return firstRun ? loadRemotePackages(treeNamePackages, false) : true;
- };
+ allTreeNameComponents = remoteTreeNameComponents;
- {
- // Loading remote package data is performed in two steps: first, components without
- // - and then components with tree names. This is to ensure components with tree
- // names do not replace other components when registering fails to a name conflict.
- PackagesList treeNamePackagesTmp;
- if (!loadRemotePackages(&treeNamePackagesTmp, true))
- return false;
- }
- allTreeNameComponents = remoteTreeNameComponents;
+ foreach (auto &package, locals) {
+ if (package.virtualComp && package.autoDependencies.isEmpty()) {
+ if (!d->m_localVirtualComponents.contains(package.name))
+ d->m_localVirtualComponents.append(package.name);
+ }
- foreach (auto &package, locals) {
- if (package.virtualComp && package.autoDependencies.isEmpty()) {
- if (!d->m_localVirtualComponents.contains(package.name))
- d->m_localVirtualComponents.append(package.name);
- }
+ std::unique_ptr<QInstaller::Component> localComponent(new QInstaller::Component(this));
+ localComponent->loadDataFromPackage(package);
+ const QString name = localComponent->treeName();
+
+ // 1. Component has a treename in local but not in remote, add with local treename
+ if (!remoteTreeNameComponents.contains(localComponent->name()) && !localComponent->value(scTreeName).isEmpty()) {
+ delete allComponents.take(localComponent->name());
+ // 2. Component has different treename in local and remote, add with local treename
+ } else if (remoteTreeNameComponents.contains(localComponent->name())) {
+ const QString remoteTreeName = remoteTreeNameComponents.value(localComponent->name());
+ const QString localTreeName = localComponent->value(scTreeName);
+ if (remoteTreeName != localTreeName) {
+ delete allComponents.take(remoteTreeNameComponents.value(localComponent->name()));
+ } else {
+ // 3. Component has same treename in local and remote, don't add the component again.
+ continue;
+ }
+ // 4. Component does not have treename in local or remote, don't add the component again.
+ } else if (allComponents.contains(localComponent->name())) {
+ Component *const component = allComponents.value(localComponent->name());
+ if (component->value(scTreeName).isEmpty() && localComponent->value(scTreeName).isEmpty())
+ continue;
+ }
+ // 5. Remote has treename for a different component that is already reserved
+ // by this local component, Or, remote adds component without treename
+ // but it conflicts with a local treename.
+ if (allComponents.contains(name)) {
+ const QString key = remoteTreeNameComponents.key(name);
+ qCritical() << "Cannot register component" << (key.isEmpty() ? name : key)
+ << "with name" << name << "! Component with identifier" << name
+ << "already exists.";
+
+ if (!key.isEmpty())
+ allTreeNameComponents.remove(key);
+
+ // Try to re-add the remote component as unstable
+ if (!key.isEmpty() && !allComponents.contains(key) && settings().allowUnstableComponents()) {
+ qCDebug(lcInstallerInstallLog)
+ << "Registering component with the original indetifier:" << key;
+
+ Component *component = allComponents.take(name);
+ component->removeValue(scTreeName);
+ const QString errorString = QLatin1String("Tree name conflicts with an existing indentifier");
+ d->m_pendingUnstableComponents.insert(component->name(),
+ QPair<Component::UnstableError, QString>(Component::InvalidTreeName, errorString));
- QScopedPointer<QInstaller::Component> localComponent(new QInstaller::Component(this));
- localComponent->loadDataFromPackage(package);
- const QString name = localComponent->treeName();
-
- // 1. Component has a treename in local but not in remote, add with local treename
- if (!remoteTreeNameComponents.contains(localComponent->name()) && !localComponent->value(scTreeName).isEmpty()) {
- delete allComponents.take(localComponent->name());
- // 2. Component has different treename in local and remote, add with local treename
- } else if (remoteTreeNameComponents.contains(localComponent->name())) {
- const QString remoteTreeName = remoteTreeNameComponents.value(localComponent->name());
- const QString localTreeName = localComponent->value(scTreeName);
- if (remoteTreeName != localTreeName) {
- delete allComponents.take(remoteTreeNameComponents.value(localComponent->name()));
- } else {
- // 3. Component has same treename in local and remote, don't add the component again.
- continue;
+ allComponents.insert(key, component);
+ } else {
+ delete allComponents.take(name);
+ }
}
- // 4. Component does not have treename in local or remote, don't add the component again.
- } else if (allComponents.contains(localComponent->name())) {
- Component *const component = allComponents.value(localComponent->name());
- if (component->value(scTreeName).isEmpty() && localComponent->value(scTreeName).isEmpty())
- continue;
+
+ const QString treeName = localComponent->value(scTreeName);
+ if (!treeName.isEmpty())
+ allTreeNameComponents.insert(localComponent->name(), treeName);
+ allComponents.insert(name, localComponent.release());
}
- // 5. Remote has treename for a different component that is already reserved
- // by this local component, Or, remote adds component without treename
- // but it conflicts with a local treename.
- if (allComponents.contains(name)) {
- const QString key = remoteTreeNameComponents.key(name);
- qCritical() << "Cannot register component" << (key.isEmpty() ? name : key)
- << "with name" << name << "! Component with identifier" << name
- << "already exists.";
-
- if (!key.isEmpty())
- allTreeNameComponents.remove(key);
-
- // Try to re-add the remote component as unstable
- if (!key.isEmpty() && !allComponents.contains(key) && settings().allowUnstableComponents()) {
- qCDebug(lcInstallerInstallLog)
- << "Registering component with the original indetifier:" << key;
- Component *component = allComponents.take(name);
- component->removeValue(scTreeName);
- const QString errorString = QLatin1String("Tree name conflicts with an existing indentifier");
- d->m_pendingUnstableComponents.insert(component->name(),
- QPair<Component::UnstableError, QString>(Component::InvalidTreeName, errorString));
+ // store all components that got a replacement
+ storeReplacedComponents(allComponents, data, &allTreeNameComponents);
- allComponents.insert(key, component);
- } else {
- delete allComponents.take(name);
- }
- }
+ // Move children of treename components
+ createAutoTreeNames(allComponents, allTreeNameComponents);
- const QString treeName = localComponent->value(scTreeName);
- if (!treeName.isEmpty())
- allTreeNameComponents.insert(localComponent->name(), treeName);
- allComponents.insert(name, localComponent.take());
- }
+ if (!d->buildComponentTree(allComponents, true))
+ return false;
- // store all components that got a replacement
- storeReplacedComponents(allComponents, data, &allTreeNameComponents);
+ d->commitPendingUnstableComponents();
- // Move children of treename components
- createAutoTreeNames(allComponents, allTreeNameComponents);
+ if (!d->buildComponentAliases())
+ return false;
- if (!d->buildComponentTree(allComponents, true))
- return false;
+ } catch (const Error &error) {
+ d->clearAllComponentLists();
+ d->setStatus(PackageManagerCore::Failure, error.message());
- d->commitPendingUnstableComponents();
+ // TODO: make sure we remove all message boxes inside the library at some point.
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("Error"),
+ tr("Error"), error.message());
+ return false;
+ }
emit finishAllComponentsReset(d->m_rootComponents);
return true;
@@ -4163,104 +4583,105 @@ bool PackageManagerCore::fetchUpdaterPackages(const PackagesList &remotes, const
{
emit startUpdaterComponentsReset();
- d->clearUpdaterComponentLists();
- QHash<QString, QInstaller::Component *> components;
+ try {
+ d->clearUpdaterComponentLists();
+ QHash<QString, QInstaller::Component *> components;
- Data data;
- data.components = &components;
- data.installedPackages = &locals;
+ Data data;
+ data.components = &components;
+ data.installedPackages = &locals;
- setFoundEssentialUpdate(false);
- LocalPackagesMap installedPackages = locals;
- QStringList replaceMes;
+ setFoundEssentialUpdate(false);
+ LocalPackagesMap installedPackages = locals;
+ QStringList replaceMes;
- foreach (Package *const update, remotes) {
- if (d->statusCanceledOrFailed())
- return false;
+ foreach (Package *const update, remotes) {
+ if (d->statusCanceledOrFailed())
+ return false;
- if (!ProductKeyCheck::instance()->isValidPackage(update->data(scName).toString()))
- continue;
+ if (!ProductKeyCheck::instance()->isValidPackage(update->data(scName).toString()))
+ continue;
- QScopedPointer<QInstaller::Component> component(new QInstaller::Component(this));
- data.package = update;
- component->loadDataFromPackage(*update);
- if (updateComponentData(data, component.data())) {
- // Keep a reference so we can resolve dependencies during update.
- d->m_updaterComponentsDeps.append(component.take());
-
-// const QString isNew = update->data(scNewComponent).toString();
-// if (isNew.toLower() != scTrue)
-// continue;
-
- const QString &name = d->m_updaterComponentsDeps.last()->name();
- const QString replaces = data.package->data(scReplaces).toString();
- installedPackages.take(name); // remove from local installed packages
-
- bool isValidUpdate = locals.contains(name);
- if (!replaces.isEmpty()) {
- const QStringList possibleNames = replaces.split(QInstaller::commaRegExp(),
- Qt::SkipEmptyParts);
- foreach (const QString &possibleName, possibleNames) {
- if (locals.contains(possibleName)) {
- isValidUpdate = true;
- replaceMes << possibleName;
+ std::unique_ptr<QInstaller::Component> component(new QInstaller::Component(this));
+ data.package = update;
+ component->loadDataFromPackage(*update);
+ if (updateComponentData(data, component.get())) {
+ // Keep a reference so we can resolve dependencies during update.
+ d->m_updaterComponentsDeps.append(component.release());
+
+ // const QString isNew = update->data(scNewComponent).toString();
+ // if (isNew.toLower() != scTrue)
+ // continue;
+
+ const QString &name = d->m_updaterComponentsDeps.last()->name();
+ const QString replaces = data.package->data(scReplaces).toString();
+ installedPackages.take(name); // remove from local installed packages
+
+ bool isValidUpdate = locals.contains(name);
+ if (!replaces.isEmpty()) {
+ const QStringList possibleNames = replaces.split(QInstaller::commaRegExp(),
+ Qt::SkipEmptyParts);
+ foreach (const QString &possibleName, possibleNames) {
+ if (locals.contains(possibleName)) {
+ isValidUpdate = true;
+ replaceMes << possibleName;
+ }
}
}
- }
- // break if the update is not valid and if it's not the maintenance tool (we might get an update
- // for the maintenance tool even if it's not currently installed - possible offline installation)
- if (!isValidUpdate && (update->data(scEssential, scFalse).toString().toLower() == scFalse))
- continue; // Update for not installed package found, skip it.
+ // break if the update is not valid and if it's not the maintenance tool (we might get an update
+ // for the maintenance tool even if it's not currently installed - possible offline installation)
+ if (!isValidUpdate && (update->data(scEssential, scFalse).toString().toLower() == scFalse))
+ continue; // Update for not installed package found, skip it.
- const LocalPackage &localPackage = locals.value(name);
- if (!d->packageNeedsUpdate(localPackage, update))
- continue;
- // It is quite possible that we may have already installed the update. Lets check the last
- // update date of the package and the release date of the update. This way we can compare and
- // figure out if the update has been installed or not.
- const QDate updateDate = update->data(scReleaseDate).toDate();
- if (localPackage.lastUpdateDate > updateDate)
- continue;
+ const LocalPackage &localPackage = locals.value(name);
+ if (!d->packageNeedsUpdate(localPackage, update))
+ continue;
+ // It is quite possible that we may have already installed the update. Lets check the last
+ // update date of the package and the release date of the update. This way we can compare and
+ // figure out if the update has been installed or not.
+ const QDate updateDate = update->data(scReleaseDate).toDate();
+ if (localPackage.lastUpdateDate > updateDate)
+ continue;
- if (update->data(scEssential, scFalse).toString().toLower() == scTrue ||
- update->data(scForcedUpdate, scFalse).toString().toLower() == scTrue) {
- setFoundEssentialUpdate(true);
- }
+ if (update->data(scEssential, scFalse).toString().toLower() == scTrue ||
+ update->data(scForcedUpdate, scFalse).toString().toLower() == scTrue) {
+ setFoundEssentialUpdate(true);
+ }
- // this is not a dependency, it is a real update
- components.insert(name, d->m_updaterComponentsDeps.takeLast());
+ // this is not a dependency, it is a real update
+ components.insert(name, d->m_updaterComponentsDeps.takeLast());
+ }
}
- }
- QHash<QString, QInstaller::Component *> localReplaceMes;
- foreach (const QString &key, installedPackages.keys()) {
- QInstaller::Component *component = new QInstaller::Component(this);
- component->loadDataFromPackage(installedPackages.value(key));
- d->m_updaterComponentsDeps.append(component);
- }
-
- foreach (const QString &key, locals.keys()) {
- LocalPackage package = locals.value(key);
- if (package.virtualComp && package.autoDependencies.isEmpty()) {
- if (!d->m_localVirtualComponents.contains(package.name))
- d->m_localVirtualComponents.append(package.name);
- }
- // Keep a list of local components that should be replaced
- // Remove from components list - we don't want to update the component
- // as it is replaced by other component
- if (replaceMes.contains(key)) {
+ QHash<QString, QInstaller::Component *> localReplaceMes;
+ foreach (const QString &key, installedPackages.keys()) {
QInstaller::Component *component = new QInstaller::Component(this);
- component->loadDataFromPackage(locals.value(key));
- localReplaceMes.insert(component->name(), component);
- delete components.take(component->name());
+ component->loadDataFromPackage(installedPackages.value(key));
+ d->m_updaterComponentsDeps.append(component);
+ }
+
+ foreach (const QString &key, locals.keys()) {
+ LocalPackage package = locals.value(key);
+ if (package.virtualComp && package.autoDependencies.isEmpty()) {
+ if (!d->m_localVirtualComponents.contains(package.name))
+ d->m_localVirtualComponents.append(package.name);
+ }
+ // Keep a list of local components that should be replaced
+ // Remove from components list - we don't want to update the component
+ // as it is replaced by other component
+ if (replaceMes.contains(key)) {
+ QInstaller::Component *component = new QInstaller::Component(this);
+ component->loadDataFromPackage(locals.value(key));
+ localReplaceMes.insert(component->name(), component);
+ delete components.take(component->name());
+ }
}
- }
- // store all components that got a replacement, but do not modify the components list
- storeReplacedComponents(localReplaceMes.unite(components), data);
+ // store all components that got a replacement, but do not modify the components list
+ localReplaceMes.insert(components);
+ storeReplacedComponents(localReplaceMes, data);
- try {
if (!components.isEmpty()) {
// append all components w/o parent to the direct list
foreach (QInstaller::Component *component, components) {
@@ -4268,22 +4689,26 @@ bool PackageManagerCore::fetchUpdaterPackages(const PackagesList &remotes, const
}
// after everything is set up, load the scripts
+ if (!d->loadComponentScripts(components))
+ return false;
+
foreach (QInstaller::Component *component, components) {
if (d->statusCanceledOrFailed())
return false;
- component->loadComponentScript();
- if (!component->isUnstable() && component->autoDependencies().isEmpty())
+ if (!component->isUnstable())
component->setCheckState(Qt::Checked);
}
+ // even for possible dependencies we need to load the scripts for example to get archives
+ if (!d->loadComponentScripts(d->m_updaterComponentsDeps))
+ return false;
+
// after everything is set up, check installed components
foreach (QInstaller::Component *component, d->m_updaterComponentsDeps) {
if (d->statusCanceledOrFailed())
return false;
- // even for possible dependency we need to load the script for example to get archives
- component->loadComponentScript();
- if (component->isInstalled() && !component->autoDependencies().isEmpty()) {
+ if (component->isInstalled()) {
// since we do not put them into the model, which would force a update of e.g. tri state
// components, we have to check all installed components ourselves
if (!component->isUnstable())
@@ -4344,14 +4769,19 @@ void PackageManagerCore::createAutoTreeNames(QHash<QString, Component *> &compon
if (treeNameComponents.isEmpty())
return;
- QHash<QString, Component *> componentsTemp = components;
- for (auto *component : qAsConst(components)) {
+ QHash<QString, Component *> componentsTemp;
+ QMutableHashIterator<QString, Component* > i(components);
+ while (i.hasNext()) {
+ i.next();
+ Component *component = i.value();
if (component->treeName() != component->name()) // already handled
continue;
QString newName;
// Check treename candidates, keep the name closest to a leaf component
- for (auto &name : treeNameComponents.keys()) {
+ QMap<QString, QString>::const_iterator j;
+ for (j = treeNameComponents.begin(); j != treeNameComponents.end(); ++j) {
+ const QString name = j.key();
if (!component->name().startsWith(name))
continue;
@@ -4359,8 +4789,8 @@ void PackageManagerCore::createAutoTreeNames(QHash<QString, Component *> &compon
if (!(parent && parent->treeNameMoveChildren()))
continue; // TreeName only applied to parent
- if (newName.split(QLatin1Char('.'), QString::SkipEmptyParts).count()
- > name.split(QLatin1Char('.'), QString::SkipEmptyParts).count()) {
+ if (newName.split(QLatin1Char('.'), Qt::SkipEmptyParts).count()
+ > name.split(QLatin1Char('.'), Qt::SkipEmptyParts).count()) {
continue;
}
newName = name;
@@ -4386,16 +4816,16 @@ void PackageManagerCore::createAutoTreeNames(QHash<QString, Component *> &compon
d->m_pendingUnstableComponents.insert(component->name(),
QPair<Component::UnstableError, QString>(Component::InvalidTreeName, errorString));
} else {
- componentsTemp.remove(componentsTemp.key(component));
+ i.remove();
}
continue;
}
component->setValue(scAutoTreeName, treeName);
- componentsTemp.remove(componentsTemp.key(component));
+ i.remove();
componentsTemp.insert(treeName, component);
}
- components = componentsTemp;
+ components.insert(componentsTemp);
}
void PackageManagerCore::restoreCheckState()
@@ -4462,10 +4892,7 @@ ComponentModel *PackageManagerCore::componentModel(PackageManagerCore *core, con
ComponentModel::tr("Release Date"));
model->setHeaderData(ComponentModelHelper::UncompressedSizeColumn, Qt::Horizontal,
ComponentModel::tr("Size"));
- connect(model, &ComponentModel::modelCheckStateChanged,
- this, &PackageManagerCore::componentsToInstallNeedsRecalculation);
- connect(model, &ComponentModel::componentsCheckStateChanged,
- this, &PackageManagerCore::calculateUserSelectedComponentsToInstall);
+
return model;
}