summaryrefslogtreecommitdiffstats
path: root/src/libs/installer
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/installer')
-rw-r--r--src/libs/installer/abstracttask.h2
-rw-r--r--src/libs/installer/binaryformat.cpp11
-rw-r--r--src/libs/installer/binaryformat.h6
-rw-r--r--src/libs/installer/binaryformatengine.cpp9
-rw-r--r--src/libs/installer/binaryformatengine.h7
-rw-r--r--src/libs/installer/calculatorbase.cpp76
-rw-r--r--src/libs/installer/calculatorbase.h85
-rw-r--r--src/libs/installer/commandlineparser.cpp15
-rw-r--r--src/libs/installer/component.cpp268
-rw-r--r--src/libs/installer/component.h9
-rw-r--r--src/libs/installer/component_p.cpp5
-rw-r--r--src/libs/installer/component_p.h1
-rw-r--r--src/libs/installer/componentalias.cpp649
-rw-r--r--src/libs/installer/componentalias.h160
-rw-r--r--src/libs/installer/componentmodel.cpp73
-rw-r--r--src/libs/installer/componentmodel.h13
-rw-r--r--src/libs/installer/componentselectionpage_p.cpp385
-rw-r--r--src/libs/installer/componentselectionpage_p.h34
-rw-r--r--src/libs/installer/concurrentoperationrunner.cpp2
-rw-r--r--src/libs/installer/constants.h73
-rw-r--r--src/libs/installer/copydirectoryoperation.cpp2
-rw-r--r--src/libs/installer/copyfiletask.cpp4
-rw-r--r--src/libs/installer/createdesktopentryoperation.cpp14
-rw-r--r--src/libs/installer/createlocalrepositoryoperation.cpp2
-rw-r--r--src/libs/installer/createshortcutoperation.cpp4
-rw-r--r--src/libs/installer/customcombobox.cpp54
-rw-r--r--src/libs/installer/customcombobox.h48
-rw-r--r--src/libs/installer/downloadarchivesjob.cpp72
-rw-r--r--src/libs/installer/downloadarchivesjob.h11
-rw-r--r--src/libs/installer/downloadfiletask.cpp22
-rw-r--r--src/libs/installer/downloadfiletask_p.h2
-rw-r--r--src/libs/installer/elevatedexecuteoperation.cpp57
-rw-r--r--src/libs/installer/environmentvariablesoperation.cpp17
-rw-r--r--src/libs/installer/extractarchiveoperation.cpp27
-rw-r--r--src/libs/installer/fakestopprocessforupdateoperation.cpp6
-rw-r--r--src/libs/installer/fileutils.cpp8
-rw-r--r--src/libs/installer/genericdatacache.cpp60
-rw-r--r--src/libs/installer/genericdatacache.h9
-rw-r--r--src/libs/installer/globals.cpp67
-rw-r--r--src/libs/installer/globals.h9
-rw-r--r--src/libs/installer/globalsettingsoperation.cpp2
-rw-r--r--src/libs/installer/installer.pro12
-rw-r--r--src/libs/installer/installer_global.h8
-rw-r--r--src/libs/installer/installercalculator.cpp312
-rw-r--r--src/libs/installer/installercalculator.h60
-rw-r--r--src/libs/installer/installiconsoperation.cpp4
-rw-r--r--src/libs/installer/lib7z_create.h3
-rw-r--r--src/libs/installer/lib7z_facade.cpp2
-rw-r--r--src/libs/installer/libarchivearchive.cpp2
-rw-r--r--src/libs/installer/libarchivewrapper_p.cpp16
-rw-r--r--src/libs/installer/licenseoperation.cpp16
-rw-r--r--src/libs/installer/link.cpp2
-rw-r--r--src/libs/installer/loggingutils.cpp42
-rw-r--r--src/libs/installer/loggingutils.h2
-rw-r--r--src/libs/installer/messageboxhandler.cpp5
-rw-r--r--src/libs/installer/metadata.cpp161
-rw-r--r--src/libs/installer/metadata.h16
-rw-r--r--src/libs/installer/metadatacache.cpp67
-rw-r--r--src/libs/installer/metadatacache.h46
-rw-r--r--src/libs/installer/metadatajob.cpp312
-rw-r--r--src/libs/installer/metadatajob.h21
-rw-r--r--src/libs/installer/metadatajob_p.h57
-rw-r--r--src/libs/installer/observer.cpp6
-rw-r--r--src/libs/installer/observer.h2
-rw-r--r--src/libs/installer/packagemanagercore.cpp892
-rw-r--r--src/libs/installer/packagemanagercore.h84
-rw-r--r--src/libs/installer/packagemanagercore_p.cpp543
-rw-r--r--src/libs/installer/packagemanagercore_p.h46
-rw-r--r--src/libs/installer/packagemanagercoredata.cpp7
-rw-r--r--src/libs/installer/packagemanagergui.cpp282
-rw-r--r--src/libs/installer/packagemanagergui.h10
-rw-r--r--src/libs/installer/packagesource.cpp6
-rw-r--r--src/libs/installer/packagesource.h8
-rw-r--r--src/libs/installer/performinstallationform.cpp6
-rw-r--r--src/libs/installer/permissionsettings.cpp26
-rw-r--r--src/libs/installer/permissionsettings.h17
-rw-r--r--src/libs/installer/productkeycheck.cpp29
-rw-r--r--src/libs/installer/productkeycheck.h9
-rw-r--r--src/libs/installer/progresscoordinator.cpp51
-rw-r--r--src/libs/installer/progresscoordinator.h6
-rw-r--r--src/libs/installer/protocol.h4
-rw-r--r--src/libs/installer/qinstallerglobal.cpp8
-rw-r--r--src/libs/installer/qprocesswrapper.cpp33
-rw-r--r--src/libs/installer/qprocesswrapper.h4
-rw-r--r--src/libs/installer/qsettingswrapper.cpp92
-rw-r--r--src/libs/installer/qsettingswrapper.h30
-rw-r--r--src/libs/installer/qtpatch.cpp6
-rw-r--r--src/libs/installer/registerfiletypeoperation.cpp2
-rw-r--r--src/libs/installer/remoteclient_p.h5
-rw-r--r--src/libs/installer/remotefileengine.cpp30
-rw-r--r--src/libs/installer/remotefileengine.h10
-rw-r--r--src/libs/installer/remoteobject.cpp7
-rw-r--r--src/libs/installer/remoteobject.h91
-rw-r--r--src/libs/installer/remoteserverconnection.cpp24
-rw-r--r--src/libs/installer/remoteserverconnection_p.h2
-rw-r--r--src/libs/installer/repository.cpp61
-rw-r--r--src/libs/installer/repository.h14
-rw-r--r--src/libs/installer/repositorycategory.cpp10
-rw-r--r--src/libs/installer/repositorycategory.h7
-rw-r--r--src/libs/installer/resources/installer.qrc1
-rw-r--r--src/libs/installer/resources/qt/etc/qt.conf0
-rw-r--r--src/libs/installer/runextensions.h374
-rw-r--r--src/libs/installer/scriptengine.cpp80
-rw-r--r--src/libs/installer/scriptengine.h3
-rw-r--r--src/libs/installer/scriptengine_p.h35
-rw-r--r--src/libs/installer/settings.cpp30
-rw-r--r--src/libs/installer/settings.h6
-rw-r--r--src/libs/installer/settingsoperation.cpp27
-rw-r--r--src/libs/installer/simplemovefileoperation.cpp2
-rw-r--r--src/libs/installer/sysinfo_win.cpp4
-rw-r--r--src/libs/installer/systeminfo.cpp30
-rw-r--r--src/libs/installer/systeminfo.h5
-rw-r--r--src/libs/installer/uninstallercalculator.cpp168
-rw-r--r--src/libs/installer/uninstallercalculator.h43
-rw-r--r--src/libs/installer/utils.cpp4
115 files changed, 4581 insertions, 2247 deletions
diff --git a/src/libs/installer/abstracttask.h b/src/libs/installer/abstracttask.h
index 7daff848f..5ee23bef5 100644
--- a/src/libs/installer/abstracttask.h
+++ b/src/libs/installer/abstracttask.h
@@ -31,7 +31,9 @@
#include "runextensions.h"
+#include <QException>
#include <QFutureInterface>
+#include <QVariant>
namespace QInstaller {
diff --git a/src/libs/installer/binaryformat.cpp b/src/libs/installer/binaryformat.cpp
index 9ed7742db..ad80ba7bb 100644
--- a/src/libs/installer/binaryformat.cpp
+++ b/src/libs/installer/binaryformat.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -153,12 +153,19 @@ void Resource::setName(const QByteArray &name)
Opens a resource in QIODevice::ReadOnly mode. The function returns \c true
if successful.
*/
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
bool Resource::open()
+#else
+bool Resource::open(std::optional<QFile::Permissions> permissions)
+#endif
{
if (isOpen())
return false;
-
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
if (!m_file.open(QIODevice::ReadOnly | QIODevice::Unbuffered)) {
+#else
+ if (!m_file.open(QIODevice::ReadOnly | QIODevice::Unbuffered, permissions)) {
+#endif
setErrorString(m_file.errorString());
return false;
}
diff --git a/src/libs/installer/binaryformat.h b/src/libs/installer/binaryformat.h
index 26d510530..e7505a341 100644
--- a/src/libs/installer/binaryformat.h
+++ b/src/libs/installer/binaryformat.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -58,7 +58,11 @@ public:
Resource(const QString &path, const Range<qint64> &segment);
~Resource();
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
bool open();
+#else
+ bool open(std::optional<QFile::Permissions> permissions = std::nullopt);
+#endif
void close() override;
bool seek(qint64 pos) override;
diff --git a/src/libs/installer/binaryformatengine.cpp b/src/libs/installer/binaryformatengine.cpp
index 681e6db79..7f00c8d47 100644
--- a/src/libs/installer/binaryformatengine.cpp
+++ b/src/libs/installer/binaryformatengine.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -127,9 +127,16 @@ bool BinaryFormatEngine::close()
/*!
\internal
*/
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
bool BinaryFormatEngine::open(QIODevice::OpenMode mode)
+#else
+bool BinaryFormatEngine::open(QIODevice::OpenMode mode, std::optional<QFile::Permissions> permissions)
+#endif
{
Q_UNUSED(mode)
+#if QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)
+ Q_UNUSED(permissions)
+#endif
return m_resource.isNull() ? false : m_resource->open();
}
diff --git a/src/libs/installer/binaryformatengine.h b/src/libs/installer/binaryformatengine.h
index bf72e5f1f..9321e9d9c 100644
--- a/src/libs/installer/binaryformatengine.h
+++ b/src/libs/installer/binaryformatengine.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -47,7 +47,12 @@ public:
bool copy(const QString &newName) override;
bool close() override;
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
bool open(QIODevice::OpenMode mode) override;
+#else
+ bool open(QIODevice::OpenMode mode,
+ std::optional<QFile::Permissions> permissions = std::nullopt) override;
+#endif
qint64 pos() const override;
qint64 read(char *data, qint64 maxlen) override;
bool seek(qint64 offset) override;
diff --git a/src/libs/installer/calculatorbase.cpp b/src/libs/installer/calculatorbase.cpp
new file mode 100644
index 000000000..4f1732677
--- /dev/null
+++ b/src/libs/installer/calculatorbase.cpp
@@ -0,0 +1,76 @@
+/**************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "calculatorbase.h"
+
+#include "component.h"
+
+namespace QInstaller {
+
+CalculatorBase::CalculatorBase(PackageManagerCore *core)
+ : m_core(core)
+{
+}
+
+CalculatorBase::~CalculatorBase()
+{
+}
+
+void CalculatorBase::insertResolution(Component *component, const Resolution resolutionType
+ , const QString &referencedComponent)
+{
+ // Keep the first reason
+ if (m_componentNameResolutionHash.contains(component->name()))
+ return;
+
+ m_componentNameResolutionHash.insert(component->name(),
+ QPair<Resolution, QString>(resolutionType, referencedComponent));
+}
+
+QList<Component *> CalculatorBase::resolvedComponents() const
+{
+ return m_resolvedComponents;
+}
+
+CalculatorBase::Resolution CalculatorBase::resolutionType(Component *component) const
+{
+ return m_componentNameResolutionHash.value(component->name()).first;
+}
+
+QString CalculatorBase::error() const
+{
+ return m_errorString;
+}
+
+QString CalculatorBase::referencedComponent(Component *component) const
+{
+ return m_componentNameResolutionHash.value(component->name()).second;
+}
+
+} // namespace QInstaller
+
diff --git a/src/libs/installer/calculatorbase.h b/src/libs/installer/calculatorbase.h
new file mode 100644
index 000000000..351658f99
--- /dev/null
+++ b/src/libs/installer/calculatorbase.h
@@ -0,0 +1,85 @@
+/**************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef CALCULATORBASE_H
+#define CALCULATORBASE_H
+
+#include "installer_global.h"
+
+#include <QList>
+#include <QString>
+#include <QMetaEnum>
+
+namespace QInstaller {
+
+class Component;
+class PackageManagerCore;
+
+class INSTALLER_EXPORT CalculatorBase
+{
+public:
+ enum class Resolution {
+ Selected = 0, // "Selected Component(s) without Dependencies" / "Deselected Component(s)"
+ Replaced, // "Component(s) replaced by other components"
+ VirtualDependent, // "No dependencies to virtual component"
+ Dependent, // "Added as dependency for %1." / "Removed as dependency component is removed"
+ Automatic, // "Component(s) added as automatic dependencies" / "Removed as autodependency component is removed"
+ Resolved, // "Component(s) that have resolved Dependencies"
+ Alias // "Components added from selected alias"
+ };
+
+ CalculatorBase(PackageManagerCore *core);
+ virtual ~CalculatorBase() = 0;
+
+ virtual bool solve(const QList<Component *> &components) = 0;
+ void insertResolution(Component *component, const Resolution resolutionType,
+ const QString &referencedComponent = QString());
+
+ QList<Component *> resolvedComponents() const;
+ virtual QString resolutionText(Component *component) const = 0;
+ Resolution resolutionType(Component *component) const;
+
+ QString error() const;
+
+protected:
+ virtual bool solveComponent(Component *component, const QString &version = QString()) = 0;
+ QString referencedComponent(Component *component) const;
+
+protected:
+ PackageManagerCore *m_core;
+ QString m_errorString;
+
+ QList<Component *> m_resolvedComponents;
+ QHash<QString, QPair<Resolution, QString> > m_componentNameResolutionHash;
+};
+
+} // namespace QInstaller
+
+Q_DECLARE_METATYPE(QInstaller::CalculatorBase::Resolution)
+
+#endif // CALCULATORBASE_H
diff --git a/src/libs/installer/commandlineparser.cpp b/src/libs/installer/commandlineparser.cpp
index a91ab0128..f9e1f663e 100644
--- a/src/libs/installer/commandlineparser.cpp
+++ b/src/libs/installer/commandlineparser.cpp
@@ -48,7 +48,7 @@ CommandLineParser::CommandLineParser()
"headless mode. The installation operations can be invoked with the following commands and "
"options. Note that the options marked with \"CLI\" are available in the headless mode only.\n")
+ QLatin1String("\nCommands:\n")
- + indent + QString::fromLatin1("%1, %2 - install default or selected packages - <pkg ...>\n")
+ + indent + QString::fromLatin1("%1, %2 - install default or selected packages and aliases - <pkg|alias ...>\n")
.arg(CommandLineOptions::scInstallShort, CommandLineOptions::scInstallLong)
+ indent + QString::fromLatin1("%1, %2 - show available updates information on maintenance tool\n")
.arg(CommandLineOptions::scCheckUpdatesShort, CommandLineOptions::scCheckUpdatesLong)
@@ -56,13 +56,16 @@ CommandLineParser::CommandLineParser()
.arg(CommandLineOptions::scUpdateShort, CommandLineOptions::scUpdateLong)
+ indent + QString::fromLatin1("%1, %2 - uninstall packages and their child components - <pkg ...>\n")
.arg(CommandLineOptions::scRemoveShort, CommandLineOptions::scRemoveLong)
- + indent + QString::fromLatin1("%1, %2 - list currently installed packages - <regexp>\n")
+ + indent + QString::fromLatin1("%1, %2 - list currently installed packages - <regexp for pkg>\n")
.arg(CommandLineOptions::scListShort, CommandLineOptions::scListLong)
- + indent + QString::fromLatin1("%1, %2 - search available packages - <regexp>\n")
+ + indent + QString::fromLatin1("%1, %2 - search available aliases or packages - <regexp for pkg|alias>\n")
.arg(CommandLineOptions::scSearchShort, CommandLineOptions::scSearchLong)
+ indent + indent + QString::fromLatin1("Note: The --%1 option can be used to specify\n")
.arg(CommandLineOptions::scFilterPackagesLong)
+ indent + indent + QLatin1String("additional filters for the search operation\n")
+ + indent + indent + QString::fromLatin1("Note: The --%1 option can be used to specify\n")
+ .arg(CommandLineOptions::scTypeLong)
+ + indent + indent + QLatin1String("the content type to search\n")
+ indent + QString::fromLatin1("%1, %2 - create offline installer from selected packages - <pkg ...>\n")
.arg(CommandLineOptions::scCreateOfflineShort, CommandLineOptions::scCreateOfflineLong)
+ indent + QString::fromLatin1("%1, %2 - clear contents of the local metadata cache\n")
@@ -177,6 +180,12 @@ CommandLineParser::CommandLineParser()
<< CommandLineOptions::scLocalCachePathShort << CommandLineOptions::scLocalCachePathLong,
QLatin1String("Sets the path used for local metadata cache. The path must be writable by the current user."),
QLatin1String("path")));
+ addOption(QCommandLineOption(QStringList()
+ << CommandLineOptions::scTypeLong,
+ QLatin1String("[CLI] Sets the type of the given arguments for commands supporting multiple argument types, "
+ "like \"search\". By default aliases are searched first, and if no matching aliases are found, "
+ "then packages are searched with the same search pattern."),
+ QLatin1String("package|alias")));
// Message query options
addOptionWithContext(QCommandLineOption(QStringList() << CommandLineOptions::scAcceptMessageQueryShort
diff --git a/src/libs/installer/component.cpp b/src/libs/installer/component.cpp
index 2ce948e7e..ce76a2927 100644
--- a/src/libs/installer/component.cpp
+++ b/src/libs/installer/component.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -37,6 +37,7 @@
#include "remoteclient.h"
#include "settings.h"
#include "utils.h"
+#include "constants.h"
#include "updateoperationfactory.h"
@@ -45,6 +46,9 @@
#include <QtCore/QDirIterator>
#include <QtCore/QTranslator>
#include <QtCore/QRegularExpression>
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+#include <QtCore/QTextCodec>
+#endif
#include <QApplication>
#include <QtConcurrentFilter>
@@ -60,20 +64,14 @@
#include <private/qv4object_p.h>
#include <algorithm>
+#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
+#include <QJSEngine>
+#else
+#include <QQmlEngine>
+#endif
using namespace QInstaller;
-static const QLatin1String scScriptTag("Script");
-static const QLatin1String scVirtual("Virtual");
-static const QLatin1String scInstalled("Installed");
-static const QLatin1String scUpdateText("UpdateText");
-static const QLatin1String scUninstalled("Uninstalled");
-static const QLatin1String scCurrentState("CurrentState");
-static const QLatin1String scForcedInstallation("ForcedInstallation");
-static const QLatin1String scCheckable("Checkable");
-static const QLatin1String scExpandedByDefault("ExpandedByDefault");
-static const QLatin1String scUnstable("Unstable");
-
/*!
\enum QInstaller::Component::UnstableError
@@ -89,6 +87,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.
*/
/*!
@@ -250,8 +250,13 @@ static const QLatin1String scUnstable("Unstable");
*/
Component::Component(PackageManagerCore *core)
: d(new ComponentPrivate(core, this))
- , m_defaultArchivePath(QLatin1String("@TargetDir@"))
+ , m_defaultArchivePath(scTargetDirPlaceholder)
{
+#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
+ QJSEngine::setObjectOwnership(this, QJSEngine::CppOwnership);
+#else
+ QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
+#endif
setPrivate(d);
connect(this, &Component::valueChanged, this, &Component::updateModelData);
@@ -291,11 +296,11 @@ void Component::loadDataFromPackage(const KDUpdater::LocalPackage &package)
setValue(scVersion, package.version);
setValue(scInheritVersion, package.inheritVersionFrom);
setValue(scInstalledVersion, package.version);
- setValue(QLatin1String("LastUpdateDate"), package.lastUpdateDate.toString());
- setValue(QLatin1String("InstallDate"), package.installDate.toString());
+ setValue(scLastUpdateDate, package.lastUpdateDate.toString());
+ setValue(scInstallDate, package.installDate.toString());
setValue(scUncompressedSize, QString::number(package.uncompressedSize));
- setValue(scDependencies, package.dependencies.join(QLatin1String(",")));
- setValue(scAutoDependOn, package.autoDependencies.join(QLatin1String(",")));
+ setValue(scDependencies, package.dependencies.join(scCommaWithSpace));
+ setValue(scAutoDependOn, package.autoDependencies.join(scCommaWithSpace));
setValue(scSortingPriority, QString::number(package.sortingPriority));
setValue(scForcedInstallation, package.forcedInstallation ? scTrue : scFalse);
@@ -342,7 +347,7 @@ void Component::loadDataFromPackage(const Package &package)
setValue(scUpdateText, package.data(scUpdateText).toString());
setValue(scNewComponent, package.data(scNewComponent).toString());
setValue(scRequiresAdminRights, package.data(scRequiresAdminRights).toString());
- d->m_scriptHash = package.data(QLatin1String(scScriptTag)).toHash();
+ d->m_scriptHash = package.data(scScriptTag).toHash();
setValue(scReplaces, package.data(scReplaces).toString());
setValue(scReleaseDate, package.data(scReleaseDate).toString());
setValue(scCheckable, package.data(scCheckable).toString());
@@ -353,8 +358,9 @@ void Component::loadDataFromPackage(const Package &package)
forced = scFalse;
setValue(scForcedInstallation, forced);
setValue(scContentSha1, package.data(scContentSha1).toString());
+ setValue(scCheckSha1CheckSum, package.data(scCheckSha1CheckSum, scTrue).toString().toLower());
- const auto treeNamePair = package.data(QLatin1String(scTreeName)).value<QPair<QString, bool>>();
+ const auto treeNamePair = package.data(scTreeName).value<QPair<QString, bool>>();
setValue(scTreeName, treeNamePair.first);
d->m_treeNameMoveChildren = treeNamePair.second;
@@ -362,20 +368,20 @@ void Component::loadDataFromPackage(const Package &package)
return;
setLocalTempPath(QInstaller::pathFromUrl(package.packageSource().url));
- const QStringList uis = package.data(QLatin1String("UserInterfaces")).toString()
- .split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
- if (!uis.isEmpty())
- loadUserInterfaces(QDir(QString::fromLatin1("%1/%2").arg(localTempPath(), name())), uis);
+
+ const QStringList uiList = QInstaller::splitStringWithComma(package.data(scUserInterfaces).toString());
+ if (!uiList.isEmpty())
+ loadUserInterfaces(QDir(scTwoArgs.arg(localTempPath(), name())), uiList);
+
#ifndef IFW_DISABLE_TRANSLATIONS
- const QStringList qms = package.data(QLatin1String("Translations")).toString()
- .split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
+ const QStringList qms = QInstaller::splitStringWithComma(package.data(scTranslations).toString());
if (!qms.isEmpty())
- loadTranslations(QDir(QString::fromLatin1("%1/%2").arg(localTempPath(), name())), qms);
+ loadTranslations(QDir(scTwoArgs.arg(localTempPath(), name())), qms);
#endif
- QHash<QString, QVariant> licenseHash = package.data(QLatin1String("Licenses")).toHash();
+ QHash<QString, QVariant> licenseHash = package.data(scLicenses).toHash();
if (!licenseHash.isEmpty())
- loadLicenses(QString::fromLatin1("%1/%2/").arg(localTempPath(), name()), licenseHash);
- QVariant operationsVariant = package.data(QLatin1String("Operations"));
+ loadLicenses(scTwoArgs.arg(localTempPath(), name()), licenseHash);
+ QVariant operationsVariant = package.data(scOperations);
if (operationsVariant.canConvert<QList<QPair<QString, QVariant>>>())
m_operationsList = operationsVariant.value<QList<QPair<QString, QVariant>>>();
}
@@ -461,12 +467,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);
}
@@ -594,17 +602,17 @@ bool Component::treeNameMoveChildren() const
/*!
Loads the component script into the script engine. Call this method with
- \c postLoad \c true to a list of components that are updated or installed
+ \a postLoad \c true to a list of components that are updated or installed
to improve performance if the amount of components is huge and there are no script
functions that need to be called before the installation starts.
*/
void Component::loadComponentScript(const bool postLoad)
{
- const QString installScript(!postLoad ? d->m_scriptHash.value(QLatin1String("installScript")).toString()
- : d->m_scriptHash.value(QLatin1String("postLoadScript")).toString());
+ const QString installScript(!postLoad ? d->m_scriptHash.value(scInstallScript).toString()
+ : d->m_scriptHash.value(scPostLoadScript).toString());
if (!localTempPath().isEmpty() && !installScript.isEmpty()) {
- evaluateComponentScript(QString::fromLatin1("%1/%2/%3").arg(localTempPath(), name()
+ evaluateComponentScript(scThreeArgs.arg(localTempPath(), name()
, installScript), postLoad);
}
}
@@ -618,13 +626,11 @@ void Component::evaluateComponentScript(const QString &fileName, const bool post
// was successful
try {
if (postScriptContent) {
- d->m_postScriptContext = d->scriptEngine()->loadInContext(QLatin1String("Component"), fileName,
- QString::fromLatin1("var component = installer.componentByName('%1'); component.name;")
- .arg(name()));
+ d->m_postScriptContext = d->scriptEngine()->loadInContext(scComponent, fileName,
+ scComponentScriptTest.arg(name()));
} else {
- d->m_scriptContext = d->scriptEngine()->loadInContext(QLatin1String("Component"), fileName,
- QString::fromLatin1("var component = installer.componentByName('%1'); component.name;")
- .arg(name()));
+ d->m_scriptContext = d->scriptEngine()->loadInContext(scComponent, fileName,
+ scComponentScriptTest.arg(name()));
}
} catch (const Error &error) {
qCWarning(QInstaller::lcDeveloperBuild) << error.message();
@@ -647,7 +653,7 @@ void Component::evaluateComponentScript(const QString &fileName, const bool post
*/
void Component::languageChanged()
{
- callScriptMethod(QLatin1String("retranslateUi"));
+ callScriptMethod(scRetranslateUi);
}
/*!
@@ -659,7 +665,7 @@ void Component::loadTranslations(const QDir &directory, const QStringList &qms)
{
QDirIterator it(directory.path(), qms, QDir::Files);
const QStringList translations = d->m_core->settings().translations();
- const QString uiLanguage = QLocale().uiLanguages().value(0, QLatin1String("en"));
+ const QString uiLanguage = QLocale().uiLanguages().value(0, scEn);
while (it.hasNext()) {
const QString filename = it.next();
const QString basename = QFileInfo(filename).baseName();
@@ -667,18 +673,18 @@ void Component::loadTranslations(const QDir &directory, const QStringList &qms)
if (!translations.isEmpty()) {
bool found = false;
foreach (const QString &translation, translations)
- found |= translation.startsWith(QLatin1String("ifw_") + basename, Qt::CaseInsensitive);
+ found |= translation.startsWith(scIfw_ + basename, Qt::CaseInsensitive);
if (!found) // don't load the file if it does match the UI language but is not allowed to be used
continue;
} else if (!uiLanguage.startsWith(QFileInfo(filename).baseName(), Qt::CaseInsensitive)) {
continue; // do not load the file if it does not match the UI language
}
- QScopedPointer<QTranslator> translator(new QTranslator(this));
+ std::unique_ptr<QTranslator> translator(new QTranslator(this));
if (translator->load(filename)) {
// Do not throw if translator returns false as it may just be an intentionally
// empty file. See also QTBUG-31031
- qApp->installTranslator(translator.take());
+ qApp->installTranslator(translator.release());
}
}
}
@@ -696,17 +702,17 @@ void Component::loadUserInterfaces(const QDir &directory, const QStringList &uis
while (it.hasNext()) {
QFile file(it.next());
if (!file.open(QIODevice::ReadOnly)) {
- throw Error(tr("Cannot open the requested UI file \"%1\": %2").arg(
- it.fileName(), file.errorString()));
+ throw Error(tr("Cannot open the requested UI file \"%1\": %2.\n\n%3 \"%4\"").arg(
+ it.fileName(), file.errorString(), tr(scClearCacheHint), packageManagerCore()->settings().localCachePath()));
}
- static QUiLoader loader;
- loader.setTranslationEnabled(true);
- loader.setLanguageChangeEnabled(true);
- QWidget *const widget = loader.load(&file, 0);
+ QUiLoader *const loader = ProductKeyCheck::instance()->uiLoader();
+ loader->setTranslationEnabled(true);
+ loader->setLanguageChangeEnabled(true);
+ QWidget *const widget = loader->load(&file, 0);
if (!widget) {
- throw Error(tr("Cannot load the requested UI file \"%1\": %2").arg(
- it.fileName(), loader.errorString()));
+ throw Error(tr("Cannot load the requested UI file \"%1\": %2.\n\n%3 \"%4\"").arg(
+ it.fileName(), loader->errorString(), tr(scClearCacheHint), packageManagerCore()->settings().localCachePath()));
}
d->scriptEngine()->newQObject(widget);
d->m_userInterfaces.insert(widget->objectName(), widget);
@@ -722,7 +728,7 @@ void Component::loadLicenses(const QString &directory, const QHash<QString, QVar
QHash<QString, QVariant>::const_iterator it;
for (it = licenseHash.begin(); it != licenseHash.end(); ++it) {
QVariantMap license = it.value().toMap();
- const QString &fileName = license.value(QLatin1String("file")).toString();
+ const QString &fileName = license.value(scFile).toString();
if (!ProductKeyCheck::instance()->isValidLicenseTextFile(fileName))
continue;
@@ -734,7 +740,7 @@ void Component::loadLicenses(const QString &directory, const QHash<QString, QVar
QList<QFileInfo> fileCandidates;
foreach (const QString &locale, QInstaller::localeCandidates(lang)) {
- fileCandidates << QFileInfo(QString::fromLatin1("%1%2_%3.%4").arg(
+ fileCandidates << QFileInfo(scLocalesArgs.arg(
directory, fileInfo.baseName(), locale,
fileInfo.completeSuffix()));
}
@@ -751,12 +757,14 @@ void Component::loadLicenses(const QString &directory, const QHash<QString, QVar
QFile file(fileInfo.filePath());
if (!file.open(QIODevice::ReadOnly)) {
- throw Error(tr("Cannot open the requested license file \"%1\": %2").arg(
- file.fileName(), file.errorString()));
+ throw Error(tr("Cannot open the requested license file \"%1\": %2.\n\n%3 \"%4\"").arg(
+ file.fileName(), file.errorString(), tr(scClearCacheHint), packageManagerCore()->settings().localCachePath()));
}
QTextStream stream(&file);
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
stream.setCodec("UTF-8");
- license.insert(QLatin1String("content"), stream.readAll());
+#endif
+ license.insert(scContent, stream.readAll());
d->m_licenses.insert(it.key(), license);
}
}
@@ -768,7 +776,7 @@ void Component::loadLicenses(const QString &directory, const QHash<QString, QVar
void Component::loadXMLOperations()
{
for (auto operation: qAsConst(m_operationsList)) {
- if (operation.first != QLatin1String("Extract"))
+ if (operation.first != scExtract)
addOperation(operation.first, operation.second.toStringList());
}
}
@@ -780,13 +788,13 @@ void Component::loadXMLOperations()
void Component::loadXMLExtractOperations()
{
for (auto &operation: qAsConst(m_operationsList)) {
- if (operation.first == QLatin1String("Extract")) {
+ if (operation.first == scExtract) {
// Create hash for Extract operations. Operation has a mandatory extract folder as
// first argument and optional archive name as second argument.
const QStringList &operationArgs = operation.second.toStringList();
if (operationArgs.count() == 2) {
const QString archiveName = value(scVersion) + operationArgs.at(1);
- const QString archivePath = QString::fromLatin1("installer://%1/%2").arg(name()).arg(archiveName);
+ const QString archivePath = scInstallerPrefixWithTwoArgs.arg(name()).arg(archiveName);
m_archivesHash.insert(archivePath, operationArgs.at(0));
} else if (operationArgs.count() == 1) {
m_defaultArchivePath = operationArgs.at(0);
@@ -844,23 +852,23 @@ void Component::createOperationsForPath(const QString &path)
const QFileInfo fi(path);
// don't copy over a checksum file
- if (fi.suffix() == QLatin1String("sha1") && QFileInfo(fi.dir(), fi.completeBaseName()).exists())
+ if (fi.suffix() == scSha1 && QFileInfo(fi.dir(), fi.completeBaseName()).exists())
return;
// the script can override this method
- if (!callScriptMethod(QLatin1String("createOperationsForPath"), QJSValueList() << path).isUndefined())
+ if (!callScriptMethod(scCreateOperationsForPath, QJSValueList() << path).isUndefined())
return;
QString target;
- static const QString prefix = QString::fromLatin1("installer://");
- target = QString::fromLatin1("@TargetDir@%1").arg(path.mid(prefix.length() + name().length()));
+ static const QString prefix = scInstallerPrefix;
+ target = scTargetDirPlaceholderWithArg.arg(path.mid(prefix.length() + name().length()));
if (fi.isFile()) {
- static const QString copy = QString::fromLatin1("Copy");
+ static const QString copy = scCopy;
addOperation(copy, QStringList() << fi.filePath() << target);
} else if (fi.isDir()) {
qApp->processEvents();
- static const QString mkdir = QString::fromLatin1("Mkdir");
+ static const QString mkdir = scMkdir;
addOperation(mkdir, QStringList(target));
QDirIterator it(fi.filePath());
@@ -888,11 +896,11 @@ void Component::createOperationsForArchive(const QString &archive)
const QFileInfo fi(archive);
// don't do anything with sha1 files
- if (fi.suffix() == QLatin1String("sha1") && QFileInfo(fi.dir(), fi.completeBaseName()).exists())
+ if (fi.suffix() == scSha1 && QFileInfo(fi.dir(), fi.completeBaseName()).exists())
return;
// the script can override this method
- if (!callScriptMethod(QLatin1String("createOperationsForArchive"), QJSValueList() << archive).isUndefined())
+ if (!callScriptMethod(scCreateOperationsForArchive, QJSValueList() << archive).isUndefined())
return;
QScopedPointer<AbstractArchive> archiveFile(ArchiveFactory::instance().create(archive));
@@ -901,9 +909,9 @@ void Component::createOperationsForArchive(const QString &archive)
if (isZip) {
// component.xml can override this value
if (m_archivesHash.contains(archive))
- addOperation(QLatin1String("Extract"), QStringList() << archive << m_archivesHash.value(archive));
+ addOperation(scExtract, QStringList() << archive << m_archivesHash.value(archive));
else
- addOperation(QLatin1String("Extract"), QStringList() << archive << m_defaultArchivePath);
+ addOperation(scExtract, QStringList() << archive << m_defaultArchivePath);
} else {
createOperationsForPath(archive);
}
@@ -915,7 +923,7 @@ void Component::createOperationsForArchive(const QString &archive)
void Component::beginInstallation()
{
// the script can override this method
- callScriptMethod(QLatin1String("beginInstallation"));
+ callScriptMethod(scBeginInstallation);
}
/*!
@@ -925,7 +933,7 @@ void Component::beginInstallation()
void Component::createOperations()
{
// the script can override this method
- if (!callScriptMethod(QLatin1String("createOperations")).isUndefined()) {
+ if (!callScriptMethod(scCreateOperations).isUndefined()) {
d->m_operationsCreated = true;
return;
}
@@ -964,10 +972,17 @@ QList<QPair<QString, bool> > Component::pathsForUninstallation() const
*/
QStringList Component::archives() const
{
- QString pathString = QString::fromLatin1("installer://%1/").arg(name());
+ static const QRegularExpression regExp(scCaretSymbol);
+ QString pathString = scInstallerPrefixWithOneArgs.arg(name());
QStringList archivesNameList = QDir(pathString).entryList();
+
+ // In resources we may have older version of archives, this can happen
+ // when there is offline installer with same component with lower version
+ // number and newer version is available online
+ archivesNameList = archivesNameList.filter(value(scVersion));
+
//RegExp "^" means line beginning
- archivesNameList.replaceInStrings(QRegularExpression(QLatin1String("^")), pathString);
+ archivesNameList.replaceInStrings(regExp, pathString);
return archivesNameList;
}
@@ -987,6 +1002,15 @@ void Component::addDownloadableArchive(const QString &path)
}
/*!
+ \internal
+*/
+void Component::addDownloadableArchives(const QString& archives)
+{
+ Q_ASSERT(isFromOnlineRepository());
+ d->m_downloadableArchivesVariable = archives;
+}
+
+/*!
Removes the archive \a path previously added via addDownloadableArchive() from this component.
This can only be called if this component was downloaded from an online repository.
@@ -1001,9 +1025,15 @@ void Component::removeDownloadableArchive(const QString &path)
/*!
Returns the archives to be downloaded from the online repository before installation.
+ Should be called only once when the installation starts.
*/
-QStringList Component::downloadableArchives() const
+QStringList Component::downloadableArchives()
{
+ const QStringList downloadableArchives = d->m_downloadableArchivesVariable
+ .split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
+ foreach (const QString downloadableArchive, downloadableArchives)
+ addDownloadableArchive(downloadableArchive);
+
return d->m_downloadableArchives;
}
@@ -1064,23 +1094,23 @@ OperationList Component::operations(const Operation::OperationGroups &mask) cons
if (!d->m_minimumProgressOperation) {
d->m_minimumProgressOperation = KDUpdater::UpdateOperationFactory::instance()
- .create(QLatin1String("MinimumProgress"), d->m_core);
- d->m_minimumProgressOperation->setValue(QLatin1String("component"), name());
+ .create(scMinimumProgress, d->m_core);
+ d->m_minimumProgressOperation->setValue(scComponentSmall, name());
d->m_operations.append(d->m_minimumProgressOperation);
}
if (!d->m_licenses.isEmpty()) {
d->m_licenseOperation = KDUpdater::UpdateOperationFactory::instance()
- .create(QLatin1String("License"), d->m_core);
- d->m_licenseOperation->setValue(QLatin1String("component"), name());
+ .create(scLicense, d->m_core);
+ d->m_licenseOperation->setValue(scComponentSmall, name());
QVariantMap licenses;
const QList<QVariantMap> values = d->m_licenses.values();
for (int i = 0; i < values.count(); ++i) {
- licenses.insert(values.at(i).value(QLatin1String("file")).toString(),
- values.at(i).value(QLatin1String("content")));
+ licenses.insert(values.at(i).value(scFile).toString(),
+ values.at(i).value(scContent));
}
- d->m_licenseOperation->setValue(QLatin1String("licenses"), licenses);
+ d->m_licenseOperation->setValue(scLicensesValue, licenses);
d->m_operations.append(d->m_licenseOperation);
}
}
@@ -1100,7 +1130,7 @@ void Component::addOperation(Operation *operation)
{
d->m_operations.append(operation);
if (RemoteClient::instance().isActive())
- operation->setValue(QLatin1String("admin"), true);
+ operation->setValue(scAdmin, true);
}
/*!
@@ -1110,7 +1140,7 @@ void Component::addOperation(Operation *operation)
void Component::addElevatedOperation(Operation *operation)
{
addOperation(operation);
- operation->setValue(QLatin1String("admin"), true);
+ operation->setValue(scAdmin, true);
}
/*!
@@ -1165,9 +1195,6 @@ Operation *Component::createOperation(const QString &operationName, const QStrin
return operation;
}
- if (operation->name() == QLatin1String("Delete"))
- operation->setValue(QLatin1String("performUndo"), false);
-
// Operation can contain variables which are resolved when performing the operation
if (operation->requiresUnreplacedVariables())
operation->setArguments(parameters);
@@ -1175,7 +1202,7 @@ Operation *Component::createOperation(const QString &operationName, const QStrin
operation->setArguments(d->m_core->replaceVariables(parameters));
- operation->setValue(QLatin1String("component"), name());
+ operation->setValue(scComponentSmall, name());
return operation;
}
@@ -1194,6 +1221,10 @@ void Component::markComponentUnstable(Component::UnstableError error, const QStr
setValue(scUnstable, scTrue);
QMetaEnum metaEnum = QMetaEnum::fromType<Component::UnstableError>();
emit packageManagerCore()->unstableComponentFound(QLatin1String(metaEnum.valueToKey(error)), errorMessage, this->name());
+
+ // Update the description and tooltip texts to contain
+ // information about the unstable error.
+ updateModelData(scDescription, QString());
}
QJSValue Component::callScriptMethod(const QString &methodName, const QJSValueList &arguments) const
@@ -1226,7 +1257,7 @@ inline bool convert(QQmlV4Function *func, QStringList *toArgs)
QV4::Object *array = val->as<QV4::Object>();
uint length = array->getLength();
for (uint ii = 0; ii < length; ++ii) {
- valtmp = array->getIndexed(ii);
+ valtmp = array->get(ii);
*toArgs << valtmp->toQStringNoThrow();
}
} else {
@@ -1341,6 +1372,15 @@ bool Component::forcedInstallation() const
}
/*!
+ Returns whether this component is essential. Essential components
+ are always installed, and updated before other components.
+*/
+bool Component::isEssential() const
+{
+ return d->m_vars.value(scEssential, scFalse).toLower() == scTrue;
+}
+
+/*!
Sets the validator callback name to \a name.
*/
void Component::setValidatorCallbackName(const QString &name)
@@ -1374,7 +1414,7 @@ void Component::addDependency(const QString &newDependency)
if (oldDependencies.isEmpty())
setValue(scDependencies, newDependency);
else
- setValue(scDependencies, oldDependencies + QLatin1String(", ") + newDependency);
+ setValue(scDependencies, oldDependencies + scCommaWithSpace + newDependency);
}
/*!
@@ -1382,7 +1422,7 @@ void Component::addDependency(const QString &newDependency)
*/
QStringList Component::dependencies() const
{
- return d->m_vars.value(scDependencies).split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
+ return QInstaller::splitStringWithComma(d->m_vars.value(scDependencies));
}
/*!
@@ -1390,7 +1430,7 @@ QStringList Component::dependencies() const
*/
QStringList Component::localDependencies() const
{
- return d->m_vars.value(scLocalDependencies).split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
+ return QInstaller::splitStringWithComma(d->m_vars.value(scLocalDependencies));
}
/*!
@@ -1408,7 +1448,7 @@ void Component::addAutoDependOn(const QString &newDependOn)
if (oldDependOn.isEmpty())
setValue(scAutoDependOn, newDependOn);
else
- setValue(scAutoDependOn, oldDependOn + QLatin1String(", ") + newDependOn);
+ setValue(scAutoDependOn, oldDependOn + scCommaWithSpace + newDependOn);
}
QStringList Component::autoDependencies() const
@@ -1500,7 +1540,7 @@ bool Component::isDefault() const
if (d->m_vars.value(scDefault).compare(scScript, Qt::CaseInsensitive) == 0) {
QJSValue valueFromScript;
try {
- valueFromScript = callScriptMethod(QLatin1String("isDefault"));
+ valueFromScript = callScriptMethod(scIsDefault);
} catch (const Error &error) {
MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
QLatin1String("isDefaultError"), tr("Cannot resolve isDefault in %1").arg(name()),
@@ -1734,24 +1774,26 @@ void Component::updateModelData(const QString &key, const QString &data)
setData(humanReadableSize(size), UncompressedSize);
}
- QString tooltipText;
- const QString &updateInfo = d->m_vars.value(scUpdateText);
- if (!d->m_core->isUpdater() || updateInfo.isEmpty()) {
- tooltipText = QString::fromLatin1("<html><body>%1</body></html>").arg(d->m_vars.value(scDescription));
- } else {
- tooltipText = d->m_vars.value(scDescription) + QLatin1String("<br><br>")
- + tr("Update Info: ") + updateInfo;
- }
- if (isUnstable()) {
- tooltipText += QLatin1String("<br>") + tr("There was an error loading the selected component. "
+ if (key == scUpdateText || key == scDescription) {
+ QString tooltipText;
+ const QString &updateInfo = d->m_vars.value(scUpdateText);
+ if (!d->m_core->isUpdater() || updateInfo.isEmpty()) {
+ tooltipText = QString::fromLatin1("<html><body>%1</body></html>").arg(d->m_vars.value(scDescription));
+ } else {
+ tooltipText = d->m_vars.value(scDescription) + scBr + scBr
+ + tr("Update Info: ") + updateInfo;
+ }
+ if (isUnstable()) {
+ tooltipText += scBr + tr("There was an error loading the selected component. "
"This component cannot be installed.");
- }
- static const QRegularExpression externalLinkRegexp(QLatin1String("{external-link}='(.*?)'"));
- static const QLatin1String externalLinkElement(QLatin1String("<a href=\"\\1\">\\1</a>"));
- // replace {external-link}='' fields in component description with proper link tags
- tooltipText.replace(externalLinkRegexp, externalLinkElement);
+ }
+ static const QRegularExpression externalLinkRegexp(QLatin1String("{external-link}='(.*?)'"));
+ static const QLatin1String externalLinkElement(QLatin1String("<a href=\"\\1\">\\1</a>"));
+ // replace {external-link}='' fields in component description with proper link tags
+ tooltipText.replace(externalLinkRegexp, externalLinkElement);
- setData(tooltipText, Qt::ToolTipRole);
+ setData(tooltipText, Qt::ToolTipRole);
+ }
}
/*!
diff --git a/src/libs/installer/component.h b/src/libs/installer/component.h
index bb1a7d1b6..8f17b2d98 100644
--- a/src/libs/installer/component.h
+++ b/src/libs/installer/component.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -32,7 +32,6 @@
#include "constants.h"
#include "component_p.h"
#include "qinstallerglobal.h"
-#include "packagemanagercore.h"
#include <QtCore/QDir>
#include <QtCore/QMetaType>
@@ -44,6 +43,8 @@ QT_FORWARD_DECLARE_CLASS(QQmlV4Function)
namespace QInstaller {
+class PackageManagerCore;
+
class INSTALLER_EXPORT Component : public QObject, public ComponentModelHelper
{
Q_OBJECT
@@ -149,9 +150,10 @@ public:
Q_INVOKABLE bool addElevatedOperation(QQmlV4Function *args);
bool addElevatedOperation(const QString &operation, const QStringList &parameters);
- QStringList downloadableArchives() const;
+ QStringList downloadableArchives();
Q_INVOKABLE void addDownloadableArchive(const QString &path);
Q_INVOKABLE void removeDownloadableArchive(const QString &path);
+ void addDownloadableArchives(const QString& archives);
QStringList stopProcessForUpdateRequests() const;
Q_INVOKABLE void addStopProcessForUpdateRequest(const QString &process);
@@ -207,6 +209,7 @@ public:
bool isVirtual() const;
bool isSelected() const;
bool forcedInstallation() const;
+ bool isEssential() const;
void setValidatorCallbackName(const QString &name);
diff --git a/src/libs/installer/component_p.cpp b/src/libs/installer/component_p.cpp
index 42a4c5277..bf3941274 100644
--- a/src/libs/installer/component_p.cpp
+++ b/src/libs/installer/component_p.cpp
@@ -153,7 +153,7 @@ void ComponentModelHelper::setEnabled(bool enabled)
*/
bool ComponentModelHelper::isTristate() const
{
- return (flags() & Qt::ItemIsTristate) != 0;
+ return (flags() & Qt::ItemIsAutoTristate) != 0;
}
/*!
@@ -164,7 +164,7 @@ bool ComponentModelHelper::isTristate() const
*/
void ComponentModelHelper::setTristate(bool tristate)
{
- changeFlags(tristate, Qt::ItemIsTristate);
+ changeFlags(tristate, Qt::ItemIsAutoTristate);
}
/*!
@@ -188,6 +188,7 @@ void ComponentModelHelper::setCheckable(bool checkable)
setData(Qt::Unchecked, Qt::CheckStateRole);
}
changeFlags(checkable, Qt::ItemIsUserCheckable);
+ m_componentPrivate->m_vars[scCheckable] = checkable ? scTrue : scFalse;
}
/*!
diff --git a/src/libs/installer/component_p.h b/src/libs/installer/component_p.h
index fe9d84023..dea2d4935 100644
--- a/src/libs/installer/component_p.h
+++ b/src/libs/installer/component_p.h
@@ -75,6 +75,7 @@ public:
QList<Component*> m_childComponents;
QList<Component*> m_allChildComponents;
QStringList m_downloadableArchives;
+ QString m_downloadableArchivesVariable;
QStringList m_stopProcessForUpdateRequests;
QHash<QString, QPointer<QWidget> > m_userInterfaces;
QHash<QString, QVariant> m_scriptHash;
diff --git a/src/libs/installer/componentalias.cpp b/src/libs/installer/componentalias.cpp
new file mode 100644
index 000000000..955d715fd
--- /dev/null
+++ b/src/libs/installer/componentalias.cpp
@@ -0,0 +1,649 @@
+/**************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "componentalias.h"
+
+#include "constants.h"
+#include "globals.h"
+#include "packagemanagercore.h"
+#include "updater.h"
+
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+
+namespace QInstaller {
+
+static const QStringList scPossibleElements {
+ scName,
+ scDisplayName,
+ scDescription,
+ scVersion,
+ scVirtual,
+ scRequiredComponents,
+ scRequiredAliases,
+ scOptionalComponents,
+ scOptionalAliases,
+ scReleaseDate
+};
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::AliasSource
+ \brief Describes a source for alias declarations.
+*/
+
+/*!
+ \enum QInstaller::AliasSource::SourceFileFormat
+
+ This enum type holds the possible file formats for alias source:
+
+ \value Unknown
+ Invalid or unknown file format.
+ \value Xml
+ XML file format.
+ \value Json
+ JSON file format.
+*/
+
+/*!
+ Constructs an alias source with empty information.
+*/
+AliasSource::AliasSource()
+ : priority(-1)
+{}
+
+/*!
+ Constructs an alias source with source file format \a aFormat, filename \a aFilename, and priority \a aPriority.
+*/
+AliasSource::AliasSource(SourceFileFormat aFormat, const QString &aFilename, int aPriority)
+ : format(aFormat)
+ , filename(aFilename)
+ , priority(aPriority)
+{}
+
+/*!
+ Copy-constructs an alias source from \a other.
+*/
+AliasSource::AliasSource(const AliasSource &other)
+{
+ format = other.format;
+ filename = other.filename;
+ priority = other.priority;
+}
+
+/*!
+ Returns the hash value for the \a key, using \a seed to seed the calculation.
+*/
+hashValue qHash(const AliasSource &key, hashValue seed)
+{
+ return qHash(key.filename, seed) ^ key.priority;
+}
+
+/*!
+ Returns \c true if \a lhs and \a rhs are equal; otherwise returns \c false.
+*/
+bool operator==(const AliasSource &lhs, const AliasSource &rhs)
+{
+ return lhs.filename == rhs.filename && lhs.priority == rhs.priority && lhs.format == rhs.format;
+}
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::AliasFinder
+ \brief Creates component alias objects from parsed alias source files, based
+ on version and source priorities.
+*/
+
+/*!
+ Constructs a new alias finder with \a core as the package manager instance.
+*/
+AliasFinder::AliasFinder(PackageManagerCore *core)
+ : m_core(core)
+{
+}
+
+/*!
+ Destroys the finder and cleans unreleased results.
+*/
+AliasFinder::~AliasFinder()
+{
+ clear();
+}
+
+/*!
+ Runs the finder. Parses the alias source files and creates component alias
+ objects based on the parsed data. Same alias may be declared in multiple source
+ files, thus source priority and version information is used to decide which
+ source is used for creating the alias object.
+
+ Any previous results are cleared when calling this.
+
+ Returns \c true if at least one alias was found, \c false otherwise.
+*/
+bool AliasFinder::run()
+{
+ clear();
+
+ if (m_sources.isEmpty())
+ return false;
+
+ // 1. Parse source files
+ for (auto &source : qAsConst(m_sources)) {
+ if (source.format == AliasSource::SourceFileFormat::Unknown) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Unknown alias source format for file:" << source.filename;
+ continue;
+ }
+ if (source.format == AliasSource::SourceFileFormat::Xml)
+ parseXml(source);
+ else if (source.format == AliasSource::SourceFileFormat::Json)
+ parseJson(source);
+ }
+
+ // 2. Create aliases based on priority & version
+ for (auto &data : qAsConst(m_aliasData)) {
+ const QString name = data.value(scName).toString();
+ const Resolution resolution = checkPriorityAndVersion(data);
+ if (resolution == Resolution::KeepExisting)
+ continue;
+
+ if (resolution == Resolution::RemoveExisting)
+ delete m_aliases.take(name);
+
+ ComponentAlias *alias = new ComponentAlias(m_core);
+ AliasData::const_iterator it;
+ for (it = data.cbegin(); it != data.cend(); ++it) {
+ if (it.value().canConvert<QString>())
+ alias->setValue(it.key(), it.value().toString());
+ }
+ m_aliases.insert(name, alias);
+ }
+
+ return !m_aliases.isEmpty();
+}
+
+/*!
+ Returns a list of the found aliases.
+*/
+QList<ComponentAlias *> AliasFinder::aliases() const
+{
+ return m_aliases.values();
+}
+
+/*!
+ Sets the alias sources to look alias information from to \a sources.
+*/
+void AliasFinder::setAliasSources(const QSet<AliasSource> &sources)
+{
+ clear();
+ m_sources = sources;
+}
+
+/*!
+ Clears the results of the finder.
+*/
+void AliasFinder::clear()
+{
+ qDeleteAll(m_aliases);
+
+ m_aliases.clear();
+ m_aliasData.clear();
+}
+
+/*!
+ Reads an XML file specified by \a filename, and constructs a variant map of
+ the data for each alias.
+
+ Returns \c true on success, \c false otherwise.
+*/
+bool AliasFinder::parseXml(AliasSource source)
+{
+ QFile file(source.filename);
+ if (!file.open(QIODevice::ReadOnly)) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Cannot open alias definition for reading:" << file.errorString();
+ return false;
+ }
+
+ QString error;
+ int errorLine;
+ int errorColumn;
+
+ QDomDocument doc;
+ if (!doc.setContent(&file, &error, &errorLine, &errorColumn)) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Cannot read alias definition document:" << error
+ << "line:" << errorLine << "column:" << errorColumn;
+ return false;
+ }
+ file.close();
+
+ const QDomElement root = doc.documentElement();
+ const QDomNodeList children = root.childNodes();
+
+ for (int i = 0; i < children.count(); ++i) {
+ const QDomElement el = children.at(i).toElement();
+ const QString tag = el.tagName();
+ if (el.isNull() || tag != scAlias) {
+ qCWarning(lcInstallerInstallLog) << "Unexpected element name:" << tag;
+ continue;
+ }
+
+ AliasData data;
+ data.insert(QLatin1String("source"), QVariant::fromValue(source));
+
+ const QDomNodeList c2 = el.childNodes();
+ for (int j = 0; j < c2.count(); ++j) {
+ const QDomElement el2 = c2.at(j).toElement();
+ const QString tag2 = el2.tagName();
+ if (!scPossibleElements.contains(tag2)) {
+ qCWarning(lcInstallerInstallLog) << "Unexpected element name:" << tag2;
+ continue;
+ }
+ data.insert(tag2, el2.text());
+ }
+
+ m_aliasData.insert(data.value(scName).toString(), data);
+ }
+
+ return true;
+}
+
+/*!
+ Reads a JSON file specified by \a source, and constructs a variant map of
+ the data for each alias.
+
+ Returns \c true on success, \c false otherwise.
+*/
+bool AliasFinder::parseJson(AliasSource source)
+{
+ QFile file(source.filename);
+ if (!file.open(QIODevice::ReadOnly)) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Cannot open alias definition for reading:" << file.errorString();
+ return false;
+ }
+
+ const QByteArray jsonData = file.readAll();
+ const QJsonDocument doc(QJsonDocument::fromJson(jsonData));
+ const QJsonObject docJsonObject = doc.object();
+
+ const QJsonArray aliases = docJsonObject.value(QLatin1String("alias-packages")).toArray();
+ for (auto &it : aliases) {
+ AliasData data;
+ data.insert(QLatin1String("source"), QVariant::fromValue(source));
+
+ QJsonObject aliasObj = it.toObject();
+ for (const auto &key : aliasObj.keys()) {
+ if (!scPossibleElements.contains(key)) {
+ qCWarning(lcInstallerInstallLog) << "Unexpected element name:" << key;
+ continue;
+ }
+
+ const QJsonValue jsonValue = aliasObj.value(key);
+ if (key == scRequiredComponents || key == scRequiredAliases
+ || key == scOptionalComponents || key == scOptionalAliases) {
+ const QJsonArray requirements = jsonValue.toArray();
+ QString requiresString;
+
+ for (const auto &it2 : requirements) {
+ requiresString.append(it2.toString());
+ if (it2 != requirements.last())
+ requiresString.append(QLatin1Char(','));
+ }
+
+ data.insert(key, requiresString);
+ } else if (key == scVirtual) {
+ data.insert(key, QVariant(jsonValue.toBool()))->toString();
+ } else {
+ data.insert(key, jsonValue.toString());
+ }
+ }
+
+ m_aliasData.insert(data.value(scName).toString(), data);
+ }
+
+ return true;
+}
+
+/*!
+ Checks whether \a data should be used for creating a new alias object,
+ based on version and source priority.
+
+ If an alias of the same name exists, always use the one with the higher
+ version. If the new alias has the same version but a higher
+ priority, use the new new alias. Otherwise keep the already existing alias.
+
+ Returns the resolution of the check.
+*/
+AliasFinder::Resolution AliasFinder::checkPriorityAndVersion(const AliasData &data) const
+{
+ for (const auto &existingData : m_aliasData.values(data.value(scName).toString())) {
+ if (existingData == data)
+ continue;
+
+ const int versionMatch = KDUpdater::compareVersion(data.value(scVersion).toString(),
+ existingData.value(scVersion).toString());
+
+ const AliasSource newSource = data.value(QLatin1String("source")).value<AliasSource>();
+ const AliasSource oldSource = existingData.value(QLatin1String("source")).value<AliasSource>();
+
+ if (versionMatch > 0) {
+ // new alias has higher version, use
+ qCDebug(QInstaller::lcDeveloperBuild).nospace() << "Remove Alias 'Name: "
+ << data.value(scName).toString() << ", Version: " << existingData.value(scVersion).toString()
+ << ", Source: " << oldSource.filename
+ << "' found an alias with higher version 'Name: "
+ << data.value(scName).toString() << ", Version: " << data.value(scVersion).toString()
+ << ", Source: " << newSource.filename << "'";
+
+ return Resolution::RemoveExisting;
+ }
+
+ if ((versionMatch == 0) && (newSource.priority > oldSource.priority)) {
+ // new alias version equals but priority is higher, use
+ qCDebug(QInstaller::lcDeveloperBuild).nospace() << "Remove Alias 'Name: "
+ << data.value(scName).toString() << ", Priority: " << oldSource.priority
+ << ", Source: " << oldSource.filename
+ << "' found an alias with higher priority 'Name: "
+ << data.value(scName).toString() << ", Priority: " << newSource.priority
+ << ", Source: " << newSource.filename << "'";
+
+ return Resolution::RemoveExisting;
+ }
+
+ return Resolution::KeepExisting; // otherwise keep existing
+ }
+
+ return Resolution::AddNew;
+}
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::ComponentAlias
+ \brief The ComponentAlias class represents an alias for single or multiple components.
+*/
+
+/*!
+ \enum QInstaller::ComponentAlias::UnstableError
+
+ This enum type holds the possible reasons for marking an alias unstable:
+
+ \value ReferenceToUnstable
+ Alias requires another alias that is marked unstable.
+ \value MissingComponent
+ Alias requires a component that is missing.
+ \value UnselectableComponent
+ Alias requires a component that cannot be selected.
+ \value MissingAlias
+ Alias requires another alias that is missing.
+ \value ComponentNameConfict
+ Alias has a name that conflicts with a name of a component
+*/
+
+/*!
+ Constructs a new component alias with \a core as the package manager instance.
+*/
+ComponentAlias::ComponentAlias(PackageManagerCore *core)
+ : m_core(core)
+ , m_selected(false)
+ , m_unstable(false)
+{
+}
+
+/*!
+ Destructs the alias.
+*/
+ComponentAlias::~ComponentAlias()
+{
+}
+
+/*!
+ Returns the name of the alias.
+*/
+QString ComponentAlias::name() const
+{
+ return m_variables.value(scName);
+}
+
+/*!
+ Returns the display name of the alias.
+*/
+QString ComponentAlias::displayName() const
+{
+ return m_variables.value(scDisplayName);
+}
+
+/*!
+ Returns the description text of the alias.
+*/
+QString ComponentAlias::description() const
+{
+ return m_variables.value(scDescription);
+}
+
+/*!
+ Returns the version of the alias.
+*/
+QString ComponentAlias::version() const
+{
+ return m_variables.value(scVersion);
+}
+
+/*!
+ Returns \c true if the alias is virtual, \c false otherwise.
+
+ Virtual aliases are aliases that cannot be selected by the
+ user, and are invisible. They can be required by other aliases however.
+*/
+bool ComponentAlias::isVirtual() const
+{
+ return m_variables.value(scVirtual, scFalse).toLower() == scTrue;
+}
+
+/*!
+ Returns \c true if the alias is selected for installation, \c false otherwise.
+*/
+bool ComponentAlias::isSelected() const
+{
+ return m_selected;
+}
+
+/*!
+ Sets the selection state of the alias to \a selected. The selection
+ does not have an effect if the alias is unselectable.
+*/
+void ComponentAlias::setSelected(bool selected)
+{
+ if (selected && (isUnstable() || isVirtual()))
+ return;
+
+ m_selected = selected;
+}
+
+/*!
+ Returns the list of components required by this alias, or an
+ empty list if this alias does not require any components.
+*/
+QList<Component *> ComponentAlias::components()
+{
+ if (m_components.isEmpty()) {
+ const QStringList componentList = QInstaller::splitStringWithComma(
+ m_variables.value(scRequiredComponents));
+
+ const QStringList optionalComponentList = QInstaller::splitStringWithComma(
+ m_variables.value(scOptionalComponents));
+
+ addRequiredComponents(componentList, false);
+ addRequiredComponents(optionalComponentList, true);
+ }
+
+ return m_components;
+}
+
+/*!
+ Returns the list of other aliases required by this alias, or an
+ empty list if this alias does not require any other aliases.
+*/
+QList<ComponentAlias *> ComponentAlias::aliases()
+{
+ if (m_aliases.isEmpty()) {
+ const QStringList aliasList = QInstaller::splitStringWithComma(
+ m_variables.value(scRequiredAliases));
+
+ const QStringList optionalAliasList = QInstaller::splitStringWithComma(
+ m_variables.value(scOptionalAliases));
+
+ addRequiredAliases(aliasList, false);
+ addRequiredAliases(optionalAliasList, true);
+ }
+
+ return m_aliases;
+}
+
+/*!
+ Returns the value specified by \a key, with an optional default value \a defaultValue.
+*/
+QString ComponentAlias::value(const QString &key, const QString &defaultValue) const
+{
+ return m_variables.value(key, defaultValue);
+}
+
+/*!
+ Sets the value specified by \a key to \a value. If the value exists already,
+ it is replaced with the new value.
+*/
+void ComponentAlias::setValue(const QString &key, const QString &value)
+{
+ const QString normalizedValue = m_core->replaceVariables(value);
+ if (m_variables.value(key) == normalizedValue)
+ return;
+
+ m_variables[key] = normalizedValue;
+}
+
+/*!
+ Returns all keys for the component alias values.
+*/
+QStringList ComponentAlias::keys() const
+{
+ return m_variables.keys();
+}
+
+/*!
+ Returns \c true if the alias is marked unstable, \c false otherwise.
+*/
+bool ComponentAlias::isUnstable() const
+{
+ return m_unstable;
+}
+
+/*!
+ Sets the alias unstable with \a error, and a \a message describing the error.
+*/
+void ComponentAlias::setUnstable(UnstableError error, const QString &message)
+{
+ setSelected(false);
+ m_unstable = true;
+
+ const QMetaEnum metaEnum = QMetaEnum::fromType<ComponentAlias::UnstableError>();
+ emit m_core->unstableComponentFound(
+ QLatin1String(metaEnum.valueToKey(error)), message, name());
+}
+
+/*!
+ \internal
+
+ Adds the \a aliases to the list of required aliases by this alias. If \a optional
+ is \c true, missing alias references are ignored.
+*/
+void ComponentAlias::addRequiredAliases(const QStringList &aliases, const bool optional)
+{
+ for (const auto &aliasName : aliases) {
+ ComponentAlias *alias = m_core->aliasByName(aliasName);
+ if (!alias) {
+ if (optional)
+ continue;
+
+ const QString error = QLatin1String("No required alias found by name: ") + aliasName;
+ qCWarning(lcInstallerInstallLog) << error;
+
+ setUnstable(UnstableError::MissingAlias, error);
+ continue;
+ }
+
+ if (alias->isUnstable()) {
+ const QString error = QLatin1String("Alias requires another alias "
+ "that is marked unstable: ") + aliasName;
+ qCWarning(lcInstallerInstallLog) << error;
+
+ setUnstable(UnstableError::ReferenceToUnstable, error);
+ continue;
+ }
+
+ m_aliases.append(alias);
+ }
+}
+
+/*!
+ \internal
+
+ Adds the \a components to the list of required components by this alias. If \a optional
+ is \c true, missing component references are ignored.
+*/
+void ComponentAlias::addRequiredComponents(const QStringList &components, const bool optional)
+{
+ for (const auto &componentName : components) {
+ Component *component = m_core->componentByName(componentName);
+ if (!component) {
+ if (optional)
+ continue;
+
+ const QString error = QLatin1String("No required component found by name: ")
+ + componentName;
+ qCWarning(lcInstallerInstallLog) << error;
+
+ setUnstable(UnstableError::MissingComponent, error);
+ continue;
+ }
+
+ if (component->isUnstable() || !component->isCheckable()) {
+ const QString error = QLatin1String("Alias requires component that is uncheckable or unstable: ")
+ + componentName;
+ qCWarning(lcInstallerInstallLog) << error;
+
+ setUnstable(UnstableError::UnselectableComponent, error);
+ continue;
+ }
+
+ m_components.append(component);
+ }
+}
+
+} // namespace QInstaller
diff --git a/src/libs/installer/componentalias.h b/src/libs/installer/componentalias.h
new file mode 100644
index 000000000..e99c343d9
--- /dev/null
+++ b/src/libs/installer/componentalias.h
@@ -0,0 +1,160 @@
+/**************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef COMPONENTALIAS_H
+#define COMPONENTALIAS_H
+
+#include "installer_global.h"
+
+#include <QHash>
+#include <QMetaEnum>
+#include <QString>
+#include <QSet>
+
+namespace QInstaller {
+
+class Component;
+class ComponentAlias;
+class PackageManagerCore;
+
+struct INSTALLER_EXPORT AliasSource
+{
+ enum class SourceFileFormat {
+ Unknown = -1,
+ Xml = 0,
+ Json
+ };
+
+ AliasSource();
+ AliasSource(SourceFileFormat aFormat, const QString &aFilename, int aPriority);
+ AliasSource(const AliasSource &other);
+
+ SourceFileFormat format;
+ QString filename;
+ int priority;
+};
+
+INSTALLER_EXPORT hashValue qHash(const AliasSource &key, hashValue seed);
+INSTALLER_EXPORT bool operator==(const AliasSource &lhs, const AliasSource &rhs);
+
+class INSTALLER_EXPORT AliasFinder
+{
+public:
+ using AliasData = QVariantMap;
+ using AliasDataHash = QMultiHash<QString, AliasData>;
+
+ enum struct Resolution {
+ AddNew,
+ KeepExisting,
+ RemoveExisting
+ };
+
+ explicit AliasFinder(PackageManagerCore *core);
+ ~AliasFinder();
+
+ bool run();
+ QList<ComponentAlias *> aliases() const;
+
+ void setAliasSources(const QSet<AliasSource> &sources);
+
+private:
+ void clear();
+ Resolution checkPriorityAndVersion(const AliasData &data) const;
+
+ bool parseXml(AliasSource source);
+ bool parseJson(AliasSource source);
+
+private:
+ PackageManagerCore *const m_core;
+
+ QSet<AliasSource> m_sources;
+ AliasDataHash m_aliasData;
+ QHash<QString, ComponentAlias *> m_aliases;
+};
+
+class INSTALLER_EXPORT ComponentAlias : public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY_MOVE(ComponentAlias)
+
+public:
+ enum UnstableError {
+ ReferenceToUnstable = 0,
+ MissingComponent,
+ UnselectableComponent,
+ MissingAlias,
+ ComponentNameConfict
+ };
+ Q_ENUM(UnstableError)
+
+ ComponentAlias(PackageManagerCore *core);
+ ~ComponentAlias();
+
+ QString name() const;
+ QString displayName() const;
+ QString description() const;
+
+ QString version() const;
+
+ bool isVirtual() const;
+
+ bool isSelected() const;
+ void setSelected(bool selected);
+
+ QList<Component *> components();
+ QList<ComponentAlias *> aliases();
+
+ QString value(const QString &key, const QString &defaultValue = QString()) const;
+ void setValue(const QString &key, const QString &value);
+ QStringList keys() const;
+
+ bool isUnstable() const;
+ void setUnstable(UnstableError error, const QString &message = QString());
+
+private:
+ void addRequiredAliases(const QStringList &aliases, const bool optional);
+ void addRequiredComponents(const QStringList &components, const bool optional);
+
+private:
+ PackageManagerCore *const m_core;
+
+ QHash<QString, QString> m_variables;
+
+ bool m_selected;
+ bool m_unstable;
+
+ QList<Component *> m_components;
+ QList<ComponentAlias *> m_aliases;
+};
+
+} // namespace QInstaller
+
+Q_DECLARE_METATYPE(QInstaller::ComponentAlias *)
+Q_DECLARE_METATYPE(QInstaller::AliasSource);
+
+#endif // COMPONENTALIAS_H
diff --git a/src/libs/installer/componentmodel.cpp b/src/libs/installer/componentmodel.cpp
index 8407e5f2d..1e8dd1ff7 100644
--- a/src/libs/installer/componentmodel.cpp
+++ b/src/libs/installer/componentmodel.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.
@@ -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
@@ -223,7 +216,10 @@ QVariant ComponentModel::data(const QModelIndex &index, int role) const
return component->data(Qt::UserRole + index.column());
}
if (role == Qt::CheckStateRole) {
- if (!component->isCheckable() || !component->autoDependencies().isEmpty() || component->isUnstable())
+ if (!component->isCheckable() || component->isUnstable())
+ return QVariant();
+
+ if (!m_core->isUpdater() && !component->autoDependencies().isEmpty())
return QVariant();
}
if (role == ComponentModelHelper::ExpandedByDefault) {
@@ -240,7 +236,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 +257,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);
@@ -343,6 +335,22 @@ QSet<Component *> ComponentModel::uncheckable() const
return m_uncheckable;
}
+bool ComponentModel::componentsSelected() const
+{
+ if (m_core->isInstaller() || m_core->isUpdater())
+ return checked().count();
+
+ if (checkedState().testFlag(ComponentModel::DefaultChecked) == false)
+ return true;
+
+ const QSet<Component *> uncheckables = uncheckable();
+ for (auto &component : uncheckables) {
+ if (component->forcedInstallation() && !component->isInstalled())
+ return true; // allow installation for new forced components
+ }
+ return false;
+}
+
/*!
Returns a pointer to the PackageManagerCore this model belongs to.
*/
@@ -448,8 +456,7 @@ void ComponentModel::setCheckedState(QInstaller::ComponentModel::ModelStateFlag
default:
break;
}
- updateModelState();
- emit modelCheckStateChanged(m_modelState);
+ updateAndEmitModelState(); // update the internal state
}
@@ -487,11 +494,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 +516,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 +561,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,20 +572,22 @@ 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--) {
Component * const node = sortedNodes.at(i - 1);
- bool checkable = true;
- if (node->value(scCheckable, scTrue).toLower() == scFalse) {
- checkable = false;
- }
+ if (!node->isEnabled() || node->isUnstable())
+ continue;
- if ((!node->isCheckable() && checkable) || !node->isEnabled() || !node->autoDependencies().isEmpty() || node->isUnstable())
+ //Do not let forced installations to be uninstalled
+ if (!m_core->isUpdater() && node->forcedInstallation() && (node->checkState() != Qt::Unchecked))
continue;
+ if (!m_core->isUpdater() && !node->autoDependencies().isEmpty())
+ continue;
+
Qt::CheckState newState = state;
const Qt::CheckState recentState = node->checkState();
if (node->isTristate())
@@ -586,10 +596,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..c93dd60ae 100644
--- a/src/libs/installer/componentmodel.h
+++ b/src/libs/installer/componentmodel.h
@@ -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,8 @@
#include "qinstallerglobal.h"
+#include "component.h"
+
#include <QtCore/QAbstractItemModel>
#include <QtCore/QList>
#include <QtCore/QSet>
@@ -38,7 +40,6 @@
namespace QInstaller {
-class Component;
class PackageManagerCore;
class INSTALLER_EXPORT ComponentModel : public QAbstractItemModel
@@ -79,6 +80,7 @@ public:
QSet<Component *> partially() const;
QSet<Component *> unchecked() const;
QSet<Component *> uncheckable() const;
+ bool componentsSelected() const;
PackageManagerCore *core() const;
ComponentModel::ModelState checkedState() const;
@@ -91,17 +93,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..b68eebf06 100644
--- a/src/libs/installer/componentselectionpage_p.cpp
+++ b/src/libs/installer/componentselectionpage_p.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -28,12 +28,14 @@
#include "componentselectionpage_p.h"
+#include "globals.h"
#include "packagemanagergui.h"
#include "componentmodel.h"
#include "settings.h"
#include "component.h"
#include "fileutils.h"
#include "messageboxhandler.h"
+#include "customcombobox.h"
#include <QTreeView>
#include <QLabel>
@@ -44,13 +46,13 @@
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QCheckBox>
-#include <QHeaderView>
#include <QStandardPaths>
#include <QFileDialog>
#include <QStackedLayout>
#include <QStackedWidget>
-#include <QToolBox>
#include <QLineEdit>
+#include <QStandardItemModel>
+#include <QStyledItemDelegate>
namespace QInstaller {
@@ -60,23 +62,29 @@ namespace QInstaller {
\internal
*/
+constexpr int scNoCheckSelectionIndex = -1;
+constexpr int scCheckDefaultIndex = 0;
+constexpr int scCheckAllIndex = 1;
+constexpr int scUncheckAllIndex = 2;
+
ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionPage *qq, PackageManagerCore *core)
: q(qq)
, m_core(core)
, m_treeView(new QTreeView(q))
- , m_allModel(m_core->defaultComponentModel())
- , m_updaterModel(m_core->updaterComponentModel())
- , m_currentModel(m_allModel)
- , m_allowCompressedRepositoryInstall(false)
- , m_toolBox(nullptr)
+ , m_tabWidget(nullptr)
, m_descriptionBaseWidget(nullptr)
, m_categoryWidget(Q_NULLPTR)
+ , m_allowCreateOfflineInstaller(false)
, m_categoryLayoutVisible(false)
- , m_proxyModel(new ComponentSortFilterProxyModel(q))
+ , m_allModel(m_core->defaultComponentModel())
+ , m_updaterModel(m_core->updaterComponentModel())
+ , m_currentModel(m_allModel)
+ , m_proxyModel(m_core->componentSortFilterProxyModel())
+ , m_componentsResolved(false)
+ , m_headerStretchLastSection(false)
{
m_treeView->setObjectName(QLatin1String("ComponentsTreeView"));
- m_proxyModel->setRecursiveFilteringEnabled(true);
- m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
+ m_treeView->setUniformRowHeights(true);
m_descriptionBaseWidget = new QWidget(q);
m_descriptionBaseWidget->setObjectName(QLatin1String("DescriptionBaseWidget"));
@@ -85,8 +93,12 @@ ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionP
descriptionVLayout->setObjectName(QLatin1String("DescriptionLayout"));
descriptionVLayout->setContentsMargins(0, 0, 0, 0);
- m_toolBox = new QToolBox(q);
- m_toolBox->setObjectName(QLatin1String("ToolBox"));
+ m_tabWidget = new QTabWidget(q);
+ m_tabWidget->setObjectName(QLatin1String("ComponentSelectionTabWidget"));
+ m_tabWidget->tabBar()->setObjectName(QLatin1String("ComponentSelectionTabBar"));
+ m_tabWidget->hide();
+
+ m_rightSideVLayout = new QVBoxLayout;
QScrollArea *descriptionScrollArea = new QScrollArea(q);
descriptionScrollArea->setWidgetResizable(true);
@@ -107,46 +119,67 @@ ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionP
m_sizeLabel->setObjectName(QLatin1String("ComponentSizeLabel"));
descriptionVLayout->addWidget(m_sizeLabel);
- QHBoxLayout *buttonHLayout = new QHBoxLayout;
- m_checkDefault = new QPushButton;
- connect(m_checkDefault, &QAbstractButton::clicked,
- this, &ComponentSelectionPagePrivate::selectDefault);
+ m_createOfflinePushButton = new QPushButton(q);
+ m_createOfflinePushButton->setVisible(false);
+ m_createOfflinePushButton->setText(ComponentSelectionPage::tr("Create Offline Installer"));
+ m_createOfflinePushButton->setToolTip(
+ ComponentSelectionPage::tr("Create offline installer from selected components, instead "
+ "of installing now."));
+
+ connect(m_createOfflinePushButton, &QPushButton::clicked,
+ this, &ComponentSelectionPagePrivate::createOfflineButtonClicked);
+ connect(q, &ComponentSelectionPage::completeChanged,
+ this, [&]() { m_createOfflinePushButton->setEnabled(q->isComplete()); });
+
+ m_qbspPushButton = new QPushButton(q);
+ m_qbspPushButton->setVisible(false);
+ m_qbspPushButton->setText(ComponentSelectionPage::tr("Browse &QBSP files"));
+ m_qbspPushButton->setToolTip(
+ ComponentSelectionPage::tr("Select a Qt Board Support Package file to install "
+ "additional content that is not directly available from the online repositories."));
+
+ connect(m_qbspPushButton, &QPushButton::clicked,
+ this, &ComponentSelectionPagePrivate::qbspButtonClicked);
+
+ m_rightSideVLayout->addWidget(m_descriptionBaseWidget);
+ m_rightSideVLayout->addWidget(m_createOfflinePushButton);
+ m_rightSideVLayout->addWidget(m_qbspPushButton);
+
+ QHBoxLayout *topHLayout = new QHBoxLayout;
+
+ // Using custom combobox to workaround QTBUG-90595
+ m_checkStateComboBox = new CustomComboBox(q);
+#ifdef Q_OS_MACOS
+ QStyledItemDelegate *delegate = new QStyledItemDelegate(this);
+ m_checkStateComboBox->setItemDelegate(delegate);
+#endif
+ m_checkStateComboBox->setObjectName(QLatin1String("CheckStateComboBox"));
+ topHLayout->addWidget(m_checkStateComboBox);
+
+ connect(m_checkStateComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
+ this, &ComponentSelectionPagePrivate::updateAllCheckStates);
+
+ // Workaround invisible placeholder text
+ QPalette palette = m_checkStateComboBox->palette();
+ palette.setColor(QPalette::PlaceholderText, palette.color(QPalette::Text));
+ m_checkStateComboBox->setPalette(palette);
+
+ m_checkStateComboBox->setPlaceholderText(ComponentSelectionPage::tr("Select"));
if (m_core->isInstaller()) {
- m_checkDefault->setObjectName(QLatin1String("SelectDefaultComponentsButton"));
- m_checkDefault->setShortcut(QKeySequence(ComponentSelectionPage::tr("Alt+A",
- "Select default components")));
- m_checkDefault->setText(ComponentSelectionPage::tr("Def&ault"));
- m_checkDefault->setToolTip(ComponentSelectionPage::tr("Select default components in the tree view."));
+ m_checkStateComboBox->insertItem(scCheckDefaultIndex, ComponentSelectionPage::tr("Default"));
+ m_checkStateComboBox->setItemData(scCheckDefaultIndex,
+ ComponentSelectionPage::tr("Select default components in the tree view."), Qt::ToolTipRole);
} else {
- m_checkDefault->setEnabled(false);
- m_checkDefault->setObjectName(QLatin1String("ResetComponentsButton"));
- m_checkDefault->setShortcut(QKeySequence(ComponentSelectionPage::tr("Alt+R",
- "Reset to already installed components")));
- m_checkDefault->setText(ComponentSelectionPage::tr("&Reset"));
- m_checkDefault->setToolTip(
- ComponentSelectionPage::tr("Reset all components to their original selection state in the tree view."));
+ m_checkStateComboBox->insertItem(scCheckDefaultIndex, ComponentSelectionPage::tr("Reset"));
+ m_checkStateComboBox->setItemData(scCheckDefaultIndex,
+ ComponentSelectionPage::tr("Reset all components to their original selection state in the tree view."), Qt::ToolTipRole);
}
- buttonHLayout->addWidget(m_checkDefault);
-
- m_checkAll = new QPushButton;
- connect(m_checkAll, &QAbstractButton::clicked,
- this, &ComponentSelectionPagePrivate::selectAll);
- m_checkAll->setObjectName(QLatin1String("SelectAllComponentsButton"));
- m_checkAll->setShortcut(QKeySequence(ComponentSelectionPage::tr("Alt+S",
- "Select all components")));
- m_checkAll->setText(ComponentSelectionPage::tr("&Select All"));
- m_checkAll->setToolTip(ComponentSelectionPage::tr("Select all components in the tree view."));
- buttonHLayout->addWidget(m_checkAll);
-
- m_uncheckAll = new QPushButton;
- connect(m_uncheckAll, &QAbstractButton::clicked,
- this, &ComponentSelectionPagePrivate::deselectAll);
- m_uncheckAll->setObjectName(QLatin1String("DeselectAllComponentsButton"));
- m_uncheckAll->setShortcut(QKeySequence(ComponentSelectionPage::tr("Alt+D",
- "Deselect all components")));
- m_uncheckAll->setText(ComponentSelectionPage::tr("&Deselect All"));
- m_uncheckAll->setToolTip(ComponentSelectionPage::tr("Deselect all components in the tree view."));
- buttonHLayout->addWidget(m_uncheckAll);
+ m_checkStateComboBox->insertItem(scCheckAllIndex, ComponentSelectionPage::tr("Select All"));
+ m_checkStateComboBox->setItemData(scCheckAllIndex,
+ ComponentSelectionPage::tr("Select all components in the tree view."), Qt::ToolTipRole);
+ m_checkStateComboBox->insertItem(scUncheckAllIndex, ComponentSelectionPage::tr("Deselect All"));
+ m_checkStateComboBox->setItemData(scUncheckAllIndex,
+ ComponentSelectionPage::tr("Deselect all components in the tree view."), Qt::ToolTipRole);
QWidget *progressStackedWidget = new QWidget();
QVBoxLayout *metaLayout = new QVBoxLayout(progressStackedWidget);
@@ -166,17 +199,24 @@ ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionP
connect(m_searchLineEdit, &QLineEdit::textChanged,
this, &ComponentSelectionPagePrivate::setSearchPattern);
connect(q, &ComponentSelectionPage::entered, m_searchLineEdit, &QLineEdit::clear);
+ topHLayout->addWidget(m_searchLineEdit);
QVBoxLayout *treeViewVLayout = new QVBoxLayout;
treeViewVLayout->setObjectName(QLatin1String("TreeviewLayout"));
treeViewVLayout->addWidget(m_treeView, 3);
- treeViewVLayout->addWidget(m_searchLineEdit);
QWidget *mainStackedWidget = new QWidget();
m_mainGLayout = new QGridLayout(mainStackedWidget);
- m_mainGLayout->addLayout(buttonHLayout, 0, 0);
+ {
+ int left = 0;
+ int top = 0;
+ int bottom = 0;
+ m_mainGLayout->getContentsMargins(&left, &top, nullptr, &bottom);
+ m_mainGLayout->setContentsMargins(left, top, 0, bottom);
+ }
+ m_mainGLayout->addLayout(topHLayout, 0, 0);
m_mainGLayout->addLayout(treeViewVLayout, 1, 0);
- m_mainGLayout->addWidget(m_descriptionBaseWidget, 1, 1);
+ m_mainGLayout->addLayout(m_rightSideVLayout, 0, 1, 0, -1);
m_mainGLayout->setColumnStretch(0, 3);
m_mainGLayout->setColumnStretch(1, 2);
@@ -185,25 +225,15 @@ 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)));
connect(m_core, &PackageManagerCore::metaJobTotalProgress, this,
&ComponentSelectionPagePrivate::setTotalProgress);
-
-#ifdef INSTALLCOMPRESSED
- allowCompressedRepositoryInstall();
-#endif
}
ComponentSelectionPagePrivate::~ComponentSelectionPagePrivate()
@@ -211,36 +241,28 @@ ComponentSelectionPagePrivate::~ComponentSelectionPagePrivate()
}
-void ComponentSelectionPagePrivate::allowCompressedRepositoryInstall()
+void ComponentSelectionPagePrivate::setAllowCreateOfflineInstaller(bool allow)
{
- m_allowCompressedRepositoryInstall = true;
+ m_allowCreateOfflineInstaller = allow;
}
void ComponentSelectionPagePrivate::showCompressedRepositoryButton()
{
- QWizard *wizard = qobject_cast<QWizard*>(m_core->guiObject());
- if (wizard && !(wizard->options() & QWizard::HaveCustomButton2) && m_allowCompressedRepositoryInstall) {
- wizard->setOption(QWizard::HaveCustomButton2, true);
- wizard->setButtonText(QWizard::CustomButton2,
- ComponentSelectionPage::tr("&Browse QBSP files"));
- wizard->button(QWizard::CustomButton2)->setToolTip(
- ComponentSelectionPage::tr("Select a Qt Board Support Package file to install "
- "additional content that is not directly available from the online repositories."));
- connect(wizard, &QWizard::customButtonClicked,
- this, &ComponentSelectionPagePrivate::customButtonClicked);
- q->gui()->updateButtonLayout();
- }
+ if (m_core->allowCompressedRepositoryInstall())
+ m_qbspPushButton->setVisible(true);
}
void ComponentSelectionPagePrivate::hideCompressedRepositoryButton()
{
- QWizard *wizard = qobject_cast<QWizard*>(m_core->guiObject());
- if (wizard && (wizard->options() & QWizard::HaveCustomButton2)) {
- wizard->setOption(QWizard::HaveCustomButton2, false);
- disconnect(wizard, &QWizard::customButtonClicked,
- this, &ComponentSelectionPagePrivate::customButtonClicked);
- q->gui()->updateButtonLayout();
- }
+ m_qbspPushButton->setVisible(false);
+}
+
+void ComponentSelectionPagePrivate::showCreateOfflineInstallerButton(bool show)
+{
+ if (show && m_allowCreateOfflineInstaller)
+ m_createOfflinePushButton->setVisible(m_core->isInstaller() && !m_core->isOfflineOnly());
+ else
+ m_createOfflinePushButton->setVisible(false);
}
void ComponentSelectionPagePrivate::setupCategoryLayout()
@@ -274,7 +296,7 @@ void ComponentSelectionPagePrivate::setupCategoryLayout()
vLayout->addWidget(m_categoryGroupBox);
vLayout->addStretch();
- m_toolBox->insertItem(1, m_categoryWidget, m_core->settings().repositoryCategoryDisplayName());
+ m_tabWidget->insertTab(1, m_categoryWidget, m_core->settings().repositoryCategoryDisplayName());
}
void ComponentSelectionPagePrivate::showCategoryLayout(bool show)
@@ -287,21 +309,22 @@ void ComponentSelectionPagePrivate::showCategoryLayout(bool show)
setupCategoryLayout();
if (show) {
- m_mainGLayout->removeWidget(m_descriptionBaseWidget);
- m_toolBox->insertItem(0, m_descriptionBaseWidget, tr("Component Information"));
- m_mainGLayout->addWidget(m_toolBox, 1, 1);
+ m_rightSideVLayout->removeWidget(m_descriptionBaseWidget);
+ m_tabWidget->insertTab(0, m_descriptionBaseWidget, tr("Information"));
+ m_rightSideVLayout->insertWidget(0, m_tabWidget);
} else {
- m_toolBox->removeItem(0);
- m_mainGLayout->removeWidget(m_toolBox);
- m_mainGLayout->addWidget(m_descriptionBaseWidget, 1, 1);
+ m_tabWidget->removeTab(0);
+ m_rightSideVLayout->removeWidget(m_tabWidget);
+ m_rightSideVLayout->insertWidget(0, m_descriptionBaseWidget);
+ m_descriptionBaseWidget->setVisible(true);
}
- m_toolBox->setVisible(show);
+ m_tabWidget->setVisible(show);
m_categoryLayoutVisible = show;
}
void ComponentSelectionPagePrivate::updateTreeView()
{
- m_checkDefault->setVisible(m_core->isInstaller() || m_core->isPackageManager());
+ setComboBoxItemEnabled(scCheckDefaultIndex, m_core->isInstaller() || m_core->isPackageManager());
if (m_treeView->selectionModel()) {
disconnect(m_treeView->selectionModel(), &QItemSelectionModel::currentChanged,
this, &ComponentSelectionPagePrivate::currentSelectedChanged);
@@ -374,6 +397,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 +415,16 @@ void ComponentSelectionPagePrivate::expandSearchResults()
index = index.parent();
}
}
+ restoreHeaderResizeModes();
+}
+
+/*!
+ Returns \c true if the components to install and uninstall are calculated
+ successfully, \c false otherwise.
+*/
+bool ComponentSelectionPagePrivate::componentsResolved() const
+{
+ return m_componentsResolved;
}
void ComponentSelectionPagePrivate::currentSelectedChanged(const QModelIndex &current)
@@ -414,6 +450,32 @@ void ComponentSelectionPagePrivate::currentSelectedChanged(const QModelIndex &cu
}
}
+/*!
+ Updates the checkstate of the components based on the value of \c which.
+*/
+void ComponentSelectionPagePrivate::updateAllCheckStates(int which)
+{
+ switch (which) {
+ case scNoCheckSelectionIndex:
+ // A 'helper' text index, no selection
+ return;
+ case scCheckDefaultIndex:
+ selectDefault();
+ break;
+ case scCheckAllIndex:
+ selectAll();
+ break;
+ case scUncheckAllIndex:
+ deselectAll();
+ break;
+ default:
+ qCWarning(lcInstallerInstallLog) << "Invalid index for check state selection!";
+ break;
+ }
+ // Reset back to 'helper' text index
+ m_checkStateComboBox->setCurrentIndex(scNoCheckSelectionIndex);
+}
+
void ComponentSelectionPagePrivate::selectAll()
{
m_currentModel->setCheckedState(ComponentModel::AllChecked);
@@ -424,27 +486,6 @@ void ComponentSelectionPagePrivate::deselectAll()
m_currentModel->setCheckedState(ComponentModel::AllUnchecked);
}
-void ComponentSelectionPagePrivate::enableRepositoryCategory(const QString &repositoryName, bool enable)
-{
- QMap<QString, RepositoryCategory> organizedRepositoryCategories = m_core->settings().organizedRepositoryCategories();
-
- QMap<QString, RepositoryCategory>::iterator i = organizedRepositoryCategories.find(repositoryName);
- RepositoryCategory repoCategory;
- while (i != organizedRepositoryCategories.end() && i.key() == repositoryName) {
- repoCategory = i.value();
- i++;
- }
-
- RepositoryCategory replacement = repoCategory;
- replacement.setEnabled(enable);
- QSet<RepositoryCategory> tmpRepoCategories = m_core->settings().repositoryCategories();
- if (tmpRepoCategories.contains(repoCategory)) {
- tmpRepoCategories.remove(repoCategory);
- tmpRepoCategories.insert(replacement);
- m_core->settings().addRepositoryCategories(tmpRepoCategories);
- }
-}
-
void ComponentSelectionPagePrivate::updateWidgetVisibility(bool show)
{
if (show)
@@ -452,8 +493,7 @@ void ComponentSelectionPagePrivate::updateWidgetVisibility(bool show)
else
m_stackedLayout->setCurrentIndex(0);
- if (QAbstractButton *bspButton = q->gui()->button(QWizard::CustomButton2))
- bspButton->setEnabled(!show);
+ m_qbspPushButton->setEnabled(!show);
if (show) {
q->gui()->button(QWizard::NextButton)->setEnabled(false);
@@ -473,7 +513,7 @@ void ComponentSelectionPagePrivate::fetchRepositoryCategories()
QList<QCheckBox*> checkboxes = m_categoryGroupBox->findChildren<QCheckBox *>();
for (int i = 0; i < checkboxes.count(); i++) {
QCheckBox *checkbox = checkboxes.at(i);
- enableRepositoryCategory(checkbox->objectName(), checkbox->isChecked());
+ m_core->enableRepositoryCategory(checkbox->objectName(), checkbox->isChecked());
}
if (!m_core->fetchRemotePackagesTree()) {
@@ -484,31 +524,28 @@ void ComponentSelectionPagePrivate::fetchRepositoryCategories()
m_searchLineEdit->text().isEmpty() ? expandDefault() : expandSearchResults();
}
-void ComponentSelectionPagePrivate::customButtonClicked(int which)
+void ComponentSelectionPagePrivate::createOfflineButtonClicked()
{
- if (QWizard::WizardButton(which) == QWizard::CustomButton2) {
- QString defaultDownloadDirectory =
- QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
- QStringList fileNames = QFileDialog::getOpenFileNames(nullptr,
- ComponentSelectionPage::tr("Open File"),defaultDownloadDirectory,
- QLatin1String("QBSP or 7z Files (*.qbsp *.7z)"));
-
- QSet<Repository> set;
- foreach (QString fileName, fileNames) {
- Repository repository = Repository::fromUserInput(fileName, true);
- repository.setEnabled(true);
- set.insert(repository);
- }
- if (set.count() > 0) {
- updateWidgetVisibility(true);
- m_core->settings().addTemporaryRepositories(set, false);
- if (!m_core->fetchCompressedPackagesTree()) {
- MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
- QLatin1String("FailToFetchPackages"), tr("Error"), m_core->error());
- }
+ m_core->setOfflineGenerator();
+ q->gui()->button(QWizard::NextButton)->click();
+}
+
+void ComponentSelectionPagePrivate::qbspButtonClicked()
+{
+ QString defaultDownloadDirectory =
+ QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
+ QStringList fileNames = QFileDialog::getOpenFileNames(nullptr,
+ ComponentSelectionPage::tr("Open File"),defaultDownloadDirectory,
+ QLatin1String("QBSP or 7z Files (*.qbsp *.7z)"));
+
+ if (m_core->addQBspRepositories(fileNames)) {
+ updateWidgetVisibility(true);
+ if (!m_core->fetchCompressedPackagesTree()) {
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("FailToFetchPackages"), tr("Error"), m_core->error());
}
- updateWidgetVisibility(false);
}
+ updateWidgetVisibility(false);
}
/*!
@@ -543,11 +580,20 @@ void ComponentSelectionPagePrivate::selectDefault()
void ComponentSelectionPagePrivate::onModelStateChanged(QInstaller::ComponentModel::ModelState state)
{
if (state.testFlag(ComponentModel::Empty)) {
- m_checkAll->setEnabled(false);
- m_uncheckAll->setEnabled(false);
- m_checkDefault->setEnabled(false);
+ setComboBoxItemEnabled(scCheckAllIndex, false);
+ setComboBoxItemEnabled(scUncheckAllIndex, false);
+ setComboBoxItemEnabled(scCheckDefaultIndex, false);
return;
}
+
+ m_componentsResolved = m_core->recalculateAllComponents();
+ if (!m_componentsResolved) {
+ const QString error = !m_core->componentsToInstallError().isEmpty()
+ ? m_core->componentsToInstallError() : m_core->componentsToUninstallError();
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("CalculateComponentsError"), tr("Error"), error);
+ }
+
q->setModified(state.testFlag(ComponentModel::DefaultChecked) == false);
// If all components in the checked list are only checkable when run without forced
// installation, set ComponentModel::AllUnchecked as well, as we cannot uncheck anything.
@@ -557,9 +603,9 @@ void ComponentSelectionPagePrivate::onModelStateChanged(QInstaller::ComponentMod
state |= ComponentModel::AllUnchecked;
}
// enable the button if the corresponding flag is not set
- m_checkAll->setEnabled(state.testFlag(ComponentModel::AllChecked) == false);
- m_uncheckAll->setEnabled(state.testFlag(ComponentModel::AllUnchecked) == false);
- m_checkDefault->setEnabled(state.testFlag(ComponentModel::DefaultChecked) == false);
+ setComboBoxItemEnabled(scCheckAllIndex, state.testFlag(ComponentModel::AllChecked) == false);
+ setComboBoxItemEnabled(scUncheckAllIndex, state.testFlag(ComponentModel::AllUnchecked) == false);
+ setComboBoxItemEnabled(scCheckDefaultIndex, state.testFlag(ComponentModel::DefaultChecked) == false);
// update the current selected node (important to reflect possible sub-node changes)
if (m_treeView->selectionModel())
@@ -588,4 +634,45 @@ 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));
+}
+
+/*!
+ Sets the enabled state of the combo box item in \a index to \a enabled.
+*/
+void ComponentSelectionPagePrivate::setComboBoxItemEnabled(int index, bool enabled)
+{
+ auto *model = qobject_cast<QStandardItemModel *>(m_checkStateComboBox->model());
+ if (!model)
+ return;
+
+ QStandardItem *item = model->item(index);
+ if (!item)
+ return;
+
+ item->setEnabled(enabled);
+}
+
} // namespace QInstaller
diff --git a/src/libs/installer/componentselectionpage_p.h b/src/libs/installer/componentselectionpage_p.h
index fc37ebdaa..187fce61d 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) 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 <QObject>
#include <QWidget>
+#include <QHeaderView>
#include "componentmodel.h"
#include "packagemanagergui.h"
@@ -47,13 +48,13 @@ class QVBoxLayout;
class QHBoxLayout;
class QGridLayout;
class QStackedLayout;
-class QToolBox;
namespace QInstaller {
class PackageManagerCore;
class ComponentModel;
class ComponentSelectionPage;
+class CustomComboBox;
class ComponentSelectionPagePrivate : public QObject
{
@@ -65,23 +66,26 @@ public:
explicit ComponentSelectionPagePrivate(ComponentSelectionPage *qq, PackageManagerCore *core);
~ComponentSelectionPagePrivate();
- void allowCompressedRepositoryInstall();
+ void setAllowCreateOfflineInstaller(bool allow);
void showCompressedRepositoryButton();
void hideCompressedRepositoryButton();
+ void showCreateOfflineInstallerButton(bool show);
void setupCategoryLayout();
void showCategoryLayout(bool show);
void updateTreeView();
void expandDefault();
void expandSearchResults();
+ bool componentsResolved() const;
public slots:
void currentSelectedChanged(const QModelIndex &current);
+ void updateAllCheckStates(int which);
void selectAll();
void deselectAll();
- void enableRepositoryCategory(const QString &repositoryName, bool enable);
void updateWidgetVisibility(bool show);
void fetchRepositoryCategories();
- void customButtonClicked(int which);
+ void createOfflineButtonClicked();
+ void qbspButtonClicked();
void onProgressChanged(int progress);
void setMessage(const QString &msg);
void setTotalProgress(int totalProgress);
@@ -90,22 +94,28 @@ public slots:
void setSearchPattern(const QString &text);
private:
+ void storeHeaderResizeModes();
+ void restoreHeaderResizeModes();
+ void setComboBoxItemEnabled(int index, bool enabled);
+
+private:
ComponentSelectionPage *q;
PackageManagerCore *m_core;
QTreeView *m_treeView;
- QToolBox *m_toolBox;
+ QTabWidget *m_tabWidget;
QWidget *m_descriptionBaseWidget;
QLabel *m_sizeLabel;
QLabel *m_descriptionLabel;
- QPushButton *m_checkAll;
- QPushButton *m_uncheckAll;
- QPushButton *m_checkDefault;
+ QPushButton *m_createOfflinePushButton;
+ QPushButton *m_qbspPushButton;
+ CustomComboBox *m_checkStateComboBox;
QWidget *m_categoryWidget;
QGroupBox *m_categoryGroupBox;
QLabel *m_metadataProgressLabel;
QProgressBar *m_progressBar;
QGridLayout *m_mainGLayout;
- bool m_allowCompressedRepositoryInstall;
+ QVBoxLayout *m_rightSideVLayout;
+ bool m_allowCreateOfflineInstaller;
bool m_categoryLayoutVisible;
ComponentModel *m_allModel;
ComponentModel *m_updaterModel;
@@ -113,6 +123,10 @@ private:
QStackedLayout *m_stackedLayout;
ComponentSortFilterProxyModel *m_proxyModel;
QLineEdit *m_searchLineEdit;
+ bool m_componentsResolved;
+
+ bool m_headerStretchLastSection;
+ QHash<int, QHeaderView::ResizeMode> m_headerResizeModes;
};
} // namespace QInstaller
diff --git a/src/libs/installer/concurrentoperationrunner.cpp b/src/libs/installer/concurrentoperationrunner.cpp
index dfcae44dd..b0eb5f582 100644
--- a/src/libs/installer/concurrentoperationrunner.cpp
+++ b/src/libs/installer/concurrentoperationrunner.cpp
@@ -156,7 +156,7 @@ QHash<Operation *, bool> ConcurrentOperationRunner::run()
this, &ConcurrentOperationRunner::onOperationfinished);
futureWatcher->setFuture(QtConcurrent::run(m_threadPool,
- this, &ConcurrentOperationRunner::runOperation, operation));
+ [this, operation] { return runOperation(operation); }));
}
if (!m_operationWatchers.isEmpty()) {
diff --git a/src/libs/installer/constants.h b/src/libs/installer/constants.h
index 485982672..7bf816b5f 100644
--- a/src/libs/installer/constants.h
+++ b/src/libs/installer/constants.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -41,6 +41,7 @@ static const QLatin1String scScript("script");
static const QLatin1String scAllUsersStartMenuProgramsPath("AllUsersStartMenuProgramsPath");
static const QLatin1String scUserStartMenuProgramsPath("UserStartMenuProgramsPath");
static const QLatin1String scUILanguage("UILanguage");
+static const QLatin1String scUpdatesXML("Updates.xml");
static const QLatin1String scName("Name");
static const QLatin1String scVersion("Version");
@@ -59,6 +60,11 @@ static const QLatin1String scDisplayName("DisplayName");
static const QLatin1String scTreeName("TreeName");
static const QLatin1String scAutoTreeName("AutoTreeName");
static const QLatin1String scDependencies("Dependencies");
+static const QLatin1String scAlias("Alias");
+static const QLatin1String scRequiredAliases("RequiredAliases");
+static const QLatin1String scRequiredComponents("RequiredComponents");
+static const QLatin1String scOptionalAliases("OptionalAliases");
+static const QLatin1String scOptionalComponents("OptionalComponents");
static const QLatin1String scLocalDependencies("LocalDependencies");
static const QLatin1String scAutoDependOn("AutoDependOn");
static const QLatin1String scNewComponent("NewComponent");
@@ -72,11 +78,66 @@ static const QLatin1String scOfflineBinaryName("OfflineBinaryName");
static const QLatin1String scSHA1("SHA1");
static const QLatin1String scMetadataName("MetadataName");
static const QLatin1String scContentSha1("ContentSha1");
+static const QLatin1String scCheckSha1CheckSum("CheckSha1CheckSum");
+
+static const char * const scClearCacheHint = QT_TR_NOOP(
+ "This may be solved by restarting the application after clearing the cache from:");
+
+// symbols
+static const QLatin1String scCaretSymbol("^");
+static const QLatin1String scCommaWithSpace(", ");
+static const QLatin1String scBr("<br>");
// constants used throughout the components class
static const QLatin1String scVirtual("Virtual");
static const QLatin1String scSortingPriority("SortingPriority");
static const QLatin1String scCheckable("Checkable");
+static const QLatin1String scScriptTag("Script");
+static const QLatin1String scInstalled("Installed");
+static const QLatin1String scUpdateText("UpdateText");
+static const QLatin1String scUninstalled("Uninstalled");
+static const QLatin1String scCurrentState("CurrentState");
+static const QLatin1String scForcedInstallation("ForcedInstallation");
+static const QLatin1String scExpandedByDefault("ExpandedByDefault");
+static const QLatin1String scUnstable("Unstable");
+static const QLatin1String scTargetDirPlaceholder("@TargetDir@");
+static const QLatin1String scTargetDirPlaceholderWithArg("@TargetDir@%1");
+static const QLatin1String scLastUpdateDate("LastUpdateDate");
+static const QLatin1String scInstallDate("InstallDate");
+static const QLatin1String scUserInterfaces("UserInterfaces");
+static const QLatin1String scTranslations("Translations");
+static const QLatin1String scLicenses("Licenses");
+static const QLatin1String scLicensesValue("licenses");
+static const QLatin1String scLicense("License");
+static const QLatin1String scOperations("Operations");
+static const QLatin1String scInstallScript("installScript");
+static const QLatin1String scPostLoadScript("postLoadScript");
+static const QLatin1String scComponent("Component");
+static const QLatin1String scComponentSmall("component");
+static const QLatin1String scRetranslateUi("retranslateUi");
+static const QLatin1String scEn("en");
+static const QLatin1String scIfw_("ifw_");
+static const QLatin1String scFile("file");
+static const QLatin1String scContent("content");
+static const QLatin1String scExtract("Extract");
+static const QLatin1String scSha1("sha1");
+static const QLatin1String scCreateOperationsForPath("createOperationsForPath");
+static const QLatin1String scCreateOperationsForArchive("createOperationsForArchive");
+static const QLatin1String scCreateOperations("createOperations");
+static const QLatin1String scBeginInstallation("beginInstallation");
+static const QLatin1String scMinimumProgress("MinimumProgress");
+static const QLatin1String scDelete("Delete");
+static const QLatin1String scCopy("Copy");
+static const QLatin1String scMkdir("Mkdir");
+static const QLatin1String scIsDefault("isDefault");
+static const QLatin1String scAdmin("admin");
+static const QLatin1String scTwoArgs("%1/%2/");
+static const QLatin1String scThreeArgs("%1/%2/%3");
+static const QLatin1String scComponentScriptTest("var component = installer.componentByName('%1'); component.name;");
+static const QLatin1String scInstallerPrefix("installer://");
+static const QLatin1String scInstallerPrefixWithOneArgs("installer://%1/");
+static const QLatin1String scInstallerPrefixWithTwoArgs("installer://%1/%2");
+static const QLatin1String scLocalesArgs("%1%2_%3.%4");
// constants used throughout the settings and package manager core class
static const QLatin1String scTitle("Title");
@@ -96,6 +157,7 @@ static const QLatin1String scRemoteRepositories("RemoteRepositories");
static const QLatin1String scRepositoryCategories("RepositoryCategories");
static const QLatin1String scRepositorySettingsPageVisible("RepositorySettingsPageVisible");
static const QLatin1String scAllowSpaceInPath("AllowSpaceInPath");
+static const QLatin1String scAllowRepositoriesForOfflineInstaller("AllowRepositoriesForOfflineInstaller");
static const QLatin1String scWizardStyle("WizardStyle");
static const QLatin1String scStyleSheet("StyleSheet");
static const QLatin1String scTitleColor("TitleColor");
@@ -118,7 +180,15 @@ static const QLatin1String scBanner("Banner");
static const QLatin1String scLogo("Logo");
static const QLatin1String scBackground("Background");
static const QLatin1String scPageListPixmap("PageListPixmap");
+static const QLatin1String scAliasDefinitionsFile("AliasDefinitionsFile");
const char scRelocatable[] = "@RELOCATABLE_PATH@";
+
+static const QStringList scMetaElements = {
+ QLatin1String("Script"),
+ QLatin1String("Licenses"),
+ QLatin1String("UserInterfaces"),
+ QLatin1String("Translations")
+};
}
namespace CommandLineOptions {
@@ -215,6 +285,7 @@ static const QLatin1String scFilterPackagesShort("fp");
static const QLatin1String scFilterPackagesLong("filter-packages");
static const QLatin1String scLocalCachePathShort("cp");
static const QLatin1String scLocalCachePathLong("cache-path");
+static const QLatin1String scTypeLong("type");
// Developer options
static const QLatin1String scScriptShort("s");
diff --git a/src/libs/installer/copydirectoryoperation.cpp b/src/libs/installer/copydirectoryoperation.cpp
index a2ef2cf5a..c0fec0649 100644
--- a/src/libs/installer/copydirectoryoperation.cpp
+++ b/src/libs/installer/copydirectoryoperation.cpp
@@ -153,7 +153,7 @@ bool CopyDirectoryOperation::performOperation()
bool CopyDirectoryOperation::undoOperation()
{
- if (parseUndoOperationArguments().count() > 0)
+ if (skipUndoOperation())
return true;
if (!checkArgumentCount(2))
diff --git a/src/libs/installer/copyfiletask.cpp b/src/libs/installer/copyfiletask.cpp
index 72b28d896..856feda01 100644
--- a/src/libs/installer/copyfiletask.cpp
+++ b/src/libs/installer/copyfiletask.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -112,7 +112,7 @@ void CopyFileTask::doTask(QFutureInterface<FileTaskResult> &fi)
observer.addSample(read);
observer.timerEvent(nullptr);
observer.addBytesTransfered(read);
- observer.addCheckSumData(buffer.data(), read);
+ observer.addCheckSumData(buffer.left(read));
fi.setProgressValueAndText(observer.progressValue(), observer.progressText());
}
diff --git a/src/libs/installer/createdesktopentryoperation.cpp b/src/libs/installer/createdesktopentryoperation.cpp
index 6c92cc178..a19fd773a 100644
--- a/src/libs/installer/createdesktopentryoperation.cpp
+++ b/src/libs/installer/createdesktopentryoperation.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -32,6 +32,7 @@
#include "globals.h"
#include "adminauthorization.h"
#include "remoteclient.h"
+#include "packagemanagercore.h"
#include <QDir>
#include <QFile>
@@ -64,7 +65,7 @@ QString CreateDesktopEntryOperation::absoluteFileName()
XDG_DATA_HOME.push_back(QDir::home().absoluteFilePath(QLatin1String(".local/share"))); // default user-specific path
- if (AdminAuthorization::hasAdminRights() || RemoteClient::instance().isActive())
+ if (packageManager() && packageManager()->hasAdminRights())
XDG_DATA_HOME.push_front(QLatin1String("/usr/local/share")); // default system-wide path
const QStringList directories = XDG_DATA_HOME;
@@ -151,15 +152,16 @@ bool CreateDesktopEntryOperation::performOperation()
setDefaultFilePermissions(filename, DefaultFilePermissions::Executable);
- QTextStream stream(&file);
- stream.setCodec("UTF-8");
- stream << QLatin1String("[Desktop Entry]") << endl;
+ QString outString;
+ QTextStream stream(&outString);
+ stream << QLatin1String("[Desktop Entry]") << Qt::endl;
// Type=Application\nExec=qtcreator\nPath=...
const QStringList pairs = values.split(QLatin1Char('\n'));
for (QStringList::const_iterator it = pairs.begin(); it != pairs.end(); ++it)
- stream << *it << endl;
+ stream << *it << Qt::endl;
+ file.write(outString.toUtf8());
return true;
}
diff --git a/src/libs/installer/createlocalrepositoryoperation.cpp b/src/libs/installer/createlocalrepositoryoperation.cpp
index 7090f9a8b..286cc9b5b 100644
--- a/src/libs/installer/createlocalrepositoryoperation.cpp
+++ b/src/libs/installer/createlocalrepositoryoperation.cpp
@@ -378,7 +378,7 @@ bool CreateLocalRepositoryOperation::performOperation()
bool CreateLocalRepositoryOperation::undoOperation()
{
- if (parseUndoOperationArguments().count() > 0)
+ if (skipUndoOperation())
return true;
if (!checkArgumentCount(2))
diff --git a/src/libs/installer/createshortcutoperation.cpp b/src/libs/installer/createshortcutoperation.cpp
index 57f901c2f..894b5843b 100644
--- a/src/libs/installer/createshortcutoperation.cpp
+++ b/src/libs/installer/createshortcutoperation.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -110,6 +110,7 @@ static bool createLink(const QString &fileName, const QString &linkName, QString
IUnknown *iunkn = nullptr;
if (fileName.toLower().startsWith(QLatin1String("http:"))
+ || fileName.toLower().startsWith(QLatin1String("https:"))
|| fileName.toLower().startsWith(QLatin1String("ftp:"))) {
IUniformResourceLocator *iurl = nullptr;
if (FAILED(CoCreateInstance(CLSID_InternetShortcut, nullptr, CLSCTX_INPROC_SERVER,
@@ -176,6 +177,7 @@ static bool createLink(const QString &fileName, const QString &linkName, QString
Q_UNUSED(linkName)
Q_UNUSED(iconPath)
Q_UNUSED(iconId)
+ Q_UNUSED(description)
return true;
#endif
}
diff --git a/src/libs/installer/customcombobox.cpp b/src/libs/installer/customcombobox.cpp
new file mode 100644
index 000000000..998364fe4
--- /dev/null
+++ b/src/libs/installer/customcombobox.cpp
@@ -0,0 +1,54 @@
+/**************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "customcombobox.h"
+
+#include <QStylePainter>
+
+using namespace QInstaller;
+
+CustomComboBox::CustomComboBox(QWidget *parent)
+ : QComboBox(parent)
+{
+}
+
+void CustomComboBox::paintEvent(QPaintEvent *e)
+{
+ if (currentIndex() < 0 && !placeholderText().isEmpty()) {
+ QStylePainter painter(this);
+ painter.setPen(palette().color(QPalette::Text));
+ QStyleOptionComboBox opt;
+ initStyleOption(&opt);
+ painter.drawComplexControl(QStyle::CC_ComboBox, opt);
+ opt.palette.setBrush(QPalette::ButtonText, opt.palette.placeholderText());
+ opt.currentText = placeholderText();
+ painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
+ } else {
+ QComboBox::paintEvent(e);
+ }
+}
diff --git a/src/libs/installer/customcombobox.h b/src/libs/installer/customcombobox.h
new file mode 100644
index 000000000..e022da5a8
--- /dev/null
+++ b/src/libs/installer/customcombobox.h
@@ -0,0 +1,48 @@
+/**************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef CUSTOMCOMBOBOX_H
+#define CUSTOMCOMBOBOX_H
+
+#include <QComboBox>
+
+namespace QInstaller {
+
+class CustomComboBox : public QComboBox
+{
+ Q_OBJECT
+public:
+ CustomComboBox(QWidget *parent = nullptr);
+
+protected:
+ void paintEvent(QPaintEvent *e) override;
+};
+
+} // namespace QInstaller
+
+#endif // CUSTOMCOMBOBOX_H
diff --git a/src/libs/installer/downloadarchivesjob.cpp b/src/libs/installer/downloadarchivesjob.cpp
index a019d1567..65eead1f9 100644
--- a/src/libs/installer/downloadarchivesjob.cpp
+++ b/src/libs/installer/downloadarchivesjob.cpp
@@ -44,10 +44,12 @@ using namespace QInstaller;
using namespace KDUpdater;
+static constexpr uint scMaxRetries = 5;
+
/*!
Creates a new DownloadArchivesJob with parent \a core.
*/
-DownloadArchivesJob::DownloadArchivesJob(PackageManagerCore *core)
+DownloadArchivesJob::DownloadArchivesJob(PackageManagerCore *core, const QString &objectName)
: Job(core)
, m_core(core)
, m_downloader(nullptr)
@@ -58,8 +60,10 @@ DownloadArchivesJob::DownloadArchivesJob(PackageManagerCore *core)
, m_progressChangedTimerId(0)
, m_totalSizeToDownload(0)
, m_totalSizeDownloaded(0)
+ , m_retryCount(scMaxRetries)
{
setCapabilities(Cancelable);
+ setObjectName(objectName);
}
/*!
@@ -75,7 +79,7 @@ DownloadArchivesJob::~DownloadArchivesJob()
Sets the \a archives to download. The first value of each pair contains the file name to register
the file in the installer's internal file system, the second one the source url.
*/
-void DownloadArchivesJob::setArchivesToDownload(const QList<QPair<QString, QString> > &archives)
+void DownloadArchivesJob::setArchivesToDownload(const QList<PackageManagerCore::DownloadItem> &archives)
{
m_archivesToDownload = archives;
m_archivesToDownloadCount = archives.count();
@@ -111,17 +115,17 @@ void DownloadArchivesJob::doCancel()
void DownloadArchivesJob::fetchNextArchiveHash()
{
- if (m_core->testChecksum()) {
+ if (m_archivesToDownload.isEmpty()) {
+ emitFinished();
+ return;
+ }
+
+ if (m_archivesToDownload.first().checkSha1CheckSum) {
if (m_canceled) {
finishWithError(tr("Canceled"));
return;
}
- if (m_archivesToDownload.isEmpty()) {
- emitFinished();
- return;
- }
-
if (m_downloader)
m_downloader->deleteLater();
@@ -281,25 +285,32 @@ void DownloadArchivesJob::registerFile()
{
Q_ASSERT(m_downloader != nullptr);
- if (m_canceled)
+ if (m_canceled || m_archivesToDownload.isEmpty())
return;
- if (m_core->testChecksum() && m_currentHash != m_downloader->sha1Sum().toHex()) {
+ if (m_archivesToDownload.first().checkSha1CheckSum && m_currentHash != m_downloader->sha1Sum().toHex()) {
//TODO: Maybe we should try to download the file again automatically
const QMessageBox::Button res =
MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
QLatin1String("DownloadError"), tr("Download Error"), tr("Hash verification while "
- "downloading failed. This is a temporary error, please retry."),
- QMessageBox::Retry | QMessageBox::Cancel, QMessageBox::Cancel);
-
- // If run from command line instance, do not continue if hash verification failed.
- // Same download is tried again and again causing infinite loop if hash not
- // fixed to repositories.
- if (res == QMessageBox::Cancel || m_core->isCommandLineInstance()) {
- finishWithError(tr("Cannot verify Hash"));
+ "downloading failed. This is a temporary error, please retry.\n\n"
+ "Expected: %1 \nDownloaded: %2").arg(QString::fromLatin1(m_currentHash), QString::fromLatin1(m_downloader->sha1Sum().toHex())),
+ QMessageBox::Retry | QMessageBox::Cancel, QMessageBox::Retry);
+
+ if (res == QMessageBox::Cancel) {
+ finishWithError(tr("Cannot verify Hash\nExpected: %1 \nDownloaded: %2")
+ .arg(QString::fromLatin1(m_currentHash), QString::fromLatin1(m_downloader->sha1Sum().toHex())));
return;
}
+ // When using command line instance, only retry a number of times to avoid
+ // infinite loop in case the automatic answer for the messagebox is "Retry"
+ if (m_core->isCommandLineInstance() && (--m_retryCount == 0)) {
+ finishWithError(tr("Retry count (%1) exceeded").arg(scMaxRetries));
+ return;
+ }
} else {
+ m_retryCount = scMaxRetries;
+
++m_archivesDownloaded;
m_totalSizeDownloaded += QFile(m_downloader->downloadedFileName()).size();
if (m_progressChangedTimerId) {
@@ -308,8 +319,8 @@ void DownloadArchivesJob::registerFile()
emit progressChanged(double(m_archivesDownloaded) / m_archivesToDownloadCount);
}
- const QPair<QString, QString> pair = m_archivesToDownload.takeFirst();
- BinaryFormatEngineHandler::instance()->registerResource(pair.first,
+ const PackageManagerCore::DownloadItem item = m_archivesToDownload.takeFirst();
+ BinaryFormatEngineHandler::instance()->registerResource(item.fileName,
m_downloader->downloadedFileName());
emit fileDownloadReady(m_downloader->downloadedFileName());
@@ -330,14 +341,21 @@ void DownloadArchivesJob::downloadFailed(const QString &error)
const QMessageBox::StandardButton b =
MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
QLatin1String("archiveDownloadError"), tr("Download Error"), tr("Cannot download archive %1: %2")
- .arg(m_archivesToDownload.first().second, error), QMessageBox::Retry | QMessageBox::Cancel);
+ .arg(m_archivesToDownload.first().sourceUrl, error), QMessageBox::Retry | QMessageBox::Cancel,
+ QMessageBox::Retry);
+
+ if (b == QMessageBox::Retry) {
+ // When using command line instance, only retry a number of times to avoid
+ // infinite loop in case the automatic answer for the messagebox is "Retry"
+ if (m_core->isCommandLineInstance() && (--m_retryCount == 0)) {
+ finishWithError(tr("Retry count (%1) exceeded").arg(scMaxRetries));
+ return;
+ }
- // Do not call fetchNextArchiveHash when using command line instance,
- // installer tries to download the same archive causing infinite loop
- if (b == QMessageBox::Retry && !m_core->isCommandLineInstance())
QMetaObject::invokeMethod(this, "fetchNextArchiveHash", Qt::QueuedConnection);
- else
+ } else {
downloadCanceled();
+ }
}
void DownloadArchivesJob::finishWithError(const QString &error)
@@ -353,13 +371,13 @@ void DownloadArchivesJob::finishWithError(const QString &error)
KDUpdater::FileDownloader *DownloadArchivesJob::setupDownloader(const QString &suffix, const QString &queryString)
{
KDUpdater::FileDownloader *downloader = nullptr;
- const QFileInfo fi = QFileInfo(m_archivesToDownload.first().first);
+ const QFileInfo fi = QFileInfo(m_archivesToDownload.first().fileName);
const Component *const component = m_core->componentByName(PackageManagerCore::checkableName(QFileInfo(fi.path()).fileName()));
if (component) {
QString fullQueryString;
if (!queryString.isEmpty())
fullQueryString = QLatin1String("?") + queryString;
- const QUrl url(m_archivesToDownload.first().second + suffix + fullQueryString);
+ const QUrl url(m_archivesToDownload.first().sourceUrl + suffix + fullQueryString);
const QString &scheme = url.scheme();
downloader = FileDownloaderFactory::instance().create(scheme, this);
diff --git a/src/libs/installer/downloadarchivesjob.h b/src/libs/installer/downloadarchivesjob.h
index c75e9307a..5155c881a 100644
--- a/src/libs/installer/downloadarchivesjob.h
+++ b/src/libs/installer/downloadarchivesjob.h
@@ -30,7 +30,7 @@
#define DOWNLOADARCHIVESJOB_H
#include "job.h"
-
+#include "packagemanagercore.h"
#include <QtCore/QPair>
#include <QtCore/QElapsedTimer>
@@ -45,18 +45,17 @@ namespace KDUpdater {
namespace QInstaller {
class MessageBoxHandler;
-class PackageManagerCore;
class DownloadArchivesJob : public Job
{
Q_OBJECT
public:
- explicit DownloadArchivesJob(PackageManagerCore *core);
+ explicit DownloadArchivesJob(PackageManagerCore *core, const QString &objectName);
~DownloadArchivesJob();
int numberOfDownloads() const { return m_archivesDownloaded; }
- void setArchivesToDownload(const QList<QPair<QString, QString> > &archives);
+ void setArchivesToDownload(const QList<PackageManagerCore::DownloadItem> &archives);
void setExpectedTotalSize(quint64 total);
Q_SIGNALS:
@@ -94,7 +93,7 @@ private:
int m_archivesDownloaded;
int m_archivesToDownloadCount;
- QList<QPair<QString, QString> > m_archivesToDownload;
+ QList<PackageManagerCore::DownloadItem> m_archivesToDownload;
bool m_canceled;
QByteArray m_currentHash;
@@ -104,6 +103,8 @@ private:
quint64 m_totalSizeToDownload;
quint64 m_totalSizeDownloaded;
QElapsedTimer m_totalDownloadSpeedTimer;
+
+ uint m_retryCount;
};
} // namespace QInstaller
diff --git a/src/libs/installer/downloadfiletask.cpp b/src/libs/installer/downloadfiletask.cpp
index 1b9f81ecc..a959677a9 100644
--- a/src/libs/installer/downloadfiletask.cpp
+++ b/src/libs/installer/downloadfiletask.cpp
@@ -1,7 +1,7 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -30,6 +30,7 @@
#include "downloadfiletask_p.h"
#include "globals.h"
+#include "productkeycheck.h"
#include <QCoreApplication>
#include <QDir>
@@ -197,7 +198,7 @@ void Downloader::onReadyRead()
data.observer->addSample(read);
data.observer->addBytesTransfered(read);
- data.observer->addCheckSumData(buffer.data(), read);
+ data.observer->addCheckSumData(buffer.left(read));
int progress = m_finished * 100;
for (const auto &pair : m_downloads)
@@ -246,7 +247,7 @@ void Downloader::onFinished(QNetworkReply *reply)
if (!ba.isEmpty()) {
data.observer->addSample(ba.size());
data.observer->addBytesTransfered(ba.size());
- data.observer->addCheckSumData(ba.data(), ba.size());
+ data.observer->addCheckSumData(ba);
}
const QByteArray expectedCheckSum = data.taskItem.value(TaskRole::Checksum).toByteArray();
@@ -269,7 +270,7 @@ void Downloader::onFinished(QNetworkReply *reply)
}
}
-void Downloader::onError(QNetworkReply::NetworkError error)
+void Downloader::errorOccurred(QNetworkReply::NetworkError error)
{
QNetworkReply *const reply = qobject_cast<QNetworkReply *>(sender());
@@ -286,6 +287,10 @@ void Downloader::onError(QNetworkReply::NetworkError error)
if (data.taskItem.source().contains(QLatin1String("Updates.xml"), Qt::CaseInsensitive)) {
qCWarning(QInstaller::lcServer) << QString::fromLatin1("Network error while downloading '%1': %2.").arg(
data.taskItem.source(), reply->errorString());
+ } else if (data.taskItem.source().contains(QLatin1String("_meta"), Qt::CaseInsensitive)) {
+ QString errorString = tr("Network error while downloading '%1': %2.").arg(data.taskItem.source(), reply->errorString());
+ errorString.append(ProductKeyCheck::instance()->additionalMetaDownloadWarning());
+ m_futureInterface->reportException(TaskException(errorString));
} else {
m_futureInterface->reportException(
TaskException(tr("Network error while downloading '%1': %2.").arg(
@@ -395,14 +400,17 @@ QNetworkReply *Downloader::startDownload(const FileTaskItem &item)
.arg(source.toString(), source.errorString())));
return 0;
}
+ QNetworkRequest request(source);
+ request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::ManualRedirectPolicy);
+ request.setAttribute(QNetworkRequest::Http2AllowedAttribute, false);
- QNetworkReply *reply = m_nam.get(QNetworkRequest(source));
+ QNetworkReply *reply = m_nam.get(request);
std::unique_ptr<Data> data(new Data(item));
m_downloads[reply] = std::move(data);
connect(reply, &QIODevice::readyRead, this, &Downloader::onReadyRead);
- connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this,
- SLOT(onError(QNetworkReply::NetworkError)));
+ connect(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this,
+ SLOT(errorOccurred(QNetworkReply::NetworkError)));
#ifndef QT_NO_SSL
connect(reply, &QNetworkReply::sslErrors, this, &Downloader::onSslErrors);
#endif
diff --git a/src/libs/installer/downloadfiletask_p.h b/src/libs/installer/downloadfiletask_p.h
index 3dfce27b4..4750d5134 100644
--- a/src/libs/installer/downloadfiletask_p.h
+++ b/src/libs/installer/downloadfiletask_p.h
@@ -86,7 +86,7 @@ private slots:
void doDownload();
void onReadyRead();
void onFinished(QNetworkReply *reply);
- void onError(QNetworkReply::NetworkError error);
+ void errorOccurred(QNetworkReply::NetworkError error);
void onSslErrors(const QList<QSslError> &sslErrors);
void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
void onAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator);
diff --git a/src/libs/installer/elevatedexecuteoperation.cpp b/src/libs/installer/elevatedexecuteoperation.cpp
index 092b0f0eb..87810211e 100644
--- a/src/libs/installer/elevatedexecuteoperation.cpp
+++ b/src/libs/installer/elevatedexecuteoperation.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -65,10 +65,12 @@ public:
private:
bool needsRerunWithReplacedVariables(QStringList &arguments, const OperationType type);
+ void setErrorMessage(const QString &message);
-
+private:
QProcessWrapper *process;
bool showStandardError;
+ QString m_customErrorMessage;
};
ElevatedExecuteOperation::ElevatedExecuteOperation(PackageManagerCore *core)
@@ -120,13 +122,12 @@ int ElevatedExecuteOperation::Private::run(QStringList &arguments, const Operati
args.removeAll(workingDirectoryArgument);
}
- QString customErrorMessage;
QStringList filteredCustomErrorMessage = args.filter(QLatin1String("errormessage="),
Qt::CaseInsensitive);
if (!filteredCustomErrorMessage.isEmpty()) {
QString customErrorMessageArgument = filteredCustomErrorMessage.at(0);
- customErrorMessage = customErrorMessageArgument;
- customErrorMessage.replace(QLatin1String("errormessage="), QString(), Qt::CaseInsensitive);
+ m_customErrorMessage = customErrorMessageArgument;
+ m_customErrorMessage.replace(QLatin1String("errormessage="), QString(), Qt::CaseInsensitive);
args.removeAll(customErrorMessageArgument);
}
@@ -157,7 +158,8 @@ int ElevatedExecuteOperation::Private::run(QStringList &arguments, const Operati
const bool success = QProcessWrapper::startDetached(args.front(), args.mid(1));
if (!success) {
q->setError(UserDefinedError);
- q->setErrorString(tr("Cannot start detached: \"%1\"").arg(callstr));
+ setErrorMessage(tr("Cannot start detached: \"%1\"").arg(callstr));
+
returnValue = Error;
}
return returnValue;
@@ -202,8 +204,7 @@ int ElevatedExecuteOperation::Private::run(QStringList &arguments, const Operati
int returnValue = NoError;
if (!success) {
q->setError(UserDefinedError);
- //TODO: pass errorString() through the wrapper */
- q->setErrorString(tr("Cannot start: \"%1\": %2").arg(callstr,
+ setErrorMessage(tr("Cannot start: \"%1\": %2").arg(callstr,
process->errorString()));
if (!needsRerunWithReplacedVariables(arguments, type)) {
returnValue = Error;
@@ -221,30 +222,25 @@ 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\"")
+ setErrorMessage(tr("Program crashed: \"%1\"").arg(callstr));
+ returnValue = Error;
+ } else if (!allowedExitCodes.contains(process->exitCode()) && returnValue != NeedsRerun) {
+ if (!needsRerunWithReplacedVariables(arguments, type)) {
+ q->setError(UserDefinedError);
+ setErrorMessage(tr("Execution failed (Unexpected exit code: %1): \"%2\"")
.arg(QString::number(process->exitCode()), callstr));
+ 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);
@@ -277,6 +273,13 @@ bool ElevatedExecuteOperation::Private::needsRerunWithReplacedVariables(QStringL
return rerun;
}
+void ElevatedExecuteOperation::Private::setErrorMessage(const QString &message)
+{
+ if (m_customErrorMessage.isEmpty())
+ q->setErrorString(message);
+ else
+ q->setErrorString(m_customErrorMessage);
+}
/*!
Cancels the ElevatedExecuteOperation. This methods tries to terminate the process
gracefully by calling QProcessWrapper::terminate. After 10 seconds, the process gets killed.
diff --git a/src/libs/installer/environmentvariablesoperation.cpp b/src/libs/installer/environmentvariablesoperation.cpp
index 902687164..94cd1e36f 100644
--- a/src/libs/installer/environmentvariablesoperation.cpp
+++ b/src/libs/installer/environmentvariablesoperation.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -30,6 +30,7 @@
#include "qsettingswrapper.h"
#include <stdlib.h>
+#include <QDir>
#include "environment.h"
#include "globals.h"
@@ -110,6 +111,12 @@ bool handleRegExpandSz(const QString &regPath, const QString &name,
}
}
}
+#else
+ Q_UNUSED(regPath)
+ Q_UNUSED(name)
+ Q_UNUSED(value)
+ Q_UNUSED(errorString)
+ Q_UNUSED(error)
#endif
return setAsExpandSZ;
}
@@ -162,11 +169,11 @@ UpdateOperation::Error undoSetting(const QString &regPath,
if (actual != value)
{
- //For unknown reason paths with @TargetDir@ variable get modified
- //so that Windows file separators get replaced with unix style separators,
- //fix separators before matching to actual value in register
+ //Ignore the separators
+ static const QRegularExpression regex(QLatin1String("(\\\\|/)"));
QString tempValue = value;
- QString fixedValue = tempValue.replace(QLatin1Char('/'), QLatin1Char('\\'));
+ QString fixedValue = tempValue.replace(regex, QDir::separator());
+ actual = actual.replace(regex, QDir::separator());
if (actual != fixedValue) //key changed, don't undo
return UpdateOperation::UserDefinedError;
diff --git a/src/libs/installer/extractarchiveoperation.cpp b/src/libs/installer/extractarchiveoperation.cpp
index 986b9d8c8..b00a67190 100644
--- a/src/libs/installer/extractarchiveoperation.cpp
+++ b/src/libs/installer/extractarchiveoperation.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -103,7 +103,7 @@ void ExtractArchiveOperation::backup()
return;
}
- const bool hasAdminRights = (AdminAuthorization::hasAdminRights() || RemoteClient::instance().isActive());
+ const bool hasAdminRights = (packageManager() && packageManager()->hasAdminRights());
const bool canCreateSymLinks = QInstaller::canCreateSymbolicLinks();
bool needsAdminRights = false;
@@ -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,9 +206,17 @@ 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;
+ if (!files.isEmpty())
+ out << files;
setValue(QLatin1String("files"), file.fileName());
file.close();
} else {
@@ -242,7 +254,8 @@ bool ExtractArchiveOperation::undoOperation()
if (!readDataFileContents(targetDir, &files))
return false;
}
- startUndoProcess(files);
+ if (!files.isEmpty())
+ startUndoProcess(files);
if (!useStringListType)
deleteDataFile(m_relocatedDataFileName);
diff --git a/src/libs/installer/fakestopprocessforupdateoperation.cpp b/src/libs/installer/fakestopprocessforupdateoperation.cpp
index 67d60a92f..bdd8625eb 100644
--- a/src/libs/installer/fakestopprocessforupdateoperation.cpp
+++ b/src/libs/installer/fakestopprocessforupdateoperation.cpp
@@ -31,6 +31,8 @@
#include "messageboxhandler.h"
#include "packagemanagercore.h"
+#include <QDir>
+
using namespace KDUpdater;
using namespace QInstaller;
@@ -79,11 +81,11 @@ bool FakeStopProcessForUpdateOperation::undoOperation()
if (processes.count() == 1) {
setError(UpdateOperation::UserDefinedError, tr("This process should be stopped before "
- "continuing: %1").arg(processes.first()));
+ "continuing: %1").arg(QDir::toNativeSeparators(processes.first())));
} else {
const QString sep = QString::fromWCharArray(L"\n \u2022 "); // Unicode bullet
setError(UpdateOperation::UserDefinedError, tr("These processes should be stopped before "
- "continuing: %1").arg(sep + processes.join(sep)));
+ "continuing: %1").arg(sep + QDir::toNativeSeparators(processes.join(sep))));
}
return false;
}
diff --git a/src/libs/installer/fileutils.cpp b/src/libs/installer/fileutils.cpp
index 16502383c..044eeb34f 100644
--- a/src/libs/installer/fileutils.cpp
+++ b/src/libs/installer/fileutils.cpp
@@ -89,7 +89,7 @@ TempPathDeleter::~TempPathDeleter()
QStringList TempPathDeleter::paths() const
{
- return m_paths.toList();
+ return m_paths.values();
}
void TempPathDeleter::add(const QString &path)
@@ -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
@@ -734,7 +734,7 @@ quint64 QInstaller::fileSize(const QFileInfo &info)
bool QInstaller::isInBundle(const QString &path, QString *bundlePath)
{
#ifdef Q_OS_MACOS
- QFileInfo fi = QFileInfo(path).absoluteFilePath();
+ QFileInfo fi(QFileInfo(path).absoluteFilePath());
while (!fi.isRoot()) {
if (fi.isBundle()) {
if (bundlePath)
diff --git a/src/libs/installer/genericdatacache.cpp b/src/libs/installer/genericdatacache.cpp
index b70cbcb0b..a1e31ccfe 100644
--- a/src/libs/installer/genericdatacache.cpp
+++ b/src/libs/installer/genericdatacache.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -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
@@ -120,6 +120,16 @@ CacheableItem::~CacheableItem()
*/
/*!
+ \enum GenericDataCache::RegisterMode
+ This enum holds the possible values for modes of registering items to cache.
+
+ \value Copy
+ The contents of the item are copied to the cache.
+ \value Move
+ The contents of the item are move to the cache.
+*/
+
+/*!
\fn template <typename T> QInstaller::GenericDataCache<T>::GenericDataCache()
Constructs a new empty cache. The cache is invalid until set with a
@@ -411,19 +421,20 @@ T *GenericDataCache<T>::itemByPath(const QString &path) const
}
/*!
- \fn template <typename T> QInstaller::GenericDataCache<T>::registerItem(T *item, bool replace)
+ \fn template <typename T> QInstaller::GenericDataCache<T>::registerItem(T *item, bool replace, RegisterMode mode)
Registers the \a item to the cache. If \a replace is set to \c true,
the new \a item replaces a previous item with the same checksum.
The cache takes ownership of the object pointed by \a item. The contents of the
- item are copied to the cache with a subdirectory name that matches the checksum
- of the item.
+ item are copied or moved to the cache with a subdirectory name that matches the checksum
+ of the item. The \a mode decides how the contents of the item are registered, either by
+ copying or moving.
Returns \c true on success or \c false if the item could not be registered.
*/
template <typename T>
-bool GenericDataCache<T>::registerItem(T *item, bool replace)
+bool GenericDataCache<T>::registerItem(T *item, bool replace, RegisterMode mode)
{
QMutexLocker _(&m_mutex);
if (m_invalidated) {
@@ -455,10 +466,27 @@ bool GenericDataCache<T>::registerItem(T *item, bool replace)
const QString newPath = m_path + QDir::separator() + QString::fromLatin1(item->checksum());
try {
// A directory is in the way but it isn't registered to the current cache, remove.
- if (QDir().exists(newPath))
+ QDir dir;
+ if (dir.exists(newPath))
QInstaller::removeDirectory(newPath);
- QInstaller::copyDirectoryContents(item->path(), newPath);
+ switch (mode) {
+ case Copy:
+ QInstaller::copyDirectoryContents(item->path(), newPath);
+ break;
+ case Move:
+ // First, try moving the top level directory
+ if (!dir.rename(item->path(), newPath)) {
+ qCDebug(lcDeveloperBuild) << "Failed to rename directory" << item->path()
+ << "to" << newPath << ". Trying again.";
+ // If that does not work, fallback to moving the contents one by one
+ QInstaller::moveDirectoryContents(item->path(), newPath);
+ }
+ break;
+ default:
+ throw Error(QCoreApplication::translate("GenericDataCache",
+ "Unknown register mode selected!"));
+ }
} catch (const Error &e) {
setErrorString(QCoreApplication::translate("GenericDataCache",
"Error while copying item to path \"%1\": %2").arg(newPath, e.message()));
@@ -512,7 +540,7 @@ QList<T *> GenericDataCache<T>::obsoleteItems() const
}
/*!
- \fn template <typename T> QInstaller::GenericDataCache<T>::invalidate()
+ \internal
Marks the cache invalid and clears all items. The contents
on disk are not deleted. Releases the lock file of the cache.
@@ -532,11 +560,11 @@ void GenericDataCache<T>::invalidate()
}
/*!
- \fn template <typename T> QInstaller::GenericDataCache<T>::setErrorString(const QString &error)
+ \internal
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;
@@ -544,7 +572,7 @@ void GenericDataCache<T>::setErrorString(const QString &error) const
}
/*!
- \fn template <typename T> QInstaller::GenericDataCache<T>::fromDisk()
+ \internal
Reads the manifest file of the cache if one exists, and populates the internal
hash from the file contents. Returns \c true if the manifests was read successfully
@@ -587,8 +615,8 @@ bool GenericDataCache<T>::fromDisk()
for (const auto &itemJsonValue : itemsJsonArray) {
const QString checksum = itemJsonValue.toString();
- QScopedPointer<T> item(new T(m_path + QDir::separator() + checksum));
- m_items.insert(checksum.toLatin1(), item.take());
+ std::unique_ptr<T> item(new T(m_path + QDir::separator() + checksum));
+ m_items.insert(checksum.toLatin1(), item.release());
// The cache directory may contain other entries (unrelated directories or
// invalid old cache items) which we don't care about, unless registering
@@ -598,7 +626,7 @@ bool GenericDataCache<T>::fromDisk()
}
/*!
- \fn template <typename T> QInstaller::GenericDataCache<T>::toDisk()
+ \internal
Writes the manifest file with the contents of the internal item hash.
Returns \c true on success, \c false otherwise.
@@ -635,8 +663,6 @@ bool GenericDataCache<T>::toDisk()
}
/*!
- \fn template <typename T> QInstaller::GenericDataCache<T>::remove(const QByteArray &checksum)
-
\internal
*/
template<typename T>
diff --git a/src/libs/installer/genericdatacache.h b/src/libs/installer/genericdatacache.h
index 2bdf6697e..94085502c 100644
--- a/src/libs/installer/genericdatacache.h
+++ b/src/libs/installer/genericdatacache.h
@@ -64,9 +64,14 @@ template <typename T>
class INSTALLER_EXPORT GenericDataCache
{
public:
+ enum RegisterMode {
+ Copy = 0,
+ Move = 1
+ };
+
GenericDataCache();
explicit GenericDataCache(const QString &path, const QString &type, const QString &version);
- ~GenericDataCache();
+ virtual ~GenericDataCache();
void setType(const QString &type);
void setVersion(const QString &version);
@@ -85,7 +90,7 @@ public:
T *itemByChecksum(const QByteArray &checksum) const;
T *itemByPath(const QString &path) const;
- bool registerItem(T *item, bool replace = false);
+ bool registerItem(T *item, bool replace = false, RegisterMode mode = Copy);
bool removeItem(const QByteArray &checksum);
QList<T *> obsoleteItems() const;
diff --git a/src/libs/installer/globals.cpp b/src/libs/installer/globals.cpp
index bea299696..3fd084768 100644
--- a/src/libs/installer/globals.cpp
+++ b/src/libs/installer/globals.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2020 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.
@@ -30,6 +30,14 @@
#include "globals.h"
+#if defined(Q_OS_MACOS) || defined(Q_OS_LINUX)
+#include <termios.h>
+#include <unistd.h>
+#elif defined(Q_OS_WIN)
+#include <conio.h>
+#endif
+#include <iostream>
+
const char IFW_SERVER[] = "ifw.server";
const char IFW_INSTALLER_INSTALLLOG[] = "ifw.installer.installlog";
const char IFW_DEVELOPER_BUILD[] = "ifw.developer.build";
@@ -60,6 +68,12 @@ namespace QInstaller
\internal
*/
+/*!
+ \fn inline QInstaller::splitStringWithComma(const QString &value())
+ Splits \a value into substrings wherever comma occurs, and returns the list of those strings.
+ \internal
+*/
+
Q_LOGGING_CATEGORY(lcServer, IFW_SERVER)
Q_LOGGING_CATEGORY(lcInstallerInstallLog, IFW_INSTALLER_INSTALLLOG)
Q_LOGGING_CATEGORY(lcProgressIndicator, IFW_PROGRESS_INDICATOR)
@@ -72,7 +86,9 @@ QStringList loggingCategories()
{
static QStringList categories = QStringList()
<< QLatin1String(IFW_INSTALLER_INSTALLLOG)
- << QLatin1String(IFW_SERVER);
+ << QLatin1String(IFW_SERVER)
+ << QLatin1String(IFW_DEVELOPER_BUILD)
+ << QLatin1String("js");
return categories;
}
@@ -111,5 +127,52 @@ QString enumToString(const QMetaObject& metaObject, const char *enumerator, int
return value;
}
+void askForCredentials(QString *username, QString *password, const QString &usernameTitle, const QString &passwordTitle)
+{
+ std::string usernameStdStr;
+ std::string passwordStdStr;
+
+ std::cout << qPrintable(usernameTitle);
+ std::cin >> usernameStdStr;
+
+ std::cout << qPrintable(passwordTitle);
+#if defined(Q_OS_MACOS) || defined(Q_OS_LINUX)
+ termios oldTerm;
+ termios term;
+
+ // Turn off echoing
+ tcgetattr(STDIN_FILENO, &oldTerm);
+ term = oldTerm;
+ term.c_lflag &= ~ECHO;
+ tcsetattr(STDIN_FILENO, TCSANOW, &term);
+
+ std::cin >> passwordStdStr;
+
+ // Clear input buffer
+ std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
+
+ // Restore old attributes
+ tcsetattr(STDIN_FILENO, TCSANOW, &oldTerm);
+#elif defined(Q_OS_WIN)
+ char ch;
+ while ((ch = _getch()) != '\r') { // Return key
+ if (ch == '\b') { // Backspace key
+ if (!passwordStdStr.empty())
+ passwordStdStr.pop_back();
+ } else {
+ passwordStdStr.push_back(ch);
+ }
+ }
+ // Clear input buffer
+ int c;
+ while ((c = getchar()) != '\n' && c != EOF);
+
+#endif
+ std::cout << "\n";
+
+ *username = username->fromStdString(usernameStdStr);
+ *password = password->fromStdString(passwordStdStr);
+}
+
} // namespace QInstaller
diff --git a/src/libs/installer/globals.h b/src/libs/installer/globals.h
index 3450c0dad..2d119048b 100644
--- a/src/libs/installer/globals.h
+++ b/src/libs/installer/globals.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -44,6 +44,11 @@ INSTALLER_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcDeveloperBuild)
QStringList INSTALLER_EXPORT loggingCategories();
QRegularExpression INSTALLER_EXPORT commaRegExp();
+inline QStringList splitStringWithComma(const QString &value) {
+ if (!value.isEmpty())
+ return value.split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
+ return QStringList();
+}
QString htmlToString(const QString &html);
QString enumToString(const QMetaObject& metaObject, const char *enumerator, int key);
@@ -54,6 +59,8 @@ QSet<T> toQSet(const C<T> &container)
return QSet<T>(container.begin(), container.end());
}
+void askForCredentials(QString *username, QString *password, const QString &usernameTitle, const QString &passwordTitle);
+
} // QInstaller
#endif // GLOBALS_H
diff --git a/src/libs/installer/globalsettingsoperation.cpp b/src/libs/installer/globalsettingsoperation.cpp
index 9608bba66..6ca50f96f 100644
--- a/src/libs/installer/globalsettingsoperation.cpp
+++ b/src/libs/installer/globalsettingsoperation.cpp
@@ -77,7 +77,7 @@ bool GlobalSettingsOperation::performOperation()
bool GlobalSettingsOperation::undoOperation()
{
- if (parseUndoOperationArguments().count() > 0)
+ if (skipUndoOperation())
return true;
const QStringList args = parsePerformOperationArguments();
diff --git a/src/libs/installer/installer.pro b/src/libs/installer/installer.pro
index 0de80b064..ff7a0eed2 100644
--- a/src/libs/installer/installer.pro
+++ b/src/libs/installer/installer.pro
@@ -46,11 +46,14 @@ greaterThan(QT_MAJOR_VERSION, 5):QT += core5compat
HEADERS += packagemanagercore.h \
aspectratiolabel.h \
+ calculatorbase.h \
+ componentalias.h \
componentsortfilterproxymodel.h \
concurrentoperationrunner.h \
genericdatacache.h \
loggingutils.h \
metadata.h \
+ metadatacache.h \
packagemanagercore_p.h \
packagemanagergui.h \
binaryformat.h \
@@ -143,12 +146,15 @@ HEADERS += packagemanagercore.h \
abstractarchive.h \
directoryguard.h \
archivefactory.h \
- operationtracer.h
+ operationtracer.h \
+ customcombobox.h
SOURCES += packagemanagercore.cpp \
abstractarchive.cpp \
archivefactory.cpp \
aspectratiolabel.cpp \
+ calculatorbase.cpp \
+ componentalias.cpp \
concurrentoperationrunner.cpp \
directoryguard.cpp \
fileguard.cpp \
@@ -156,6 +162,7 @@ SOURCES += packagemanagercore.cpp \
genericdatacache.cpp \
loggingutils.cpp \
metadata.cpp \
+ metadatacache.cpp \
operationtracer.cpp \
packagemanagercore_p.cpp \
packagemanagergui.cpp \
@@ -229,7 +236,8 @@ SOURCES += packagemanagercore.cpp \
packagesource.cpp \
repositorycategory.cpp \
componentselectionpage_p.cpp \
- commandlineparser.cpp
+ commandlineparser.cpp \
+ customcombobox.cpp
macos:SOURCES += fileutils_mac.mm
diff --git a/src/libs/installer/installer_global.h b/src/libs/installer/installer_global.h
index ea6865042..285eff910 100644
--- a/src/libs/installer/installer_global.h
+++ b/src/libs/installer/installer_global.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -40,4 +40,10 @@
# define INSTALLER_EXPORT
#endif
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+typedef uint hashValue;
+#else
+typedef size_t hashValue;
+#endif
+
#endif //INSTALLER_GLOBAL_H
diff --git a/src/libs/installer/installercalculator.cpp b/src/libs/installer/installercalculator.cpp
index f285158f8..4c53824af 100644
--- a/src/libs/installer/installercalculator.cpp
+++ b/src/libs/installer/installercalculator.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -29,6 +29,8 @@
#include "installercalculator.h"
#include "component.h"
+#include "componentalias.h"
+#include "componentmodel.h"
#include "packagemanagercore.h"
#include "settings.h"
#include <globals.h>
@@ -42,143 +44,174 @@ namespace QInstaller {
*/
InstallerCalculator::InstallerCalculator(PackageManagerCore *core, const AutoDependencyHash &autoDependencyComponentHash)
- : m_core(core)
+ : CalculatorBase(core)
, m_autoDependencyComponentHash(autoDependencyComponentHash)
{
}
-InstallerCalculator::InstallReasonType InstallerCalculator::installReasonType(const Component *c) const
+InstallerCalculator::~InstallerCalculator()
{
- return m_toInstallComponentIdReasonHash.value(c->name()).first;
}
-QString InstallerCalculator::installReason(const Component *component) const
+bool InstallerCalculator::solve()
{
- InstallerCalculator::InstallReasonType reason = installReasonType(component);
+ if (!solve(m_core->aliasesMarkedForInstallation()))
+ return false;
+
+ // Subtract components added by aliases
+ QList<Component *> components = m_core->componentsMarkedForInstallation();
+ for (auto *component : qAsConst(m_resolvedComponents))
+ components.removeAll(component);
+
+ return solve(components);
+}
+
+QString InstallerCalculator::resolutionText(Component *component) const
+{
+ const Resolution reason = resolutionType(component);
switch (reason) {
- case Automatic:
+ case Resolution::Automatic:
return QCoreApplication::translate("InstallerCalculator",
"Components added as automatic dependencies:");
- case Dependent:
+ case Resolution::Dependent:
return QCoreApplication::translate("InstallerCalculator", "Components added as "
- "dependency for \"%1\":").arg(installReasonReferencedComponent(component));
- case Resolved:
+ "dependency for \"%1\":").arg(referencedComponent(component));
+ case Resolution::Resolved:
return QCoreApplication::translate("InstallerCalculator",
"Components that have resolved dependencies:");
- case Selected:
+ case Resolution::Selected:
return QCoreApplication::translate("InstallerCalculator",
"Selected components without dependencies:");
+ case Resolution::Alias:
+ return QCoreApplication::translate("InstallerCalculator",
+ "Components selected by alias \"%1\":").arg(referencedComponent(component));
+ default:
+ Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid install resolution detected!");
}
return QString();
}
-QList<Component*> InstallerCalculator::orderedComponentsToInstall() const
-{
- return m_orderedComponentsToInstall;
-}
-
-QString InstallerCalculator::componentsToInstallError() const
-{
- return m_componentsToInstallError;
-}
-
-bool InstallerCalculator::appendComponentsToInstall(const QList<Component *> &components, bool modelReset, const bool revertFromInstall)
+bool InstallerCalculator::solve(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_errorString.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);
+ addComponentForInstall(component);
else
notAppendedComponents.append(component);
}
- foreach (Component *component, notAppendedComponents) {
- if (!appendComponentToInstall(component, QString(), revertFromInstall))
+ for (Component *component : qAsConst(notAppendedComponents)) {
+ if (!solveComponent(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 solve(foundAutoDependOnList.values());
return true;
}
-bool InstallerCalculator::removeComponentsFromInstall(const QList<Component *> &components)
+bool InstallerCalculator::solve(const QList<ComponentAlias *> &aliases)
{
- return appendComponentsToInstall(components, false, true);
+ if (aliases.isEmpty())
+ return true;
+
+ QList<ComponentAlias *> notAppendedAliases; // Aliases that require other aliases
+ for (auto *alias : aliases) {
+ if (!alias)
+ continue;
+
+ if (m_toInstallComponentAliases.contains(alias->name())) {
+ const QString errorMessage = QCoreApplication::translate("InstallerCalculator",
+ "Recursion detected, component alias \"%1\" already added.").arg(alias->name());
+ qCWarning(QInstaller::lcInstallerInstallLog).noquote() << errorMessage;
+ m_errorString.append(errorMessage);
+
+ Q_ASSERT_X(!m_toInstallComponentAliases.contains(alias->name()), Q_FUNC_INFO,
+ qPrintable(errorMessage));
+
+ return false;
+ }
+
+ if (alias->aliases().isEmpty()) {
+ if (!addComponentsFromAlias(alias))
+ return false;
+ } else {
+ notAppendedAliases.append(alias);
+ }
+ }
+
+ for (auto *alias : qAsConst(notAppendedAliases)) {
+ if (!solveAlias(alias))
+ return false;
+ }
+
+ return true;
}
-QString InstallerCalculator::installReasonReferencedComponent(const Component *component) const
+void InstallerCalculator::addComponentForInstall(Component *component, const QString &version)
{
- 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;
+ if (!m_componentsForAutodepencencyCheck.contains(component))
+ m_componentsForAutodepencencyCheck.append(component);
+
+ if (!component->isInstalled(version) || (m_core->isUpdater() && component->isUpdateAvailable())) {
+ m_resolvedComponents.append(component);
+ m_toInstallComponentIds.insert(component->name());
+ }
}
-void InstallerCalculator::insertInstallReason(const Component *component,
- const InstallReasonType installReason, const QString &referencedComponentName, const bool revertFromInstall)
+bool InstallerCalculator::addComponentsFromAlias(ComponentAlias *alias)
{
- 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));
+ QList<Component *> componentsToAdd;
+ for (auto *component : alias->components()) {
+ if (m_toInstallComponentIds.contains(component->name()))
+ continue; // Already added
+
+ componentsToAdd.append(component);
+ // Updates the model, so that we also check the descendant
+ // components when calculating components to install
+ updateCheckState(component, Qt::Checked);
+ insertResolution(component, Resolution::Alias, alias->name());
}
+
+ m_toInstallComponentAliases.insert(alias->name());
+ return solve(componentsToAdd);
}
-void InstallerCalculator::realAppendToInstallComponents(Component *component, const QString &version, const bool revertFromInstall)
+QString InstallerCalculator::recursionError(Component *component) const
{
- 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);
- }
- }
+ return QCoreApplication::translate("InstallerCalculator", "Recursion detected, component \"%1\" "
+ "already added with reason: \"%2\"").arg(component->name(), resolutionText(component));
}
-bool InstallerCalculator::appendComponentToInstall(Component *component, const QString &version, bool revertFromInstall)
+bool InstallerCalculator::updateCheckState(Component *component, Qt::CheckState state)
+{
+ ComponentModel *currentModel = m_core->isUpdater()
+ ? m_core->updaterComponentModel()
+ : m_core->defaultComponentModel();
+
+ const QModelIndex &idx = currentModel->indexFromComponentName(component->treeName());
+ return currentModel->setData(idx, state, Qt::CheckStateRole);
+}
+
+bool InstallerCalculator::solveComponent(Component *component, const QString &version)
{
const QStringList dependenciesList = component->currentDependencies();
QString requiredDependencyVersion = version;
@@ -191,7 +224,7 @@ bool InstallerCalculator::appendComponentToInstall(Component *component, const Q
"Cannot find missing dependency \"%1\" for \"%2\".").arg(dependencyComponentName,
component->name());
qCWarning(QInstaller::lcInstallerInstallLog).noquote() << errorMessage;
- m_componentsToInstallError.append(errorMessage);
+ m_errorString.append(errorMessage);
if (component->packageManagerCore()->settings().allowUnstableComponents()) {
component->setUnstable(Component::UnstableError::MissingDependency, errorMessage);
continue;
@@ -199,8 +232,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 +252,52 @@ 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_errorString = 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
+ insertResolution(dependencyComponent, Resolution::Dependent,
+ component->name());
- insertInstallReason(dependencyComponent, InstallerCalculator::Dependent, component->name());
- if (!appendComponentToInstall(dependencyComponent, requiredDependencyVersion, revertFromInstall))
+ if (!solveComponent(dependencyComponent, requiredDependencyVersion))
return false;
}
}
- if (!revertFromInstall && !m_toInstallComponentIds.contains(component->name())) {
- realAppendToInstallComponents(component, requiredDependencyVersion, revertFromInstall);
- insertInstallReason(component, InstallerCalculator::Resolved);
- }
- if (revertFromInstall && m_toInstallComponentIds.contains(component->name())) {
- realAppendToInstallComponents(component, requiredDependencyVersion, revertFromInstall);
+
+ if (!m_toInstallComponentIds.contains(component->name())) {
+ addComponentForInstall(component, requiredDependencyVersion);
+ insertResolution(component, Resolution::Resolved);
}
return true;
}
-QString InstallerCalculator::recursionError(const Component *component) const
+bool InstallerCalculator::solveAlias(ComponentAlias *alias)
{
- return QCoreApplication::translate("InstallerCalculator", "Recursion detected, component \"%1\" "
- "already added with reason: \"%2\"").arg(component->name(), installReason(component));
+ for (auto *requiredAlias : alias->aliases()) {
+ if (!solveAlias(requiredAlias))
+ return false;
+ }
+
+ if (m_toInstallComponentAliases.contains(alias->name()))
+ return true;
+
+ return addComponentsFromAlias(alias);
}
-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 +306,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);
@@ -314,12 +324,8 @@ QSet<Component *> InstallerCalculator::autodependencyComponents(const bool rever
// are other autodependencies as well
if (autoDependComponent->isAutoDependOn(m_toInstallComponentIds)) {
foundAutoDependOnList.insert(autoDependComponent);
- insertInstallReason(autoDependComponent, InstallerCalculator::Automatic);
+ insertResolution(autoDependComponent, Resolution::Automatic);
}
- } else if (revertFromInstall
- && m_toInstallComponentIds.contains(autoDependComponent->name())
- && !m_toInstallComponentIds.contains(component->name())) {
- foundAutoDependOnList.insert(autoDependComponent);
}
}
}
@@ -327,20 +333,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..e542dc664 100644
--- a/src/libs/installer/installercalculator.h
+++ b/src/libs/installer/installercalculator.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -30,6 +30,7 @@
#include "installer_global.h"
#include "qinstallerglobal.h"
+#include "calculatorbase.h"
#include <QHash>
#include <QList>
@@ -39,58 +40,41 @@
namespace QInstaller {
class Component;
+class ComponentAlias;
+class PackageManagerCore;
-class INSTALLER_EXPORT InstallerCalculator
+class INSTALLER_EXPORT InstallerCalculator : public CalculatorBase
{
public:
InstallerCalculator(PackageManagerCore *core, const AutoDependencyHash &autoDependencyComponentHash);
+ ~InstallerCalculator();
- enum InstallReasonType
- {
- Selected, // "Selected Component(s) without Dependencies"
- Automatic, // "Component(s) added as automatic dependencies"
- Dependent, // "Added as dependency for %1."
- Resolved // "Component(s) that have resolved Dependencies"
- };
+ bool solve();
+ bool solve(const QList<Component *> &components) override;
+ bool solve(const QList<ComponentAlias *> &aliases);
- InstallReasonType installReasonType(const Component *c) const;
- QString installReason(const Component *component) const;
- QList<Component*> orderedComponentsToInstall() const;
- QString componentsToInstallError() const;
-
- bool appendComponentsToInstall(const QList<Component*> &components, bool modelReset = false, const bool revertFromInstall = false);
- bool removeComponentsFromInstall(const QList<Component*> &components);
+ QString resolutionText(Component *component) const override;
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);
+ bool solveComponent(Component *component, const QString &version = QString()) override;
+ bool solveAlias(ComponentAlias *alias);
+
+ void addComponentForInstall(Component *component, const QString &version = QString());
+ bool addComponentsFromAlias(ComponentAlias *alias);
+ QSet<Component *> autodependencyComponents();
+ QString recursionError(Component *component) const;
+
+ bool updateCheckState(Component *component, Qt::CheckState state);
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;
- QString m_componentsToInstallError;
- //calculate installation order variables
- QList<Component*> m_orderedComponentsToInstall;
- //we can't use this reason hash as component id hash, because some reasons are ready before
- //the component is added
- QHash<QString, QPair<InstallReasonType, QString> > m_toInstallComponentIdReasonHash;
+ QSet<QString> m_toInstallComponentIds; //for faster lookups
+ QSet<QString> m_toInstallComponentAliases;
//Helper hash for quicker search for autodependency components
AutoDependencyHash m_autoDependencyComponentHash;
};
-}
-
+} // namespace QInstaller
#endif // INSTALLERCALCULATOR_H
diff --git a/src/libs/installer/installiconsoperation.cpp b/src/libs/installer/installiconsoperation.cpp
index 5927ebf74..b21634cd7 100644
--- a/src/libs/installer/installiconsoperation.cpp
+++ b/src/libs/installer/installiconsoperation.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -57,7 +57,7 @@ QString InstallIconsOperation::targetDirectory()
Qt::SkipEmptyParts);
XDG_DATA_HOME.push_back(QDir::home().absoluteFilePath(QLatin1String(".local/share"))); // default user-specific path
- if (AdminAuthorization::hasAdminRights() || RemoteClient::instance().isActive())
+ if (packageManager() && packageManager()->hasAdminRights())
XDG_DATA_HOME.push_front(QLatin1String("/usr/local/share")); // default system-wide path
QString directory;
diff --git a/src/libs/installer/lib7z_create.h b/src/libs/installer/lib7z_create.h
index 9e920aa55..b908b028f 100644
--- a/src/libs/installer/lib7z_create.h
+++ b/src/libs/installer/lib7z_create.h
@@ -35,9 +35,10 @@
#include <Common/MyCom.h>
#include <7zip/UI/Common/Update.h>
+#include <QStringList>
+
QT_BEGIN_NAMESPACE
class QFileDevice;
-class QStringList;
QT_END_NAMESPACE
namespace Lib7z
diff --git a/src/libs/installer/lib7z_facade.cpp b/src/libs/installer/lib7z_facade.cpp
index bd08c68c5..e0d8a53e4 100644
--- a/src/libs/installer/lib7z_facade.cpp
+++ b/src/libs/installer/lib7z_facade.cpp
@@ -363,7 +363,7 @@ static quint32 getUInt32Property(IInArchive *archive, int index, int propId, qui
static QFile::Permissions getPermissions(IInArchive *archive, int index, bool *hasPermissions)
{
quint32 attributes = getUInt32Property(archive, index, kpidAttrib, 0);
- QFile::Permissions permissions = nullptr;
+ QFile::Permissions permissions = QFile::Permissions();
if (attributes & FILE_ATTRIBUTE_UNIX_EXTENSION) {
if (hasPermissions != nullptr)
*hasPermissions = true;
diff --git a/src/libs/installer/libarchivearchive.cpp b/src/libs/installer/libarchivearchive.cpp
index 4102518f4..233a4c28b 100644
--- a/src/libs/installer/libarchivearchive.cpp
+++ b/src/libs/installer/libarchivearchive.cpp
@@ -832,7 +832,7 @@ QVector<ArchiveEntry> LibArchiveArchive::list()
ArchiveEntry archiveEntry;
archiveEntry.path = ArchiveEntryPaths::callWithSystemLocale<QString>(ArchiveEntryPaths::pathname, entry);
- archiveEntry.utcTime = QDateTime::fromTime_t(archive_entry_mtime(entry));
+ archiveEntry.utcTime = QDateTime::fromSecsSinceEpoch(archive_entry_mtime(entry));
archiveEntry.isDirectory = (archive_entry_filetype(entry) == AE_IFDIR);
archiveEntry.isSymbolicLink = (archive_entry_filetype(entry) == AE_IFLNK);
archiveEntry.uncompressedSize = archive_entry_size(entry);
diff --git a/src/libs/installer/libarchivewrapper_p.cpp b/src/libs/installer/libarchivewrapper_p.cpp
index e5c1e8598..b4325243d 100644
--- a/src/libs/installer/libarchivewrapper_p.cpp
+++ b/src/libs/installer/libarchivewrapper_p.cpp
@@ -41,12 +41,14 @@ namespace QInstaller {
*/
/*!
+ \internal
\fn QInstaller::LibArchiveWrapperPrivate::dataBlockRequested()
Emitted when the server process has requested another data block.
*/
/*!
+ \internal
\fn QInstaller::LibArchiveWrapperPrivate::remoteWorkerFinished()
Emitted when the server process has finished extracting an archive.
@@ -105,7 +107,7 @@ void LibArchiveWrapperPrivate::setFilename(const QString &filename)
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::AbstractArchiveSetFilename), filename, dummy);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::AbstractArchiveSetFilename), filename);
m_lock.unlock();
}
m_archive.setFilename(filename);
@@ -145,7 +147,7 @@ bool LibArchiveWrapperPrivate::extract(const QString &dirPath, const quint64 tot
timer.start();
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::AbstractArchiveExtract), dirPath, total);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::AbstractArchiveExtract), dirPath, total);
m_lock.unlock();
{
QEventLoop loop;
@@ -203,7 +205,7 @@ void LibArchiveWrapperPrivate::setCompressionLevel(const AbstractArchive::Compre
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::AbstractArchiveSetCompressionLevel), level, dummy);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::AbstractArchiveSetCompressionLevel), level);
m_lock.unlock();
return;
}
@@ -219,7 +221,7 @@ void LibArchiveWrapperPrivate::cancel()
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::AbstractArchiveCancel));
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::AbstractArchiveCancel));
m_lock.unlock();
return;
}
@@ -354,7 +356,7 @@ void LibArchiveWrapperPrivate::addDataBlock(const QByteArray &buffer)
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::AbstractArchiveAddDataBlock), buffer, dummy);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::AbstractArchiveAddDataBlock), buffer);
m_lock.unlock();
}
}
@@ -367,7 +369,7 @@ void LibArchiveWrapperPrivate::setClientDataAtEnd()
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::AbstractArchiveSetClientDataAtEnd));
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::AbstractArchiveSetClientDataAtEnd));
m_lock.unlock();
}
}
@@ -379,7 +381,7 @@ void LibArchiveWrapperPrivate::setClientFilePosition(qint64 pos)
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::AbstractArchiveSetFilePosition), pos, dummy);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::AbstractArchiveSetFilePosition), pos);
m_lock.unlock();
}
}
diff --git a/src/libs/installer/licenseoperation.cpp b/src/libs/installer/licenseoperation.cpp
index dc863c1da..0d30ab514 100644
--- a/src/libs/installer/licenseoperation.cpp
+++ b/src/libs/installer/licenseoperation.cpp
@@ -31,6 +31,7 @@
#include "packagemanagercore.h"
#include "settings.h"
#include "fileutils.h"
+#include "globals.h"
#include <QtCore/QDir>
#include <QtCore/QFile>
@@ -56,7 +57,7 @@ void LicenseOperation::backup()
bool LicenseOperation::performOperation()
{
- QVariantMap licenses = value(QLatin1String("licenses")).toMap();
+ QVariantMap licenses = value(scLicensesValue).toMap();
if (licenses.isEmpty()) {
setError(UserDefinedError);
setErrorString(tr("No license files found to copy."));
@@ -86,9 +87,11 @@ bool LicenseOperation::performOperation()
return false;
}
- QTextStream stream(&file);
- stream.setCodec("UTF-8");
+ QString outString;
+ QTextStream stream(&outString);
stream << it.value().toString();
+
+ file.write(outString.toUtf8());
}
return true;
@@ -96,11 +99,10 @@ bool LicenseOperation::performOperation()
bool LicenseOperation::undoOperation()
{
- const QVariantMap licenses = value(QLatin1String("licenses")).toMap();
+ const QVariantMap licenses = value(scLicensesValue).toMap();
if (licenses.isEmpty()) {
- setError(UserDefinedError);
- setErrorString(tr("No license files found to delete."));
- return false;
+ qCWarning(QInstaller::lcInstallerInstallLog) << "No license files found to delete.";
+ return true;
}
QString targetDir = arguments().value(0);
diff --git a/src/libs/installer/link.cpp b/src/libs/installer/link.cpp
index ff9f49696..027dac232 100644
--- a/src/libs/installer/link.cpp
+++ b/src/libs/installer/link.cpp
@@ -263,7 +263,7 @@ QString Link::targetPath() const
#ifdef Q_OS_WIN
return readWindowsSymLink(m_path);
#else
- return QFileInfo(m_path).readLink();
+ return QFileInfo(m_path).symLinkTarget();
#endif
}
diff --git a/src/libs/installer/loggingutils.cpp b/src/libs/installer/loggingutils.cpp
index 189a1d050..9a36720dd 100644
--- a/src/libs/installer/loggingutils.cpp
+++ b/src/libs/installer/loggingutils.cpp
@@ -29,8 +29,10 @@
#include "loggingutils.h"
#include "component.h"
+#include "componentalias.h"
#include "globals.h"
#include "fileutils.h"
+#include "packagemanagercore.h"
#include "remoteclient.h"
#include "remotefileengine.h"
@@ -366,6 +368,44 @@ void LoggingHandler::printPackageInformation(const PackagesList &matchedPackages
}
/*!
+ Prints basic or more detailed information about component \a aliases,
+ depending on the current verbosity level.
+*/
+void LoggingHandler::printAliasInformation(const QList<ComponentAlias *> &aliases)
+{
+ QList<ComponentAlias *> sortedAliases = aliases;
+ std::sort(sortedAliases.begin(), sortedAliases.end(),
+ [](const ComponentAlias *lhs, const ComponentAlias *rhs) {
+ return lhs->name() < rhs->name();
+ }
+ );
+
+ QString output;
+ QTextStream stream(&output);
+
+ stream << Qt::endl;
+ for (auto *alias : qAsConst(sortedAliases)) {
+ stream << "Name: " << alias->name() << Qt::endl;
+ stream << "Display name: " << alias->displayName() << Qt::endl;
+ stream << "Description: " << alias->description() << Qt::endl;
+ stream << "Version: " << alias->version() << Qt::endl;
+ if (verboseLevel() == VerbosityLevel::Detailed)
+ stream << "Virtual: " << alias->value(scVirtual) << Qt::endl;
+
+ stream << "Components: " << alias->value(scRequiredComponents) << Qt::endl;
+ stream << "Required aliases: " << alias->value(scRequiredAliases) << Qt::endl;
+
+ stream << "Optional components: " << alias->value(scOptionalComponents) << Qt::endl;
+ stream << "Optional aliases: " << alias->value(scOptionalAliases) << Qt::endl;
+
+ if (sortedAliases.indexOf(alias) != (sortedAliases.count() - 1))
+ stream << "========================================" << Qt::endl;
+ }
+
+ std::cout << qPrintable(output);
+}
+
+/*!
\internal
*/
VerboseWriter::VerboseWriter()
@@ -442,7 +482,7 @@ VerboseWriter *VerboseWriter::instance()
*/
void VerboseWriter::appendLine(const QString &msg)
{
- m_stream << msg << endl;
+ m_stream << msg << Qt::endl;
}
/*!
diff --git a/src/libs/installer/loggingutils.h b/src/libs/installer/loggingutils.h
index 8bd4217ee..18ff2d2c5 100644
--- a/src/libs/installer/loggingutils.h
+++ b/src/libs/installer/loggingutils.h
@@ -41,6 +41,7 @@
namespace QInstaller {
class Component;
+class ComponentAlias;
class INSTALLER_EXPORT LoggingHandler
{
@@ -67,6 +68,7 @@ public:
void printUpdateInformation(const QList<Component *> &components) const;
void printLocalPackageInformation(const QList<KDUpdater::LocalPackage> &packages) const;
void printPackageInformation(const PackagesList &matchedPackages, const LocalPackagesMap &installedPackages) const;
+ void printAliasInformation(const QList<ComponentAlias *> &aliases);
friend VerbosityLevel &operator++(VerbosityLevel &level, int);
friend VerbosityLevel &operator--(VerbosityLevel &level, int);
diff --git a/src/libs/installer/messageboxhandler.cpp b/src/libs/installer/messageboxhandler.cpp
index e2d461ba8..052709e51 100644
--- a/src/libs/installer/messageboxhandler.cpp
+++ b/src/libs/installer/messageboxhandler.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -368,6 +368,9 @@ static QMessageBox::StandardButton showNewMessageBox(QWidget *parent, QMessageBo
QMessageBox::StandardButton defaultButton)
{
QMessageBox msgBox(icon, title, text, QMessageBox::NoButton, parent);
+ msgBox.setTextInteractionFlags(Qt::TextBrowserInteraction);
+ msgBox.setTextFormat(Qt::RichText);
+
QDialogButtonBox *buttonBox = msgBox.findChild<QDialogButtonBox *>();
Q_ASSERT(buttonBox != nullptr);
diff --git a/src/libs/installer/metadata.cpp b/src/libs/installer/metadata.cpp
index 0469ed8b1..2eccb020e 100644
--- a/src/libs/installer/metadata.cpp
+++ b/src/libs/installer/metadata.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -36,6 +36,7 @@
#include <QDir>
#include <QDomDocument>
#include <QFile>
+#include <QByteArrayMatcher>
namespace QInstaller {
@@ -45,6 +46,52 @@ namespace QInstaller {
\brief The Metadata class represents fetched metadata from a repository.
*/
+
+/*!
+ \internal
+*/
+static bool verifyFileIntegrityFromElement(const QDomElement &element, const QString &childNodeName,
+ const QString &attribute, const QString &metaDirectory, bool testChecksum)
+{
+ const QDomNodeList nodes = element.childNodes();
+ for (int i = 0; i < nodes.count(); ++i) {
+ const QDomNode node = nodes.at(i);
+ if (node.nodeName() != childNodeName)
+ continue;
+
+ const QDir dir(metaDirectory);
+ const QString filename = attribute.isEmpty()
+ ? node.toElement().text()
+ : node.toElement().attribute(attribute);
+
+ if (filename.isEmpty())
+ continue;
+
+ QFile file(dir.absolutePath() + QDir::separator() + filename);
+ if (!file.open(QIODevice::ReadOnly)) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Cannot open" << file.fileName()
+ << "for reading:" << file.errorString();
+ return false;
+ }
+
+ if (!testChecksum)
+ continue;
+
+ QCryptographicHash hash(QCryptographicHash::Sha1);
+ hash.addData(&file);
+
+ const QByteArray checksum = hash.result().toHex();
+ if (!QFileInfo::exists(dir.absolutePath() + QDir::separator()
+ + QString::fromLatin1(checksum) + QLatin1String(".sha1"))) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Unexpected checksum for file" << file.fileName();
+ return false;
+ }
+ }
+ return true;
+}
+
/*!
Constructs a new metadata object.
*/
@@ -121,8 +168,11 @@ QDomDocument Metadata::updatesDocument() const
}
/*!
- Returns \c true if the \c Updates.xml document of this metadata
- exists and can be opened for reading, \c false otherwise.
+ Returns \c true if the \c Updates.xml document of this metadata exists, and that all
+ meta files referenced in the document exist. If the \c Updates.xml contains a \c Checksum
+ element with a value of \c true, the integrity of the files is also verified.
+
+ Returns \c false otherwise.
*/
bool Metadata::isValid() const
{
@@ -133,7 +183,8 @@ bool Metadata::isValid() const
<< "for reading:" << updateFile.errorString();
return false;
}
- return true;
+
+ return verifyMetaFiles(&updateFile);
}
/*!
@@ -254,4 +305,106 @@ QString Metadata::persistentRepositoryPath()
return m_persistentRepositoryPath;
}
+/*!
+ Returns true if the updates document of this metadata contains the repository
+ update element, which can include actions to \c add, \c remove, and \c replace
+ repositories.
+
+ \note This function does not check that the repository updates are actually
+ valid, only that the updates document contains the \c RepositoryUpdate element.
+*/
+bool Metadata::containsRepositoryUpdates() const
+{
+ QFile updateFile(path() + QLatin1String("/Updates.xml"));
+ if (!updateFile.open(QIODevice::ReadOnly)) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Cannot open" << updateFile.fileName()
+ << "for reading:" << updateFile.errorString();
+ return false;
+ }
+
+ static const auto matcher = qMakeStaticByteArrayMatcher("<RepositoryUpdate>");
+ while (!updateFile.atEnd()) {
+ const QByteArray line = updateFile.readLine().simplified();
+ if (matcher.indexIn(line) != -1)
+ return true;
+ }
+
+ return false;
+}
+
+/*!
+ Verifies that the files referenced in \a updateFile document exist
+ on disk. If the document contains a \c Checksum element with a value
+ of \c true, the integrity of the files is also verified.
+
+ Returns \c true if the meta files are valid, \c false otherwise.
+*/
+bool Metadata::verifyMetaFiles(QFile *updateFile) const
+{
+ QDomDocument doc;
+ QString errorString;
+ if (!doc.setContent(updateFile, &errorString)) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Cannot set document content:" << errorString;
+ return false;
+ }
+
+ const QDomElement rootElement = doc.documentElement();
+ const QDomNodeList childNodes = rootElement.childNodes();
+
+ bool testChecksum = true;
+ const QDomElement checksumElement = rootElement.firstChildElement(QLatin1String("Checksum"));
+ if (!checksumElement.isNull())
+ testChecksum = (checksumElement.text().toLower() == scTrue);
+
+ for (int i = 0; i < childNodes.count(); ++i) {
+ const QDomElement element = childNodes.at(i).toElement();
+ if (element.isNull() || element.tagName() != QLatin1String("PackageUpdate"))
+ continue;
+
+ const QDomNodeList c2 = element.childNodes();
+ QString packageName;
+ QString unused1;
+ QString unused2;
+
+ // Only need the package name, so values for "online" and "testCheckSum" do not matter
+ if (!MetadataJob::parsePackageUpdate(c2, packageName, unused1, unused2, true, true))
+ continue; // nothing to check for this package
+
+ const QString packagePath = QString::fromLatin1("%1/%2/").arg(path(), packageName);
+ for (auto &metaTagName : scMetaElements) {
+ const QDomElement metaElement = element.firstChildElement(metaTagName);
+ if (metaElement.isNull())
+ continue;
+
+ if (metaElement.tagName() == QLatin1String("Licenses")) {
+ if (!verifyFileIntegrityFromElement(metaElement, QLatin1String("License"),
+ QLatin1String("file"), packagePath, testChecksum)) {
+ return false;
+ }
+ } else if (metaElement.tagName() == QLatin1String("UserInterfaces")) {
+ if (!verifyFileIntegrityFromElement(metaElement, QLatin1String("UserInterface"),
+ QString(), packagePath, testChecksum)) {
+ return false;
+ }
+ } else if (metaElement.tagName() == QLatin1String("Translations")) {
+ if (!verifyFileIntegrityFromElement(metaElement, QLatin1String("Translation"),
+ QString(), packagePath, testChecksum)) {
+ return false;
+ }
+ } else if (metaElement.tagName() == QLatin1String("Script")) {
+ if (!verifyFileIntegrityFromElement(metaElement.parentNode().toElement(),
+ QLatin1String("Script"), QString(), packagePath, testChecksum)) {
+ return false;
+ }
+ } else {
+ Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown meta element.");
+ }
+ }
+ }
+
+ return true;
+}
+
} // namespace QInstaller
diff --git a/src/libs/installer/metadata.h b/src/libs/installer/metadata.h
index af7acf6b9..c7e4e857c 100644
--- a/src/libs/installer/metadata.h
+++ b/src/libs/installer/metadata.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -35,6 +35,8 @@
#include <QDomDocument>
+class QFile;
+
namespace QInstaller {
class INSTALLER_EXPORT Metadata : public CacheableItem
@@ -61,6 +63,11 @@ public:
void setPersistentRepositoryPath(const QUrl &url);
QString persistentRepositoryPath();
+ bool containsRepositoryUpdates() const;
+
+private:
+ bool verifyMetaFiles(QFile *updateFile) const;
+
private:
Repository m_repository;
QString m_persistentRepositoryPath;
@@ -69,13 +76,6 @@ private:
bool m_fromDefaultRepository;
};
-Q_GLOBAL_STATIC_WITH_ARGS(QStringList, scMetaElements, (QStringList(
- QLatin1String("Script")) <<
- QLatin1String("Licenses") <<
- QLatin1String("UserInterfaces") <<
- QLatin1String("Translations")
-));
-
} // namespace QInstaller
#endif // METADATA_H
diff --git a/src/libs/installer/metadatacache.cpp b/src/libs/installer/metadatacache.cpp
new file mode 100644
index 000000000..744e455f4
--- /dev/null
+++ b/src/libs/installer/metadatacache.cpp
@@ -0,0 +1,67 @@
+/**************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "metadatacache.h"
+
+#define QUOTE_(x) #x
+#define QUOTE(x) QUOTE_(x)
+
+namespace QInstaller {
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::MetadataCache
+ \brief The MetadataCache is a class for a checksum based storage of \c Metadata objects on disk.
+
+ MetadataCache manages a cache storage for a set \l{path()}, which contains
+ a subdirectory for each registered \c Metadata item. The cache has a manifest file in
+ its root directory, which lists the version and type of the cache, and all its items.
+ The file is updated automatically when the metadata cache object is destructed, or
+ it can be updated periodically by calling \l{sync()}.
+*/
+
+/*!
+ Constructs a new empty cache. The cache is invalid until set with a
+ path and initialized.
+*/
+MetadataCache::MetadataCache()
+ : GenericDataCache<Metadata>()
+{
+ setType(QLatin1String("Metadata"));
+ setVersion(QLatin1String(QUOTE(IFW_CACHE_FORMAT_VERSION)));
+}
+
+/*!
+ Constructs a cache to \a path. The cache is initialized automatically.
+*/
+MetadataCache::MetadataCache(const QString &path)
+ : GenericDataCache(path, QLatin1String("Metadata"), QLatin1String(QUOTE(IFW_CACHE_FORMAT_VERSION)))
+{
+}
+
+} // namespace QInstaller
diff --git a/src/libs/installer/metadatacache.h b/src/libs/installer/metadatacache.h
new file mode 100644
index 000000000..804d1b6db
--- /dev/null
+++ b/src/libs/installer/metadatacache.h
@@ -0,0 +1,46 @@
+/**************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef METADATACACHE_H
+#define METADATACACHE_H
+
+#include "genericdatacache.h"
+#include "metadata.h"
+
+namespace QInstaller {
+
+class MetadataCache : public GenericDataCache<Metadata>
+{
+public:
+ MetadataCache();
+ explicit MetadataCache(const QString &path);
+};
+
+} // namespace QInstaller
+
+#endif // METADATACACHE_H
diff --git a/src/libs/installer/metadatajob.cpp b/src/libs/installer/metadatajob.cpp
index 5179896ac..1bed304c6 100644
--- a/src/libs/installer/metadatajob.cpp
+++ b/src/libs/installer/metadatajob.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.
@@ -41,9 +41,7 @@
#include <QtConcurrent>
#include <QtMath>
#include <QRandomGenerator>
-
-#define QUOTE_(x) #x
-#define QUOTE(x) QUOTE_(x)
+#include <QApplication>
namespace QInstaller {
@@ -52,7 +50,6 @@ namespace QInstaller {
\value All
\value CompressedPackage
- \value UpdatesXML
*/
/*!
@@ -145,9 +142,6 @@ QList<Metadata *> MetadataJob::metadata() const
QHash<RepositoryCategory, QSet<Repository>>::const_iterator it;
for (it = repositoryHash.constBegin(); it != repositoryHash.constEnd(); ++it) {
- if (m_core->isUpdater())
- return true;
-
if (!it.key().isEnabled())
continue; // Let's try the next one
@@ -192,8 +186,6 @@ bool MetadataJob::resetCache(bool init)
m_metaFromCache.clear();
m_metaFromCache.setPath(m_core->settings().localCachePath());
- m_metaFromCache.setType(QLatin1String("Metadata"));
- m_metaFromCache.setVersion(QLatin1String(QUOTE(IFW_REPOSITORY_FORMAT_VERSION)));
if (!init)
return true;
@@ -218,6 +210,11 @@ bool MetadataJob::clearCache()
return false;
}
+bool MetadataJob::isValidCache() const
+{
+ return m_metaFromCache.isValid();
+}
+
// -- private slots
void MetadataJob::doStart()
@@ -225,6 +222,8 @@ void MetadataJob::doStart()
setError(Job::NoError);
setErrorString(QString());
m_metadataResult.clear();
+ setProgressTotalAmount(100);
+
if (!m_core) {
emitFinishedWithError(Job::Canceled, tr("Missing package manager core engine."));
return; // We can't do anything here without core, so avoid tons of !m_core checks.
@@ -235,34 +234,78 @@ void MetadataJob::doStart()
}
const ProductKeyCheck *const productKeyCheck = ProductKeyCheck::instance();
- if (m_downloadType == DownloadType::All || m_downloadType == DownloadType::UpdatesXML) {
+ if (m_downloadType != DownloadType::CompressedPackage) {
emit infoMessage(this, tr("Fetching latest update information..."));
const bool onlineInstaller = m_core->isInstaller() && !m_core->isOfflineOnly();
- if (onlineInstaller || m_core->isMaintainer()) {
- QList<FileTaskItem> items;
- QSet<Repository> repositories = getRepositories();
+ const QSet<Repository> repositories = getRepositories();
+
+ if (onlineInstaller || m_core->isMaintainer()
+ || (m_core->settings().allowRepositoriesForOfflineInstaller() && !repositories.isEmpty())) {
+ static const QString updateFilePath(QLatin1Char('/') + scUpdatesXML + QLatin1Char('?'));
+ static const QString randomQueryString = QString::number(QRandomGenerator::global()->generate());
+
+ quint64 cachedCount = 0;
+ setProgressTotalAmount(0); // Show only busy indicator during this loop as we have no progress to measure
foreach (const Repository &repo, repositories) {
+ // For not blocking the UI
+ qApp->processEvents();
+
if (repo.isEnabled() &&
productKeyCheck->isValidRepository(repo)) {
QAuthenticator authenticator;
authenticator.setUser(repo.username());
authenticator.setPassword(repo.password());
- if (!repo.isCompressed()) {
- QString url = repo.url().toString() + QLatin1String("/Updates.xml?");
- if (!m_core->value(scUrlQueryString).isEmpty())
- url += m_core->value(scUrlQueryString) + QLatin1Char('&');
+ if (repo.isCompressed())
+ continue;
+
+ QString url;
+ url = repo.url().toString() + updateFilePath;
+ if (!m_core->value(scUrlQueryString).isEmpty())
+ url += m_core->value(scUrlQueryString) + QLatin1Char('&');
+ // also append a random string to avoid proxy caches
+ url.append(randomQueryString);
+
+ // Check if we can skip downloading already cached repositories
+ const Status foundStatus = findCachedUpdatesFile(repo, url);
+ if (foundStatus == XmlDownloadSuccess) {
+ // Found existing Updates.xml
+ ++cachedCount;
+ continue;
+ } else if (foundStatus == XmlDownloadRetry) {
+ // Repositories changed, restart with the new repositories
+ QMetaObject::invokeMethod(this, "doStart", Qt::QueuedConnection);
+ return;
+ }
- // also append a random string to avoid proxy caches
- FileTaskItem item(url.append(QString::number(QRandomGenerator::global()->generate())));
- item.insert(TaskRole::UserRole, QVariant::fromValue(repo));
- item.insert(TaskRole::Authenticator, QVariant::fromValue(authenticator));
- items.append(item);
+ QTemporaryDir tmp(QDir::tempPath() + QLatin1String("/remoterepo-XXXXXX"));
+ if (!tmp.isValid()) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot create unique temporary directory.";
+ continue;
}
+ tmp.setAutoRemove(false);
+ m_tempDirDeleter.add(tmp.path());
+ FileTaskItem item(url, tmp.path() + QLatin1String("/Updates.xml"));
+ item.insert(TaskRole::UserRole, QVariant::fromValue(repo));
+ item.insert(TaskRole::Authenticator, QVariant::fromValue(authenticator));
+ m_updatesXmlItems.append(item);
}
}
- if (items.count() > 0) {
- startXMLTask(items);
+ setProgressTotalAmount(100);
+ const quint64 totalCount = repositories.count();
+ if (cachedCount > 0) {
+ qCDebug(lcInstallerInstallLog).nospace() << "Loaded from cache "
+ << cachedCount << "/" << totalCount << ". Downloading remaining "
+ << m_updatesXmlItems.count() << "/" << totalCount <<".";
+ } else {
+ qCDebug(lcInstallerInstallLog).nospace() <<"Downloading " << m_updatesXmlItems.count()
+ << " items to cache.";
+ }
+ if (m_updatesXmlItems.count() > 0) {
+ double taskCount = m_updatesXmlItems.length()/static_cast<double>(m_downloadableChunkSize);
+ m_totalTaskCount = qCeil(taskCount);
+ m_taskNumber = 0;
+ startXMLTask();
} else {
emitFinished();
}
@@ -301,14 +344,22 @@ void MetadataJob::doStart()
}
}
-void MetadataJob::startXMLTask(const QList<FileTaskItem> &items)
+bool MetadataJob::startXMLTask()
{
- DownloadFileTask *const xmlTask = new DownloadFileTask(items);
- xmlTask->setProxyFactory(m_core->proxyFactory());
- setProgressTotalAmount(100);
- connect(&m_xmlTask, &QFutureWatcher<FileTaskResult>::progressValueChanged, this,
- &MetadataJob::progressChanged);
- m_xmlTask.setFuture(QtConcurrent::run(&DownloadFileTask::doTask, xmlTask));
+ int chunkSize = qMin(m_updatesXmlItems.length(), m_downloadableChunkSize);
+ QList<FileTaskItem> tempPackages = m_updatesXmlItems.mid(0, chunkSize);
+ m_updatesXmlItems = m_updatesXmlItems.mid(chunkSize, m_updatesXmlItems.length());
+ if (tempPackages.length() > 0) {
+ DownloadFileTask *const xmlTask = new DownloadFileTask(tempPackages);
+ xmlTask->setProxyFactory(m_core->proxyFactory());
+ connect(&m_xmlTask, &QFutureWatcher<FileTaskResult>::progressValueChanged, this,
+ &MetadataJob::progressChanged);
+ m_xmlTask.setFuture(QtConcurrent::run(&DownloadFileTask::doTask, xmlTask));
+
+ setInfoMessage(tr("Retrieving information from remote repositories..."));
+ return true;
+ }
+ return false;
}
void MetadataJob::doCancel()
@@ -385,9 +436,17 @@ void MetadataJob::unzipRepositoryTaskFinished()
error = testJob.error();
errorString = testJob.errorString();
if (error == Job::NoError) {
- FileTaskItem item(url);
+ QTemporaryDir tmp(QDir::tempPath() + QLatin1String("/remoterepo-XXXXXX"));
+ if (!tmp.isValid()) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot create unique temporary directory.";
+ continue;
+ }
+ tmp.setAutoRemove(false);
+ m_tempDirDeleter.add(tmp.path());
+ FileTaskItem item(url, tmp.path() + QLatin1String("/Updates.xml"));
+
item.insert(TaskRole::UserRole, QVariant::fromValue(repo));
- m_unzipRepositoryitems.append(item);
+ m_updatesXmlItems.append(item);
} else {
//Repository is not valid, remove it
Settings &s = m_core->settings();
@@ -407,8 +466,8 @@ void MetadataJob::unzipRepositoryTaskFinished()
//One can specify many zipped repository items at once. As the repositories are
//unzipped one by one, we collect here all items before parsing xml files from those.
- if (m_unzipRepositoryitems.count() > 0 && m_unzipRepositoryTasks.isEmpty()) {
- startXMLTask(m_unzipRepositoryitems);
+ if (m_updatesXmlItems.count() > 0 && m_unzipRepositoryTasks.isEmpty()) {
+ startXMLTask();
} else {
if (error != Job::NoError) {
emitFinishedWithError(QInstaller::DownloadError, errorString);
@@ -432,19 +491,34 @@ void MetadataJob::xmlTaskFinished()
Status status = XmlDownloadFailure;
try {
m_xmlTask.waitForFinished();
- status = parseUpdatesXml(m_xmlTask.future().results());
+ m_updatesXmlResult.append(m_xmlTask.future().results());
+ if (!startXMLTask()) {
+ status = parseUpdatesXml(m_updatesXmlResult);
+ m_updatesXmlResult.clear();
+ } else {
+ return;
+ }
} catch (const AuthenticationRequiredException &e) {
if (e.type() == AuthenticationRequiredException::Type::Proxy) {
- const QNetworkProxy proxy = e.proxy();
- ProxyCredentialsDialog proxyCredentials(proxy);
qCWarning(QInstaller::lcInstallerInstallLog) << e.message();
-
- if (proxyCredentials.exec() == QDialog::Accepted) {
+ QString username;
+ QString password;
+ const QNetworkProxy proxy = e.proxy();
+ if (m_core->isCommandLineInstance()) {
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote() << QString::fromLatin1("The proxy %1:%2 requires a username and password").arg(proxy.hostName(), proxy.port());
+ askForCredentials(&username, &password, QLatin1String("Username: "), QLatin1String("Password: "));
+ } else {
+ ProxyCredentialsDialog proxyCredentials(proxy);
+ if (proxyCredentials.exec() == QDialog::Accepted) {
+ username = proxyCredentials.userName();
+ password = proxyCredentials.password();
+ }
+ }
+ if (!username.isEmpty()) {
qCDebug(QInstaller::lcInstallerInstallLog) << "Retrying with new credentials ...";
PackageManagerProxyFactory *factory = m_core->proxyFactory();
- factory->setProxyCredentials(proxy, proxyCredentials.userName(),
- proxyCredentials.password());
+ factory->setProxyCredentials(proxy, username, password);
m_core->setProxyFactory(factory);
status = XmlDownloadRetry;
} else {
@@ -453,13 +527,25 @@ void MetadataJob::xmlTaskFinished()
}
} else if (e.type() == AuthenticationRequiredException::Type::Server) {
qCWarning(QInstaller::lcInstallerInstallLog) << e.message();
- ServerAuthenticationDialog dlg(e.message(), e.taskItem());
- if (dlg.exec() == QDialog::Accepted) {
+ QString username;
+ QString password;
+ if (m_core->isCommandLineInstance()) {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Server Requires Authentication";
+ qCDebug(QInstaller::lcInstallerInstallLog) << "You need to supply a username and password to access this site.";
+ askForCredentials(&username, &password, QLatin1String("Username: "), QLatin1String("Password: "));
+ } else {
+ ServerAuthenticationDialog dlg(e.message(), e.taskItem());
+ if (dlg.exec() == QDialog::Accepted) {
+ username = dlg.user();
+ password = dlg.password();
+ }
+ }
+ if (!username.isEmpty()) {
Repository original = e.taskItem().value(TaskRole::UserRole)
.value<Repository>();
Repository replacement = original;
- replacement.setUsername(dlg.user());
- replacement.setPassword(dlg.password());
+ replacement.setUsername(username);
+ replacement.setPassword(password);
Settings &s = m_core->settings();
QSet<Repository> temporaries = s.temporaryRepositories();
@@ -468,7 +554,7 @@ void MetadataJob::xmlTaskFinished()
temporaries.insert(replacement);
s.addTemporaryRepositories(temporaries, true);
} else {
- QHash<QString, QPair<Repository, Repository> > update;
+ QMultiHash<QString, QPair<Repository, Repository> > update;
update.insert(QLatin1String("replace"), qMakePair(original, replacement));
if (s.updateRepositoryCategories(update) == Settings::UpdatesApplied)
@@ -509,16 +595,13 @@ void MetadataJob::xmlTaskFinished()
return;
if (status == XmlDownloadSuccess) {
- if (m_downloadType != DownloadType::UpdatesXML) {
- if (!fetchMetaDataPackages()) {
- // No new metadata packages to fetch, still need to update the cache
- // for refreshed repositories.
- startUpdateCacheTask();
- }
- } else {
- startUpdateCacheTask();
+ if (!fetchMetaDataPackages()) {
+ // No new metadata packages to fetch, still need to update the cache
+ // for refreshed repositories.
+ startUpdateCacheTask();
}
} else if (status == XmlDownloadRetry) {
+ reset();
QMetaObject::invokeMethod(this, "doStart", Qt::QueuedConnection);
} else {
reset();
@@ -579,15 +662,27 @@ void MetadataJob::metadataTaskFinished()
} else {
throw QInstaller::TaskException(mismatchMessage);
}
+ QFileInfo fi(result.target());
+ QString targetPath = fi.absolutePath();
+ if (m_fetchedMetadata.contains(targetPath)) {
+ delete m_fetchedMetadata.value(targetPath);
+ m_fetchedMetadata.remove(targetPath);
+ }
+ continue;
}
UnzipArchiveTask *task = new UnzipArchiveTask(result.target(),
item.value(TaskRole::UserRole).toString());
+ task->setRemoveArchive(true);
+ task->setStoreChecksums(true);
QFutureWatcher<void> *watcher = new QFutureWatcher<void>();
m_unzipTasks.insert(watcher, qobject_cast<QObject*> (task));
connect(watcher, &QFutureWatcherBase::finished, this, &MetadataJob::unzipTaskFinished);
watcher->setFuture(QtConcurrent::run(&UnzipArchiveTask::doTask, task));
}
+ if (m_unzipTasks.isEmpty())
+ startUpdateCacheTask();
+
} else {
startUpdateCacheTask();
}
@@ -633,17 +728,11 @@ bool MetadataJob::fetchMetaDataPackages()
QList<FileTaskItem> tempPackages = m_packages.mid(0, chunkSize);
m_packages = m_packages.mid(chunkSize, m_packages.length());
if (tempPackages.length() > 0) {
- m_taskNumber++;
setProcessedAmount(0);
DownloadFileTask *const metadataTask = new DownloadFileTask(tempPackages);
metadataTask->setProxyFactory(m_core->proxyFactory());
m_metadataTask.setFuture(QtConcurrent::run(&DownloadFileTask::doTask, metadataTask));
- QString metaInformation;
- if (m_totalTaskCount > 1)
- metaInformation = tr("Retrieving meta information from remote repository... %1/%2 ").arg(m_taskNumber).arg(m_totalTaskCount);
- else
- metaInformation = tr("Retrieving meta information from remote repository... ");
- emit infoMessage(this, metaInformation);
+ setInfoMessage(tr("Retrieving meta information from remote repository..."));
return true;
}
return false;
@@ -652,6 +741,7 @@ bool MetadataJob::fetchMetaDataPackages()
void MetadataJob::reset()
{
m_packages.clear();
+ m_updatesXmlItems.clear();
m_defaultRepositoriesFetched = false;
m_fetchedCategorizedRepositories.clear();
@@ -670,6 +760,7 @@ void MetadataJob::reset()
} catch (...) {}
m_tempDirDeleter.releaseAndDeleteAll();
m_metadataResult.clear();
+ m_updatesXmlResult.clear();
m_taskNumber = 0;
}
@@ -677,7 +768,6 @@ void MetadataJob::resetCompressedFetch()
{
setError(Job::NoError);
setErrorString(QString());
- m_unzipRepositoryitems.clear();
try {
foreach (QFutureWatcher<void> *const watcher, m_unzipTasks.keys()) {
@@ -709,36 +799,31 @@ MetadataJob::Status MetadataJob::parseUpdatesXml(const QList<FileTaskResult> &re
if (result.target().isEmpty()) {
continue;
}
- QTemporaryDir tmp(QDir::tempPath() + QLatin1String("/remoterepo-XXXXXX"));
- if (!tmp.isValid()) {
- qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot create unique temporary directory.";
- return XmlDownloadFailure;
- }
-
- tmp.setAutoRemove(false);
- QScopedPointer<Metadata> metadata(new Metadata(tmp.path()));
- m_tempDirDeleter.add(metadata->path());
+ QFileInfo fileInfo(result.target());
+ std::unique_ptr<Metadata> metadata(new Metadata(fileInfo.absolutePath()));
QFile file(result.target());
- if (!file.rename(metadata->path() + QLatin1String("/Updates.xml"))) {
- qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot rename target to Updates.xml:"
- << file.errorString();
- return XmlDownloadFailure;
- }
-
if (!file.open(QIODevice::ReadOnly)) {
qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot open Updates.xml for reading:"
<< file.errorString();
return XmlDownloadFailure;
}
const FileTaskItem item = result.value(TaskRole::TaskItem).value<FileTaskItem>();
+ const Repository repository = item.value(TaskRole::UserRole).value<Repository>();
- // Check if we have cached the metadata for this repository already
QCryptographicHash hash(QCryptographicHash::Sha1);
hash.addData(&file);
const QByteArray updatesChecksum = hash.result().toHex();
+ if (!repository.xmlChecksum().isEmpty() && updatesChecksum != repository.xmlChecksum()) {
+ qCWarning(lcDeveloperBuild).noquote().nospace() << "The checksum for Updates.xml "
+ "file downloaded from repository:\n" << repository.url().toString() << "\ndoes not "
+ "match the expected value:\n\tActual SHA1: " << updatesChecksum << "\n\tExpected SHA1: "
+ << repository.xmlChecksum() << Qt::endl;
+ }
+
bool refreshed;
+ // Check if we have cached the metadata for this repository already
Status status = refreshCacheItem(result, updatesChecksum, &refreshed);
if (status != XmlDownloadSuccess)
return status;
@@ -760,7 +845,7 @@ MetadataJob::Status MetadataJob::parseUpdatesXml(const QList<FileTaskResult> &re
}
file.close();
- metadata->setRepository(item.value(TaskRole::UserRole).value<Repository>());
+ metadata->setRepository(repository);
const bool online = !(metadata->repository().url().scheme()).isEmpty();
bool testCheckSum = true;
@@ -817,7 +902,7 @@ MetadataJob::Status MetadataJob::parseUpdatesXml(const QList<FileTaskResult> &re
m_fetchedCategorizedRepositories.insert(metadataPtr->repository()); // For faster lookups
const QString metadataPath = metadata->path();
- m_fetchedMetadata.insert(metadataPath, metadata.take());
+ m_fetchedMetadata.insert(metadataPath, metadata.release());
// search for additional repositories that we might need to check
status = parseRepositoryUpdates(root, result, metadataPtr);
@@ -847,6 +932,7 @@ MetadataJob::Status MetadataJob::refreshCacheItem(const FileTaskResult &result,
const FileTaskItem item = result.value(TaskRole::TaskItem).value<FileTaskItem>();
const Repository repository = item.value(TaskRole::UserRole).value<Repository>();
+
if (cachedMetadata->isValid() && !repository.isCompressed()) {
// Refresh repository information to cache. Same repository may appear in multiple
// categories and the metadata may be available from default repositories simultaneously.
@@ -861,13 +947,15 @@ MetadataJob::Status MetadataJob::refreshCacheItem(const FileTaskResult &result,
cachedMetadata->setPersistentRepositoryPath(repository.url());
// search for additional repositories that we might need to check
- QDomDocument doc = cachedMetadata->updatesDocument();
- const Status status = parseRepositoryUpdates(doc.documentElement(), result, cachedMetadata);
- if (status == XmlDownloadRetry) {
- // The repository update may have removed or replaced current repositories,
- // clear repository information from cached items and refresh on next fetch run.
- resetCacheRepositories();
- return status;
+ if (cachedMetadata->containsRepositoryUpdates()) {
+ QDomDocument doc = cachedMetadata->updatesDocument();
+ const Status status = parseRepositoryUpdates(doc.documentElement(), result, cachedMetadata);
+ if (status == XmlDownloadRetry) {
+ // The repository update may have removed or replaced current repositories,
+ // clear repository information from cached items and refresh on next fetch run.
+ resetCacheRepositories();
+ return status;
+ }
}
*refreshed = true;
return XmlDownloadSuccess;
@@ -882,13 +970,38 @@ MetadataJob::Status MetadataJob::refreshCacheItem(const FileTaskResult &result,
return XmlDownloadSuccess;
}
+MetadataJob::Status MetadataJob::findCachedUpdatesFile(const Repository &repository, const QString &fileUrl)
+{
+ if (repository.xmlChecksum().isEmpty())
+ return XmlDownloadFailure;
+
+ Metadata *metadata = m_metaFromCache.itemByChecksum(repository.xmlChecksum());
+ if (!metadata)
+ return XmlDownloadFailure;
+
+ const QString targetPath = metadata->path() + QLatin1Char('/') + scUpdatesXML;
+
+ FileTaskItem cachedMetaTaskItem(fileUrl, targetPath);
+ cachedMetaTaskItem.insert(TaskRole::UserRole, QVariant::fromValue(repository));
+ const FileTaskResult cachedMetaTaskResult(targetPath, repository.xmlChecksum(), cachedMetaTaskItem, false);
+
+ bool isCached = false;
+ const Status status = refreshCacheItem(cachedMetaTaskResult, repository.xmlChecksum(), &isCached);
+ if (isCached)
+ return XmlDownloadSuccess;
+ else if (status == XmlDownloadRetry)
+ return XmlDownloadRetry;
+ else
+ return XmlDownloadFailure;
+}
+
MetadataJob::Status MetadataJob::parseRepositoryUpdates(const QDomElement &root,
const FileTaskResult &result, Metadata *metadata)
{
MetadataJob::Status status = XmlDownloadSuccess;
const QDomNode repositoryUpdate = root.firstChildElement(QLatin1String("RepositoryUpdate"));
if (!repositoryUpdate.isNull()) {
- const QHash<QString, QPair<Repository, Repository> > repositoryUpdates
+ const QMultiHash<QString, QPair<Repository, Repository> > repositoryUpdates
= searchAdditionalRepositories(repositoryUpdate, result, *metadata);
if (!repositoryUpdates.isEmpty())
status = setAdditionalRepositories(repositoryUpdates, result, *metadata);
@@ -908,9 +1021,8 @@ QSet<Repository> MetadataJob::getRepositories()
// Fetch repositories under archive which are selected in UI.
// If repository is already fetched, do not fetch it again.
- // In updater mode, fetch always all archive repositories to get updates
for (const RepositoryCategory &repositoryCategory : m_core->settings().repositoryCategories()) {
- if (!m_core->isUpdater() && !repositoryCategory.isEnabled())
+ if (!repositoryCategory.isEnabled())
continue;
for (const Repository &repository : repositoryCategory.repositories()) {
@@ -950,7 +1062,7 @@ bool MetadataJob::parsePackageUpdate(const QDomNodeList &c2, QString &packageNam
else if ((element.tagName() == QLatin1String("SHA1")) && testCheckSum)
packageHash = element.text();
else {
- foreach (QString meta, *scMetaElements) {
+ foreach (QString meta, scMetaElements) {
if (element.tagName() == meta) {
metaFound = true;
break;
@@ -961,7 +1073,7 @@ bool MetadataJob::parsePackageUpdate(const QDomNodeList &c2, QString &packageNam
return metaFound;
}
-QHash<QString, QPair<Repository, Repository> > MetadataJob::searchAdditionalRepositories
+QMultiHash<QString, QPair<Repository, Repository> > MetadataJob::searchAdditionalRepositories
(const QDomNode &repositoryUpdate, const FileTaskResult &result, const Metadata &metadata)
{
QMultiHash<QString, QPair<Repository, Repository> > repositoryUpdates;
@@ -1010,7 +1122,7 @@ QHash<QString, QPair<Repository, Repository> > MetadataJob::searchAdditionalRepo
return repositoryUpdates;
}
-MetadataJob::Status MetadataJob::setAdditionalRepositories(QHash<QString, QPair<Repository, Repository> > repositoryUpdates,
+MetadataJob::Status MetadataJob::setAdditionalRepositories(QMultiHash<QString, QPair<Repository, Repository> > repositoryUpdates,
const FileTaskResult &result, const Metadata& metadata)
{
MetadataJob::Status status = XmlDownloadSuccess;
@@ -1057,4 +1169,14 @@ MetadataJob::Status MetadataJob::setAdditionalRepositories(QHash<QString, QPair<
}
return status;
}
+
+void MetadataJob::setInfoMessage(const QString &message)
+{
+ m_taskNumber++;
+ QString metaInformation = message;
+ if (m_totalTaskCount > 1)
+ metaInformation = QLatin1String(" %1 %2/%3 ").arg(message).arg(m_taskNumber).arg(m_totalTaskCount);
+ emit infoMessage(this, metaInformation);
+
+}
} // namespace QInstaller
diff --git a/src/libs/installer/metadatajob.h b/src/libs/installer/metadatajob.h
index 18554ca08..13ad3ea8c 100644
--- a/src/libs/installer/metadatajob.h
+++ b/src/libs/installer/metadatajob.h
@@ -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.
@@ -33,7 +33,7 @@
#include "fileutils.h"
#include "job.h"
#include "metadata.h"
-#include "genericdatacache.h"
+#include "metadatacache.h"
#include "repository.h"
#include <QFutureWatcher>
@@ -48,8 +48,7 @@ class PackageManagerCore;
enum DownloadType
{
All,
- CompressedPackage,
- UpdatesXML
+ CompressedPackage
};
class INSTALLER_EXPORT MetadataJob : public Job
@@ -75,6 +74,7 @@ public:
bool resetCache(bool init = false);
bool clearCache();
+ bool isValidCache() const;
private slots:
void doStart() override;
@@ -87,7 +87,7 @@ private slots:
void progressChanged(int progress);
void setProgressTotalAmount(int maximum);
void unzipRepositoryTaskFinished();
- void startXMLTask(const QList<FileTaskItem> &items);
+ bool startXMLTask();
private:
bool fetchMetaDataPackages();
@@ -98,16 +98,18 @@ private:
void resetCompressedFetch();
Status parseUpdatesXml(const QList<FileTaskResult> &results);
Status refreshCacheItem(const FileTaskResult &result, const QByteArray &checksum, bool *refreshed);
+ Status findCachedUpdatesFile(const Repository &repository, const QString &fileUrl);
Status parseRepositoryUpdates(const QDomElement &root, const FileTaskResult &result, Metadata *metadata);
QSet<Repository> getRepositories();
void addFileTaskItem(const QString &source, const QString &target, Metadata *metadata,
const QString &sha1, const QString &packageName);
static bool parsePackageUpdate(const QDomNodeList &c2, QString &packageName, QString &packageVersion,
QString &packageHash, bool online, bool testCheckSum);
- QHash<QString, QPair<Repository, Repository> > searchAdditionalRepositories(const QDomNode &repositoryUpdate,
+ QMultiHash<QString, QPair<Repository, Repository> > searchAdditionalRepositories(const QDomNode &repositoryUpdate,
const FileTaskResult &result, const Metadata &metadata);
- MetadataJob::Status setAdditionalRepositories(QHash<QString, QPair<Repository, Repository> > repositoryUpdates,
+ MetadataJob::Status setAdditionalRepositories(QMultiHash<QString, QPair<Repository, Repository> > repositoryUpdates,
const FileTaskResult &result, const Metadata& metadata);
+ void setInfoMessage(const QString &message);
private:
friend class Metadata;
@@ -116,6 +118,7 @@ private:
PackageManagerCore *m_core;
QList<FileTaskItem> m_packages;
+ QList<FileTaskItem> m_updatesXmlItems;
TempPathDeleter m_tempDirDeleter;
QFutureWatcher<FileTaskResult> m_xmlTask;
QFutureWatcher<FileTaskResult> m_metadataTask;
@@ -123,8 +126,8 @@ private:
QHash<QFutureWatcher<void> *, QObject*> m_unzipTasks;
QHash<QFutureWatcher<void> *, QObject*> m_unzipRepositoryTasks;
DownloadType m_downloadType;
- QList<FileTaskItem> m_unzipRepositoryitems;
QList<FileTaskResult> m_metadataResult;
+ QList<FileTaskResult> m_updatesXmlResult;
int m_downloadableChunkSize;
int m_taskNumber;
int m_totalTaskCount;
@@ -133,7 +136,7 @@ private:
QSet<Repository> m_fetchedCategorizedRepositories;
QHash<QString, Metadata *> m_fetchedMetadata;
- GenericDataCache<Metadata> m_metaFromCache;
+ MetadataCache m_metaFromCache;
};
} // namespace QInstaller
diff --git a/src/libs/installer/metadatajob_p.h b/src/libs/installer/metadatajob_p.h
index 5a4fcad2d..837a7e9ae 100644
--- a/src/libs/installer/metadatajob_p.h
+++ b/src/libs/installer/metadatajob_p.h
@@ -62,10 +62,17 @@ 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)
+ , m_storeChecksums(false)
{}
+
QString target() { return m_targetDir; }
QString archive() { return m_archive; }
+ void setRemoveArchive(bool remove) { m_removeArchive = remove; }
+ void setStoreChecksums(bool store) { m_storeChecksums = store; }
+
void doTask(QFutureInterface<void> &fi) override
{
fi.reportStarted();
@@ -80,16 +87,48 @@ public:
if (!archive) {
fi.reportException(UnzipArchiveException(MetadataJob::tr("Unsupported archive \"%1\": no handler "
"registered for file suffix \"%2\".").arg(m_archive, QFileInfo(m_archive).suffix())));
+ return;
} else if (!archive->open(QIODevice::ReadOnly)) {
fi.reportException(UnzipArchiveException(MetadataJob::tr("Cannot open file \"%1\" for "
"reading: %2").arg(QDir::toNativeSeparators(m_archive), archive->errorString())));
+ return;
} else if (!archive->extract(m_targetDir)) {
fi.reportException(UnzipArchiveException(MetadataJob::tr("Error while extracting "
"archive \"%1\": %2").arg(QDir::toNativeSeparators(m_archive), archive->errorString())));
+ return;
}
- // Don't need the archive anymore
+
+ if (m_storeChecksums) {
+ // Calculate and store checksums of extracted files for later use
+ const QVector<ArchiveEntry> entries = archive->list();
+ for (auto &entry : entries) {
+ if (entry.isDirectory)
+ continue;
+
+ QFile file(m_targetDir + QDir::separator() + entry.path);
+ if (!file.open(QIODevice::ReadOnly)) {
+ fi.reportException(UnzipArchiveException(MetadataJob::tr("Cannot open extracted file \"%1\" for "
+ "reading: %2").arg(QDir::toNativeSeparators(file.fileName()), file.errorString())));
+ break;
+ }
+ QCryptographicHash hash(QCryptographicHash::Sha1);
+ hash.addData(&file);
+
+ const QByteArray hexChecksum = hash.result().toHex();
+ QFileInfo fileInfo(file.fileName());
+ QFile hashFile(fileInfo.absolutePath() + QDir::separator()
+ + QString::fromLatin1(hexChecksum) + QLatin1String(".sha1"));
+ if (!hashFile.open(QIODevice::WriteOnly)) {
+ fi.reportException(UnzipArchiveException(MetadataJob::tr("Cannot open file \"%1\" for "
+ "writing: %2").arg(QDir::toNativeSeparators(hashFile.fileName()), hashFile.errorString())));
+ break;
+ }
+ }
+ }
+
archive->close();
- QFile::remove(m_archive);
+ if (m_removeArchive)
+ QFile::remove(m_archive);
fi.reportFinished();
}
@@ -97,6 +136,8 @@ public:
private:
QString m_archive;
QString m_targetDir;
+ bool m_removeArchive;
+ bool m_storeChecksums;
};
class CacheTaskException : public QException
@@ -122,7 +163,7 @@ class UpdateCacheTask : public AbstractTask<void>
Q_DISABLE_COPY(UpdateCacheTask)
public:
- UpdateCacheTask(GenericDataCache<Metadata> &cache, QHash<QString, Metadata *> &updates)
+ UpdateCacheTask(MetadataCache &cache, QHash<QString, Metadata *> &updates)
: m_cache(&cache)
, m_updates(&updates)
{}
@@ -136,7 +177,7 @@ public:
QStringList registeredKeys;
bool success = true;
for (auto *meta : qAsConst(*m_updates)) {
- if (!m_cache->registerItem(meta, true)) {
+ if (!m_cache->registerItem(meta, true, MetadataCache::Move)) {
success = false;
break;
}
@@ -149,7 +190,7 @@ public:
// Bail out if there was error while registering items
if (!success) {
- fi.reportException(UnzipArchiveException(m_cache->errorString() + u' '
+ fi.reportException(CacheTaskException(m_cache->errorString() + u' '
+ MetadataJob::tr("Clearing the cache directory and restarting the application may solve this.")));
m_cache->sync();
fi.reportFinished();
@@ -162,7 +203,7 @@ public:
m_cache->removeItem(meta->checksum());
if (!m_cache->sync()) {
- fi.reportException(UnzipArchiveException(m_cache->errorString() + u' '
+ fi.reportException(CacheTaskException(m_cache->errorString() + u' '
+ MetadataJob::tr("Clearing the cache directory and restarting the application may solve this.")));
}
@@ -170,7 +211,7 @@ public:
}
private:
- GenericDataCache<Metadata> *const m_cache;
+ MetadataCache *const m_cache;
QHash<QString, Metadata *> *const m_updates;
};
diff --git a/src/libs/installer/observer.cpp b/src/libs/installer/observer.cpp
index 30afce719..57b67d8e1 100644
--- a/src/libs/installer/observer.cpp
+++ b/src/libs/installer/observer.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -121,9 +121,9 @@ QByteArray FileTaskObserver::checkSum() const
return m_hash.result();
}
-void FileTaskObserver::addCheckSumData(const char *data, int length)
+void FileTaskObserver::addCheckSumData(const QByteArray &data)
{
- m_hash.addData(data, length);
+ m_hash.addData(data);
}
void FileTaskObserver::addSample(qint64 sample)
diff --git a/src/libs/installer/observer.h b/src/libs/installer/observer.h
index 198a0f89c..d638d8ee4 100644
--- a/src/libs/installer/observer.h
+++ b/src/libs/installer/observer.h
@@ -60,7 +60,7 @@ public:
QString progressText() const override;
QByteArray checkSum() const;
- void addCheckSumData(const char *data, int length);
+ void addCheckSumData(const QByteArray &data);
void addSample(qint64 sample);
void timerEvent(QTimerEvent *event) override;
diff --git a/src/libs/installer/packagemanagercore.cpp b/src/libs/installer/packagemanagercore.cpp
index 45f8864a7..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>
@@ -56,9 +58,15 @@
#include <QtCore/QMutex>
#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>
@@ -175,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()
*/
/*!
@@ -213,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.
@@ -531,7 +520,6 @@ void PackageManagerCore::reset()
d->m_status = PackageManagerCore::Unfinished;
d->m_installerBaseBinaryUnreplaced.clear();
d->m_coreCheckedHash.clear();
- d->m_componentsToInstallCalculated = false;
}
/*!
@@ -601,6 +589,14 @@ bool PackageManagerCore::clearLocalCache(QString *error)
}
/*!
+ Returns \c true if the metadata cache is initialized and valid, \c false otherwise.
+*/
+bool PackageManagerCore::isValidCache() const
+{
+ return d->m_metadataJob.isValidCache();
+}
+
+/*!
\internal
*/
template <typename T>
@@ -613,92 +609,65 @@ template bool PackageManagerCore::loadComponentScripts<QList<Component *>>(const
template bool PackageManagerCore::loadComponentScripts<QHash<QString, Component *>>(const QHash<QString, Component *> &, const bool);
/*!
+ 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()
{
- d->clearInstallerCalculator();
-
- QList<Component*> selectedComponentsToInstall = componentsMarkedForInstallation();
-
- d->m_componentsToInstallCalculated =
- d->installerCalculator()->appendComponentsToInstall(selectedComponentsToInstall, true);
-
- d->calculateUninstallComponents();
- d->updateComponentCheckedState();
-
- // update all nodes uncompressed size
- foreach (Component *const component, components(ComponentType::Root))
- component->updateUncompressedSize(); // this is a recursive call
+ recalculateAllComponents();
}
/*!
- 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);
- }
+ \fn QInstaller::PackageManagerCore::clearComponentsToInstallCalculated()
- d->updateComponentCheckedState();
-}
+ \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}
+ */
/*!
- Forces a recalculation of components to install.
- \sa {installer::clearComponentsToInstallCalculated}{installer.clearComponentsToInstallCalculated}
+ Recalculates all components to install and uninstall. Returns \c true
+ on success, \c false otherwise. Detailed error messages can be retrieved
+ with {installer::componentsToInstallError} and {installer::componentsToUninstallError}.
*/
-void PackageManagerCore::clearComponentsToInstallCalculated()
+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;
}
/*!
@@ -842,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;
}
@@ -860,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();
}
@@ -880,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);
@@ -1073,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()));
}
/*!
@@ -1315,6 +1287,10 @@ 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,
@@ -1559,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)) {
@@ -1585,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)
@@ -1630,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();
@@ -1695,7 +1672,7 @@ bool PackageManagerCore::fetchCompressedPackagesTree()
if (!d->fetchMetaInformationFromRepositories(DownloadType::CompressedPackage))
return false;
- if (!d->addUpdateResourcesFromRepositories(true, true)) {
+ if (!d->addUpdateResourcesFromRepositories(true)) {
return false;
}
@@ -1706,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);
@@ -1734,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);
}
@@ -1988,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
{
- d->m_testChecksum = test;
+ foreach (const Repository &repo, settings().repositories()) {
+ if (repo.isEnabled() && repo.isValid()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void PackageManagerCore::setAllowCompressedRepositoryInstall(bool allow)
+{
+ 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.
@@ -2163,6 +2235,15 @@ Component *PackageManagerCore::componentByName(const QString &name) const
}
/*!
+ 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);
+}
+
+/*!
Searches \a components for a component matching \a name and returns it.
\a name can also contain a version requirement. For example, \c org.qt-project.sdk.qt
returns any component with that name, whereas \c{org.qt-project.sdk.qt->=4.5} requires
@@ -2188,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.
*/
@@ -2225,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.
@@ -2237,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;
}
/*!
@@ -2254,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();
@@ -2309,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;
}
/*!
@@ -2342,7 +2476,7 @@ bool PackageManagerCore::calculateComponentsToUninstall() const
*/
QList<Component *> PackageManagerCore::componentsToUninstall() const
{
- return d->uninstallerCalculator()->componentsToUninstall().values();
+ return d->uninstallerCalculator()->resolvedComponents();
}
/*!
@@ -2350,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();
}
/*!
@@ -2364,7 +2506,7 @@ QString PackageManagerCore::componentsToInstallError() const
*/
QString PackageManagerCore::installReason(Component *component) const
{
- return d->installerCalculator()->installReason(component);
+ return d->installerCalculator()->resolutionText(component);
}
/*!
@@ -2380,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);
}
/*!
@@ -2483,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;
}
@@ -2501,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)
@@ -2560,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)
@@ -2577,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()
@@ -2601,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 "
@@ -2689,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.
@@ -2696,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();
@@ -2808,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.
@@ -2832,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;
@@ -2873,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())) {
@@ -2896,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);
}
/*!
@@ -2924,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
@@ -2939,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);
}
/*!
@@ -3022,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)
@@ -3030,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());
@@ -3091,21 +3367,21 @@ bool PackageManagerCore::checkAvailableSpace(QString &message) const
}
if (cacheOnSameVolume && (installVolumeAvailableSize <= (required + tempRequired))) {
- message = tr("Not enough disk space to store temporary files and the "
+ 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 (cacheVolumeAvailableSize < tempRequired) {
- message = tr("Not enough disk space to store temporary files! %1 are available, "
+ 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 local cache path from the installer settings.")
.arg(humanReadableSize(cacheVolumeAvailableSize), humanReadableSize(tempRequired));
@@ -3114,22 +3390,25 @@ bool PackageManagerCore::checkAvailableSpace(QString &message) const
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;
@@ -3188,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.
@@ -3199,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.
@@ -3505,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.
@@ -3703,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()
*/
@@ -3761,6 +4067,7 @@ bool PackageManagerCore::isUninstaller() const
void PackageManagerCore::setUpdater()
{
d->m_magicBinaryMarker = BinaryContent::MagicUpdaterMarker;
+ d->m_componentByNameHash.clear();
emit installerBinaryMarkerChanged(d->m_magicBinaryMarker);
}
@@ -3781,6 +4088,7 @@ bool PackageManagerCore::isUpdater() const
void PackageManagerCore::setPackageManager()
{
d->m_magicBinaryMarker = BinaryContent::MagicPackageManagerMarker;
+ d->m_componentByNameHash.clear();
emit installerBinaryMarkerChanged(d->m_magicBinaryMarker);
}
@@ -3802,6 +4110,7 @@ bool PackageManagerCore::isPackageManager() const
void PackageManagerCore::setOfflineGenerator()
{
d->m_magicMarkerSupplement = BinaryContent::OfflineGenerator;
+ emit installerBinaryMarkerChanged(d->m_magicBinaryMarker);
}
/*!
@@ -3820,6 +4129,7 @@ bool PackageManagerCore::isOfflineGenerator() const
void PackageManagerCore::setPackageViewer()
{
d->m_magicMarkerSupplement = BinaryContent::PackageViewer;
+ emit installerBinaryMarkerChanged(d->m_magicBinaryMarker);
}
/*!
@@ -3833,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.
*/
@@ -4007,13 +4328,6 @@ 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.repositoryForCacheDirectory(localPath);
if (repo.isValid()) {
@@ -4022,18 +4336,10 @@ bool PackageManagerCore::updateComponentData(struct Data &data, Component *compo
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.
@@ -4153,10 +4459,10 @@ bool PackageManagerCore::fetchAllPackages(const PackagesList &remotes, const Loc
continue;
}
- QScopedPointer<QInstaller::Component> remoteComponent(new QInstaller::Component(this));
+ std::unique_ptr<QInstaller::Component> remoteComponent(new QInstaller::Component(this));
data.package = package;
remoteComponent->loadDataFromPackage(*package);
- if (updateComponentData(data, remoteComponent.data())) {
+ 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.
@@ -4164,7 +4470,7 @@ bool PackageManagerCore::fetchAllPackages(const PackagesList &remotes, const Loc
if (!treeName.isEmpty())
remoteTreeNameComponents.insert(remoteComponent->name(), treeName);
const QString name = remoteComponent->treeName();
- allComponents.insert(name, remoteComponent.take());
+ allComponents.insert(name, remoteComponent.release());
}
}
// Second pass with leftover packages
@@ -4187,7 +4493,7 @@ bool PackageManagerCore::fetchAllPackages(const PackagesList &remotes, const Loc
d->m_localVirtualComponents.append(package.name);
}
- QScopedPointer<QInstaller::Component> localComponent(new QInstaller::Component(this));
+ std::unique_ptr<QInstaller::Component> localComponent(new QInstaller::Component(this));
localComponent->loadDataFromPackage(package);
const QString name = localComponent->treeName();
@@ -4242,7 +4548,7 @@ bool PackageManagerCore::fetchAllPackages(const PackagesList &remotes, const Loc
const QString treeName = localComponent->value(scTreeName);
if (!treeName.isEmpty())
allTreeNameComponents.insert(localComponent->name(), treeName);
- allComponents.insert(name, localComponent.take());
+ allComponents.insert(name, localComponent.release());
}
// store all components that got a replacement
@@ -4256,6 +4562,9 @@ bool PackageManagerCore::fetchAllPackages(const PackagesList &remotes, const Loc
d->commitPendingUnstableComponents();
+ if (!d->buildComponentAliases())
+ return false;
+
} catch (const Error &error) {
d->clearAllComponentLists();
d->setStatus(PackageManagerCore::Failure, error.message());
@@ -4293,12 +4602,12 @@ bool PackageManagerCore::fetchUpdaterPackages(const PackagesList &remotes, const
if (!ProductKeyCheck::instance()->isValidPackage(update->data(scName).toString()))
continue;
- QScopedPointer<QInstaller::Component> component(new QInstaller::Component(this));
+ std::unique_ptr<QInstaller::Component> component(new QInstaller::Component(this));
data.package = update;
component->loadDataFromPackage(*update);
- if (updateComponentData(data, component.data())) {
+ if (updateComponentData(data, component.get())) {
// Keep a reference so we can resolve dependencies during update.
- d->m_updaterComponentsDeps.append(component.take());
+ d->m_updaterComponentsDeps.append(component.release());
// const QString isNew = update->data(scNewComponent).toString();
// if (isNew.toLower() != scTrue)
@@ -4370,7 +4679,8 @@ bool PackageManagerCore::fetchUpdaterPackages(const PackagesList &remotes, const
}
// store all components that got a replacement, but do not modify the components list
- storeReplacedComponents(localReplaceMes.unite(components), data);
+ localReplaceMes.insert(components);
+ storeReplacedComponents(localReplaceMes, data);
if (!components.isEmpty()) {
// append all components w/o parent to the direct list
@@ -4386,7 +4696,7 @@ bool PackageManagerCore::fetchUpdaterPackages(const PackagesList &remotes, const
if (d->statusCanceledOrFailed())
return false;
- if (!component->isUnstable() && component->autoDependencies().isEmpty())
+ if (!component->isUnstable())
component->setCheckState(Qt::Checked);
}
@@ -4398,7 +4708,7 @@ bool PackageManagerCore::fetchUpdaterPackages(const PackagesList &remotes, const
foreach (QInstaller::Component *component, d->m_updaterComponentsDeps) {
if (d->statusCanceledOrFailed())
return false;
- if (component->isInstalled() && !component->autoDependencies().isEmpty()) {
+ if (component->isInstalled()) {
// since we do not put them into the model, which would force a update of e.g. tri state
// components, we have to check all installed components ourselves
if (!component->isUnstable())
@@ -4459,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;
@@ -4474,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;
@@ -4501,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()
@@ -4577,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;
}
diff --git a/src/libs/installer/packagemanagercore.h b/src/libs/installer/packagemanagercore.h
index cd44c2ef1..170ddf557 100644
--- a/src/libs/installer/packagemanagercore.h
+++ b/src/libs/installer/packagemanagercore.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -29,6 +29,8 @@
#define PACKAGEMANAGERCORE_H
#include "binaryformat.h"
+#include "binarycontent.h"
+#include "component.h"
#include "protocol.h"
#include "repository.h"
#include "qinstallerglobal.h"
@@ -38,17 +40,20 @@
#include <QtCore/QHash>
#include <QtCore/QObject>
#include <QtCore/QStringList>
-#include <QtCore/QVector>
+#include <QtCore/QList>
#include <QSettings>
+#include <QModelIndex>
namespace QInstaller {
-class Component;
+struct AliasSource;
class ComponentModel;
+class ComponentAlias;
class ScriptEngine;
class PackageManagerCorePrivate;
class PackageManagerProxyFactory;
class Settings;
+class ComponentSortFilterProxyModel;
// -- PackageManagerCore
@@ -79,7 +84,8 @@ public:
Canceled = 3,
Unfinished = 4,
ForceUpdate = 5,
- EssentialUpdated = 6
+ EssentialUpdated = 6,
+ NoPackagesFound = 7
};
Status status() const;
QString error() const;
@@ -104,6 +110,14 @@ public:
AllNoReplacements = (Root | Descendants | Dependencies),
All = (Root | Descendants | Dependencies | Replacements)
};
+
+ struct DownloadItem
+ {
+ QString fileName;
+ QString sourceUrl;
+ bool checkSha1CheckSum;
+ };
+
Q_DECLARE_FLAGS(ComponentTypes, ComponentType)
static QFont virtualComponentsFont();
@@ -136,8 +150,9 @@ public:
void setProxyFactory(PackageManagerProxyFactory *factory);
PackagesList remotePackages();
- bool fetchRemotePackagesTree();
+ bool fetchRemotePackagesTree(const QStringList& components = QStringList());
bool fetchCompressedPackagesTree();
+ bool fetchPackagesWithFallbackRepositories(const QStringList& components, bool &fallBackReposFetched);
bool run();
void reset();
@@ -166,6 +181,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);
@@ -192,12 +208,20 @@ public:
void setOfflineBinaryName(const QString &name);
QString offlineBinaryName() const;
- bool testChecksum() const;
- void setTestChecksum(bool test);
+ void addAliasSource(const AliasSource &source);
Q_INVOKABLE void addUserRepositories(const QStringList &repositories);
Q_INVOKABLE void setTemporaryRepositories(const QStringList &repositories,
bool replace = false, bool compressed = false);
+ bool addQBspRepositories(const QStringList &repositories);
+ bool validRepositoriesAvailable() const;
+ Q_INVOKABLE void setAllowCompressedRepositoryInstall(bool allow);
+ bool allowCompressedRepositoryInstall() const;
+ bool showRepositoryCategories() const;
+ QVariantMap organizedRepositoryCategories() const;
+ void enableRepositoryCategory(const QString &repositoryName, bool enable);
+ void runProgram();
+
Q_INVOKABLE void autoAcceptMessageBoxes();
Q_INVOKABLE void autoRejectMessageBoxes();
Q_INVOKABLE void setMessageBoxAutomaticAnswer(const QString &identifier, int button);
@@ -234,16 +258,25 @@ public:
void appendUpdaterComponent(Component *components);
QList<Component *> components(ComponentTypes mask, const QString &regexp = QString()) const;
- Component *componentByName(const QString &identifier) const;
+ Q_INVOKABLE QInstaller::Component *componentByName(const QString &identifier) const;
+ Q_INVOKABLE QList<QInstaller::Component *> components(const QString &regexp = QString()) const;
+
+ ComponentAlias *aliasByName(const QString &name) const;
Q_INVOKABLE bool calculateComponentsToInstall() const;
QList<Component*> orderedComponentsToInstall() const;
- bool calculateComponents(QString *displayString);
+
+ Q_INVOKABLE bool recalculateAllComponents();
+ QString componentResolveReasons() const;
Q_INVOKABLE bool calculateComponentsToUninstall() const;
QList<Component*> componentsToUninstall() const;
+ QList<Component *> componentsMarkedForInstallation() const;
+ QList<ComponentAlias *> aliasesMarkedForInstallation() const;
+
QString componentsToInstallError() const;
+ QString componentsToUninstallError() const;
QString installReason(Component *component) const;
QString uninstallReason(Component *component) const;
@@ -253,9 +286,14 @@ public:
ComponentModel *defaultComponentModel() const;
ComponentModel *updaterComponentModel() const;
+ ComponentSortFilterProxyModel *componentSortFilterProxyModel();
+
void listInstalledPackages(const QString &regexp = QString());
- void listAvailablePackages(const QString &regexp = QString(),
+ bool listAvailablePackages(const QString &regexp = QString(),
const QHash<QString, QString> &filters = QHash<QString, QString>());
+ bool listAvailableAliases(const QString &regexp = QString());
+
+ PackageManagerCore::Status searchAvailableUpdates();
PackageManagerCore::Status updateComponentsSilently(const QStringList &componentsToUpdate);
PackageManagerCore::Status installSelectedComponentsSilently(const QStringList& components);
PackageManagerCore::Status installDefaultComponentsSilently();
@@ -283,6 +321,8 @@ public:
void setPackageViewer();
Q_INVOKABLE bool isPackageViewer() const;
+ void resetBinaryMarkerSupplement();
+
void setUserSetBinaryMarker(qint64 magicMarker);
Q_INVOKABLE bool isUserSetBinaryMarker() const;
@@ -297,9 +337,11 @@ public:
Q_INVOKABLE bool gainAdminRights();
Q_INVOKABLE void dropAdminRights();
+ Q_INVOKABLE bool hasAdminRights() const;
void setCheckAvailableSpace(bool check);
- bool checkAvailableSpace(QString &message) const;
+ bool checkAvailableSpace();
+ QString availableSpaceMessage() const;
Q_INVOKABLE quint64 requiredDiskSpace() const;
Q_INVOKABLE quint64 requiredTemporaryDiskSpace() const;
@@ -343,14 +385,20 @@ public:
void clearLicenses();
QHash<QString, QMap<QString, QString>> sortedLicenses();
void addLicenseItem(const QHash<QString, QVariantMap> &licenses);
+ bool hasLicenses() const;
void createLocalDependencyHash(const QString &component, const QString &dependencies) const;
void createAutoDependencyHash(const QString &component, const QString &oldDependencies, const QString &newDependencies) const;
bool resetLocalCache(bool init = false);
bool clearLocalCache(QString *error = nullptr);
+ bool isValidCache() const;
+
template <typename T>
bool loadComponentScripts(const T &components, const bool postScript = false);
+ void saveGivenArguments(const QStringList &args);
+ QStringList givenArguments() const;
+
public Q_SLOTS:
bool runInstaller();
bool runUninstaller();
@@ -361,9 +409,8 @@ public Q_SLOTS:
void languageChanged();
void setCompleteUninstallation(bool complete);
void cancelMetaInfoJob();
- void componentsToInstallNeedsRecalculation();
- void calculateUserSelectedComponentsToInstall(const QList<QModelIndex> &indexes);
- void clearComponentsToInstallCalculated();
+ void componentsToInstallNeedsRecalculation(); // TODO: deprecated, remove
+ void clearComponentsToInstallCalculated() {} // TODO: deprecated, remove
Q_SIGNALS:
void aboutCalculateComponentsToInstall() const;
@@ -371,11 +418,9 @@ Q_SIGNALS:
void aboutCalculateComponentsToUninstall() const;
void finishedCalculateComponentsToUninstall() const;
void componentAdded(QInstaller::Component *comp);
- void rootComponentsAdded(QList<QInstaller::Component*> components);
- void updaterComponentsAdded(QList<QInstaller::Component*> components);
void valueChanged(const QString &key, const QString &value);
void statusChanged(QInstaller::PackageManagerCore::Status);
- void defaultTranslationsLoadedForLanguage(QLocale::Language lang);
+ void defaultTranslationsLoadedForLanguage(QLocale lang);
void currentPageChanged(int page);
void finishButtonClicked();
@@ -438,17 +483,18 @@ private:
QString findDisplayVersion(const QString &componentName, const QHash<QString, QInstaller::Component*> &components,
const QString& versionKey, QHash<QString, bool> &visited);
ComponentModel *componentModel(PackageManagerCore *core, const QString &objectName) const;
- QList<Component *> componentsMarkedForInstallation() const;
bool fetchPackagesTree(const PackagesList &packages, const LocalPackagesMap installedPackages);
bool componentUninstallableFromCommandLine(const QString &componentName);
- bool checkComponentsForInstallation(const QStringList &components, QString &errorMessage);
+ bool checkComponentsForInstallation(const QStringList &names, QString &errorMessage, bool &unstableAliasFound);
private:
PackageManagerCorePrivate *const d;
friend class PackageManagerCorePrivate;
QHash<QString, QString> m_fileDialogAutomaticAnswers;
QHash<QString, QStringList> m_localVirtualWithDependants;
+ QString m_availableSpaceMessage;
+ QStringList m_arguments;
private:
// remove once we deprecate isSelected, setSelected etc...
diff --git a/src/libs/installer/packagemanagercore_p.cpp b/src/libs/installer/packagemanagercore_p.cpp
index 3a30244f2..10ca27d00 100644
--- a/src/libs/installer/packagemanagercore_p.cpp
+++ b/src/libs/installer/packagemanagercore_p.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -45,6 +45,7 @@
#include "qsettingswrapper.h"
#include "installercalculator.h"
#include "uninstallercalculator.h"
+#include "componentalias.h"
#include "componentchecker.h"
#include "globals.h"
#include "binarycreator.h"
@@ -56,11 +57,13 @@
#include "selfrestarter.h"
#include "filedownloaderfactory.h"
#include "updateoperationfactory.h"
+#include "constants.h"
#include <productkeycheck.h>
#include <QSettings>
#include <QtConcurrentRun>
+#include <QtConcurrent>
#include <QtCore/QCoreApplication>
#include <QtCore/QDir>
#include <QtCore/QDirIterator>
@@ -151,26 +154,56 @@ static void deferredRename(const QString &oldName, const QString &newName, bool
#endif
}
+static bool filterMissingAliasesToInstall(const QString& component, const QList<ComponentAlias *> packages)
+{
+ bool packageFound = false;
+ for (qsizetype i = 0; i < packages.size(); ++i) {
+ packageFound = (packages.at(i)->name() == component);
+ if (packageFound)
+ break;
+ }
+ return !packageFound;
+}
+
+static bool filterMissingPackagesToInstall(const QString& component, const PackagesList& packages)
+{
+ bool packageFound = false;
+ for (qsizetype i = 0; i < packages.size(); ++i) {
+ packageFound = (packages.at(i)->data(scName).toString() == component);
+ if (packageFound)
+ break;
+ }
+ return !packageFound;
+}
// -- PackageManagerCorePrivate
PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core)
: m_updateFinder(nullptr)
+ , m_aliasFinder(nullptr)
, m_localPackageHub(std::make_shared<LocalPackageHub>())
, m_status(PackageManagerCore::Unfinished)
, m_needsHardRestart(false)
, m_testChecksum(false)
, m_launchedAsRoot(AdminAuthorization::hasAdminRights())
+ , m_commandLineInstance(false)
+ , m_defaultInstall(false)
+ , m_userSetBinaryMarker(false)
+ , m_checkAvailableSpace(true)
, m_completeUninstall(false)
, m_needToWriteMaintenanceTool(false)
, m_dependsOnLocalInstallerBinary(false)
+ , m_autoAcceptLicenses(false)
+ , m_disableWriteMaintenanceTool(false)
+ , m_autoConfirmCommand(false)
, m_core(core)
, m_updates(false)
+ , m_aliases(false)
, m_repoFetched(false)
, m_updateSourcesAdded(false)
, m_magicBinaryMarker(0) // initialize with pseudo marker
, m_magicMarkerSupplement(BinaryContent::Default)
- , m_componentsToInstallCalculated(false)
+ , m_foundEssentialUpdate(false)
, m_componentScriptEngine(nullptr)
, m_controlScriptEngine(nullptr)
, m_installerCalculator(nullptr)
@@ -178,38 +211,46 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core)
, m_proxyFactory(nullptr)
, m_defaultModel(nullptr)
, m_updaterModel(nullptr)
+ , m_componentSortFilterProxyModel(nullptr)
, m_guiObject(nullptr)
, m_remoteFileEngineHandler(nullptr)
- , m_foundEssentialUpdate(false)
- , m_commandLineInstance(false)
- , m_defaultInstall(false)
- , m_userSetBinaryMarker(false)
- , m_checkAvailableSpace(true)
- , m_autoAcceptLicenses(false)
- , m_disableWriteMaintenanceTool(false)
- , m_autoConfirmCommand(false)
, m_datFileName(QString())
+#ifdef INSTALLCOMPRESSED
+ , m_allowCompressedRepositoryInstall(true)
+#else
+ , m_allowCompressedRepositoryInstall(false)
+#endif
+ , m_connectedOperations(0)
{
}
PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, qint64 magicInstallerMaker,
const QList<OperationBlob> &performedOperations, const QString &datFileName)
: m_updateFinder(nullptr)
+ , m_aliasFinder(nullptr)
, m_localPackageHub(std::make_shared<LocalPackageHub>())
, m_status(PackageManagerCore::Unfinished)
, m_needsHardRestart(false)
, m_testChecksum(false)
, m_launchedAsRoot(AdminAuthorization::hasAdminRights())
+ , m_commandLineInstance(false)
+ , m_defaultInstall(false)
+ , m_userSetBinaryMarker(false)
+ , m_checkAvailableSpace(true)
, m_completeUninstall(false)
, m_needToWriteMaintenanceTool(false)
, m_dependsOnLocalInstallerBinary(false)
+ , m_autoAcceptLicenses(false)
+ , m_disableWriteMaintenanceTool(false)
+ , m_autoConfirmCommand(false)
, m_core(core)
, m_updates(false)
+ , m_aliases(false)
, m_repoFetched(false)
, m_updateSourcesAdded(false)
, m_magicBinaryMarker(magicInstallerMaker)
, m_magicMarkerSupplement(BinaryContent::Default)
- , m_componentsToInstallCalculated(false)
+ , m_foundEssentialUpdate(false)
, m_componentScriptEngine(nullptr)
, m_controlScriptEngine(nullptr)
, m_installerCalculator(nullptr)
@@ -217,22 +258,21 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, q
, m_proxyFactory(nullptr)
, m_defaultModel(nullptr)
, m_updaterModel(nullptr)
+ , m_componentSortFilterProxyModel(nullptr)
, m_guiObject(nullptr)
, m_remoteFileEngineHandler(new RemoteFileEngineHandler)
- , m_foundEssentialUpdate(false)
- , m_commandLineInstance(false)
- , m_defaultInstall(false)
- , m_userSetBinaryMarker(false)
- , m_checkAvailableSpace(true)
- , m_autoAcceptLicenses(false)
- , m_disableWriteMaintenanceTool(false)
- , m_autoConfirmCommand(false)
, m_datFileName(datFileName)
+#ifdef INSTALLCOMPRESSED
+ , m_allowCompressedRepositoryInstall(true)
+#else
+ , m_allowCompressedRepositoryInstall(false)
+#endif
+ , m_connectedOperations(0)
{
foreach (const OperationBlob &operation, performedOperations) {
- QScopedPointer<QInstaller::Operation> op(KDUpdater::UpdateOperationFactory::instance()
+ std::unique_ptr<QInstaller::Operation> op(KDUpdater::UpdateOperationFactory::instance()
.create(operation.name, core));
- if (op.isNull()) {
+ if (!op) {
qCWarning(QInstaller::lcInstallerInstallLog) << "Failed to load unknown operation"
<< operation.name;
continue;
@@ -243,7 +283,7 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, q
<< operation.name;
continue;
}
- m_performedOperationsOld.append(op.take());
+ m_performedOperationsOld.append(op.release());
}
connect(this, &PackageManagerCorePrivate::installationStarted,
@@ -272,6 +312,7 @@ PackageManagerCorePrivate::~PackageManagerCorePrivate()
qDeleteAll(m_performedOperationsCurrentSession);
delete m_updateFinder;
+ delete m_aliasFinder;
delete m_proxyFactory;
delete m_defaultModel;
@@ -403,10 +444,10 @@ bool PackageManagerCorePrivate::buildComponentTree(QHash<QString, Component*> &c
component->setCheckState(Qt::Checked);
clearInstallerCalculator();
- if (installerCalculator()->appendComponentsToInstall(components.values()) == false) {
- setStatus(PackageManagerCore::Failure, installerCalculator()->componentsToInstallError());
+ if (installerCalculator()->solve(components.values()) == false) {
+ setStatus(PackageManagerCore::Failure, installerCalculator()->error());
MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("Error"),
- tr("Unresolved dependencies"), installerCalculator()->componentsToInstallError());
+ tr("Unresolved dependencies"), installerCalculator()->error());
return false;
}
@@ -432,6 +473,90 @@ bool PackageManagerCorePrivate::buildComponentTree(QHash<QString, Component*> &c
return true;
}
+bool PackageManagerCorePrivate::buildComponentAliases()
+{
+ // For now, aliases are only used for command line runs
+ if (!m_core->isCommandLineInstance())
+ return true;
+
+ {
+ const QList<ComponentAlias *> aliasList = componentAliases();
+ if (aliasList.isEmpty())
+ return true;
+
+ for (const auto *alias : aliasList) {
+ // Create a new alias object for package manager core to take ownership of
+ ComponentAlias *newAlias = new ComponentAlias(m_core);
+ for (const QString &key : alias->keys())
+ newAlias->setValue(key, alias->value(key));
+
+ m_componentAliases.insert(alias->name(), newAlias);
+ }
+ }
+ // After aliases are loaded, perform sanity checks:
+
+ // 1. Component check state is changed by alias selection, so store the initial state
+ storeCheckState();
+
+ Graph<QString> aliasGraph;
+ for (auto *alias : qAsConst(m_componentAliases)) {
+ aliasGraph.addNode(alias->name());
+ aliasGraph.addEdges(alias->name(),
+ QInstaller::splitStringWithComma(alias->value(scRequiredAliases)) <<
+ QInstaller::splitStringWithComma(alias->value(scOptionalAliases)));
+
+ if (!m_core->componentByName(alias->name())) {
+ // Name ok, select for sanity check calculation
+ alias->setSelected(true);
+ } else {
+ alias->setUnstable(ComponentAlias::ComponentNameConfict,
+ tr("Alias declares name that conflicts with an existing component \"%1\"")
+ .arg(alias->name()));
+ }
+ }
+
+ const QList<QString> sortedAliases = aliasGraph.sort();
+ // 2. Check for cyclic dependency errors
+ if (aliasGraph.hasCycle()) {
+ setStatus(PackageManagerCore::Failure, installerCalculator()->error());
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("Error"),
+ tr("Unresolved component aliases"),
+ tr("Cyclic dependency between aliases \"%1\" and \"%2\" detected.")
+ .arg(aliasGraph.cycle().first, aliasGraph.cycle().second));
+
+ return false;
+ }
+
+ // 3. Test for required aliases and components, this triggers setting the
+ // alias unstable in case of a broken reference.
+ for (const auto &aliasName : sortedAliases) {
+ ComponentAlias *alias = m_componentAliases.value(aliasName);
+ if (!alias) // sortedAliases may contain dependencies that don't exist, we don't know it yet
+ continue;
+
+ alias->components();
+ alias->aliases();
+ }
+
+ clearInstallerCalculator();
+ // 4. Check for other errors preventing resolving components to install
+ if (!installerCalculator()->solve(m_componentAliases.values())) {
+ setStatus(PackageManagerCore::Failure, installerCalculator()->error());
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("Error"),
+ tr("Unresolved component aliases"), installerCalculator()->error());
+
+ return false;
+ }
+
+ for (auto *alias : qAsConst(m_componentAliases))
+ alias->setSelected(false);
+
+ // 5. Restore original state
+ restoreCheckState();
+
+ return true;
+}
+
template <typename T>
bool PackageManagerCorePrivate::loadComponentScripts(const T &components, const bool postScript)
{
@@ -470,6 +595,10 @@ void PackageManagerCorePrivate::cleanUpComponentEnvironment()
// so we need to remove the current component script engine
delete m_componentScriptEngine;
m_componentScriptEngine = nullptr;
+
+ // Calculators become invalid after clearing components
+ clearInstallerCalculator();
+ clearUninstallerCalculator();
}
ScriptEngine *PackageManagerCorePrivate::componentScriptEngine() const
@@ -488,6 +617,9 @@ ScriptEngine *PackageManagerCorePrivate::controlScriptEngine() const
void PackageManagerCorePrivate::clearAllComponentLists()
{
+ qDeleteAll(m_componentAliases);
+ m_componentAliases.clear();
+
QList<QInstaller::Component*> toDelete;
toDelete << m_rootComponents << m_deletedReplacedComponents;
@@ -496,7 +628,7 @@ void PackageManagerCorePrivate::clearAllComponentLists()
m_deletedReplacedComponents.clear();
m_componentsToReplaceAllMode.clear();
- m_componentsToInstallCalculated = false;
+ m_foundEssentialUpdate = false;
qDeleteAll(toDelete);
cleanUpComponentEnvironment();
@@ -504,8 +636,10 @@ void PackageManagerCorePrivate::clearAllComponentLists()
void PackageManagerCorePrivate::clearUpdaterComponentLists()
{
- QSet<Component*> usedComponents =
- QSet<Component*>::fromList(m_updaterComponents + m_updaterComponentsDeps);
+
+ QSet<Component*> usedComponents(m_updaterComponents.begin(), m_updaterComponents.end());
+ usedComponents.unite(QSet<Component*>(m_updaterComponentsDeps.begin(),
+ m_updaterComponentsDeps.end()));
const QList<QPair<Component*, Component*> > list = m_componentsToReplaceUpdaterMode.values();
for (int i = 0; i < list.count(); ++i) {
@@ -521,7 +655,7 @@ void PackageManagerCorePrivate::clearUpdaterComponentLists()
m_updaterDependencyReplacements.clear();
m_componentsToReplaceUpdaterMode.clear();
- m_componentsToInstallCalculated = false;
+ m_foundEssentialUpdate = false;
qDeleteAll(usedComponents);
cleanUpComponentEnvironment();
@@ -542,21 +676,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;
@@ -591,7 +710,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;
@@ -601,7 +720,6 @@ void PackageManagerCorePrivate::initialize(const QHash<QString, QString> &params
{
m_coreCheckedHash.clear();
m_data = PackageManagerCoreData(params, isInstaller());
- m_componentsToInstallCalculated = false;
#ifdef Q_OS_LINUX
if (m_launchedAsRoot && isInstaller())
@@ -661,6 +779,10 @@ void PackageManagerCorePrivate::initialize(const QHash<QString, QString> &params
if (isInstaller())
m_packageSources.insert(PackageSource(QUrl(QLatin1String("resource://metadata/")), 1));
+ const QString aliasFilePath = m_core->settings().aliasDefinitionsFile();
+ if (!aliasFilePath.isEmpty())
+ m_aliasSources.insert(AliasSource(AliasSource::SourceFileFormat::Xml, aliasFilePath, -1));
+
m_metadataJob.disconnect();
m_metadataJob.setAutoDelete(false);
m_metadataJob.setPackageManagerCore(m_core);
@@ -789,7 +911,7 @@ QString PackageManagerCorePrivate::maintenanceToolAliasPath() const
if (aliasName.isEmpty())
return QString();
- const bool isRoot = (AdminAuthorization::hasAdminRights() || RemoteClient::instance().isActive());
+ const bool isRoot = m_core->hasAdminRights();
const QString applicationsDir = m_core->value(
isRoot ? QLatin1String("ApplicationsDir") : QLatin1String("ApplicationsDirUser")
);
@@ -880,7 +1002,7 @@ void PackageManagerCorePrivate::writeMaintenanceConfigFiles()
if (key == scRunProgramDescription || key == scRunProgram || key == scRunProgramArguments)
continue;
QVariant value = m_data.value(key);
- if (value.canConvert(QVariant::String))
+ if (value.canConvert<QString>())
value = replacePath(value.toString(), targetDir(), QLatin1String(scRelocatable));
variables.insert(key, value);
}
@@ -904,8 +1026,8 @@ void PackageManagerCorePrivate::writeMaintenanceConfigFiles()
QFile file(targetDir() + QLatin1Char('/') + QLatin1String("network.xml"));
if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
- QXmlStreamWriter writer(&file);
- writer.setCodec("UTF-8");
+ QString outputStr;
+ QXmlStreamWriter writer(&outputStr);
writer.setAutoFormatting(true);
writer.writeStartDocument();
@@ -938,6 +1060,8 @@ void PackageManagerCorePrivate::writeMaintenanceConfigFiles()
writer.writeEndElement();
writer.writeTextElement(QLatin1String("LocalCachePath"), m_data.settings().localCachePath());
writer.writeEndElement();
+
+ file.write(outputStr.toUtf8());
}
setDefaultFilePermissions(&file, DefaultFilePermissions::NonExecutable);
}
@@ -977,7 +1101,7 @@ void PackageManagerCorePrivate::readMaintenanceConfigFiles(const QString &target
case QXmlStreamReader::StartElement: {
if (reader.name() == QLatin1String("Network")) {
while (reader.readNextStartElement()) {
- const QStringRef name = reader.name();
+ const QStringView name = reader.name();
if (name == QLatin1String("Ftp")) {
m_data.settings().setFtpProxy(readProxy(reader));
} else if (name == QLatin1String("Http")) {
@@ -1086,8 +1210,11 @@ void PackageManagerCorePrivate::connectOperationToInstaller(Operation *const ope
connect(m_core, SIGNAL(installationInterrupted()), operationObject, SLOT(cancelOperation()));
if (mo->indexOfSignal(QMetaObject::normalizedSignature("progressChanged(double)")) > -1) {
+ // create unique object names for progress information track
+ operationObject->setObjectName(QLatin1String("operation_%1").arg(QString::number(m_connectedOperations)));
ProgressCoordinator::instance()->registerPartProgress(operationObject,
SIGNAL(progressChanged(double)), operationPartSize);
+ m_connectedOperations++;
}
}
}
@@ -1312,7 +1439,7 @@ void PackageManagerCorePrivate::writeMaintenanceToolAppBundle(OperationList &per
const QString after = QLatin1String("<string>") + QFileInfo(maintenanceToolName()).baseName()
+ QLatin1String("</string>");
while (!in.atEnd())
- out << in.readLine().replace(before, after) << endl;
+ out << in.readLine().replace(before, after) << Qt::endl;
// copy qt_menu.nib if it exists
op = createOwnedOperation(QLatin1String("Mkdir"));
@@ -1756,7 +1883,7 @@ bool PackageManagerCorePrivate::runInstaller()
throw Error(tr("It is not possible to install from network location"));
}
- if (!adminRightsGained) {
+ if (!m_core->hasAdminRights()) {
foreach (Component *component, componentsToInstall) {
if (component->value(scRequiresAdminRights, scFalse) == scFalse)
continue;
@@ -1790,7 +1917,7 @@ bool PackageManagerCorePrivate::runInstaller()
double progressOperationSize = componentsInstallPartProgressSize / progressOperationCount;
// Now install the requested components
- unpackAndInstallComponents(componentsToInstall, progressOperationSize, adminRightsGained);
+ unpackAndInstallComponents(componentsToInstall, progressOperationSize);
if (m_core->isOfflineOnly() && PackageManagerCore::createLocalRepositoryFromBinary()) {
emit m_core->titleMessageChanged(tr("Creating local repository"));
@@ -1948,7 +2075,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->resolvedComponents().contains(m_componentsToReplaceUpdaterMode.value(name).first)) {
nonRevertedOperations.append(operation);
continue;
}
@@ -1988,7 +2115,7 @@ bool PackageManagerCorePrivate::runPackageUpdater()
}
// we did not request admin rights till we found out that a component/ undo needs admin rights
- if (updateAdminRights && !adminRightsGained) {
+ if (updateAdminRights && !m_core->hasAdminRights()) {
m_core->gainAdminRights();
m_core->dropAdminRights();
}
@@ -2009,7 +2136,7 @@ bool PackageManagerCorePrivate::runPackageUpdater()
if (undoOperations.count() > 0) {
ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("Removing deselected components..."));
- runUndoOperations(undoOperations, undoOperationProgressSize, adminRightsGained, true);
+ runUndoOperations(undoOperations, undoOperationProgressSize, true);
}
m_performedOperationsOld = nonRevertedOperations; // these are all operations left: those not reverted
@@ -2017,7 +2144,7 @@ bool PackageManagerCorePrivate::runPackageUpdater()
const double progressOperationSize = componentsInstallPartProgressSize / progressOperationCount;
// Now install the requested new components
- unpackAndInstallComponents(componentsToInstall, progressOperationSize, adminRightsGained);
+ unpackAndInstallComponents(componentsToInstall, progressOperationSize);
emit m_core->titleMessageChanged(tr("Creating Maintenance Tool"));
@@ -2086,7 +2213,7 @@ bool PackageManagerCorePrivate::runUninstaller()
}
// We did not yet request elevated permissions but they are required.
- if (updateAdminRights && !adminRightsGained) {
+ if (updateAdminRights && !m_core->hasAdminRights()) {
m_core->gainAdminRights();
m_core->dropAdminRights();
}
@@ -2094,7 +2221,7 @@ bool PackageManagerCorePrivate::runUninstaller()
const int uninstallOperationCount = countProgressOperations(undoOperations);
const double undoOperationProgressSize = double(1) / double(uninstallOperationCount);
- runUndoOperations(undoOperations, undoOperationProgressSize, adminRightsGained, false);
+ runUndoOperations(undoOperations, undoOperationProgressSize, false);
// No operation delete here, as all old undo operations are deleted in the destructor.
deleteMaintenanceTool(); // this will also delete the TargetDir on Windows
@@ -2103,7 +2230,7 @@ bool PackageManagerCorePrivate::runUninstaller()
// If not on Windows, we need to remove TargetDir manually.
#ifndef Q_OS_WIN
if (QVariant(m_core->value(scRemoveTargetDir)).toBool() && !targetDir().isEmpty()) {
- if (updateAdminRights && !adminRightsGained)
+ if (updateAdminRights && !m_core->hasAdminRights())
adminRightsGained = m_core->gainAdminRights();
removeDirectoryThreaded(targetDir(), true);
qCDebug(QInstaller::lcInstallerInstallLog) << "Complete uninstallation was chosen.";
@@ -2265,7 +2392,7 @@ bool PackageManagerCorePrivate::runOfflineGenerator()
}
void PackageManagerCorePrivate::unpackComponents(const QList<Component *> &components,
- double progressOperationSize, bool adminRightsGained)
+ double progressOperationSize)
{
OperationList unpackOperations;
bool becameAdmin = false;
@@ -2289,7 +2416,7 @@ void PackageManagerCorePrivate::unpackComponents(const QList<Component *> &compo
// There's currently no way to control this on a per-operation basis, so
// any op requesting execution as admin means all extracts are done as admin.
- if (!adminRightsGained && !becameAdmin && op->value(QLatin1String("admin")).toBool())
+ if (!m_core->hasAdminRights() && op->value(QLatin1String("admin")).toBool())
becameAdmin = m_core->gainAdminRights();
}
}
@@ -2337,7 +2464,7 @@ void PackageManagerCorePrivate::unpackComponents(const QList<Component *> &compo
continue;
}
// Backup may request performing operation as admin
- if (!adminRightsGained && !becameAdmin && operation->value(QLatin1String("admin")).toBool())
+ if (!m_core->hasAdminRights() && operation->value(QLatin1String("admin")).toBool())
becameAdmin = m_core->gainAdminRights();
}
@@ -2406,8 +2533,7 @@ void PackageManagerCorePrivate::unpackComponents(const QList<Component *> &compo
ProgressCoordinator::instance()->emitDetailTextChanged(tr("Done"));
}
-void PackageManagerCorePrivate::installComponent(Component *component, double progressOperationSize,
- bool adminRightsGained)
+void PackageManagerCorePrivate::installComponent(Component *component, double progressOperationSize)
{
OperationList operations = component->operations(Operation::Install);
if (!component->operationsCreatedSuccessfully())
@@ -2428,7 +2554,7 @@ void PackageManagerCorePrivate::installComponent(Component *component, double pr
// maybe this operations wants us to be admin...
bool becameAdmin = false;
- if (!adminRightsGained && operation->value(QLatin1String("admin")).toBool()) {
+ if (!m_core->hasAdminRights() && operation->value(QLatin1String("admin")).toBool()) {
becameAdmin = m_core->gainAdminRights();
qCDebug(QInstaller::lcInstallerInstallLog) << operation->name() << "as admin:" << becameAdmin;
}
@@ -2517,23 +2643,54 @@ void PackageManagerCorePrivate::installComponent(Component *component, double pr
ProgressCoordinator::instance()->emitDetailTextChanged(tr("Done"));
}
-bool PackageManagerCorePrivate::runningProcessesFound()
+PackageManagerCore::Status PackageManagerCorePrivate::fetchComponentsAndInstall(const QStringList& components)
{
- //Check if there are processes running in the install
- QStringList excludeFiles = m_allowedRunningProcesses;
- excludeFiles.append(maintenanceToolName());
+ // init default model before fetching remote packages tree
+ ComponentModel *model = m_core->defaultComponentModel();
+ Q_UNUSED(model);
- const QString performModeWarning = m_completeUninstall
- ? QLatin1String("Unable to remove components.")
- : QLatin1String("Unable to update components.");
+ bool fallbackReposFetched = false;
+ auto fetchComponents = [&]() {
+ bool packagesFound = m_core->fetchPackagesWithFallbackRepositories(components, fallbackReposFetched);
- QStringList runningProcesses = runningInstallerProcesses(excludeFiles);
- if (!runningProcesses.isEmpty()) {
- qCWarning(QInstaller::lcInstallerInstallLog).noquote().nospace() << performModeWarning
- << " Please stop these processes: " << runningProcesses << " and try again.";
+ if (!packagesFound) {
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote().nospace()
+ << "No components available with the current selection.";
+ setStatus(PackageManagerCore::Canceled);
+ return false;
+ }
+ QString errorMessage;
+ bool unstableAliasFound = false;
+ if (m_core->checkComponentsForInstallation(components, errorMessage, unstableAliasFound)) {
+ if (!errorMessage.isEmpty())
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote().nospace() << errorMessage;
+ if (calculateComponentsAndRun()) {
+ if (m_core->isOfflineGenerator())
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Created installer to:" << offlineBinaryName();
+ else
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Components installed successfully";
+ }
+ } else {
+ // We found unstable alias and all repos were not fetched. Alias might have dependency to component
+ // which exists in non-default repository. Fetch all repositories now.
+ if (unstableAliasFound && !fallbackReposFetched) {
+ return false;
+ }
+ else {
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote().nospace() << errorMessage
+ << "No components available with the current selection.";
+ }
+ }
return true;
+ };
+
+ if (!fetchComponents() && !fallbackReposFetched) {
+ setStatus(PackageManagerCore::Running);
+ enableAllCategories();
+ fetchComponents();
}
- return false;
+
+ return m_core->status();
}
void PackageManagerCorePrivate::setComponentSelection(const QString &id, Qt::CheckState state)
@@ -2653,9 +2810,12 @@ void PackageManagerCorePrivate::registerMaintenanceTool()
settings.setValue(QLatin1String("Comments"), m_data.value(scTitle));
settings.setValue(QLatin1String("InstallDate"), QDateTime::currentDateTime().toString());
settings.setValue(QLatin1String("InstallLocation"), QDir::toNativeSeparators(targetDir()));
- settings.setValue(QLatin1String("UninstallString"), quoted(maintenanceTool));
- settings.setValue(QLatin1String("ModifyPath"), QString(quoted(maintenanceTool)
- + QLatin1String(" --manage-packages")));
+ settings.setValue(QLatin1String("UninstallString"), QString(quoted(maintenanceTool)
+ + QLatin1String(" --") + CommandLineOptions::scStartUninstallerLong));
+ if (!isOfflineOnly()) {
+ settings.setValue(QLatin1String("ModifyPath"), QString(quoted(maintenanceTool)
+ + QLatin1String(" --") + CommandLineOptions::scStartPackageManagerLong));
+ }
// required disk space of the installed components
quint64 estimatedSizeKB = m_core->requiredDiskSpace() / 1024;
// add required space for the maintenance tool
@@ -2692,8 +2852,8 @@ void PackageManagerCorePrivate::unregisterMaintenanceTool()
#endif
}
-void PackageManagerCorePrivate::runUndoOperations(const OperationList &undoOperations, double progressSize,
- bool adminRightsGained, bool deleteOperation)
+void PackageManagerCorePrivate::runUndoOperations(const OperationList &undoOperations,
+ double progressSize, bool deleteOperation)
{
try {
const int operationsCount = undoOperations.size();
@@ -2704,7 +2864,7 @@ void PackageManagerCorePrivate::runUndoOperations(const OperationList &undoOpera
throw Error(tr("Installation canceled by user"));
bool becameAdmin = false;
- if (!adminRightsGained && undoOperation->value(QLatin1String("admin")).toBool())
+ if (!m_core->hasAdminRights() && undoOperation->value(QLatin1String("admin")).toBool())
becameAdmin = m_core->gainAdminRights();
connectOperationToInstaller(undoOperation, progressSize);
@@ -2812,6 +2972,25 @@ LocalPackagesMap PackageManagerCorePrivate::localInstalledPackages()
return m_localPackageHub->localPackages();
}
+QList<ComponentAlias *> PackageManagerCorePrivate::componentAliases()
+{
+ if (m_aliases && m_aliasFinder)
+ return m_aliasFinder->aliases();
+
+ m_aliases = false;
+ delete m_aliasFinder;
+
+ m_aliasFinder = new AliasFinder(m_core);
+ m_aliasFinder->setAliasSources(m_aliasSources);
+ if (!m_aliasFinder->run()) {
+ qCDebug(lcDeveloperBuild) << "No component aliases found." << Qt::endl;
+ return QList<ComponentAlias *>();
+ }
+
+ m_aliases = true;
+ return m_aliasFinder->aliases();
+}
+
bool PackageManagerCorePrivate::fetchMetaInformationFromRepositories(DownloadType type)
{
m_updates = false;
@@ -2842,7 +3021,7 @@ bool PackageManagerCorePrivate::fetchMetaInformationFromRepositories(DownloadTyp
return m_repoFetched;
}
-bool PackageManagerCorePrivate::addUpdateResourcesFromRepositories(bool parseChecksum, bool compressedRepository)
+bool PackageManagerCorePrivate::addUpdateResourcesFromRepositories(bool compressedRepository)
{
if (!compressedRepository && m_updateSourcesAdded)
return m_updateSourcesAdded;
@@ -2873,38 +3052,10 @@ bool PackageManagerCorePrivate::addUpdateResourcesFromRepositories(bool parseChe
if (data->path().isEmpty())
continue;
- if (parseChecksum) {
- const QString updatesXmlPath = data->path() + QLatin1String("/Updates.xml");
- QFile updatesFile(updatesXmlPath);
- try {
- QInstaller::openForRead(&updatesFile);
- } catch(const Error &e) {
- qCWarning(QInstaller::lcInstallerInstallLog) << "Error opening Updates.xml:"
- << e.message();
- setStatus(PackageManagerCore::Failure, tr("Cannot add temporary update source information."));
- return false;
- }
-
- int line = 0;
- int column = 0;
- QString error;
- QDomDocument doc;
- if (!doc.setContent(&updatesFile, &error, &line, &column)) {
- qCWarning(QInstaller::lcInstallerInstallLog).nospace() << "Parse error in file "
- << updatesFile.fileName() << ": " << error << " at line " << line
- << " col " << column;
- setStatus(PackageManagerCore::Failure, tr("Cannot add temporary update source information."));
- return false;
- }
-
- const QDomNode checksum = doc.documentElement().firstChildElement(QLatin1String("Checksum"));
- if (!checksum.isNull())
- m_core->setTestChecksum(checksum.toElement().text().toLower() == scTrue);
- }
if (data->repository().isCompressed())
- m_compressedPackageSources.insert(PackageSource(QUrl::fromLocalFile(data->path()), 2));
+ m_compressedPackageSources.insert(PackageSource(QUrl::fromLocalFile(data->path()), 2, data->repository().postLoadComponentScript()));
else
- m_packageSources.insert(PackageSource(QUrl::fromLocalFile(data->path()), 0));
+ m_packageSources.insert(PackageSource(QUrl::fromLocalFile(data->path()), 0, data->repository().postLoadComponentScript()));
ProductKeyCheck::instance()->addPackagesFromXml(data->path() + QLatin1String("/Updates.xml"));
}
@@ -2932,7 +3083,6 @@ void PackageManagerCorePrivate::restoreCheckState()
}
m_coreCheckedHash.clear();
- m_componentsToInstallCalculated = false;
}
void PackageManagerCorePrivate::storeCheckState()
@@ -2945,19 +3095,76 @@ void PackageManagerCorePrivate::storeCheckState()
m_coreCheckedHash.insert(component, component->checkState());
}
-void PackageManagerCorePrivate::updateComponentCheckedState()
+void PackageManagerCorePrivate::updateComponentInstallActions()
{
for (Component *component : m_core->components(PackageManagerCore::ComponentType::All)) {
component->setInstallAction(component->isInstalled()
? ComponentModelHelper::KeepInstalled
: ComponentModelHelper::KeepUninstalled);
}
- for (Component *component : uninstallerCalculator()->componentsToUninstall())
+ for (Component *component : uninstallerCalculator()->resolvedComponents())
component->setInstallAction(ComponentModelHelper::Uninstall);
- for (Component *component : installerCalculator()->orderedComponentsToInstall())
+ for (Component *component : installerCalculator()->resolvedComponents())
component->setInstallAction(ComponentModelHelper::Install);
}
+bool PackageManagerCorePrivate::enableAllCategories()
+{
+ QSet<RepositoryCategory> repoCategories = m_data.settings().repositoryCategories();
+ bool additionalRepositoriesEnabled = false;
+ for (const auto &category : repoCategories) {
+ if (!category.isEnabled()) {
+ additionalRepositoriesEnabled = true;
+ enableRepositoryCategory(category, true);
+ }
+ }
+ return additionalRepositoriesEnabled;
+}
+
+void PackageManagerCorePrivate::enableRepositoryCategory(const RepositoryCategory &repoCategory, const bool enable)
+{
+ RepositoryCategory replacement = repoCategory;
+ replacement.setEnabled(enable);
+ QSet<RepositoryCategory> tmpRepoCategories = m_data.settings().repositoryCategories();
+ if (tmpRepoCategories.contains(repoCategory)) {
+ tmpRepoCategories.remove(repoCategory);
+ tmpRepoCategories.insert(replacement);
+ m_data.settings().addRepositoryCategories(tmpRepoCategories);
+ }
+}
+
+bool PackageManagerCorePrivate::installablePackagesFound(const QStringList& components)
+{
+ if (components.isEmpty())
+ return true;
+
+ PackagesList remotes = remotePackages();
+
+ auto componentsNotFoundForInstall = QtConcurrent::blockingFiltered(
+ components,
+ [remotes](const QString& installerPackage) {
+ return filterMissingPackagesToInstall(installerPackage, remotes);
+ }
+ );
+
+ if (componentsNotFoundForInstall.count() > 0) {
+ QList<ComponentAlias *> aliasList = componentAliases();
+ auto aliasesNotFoundForInstall = QtConcurrent::blockingFiltered(
+ components,
+ [aliasList](const QString& installerPackage) {
+ return filterMissingAliasesToInstall(installerPackage, aliasList);
+ }
+ );
+
+ if (aliasesNotFoundForInstall.count() > 0) {
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote().nospace() << "Cannot select " << aliasesNotFoundForInstall.join(QLatin1String(", ")) << ". Component(s) not found.";
+ setStatus(PackageManagerCore::NoPackagesFound);
+ return false;
+ }
+ }
+ return true;
+}
+
void PackageManagerCorePrivate::connectOperationCallMethodRequest(Operation *const operation)
{
QObject *const operationObject = dynamic_cast<QObject *> (operation);
@@ -3018,16 +3225,16 @@ void PackageManagerCorePrivate::addPathForDeletion(const QString &path)
}
void PackageManagerCorePrivate::unpackAndInstallComponents(const QList<Component *> &components,
- const double progressOperationSize, const bool adminRightsGained)
+ const double progressOperationSize)
{
// Perform extract operations
- unpackComponents(components, progressOperationSize, adminRightsGained);
+ unpackComponents(components, progressOperationSize);
// Perform rest of the operations and mark component as installed
const int componentsToInstallCount = components.size();
int installedComponents = 0;
foreach (Component *component, components) {
- installComponent(component, progressOperationSize, adminRightsGained);
+ installComponent(component, progressOperationSize);
++installedComponents;
ProgressCoordinator::instance()->emitAdditionalProgressStatus(tr("%1 of %2 components installed.")
@@ -3053,44 +3260,24 @@ void PackageManagerCorePrivate::processFilesForDelayedDeletion()
}
}
-void PackageManagerCorePrivate::findExecutablesRecursive(const QString &path, const QStringList &excludeFiles, QStringList *result)
-{
- QDirIterator it(path, QDir::NoDotAndDotDot | QDir::Executable | QDir::Files | QDir::System, QDirIterator::Subdirectories );
-
- while (it.hasNext())
- result->append(QDir::toNativeSeparators(it.next().toLower()));
-
- foreach (const QString &process, excludeFiles)
- result->removeAll(QDir::toNativeSeparators(process.toLower()));
-}
-
-QStringList PackageManagerCorePrivate::runningInstallerProcesses(const QStringList &excludeFiles)
-{
- QStringList resultFiles;
- findExecutablesRecursive(QCoreApplication::applicationDirPath(), excludeFiles, &resultFiles);
- return checkRunningProcessesFromList(resultFiles);
-}
-
bool PackageManagerCorePrivate::calculateComponentsAndRun()
{
- QString htmlOutput;
- bool componentsOk = m_core->calculateComponents(&htmlOutput);
-
- try {
- loadComponentScripts(installerCalculator()->orderedComponentsToInstall(), true);
- } catch (const Error &error) {
- qCWarning(QInstaller::lcInstallerInstallLog) << error.message();
- return false;
- }
+ bool componentsOk = m_core->recalculateAllComponents();
if (statusCanceledOrFailed()) {
qCDebug(QInstaller::lcInstallerInstallLog) << "Installation canceled.";
} else if (componentsOk && acceptLicenseAgreements()) {
- qCDebug(QInstaller::lcInstallerInstallLog).noquote() << htmlToString(htmlOutput);
+ try {
+ loadComponentScripts(installerCalculator()->resolvedComponents(), true);
+ } catch (const Error &error) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << error.message();
+ return false;
+ }
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote()
+ << htmlToString(m_core->componentResolveReasons());
- QString spaceInfo;
- const bool spaceOk = m_core->checkAvailableSpace(spaceInfo);
- qCDebug(QInstaller::lcInstallerInstallLog) << spaceInfo;
+ const bool spaceOk = m_core->checkAvailableSpace();
+ qCDebug(QInstaller::lcInstallerInstallLog) << m_core->availableSpaceMessage();
if (!spaceOk || !(m_autoConfirmCommand || askUserConfirmCommand())) {
qCDebug(QInstaller::lcInstallerInstallLog) << "Installation aborted.";
@@ -3103,30 +3290,6 @@ bool PackageManagerCorePrivate::calculateComponentsAndRun()
return false;
}
-void PackageManagerCorePrivate::calculateUninstallComponents()
-{
- clearUninstallerCalculator();
- const QList<Component *> componentsToInstallList = installerCalculator()->orderedComponentsToInstall();
-
- QList<Component *> selectedComponentsToUninstall;
- foreach (Component* component, m_core->components(PackageManagerCore::ComponentType::Replacements)) {
- // Uninstall the component if replacement is selected for install or update
- QPair<Component*, Component*> comp = componentsToReplace().value(component->name());
- if (comp.first) {
- if (comp.first->isSelectedForInstallation() || comp.first->updateRequested()) {
- uninstallerCalculator()->insertUninstallReason(component,
- UninstallerCalculator::Replaced, comp.first->name());
- selectedComponentsToUninstall.append(comp.second);
- }
- }
- }
- foreach (Component *component, m_core->components(PackageManagerCore::ComponentType::AllNoReplacements)) {
- if (component->uninstallationRequested() && !componentsToInstallList.contains(component))
- selectedComponentsToUninstall.append(component);
- }
- uninstallerCalculator()->appendComponentsToUninstall(selectedComponentsToUninstall);
-}
-
bool PackageManagerCorePrivate::acceptLicenseAgreements() const
{
// Always skip for uninstaller
@@ -3141,6 +3304,13 @@ bool PackageManagerCorePrivate::acceptLicenseAgreements() const
m_core->addLicenseItem(component->licenses());
}
+ const QString acceptanceText = ProductKeyCheck::instance()->licenseAcceptanceText();
+ if (!acceptanceText.isEmpty()) {
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote() << acceptanceText;
+ if (!m_autoAcceptLicenses && !acceptRejectCliQuery())
+ return false;
+ }
+
QHash<QString, QMap<QString, QString>> priorityHash = m_core->sortedLicenses();
QStringList priorities = priorityHash.keys();
priorities.sort();
@@ -3189,6 +3359,23 @@ bool PackageManagerCorePrivate::askUserAcceptLicense(const QString &name, const
}
}
+bool PackageManagerCorePrivate::acceptRejectCliQuery() const
+{
+ forever {
+ const QString input = m_core->readConsoleLine(QLatin1String("Accept|Reject"));
+
+ if (QString::compare(input, QLatin1String("Accept"), Qt::CaseInsensitive) == 0
+ || QString::compare(input, QLatin1String("A"), Qt::CaseInsensitive) == 0) {
+ return true;
+ } else if (QString::compare(input, QLatin1String("Reject"), Qt::CaseInsensitive) == 0
+ || QString::compare(input, QLatin1String("R"), Qt::CaseInsensitive) == 0) {
+ return false;
+ } else {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Unknown answer:" << input;
+ }
+ }
+}
+
bool PackageManagerCorePrivate::askUserConfirmCommand() const
{
qCDebug(QInstaller::lcInstallerInstallLog) << "Do you want to continue?";
diff --git a/src/libs/installer/packagemanagercore_p.h b/src/libs/installer/packagemanagercore_p.h
index 03047240c..c0c55c4cc 100644
--- a/src/libs/installer/packagemanagercore_p.h
+++ b/src/libs/installer/packagemanagercore_p.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -54,11 +54,15 @@ using namespace KDUpdater;
namespace QInstaller {
struct BinaryLayout;
+struct AliasSource;
+class AliasFinder;
class ScriptEngine;
class ComponentModel;
+class ComponentAlias;
class InstallerCalculator;
class UninstallerCalculator;
class RemoteFileEngineHandler;
+class ComponentSortFilterProxyModel;
class PackageManagerCorePrivate : public QObject
{
@@ -106,6 +110,7 @@ public:
QString configurationFileName() const;
bool buildComponentTree(QHash<QString, Component*> &components, bool loadScript);
+ bool buildComponentAliases();
template <typename T>
bool loadComponentScripts(const T &components, const bool postScript = false);
@@ -119,7 +124,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;
@@ -170,13 +174,11 @@ public:
m_performedOperationsCurrentSession.clear();
}
- void unpackComponents(const QList<Component *> &components, double progressOperationSize,
- bool adminRightsGained = false);
+ void unpackComponents(const QList<Component *> &components, double progressOperationSize);
- void installComponent(Component *component, double progressOperationSize,
- bool adminRightsGained = false);
+ void installComponent(Component *component, double progressOperationSize);
+ PackageManagerCore::Status fetchComponentsAndInstall(const QStringList& components);
- bool runningProcessesFound();
void setComponentSelection(const QString &id, Qt::CheckState state);
signals:
@@ -189,8 +191,10 @@ signals:
public:
UpdateFinder *m_updateFinder;
+ AliasFinder *m_aliasFinder;
QSet<PackageSource> m_packageSources;
QSet<PackageSource> m_compressedPackageSources;
+ QSet<AliasSource> m_aliasSources;
std::shared_ptr<LocalPackageHub> m_localPackageHub;
QStringList m_filesForDelayedDeletion;
@@ -218,6 +222,8 @@ public:
QList<QInstaller::Component*> m_updaterComponentsDeps;
QList<QInstaller::Component*> m_updaterDependencyReplacements;
+ QHash<QString, QInstaller::ComponentAlias *> m_componentAliases;
+
OperationList m_ownedOperations;
OperationList m_performedOperationsOld;
OperationList m_performedOperationsCurrentSession;
@@ -245,7 +251,7 @@ private slots:
private:
void unpackAndInstallComponents(const QList<Component *> &components,
- const double progressOperationSize, const bool adminRightsGained);
+ const double progressOperationSize);
void deleteMaintenanceTool();
void deleteMaintenanceToolAlias();
@@ -258,25 +264,30 @@ private:
void writeMaintenanceToolAppBundle(OperationList &performedOperations);
void runUndoOperations(const OperationList &undoOperations, double undoOperationProgressSize,
- bool adminRightsGained, bool deleteOperation);
+ bool deleteOperation);
PackagesList remotePackages();
LocalPackagesMap localInstalledPackages();
+ QList<ComponentAlias *> componentAliases();
+
bool fetchMetaInformationFromRepositories(DownloadType type = DownloadType::All);
- bool addUpdateResourcesFromRepositories(bool parseChecksum, bool compressedRepository = false);
+ bool addUpdateResourcesFromRepositories(bool compressedRepository = false);
void processFilesForDelayedDeletion();
- void findExecutablesRecursive(const QString &path, const QStringList &excludeFiles, QStringList *result);
- QStringList runningInstallerProcesses(const QStringList &exludeFiles);
bool calculateComponentsAndRun();
- void calculateUninstallComponents();
bool acceptLicenseAgreements() const;
bool askUserAcceptLicense(const QString &name, const QString &content) const;
+ bool acceptRejectCliQuery() const;
bool askUserConfirmCommand() const;
bool packageNeedsUpdate(const LocalPackage &localPackage, const Package *update) const;
void commitPendingUnstableComponents();
void createAutoDependencyHash(const QString &componentName, const QString &oldValue, const QString &newValue);
void createLocalDependencyHash(const QString &componentName, const QString &dependencies);
- void updateComponentCheckedState();
+ void updateComponentInstallActions();
+
+ bool enableAllCategories();
+ void enableRepositoryCategory(const RepositoryCategory &repoCategory, const bool enable);
+
+ bool installablePackagesFound(const QStringList& components);
// remove once we deprecate isSelected, setSelected etc...
void restoreCheckState();
@@ -288,13 +299,13 @@ private:
TempPathDeleter m_tmpPathDeleter;
bool m_updates;
+ bool m_aliases;
bool m_repoFetched;
bool m_updateSourcesAdded;
qint64 m_magicBinaryMarker;
int m_magicMarkerSupplement;
- bool m_componentsToInstallCalculated;
- bool m_foundEssentialUpdate;;
+ bool m_foundEssentialUpdate;
mutable ScriptEngine *m_componentScriptEngine;
mutable ScriptEngine *m_controlScriptEngine;
@@ -311,6 +322,7 @@ private:
ComponentModel *m_defaultModel;
ComponentModel *m_updaterModel;
+ ComponentSortFilterProxyModel *m_componentSortFilterProxyModel;
QObject *m_guiObject;
QScopedPointer<RemoteFileEngineHandler> m_remoteFileEngineHandler;
@@ -328,6 +340,8 @@ private:
QHash<QString, QStringList > m_componentReplaces;
QString m_datFileName;
+ bool m_allowCompressedRepositoryInstall;
+ int m_connectedOperations;
};
} // namespace QInstaller
diff --git a/src/libs/installer/packagemanagercoredata.cpp b/src/libs/installer/packagemanagercoredata.cpp
index ff246cb71..1113908bd 100644
--- a/src/libs/installer/packagemanagercoredata.cpp
+++ b/src/libs/installer/packagemanagercoredata.cpp
@@ -36,6 +36,7 @@
#include <QDir>
#include <QRegularExpression>
#include <QSettings>
+#include <QStandardPaths>
#ifdef Q_OS_WIN
# include <windows.h>
@@ -95,7 +96,7 @@ PackageManagerCoreData::PackageManagerCoreData(const QHash<QString, QString> &va
if (isInstaller) {
addNewVariable(scTargetDir, replaceVariables(m_settings.targetDir()));
addNewVariable(scTargetConfigurationFile, m_settings.configurationFileName());
- addNewVariable(scStartMenuDir, m_settings.startMenuDir());
+ addNewVariable(scStartMenuDir, replaceVariables(m_settings.startMenuDir()));
} else {
#ifdef Q_OS_MACOS
addNewVariable(scTargetDir, QFileInfo(QCoreApplication::applicationDirPath() + QLatin1String("/../../..")).absoluteFilePath());
@@ -257,6 +258,8 @@ QVariant PackageManagerCoreData::value(const QString &key, const QVariant &_defa
if (!filename.isEmpty() && !regKey.isEmpty() && registry.contains(regKey))
return registry.value(regKey).toString();
}
+#else
+ Q_UNUSED(format)
#endif
if (m_variables.contains(key))
@@ -293,7 +296,7 @@ QString PackageManagerCoreData::replaceVariables(const QString &str) const
QByteArray PackageManagerCoreData::replaceVariables(const QByteArray &ba) const
{
- static const QChar at = QLatin1Char('@');
+ static const char at = '@';
QByteArray res;
int pos = 0;
while (true) {
diff --git a/src/libs/installer/packagemanagergui.cpp b/src/libs/installer/packagemanagergui.cpp
index f7c500811..1f0462eea 100644
--- a/src/libs/installer/packagemanagergui.cpp
+++ b/src/libs/installer/packagemanagergui.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -80,13 +80,15 @@
#include <QShowEvent>
#include <QFileDialog>
#include <QGroupBox>
-#include <QDesktopWidget>
+#include <QScreen>
#ifdef Q_OS_WIN
# include <qt_windows.h>
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
# include <QWinTaskbarButton>
# include <QWinTaskbarProgress>
#endif
+#endif
using namespace KDUpdater;
using namespace QInstaller;
@@ -210,8 +212,9 @@ Q_DECLARE_METATYPE(DynamicInstallerPage*)
class PackageManagerGui::Private
{
public:
- Private()
- : m_currentId(-1)
+ Private(PackageManagerGui *qq)
+ : q(qq)
+ , m_currentId(-1)
, m_modified(false)
, m_autoSwitchPage(true)
, m_showSettingsButton(false)
@@ -235,6 +238,20 @@ public:
QLatin1String("unknown button"));
}
+ void showSettingsButton(bool show)
+ {
+ if (m_showSettingsButton == show)
+ return;
+ q->setOption(QWizard::HaveCustomButton1, show);
+ q->setButtonText(QWizard::CustomButton1, tr("&Settings"));
+ q->button(QWizard::CustomButton1)->setToolTip(
+ PackageManagerGui::tr("Specify proxy settings and configure repositories for add-on components."));
+
+ q->updateButtonLayout();
+ m_showSettingsButton = show;
+ }
+
+ PackageManagerGui *q;
int m_currentId;
bool m_modified;
bool m_autoSwitchPage;
@@ -283,6 +300,11 @@ public:
*/
/*!
+ \fn void QInstaller::PackageManagerGui::aboutApplicationClicked()
+ \sa {gui::aboutApplicationClicked}{gui.aboutApplicationClicked}
+*/
+
+/*!
\fn void QInstaller::PackageManagerGui::packageManagerCore() const
Returns the package manager core.
@@ -294,7 +316,7 @@ public:
*/
PackageManagerGui::PackageManagerGui(PackageManagerCore *core, QWidget *parent)
: QWizard(parent)
- , d(new Private)
+ , d(new Private(this))
, m_core(core)
{
if (m_core->isInstaller())
@@ -390,10 +412,13 @@ PackageManagerGui::PackageManagerGui(PackageManagerCore *core, QWidget *parent)
connect(this, &QDialog::rejected, m_core, &PackageManagerCore::setCanceled);
connect(this, &PackageManagerGui::interrupted, m_core, &PackageManagerCore::interrupt);
- // both queued to show the finished page once everything is done
+ // all queued to show the finished page once everything is done
connect(m_core, &PackageManagerCore::installationFinished,
this, &PackageManagerGui::showFinishedPage,
Qt::QueuedConnection);
+ connect(m_core, &PackageManagerCore::offlineGenerationFinished,
+ this, &PackageManagerGui::showFinishedPage,
+ Qt::QueuedConnection);
connect(m_core, &PackageManagerCore::uninstallationFinished,
this, &PackageManagerGui::showFinishedPage,
Qt::QueuedConnection);
@@ -446,7 +471,7 @@ PackageManagerGui::PackageManagerGui(PackageManagerCore *core, QWidget *parent)
*/
void PackageManagerGui::setMaxSize()
{
- QSize size = qApp->desktop()->availableGeometry(this).size();
+ QSize size = this->screen()->availableGeometry().size();
int windowFrameHeight = frameGeometry().height() - geometry().height();
int availableHeight = size.height() - windowFrameHeight;
@@ -498,7 +523,7 @@ void PackageManagerGui::updatePageListWidget()
item->setFont(currentItemFont);
// Current item should be always visible on the list
m_pageListWidget->scrollToItem(item);
- } else if (id > d->m_currentId) {
+ } else {
item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
}
}
@@ -667,6 +692,22 @@ bool PackageManagerGui::isButtonEnabled(int wb)
}
/*!
+ Sets \a buttonText for button specified by \a buttonId to a installer page \a pageId.
+
+ \note In some pages, installer will change the button text when entering
+ the page. In that case, you need to connect to \c entered() -signal of the
+ page to change the \a buttonText.
+
+ \sa {gui::setWizardPageButtonText}{gui.setWizardPageButtonText}
+*/
+void PackageManagerGui::setWizardPageButtonText(int pageId, int buttonId, const QString &buttonText)
+{
+ PackageManagerPage *const p = qobject_cast<PackageManagerPage*> (page(pageId));
+ if (p)
+ p->setButtonText(static_cast<QWizard::WizardButton>(buttonId), buttonText);
+}
+
+/*!
Sets a validator for the custom page specified by \a name and
\a callbackName requested by \a component.
*/
@@ -1029,16 +1070,18 @@ void PackageManagerGui::showFinishedPage()
*/
void PackageManagerGui::showSettingsButton(bool show)
{
- if (d->m_showSettingsButton == show)
- return;
-
- d->m_showSettingsButton = show;
- setOption(QWizard::HaveCustomButton1, show);
- setButtonText(QWizard::CustomButton1, tr("&Settings"));
- button(QWizard::CustomButton1)->setToolTip(
- PackageManagerGui::tr("Specify proxy settings and configure repositories for add-on components."));
+ m_core->setValue(QLatin1String("ShowSettingsButton"), QString::number(show));
+ d->showSettingsButton(show);
+}
- updateButtonLayout();
+/*!
+ Shows the \uicontrol Settings button if \a request is \c true. If script has
+ set the settings button visibility, this function has no effect.
+*/
+void PackageManagerGui::requestSettingsButtonByInstaller(bool request)
+{
+ if (m_core->value(QLatin1String("ShowSettingsButton")).isEmpty())
+ d->showSettingsButton(request);
}
/*!
@@ -1221,10 +1264,10 @@ void PackageManagerGui::currentPageChanged(int newId)
PackageManagerPage::PackageManagerPage(PackageManagerCore *core)
: m_complete(true)
, m_titleColor(QString())
+ , m_showOnPageList(true)
, m_needsSettingsButton(false)
, m_core(core)
, validatorComponent(nullptr)
- , m_showOnPageList(true)
{
if (!m_core->settings().titleColor().isEmpty())
m_titleColor = m_core->settings().titleColor();
@@ -1288,7 +1331,7 @@ QString PackageManagerPage::productName() const
*/
void PackageManagerPage::setColoredTitle(const QString &title)
{
- setTitle(QString::fromLatin1("<font color=\"%1\">%2</font>").arg(m_titleColor, title));
+ setTitle(QString::fromLatin1("<center><font color=\"%1\">%2</font></center>").arg(m_titleColor, title));
}
/*!
@@ -1296,7 +1339,7 @@ void PackageManagerPage::setColoredTitle(const QString &title)
*/
void PackageManagerPage::setColoredSubTitle(const QString &subTitle)
{
- setSubTitle(QString::fromLatin1("<font color=\"%1\">%2</font>").arg(m_titleColor, subTitle));
+ setSubTitle(QString::fromLatin1("<center><font color=\"%1\">%2</font></center>").arg(m_titleColor, subTitle));
}
/*!
@@ -1428,20 +1471,12 @@ int PackageManagerPage::nextId() const
if (next == PackageManagerCore::LicenseCheck) {
// calculate the page after the license page
const int nextNextId = gui()->pageIds().value(gui()->pageIds().indexOf(next) + 1, -1);
- const PackageManagerCore *const core = packageManagerCore();
+ PackageManagerCore *const core = packageManagerCore();
if (core->isUninstaller())
return nextNextId; // forcibly hide the license page if we run as uninstaller
-
- core->calculateComponentsToInstall();
- foreach (Component* component, core->orderedComponentsToInstall()) {
- if (core->isMaintainer() && component->isInstalled())
- continue; // package manager or updater, hide as long as the component is installed
-
- // The component is about to be installed and provides a license, so the page needs to
- // be shown.
- if (!component->licenses().isEmpty())
- return next;
- }
+ core->recalculateAllComponents();
+ if (core->hasLicenses())
+ return next;
return nextNextId; // no component with a license or all components with license installed
}
return next; // default, show the next page
@@ -1480,7 +1515,7 @@ IntroductionPage::IntroductionPage(PackageManagerCore *core)
, m_removeAllComponents(nullptr)
{
setObjectName(QLatin1String("IntroductionPage"));
- setColoredTitle(tr("Setup - %1").arg(productName()));
+ setColoredTitle(tr("Welcome"));
QVBoxLayout *layout = new QVBoxLayout(this);
setLayout(layout);
@@ -1528,6 +1563,8 @@ IntroductionPage::IntroductionPage(PackageManagerCore *core)
m_errorLabel = new QLabel(this);
m_errorLabel->setWordWrap(true);
+ m_errorLabel->setTextFormat(Qt::RichText);
+ m_errorLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
boxLayout->addWidget(m_errorLabel);
m_errorLabel->setObjectName(QLatin1String("ErrorLabel"));
@@ -1544,6 +1581,7 @@ IntroductionPage::IntroductionPage(PackageManagerCore *core)
m_updateComponents->setEnabled(!m_offlineMaintenanceTool && ProductKeyCheck::instance()->hasValidKey());
#ifdef Q_OS_WIN
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS7) {
m_taskButton = new QWinTaskbarButton(this);
connect(core, &PackageManagerCore::metaJobProgress,
@@ -1552,6 +1590,7 @@ IntroductionPage::IntroductionPage(PackageManagerCore *core)
m_taskButton = nullptr;
}
#endif
+#endif
}
/*!
@@ -1582,7 +1621,7 @@ bool IntroductionPage::validatePage()
bool isOfflineOnlyInstaller = core->isInstaller() && core->isOfflineOnly();
// If not offline only installer, at least one valid repository needs to be available
- if (!isOfflineOnlyInstaller && !validRepositoriesAvailable()) {
+ if (!isOfflineOnlyInstaller && !core->validRepositoriesAvailable()) {
setErrorMessage(QLatin1String("<font color=\"red\">") + tr("At least one valid and enabled "
"repository required for this action to succeed.") + QLatin1String("</font>"));
return isComplete();
@@ -1597,6 +1636,7 @@ bool IntroductionPage::validatePage()
}
#ifdef Q_OS_WIN
+#if QT_VERSION < QT_VERSION_CHECK(6, 0 ,0)
if (m_taskButton) {
if (!m_taskButton->window()) {
if (QWidget *widget = QApplication::activeWindow())
@@ -1608,6 +1648,7 @@ bool IntroductionPage::validatePage()
m_taskButton->progress()->setVisible(true);
}
#endif
+#endif
// fetch updater packages
if (core->isUpdater()) {
@@ -1627,21 +1668,12 @@ bool IntroductionPage::validatePage()
// fetch common packages
if (core->isInstaller() || core->isPackageManager()) {
- bool localPackagesTreeFetched = false;
if (!m_allPackagesFetched) {
// first try to fetch the server side packages tree
m_allPackagesFetched = core->fetchRemotePackagesTree();
if (!m_allPackagesFetched) {
QString error = core->error();
- if (core->isPackageManager() && core->status() != PackageManagerCore::ForceUpdate) {
- // if that fails and we're in maintenance mode, try to fetch local installed tree
- localPackagesTreeFetched = core->fetchLocalPackagesTree();
- if (localPackagesTreeFetched) {
- // if that succeeded, adjust error message
- error = QLatin1String("<font color=\"red\">") + error + tr(" Only local package "
- "management available.") + QLatin1String("</font>");
- }
- } else if (core->status() == PackageManagerCore::ForceUpdate) {
+ if (core->status() == PackageManagerCore::ForceUpdate) {
// replaces the error string from packagemanagercore
error = tr("There is an important update available. Please select '%1' first")
.arg(m_updateComponents->text().remove(QLatin1Char('&')));
@@ -1659,7 +1691,7 @@ bool IntroductionPage::validatePage()
}
}
- if (m_allPackagesFetched || localPackagesTreeFetched)
+ if (m_allPackagesFetched)
setComplete(true);
}
@@ -1672,9 +1704,11 @@ bool IntroductionPage::validatePage()
gui()->setSettingsButtonEnabled(true);
#ifdef Q_OS_WIN
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if (m_taskButton)
m_taskButton->progress()->setVisible(!isComplete());
#endif
+#endif
return isComplete();
}
@@ -1740,6 +1774,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
/*!
@@ -1784,29 +1829,15 @@ void IntroductionPage::setErrorMessage(const QString &error)
m_errorLabel->setPalette(palette);
#ifdef Q_OS_WIN
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if (m_taskButton) {
m_taskButton->progress()->stop();
m_taskButton->progress()->setValue(100);
}
#endif
+#endif
}
-/*!
- Returns \c true if at least one valid and enabled repository is available.
-*/
-bool IntroductionPage::validRepositoriesAvailable() const
-{
- const PackageManagerCore *const core = packageManagerCore();
- bool valid = false;
-
- foreach (const Repository &repo, core->settings().repositories()) {
- if (repo.isEnabled() && repo.isValid()) {
- valid = true;
- break;
- }
- }
- return valid;
-}
// -- private slots
@@ -1814,7 +1845,7 @@ void IntroductionPage::setUpdater(bool value)
{
if (value) {
entering();
- gui()->showSettingsButton(true);
+ gui()->requestSettingsButtonByInstaller(true);
packageManagerCore()->setUpdater();
emit packageManagerCoreTypeChanged();
@@ -1826,7 +1857,7 @@ void IntroductionPage::setUninstaller(bool value)
{
if (value) {
entering();
- gui()->showSettingsButton(true);
+ gui()->requestSettingsButtonByInstaller(true);
packageManagerCore()->setUninstaller();
emit packageManagerCoreTypeChanged();
@@ -1838,7 +1869,7 @@ void IntroductionPage::setPackageManager(bool value)
{
if (value) {
entering();
- gui()->showSettingsButton(true);
+ gui()->requestSettingsButtonByInstaller(true);
packageManagerCore()->setPackageManager();
emit packageManagerCoreTypeChanged();
@@ -1850,9 +1881,9 @@ void IntroductionPage::setPackageManager(bool value)
*/
void IntroductionPage::initializePage()
{
- const bool repositoriesAvailable = validRepositoriesAvailable();
-
PackageManagerCore *core = packageManagerCore();
+ const bool repositoriesAvailable = core->validRepositoriesAvailable();
+
if (core->isPackageManager()) {
m_packageManager->setChecked(true);
} else if (core->isUpdater()) {
@@ -1881,13 +1912,11 @@ void IntroductionPage::initializePage()
*/
void IntroductionPage::onCoreNetworkSettingsChanged()
{
- m_updatesFetched = false;
- m_allPackagesFetched = false;
- m_forceUpdate = false;
+ resetFetchedState();
PackageManagerCore *core = packageManagerCore();
if (core->isUninstaller() || core->isMaintainer()) {
- m_offlineMaintenanceTool = !validRepositoriesAvailable();
+ m_offlineMaintenanceTool = !core->validRepositoriesAvailable();
setMaintainerToolsEnabled(!m_offlineMaintenanceTool);
m_removeAllComponents->setChecked(m_offlineMaintenanceTool);
@@ -1926,9 +1955,6 @@ void IntroductionPage::entering()
*/
void IntroductionPage::leaving()
{
- // force a recalculation of components to install to keep the state correct
- if (!packageManagerCore()->isUninstaller())
- packageManagerCore()->componentsToInstallNeedsRecalculation();
m_progressBar->setValue(0);
m_progressBar->setRange(0, 0);
setButtonText(QWizard::CancelButton, gui()->defaultButtonText(QWizard::CancelButton));
@@ -2058,7 +2084,6 @@ void LicenseAgreementPage::entering()
m_textBrowser->setHtml(QString());
m_licenseListWidget->setVisible(false);
- packageManagerCore()->calculateComponentsToInstall();
foreach (QInstaller::Component *component, packageManagerCore()->orderedComponentsToInstall())
packageManagerCore()->addLicenseItem(component->licenses());
@@ -2081,7 +2106,7 @@ void LicenseAgreementPage::entering()
*/
bool LicenseAgreementPage::isComplete() const
{
- return m_acceptCheckBox->isChecked();
+ return m_acceptCheckBox->isChecked() && ProductKeyCheck::instance()->hasAcceptedAllLicenses();
}
void LicenseAgreementPage::openLicenseUrl(const QUrl &url)
@@ -2174,7 +2199,7 @@ void ComponentSelectionPage::entering()
QT_TR_NOOP("Please select the components you want to update."),
QT_TR_NOOP("Please select the components you want to install."),
QT_TR_NOOP("Please select the components you want to uninstall."),
- QT_TR_NOOP("Select the components to install. Deselect installed components to uninstall them. Any components already installed will not be updated."),
+ QT_TR_NOOP("Select the components to install. Deselect installed components to uninstall them.<br>Any components already installed will not be updated."),
QT_TR_NOOP("Mandatory components need to be updated first before you can select other components to update.")
};
@@ -2189,18 +2214,16 @@ void ComponentSelectionPage::entering()
d->updateTreeView();
// check component model state so we can enable needed component selection buttons
- if (core->isUpdater())
- d->onModelStateChanged(d->m_currentModel->checkedState());
+ d->onModelStateChanged(d->m_currentModel->checkedState());
setModified(isComplete());
- if (core->settings().repositoryCategories().count() > 0 && !core->isOfflineOnly()
- && !core->isUpdater()) {
- d->showCategoryLayout(true);
- core->settings().setAllowUnstableComponents(true);
- } else {
- d->showCategoryLayout(false);
- }
+ d->showCategoryLayout(core->showRepositoryCategories());
d->showCompressedRepositoryButton();
+ d->showCreateOfflineInstallerButton(true);
+
+ // Reset to default supplement state. The page may set it to OfflineGenerator
+ // which needs to be reset after navigating back to the page.
+ core->resetBinaryMarkerSupplement();
}
/*!
@@ -2210,6 +2233,7 @@ void ComponentSelectionPage::entering()
void ComponentSelectionPage::leaving()
{
d->hideCompressedRepositoryButton();
+ d->showCreateOfflineInstallerButton(false);
}
/*!
@@ -2227,6 +2251,11 @@ void ComponentSelectionPage::showEvent(QShowEvent *event)
QWizardPage::showEvent(event);
}
+/*!
+ Called when \c ComponentSelectionPage is validated.
+ Tries to load \c component scripts for components about to be installed.
+ Returns \c true if the script loading succeeded and the next page is shown.
+*/
bool ComponentSelectionPage::validatePage()
{
PackageManagerCore *core = packageManagerCore();
@@ -2288,16 +2317,6 @@ void ComponentSelectionPage::deselectComponent(const QString &id)
}
/*!
- Adds the possibility to install a compressed repository on component selection
- page. A new button which opens a file browser is added for compressed
- repository selection.
-*/
-void ComponentSelectionPage::allowCompressedRepositoryInstall()
-{
- d->allowCompressedRepositoryInstall();
-}
-
-/*!
Adds an additional virtual component with the \a name to be installed.
Returns \c true if the virtual component is found and not installed.
@@ -2310,13 +2329,18 @@ bool ComponentSelectionPage::addVirtualComponentToUninstall(const QString &name)
name, allComponents);
if (component && component->isInstalled() && component->isVirtual()) {
component->setCheckState(Qt::Unchecked);
- core->componentsToInstallNeedsRecalculation();
+ core->recalculateAllComponents();
qCDebug(QInstaller::lcDeveloperBuild) << "Virtual component " << name << " was selected for uninstall by script.";
return true;
}
return false;
}
+void ComponentSelectionPage::setAllowCreateOfflineInstaller(bool allow)
+{
+ d->setAllowCreateOfflineInstaller(allow);
+}
+
void ComponentSelectionPage::setModified(bool modified)
{
setComplete(modified);
@@ -2327,18 +2351,10 @@ void ComponentSelectionPage::setModified(bool modified)
*/
bool ComponentSelectionPage::isComplete() const
{
- if (packageManagerCore()->isInstaller() || packageManagerCore()->isUpdater())
- return d->m_currentModel->checked().count();
-
- if (d->m_currentModel->checkedState().testFlag(ComponentModel::DefaultChecked) == false)
- return true;
+ if (!d->componentsResolved())
+ return false;
- const QSet<Component *> uncheckable = d->m_currentModel->uncheckable();
- for (auto &component : uncheckable) {
- if (component->forcedInstallation() && !component->isInstalled())
- return true; // allow installation for new forced components
- }
- return false;
+ return d->m_currentModel->componentsSelected();
}
@@ -2676,6 +2692,10 @@ void ReadyForInstallationPage::entering()
setButtonText(QWizard::CommitButton, tr("U&pdate"));
setColoredTitle(tr("Ready to Update Packages"));
m_msgLabel->setText(tr("All required information is now available to begin updating your installation."));
+ } else if (packageManagerCore()->isOfflineGenerator()) {
+ setButtonText(QWizard::CommitButton, tr("Create Offline Installer"));
+ setColoredTitle(tr("Ready to Create Offline Installer"));
+ m_msgLabel->setText(tr("All required information is now available to create an offline installer for selected components."));
} else {
Q_ASSERT(packageManagerCore()->isInstaller());
setButtonText(QWizard::CommitButton, tr("&Install"));
@@ -2684,18 +2704,18 @@ void ReadyForInstallationPage::entering()
.arg(productName()));
}
- QString htmlOutput;
- bool componentsOk = packageManagerCore()->calculateComponents(&htmlOutput);
+ bool componentsOk = packageManagerCore()->recalculateAllComponents();
+ const QString htmlOutput = packageManagerCore()->componentResolveReasons();
+
qCDebug(QInstaller::lcInstallerInstallLog).noquote() << htmlToString(htmlOutput);
m_taskDetailsBrowser->setHtml(htmlOutput);
m_taskDetailsBrowser->setVisible(!componentsOk || LoggingHandler::instance().isVerbose());
setComplete(componentsOk);
- QString spaceInfo;
- if (packageManagerCore()->checkAvailableSpace(spaceInfo)) {
- m_msgLabel->setText(QString::fromLatin1("%1 %2").arg(m_msgLabel->text(), spaceInfo));
+ if (packageManagerCore()->checkAvailableSpace()) {
+ m_msgLabel->setText(QString::fromLatin1("%1 %2").arg(m_msgLabel->text(), packageManagerCore()->availableSpaceMessage()));
} else {
- m_msgLabel->setText(spaceInfo);
+ m_msgLabel->setText(packageManagerCore()->availableSpaceMessage());
setComplete(false);
}
}
@@ -2715,7 +2735,9 @@ void ReadyForInstallationPage::leaving()
void ReadyForInstallationPage::updatePageListTitle()
{
PackageManagerCore *core = packageManagerCore();
- if (core->isInstaller())
+ if (core->isOfflineGenerator())
+ setPageListTitle(tr("Ready to Create Offline Installer"));
+ else if (core->isInstaller())
setPageListTitle(tr("Ready to Install"));
else if (core->isMaintainer())
setPageListTitle(tr("Ready to Update"));
@@ -2773,6 +2795,11 @@ PerformInstallationPage::PerformInstallationPage(PackageManagerCore *core)
connect(core, &PackageManagerCore::installationFinished,
this, &PerformInstallationPage::installationFinished);
+ connect(core, &PackageManagerCore::offlineGenerationStarted,
+ this, &PerformInstallationPage::installationStarted);
+ connect(core, &PackageManagerCore::offlineGenerationFinished,
+ this, &PerformInstallationPage::installationFinished);
+
connect(core, &PackageManagerCore::uninstallationStarted,
this, &PerformInstallationPage::uninstallationStarted);
connect(core, &PackageManagerCore::uninstallationFinished,
@@ -2842,6 +2869,11 @@ void PerformInstallationPage::entering()
setColoredTitle(tr("Updating components of %1").arg(productName()));
QTimer::singleShot(30, packageManagerCore(), SLOT(runPackageUpdater()));
+ } else if (packageManagerCore()->isOfflineGenerator()) {
+ setButtonText(QWizard::CommitButton, tr("&Create Offline Installer"));
+ setColoredTitle(tr("Creating Offline Installer for %1").arg(productName()));
+
+ QTimer::singleShot(30, packageManagerCore(), SLOT(runOfflineGenerator()));
} else {
setButtonText(QWizard::CommitButton, tr("&Install"));
setColoredTitle(tr("Installing %1").arg(productName()));
@@ -2866,7 +2898,9 @@ void PerformInstallationPage::leaving()
void PerformInstallationPage::updatePageListTitle()
{
PackageManagerCore *core = packageManagerCore();
- if (core->isInstaller())
+ if (core->isOfflineGenerator())
+ setPageListTitle(tr("Creating Offline Installer"));
+ else if (core->isInstaller())
setPageListTitle(tr("Installing"));
else if (core->isMaintainer())
setPageListTitle(tr("Updating"));
@@ -2970,7 +3004,7 @@ FinishedPage::FinishedPage(PackageManagerCore *core)
, m_commitButton(nullptr)
{
setObjectName(QLatin1String("FinishedPage"));
- setColoredTitle(tr("Completing the %1 Setup").arg(productName()));
+ setColoredTitle(tr("Finished the %1 Setup").arg(productName()));
setPageListTitle(tr("Finished"));
m_msgLabel = new QLabel(this);
@@ -2995,7 +3029,7 @@ FinishedPage::FinishedPage(PackageManagerCore *core)
*/
void FinishedPage::entering()
{
- m_msgLabel->setText(tr("Click %1 to exit the %2 Wizard.")
+ m_msgLabel->setText(tr("Click %1 to exit the %2 Setup.")
.arg(gui()->defaultButtonText(QWizard::FinishButton).remove(QLatin1Char('&')))
.arg(productName()));
@@ -3088,16 +3122,8 @@ void FinishedPage::leaving()
*/
void FinishedPage::handleFinishClicked()
{
- const QString program =
- packageManagerCore()->replaceVariables(packageManagerCore()->value(scRunProgram));
-
- const QStringList args = packageManagerCore()->replaceVariables(packageManagerCore()
- ->values(scRunProgramArguments));
- if (!m_runItCheckBox->isChecked() || program.isEmpty())
- return;
-
- qCDebug(QInstaller::lcInstallerInstallLog) << "starting" << program << args;
- QProcess::startDetached(program, args);
+ if (m_runItCheckBox->isChecked())
+ packageManagerCore()->runProgram();
}
/*!
@@ -3142,7 +3168,7 @@ RestartPage::RestartPage(PackageManagerCore *core)
: PackageManagerPage(core)
{
setObjectName(QLatin1String("RestartPage"));
- setColoredTitle(tr("Completing the %1 Setup").arg(productName()));
+ setColoredTitle(tr("Finished the %1 Setup").arg(productName()));
// Never show this page on the page list
setShowOnPageList(false);
diff --git a/src/libs/installer/packagemanagergui.h b/src/libs/installer/packagemanagergui.h
index cce36e4ae..d83643005 100644
--- a/src/libs/installer/packagemanagergui.h
+++ b/src/libs/installer/packagemanagergui.h
@@ -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.
@@ -82,8 +82,10 @@ public:
void clickButton(int wizardButton, int delayInMs = 0);
void clickButton(const QString &objectName, int delayInMs = 0) const;
bool isButtonEnabled(int wizardButton);
+ void setWizardPageButtonText(int pageId, int buttonId, const QString &buttonText);
void showSettingsButton(bool show);
+ void requestSettingsButtonByInstaller(bool request);
void setSettingsButtonEnabled(bool enable);
void updateButtonLayout();
@@ -236,6 +238,8 @@ public:
void setMaintenanceToolsEnabled(bool enable);
void setMaintainerToolsEnabled(bool enable);
+ void resetFetchedState();
+
public Q_SLOTS:
void onCoreNetworkSettingsChanged();
void setMessage(const QString &msg);
@@ -258,7 +262,6 @@ private:
void leaving() override;
void showWidgets(bool show);
- bool validRepositoriesAvailable() const;
private:
bool m_updatesFetched;
@@ -327,9 +330,10 @@ public:
Q_INVOKABLE void selectDefault();
Q_INVOKABLE void selectComponent(const QString &id);
Q_INVOKABLE void deselectComponent(const QString &id);
- Q_INVOKABLE void allowCompressedRepositoryInstall();
Q_INVOKABLE bool addVirtualComponentToUninstall(const QString &name);
+ void setAllowCreateOfflineInstaller(bool allow);
+
protected:
void entering() override;
void leaving() override;
diff --git a/src/libs/installer/packagesource.cpp b/src/libs/installer/packagesource.cpp
index 3b9fbe813..0f87e0def 100644
--- a/src/libs/installer/packagesource.cpp
+++ b/src/libs/installer/packagesource.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -50,7 +50,7 @@ namespace QInstaller {
*/
/*!
- \fn QInstaller::PackageSource::PackageSource(const QUrl &u, int p)
+ \fn QInstaller::PackageSource::PackageSource(const QUrl &u, int p, bool pl)
Constructs a package source info object. The object's url is set to \a u, while the priority
is set to \a p.
@@ -69,7 +69,7 @@ namespace QInstaller {
/*!
Returns the hash value for the \a key, using \a seed to seed the calculation.
*/
-uint qHash(const PackageSource &key, uint seed)
+hashValue qHash(const PackageSource &key, hashValue seed)
{
return qHash(key.url, seed) ^ key.priority;
}
diff --git a/src/libs/installer/packagesource.h b/src/libs/installer/packagesource.h
index 1193c1f76..f63b53cd8 100644
--- a/src/libs/installer/packagesource.h
+++ b/src/libs/installer/packagesource.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -40,16 +40,18 @@ struct INSTALLER_EXPORT PackageSource
PackageSource()
: priority(-1)
{}
- PackageSource(const QUrl &u, int p)
+ PackageSource(const QUrl &u, int p, bool pl = false)
: url(u)
, priority(p)
+ , postLoadComponentScript(pl)
{}
QUrl url;
int priority;
+ bool postLoadComponentScript;
};
-INSTALLER_EXPORT uint qHash(const PackageSource &key, uint seed);
+INSTALLER_EXPORT hashValue qHash(const PackageSource &key, hashValue seed);
INSTALLER_EXPORT bool operator==(const PackageSource &lhs, const PackageSource &rhs);
} // namespace QInstaller
diff --git a/src/libs/installer/performinstallationform.cpp b/src/libs/installer/performinstallationform.cpp
index d8d1e3dcb..0e4c561f6 100644
--- a/src/libs/installer/performinstallationform.cpp
+++ b/src/libs/installer/performinstallationform.cpp
@@ -49,9 +49,11 @@
#include <QtCore/QTimer>
#ifdef Q_OS_WIN
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
# include <QWinTaskbarButton>
# include <QWinTaskbarProgress>
#endif
+#endif
using namespace QInstaller;
@@ -96,6 +98,7 @@ PerformInstallationForm::PerformInstallationForm(PackageManagerCore *core, QObje
, m_core(core)
{
#ifdef Q_OS_WIN
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS7) {
m_taskButton = new QWinTaskbarButton(this);
m_taskButton->progress()->setVisible(true);
@@ -103,6 +106,7 @@ PerformInstallationForm::PerformInstallationForm(PackageManagerCore *core, QObje
m_taskButton = nullptr;
}
#endif
+#endif
}
/*!
@@ -204,12 +208,14 @@ void PerformInstallationForm::updateProgress()
m_progressBar->setValue(progressPercentage);
#ifdef Q_OS_WIN
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if (m_taskButton) {
if (!m_taskButton->window() && QApplication::activeWindow())
m_taskButton->setWindow(QApplication::activeWindow()->windowHandle());
m_taskButton->progress()->setValue(progressPercentage);
}
#endif
+#endif
static QString lastLabelText;
if (lastLabelText == progressCoordninator->labelText())
diff --git a/src/libs/installer/permissionsettings.cpp b/src/libs/installer/permissionsettings.cpp
index 235c6667b..d70cf5625 100644
--- a/src/libs/installer/permissionsettings.cpp
+++ b/src/libs/installer/permissionsettings.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -37,30 +37,6 @@ using namespace QInstaller;
\internal
*/
-PermissionSettings::PermissionSettings(const QString &organization, const QString &application, QObject *parent)
- : QSettings(organization, application, parent)
-{
- setIniCodec("UTF-8"); // to workaround QTBUG-102334
-}
-
-PermissionSettings::PermissionSettings(Scope scope, const QString &organization, const QString &application, QObject *parent)
- : QSettings(scope, organization, application, parent)
-{
- setIniCodec("UTF-8"); // QTBUG-102334
-}
-
-PermissionSettings::PermissionSettings(Format format, Scope scope, const QString &organization, const QString &application, QObject *parent)
- : QSettings(format, scope, organization, application, parent)
-{
- setIniCodec("UTF-8"); // QTBUG-102334
-}
-
-PermissionSettings::PermissionSettings(const QString &fileName, Format format, QObject *parent)
- : QSettings(fileName, format, parent)
-{
- setIniCodec("UTF-8"); // QTBUG-102334
-}
-
PermissionSettings::~PermissionSettings()
{
if (!fileName().isEmpty()) {
diff --git a/src/libs/installer/permissionsettings.h b/src/libs/installer/permissionsettings.h
index 2621624c2..c950d9c17 100644
--- a/src/libs/installer/permissionsettings.h
+++ b/src/libs/installer/permissionsettings.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -37,12 +37,19 @@ class PermissionSettings : public QSettings
{
public:
explicit PermissionSettings(const QString &organization,
- const QString &application = QString(), QObject *parent = 0);
+ const QString &application = QString(), QObject *parent = 0)
+ : QSettings(organization, application, parent) {}
+
PermissionSettings(Scope scope, const QString &organization,
- const QString &application = QString(), QObject *parent = 0);
+ const QString &application = QString(), QObject *parent = 0)
+ : QSettings(scope, organization, application, parent) {}
+
PermissionSettings(Format format, Scope scope, const QString &organization,
- const QString &application = QString(), QObject *parent = 0);
- PermissionSettings(const QString &fileName, Format format, QObject *parent = 0);
+ const QString &application = QString(), QObject *parent = 0)
+ : QSettings(format, scope, organization, application, parent) {}
+ PermissionSettings(const QString &fileName, Format format, QObject *parent = 0)
+ : QSettings(fileName, format, parent) {}
+
~PermissionSettings();
};
diff --git a/src/libs/installer/productkeycheck.cpp b/src/libs/installer/productkeycheck.cpp
index c1dfe83d6..ed128fa61 100644
--- a/src/libs/installer/productkeycheck.cpp
+++ b/src/libs/installer/productkeycheck.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -29,6 +29,8 @@
#include "productkeycheck.h"
#include "packagemanagercore.h"
+#include <QtUiTools/QUiLoader>
+
class ProductKeyCheckPrivate
{
};
@@ -49,6 +51,12 @@ ProductKeyCheck *ProductKeyCheck::instance()
return &instance;
}
+QUiLoader *ProductKeyCheck::uiLoader()
+{
+ static QUiLoader loader;
+ return &loader;
+}
+
void ProductKeyCheck::init(QInstaller::PackageManagerCore *core)
{
Q_UNUSED(core)
@@ -106,3 +114,22 @@ bool ProductKeyCheck::hasValidLicense() const
{
return true;
}
+
+bool ProductKeyCheck::hasAcceptedAllLicenses() const
+{
+ return true;
+}
+
+QString ProductKeyCheck::licenseAcceptanceText() const
+{
+ return QString();
+}
+QString ProductKeyCheck::securityWarning() const
+{
+ return QString();
+}
+
+QString ProductKeyCheck::additionalMetaDownloadWarning() const
+{
+ return QString();
+}
diff --git a/src/libs/installer/productkeycheck.h b/src/libs/installer/productkeycheck.h
index b7e8c6d52..8e7d6724f 100644
--- a/src/libs/installer/productkeycheck.h
+++ b/src/libs/installer/productkeycheck.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -32,6 +32,7 @@
#include "installer_global.h"
#include <QString>
+#include <QUiLoader>
namespace QInstaller {
@@ -49,6 +50,8 @@ public:
static ProductKeyCheck *instance();
void init(QInstaller::PackageManagerCore *core);
+ static QUiLoader *uiLoader();
+
// was validLicense
bool hasValidKey();
QString lastErrorString();
@@ -67,6 +70,10 @@ public:
QList<int> registeredPages() const;
bool hasValidLicense() const;
+ bool hasAcceptedAllLicenses() const;
+ QString licenseAcceptanceText() const;
+ QString securityWarning() const;
+ QString additionalMetaDownloadWarning() const;
private:
ProductKeyCheck();
diff --git a/src/libs/installer/progresscoordinator.cpp b/src/libs/installer/progresscoordinator.cpp
index 8b02711b1..6413efe28 100644
--- a/src/libs/installer/progresscoordinator.cpp
+++ b/src/libs/installer/progresscoordinator.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -40,7 +40,7 @@
using namespace QInstaller;
QT_BEGIN_NAMESPACE
-uint qHash(QPointer<QObject> key)
+hashValue qHash(QPointer<QObject> key)
{
return qHash(key.data());
}
@@ -76,7 +76,8 @@ ProgressCoordinator *ProgressCoordinator::instance()
void ProgressCoordinator::reset()
{
- disconnectAllSenders();
+ m_senderPartProgressSizeHash.clear();
+ m_senderPendingCalculatedPercentageHash.clear();
m_installationLabelText.clear();
m_currentCompletePercentage = 0;
m_currentBasePercentage = 0;
@@ -90,10 +91,11 @@ void ProgressCoordinator::reset()
void ProgressCoordinator::registerPartProgress(QObject *sender, const char *signal, double partProgressSize)
{
Q_ASSERT(sender);
+ Q_ASSERT(!sender->objectName().isEmpty());
Q_ASSERT(QString::fromLatin1(signal).contains(QLatin1String("(double)")));
Q_ASSERT(partProgressSize <= 1);
- m_senderPartProgressSizeHash.insert(sender, partProgressSize);
+ m_senderPartProgressSizeHash.insert(sender->objectName(), partProgressSize);
bool isConnected = connect(sender, signal, this, SLOT(partProgressChanged(double)));
Q_UNUSED(isConnected);
Q_ASSERT(isConnected);
@@ -116,16 +118,17 @@ void ProgressCoordinator::partProgressChanged(double fraction)
}
// no fraction no change
- if (fraction == 0)
+ if (fraction == 0 || !sender())
return;
+ QString senderObjectName = sender()->objectName();
// ignore senders sending 100% multiple times
- if (fraction == 1 && m_senderPendingCalculatedPercentageHash.contains(sender())
- && m_senderPendingCalculatedPercentageHash.value(sender()) == 0) {
+ if (fraction == 1 && m_senderPendingCalculatedPercentageHash.contains(senderObjectName)
+ && m_senderPendingCalculatedPercentageHash.value(senderObjectName) == 0) {
return;
}
- double partProgressSize = m_senderPartProgressSizeHash.value(sender(), 0);
+ double partProgressSize = m_senderPartProgressSizeHash.value(senderObjectName, 0);
if (partProgressSize == 0) {
qCWarning(QInstaller::lcInstallerInstallLog) << "It seems that this sender was not registered "
"in the right way:" << sender();
@@ -138,7 +141,7 @@ void ProgressCoordinator::partProgressChanged(double fraction)
// allPendingCalculatedPartPercentages has negative values
double newCurrentCompletePercentage = m_currentBasePercentage - pendingCalculatedPartPercentage
- + allPendingCalculatedPartPercentages(sender());
+ + allPendingCalculatedPartPercentages(senderObjectName);
//we can't check this here, because some round issues can make it little bit under 0 or over 100
//Q_ASSERT(newCurrentCompletePercentage >= 0);
@@ -163,9 +166,9 @@ void ProgressCoordinator::partProgressChanged(double fraction)
m_currentCompletePercentage = newCurrentCompletePercentage;
if (fraction == 1) {
m_currentBasePercentage = m_currentBasePercentage - pendingCalculatedPartPercentage;
- m_senderPendingCalculatedPercentageHash.insert(sender(), 0);
+ m_senderPendingCalculatedPercentageHash.insert(senderObjectName, 0);
} else {
- m_senderPendingCalculatedPercentageHash.insert(sender(), pendingCalculatedPartPercentage);
+ m_senderPendingCalculatedPercentageHash.insert(senderObjectName, pendingCalculatedPartPercentage);
}
} else { //if (m_undoMode)
@@ -174,7 +177,7 @@ void ProgressCoordinator::partProgressChanged(double fraction)
//double checkValue = allPendingCalculatedPartPercentages(sender());
double newCurrentCompletePercentage = m_manualAddedPercentage + m_currentBasePercentage
- + pendingCalculatedPartPercentage + allPendingCalculatedPartPercentages(sender());
+ + pendingCalculatedPartPercentage + allPendingCalculatedPartPercentages(senderObjectName);
//we can't check this here, because some round issues can make it little bit under 0 or over 100
//Q_ASSERT(newCurrentCompletePercentage >= 0);
@@ -199,9 +202,9 @@ void ProgressCoordinator::partProgressChanged(double fraction)
if (fraction == 1) {
m_currentBasePercentage = m_currentBasePercentage + pendingCalculatedPartPercentage;
- m_senderPendingCalculatedPercentageHash.insert(sender(), 0);
+ m_senderPendingCalculatedPercentageHash.insert(senderObjectName, 0);
} else {
- m_senderPendingCalculatedPercentageHash.insert(sender(), pendingCalculatedPartPercentage);
+ m_senderPendingCalculatedPercentageHash.insert(senderObjectName, pendingCalculatedPartPercentage);
}
} //if (m_undoMode)
printProgressPercentage(progressInPercentage());
@@ -219,25 +222,13 @@ int ProgressCoordinator::progressInPercentage() const
return currentValue;
}
-void ProgressCoordinator::disconnectAllSenders()
-{
- foreach (QPointer<QObject> sender, m_senderPartProgressSizeHash.keys()) {
- if (!sender.isNull()) {
- bool isDisconnected = sender->disconnect(this);
- Q_UNUSED(isDisconnected);
- Q_ASSERT(isDisconnected);
- }
- }
- m_senderPartProgressSizeHash.clear();
- m_senderPendingCalculatedPercentageHash.clear();
-}
-
void ProgressCoordinator::setUndoMode()
{
Q_ASSERT(!m_undoMode);
m_undoMode = true;
- disconnectAllSenders();
+ m_senderPartProgressSizeHash.clear();
+ m_senderPendingCalculatedPercentageHash.clear();
m_reachedPercentageBeforeUndo = progressInPercentage();
m_currentBasePercentage = m_reachedPercentageBeforeUndo;
}
@@ -294,10 +285,10 @@ void ProgressCoordinator::emitLabelAndDetailTextChanged(const QString &text)
qApp->processEvents(); //makes the result available in the ui
}
-double ProgressCoordinator::allPendingCalculatedPartPercentages(QObject *excludeKeyObject)
+double ProgressCoordinator::allPendingCalculatedPartPercentages(const QString &excludeKeyObject)
{
double result = 0;
- QHash<QPointer<QObject>, double>::iterator it = m_senderPendingCalculatedPercentageHash.begin();
+ QHash<QString, double>::iterator it = m_senderPendingCalculatedPercentageHash.begin();
while (it != m_senderPendingCalculatedPercentageHash.end()) {
if (it.key() != excludeKeyObject)
result += it.value();
diff --git a/src/libs/installer/progresscoordinator.h b/src/libs/installer/progresscoordinator.h
index 3540b5d16..75d5a5d30 100644
--- a/src/libs/installer/progresscoordinator.h
+++ b/src/libs/installer/progresscoordinator.h
@@ -85,12 +85,12 @@ protected:
explicit ProgressCoordinator(QObject *parent);
private:
- double allPendingCalculatedPartPercentages(QObject *excludeKeyObject = 0);
+ double allPendingCalculatedPartPercentages(const QString &excludeKeyObject = QString());
void disconnectAllSenders();
private:
- QHash<QPointer<QObject>, double> m_senderPendingCalculatedPercentageHash;
- QHash<QPointer<QObject>, double> m_senderPartProgressSizeHash;
+ QHash<QString, double> m_senderPendingCalculatedPercentageHash;
+ QHash<QString, double> m_senderPartProgressSizeHash;
ProgressSpinner *m_progressSpinner;
QString m_installationLabelText;
double m_currentCompletePercentage;
diff --git a/src/libs/installer/protocol.h b/src/libs/installer/protocol.h
index 2dd1a5b1c..8b2288a89 100644
--- a/src/libs/installer/protocol.h
+++ b/src/libs/installer/protocol.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -90,7 +90,7 @@ const char GetQProcessSignals[] = "GetQProcessSignals";
const char QProcessSignalBytesWritten[] = "QProcess::bytesWritten";
const char QProcessSignalAboutToClose[] = "QProcess::aboutToClose";
const char QProcessSignalReadChannelFinished[] = "QProcess::readChannelFinished";
-const char QProcessSignalError[] = "QProcess::error";
+const char QProcessSignalError[] = "QProcess::errorOccurred";
const char QProcessSignalReadyReadStandardOutput[] = "QProcess::readyReadStandardOutput";
const char QProcessSignalReadyReadStandardError[] = "QProcess::readyReadStandardError";
const char QProcessSignalStarted[] = "QProcess::started";
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/qprocesswrapper.cpp b/src/libs/installer/qprocesswrapper.cpp
index f37d65b29..44117eefb 100644
--- a/src/libs/installer/qprocesswrapper.cpp
+++ b/src/libs/installer/qprocesswrapper.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -32,6 +32,7 @@
#include "utils.h"
#include <QDir>
+#include <QVariant>
namespace QInstaller {
@@ -53,7 +54,7 @@ QProcessWrapper::QProcessWrapper(QObject *parent)
connect(&process, &QIODevice::bytesWritten, this, &QProcessWrapper::bytesWritten);
connect(&process, &QIODevice::aboutToClose, this, &QProcessWrapper::aboutToClose);
connect(&process, &QIODevice::readChannelFinished, this, &QProcessWrapper::readChannelFinished);
- connect(&process, SIGNAL(error(QProcess::ProcessError)), SIGNAL(error(QProcess::ProcessError)));
+ connect(&process, SIGNAL(errorOccurred(QProcess::ProcessError)), SIGNAL(errorOccurred(QProcess::ProcessError)));
connect(&process, &QProcess::readyReadStandardOutput, this, &QProcessWrapper::readyReadStandardOutput);
connect(&process, &QProcess::readyReadStandardError, this, &QProcessWrapper::readyReadStandardError);
connect(&process, SIGNAL(finished(int,QProcess::ExitStatus)), SIGNAL(finished(int,QProcess::ExitStatus)));
@@ -87,7 +88,7 @@ void QProcessWrapper::processSignals()
} else if (name == QLatin1String(Protocol::QProcessSignalReadChannelFinished)) {
emit readChannelFinished();
} else if (name == QLatin1String(Protocol::QProcessSignalError)) {
- emit error(static_cast<QProcess::ProcessError> (receivedSignals.takeFirst().toInt()));
+ emit errorOccurred(static_cast<QProcess::ProcessError> (receivedSignals.takeFirst().toInt()));
} else if (name == QLatin1String(Protocol::QProcessSignalReadyReadStandardOutput)) {
emit readyReadStandardOutput();
} else if (name == QLatin1String(Protocol::QProcessSignalReadyReadStandardError)) {
@@ -190,8 +191,8 @@ void QProcessWrapper::setProcessChannelMode(QProcessWrapper::ProcessChannelMode
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::QProcessSetProcessChannelMode),
- static_cast<QProcess::ProcessChannelMode>(mode), dummy);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessSetProcessChannelMode),
+ static_cast<QProcess::ProcessChannelMode>(mode));
m_lock.unlock();
} else {
process.setProcessChannelMode(static_cast<QProcess::ProcessChannelMode>(mode));
@@ -215,8 +216,8 @@ void QProcessWrapper::setReadChannel(QProcessWrapper::ProcessChannel chan)
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::QProcessSetReadChannel),
- static_cast<QProcess::ProcessChannel>(chan), dummy);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessSetReadChannel),
+ static_cast<QProcess::ProcessChannel>(chan));
m_lock.unlock();
} else {
process.setReadChannel(static_cast<QProcess::ProcessChannel>(chan));
@@ -262,7 +263,7 @@ void QProcessWrapper::closeWriteChannel()
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::QProcessCloseWriteChannel));
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessCloseWriteChannel));
m_lock.unlock();
} else {
process.closeWriteChannel();
@@ -295,7 +296,7 @@ void QProcessWrapper::kill()
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::QProcessKill));
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessKill));
m_lock.unlock();
} else {
process.kill();
@@ -342,7 +343,7 @@ void QProcessWrapper::start(const QString &param1, const QStringList &param2,
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::QProcessStart3Arg), param1, param2, param3);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessStart3Arg), param1, param2, param3);
m_lock.unlock();
} else {
process.start(param1, param2, param3);
@@ -353,10 +354,10 @@ void QProcessWrapper::start(const QString &param1, QIODevice::OpenMode param2)
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::QProcessStart2Arg), param1, param2);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessStart2Arg), param1, param2);
m_lock.unlock();
} else {
- process.start(param1, param2);
+ process.start(param1, {}, param2);
}
}
@@ -375,7 +376,7 @@ void QProcessWrapper::terminate()
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::QProcessTerminate));
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessTerminate));
m_lock.unlock();
} else {
process.terminate();
@@ -442,7 +443,7 @@ void QProcessWrapper::setEnvironment(const QStringList &param1)
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::QProcessSetEnvironment), param1, dummy);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessSetEnvironment), param1);
m_lock.unlock();
} else {
process.setEnvironment(param1);
@@ -454,7 +455,7 @@ void QProcessWrapper::setNativeArguments(const QString &param1)
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::QProcessSetNativeArguments), param1, dummy);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessSetNativeArguments), param1);
m_lock.unlock();
} else {
process.setNativeArguments(param1);
@@ -466,7 +467,7 @@ void QProcessWrapper::setWorkingDirectory(const QString &param1)
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::QProcessSetWorkingDirectory), param1, dummy);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessSetWorkingDirectory), param1);
m_lock.unlock();
} else {
process.setWorkingDirectory(param1);
diff --git a/src/libs/installer/qprocesswrapper.h b/src/libs/installer/qprocesswrapper.h
index 1a68bbf92..b34d6f82c 100644
--- a/src/libs/installer/qprocesswrapper.h
+++ b/src/libs/installer/qprocesswrapper.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -119,7 +119,7 @@ Q_SIGNALS:
void bytesWritten(qint64);
void aboutToClose();
void readChannelFinished();
- void error(QProcess::ProcessError);
+ void errorOccurred(QProcess::ProcessError);
void readyReadStandardOutput();
void readyReadStandardError();
void finished(int exitCode, QProcess::ExitStatus exitStatus);
diff --git a/src/libs/installer/qsettingswrapper.cpp b/src/libs/installer/qsettingswrapper.cpp
index f2dd53767..d322728f5 100644
--- a/src/libs/installer/qsettingswrapper.cpp
+++ b/src/libs/installer/qsettingswrapper.cpp
@@ -141,27 +141,39 @@ QString QSettingsWrapper::applicationName() const
return d->settings.applicationName();
}
-void QSettingsWrapper::beginGroup(const QString &param1)
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
+ void QSettingsWrapper::beginGroup(const QString &prefix)
+#else
+ void QSettingsWrapper::beginGroup(QAnyStringView prefix)
+#endif
{
if (createSocket())
- callRemoteMethod(QLatin1String(Protocol::QSettingsBeginGroup), param1, dummy);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsBeginGroup), prefix);
else
- d->settings.beginGroup(param1);
+ d->settings.beginGroup(prefix);
}
-int QSettingsWrapper::beginReadArray(const QString &param1)
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
+ int QSettingsWrapper::beginReadArray(const QString &prefix)
+#else
+ int QSettingsWrapper::beginReadArray(QAnyStringView prefix)
+#endif
{
if (createSocket())
- return callRemoteMethod<qint32>(QLatin1String(Protocol::QSettingsBeginReadArray), param1);
- return d->settings.beginReadArray(param1);
+ return callRemoteMethod<qint32>(QLatin1String(Protocol::QSettingsBeginReadArray), prefix);
+ return d->settings.beginReadArray(prefix);
}
-void QSettingsWrapper::beginWriteArray(const QString &param1, int param2)
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
+ void QSettingsWrapper::beginWriteArray(const QString &prefix, int size)
+#else
+ void QSettingsWrapper::beginWriteArray(QAnyStringView prefix, int size)
+#endif
{
if (createSocket())
- callRemoteMethod(QLatin1String(Protocol::QSettingsBeginWriteArray), param1, qint32(param2));
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsBeginWriteArray), prefix, qint32(size));
else
- d->settings.beginWriteArray(param1, param2);
+ d->settings.beginWriteArray(prefix, size);
}
QStringList QSettingsWrapper::childGroups() const
@@ -181,21 +193,25 @@ QStringList QSettingsWrapper::childKeys() const
void QSettingsWrapper::clear()
{
if (createSocket())
- callRemoteMethod(QLatin1String(Protocol::QSettingsClear));
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsClear));
else d->settings.clear();
}
-bool QSettingsWrapper::contains(const QString &param1) const
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
+ bool QSettingsWrapper::contains(const QString &key) const
+#else
+ bool QSettingsWrapper::contains(QAnyStringView key) const
+#endif
{
if (createSocket())
- return callRemoteMethod<bool>(QLatin1String(Protocol::QSettingsContains), param1);
- return d->settings.contains(param1);
+ return callRemoteMethod<bool>(QLatin1String(Protocol::QSettingsContains), key);
+ return d->settings.contains(key);
}
void QSettingsWrapper::endArray()
{
if (createSocket())
- callRemoteMethod(QLatin1String(Protocol::QSettingsEndArray));
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsEndArray));
else
d->settings.endArray();
}
@@ -203,7 +219,7 @@ void QSettingsWrapper::endArray()
void QSettingsWrapper::endGroup()
{
if (createSocket())
- callRemoteMethod(QLatin1String(Protocol::QSettingsEndGroup));
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsEndGroup));
else
d->settings.endGroup();
}
@@ -249,12 +265,16 @@ QString QSettingsWrapper::organizationName() const
return d->settings.organizationName();
}
-void QSettingsWrapper::remove(const QString &param1)
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
+ void QSettingsWrapper::remove(const QString &key)
+#else
+ void QSettingsWrapper::remove(QAnyStringView key)
+#endif
{
if (createSocket())
- callRemoteMethod(QLatin1String(Protocol::QSettingsRemove), param1, dummy);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsRemove), key);
else
- d->settings.remove(param1);
+ d->settings.remove(key);
}
QSettingsWrapper::Scope QSettingsWrapper::scope() const
@@ -266,7 +286,7 @@ QSettingsWrapper::Scope QSettingsWrapper::scope() const
void QSettingsWrapper::setArrayIndex(int param1)
{
if (createSocket())
- callRemoteMethod(QLatin1String(Protocol::QSettingsSetArrayIndex), qint32(param1), dummy);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsSetArrayIndex), qint32(param1));
else
d->settings.setArrayIndex(param1);
}
@@ -274,17 +294,20 @@ void QSettingsWrapper::setArrayIndex(int param1)
void QSettingsWrapper::setFallbacksEnabled(bool param1)
{
if (createSocket())
- callRemoteMethod(QLatin1String(Protocol::QSettingsSetFallbacksEnabled), param1, dummy);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsSetFallbacksEnabled), param1);
else
d->settings.setFallbacksEnabled(param1);
}
-
-void QSettingsWrapper::setValue(const QString &param1, const QVariant &param2)
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
+void QSettingsWrapper::setValue(const QString &key, const QVariant &value)
+#else
+void QSettingsWrapper::setValue(QAnyStringView key, const QVariant &value)
+#endif
{
if (createSocket())
- callRemoteMethod(QLatin1String(Protocol::QSettingsSetValue), param1, param2);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsSetValue), key, value);
else
- d->settings.setValue(param1, param2);
+ d->settings.setValue(key, value);
}
QSettingsWrapper::Status QSettingsWrapper::status() const
@@ -299,17 +322,30 @@ QSettingsWrapper::Status QSettingsWrapper::status() const
void QSettingsWrapper::sync()
{
if (createSocket())
- callRemoteMethod(QLatin1String(Protocol::QSettingsSync));
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsSync));
else
d->settings.sync();
}
-QVariant QSettingsWrapper::value(const QString &param1, const QVariant &param2) const
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
+QVariant QSettingsWrapper::value(const QString &key, const QVariant &value) const
+#else
+QVariant QSettingsWrapper::value(QAnyStringView key, const QVariant &value) const
+#endif
+{
+ if (createSocket())
+ return callRemoteMethod<QVariant>(QLatin1String(Protocol::QSettingsValue), key, value);
+ return d->settings.value(key, value);
+}
+
+#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
+QVariant QSettingsWrapper::value(QAnyStringView key) const
{
if (createSocket())
- return callRemoteMethod<QVariant>(QLatin1String(Protocol::QSettingsValue), param1, param2);
- return d->settings.value(param1, param2);
+ return callRemoteMethod<QVariant>(QLatin1String(Protocol::QSettingsValue), key);
+ return d->settings.value(key);
}
+#endif
// -- private
diff --git a/src/libs/installer/qsettingswrapper.h b/src/libs/installer/qsettingswrapper.h
index 92221c117..f5d428b1e 100644
--- a/src/libs/installer/qsettingswrapper.h
+++ b/src/libs/installer/qsettingswrapper.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -36,6 +36,8 @@
#include <QSettings>
+QT_FORWARD_DECLARE_CLASS(QTextCodec)
+
namespace QInstaller {
class INSTALLER_EXPORT QSettingsWrapper : public RemoteObject
@@ -68,12 +70,21 @@ public:
void sync();
Status status() const;
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
void beginGroup(const QString &prefix);
+#else
+ void beginGroup(QAnyStringView prefix);
+#endif
void endGroup();
QString group() const;
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
int beginReadArray(const QString &prefix);
void beginWriteArray(const QString &prefix, int size = -1);
+#else
+ int beginReadArray(QAnyStringView prefix);
+ void beginWriteArray(QAnyStringView prefix, int size = -1);
+#endif
void endArray();
void setArrayIndex(int i);
@@ -82,12 +93,22 @@ public:
QStringList childGroups() const;
bool isWritable() const;
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
void setValue(const QString &key, const QVariant &value);
QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
+#else
+ void setValue(QAnyStringView key, const QVariant &value);
+ QVariant value(QAnyStringView key, const QVariant &defaultValue) const;
+ QVariant value(QAnyStringView key) const;
+#endif
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
void remove(const QString &key);
bool contains(const QString &key) const;
-
+#else
+ void remove(QAnyStringView key);
+ bool contains(QAnyStringView key) const;
+#endif
void setFallbacksEnabled(bool b);
bool fallbacksEnabled() const;
@@ -105,14 +126,13 @@ private: // we cannot support the following functionality
: RemoteObject(QLatin1String(Protocol::QSettings), parent)
{}
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
void setIniCodec(QTextCodec * /*codec*/);
void setIniCodec(const char * /*codecName*/);
QTextCodec *iniCodec() const { return 0; }
-
+#endif
static void setDefaultFormat(QSettings::Format /*format*/);
static QSettings::Format defaultFormat() { return QSettings::NativeFormat; }
- static void setSystemIniPath(const QString & /*dir*/);
- static void setUserIniPath(const QString & /*dir*/);
static void setPath(QSettings::Format /*format*/, Scope /*scope*/, const QString & /*path*/);
typedef QMap<QString, QVariant> SettingsMap;
diff --git a/src/libs/installer/qtpatch.cpp b/src/libs/installer/qtpatch.cpp
index f72e67867..a29e99f94 100644
--- a/src/libs/installer/qtpatch.cpp
+++ b/src/libs/installer/qtpatch.cpp
@@ -67,11 +67,11 @@ QHash<QString, QByteArray> QtPatch::qmakeValues(const QString &qmakePath, QByteA
QFileInfo qmake(qmakePath);
if (!qmake.exists()) {
- qmakeOutput->append(QString::fromLatin1("%1 is not existing").arg(qmakePath));
+ qmakeOutput->append(QString::fromLatin1("%1 is not existing").arg(qmakePath).toUtf8());
return qmakeValueHash;
}
if (!qmake.isExecutable()) {
- qmakeOutput->append(QString::fromLatin1("%1 is not executable").arg(qmakePath));
+ qmakeOutput->append(QString::fromLatin1("%1 is not executable").arg(qmakePath).toUtf8());
return qmakeValueHash;
}
@@ -88,7 +88,7 @@ QHash<QString, QByteArray> QtPatch::qmakeValues(const QString &qmakePath, QByteA
, QString::fromLatin1("Standard output: \"%1\".").arg(QLatin1String(output))
, QString::fromLatin1("Error output: \"%1\".").arg(QLatin1String(process.readAllStandardError()))
};
- qmakeOutput->append(detailedOutput.join(QLatin1Char('\n')));
+ qmakeOutput->append(detailedOutput.join(QLatin1Char('\n')).toUtf8());
return qmakeValueHash;
}
qmakeOutput->append(output);
diff --git a/src/libs/installer/registerfiletypeoperation.cpp b/src/libs/installer/registerfiletypeoperation.cpp
index 1754b664d..852714dfb 100644
--- a/src/libs/installer/registerfiletypeoperation.cpp
+++ b/src/libs/installer/registerfiletypeoperation.cpp
@@ -153,7 +153,7 @@ bool RegisterFileTypeOperation::undoOperation()
{
#ifdef Q_OS_WIN
ensureOptionalArgumentsRead();
- if (parseUndoOperationArguments().count() > 0)
+ if (skipUndoOperation())
return true;
QStringList args = arguments();
diff --git a/src/libs/installer/remoteclient_p.h b/src/libs/installer/remoteclient_p.h
index e1809e0af..3dec97c95 100644
--- a/src/libs/installer/remoteclient_p.h
+++ b/src/libs/installer/remoteclient_p.h
@@ -40,8 +40,8 @@
#include <QCoreApplication>
#include <QDeadlineTimer>
+#include <QRecursiveMutex>
#include <QTimer>
-#include <QMutex>
#include <QThread>
namespace QInstaller {
@@ -55,7 +55,6 @@ public:
RemoteClientPrivate(RemoteClient *parent)
: RemoteObject(QLatin1String("RemoteClientPrivate"))
, q_ptr(parent)
- , m_mutex(QMutex::Recursive)
, m_startServerAs(Protocol::StartAs::User)
, m_serverStarted(false)
, m_active(false)
@@ -201,7 +200,7 @@ public:
private:
RemoteClient *q_ptr;
- QMutex m_mutex;
+ QRecursiveMutex m_mutex;
QString m_socketName;
Protocol::StartAs m_startServerAs;
bool m_serverStarted;
diff --git a/src/libs/installer/remotefileengine.cpp b/src/libs/installer/remotefileengine.cpp
index b600da618..7a5e91682 100644
--- a/src/libs/installer/remotefileengine.cpp
+++ b/src/libs/installer/remotefileengine.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -55,10 +55,10 @@ QAbstractFileEngine* RemoteFileEngineHandler::create(const QString &fileName) co
if (fileName.isEmpty() || fileName.startsWith(QLatin1String(":")))
return 0; // empty filename or Qt resource
- QScopedPointer<RemoteFileEngine> client(new RemoteFileEngine());
+ std::unique_ptr<RemoteFileEngine> client(new RemoteFileEngine());
client->setFileName(fileName);
if (client->isConnectedToServer())
- return client.take();
+ return client.release();
return 0;
}
@@ -313,6 +313,7 @@ bool RemoteFileEngine::link(const QString &newName)
/*!
\reimp
*/
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
bool RemoteFileEngine::mkdir(const QString &dirName, bool createParentDirectories) const
{
if ((const_cast<RemoteFileEngine *>(this))->connectToServer()) {
@@ -320,18 +321,38 @@ bool RemoteFileEngine::mkdir(const QString &dirName, bool createParentDirectorie
dirName, createParentDirectories);
}
return m_fileEngine.mkdir(dirName, createParentDirectories);
+
+}
+#else
+bool RemoteFileEngine::mkdir(const QString &dirName, bool createParentDirectories,
+ std::optional<QFile::Permissions> permissions) const
+{
+ if ((const_cast<RemoteFileEngine *>(this))->connectToServer()) {
+ return callRemoteMethod<bool>(QString::fromLatin1(Protocol::QAbstractFileEngineMkdir),
+ dirName, createParentDirectories);
+ }
+ return m_fileEngine.mkdir(dirName, createParentDirectories, permissions);
}
+#endif
/*!
\reimp
*/
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
bool RemoteFileEngine::open(QIODevice::OpenMode mode)
+#else
+bool RemoteFileEngine::open(QIODevice::OpenMode mode, std::optional<QFile::Permissions> permissions)
+#endif
{
if (connectToServer()) {
return callRemoteMethod<bool>(QString::fromLatin1(Protocol::QAbstractFileEngineOpen),
static_cast<qint32>(mode | QIODevice::Unbuffered));
}
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
return m_fileEngine.open(mode | QIODevice::Unbuffered);
+#else
+ return m_fileEngine.open(mode | QIODevice::Unbuffered, permissions);
+#endif
}
/*!
@@ -418,8 +439,7 @@ bool RemoteFileEngine::seek(qint64 offset)
void RemoteFileEngine::setFileName(const QString &fileName)
{
if (connectToServer()) {
- callRemoteMethod(QString::fromLatin1(Protocol::QAbstractFileEngineSetFileName), fileName,
- dummy);
+ callRemoteMethodDefaultReply(QString::fromLatin1(Protocol::QAbstractFileEngineSetFileName), fileName);
}
m_fileEngine.setFileName(fileName);
}
diff --git a/src/libs/installer/remotefileengine.h b/src/libs/installer/remotefileengine.h
index 35ebf7742..31ae67d77 100644
--- a/src/libs/installer/remotefileengine.h
+++ b/src/libs/installer/remotefileengine.h
@@ -53,7 +53,12 @@ public:
RemoteFileEngine();
~RemoteFileEngine();
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
bool open(QIODevice::OpenMode mode) override;
+#else
+ bool open(QIODevice::OpenMode mode,
+ std::optional<QFile::Permissions> permissions = std::nullopt) override;
+#endif
bool close() override;
bool flush() override;
bool syncToDisk() override;
@@ -66,7 +71,12 @@ public:
bool rename(const QString &newName) override;
bool renameOverwrite(const QString &newName) override;
bool link(const QString &newName) override;
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
bool mkdir(const QString &dirName, bool createParentDirectories) const override;
+#else
+ bool mkdir(const QString &dirName, bool createParentDirectories,
+ std::optional<QFile::Permissions> permissions = std::nullopt) const override;
+#endif
bool rmdir(const QString &dirName, bool recurseParentDirectories) const override;
bool setSize(qint64 size) override;
bool caseSensitive() const override;
diff --git a/src/libs/installer/remoteobject.cpp b/src/libs/installer/remoteobject.cpp
index 70ab48af3..b4dd0cbb7 100644
--- a/src/libs/installer/remoteobject.cpp
+++ b/src/libs/installer/remoteobject.cpp
@@ -46,7 +46,6 @@ namespace QInstaller {
RemoteObject::RemoteObject(const QString &wrappedType, QObject *parent)
: QObject(parent)
- , dummy(nullptr)
, m_type(wrappedType)
, m_socket(nullptr)
{
@@ -140,10 +139,4 @@ bool RemoteObject::isConnectedToServer() const
return false;
}
-void RemoteObject::callRemoteMethod(const QString &name)
-{
- const QString reply = sendReceivePacket<QString>(name, dummy, dummy, dummy);
- Q_ASSERT(reply == QLatin1String(Protocol::DefaultReply));
-}
-
} // namespace QInstaller
diff --git a/src/libs/installer/remoteobject.h b/src/libs/installer/remoteobject.h
index 4dce2a218..ddd512588 100644
--- a/src/libs/installer/remoteobject.h
+++ b/src/libs/installer/remoteobject.h
@@ -35,8 +35,10 @@
#include <QCoreApplication>
#include <QDataStream>
-#include <QObject>
#include <QLocalSocket>
+#include <QObject>
+#include <QVariant>
+
namespace QInstaller {
@@ -50,93 +52,58 @@ public:
virtual ~RemoteObject() = 0;
bool isConnectedToServer() const;
- void callRemoteMethod(const QString &name);
-
- template<typename T1, typename T2>
- void callRemoteMethod(const QString &name, const T1 &arg, const T2 &arg2)
- {
- const QString reply = sendReceivePacket<QString>(name, arg, arg2, dummy);
- Q_ASSERT(reply == QLatin1String(Protocol::DefaultReply));
- }
- template<typename T1, typename T2, typename T3>
- void callRemoteMethod(const QString &name, const T1 &arg, const T2 &arg2, const T3 & arg3)
+ template<typename... Args>
+ void callRemoteMethodDefaultReply(const QString &name, const Args&... args)
{
- const QString reply = sendReceivePacket<QString>(name, arg, arg2, arg3);
+ const QString reply = sendReceivePacket<QString>(name, args...);
Q_ASSERT(reply == QLatin1String(Protocol::DefaultReply));
}
- template<typename T>
- T callRemoteMethod(const QString &name) const
- {
- return sendReceivePacket<T>(name, dummy, dummy, dummy);
- }
-
- template<typename T, typename T1>
- T callRemoteMethod(const QString &name, const T1 &arg) const
- {
- return sendReceivePacket<T>(name, arg, dummy, dummy);
- }
-
- template<typename T, typename T1, typename T2>
- T callRemoteMethod(const QString &name, const T1 & arg, const T2 &arg2) const
- {
- return sendReceivePacket<T>(name, arg, arg2, dummy);
- }
-
- template<typename T, typename T1, typename T2, typename T3>
- T callRemoteMethod(const QString &name, const T1 &arg, const T2 &arg2, const T3 &arg3) const
+ template<typename T, typename... Args>
+ T callRemoteMethod(const QString &name, const Args&... args) const
{
- return sendReceivePacket<T>(name, arg, arg2, arg3);
+ return sendReceivePacket<T>(name, args...);
}
protected:
bool authorize();
bool connectToServer(const QVariantList &arguments = QVariantList());
- // Use this structure to allow derived classes to manipulate the template
- // function signature of the callRemoteMethod templates, since most of the
- // generated functions will differ in return type rather given arguments.
- struct Dummy {}; Dummy *dummy;
-
private:
- template<typename T> bool isValueType(T) const
- {
- return true;
- }
- template<typename T> bool isValueType(T *dummy) const
+ template<typename T, typename... Args>
+ T sendReceivePacket(const QString &name, const Args&... args) const
{
- // Force compiler error while passing anything different then Dummy* to the function.
- // It really doesn't make sense to send any pointer over to the server, so bail early.
- Q_UNUSED(static_cast<Dummy*> (dummy))
- return false;
- }
-
- template<typename T, typename T1, typename T2, typename T3>
- T sendReceivePacket(const QString &name, const T1 &arg, const T2 &arg2, const T3 &arg3) const
- {
- writeData(name, arg, arg2, arg3);
+ writeData(name, args...);
while (m_socket->bytesToWrite())
m_socket->waitForBytesWritten();
return readData<T>(name);
}
+ template <class T> int writeObject(QDataStream& out, const T& t) const
+ {
+ static_assert(!std::is_pointer<T>::value, "Pointer passed to remote server");
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ out << t;
+#else
+ if constexpr (std::is_same<T, QAnyStringView>::value)
+ out << t.toString();
+ else
+ out << t;
+#endif
+
+ return 0;
+ }
- template<typename T1, typename T2, typename T3>
- void writeData(const QString &name, const T1 &arg, const T2 &arg2, const T3 &arg3) const
+ template<typename... Args>
+ void writeData(const QString &name, const Args&... args) const
{
QByteArray data;
QDataStream out(&data, QIODevice::WriteOnly);
- if (isValueType(arg))
- out << arg;
- if (isValueType(arg2))
- out << arg2;
- if (isValueType(arg3))
- out << arg3;
-
+ (void)std::initializer_list<int>{writeObject(out, args)...};
sendPacket(m_socket, name.toLatin1(), data);
m_socket->flush();
}
diff --git a/src/libs/installer/remoteserverconnection.cpp b/src/libs/installer/remoteserverconnection.cpp
index 05446ec19..51619983e 100644
--- a/src/libs/installer/remoteserverconnection.cpp
+++ b/src/libs/installer/remoteserverconnection.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -278,7 +278,7 @@ void RemoteServerConnection::handleQProcess(RemoteServerReply *reply, const QStr
qint64 pid = -1;
bool success = QInstaller::startDetached(program, arguments, workingDirectory, &pid);
- reply->send(qMakePair< bool, qint64>(success, pid));
+ reply->send(QPair<bool, qint64>(success, pid));
} else if (command == QLatin1String(Protocol::QProcessStartDetached2)) {
QString program;
QStringList arguments;
@@ -289,7 +289,7 @@ void RemoteServerConnection::handleQProcess(RemoteServerReply *reply, const QStr
qint64 pid = -1;
bool success = QProcess::startDetached(program, arguments, workingDirectory, &pid);
- reply->send(qMakePair< bool, qint64>(success, pid));
+ reply->send(QPair<bool, qint64>(success, pid));
} else if (command == QLatin1String(Protocol::QProcessSetWorkingDirectory)) {
QString dir;
data >> dir;
@@ -313,7 +313,7 @@ void RemoteServerConnection::handleQProcess(RemoteServerReply *reply, const QStr
qint32 mode;
data >> program;
data >> mode;
- m_process->start(program, static_cast<QIODevice::OpenMode> (mode));
+ m_process->start(program, {}, static_cast<QIODevice::OpenMode> (mode));
} else if (command == QLatin1String(Protocol::QProcessState)) {
reply->send(static_cast<qint32> (m_process->state()));
} else if (command == QLatin1String(Protocol::QProcessTerminate)) {
@@ -429,7 +429,8 @@ void RemoteServerConnection::handleQSettings(RemoteServerReply *reply, const QSt
QString key;
QVariant defaultValue;
data >> key;
- data >> defaultValue;
+ if (!data.atEnd())
+ data >> defaultValue;
reply->send(settings->value(key, defaultValue));
} else if (command == QLatin1String(Protocol::QSettingsOrganizationName)) {
reply->send(settings->organizationName());
@@ -497,11 +498,20 @@ void RemoteServerConnection::handleQFSFileEngine(RemoteServerReply *reply, const
bool createParentDirectories;
data >>dirName;
data >>createParentDirectories;
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
reply->send(m_engine->mkdir(dirName, createParentDirectories));
+#else
+ reply->send(m_engine->mkdir(dirName, createParentDirectories, std::nullopt));
+#endif
+
} else if (command == QLatin1String(Protocol::QAbstractFileEngineOpen)) {
qint32 openMode;
data >>openMode;
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
reply->send(m_engine->open(static_cast<QIODevice::OpenMode> (openMode)));
+#else
+ reply->send(m_engine->open(static_cast<QIODevice::OpenMode> (openMode), std::nullopt));
+#endif
} else if (command == QLatin1String(Protocol::QAbstractFileEngineOwner)) {
qint32 owner;
data >>owner;
@@ -517,13 +527,13 @@ void RemoteServerConnection::handleQFSFileEngine(RemoteServerReply *reply, const
data >> maxlen;
QByteArray byteArray(maxlen, '\0');
const qint64 r = m_engine->read(byteArray.data(), maxlen);
- reply->send(qMakePair<qint64, QByteArray>(r, byteArray));
+ reply->send(QPair<qint64, QByteArray>(r, byteArray));
} else if (command == QLatin1String(Protocol::QAbstractFileEngineReadLine)) {
qint64 maxlen;
data >> maxlen;
QByteArray byteArray(maxlen, '\0');
const qint64 r = m_engine->readLine(byteArray.data(), maxlen);
- reply->send(qMakePair<qint64, QByteArray>(r, byteArray));
+ reply->send(QPair<qint64, QByteArray>(r, byteArray));
} else if (command == QLatin1String(Protocol::QAbstractFileEngineRemove)) {
reply->send(m_engine->remove());
} else if (command == QLatin1String(Protocol::QAbstractFileEngineRename)) {
diff --git a/src/libs/installer/remoteserverconnection_p.h b/src/libs/installer/remoteserverconnection_p.h
index 977a64711..09e6de7d7 100644
--- a/src/libs/installer/remoteserverconnection_p.h
+++ b/src/libs/installer/remoteserverconnection_p.h
@@ -52,7 +52,7 @@ private:
connect(process, &QIODevice::bytesWritten, this, &QProcessSignalReceiver::onBytesWritten);
connect(process, &QIODevice::aboutToClose, this, &QProcessSignalReceiver::onAboutToClose);
connect(process, &QIODevice::readChannelFinished, this, &QProcessSignalReceiver::onReadChannelFinished);
- connect(process, SIGNAL(error(QProcess::ProcessError)),
+ connect(process, SIGNAL(errorOccurred(QProcess::ProcessError)),
SLOT(onError(QProcess::ProcessError)));
connect(process, &QProcess::readyReadStandardOutput,
this, &QProcessSignalReceiver::onReadyReadStandardOutput);
diff --git a/src/libs/installer/repository.cpp b/src/libs/installer/repository.cpp
index ddbf3b382..f7035e732 100644
--- a/src/libs/installer/repository.cpp
+++ b/src/libs/installer/repository.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -35,7 +35,7 @@
#include <QDir>
/*!
- \fn inline uint QInstaller::qHash(const Repository &repository)
+ \fn inline hashValue QInstaller::qHash(const Repository &repository)
Returns a hash of the \a repository.
*/
@@ -49,6 +49,7 @@ Repository::Repository()
: m_default(false)
, m_enabled(false)
, m_compressed(false)
+ , m_postLoadComponentScript(false)
{
}
@@ -62,8 +63,10 @@ Repository::Repository(const Repository &other)
, m_username(other.m_username)
, m_password(other.m_password)
, m_displayname(other.m_displayname)
- , m_compressed(other.m_compressed)
, m_categoryname(other.m_categoryname)
+ , m_compressed(other.m_compressed)
+ , m_xmlChecksum(other.m_xmlChecksum)
+ , m_postLoadComponentScript(other.m_postLoadComponentScript)
{
}
@@ -76,6 +79,7 @@ Repository::Repository(const QUrl &url, bool isDefault, bool compressed)
, m_default(isDefault)
, m_enabled(true)
, m_compressed(compressed)
+ , m_postLoadComponentScript(false)
{
}
@@ -221,6 +225,28 @@ void Repository::setCategoryName(const QString &categoryname)
}
/*!
+ Returns the expected checksum of the repository, which is the checksum
+ calculated from the \c Updates.xml document at the root of the repository.
+
+ This value is used as a hint when looking for already fetched repositories
+ from the local cache. If the installer has cached a repository with a matching
+ checksum, it can skip downloading the \c Updates.xml file for that repository again.
+*/
+QByteArray Repository::xmlChecksum() const
+{
+ return m_xmlChecksum;
+}
+
+/*!
+ Sets the expected checksum of the repository to \c checksum. The checksum
+ is calculated from the \c Updates.xml document at the root of the repository.
+*/
+void Repository::setXmlChecksum(const QByteArray &checksum)
+{
+ m_xmlChecksum = checksum;
+}
+
+/*!
Returns true if repository is compressed
*/
bool Repository::isCompressed() const
@@ -229,13 +255,31 @@ bool Repository::isCompressed() const
}
/*!
+ \internal
+*/
+bool Repository::postLoadComponentScript() const
+{
+ return m_postLoadComponentScript;
+}
+
+/*!
+ \internal
+*/
+void Repository::setPostLoadComponentScript(const bool postLoad)
+{
+ m_postLoadComponentScript = postLoad;
+}
+
+/*!
Compares the values of this repository to \a other and returns true if they are equal (same server,
default state, enabled state as well as username and password). \sa operator!=()
*/
bool Repository::operator==(const Repository &other) const
{
return m_url == other.m_url && m_default == other.m_default && m_enabled == other.m_enabled
- && m_username == other.m_username && m_password == other.m_password && m_displayname == other.m_displayname;
+ && m_username == other.m_username && m_password == other.m_password
+ && m_displayname == other.m_displayname && m_xmlChecksum == other.m_xmlChecksum
+ && m_postLoadComponentScript == other.m_postLoadComponentScript;
}
/*!
@@ -263,6 +307,8 @@ const Repository &Repository::operator=(const Repository &other)
m_displayname = other.m_displayname;
m_compressed = other.m_compressed;
m_categoryname = other.m_categoryname;
+ m_xmlChecksum = other.m_xmlChecksum;
+ m_postLoadComponentScript = other.m_postLoadComponentScript;
return *this;
}
@@ -270,7 +316,9 @@ const Repository &Repository::operator=(const Repository &other)
void Repository::registerMetaType()
{
qRegisterMetaType<Repository>("Repository");
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
qRegisterMetaTypeStreamOperators<Repository>("Repository");
+#endif
}
/*!
@@ -280,7 +328,7 @@ QDataStream &operator>>(QDataStream &istream, Repository &repository)
{
QByteArray url, username, password, displayname, compressed;
istream >> url >> repository.m_default >> repository.m_enabled >> username >> password
- >> displayname >> repository.m_categoryname;
+ >> displayname >> repository.m_categoryname >> repository.m_xmlChecksum >> repository.m_postLoadComponentScript;
repository.setUrl(QUrl::fromEncoded(QByteArray::fromBase64(url)));
repository.setUsername(QString::fromUtf8(QByteArray::fromBase64(username)));
repository.setPassword(QString::fromUtf8(QByteArray::fromBase64(password)));
@@ -295,7 +343,8 @@ QDataStream &operator<<(QDataStream &ostream, const Repository &repository)
{
return ostream << repository.m_url.toEncoded().toBase64() << repository.m_default << repository.m_enabled
<< repository.m_username.toUtf8().toBase64() << repository.m_password.toUtf8().toBase64()
- << repository.m_displayname.toUtf8().toBase64() << repository.m_categoryname.toUtf8().toBase64();
+ << repository.m_displayname.toUtf8().toBase64() << repository.m_categoryname.toUtf8().toBase64()
+ << repository.m_xmlChecksum.toBase64() << repository.m_postLoadComponentScript;
}
}
diff --git a/src/libs/installer/repository.h b/src/libs/installer/repository.h
index 3f28e4d99..0a589a43e 100644
--- a/src/libs/installer/repository.h
+++ b/src/libs/installer/repository.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -67,11 +67,17 @@ public:
QString categoryname() const;
void setCategoryName(const QString &categoryname);
+ QByteArray xmlChecksum() const;
+ void setXmlChecksum(const QByteArray &checksum);
+
bool isCompressed() const;
+ bool postLoadComponentScript() const;
+ void setPostLoadComponentScript(const bool postLoad);
+
bool operator==(const Repository &other) const;
bool operator!=(const Repository &other) const;
- uint qHash(const Repository &repository);
+ hashValue qHash(const Repository &repository);
const Repository &operator=(const Repository &other);
friend INSTALLER_EXPORT QDataStream &operator>>(QDataStream &istream, Repository &repository);
@@ -86,9 +92,11 @@ private:
QString m_displayname;
QString m_categoryname;
bool m_compressed;
+ QByteArray m_xmlChecksum;
+ bool m_postLoadComponentScript;
};
-inline uint qHash(const Repository &repository)
+inline hashValue qHash(const Repository &repository)
{
return qHash(repository.url());
}
diff --git a/src/libs/installer/repositorycategory.cpp b/src/libs/installer/repositorycategory.cpp
index e651b7f0c..c9bee6e3a 100644
--- a/src/libs/installer/repositorycategory.cpp
+++ b/src/libs/installer/repositorycategory.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -35,7 +35,7 @@
#include <QStringList>
/*!
- \fn inline uint QInstaller::qHash(const RepositoryCategory &repository)
+ \fn inline hashValue QInstaller::qHash(const RepositoryCategory &repository)
Returns a hash of the repository category \a repository.
*/
@@ -65,8 +65,8 @@ RepositoryCategory::RepositoryCategory()
Constructs a new category by using all fields of the given category \a other.
*/
RepositoryCategory::RepositoryCategory(const RepositoryCategory &other)
- : m_displayname(other.m_displayname), m_data(other.m_data), m_enabled(other.m_enabled),
- m_tooltip(other.m_tooltip)
+ : m_data(other.m_data), m_displayname(other.m_displayname), m_tooltip(other.m_tooltip),
+ m_enabled(other.m_enabled)
{
registerMetaType();
}
@@ -75,7 +75,9 @@ RepositoryCategory::RepositoryCategory(const RepositoryCategory &other)
void RepositoryCategory::registerMetaType()
{
qRegisterMetaType<RepositoryCategory>("RepositoryCategory");
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
qRegisterMetaTypeStreamOperators<RepositoryCategory>("RepositoryCategory");
+#endif
}
/*!
diff --git a/src/libs/installer/repositorycategory.h b/src/libs/installer/repositorycategory.h
index dc45527eb..993ae78aa 100644
--- a/src/libs/installer/repositorycategory.h
+++ b/src/libs/installer/repositorycategory.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -31,6 +31,7 @@
#include "installer_global.h"
#include "repository.h"
+#include "qinstallerglobal.h"
#include <QtCore/QMetaType>
#include <QtCore/QUrl>
@@ -63,7 +64,7 @@ public:
bool operator==(const RepositoryCategory &other) const;
bool operator!=(const RepositoryCategory &other) const;
- uint qHash(const RepositoryCategory &repository);
+ hashValue qHash(const RepositoryCategory &repository);
friend INSTALLER_EXPORT QDataStream &operator>>(QDataStream &istream, RepositoryCategory &repository);
friend INSTALLER_EXPORT QDataStream &operator<<(QDataStream &ostream, const RepositoryCategory &repository);
@@ -75,7 +76,7 @@ private:
bool m_enabled;
};
-inline uint qHash(const RepositoryCategory &repository)
+inline hashValue qHash(const RepositoryCategory &repository)
{
return qHash(repository.displayname());
}
diff --git a/src/libs/installer/resources/installer.qrc b/src/libs/installer/resources/installer.qrc
index 48a7c65bd..a3855b5c4 100644
--- a/src/libs/installer/resources/installer.qrc
+++ b/src/libs/installer/resources/installer.qrc
@@ -7,5 +7,6 @@
<file>uninstall.png</file>
<file>keepinstalled.png</file>
<file>keepuninstalled.png</file>
+ <file>qt/etc/qt.conf</file>
</qresource>
</RCC>
diff --git a/src/libs/installer/resources/qt/etc/qt.conf b/src/libs/installer/resources/qt/etc/qt.conf
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/libs/installer/resources/qt/etc/qt.conf
diff --git a/src/libs/installer/runextensions.h b/src/libs/installer/runextensions.h
index e39ae6c81..9c7147141 100644
--- a/src/libs/installer/runextensions.h
+++ b/src/libs/installer/runextensions.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -37,384 +37,70 @@ QT_BEGIN_NAMESPACE
namespace QtConcurrent {
-template <typename T, typename FunctionPointer>
-class StoredInterfaceFunctionCall0 : public QRunnable
+template <typename T, typename FunctionPointer, typename... Args>
+class StoredInterfaceFunctionCall : public QRunnable
{
public:
- StoredInterfaceFunctionCall0(void (fn)(QFutureInterface<T> &))
- : fn(fn) { }
+ StoredInterfaceFunctionCall(void (fn)(QFutureInterface<T> &, Args...), const Args&&... args)
+ : m_fn(fn), m_args(std::make_tuple(std::forward<Args>(args)...)) { }
QFuture<T> start()
{
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
+ m_futureInterface.reportStarted();
+ QFuture<T> future = m_futureInterface.future();
QThreadPool::globalInstance()->start(this);
return future;
}
void run() override
{
- fn(futureInterface);
- futureInterface.reportFinished();
+ fn(m_futureInterface, std::forward<Args>(m_args)...);
+ m_futureInterface.reportFinished();
}
private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
-
-};
-template <typename T, typename FunctionPointer, typename Class>
-class StoredInterfaceMemberFunctionCall0 : public QRunnable
-{
-public:
- StoredInterfaceMemberFunctionCall0(void (Class::*fn)(QFutureInterface<T> &), Class *object)
- : fn(fn), object(object) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run() override
- {
- (object->*fn)(futureInterface);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Class *object;
-
-};
-
-template <typename T, typename FunctionPointer, typename Arg1>
-class StoredInterfaceFunctionCall1 : public QRunnable
-{
-public:
- StoredInterfaceFunctionCall1(void (fn)(QFutureInterface<T> &, Arg1), const Arg1 &arg1)
- : fn(fn), arg1(arg1) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run() override
- {
- fn(futureInterface, arg1);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Arg1 arg1;
-};
-template <typename T, typename FunctionPointer, typename Class, typename Arg1>
-class StoredInterfaceMemberFunctionCall1 : public QRunnable
-{
-public:
- StoredInterfaceMemberFunctionCall1(void (Class::*fn)(QFutureInterface<T> &, Arg1), Class *object, const Arg1 &arg1)
- : fn(fn), object(object), arg1(arg1) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run() override
- {
- (object->*fn)(futureInterface, arg1);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Class *object;
- Arg1 arg1;
-};
-
-template <typename T, typename FunctionPointer, typename Arg1, typename Arg2>
-class StoredInterfaceFunctionCall2 : public QRunnable
-{
-public:
- StoredInterfaceFunctionCall2(void (fn)(QFutureInterface<T> &, Arg1, Arg2), const Arg1 &arg1, const Arg2 &arg2)
- : fn(fn), arg1(arg1), arg2(arg2) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run() override
- {
- fn(futureInterface, arg1, arg2);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Arg1 arg1; Arg2 arg2;
+ QFutureInterface<T> m_futureInterface;
+ FunctionPointer m_fn;
+ std::tuple<Args...> m_args;
};
-template <typename T, typename FunctionPointer, typename Class, typename Arg1, typename Arg2>
-class StoredInterfaceMemberFunctionCall2 : public QRunnable
+template <typename T, typename FunctionPointer, typename Class, typename... Args>
+class StoredInterfaceMemberFunctionCall : public QRunnable
{
public:
- StoredInterfaceMemberFunctionCall2(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2), Class *object, const Arg1 &arg1, const Arg2 &arg2)
- : fn(fn), object(object), arg1(arg1), arg2(arg2) { }
+ StoredInterfaceMemberFunctionCall(void (Class::*fn)(QFutureInterface<T> &, Args...), Class *object, const Args&&... args)
+ : m_fn(fn), m_object(object), m_args(std::make_tuple(std::forward<Args>(args)...)) { }
QFuture<T> start()
{
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
+ m_futureInterface.reportStarted();
+ QFuture<T> future = m_futureInterface.future();
QThreadPool::globalInstance()->start(this);
return future;
}
void run() override
{
- (object->*fn)(futureInterface, arg1, arg2);
- futureInterface.reportFinished();
+ (m_object->*m_fn)(m_futureInterface, std::forward<Args>(m_args)...);
+ m_futureInterface.reportFinished();
}
private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Class *object;
- Arg1 arg1; Arg2 arg2;
+ QFutureInterface<T> m_futureInterface;
+ FunctionPointer m_fn;
+ Class *m_object;
+ std::tuple<Args...> m_args;
};
-template <typename T, typename FunctionPointer, typename Arg1, typename Arg2, typename Arg3>
-class StoredInterfaceFunctionCall3 : public QRunnable
+template <typename T, typename... Args>
+QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Args...), Args... args)
{
-public:
- StoredInterfaceFunctionCall3(void (fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3)
- : fn(fn), arg1(arg1), arg2(arg2), arg3(arg3) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run() override
- {
- fn(futureInterface, arg1, arg2, arg3);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Arg1 arg1; Arg2 arg2; Arg3 arg3;
-};
-template <typename T, typename FunctionPointer, typename Class, typename Arg1, typename Arg2, typename Arg3>
-class StoredInterfaceMemberFunctionCall3 : public QRunnable
-{
-public:
- StoredInterfaceMemberFunctionCall3(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3)
- : fn(fn), object(object), arg1(arg1), arg2(arg2), arg3(arg3) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run() override
- {
- (object->*fn)(futureInterface, arg1, arg2, arg3);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Class *object;
- Arg1 arg1; Arg2 arg2; Arg3 arg3;
-};
-
-template <typename T, typename FunctionPointer, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
-class StoredInterfaceFunctionCall4 : public QRunnable
-{
-public:
- StoredInterfaceFunctionCall4(void (fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4)
- : fn(fn), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run() override
- {
- fn(futureInterface, arg1, arg2, arg3, arg4);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Arg1 arg1; Arg2 arg2; Arg3 arg3; Arg4 arg4;
-};
-template <typename T, typename FunctionPointer, typename Class, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
-class StoredInterfaceMemberFunctionCall4 : public QRunnable
-{
-public:
- StoredInterfaceMemberFunctionCall4(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4)
- : fn(fn), object(object), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run() override
- {
- (object->*fn)(futureInterface, arg1, arg2, arg3, arg4);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Class *object;
- Arg1 arg1; Arg2 arg2; Arg3 arg3; Arg4 arg4;
-};
-
-template <typename T, typename FunctionPointer, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5>
-class StoredInterfaceFunctionCall5 : public QRunnable
-{
-public:
- StoredInterfaceFunctionCall5(void (fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4, const Arg5 &arg5)
- : fn(fn), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4), arg5(arg5) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run() override
- {
- fn(futureInterface, arg1, arg2, arg3, arg4, arg5);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Arg1 arg1; Arg2 arg2; Arg3 arg3; Arg4 arg4; Arg5 arg5;
-};
-template <typename T, typename FunctionPointer, typename Class, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5>
-class StoredInterfaceMemberFunctionCall5 : public QRunnable
-{
-public:
- StoredInterfaceMemberFunctionCall5(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4, const Arg5 &arg5)
- : fn(fn), object(object), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4), arg5(arg5) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run() override
- {
- (object->*fn)(futureInterface, arg1, arg2, arg3, arg4, arg5);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Class *object;
- Arg1 arg1; Arg2 arg2; Arg3 arg3; Arg4 arg4; Arg5 arg5;
-};
-
-template <typename T>
-QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &))
-{
- return (new StoredInterfaceFunctionCall0<T, void (*)(QFutureInterface<T> &)>(functionPointer))->start();
-}
-template <typename T, typename Arg1>
-QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1), const Arg1 &arg1)
-{
- return (new StoredInterfaceFunctionCall1<T, void (*)(QFutureInterface<T> &, Arg1), Arg1>(functionPointer, arg1))->start();
-}
-template <typename T, typename Arg1, typename Arg2>
-QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1, Arg2), const Arg1 &arg1, const Arg2 &arg2)
-{
- return (new StoredInterfaceFunctionCall2<T, void (*)(QFutureInterface<T> &, Arg1, Arg2), Arg1, Arg2>(functionPointer, arg1, arg2))->start();
-}
-template <typename T, typename Arg1, typename Arg2, typename Arg3>
-QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1, Arg2, Arg3), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3)
-{
- return (new StoredInterfaceFunctionCall3<T, void (*)(QFutureInterface<T> &, Arg1, Arg2, Arg3), Arg1, Arg2, Arg3>(functionPointer, arg1, arg2, arg3))->start();
-}
-template <typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
-QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4)
-{
- return (new StoredInterfaceFunctionCall4<T, void (*)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), Arg1, Arg2, Arg3, Arg4>(functionPointer, arg1, arg2, arg3, arg4))->start();
-}
-template <typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5>
-QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4, const Arg5 &arg5)
-{
- return (new StoredInterfaceFunctionCall5<T, void (*)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), Arg1, Arg2, Arg3, Arg4, Arg5>(functionPointer, arg1, arg2, arg3, arg4, arg5))->start();
+ return (new StoredInterfaceFunctionCall<T, void (*)(QFutureInterface<T> &, Args...), Args...>(functionPointer, args...))->start();
}
-template <typename Class, typename T>
-QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &), Class *object)
+template <typename Class, typename T, typename... Args>
+QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Args...), Class *object, Args... args)
{
- return (new StoredInterfaceMemberFunctionCall0<T, void (Class::*)(QFutureInterface<T> &), Class>(fn, object))->start();
+ return (new StoredInterfaceMemberFunctionCall<T, void (Class::*)(QFutureInterface<T> &, Args...), Class, Args...>(fn, object, args...))->start();
}
-template <typename Class, typename T, typename Arg1>
-QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1), Class *object, Arg1 arg1)
-{
- return (new StoredInterfaceMemberFunctionCall1<T, void (Class::*)(QFutureInterface<T> &, Arg1), Class, Arg1>(fn, object, arg1))->start();
-}
-
-template <typename Class, typename T, typename Arg1, typename Arg2>
-QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2), Class *object, const Arg1 &arg1, const Arg2 &arg2)
-{
- return (new StoredInterfaceMemberFunctionCall2<T, void (Class::*)(QFutureInterface<T> &, Arg1, Arg2), Class, Arg1, Arg2>(fn, object, arg1, arg2))->start();
-}
-
-template <typename Class, typename T, typename Arg1, typename Arg2, typename Arg3>
-QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3)
-{
- return (new StoredInterfaceMemberFunctionCall3<T, void (Class::*)(QFutureInterface<T> &, Arg1, Arg2, Arg3), Class, Arg1, Arg2, Arg3>(fn, object, arg1, arg2, arg3))->start();
-}
-
-template <typename Class, typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
-QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4)
-{
- return (new StoredInterfaceMemberFunctionCall4<T, void (Class::*)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), Class, Arg1, Arg2, Arg3, Arg4>(fn, object, arg1, arg2, arg3, arg4))->start();
-}
-
-template <typename Class, typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5>
-QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4, const Arg5 &arg5)
-{
- return (new StoredInterfaceMemberFunctionCall5<T, void (Class::*)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), Class, Arg1, Arg2, Arg3, Arg4, Arg5>(fn, object, arg1, arg2, arg3, arg4, arg5))->start();
-}
} // namespace QtConcurrent
QT_END_NAMESPACE
diff --git a/src/libs/installer/scriptengine.cpp b/src/libs/installer/scriptengine.cpp
index 3b2e3ae1d..7e3e69eb2 100644
--- a/src/libs/installer/scriptengine.cpp
+++ b/src/libs/installer/scriptengine.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.
@@ -34,6 +34,7 @@
#include "loggingutils.h"
#include "packagemanagergui.h"
#include "component.h"
+#include "settings.h"
#include <QMetaEnum>
#include <QQmlEngine>
@@ -55,18 +56,6 @@ namespace QInstaller {
/*!
\inmodule QtInstallerFramework
- \class QInstaller::ConsoleProxy
- \internal
-*/
-
-/*!
- \inmodule QtInstallerFramework
- \class QInstaller::InstallerProxy
- \internal
-*/
-
-/*!
- \inmodule QtInstallerFramework
\class QInstaller::QDesktopServicesProxy
\internal
*/
@@ -77,28 +66,6 @@ namespace QInstaller {
\internal
*/
-QJSValue InstallerProxy::components(const QString &regexp) const
-{
- if (m_core) {
- const QList<Component*> all = m_core->components(PackageManagerCore::ComponentType::All, regexp);
- QJSValue scriptComponentsObject = m_engine->newArray(all.count());
- for (int i = 0; i < all.count(); ++i) {
- Component *const component = all.at(i);
- QQmlEngine::setObjectOwnership(component, QQmlEngine::CppOwnership);
- scriptComponentsObject.setProperty(i, m_engine->newQObject(component));
- }
- return scriptComponentsObject;
- }
- return m_engine->newArray();
-}
-
-QJSValue InstallerProxy::componentByName(const QString &componentName)
-{
- if (m_core)
- return m_engine->newQObject(m_core->componentByName(componentName), false);
- return QJSValue();
-}
-
QJSValue QDesktopServicesProxy::findFiles(const QString &path, const QString &pattern)
{
QStringList result;
@@ -222,6 +189,12 @@ bool GuiProxy::isButtonEnabled(int wizardButton)
return m_gui->isButtonEnabled(wizardButton);
}
+void GuiProxy::setWizardPageButtonText(int pageId, int buttonId, const QString &buttonText)
+{
+ if (m_gui)
+ m_gui->setWizardPageButtonText(pageId, buttonId, buttonText);
+}
+
void GuiProxy::showSettingsButton(bool show)
{
if (m_gui)
@@ -377,18 +350,14 @@ QString QFileDialogProxy::getExistingFileOrDirectory(const QString &caption,
/*!
Constructs a script engine with \a core as parent.
*/
-ScriptEngine::ScriptEngine(PackageManagerCore *core) :
- QObject(core),
- m_guiProxy(new GuiProxy(this, this))
+ScriptEngine::ScriptEngine(PackageManagerCore *core) : QObject(core)
+ , m_guiProxy(new GuiProxy(this, this))
+ , m_core(core)
{
- m_engine.installExtensions(QJSEngine::TranslationExtension);
+ m_engine.installExtensions(QJSEngine::TranslationExtension | QJSEngine::ConsoleExtension);
QJSValue global = m_engine.globalObject();
- global.setProperty(QLatin1String("console"), m_engine.newQObject(new ConsoleProxy));
+
global.setProperty(QLatin1String("QFileDialog"), m_engine.newQObject(new QFileDialogProxy(core)));
- const QJSValue proxy = m_engine.newQObject(new InstallerProxy(this, core));
- global.setProperty(QLatin1String("InstallerProxy"), proxy);
- global.setProperty(QLatin1String("print"), m_engine.newQObject(new ConsoleProxy)
- .property(QLatin1String("log")));
global.setProperty(QLatin1String("systemInfo"), m_engine.newQObject(new SystemInfo));
global.setProperty(QLatin1String("QInstaller"), generateQInstallerObject());
@@ -408,11 +377,6 @@ ScriptEngine::ScriptEngine(PackageManagerCore *core) :
global.setProperty(QLatin1String("installer"), m_engine.newQObject(new QObject));
}
global.setProperty(QLatin1String("gui"), m_engine.newQObject(m_guiProxy));
-
- global.property(QLatin1String("installer")).setProperty(QLatin1String("components"),
- proxy.property(QLatin1String("components")));
- global.property(QLatin1String("installer")).setProperty(QLatin1String("componentByName"),
- proxy.property(QLatin1String("componentByName")));
}
/*!
@@ -574,14 +538,17 @@ QJSValue ScriptEngine::callScriptMethod(const QJSValue &scriptContext, const QSt
if (!method.isCallable())
return QJSValue(QJSValue::UndefinedValue);
if (method.isError()) {
- throw Error(method.toString().isEmpty() ? QString::fromLatin1("Unknown error.")
- : method.toString());
+ QString errorString = method.toString().isEmpty() ? QString::fromLatin1("Unknown error.")
+ : method.toString();
+
+ throw Error(QString::fromLatin1("%1 \n%2 \"%3\"").arg(errorString, tr(scClearCacheHint), m_core->settings().localCachePath()));
}
const QJSValue result = method.call(arguments);
if (result.isError()) {
- throw Error(result.toString().isEmpty() ? QString::fromLatin1("Unknown error.")
- : result.toString());
+ QString errorString = result.toString().isEmpty() ? QString::fromLatin1("Unknown error.")
+ : result.toString();
+ throw Error(QString::fromLatin1("%1 \n%2 \"%3\"").arg(errorString, tr(scClearCacheHint), m_core->settings().localCachePath()));
}
stack.removeLast();
@@ -657,14 +624,19 @@ QJSValue ScriptEngine::generateDesktopServicesObject()
SETPROPERTY(desktopServices, PicturesLocation, QStandardPaths)
SETPROPERTY(desktopServices, TempLocation, QStandardPaths)
SETPROPERTY(desktopServices, HomeLocation, QStandardPaths)
- SETPROPERTY(desktopServices, DataLocation, QStandardPaths)
+ SETPROPERTY(desktopServices, AppLocalDataLocation, QStandardPaths)
SETPROPERTY(desktopServices, CacheLocation, QStandardPaths)
+ SETPROPERTY(desktopServices, GenericCacheLocation, QStandardPaths)
SETPROPERTY(desktopServices, GenericDataLocation, QStandardPaths)
SETPROPERTY(desktopServices, RuntimeLocation, QStandardPaths)
SETPROPERTY(desktopServices, ConfigLocation, QStandardPaths)
SETPROPERTY(desktopServices, DownloadLocation, QStandardPaths)
SETPROPERTY(desktopServices, GenericCacheLocation, QStandardPaths)
SETPROPERTY(desktopServices, GenericConfigLocation, QStandardPaths)
+ SETPROPERTY(desktopServices, AppDataLocation, QStandardPaths)
+ SETPROPERTY(desktopServices, AppConfigLocation, QStandardPaths)
+ SETPROPERTY(desktopServices, PublicShareLocation, QStandardPaths)
+ SETPROPERTY(desktopServices, TemplatesLocation, QStandardPaths)
QJSValue object = m_engine.newQObject(new QDesktopServicesProxy(this));
object.setPrototype(desktopServices); // attach the properties
diff --git a/src/libs/installer/scriptengine.h b/src/libs/installer/scriptengine.h
index a7c0ef253..0b43465cb 100644
--- a/src/libs/installer/scriptengine.h
+++ b/src/libs/installer/scriptengine.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -77,6 +77,7 @@ private:
QJSEngine m_engine;
QHash<QString, QStringList> m_callstack;
GuiProxy *m_guiProxy;
+ PackageManagerCore *m_core;
};
}
diff --git a/src/libs/installer/scriptengine_p.h b/src/libs/installer/scriptengine_p.h
index a0936fe75..101d4f303 100644
--- a/src/libs/installer/scriptengine_p.h
+++ b/src/libs/installer/scriptengine_p.h
@@ -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.
@@ -43,36 +43,6 @@ class PackageManagerCore;
class PackageManagerGui;
class ScriptEngine;
-class ConsoleProxy : public QObject
-{
- Q_OBJECT
- Q_DISABLE_COPY(ConsoleProxy)
-
-public:
- ConsoleProxy() {}
-
-public slots :
- void log(const QString &log) { qCDebug(QInstaller::lcInstallerInstallLog).noquote() << log; }
-};
-
-class InstallerProxy : public QObject
-{
- Q_OBJECT
- Q_DISABLE_COPY(InstallerProxy)
-
-public:
- InstallerProxy(ScriptEngine *engine, PackageManagerCore *core)
- : m_engine(engine), m_core(core) {}
-
-public slots:
- QJSValue components(const QString &regexp = QString()) const;
- QJSValue componentByName(const QString &componentName);
-
-private:
- ScriptEngine *m_engine;
- PackageManagerCore *m_core;
-};
-
class QFileDialogProxy : public QObject
{
Q_OBJECT
@@ -146,6 +116,7 @@ public:
Q_INVOKABLE void clickButton(int wizardButton, int delayInMs = 0);
Q_INVOKABLE void clickButton(const QString &objectName, int delayInMs = 0) const;
Q_INVOKABLE bool isButtonEnabled(int wizardButton);
+ Q_INVOKABLE void setWizardPageButtonText(int pageId, int buttonId, const QString &buttonText);
Q_INVOKABLE void showSettingsButton(bool show);
Q_INVOKABLE void setSettingsButtonEnabled(bool enable);
@@ -178,8 +149,6 @@ private:
} // namespace QInstaller
-Q_DECLARE_METATYPE(QInstaller::ConsoleProxy*)
-Q_DECLARE_METATYPE(QInstaller::InstallerProxy*)
Q_DECLARE_METATYPE(QInstaller::QFileDialogProxy*)
Q_DECLARE_METATYPE(QInstaller::QDesktopServicesProxy*)
diff --git a/src/libs/installer/settings.cpp b/src/libs/installer/settings.cpp
index df7d5f8cc..e18f63689 100644
--- a/src/libs/installer/settings.cpp
+++ b/src/libs/installer/settings.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -318,13 +318,13 @@ Settings Settings::fromFileAndPrefix(const QString &path, const QString &prefix,
elementList << scName << scVersion << scTitle << scPublisher << scProductUrl
<< scTargetDir << scAdminTargetDir
<< scInstallerApplicationIcon << scInstallerWindowIcon
- << scLogo << scWatermark << scBanner << scBackground << scPageListPixmap
+ << scLogo << scWatermark << scBanner << scBackground << scPageListPixmap << scAliasDefinitionsFile
<< scStartMenuDir << scMaintenanceToolName << scMaintenanceToolIniFile << scMaintenanceToolAlias
<< scRemoveTargetDir << scLocalCacheDir << scPersistentLocalCache
<< scRunProgram << scRunProgramArguments << scRunProgramDescription
<< scDependsOnLocalInstallerBinary
- << scAllowSpaceInPath << scAllowNonAsciiCharacters << scDisableAuthorizationFallback
- << scDisableCommandLineInterface
+ << scAllowSpaceInPath << scAllowNonAsciiCharacters << scAllowRepositoriesForOfflineInstaller
+ << scDisableAuthorizationFallback << scDisableCommandLineInterface
<< scWizardStyle << scStyleSheet << scTitleColor
<< scWizardDefaultWidth << scWizardDefaultHeight << scWizardMinimumWidth << scWizardMinimumHeight
<< scWizardShowPageList << scProductImages
@@ -485,11 +485,11 @@ static int lengthToInt(const QVariant &variant)
QString length = variant.toString().trimmed();
if (length.endsWith(QLatin1String("em"), Qt::CaseInsensitive)) {
length.chop(2);
- return qRound(length.toDouble() * QApplication::fontMetrics().height());
+ return qRound(length.toDouble() * QFontMetricsF(QApplication::font()).height());
}
if (length.endsWith(QLatin1String("ex"), Qt::CaseInsensitive)) {
length.chop(2);
- return qRound(length.toDouble() * QApplication::fontMetrics().xHeight());
+ return qRound(length.toDouble() * QFontMetricsF(QApplication::font()).xHeight());
}
if (length.endsWith(QLatin1String("px"), Qt::CaseInsensitive)) {
length.chop(2);
@@ -539,6 +539,11 @@ void Settings::setProductImages(const QMap<QString, QVariant> &images)
d->m_data.insert(scProductImages, QVariant::fromValue(images));
}
+QString Settings::aliasDefinitionsFile() const
+{
+ return d->absolutePathFromKey(scAliasDefinitionsFile);
+}
+
QString Settings::installerApplicationIcon() const
{
return d->absolutePathFromKey(scInstallerApplicationIcon, systemIconSuffix());
@@ -644,6 +649,11 @@ bool Settings::allowNonAsciiCharacters() const
return d->m_data.value(scAllowNonAsciiCharacters, false).toBool();
}
+bool Settings::allowRepositoriesForOfflineInstaller() const
+{
+ return d->m_data.value(scAllowRepositoriesForOfflineInstaller, true).toBool();
+}
+
bool Settings::disableAuthorizationFallback() const
{
return d->m_data.value(scDisableAuthorizationFallback, false).toBool();
@@ -745,7 +755,7 @@ Settings::Update Settings::updateRepositoryCategories(const RepoHash &updates)
return update ? Settings::UpdatesApplied : Settings::NoUpdatesApplied;
}
-static bool apply(const RepoHash &updates, QHash<QUrl, Repository> *reposToUpdate)
+static bool apply(const RepoHash &updates, QMultiHash<QUrl, Repository> *reposToUpdate)
{
bool update = false;
QList<QPair<Repository, Repository> > values = updates.values(QLatin1String("replace"));
@@ -783,7 +793,7 @@ Settings::Update Settings::updateDefaultRepositories(const RepoHash &updates)
if (updates.isEmpty())
return Settings::NoUpdatesApplied;
- QHash <QUrl, Repository> defaultRepos;
+ QMultiHash <QUrl, Repository> defaultRepos;
foreach (const QVariant &variant, d->m_data.values(scRepositories)) {
const Repository repository = variant.value<Repository>();
defaultRepos.insert(repository.url(), repository);
@@ -837,7 +847,7 @@ Settings::Update Settings::updateUserRepositories(const RepoHash &updates)
if (updates.isEmpty())
return Settings::NoUpdatesApplied;
- QHash <QUrl, Repository> reposToUpdate;
+ QMultiHash <QUrl, Repository> reposToUpdate;
foreach (const QVariant &variant, d->m_data.values(scUserRepositories)) {
const Repository repository = variant.value<Repository>();
reposToUpdate.insert(repository.url(), repository);
@@ -993,7 +1003,7 @@ void Settings::setSaveDefaultRepositories(bool save)
QString Settings::repositoryCategoryDisplayName() const
{
QString displayName = d->m_data.value(QLatin1String(scRepositoryCategoryDisplayName)).toString();
- return displayName.isEmpty() ? tr("Select Categories") : displayName;
+ return displayName.isEmpty() ? tr("Categories") : displayName;
}
void Settings::setRepositoryCategoryDisplayName(const QString& name)
diff --git a/src/libs/installer/settings.h b/src/libs/installer/settings.h
index ebb65a382..85b59869c 100644
--- a/src/libs/installer/settings.h
+++ b/src/libs/installer/settings.h
@@ -42,7 +42,7 @@
namespace QInstaller {
class Repository;
-typedef QHash<QString, QPair<Repository, Repository> > RepoHash;
+typedef QMultiHash<QString, QPair<Repository, Repository> > RepoHash;
class INSTALLER_EXPORT Settings
{
@@ -95,6 +95,8 @@ public:
QMap<QString, QVariant> productImages() const;
void setProductImages(const QMap<QString, QVariant> &images);
+ QString aliasDefinitionsFile() const;
+
QString applicationName() const;
QString version() const;
@@ -143,6 +145,8 @@ public:
bool allowSpaceInPath() const;
bool allowNonAsciiCharacters() const;
+ bool allowRepositoriesForOfflineInstaller() const;
+
bool disableAuthorizationFallback() const;
bool disableCommandLineInterface() const;
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/simplemovefileoperation.cpp b/src/libs/installer/simplemovefileoperation.cpp
index 5f3000be0..5bbbdabb4 100644
--- a/src/libs/installer/simplemovefileoperation.cpp
+++ b/src/libs/installer/simplemovefileoperation.cpp
@@ -93,7 +93,7 @@ bool SimpleMoveFileOperation::performOperation()
bool SimpleMoveFileOperation::undoOperation()
{
- if (parseUndoOperationArguments().count() > 0)
+ if (skipUndoOperation())
return true;
const QString source = arguments().at(0);
const QString target = arguments().at(1);
diff --git a/src/libs/installer/sysinfo_win.cpp b/src/libs/installer/sysinfo_win.cpp
index e74eb1d4e..e5df8e35f 100644
--- a/src/libs/installer/sysinfo_win.cpp
+++ b/src/libs/installer/sysinfo_win.cpp
@@ -64,7 +64,7 @@ VolumeInfo updateVolumeSizeInformation(const VolumeInfo &info)
return update;
}
-/*!
+/*
Returns a list of volume info objects that are mounted as network drive shares.
*/
QList<VolumeInfo> networkVolumeInfosFromMountPoints()
@@ -95,7 +95,7 @@ QList<VolumeInfo> networkVolumeInfosFromMountPoints()
return volumes;
}
-/*!
+/*
Returns a list of volume info objects based on the given \a volumeGUID. The function also solves mounted
volume folder paths. It does not return any network drive shares.
*/
diff --git a/src/libs/installer/systeminfo.cpp b/src/libs/installer/systeminfo.cpp
index 6a1976c4a..aed02c569 100644
--- a/src/libs/installer/systeminfo.cpp
+++ b/src/libs/installer/systeminfo.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -55,6 +55,7 @@ SystemInfo::SystemInfo(QObject *parent) : QObject(parent)
\list
\li "i386"
\li "x86_64"
+ \li "arm64"
\endlist
\note This function depends on what the OS will report and may not detect the actual CPU
@@ -62,7 +63,7 @@ SystemInfo::SystemInfo(QObject *parent) : QObject(parent)
OS running on a 64-bit CPU is usually unable to determine whether the CPU is actually capable
of running 64-bit programs.
- \sa QSysInfo::currentCpuArchitecture()
+ \sa QSysInfo::currentCpuArchitecture() buildCpuArchitecture()
*/
QString SystemInfo::currentCpuArchitecture() const
{
@@ -70,6 +71,29 @@ QString SystemInfo::currentCpuArchitecture() const
}
/*!
+ \property SystemInfo::buildCpuArchitecture
+
+ The architecture of the CPU that the application was compiled for, in text format.
+
+ Possible values include:
+ \list
+ \li "i386"
+ \li "x86_64"
+ \li "arm64"
+ \endlist
+
+ \note Note that this may not match the actual CPU that the application is running on if
+ there's an emulation layer or if the CPU supports multiple architectures (like x86-64
+ processors supporting i386 applications). To detect that, use \c installer.currentCpuArchitecture()
+
+ \sa QSysInfo::buildCpuArchitecture() currentCpuArchitecture()
+*/
+QString SystemInfo::buildCpuArchitecture() const
+{
+ return QSysInfo::buildCpuArchitecture();
+}
+
+/*!
\property SystemInfo::kernelType
The type of the operating system kernel the installer was compiled for. It is also the
@@ -124,7 +148,7 @@ QString SystemInfo::kernelVersion() const
\list
\li "windows"
\li "opensuse" (for the Linux openSUSE distribution)
- \li "osx"
+ \li "macos"
\endlist
\sa QSysInfo::productType()
diff --git a/src/libs/installer/systeminfo.h b/src/libs/installer/systeminfo.h
index c5451605e..a1393397a 100644
--- a/src/libs/installer/systeminfo.h
+++ b/src/libs/installer/systeminfo.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -39,6 +39,7 @@ class SystemInfo : public QObject
Q_DISABLE_COPY(SystemInfo)
Q_PROPERTY(QString currentCpuArchitecture READ currentCpuArchitecture CONSTANT)
+ Q_PROPERTY(QString buildCpuArchitecture READ buildCpuArchitecture CONSTANT)
Q_PROPERTY(QString kernelType READ kernelType CONSTANT)
Q_PROPERTY(QString kernelVersion READ kernelVersion CONSTANT)
Q_PROPERTY(QString productType READ productType CONSTANT)
@@ -49,7 +50,7 @@ public:
explicit SystemInfo(QObject *parent = 0);
QString currentCpuArchitecture() const;
-
+ QString buildCpuArchitecture() const;
QString kernelType() const;
QString kernelVersion() const;
QString productType() const;
diff --git a/src/libs/installer/uninstallercalculator.cpp b/src/libs/installer/uninstallercalculator.cpp
index 36d769207..de753f71a 100644
--- a/src/libs/installer/uninstallercalculator.cpp
+++ b/src/libs/installer/uninstallercalculator.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -32,8 +32,6 @@
#include "packagemanagercore.h"
#include "globals.h"
-#include <QDebug>
-
namespace QInstaller {
/*!
@@ -42,61 +40,73 @@ 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)
+ : CalculatorBase(core)
, m_autoDependencyComponentHash(autoDependencyComponentHash)
, m_localDependencyComponentHash(localDependencyComponentHash)
, m_localVirtualComponents(localVirtualComponents)
{
}
-QSet<Component *> UninstallerCalculator::componentsToUninstall() const
+UninstallerCalculator::~UninstallerCalculator()
{
- return m_componentsToUninstall;
}
-void UninstallerCalculator::appendComponentToUninstall(Component *component, const bool reverse)
+bool UninstallerCalculator::solveComponent(Component *component, const QString &version)
{
+ Q_UNUSED(version)
+
if (!component)
- return;
+ return true;
if (!component->isInstalled())
- return;
+ return true;
if (m_localDependencyComponentHash.contains(component->name())) {
const QStringList &dependencies = PackageManagerCore::parseNames(m_localDependencyComponentHash.value(component->name()));
for (const QString &dependencyComponent : dependencies) {
Component *depComponent = m_core->componentByName(dependencyComponent);
- if (!depComponent)
+ if (!depComponent || !depComponent->isInstalled())
+ continue;
+
+ if (m_resolvedComponents.contains(depComponent)
+ || m_core->orderedComponentsToInstall().contains(depComponent)) {
+ // Component is already selected for uninstall or update
continue;
- if (depComponent->isInstalled() && !m_componentsToUninstall.contains(depComponent)) {
- appendComponentToUninstall(depComponent, reverse);
- 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 (depComponent->isEssential() || depComponent->forcedInstallation()) {
+ const QString errorMessage = QCoreApplication::translate("InstallerCalculator",
+ "Impossible dependency resolution detected. Forced install component \"%1\" would be uninstalled "
+ "because its dependency \"%2\" is marked for uninstallation with reason: \"%3\".")
+ .arg(depComponent->name(), component->name(), resolutionText(component));
+
+ qCWarning(QInstaller::lcInstallerInstallLog).noquote() << errorMessage;
+ m_errorString.append(errorMessage);
+ return false;
+ }
+ // Resolve also all cascading dependencies
+ if (!solveComponent(depComponent))
+ return false;
+
+ insertResolution(depComponent, Resolution::Dependent, component->name());
}
}
- if (reverse) {
- m_componentsToUninstall.remove(component);
- } else {
- m_componentsToUninstall.insert(component);
- }
+
+ m_resolvedComponents.append(component);
+ return true;
}
-void UninstallerCalculator::appendComponentsToUninstall(const QList<Component*> &components, const bool reverse)
+bool UninstallerCalculator::solve(const QList<Component*> &components)
{
- if (components.isEmpty())
- return;
- foreach (Component *component, components)
- appendComponentToUninstall(component, reverse);
+ foreach (Component *component, components) {
+ if (!solveComponent(component))
+ return false;
+ }
+
QList<Component*> autoDependOnList;
// All regular dependees are resolved. Now we are looking for auto depend on components.
for (Component *component : components) {
@@ -108,102 +118,71 @@ 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)) {
- insertUninstallReason(autoDepComponent, UninstallerCalculator::AutoDependent, component->name());
+ if (!m_resolvedComponents.contains(autoDepComponent)) {
+ insertResolution(autoDepComponent, Resolution::Automatic, component->name());
autoDepComponent->setInstallAction(ComponentModelHelper::AutodependUninstallation);
autoDependOnList.append(autoDepComponent);
}
}
}
}
+
if (!autoDependOnList.isEmpty())
- appendComponentsToUninstall(autoDependOnList, reverse);
+ solve(autoDependOnList);
else
- appendVirtualComponentsToUninstall(reverse);
-}
+ appendVirtualComponentsToUninstall();
-void UninstallerCalculator::removeComponentsFromUnInstall(const QList<Component*> &components)
-{
- appendComponentsToUninstall(components, true);
+ return true;
}
-void UninstallerCalculator::insertUninstallReason(Component *component, const UninstallReasonType uninstallReason,
- const QString &referencedComponentName)
+QString UninstallerCalculator::resolutionText(Component *component) const
{
- // keep the first reason
- if (m_toUninstallComponentIdReasonHash.contains(component->name()))
- return;
- m_toUninstallComponentIdReasonHash.insert(component->name(),
- qMakePair(uninstallReason, referencedComponentName));
-}
-
-QString UninstallerCalculator::uninstallReason(Component *component) const
-{
- UninstallerCalculator::UninstallReasonType reason = uninstallReasonType(component);
+ Resolution reason = resolutionType(component);
switch (reason) {
- case Selected:
+ case Resolution::Selected:
return QCoreApplication::translate("UninstallerCalculator",
"Deselected Components:");
- case Replaced:
+ case Resolution::Replaced:
return QCoreApplication::translate("UninstallerCalculator", "Components replaced "
- "by \"%1\":").arg(uninstallReasonReferencedComponent(component));
- case VirtualDependent:
+ "by \"%1\":").arg(referencedComponent(component));
+ case Resolution::VirtualDependent:
return QCoreApplication::translate("UninstallerCalculator",
"Removing virtual components without existing dependencies:");
- case Dependent:
+ case Resolution::Dependent:
return QCoreApplication::translate("UninstallerCalculator", "Components "
- "dependency \"%1\" removed:").arg(uninstallReasonReferencedComponent(component));
- case AutoDependent:
+ "dependency \"%1\" removed:").arg(referencedComponent(component));
+ case Resolution::Automatic:
return QCoreApplication::translate("UninstallerCalculator", "Components "
- "autodependency \"%1\" removed:").arg(uninstallReasonReferencedComponent(component));
+ "autodependency \"%1\" removed:").arg(referencedComponent(component));
+ default:
+ Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid uninstall resolution detected!");
}
return QString();
}
-UninstallerCalculator::UninstallReasonType UninstallerCalculator::uninstallReasonType(Component *c) const
-{
- return m_toUninstallComponentIdReasonHash.value(c->name()).first;
-}
-
-QString UninstallerCalculator::uninstallReasonReferencedComponent(Component *component) const
-{
- 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_resolvedComponents.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);
+ insertResolution(virtualComponent, Resolution::VirtualDependent);
+ }
}
}
if (!unneededVirtualList.isEmpty())
- appendComponentsToUninstall(unneededVirtualList, reverse);
+ solve(unneededVirtualList);
}
bool UninstallerCalculator::isRequiredVirtualPackage(Component *component)
@@ -211,10 +190,11 @@ bool UninstallerCalculator::isRequiredVirtualPackage(Component *component)
const QStringList localInstallDependents = m_core->localDependenciesToComponent(component);
for (const QString &dependent : localInstallDependents) {
Component *comp = m_core->componentByName(dependent);
- if (!m_componentsToUninstall.contains(comp)) {
+ if (!m_resolvedComponents.contains(comp)) {
return true;
}
}
return m_core->isDependencyForRequestedComponent(component);
}
+
} // namespace QInstaller
diff --git a/src/libs/installer/uninstallercalculator.h b/src/libs/installer/uninstallercalculator.h
index 57f2f926a..24979a9bb 100644
--- a/src/libs/installer/uninstallercalculator.h
+++ b/src/libs/installer/uninstallercalculator.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -30,6 +30,7 @@
#include "installer_global.h"
#include "qinstallerglobal.h"
+#include "calculatorbase.h"
#include <QHash>
#include <QList>
@@ -41,50 +42,30 @@ namespace QInstaller {
class Component;
class PackageManagerCore;
-class INSTALLER_EXPORT UninstallerCalculator
+class INSTALLER_EXPORT UninstallerCalculator : public CalculatorBase
{
public:
- enum UninstallReasonType
- {
- Selected, // "Deselected Component(s)"
- Replaced, // "Component(s) replaced by other components"
- VirtualDependent, // "No dependencies to virtual component"
- Dependent, // "Removed as dependency component is removed"
- 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);
+ ~UninstallerCalculator();
+
+ bool solve(const QList<Component*> &components) override;
+ QString resolutionText(Component *component) const override;
- QSet<Component*> componentsToUninstall() const;
+private:
+ bool solveComponent(Component *component, const QString &version = QString()) override;
- void appendComponentsToUninstall(const QList<Component*> &components, const bool reverse = false);
- void removeComponentsFromUnInstall(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);
+ void appendVirtualComponentsToUninstall();
private:
- void appendComponentToUninstall(Component *component, const bool reverse);
-
- QList<Component *> m_installedComponents;
- QSet<Component *> m_componentsToUninstall;
- PackageManagerCore *m_core;
- QHash<QString, QPair<UninstallReasonType, QString> > m_toUninstallComponentIdReasonHash;
AutoDependencyHash m_autoDependencyComponentHash;
LocalDependencyHash m_localDependencyComponentHash;
QStringList m_localVirtualComponents;
- QList<Component *> m_virtualComponentsForReverse;
};
-}
-
+} // namespace QInstaller
#endif // UNINSTALLERCALCULATOR_H
diff --git a/src/libs/installer/utils.cpp b/src/libs/installer/utils.cpp
index f677ba5f0..9b64ade37 100644
--- a/src/libs/installer/utils.cpp
+++ b/src/libs/installer/utils.cpp
@@ -57,7 +57,7 @@
*/
void QInstaller::uiDetachedWait(int ms)
{
- QTime timer;
+ QElapsedTimer timer;
timer.start();
do {
QCoreApplication::processEvents(QEventLoop::AllEvents, ms);
@@ -171,7 +171,7 @@ QByteArray QInstaller::calculateHash(QIODevice *device, QCryptographicHash::Algo
const qint64 numRead = device->read(buffer.data(), buffer.size());
if (numRead <= 0)
return hash.result();
- hash.addData(buffer.constData(), numRead);
+ hash.addData(buffer.left(numRead));
}
return QByteArray(); // never reached
}