summaryrefslogtreecommitdiffstats
path: root/src/libs/installer
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/installer')
-rw-r--r--src/libs/installer/binaryformat.cpp13
-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.h3
-rw-r--r--src/libs/installer/commandlineparser.cpp15
-rw-r--r--src/libs/installer/component.cpp42
-rw-r--r--src/libs/installer/component_p.cpp1
-rw-r--r--src/libs/installer/componentalias.cpp672
-rw-r--r--src/libs/installer/componentalias.h164
-rw-r--r--src/libs/installer/componentmodel.cpp27
-rw-r--r--src/libs/installer/componentmodel.h3
-rw-r--r--src/libs/installer/componentselectionpage_p.cpp103
-rw-r--r--src/libs/installer/componentselectionpage_p.h14
-rw-r--r--src/libs/installer/constants.h19
-rw-r--r--src/libs/installer/copydirectoryoperation.cpp2
-rw-r--r--src/libs/installer/copyfiletask.cpp4
-rw-r--r--src/libs/installer/createdesktopentryoperation.cpp12
-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.cpp46
-rw-r--r--src/libs/installer/downloadarchivesjob.h4
-rw-r--r--src/libs/installer/downloadfiletask.cpp22
-rw-r--r--src/libs/installer/downloadfiletask_p.h2
-rw-r--r--src/libs/installer/environmentvariablesoperation.cpp17
-rw-r--r--src/libs/installer/extractarchiveoperation.cpp10
-rw-r--r--src/libs/installer/fakestopprocessforupdateoperation.cpp6
-rw-r--r--src/libs/installer/fileutils.cpp2
-rw-r--r--src/libs/installer/genericdatacache.cpp46
-rw-r--r--src/libs/installer/genericdatacache.h9
-rw-r--r--src/libs/installer/globals.cpp61
-rw-r--r--src/libs/installer/globals.h2
-rw-r--r--src/libs/installer/globalsettingsoperation.cpp2
-rw-r--r--src/libs/installer/installer.pro10
-rw-r--r--src/libs/installer/installer_global.h8
-rw-r--r--src/libs/installer/installercalculator.cpp97
-rw-r--r--src/libs/installer/installercalculator.h9
-rw-r--r--src/libs/installer/installiconsoperation.cpp4
-rw-r--r--src/libs/installer/libarchivewrapper_p.cpp16
-rw-r--r--src/libs/installer/licenseoperation.cpp16
-rw-r--r--src/libs/installer/loggingutils.cpp39
-rw-r--r--src/libs/installer/loggingutils.h2
-rw-r--r--src/libs/installer/messageboxhandler.cpp4
-rw-r--r--src/libs/installer/metadata.cpp139
-rw-r--r--src/libs/installer/metadata.h14
-rw-r--r--src/libs/installer/metadatacache.cpp67
-rw-r--r--src/libs/installer/metadatacache.h46
-rw-r--r--src/libs/installer/metadatajob.cpp163
-rw-r--r--src/libs/installer/metadatajob.h13
-rw-r--r--src/libs/installer/metadatajob_p.h45
-rw-r--r--src/libs/installer/observer.cpp6
-rw-r--r--src/libs/installer/observer.h2
-rw-r--r--src/libs/installer/packagemanagercore.cpp610
-rw-r--r--src/libs/installer/packagemanagercore.h59
-rw-r--r--src/libs/installer/packagemanagercore_p.cpp591
-rw-r--r--src/libs/installer/packagemanagercore_p.h40
-rw-r--r--src/libs/installer/packagemanagercoredata.cpp5
-rw-r--r--src/libs/installer/packagemanagergui.cpp174
-rw-r--r--src/libs/installer/packagemanagergui.h7
-rw-r--r--src/libs/installer/packagesource.cpp6
-rw-r--r--src/libs/installer/packagesource.h8
-rw-r--r--src/libs/installer/permissionsettings.cpp34
-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/qprocesswrapper.cpp30
-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/registerfiletypeoperation.cpp2
-rw-r--r--src/libs/installer/remotefileengine.cpp34
-rw-r--r--src/libs/installer/remotefileengine.h16
-rw-r--r--src/libs/installer/remoteobject.cpp7
-rw-r--r--src/libs/installer/remoteobject.h87
-rw-r--r--src/libs/installer/remoteserverconnection.cpp18
-rw-r--r--src/libs/installer/remoteserverconnection_p.h2
-rw-r--r--src/libs/installer/repository.cpp33
-rw-r--r--src/libs/installer/repository.h10
-rw-r--r--src/libs/installer/repositorycategory.cpp8
-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/scriptengine.cpp62
-rw-r--r--src/libs/installer/scriptengine_p.h35
-rw-r--r--src/libs/installer/settings.cpp22
-rw-r--r--src/libs/installer/settings.h4
-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/utils.cpp2
96 files changed, 3391 insertions, 959 deletions
diff --git a/src/libs/installer/binaryformat.cpp b/src/libs/installer/binaryformat.cpp
index 9ed7742db..54af0c310 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.
@@ -151,14 +151,21 @@ void Resource::setName(const QByteArray &name)
/*!
Opens a resource in QIODevice::ReadOnly mode. The function returns \c true
- if successful.
+ if successful. Optionally, \a permissions can be given.
*/
+#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.h b/src/libs/installer/calculatorbase.h
index baf51c5b4..351658f99 100644
--- a/src/libs/installer/calculatorbase.h
+++ b/src/libs/installer/calculatorbase.h
@@ -49,7 +49,8 @@ public:
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"
+ Resolved, // "Component(s) that have resolved Dependencies"
+ Alias // "Components added from selected alias"
};
CalculatorBase(PackageManagerCore *core);
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 edd03a191..ce76a2927 100644
--- a/src/libs/installer/component.cpp
+++ b/src/libs/installer/component.cpp
@@ -46,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>
@@ -61,6 +64,11 @@
#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;
@@ -244,6 +252,11 @@ Component::Component(PackageManagerCore *core)
: d(new ComponentPrivate(core, this))
, 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);
@@ -667,11 +680,11 @@ void Component::loadTranslations(const QDir &directory, const QStringList &qms)
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());
}
}
}
@@ -693,13 +706,13 @@ void Component::loadUserInterfaces(const QDir &directory, const QStringList &uis
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.\n\n%3 \"%4\"").arg(
- it.fileName(), loader.errorString(), tr(scClearCacheHint), packageManagerCore()->settings().localCachePath()));
+ it.fileName(), loader->errorString(), tr(scClearCacheHint), packageManagerCore()->settings().localCachePath()));
}
d->scriptEngine()->newQObject(widget);
d->m_userInterfaces.insert(widget->objectName(), widget);
@@ -748,7 +761,9 @@ void Component::loadLicenses(const QString &directory, const QHash<QString, QVar
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");
+#endif
license.insert(scContent, stream.readAll());
d->m_licenses.insert(it.key(), license);
}
@@ -960,6 +975,12 @@ QStringList Component::archives() const
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(regExp, pathString);
return archivesNameList;
@@ -1089,7 +1110,7 @@ OperationList Component::operations(const Operation::OperationGroups &mask) cons
licenses.insert(values.at(i).value(scFile).toString(),
values.at(i).value(scContent));
}
- d->m_licenseOperation->setValue(scLicenses, licenses);
+ d->m_licenseOperation->setValue(scLicensesValue, licenses);
d->m_operations.append(d->m_licenseOperation);
}
}
@@ -1174,9 +1195,6 @@ Operation *Component::createOperation(const QString &operationName, const QStrin
return operation;
}
- if (operation->name() == scDelete)
- operation->setValue(scPerformUndo, false);
-
// Operation can contain variables which are resolved when performing the operation
if (operation->requiresUnreplacedVariables())
operation->setArguments(parameters);
@@ -1239,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 {
diff --git a/src/libs/installer/component_p.cpp b/src/libs/installer/component_p.cpp
index 7cf47c925..bf3941274 100644
--- a/src/libs/installer/component_p.cpp
+++ b/src/libs/installer/component_p.cpp
@@ -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/componentalias.cpp b/src/libs/installer/componentalias.cpp
new file mode 100644
index 000000000..311ca4475
--- /dev/null
+++ b/src/libs/installer/componentalias.cpp
@@ -0,0 +1,672 @@
+/**************************************************************************
+**
+** 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)
+ , m_missingOptionalComponents (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()) {
+ m_componentErrorMessages.clear();
+ m_missingOptionalComponents = false;
+ 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());
+}
+
+QString ComponentAlias::componentErrorMessage() const
+{
+ return m_componentErrorMessages;
+}
+
+bool ComponentAlias::missingOptionalComponents() const
+{
+ return m_missingOptionalComponents;
+}
+
+/*!
+ \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()) {
+ if (optional)
+ continue;
+ 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) {
+ m_missingOptionalComponents = true;
+ continue;
+ }
+
+ const QString error = QLatin1String("No required component found by name: ")
+ + componentName;
+ if (!m_componentErrorMessages.isEmpty())
+ m_componentErrorMessages.append(QLatin1String("\n"));
+ m_componentErrorMessages.append(error);
+
+ setUnstable(UnstableError::MissingComponent, error);
+ continue;
+ }
+
+ if (component->isUnstable() || !component->isCheckable()) {
+ if (optional)
+ continue;
+ const QString error = QLatin1String("Alias requires component that is uncheckable or unstable: ")
+ + componentName;
+ if (!m_componentErrorMessages.isEmpty())
+ m_componentErrorMessages.append(QLatin1String("\n"));
+ m_componentErrorMessages.append(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..3083260cd
--- /dev/null
+++ b/src/libs/installer/componentalias.h
@@ -0,0 +1,164 @@
+/**************************************************************************
+**
+** 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());
+ QString componentErrorMessage() const;
+ bool missingOptionalComponents() const;
+
+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;
+ bool m_missingOptionalComponents;
+ QList<ComponentAlias *> m_aliases;
+ QString m_componentErrorMessages;
+};
+
+} // 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 dbb80f2f6..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.
@@ -335,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.
*/
@@ -562,12 +578,11 @@ QSet<QModelIndex> ComponentModel::updateCheckedState(const ComponentSet &compone
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->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())
diff --git a/src/libs/installer/componentmodel.h b/src/libs/installer/componentmodel.h
index 1e8a2d297..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.
@@ -80,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;
diff --git a/src/libs/installer/componentselectionpage_p.cpp b/src/libs/installer/componentselectionpage_p.cpp
index 7f82ef47d..b68eebf06 100644
--- a/src/libs/installer/componentselectionpage_p.cpp
+++ b/src/libs/installer/componentselectionpage_p.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2023 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.
@@ -35,6 +35,7 @@
#include "component.h"
#include "fileutils.h"
#include "messageboxhandler.h"
+#include "customcombobox.h"
#include <QTreeView>
#include <QLabel>
@@ -50,7 +51,6 @@
#include <QStackedLayout>
#include <QStackedWidget>
#include <QLineEdit>
-#include <QComboBox>
#include <QStandardItemModel>
#include <QStyledItemDelegate>
@@ -71,22 +71,20 @@ ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionP
: 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_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_treeView->setUniformRowHeights(true);
- m_proxyModel->setRecursiveFilteringEnabled(true);
- m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
m_descriptionBaseWidget = new QWidget(q);
m_descriptionBaseWidget->setObjectName(QLatin1String("DescriptionBaseWidget"));
@@ -121,10 +119,21 @@ ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionP
m_sizeLabel->setObjectName(QLatin1String("ComponentSizeLabel"));
descriptionVLayout->addWidget(m_sizeLabel);
+ 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->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
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."));
@@ -133,18 +142,28 @@ ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionP
this, &ComponentSelectionPagePrivate::qbspButtonClicked);
m_rightSideVLayout->addWidget(m_descriptionBaseWidget);
- m_rightSideVLayout->addWidget(m_qbspPushButton, 0, Qt::AlignRight | Qt::AlignBottom);
+ m_rightSideVLayout->addWidget(m_createOfflinePushButton);
+ m_rightSideVLayout->addWidget(m_qbspPushButton);
QHBoxLayout *topHLayout = new QHBoxLayout;
- m_checkStateComboBox = new QComboBox(q);
+
+ // 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_checkStateComboBox->insertItem(scCheckDefaultIndex, ComponentSelectionPage::tr("Default"));
@@ -215,10 +234,6 @@ ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionP
connect(m_core, SIGNAL(metaJobInfoMessage(QString)), this, SLOT(setMessage(QString)));
connect(m_core, &PackageManagerCore::metaJobTotalProgress, this,
&ComponentSelectionPagePrivate::setTotalProgress);
-
-#ifdef INSTALLCOMPRESSED
- allowCompressedRepositoryInstall();
-#endif
}
ComponentSelectionPagePrivate::~ComponentSelectionPagePrivate()
@@ -226,14 +241,14 @@ ComponentSelectionPagePrivate::~ComponentSelectionPagePrivate()
}
-void ComponentSelectionPagePrivate::allowCompressedRepositoryInstall()
+void ComponentSelectionPagePrivate::setAllowCreateOfflineInstaller(bool allow)
{
- m_allowCompressedRepositoryInstall = true;
+ m_allowCreateOfflineInstaller = allow;
}
void ComponentSelectionPagePrivate::showCompressedRepositoryButton()
{
- if (m_allowCompressedRepositoryInstall)
+ if (m_core->allowCompressedRepositoryInstall())
m_qbspPushButton->setVisible(true);
}
@@ -242,6 +257,14 @@ void ComponentSelectionPagePrivate::hideCompressedRepositoryButton()
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()
{
if (m_categoryWidget)
@@ -288,11 +311,11 @@ void ComponentSelectionPagePrivate::showCategoryLayout(bool show)
if (show) {
m_rightSideVLayout->removeWidget(m_descriptionBaseWidget);
m_tabWidget->insertTab(0, m_descriptionBaseWidget, tr("Information"));
- m_rightSideVLayout->insertWidget(m_rightSideVLayout->count() - 1, m_tabWidget);
+ m_rightSideVLayout->insertWidget(0, m_tabWidget);
} else {
m_tabWidget->removeTab(0);
m_rightSideVLayout->removeWidget(m_tabWidget);
- m_rightSideVLayout->insertWidget(m_rightSideVLayout->count() - 1, m_descriptionBaseWidget);
+ m_rightSideVLayout->insertWidget(0, m_descriptionBaseWidget);
m_descriptionBaseWidget->setVisible(true);
}
m_tabWidget->setVisible(show);
@@ -463,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)
@@ -511,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()) {
@@ -522,6 +524,12 @@ void ComponentSelectionPagePrivate::fetchRepositoryCategories()
m_searchLineEdit->text().isEmpty() ? expandDefault() : expandSearchResults();
}
+void ComponentSelectionPagePrivate::createOfflineButtonClicked()
+{
+ m_core->setOfflineGenerator();
+ q->gui()->button(QWizard::NextButton)->click();
+}
+
void ComponentSelectionPagePrivate::qbspButtonClicked()
{
QString defaultDownloadDirectory =
@@ -530,15 +538,8 @@ void ComponentSelectionPagePrivate::qbspButtonClicked()
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) {
+ if (m_core->addQBspRepositories(fileNames)) {
updateWidgetVisibility(true);
- m_core->settings().addTemporaryRepositories(set, false);
if (!m_core->fetchCompressedPackagesTree()) {
MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
QLatin1String("FailToFetchPackages"), tr("Error"), m_core->error());
diff --git a/src/libs/installer/componentselectionpage_p.h b/src/libs/installer/componentselectionpage_p.h
index ed68fafa4..187fce61d 100644
--- a/src/libs/installer/componentselectionpage_p.h
+++ b/src/libs/installer/componentselectionpage_p.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2023 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.
@@ -48,13 +48,13 @@ class QVBoxLayout;
class QHBoxLayout;
class QGridLayout;
class QStackedLayout;
-class QComboBox;
namespace QInstaller {
class PackageManagerCore;
class ComponentModel;
class ComponentSelectionPage;
+class CustomComboBox;
class ComponentSelectionPagePrivate : public QObject
{
@@ -66,9 +66,10 @@ 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();
@@ -81,9 +82,9 @@ public slots:
void updateAllCheckStates(int which);
void selectAll();
void deselectAll();
- void enableRepositoryCategory(const QString &repositoryName, bool enable);
void updateWidgetVisibility(bool show);
void fetchRepositoryCategories();
+ void createOfflineButtonClicked();
void qbspButtonClicked();
void onProgressChanged(int progress);
void setMessage(const QString &msg);
@@ -105,15 +106,16 @@ private:
QWidget *m_descriptionBaseWidget;
QLabel *m_sizeLabel;
QLabel *m_descriptionLabel;
+ QPushButton *m_createOfflinePushButton;
QPushButton *m_qbspPushButton;
- QComboBox *m_checkStateComboBox;
+ CustomComboBox *m_checkStateComboBox;
QWidget *m_categoryWidget;
QGroupBox *m_categoryGroupBox;
QLabel *m_metadataProgressLabel;
QProgressBar *m_progressBar;
QGridLayout *m_mainGLayout;
QVBoxLayout *m_rightSideVLayout;
- bool m_allowCompressedRepositoryInstall;
+ bool m_allowCreateOfflineInstaller;
bool m_categoryLayoutVisible;
ComponentModel *m_allModel;
ComponentModel *m_updaterModel;
diff --git a/src/libs/installer/constants.h b/src/libs/installer/constants.h
index ecd85fe11..7bf816b5f 100644
--- a/src/libs/installer/constants.h
+++ b/src/libs/installer/constants.h
@@ -60,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");
@@ -75,7 +80,7 @@ static const QLatin1String scMetadataName("MetadataName");
static const QLatin1String scContentSha1("ContentSha1");
static const QLatin1String scCheckSha1CheckSum("CheckSha1CheckSum");
-static const char *scClearCacheHint = QT_TR_NOOP(
+static const char * const scClearCacheHint = QT_TR_NOOP(
"This may be solved by restarting the application after clearing the cache from:");
// symbols
@@ -102,6 +107,7 @@ 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");
@@ -123,7 +129,6 @@ static const QLatin1String scMinimumProgress("MinimumProgress");
static const QLatin1String scDelete("Delete");
static const QLatin1String scCopy("Copy");
static const QLatin1String scMkdir("Mkdir");
-static const QLatin1String scPerformUndo("performUndo");
static const QLatin1String scIsDefault("isDefault");
static const QLatin1String scAdmin("admin");
static const QLatin1String scTwoArgs("%1/%2/");
@@ -152,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");
@@ -174,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 {
@@ -271,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 c3988a8ec..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 << 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 48700bd12..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);
}
/*!
@@ -289,17 +293,24 @@ void DownloadArchivesJob::registerFile()
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) {
@@ -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().sourceUrl, 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)
diff --git a/src/libs/installer/downloadarchivesjob.h b/src/libs/installer/downloadarchivesjob.h
index c156d0244..5155c881a 100644
--- a/src/libs/installer/downloadarchivesjob.h
+++ b/src/libs/installer/downloadarchivesjob.h
@@ -51,7 +51,7 @@ 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; }
@@ -103,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/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 162bd1609..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;
@@ -215,7 +215,8 @@ bool ExtractArchiveOperation::performOperation()
}
files[i] = replacePath(files.at(i), installDir, QLatin1String(scRelocatable));
}
- out << files;
+ if (!files.isEmpty())
+ out << files;
setValue(QLatin1String("files"), file.fileName());
file.close();
} else {
@@ -253,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 2147a8978..044eeb34f 100644
--- a/src/libs/installer/fileutils.cpp
+++ b/src/libs/installer/fileutils.cpp
@@ -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 fd264ce63..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.
@@ -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()));
@@ -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
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 5240138c4..3fd084768 100644
--- a/src/libs/installer/globals.cpp
+++ b/src/libs/installer/globals.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2023 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";
@@ -78,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;
}
@@ -117,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 3b3f4e3ab..2d119048b 100644
--- a/src/libs/installer/globals.h
+++ b/src/libs/installer/globals.h
@@ -59,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 5a58968ee..ff7a0eed2 100644
--- a/src/libs/installer/installer.pro
+++ b/src/libs/installer/installer.pro
@@ -47,11 +47,13 @@ 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 \
@@ -144,13 +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 \
@@ -158,6 +162,7 @@ SOURCES += packagemanagercore.cpp \
genericdatacache.cpp \
loggingutils.cpp \
metadata.cpp \
+ metadatacache.cpp \
operationtracer.cpp \
packagemanagercore_p.cpp \
packagemanagergui.cpp \
@@ -231,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 105e99fd8..4c53824af 100644
--- a/src/libs/installer/installercalculator.cpp
+++ b/src/libs/installer/installercalculator.cpp
@@ -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>
@@ -51,6 +53,19 @@ InstallerCalculator::~InstallerCalculator()
{
}
+bool InstallerCalculator::solve()
+{
+ 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);
@@ -67,6 +82,9 @@ QString InstallerCalculator::resolutionText(Component *component) const
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!");
}
@@ -110,6 +128,44 @@ bool InstallerCalculator::solve(const QList<Component *> &components)
return true;
}
+bool InstallerCalculator::solve(const QList<ComponentAlias *> &aliases)
+{
+ 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;
+}
+
void InstallerCalculator::addComponentForInstall(Component *component, const QString &version)
{
if (!m_componentsForAutodepencencyCheck.contains(component))
@@ -121,12 +177,40 @@ void InstallerCalculator::addComponentForInstall(Component *component, const QSt
}
}
+bool InstallerCalculator::addComponentsFromAlias(ComponentAlias *alias)
+{
+ 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);
+}
+
QString InstallerCalculator::recursionError(Component *component) const
{
return QCoreApplication::translate("InstallerCalculator", "Recursion detected, component \"%1\" "
"already added with reason: \"%2\"").arg(component->name(), resolutionText(component));
}
+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();
@@ -200,6 +284,19 @@ bool InstallerCalculator::solveComponent(Component *component, const QString &ve
return true;
}
+bool InstallerCalculator::solveAlias(ComponentAlias *alias)
+{
+ 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()
{
// All regular dependencies are resolved. Now we are looking for auto depend on components.
diff --git a/src/libs/installer/installercalculator.h b/src/libs/installer/installercalculator.h
index 339dbeffd..e542dc664 100644
--- a/src/libs/installer/installercalculator.h
+++ b/src/libs/installer/installercalculator.h
@@ -40,6 +40,7 @@
namespace QInstaller {
class Component;
+class ComponentAlias;
class PackageManagerCore;
class INSTALLER_EXPORT InstallerCalculator : public CalculatorBase
@@ -48,20 +49,28 @@ public:
InstallerCalculator(PackageManagerCore *core, const AutoDependencyHash &autoDependencyComponentHash);
~InstallerCalculator();
+ bool solve();
bool solve(const QList<Component *> &components) override;
+ bool solve(const QList<ComponentAlias *> &aliases);
+
QString resolutionText(Component *component) const override;
private:
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:
QHash<Component*, QSet<Component*> > m_visitedComponents;
QList<const Component*> m_componentsForAutodepencencyCheck;
QSet<QString> m_toInstallComponentIds; //for faster lookups
+ QSet<QString> m_toInstallComponentAliases;
//Helper hash for quicker search for autodependency components
AutoDependencyHash m_autoDependencyComponentHash;
};
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/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 6cf0c8e3e..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(scLicenses).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(scLicenses).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/loggingutils.cpp b/src/libs/installer/loggingutils.cpp
index 42bbc0117..9a36720dd 100644
--- a/src/libs/installer/loggingutils.cpp
+++ b/src/libs/installer/loggingutils.cpp
@@ -29,6 +29,7 @@
#include "loggingutils.h"
#include "component.h"
+#include "componentalias.h"
#include "globals.h"
#include "fileutils.h"
#include "packagemanagercore.h"
@@ -367,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()
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 78abc88fa..052709e51 100644
--- a/src/libs/installer/messageboxhandler.cpp
+++ b/src/libs/installer/messageboxhandler.cpp
@@ -368,7 +368,9 @@ static QMessageBox::StandardButton showNewMessageBox(QWidget *parent, QMessageBo
QMessageBox::StandardButton defaultButton)
{
QMessageBox msgBox(icon, title, text, QMessageBox::NoButton, parent);
- msgBox.setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
+ 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 9ae817127..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.
@@ -46,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.
*/
@@ -122,18 +168,23 @@ QDomDocument Metadata::updatesDocument() const
}
/*!
- Returns \c true if the \c Updates.xml document of this metadata
- exists, \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
{
- const QString updateFile(path() + QLatin1String("/Updates.xml"));
- if (!QFileInfo::exists(updateFile)) {
+ QFile updateFile(path() + QLatin1String("/Updates.xml"));
+ if (!updateFile.open(QIODevice::ReadOnly)) {
qCWarning(QInstaller::lcInstallerInstallLog)
- << "File" << updateFile << "does not exist.";
+ << "Cannot open" << updateFile.fileName()
+ << "for reading:" << updateFile.errorString();
return false;
}
- return true;
+
+ return verifyMetaFiles(&updateFile);
}
/*!
@@ -282,4 +333,78 @@ bool Metadata::containsRepositoryUpdates() const
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 3063be829..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
@@ -64,6 +66,9 @@ public:
bool containsRepositoryUpdates() const;
private:
+ bool verifyMetaFiles(QFile *updateFile) const;
+
+private:
Repository m_repository;
QString m_persistentRepositoryPath;
mutable QByteArray m_checksum;
@@ -71,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 ff15d7b0d..1bed304c6 100644
--- a/src/libs/installer/metadatajob.cpp
+++ b/src/libs/installer/metadatajob.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2023 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()
@@ -240,13 +237,15 @@ void MetadataJob::doStart()
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()) {
+ 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());
- QList<FileTaskItem> items;
- QSet<Repository> repositories = getRepositories();
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();
@@ -289,17 +288,24 @@ void MetadataJob::doStart()
FileTaskItem item(url, tmp.path() + QLatin1String("/Updates.xml"));
item.insert(TaskRole::UserRole, QVariant::fromValue(repo));
item.insert(TaskRole::Authenticator, QVariant::fromValue(authenticator));
- items.append(item);
+ m_updatesXmlItems.append(item);
}
}
+ setProgressTotalAmount(100);
const quint64 totalCount = repositories.count();
if (cachedCount > 0) {
qCDebug(lcInstallerInstallLog).nospace() << "Loaded from cache "
<< cachedCount << "/" << totalCount << ". Downloading remaining "
- << items.count() << "/" << totalCount <<".";
+ << m_updatesXmlItems.count() << "/" << totalCount <<".";
+ } else {
+ qCDebug(lcInstallerInstallLog).nospace() <<"Downloading " << m_updatesXmlItems.count()
+ << " items to cache.";
}
- if (items.count() > 0) {
- startXMLTask(items);
+ 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();
}
@@ -338,13 +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());
- 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()
@@ -431,7 +446,7 @@ void MetadataJob::unzipRepositoryTaskFinished()
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();
@@ -451,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);
@@ -476,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 {
@@ -497,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();
@@ -557,8 +599,9 @@ void MetadataJob::xmlTaskFinished()
// 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();
@@ -630,6 +673,7 @@ void MetadataJob::metadataTaskFinished()
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));
@@ -684,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;
@@ -703,6 +741,7 @@ bool MetadataJob::fetchMetaDataPackages()
void MetadataJob::reset()
{
m_packages.clear();
+ m_updatesXmlItems.clear();
m_defaultRepositoriesFetched = false;
m_fetchedCategorizedRepositories.clear();
@@ -721,6 +760,7 @@ void MetadataJob::reset()
} catch (...) {}
m_tempDirDeleter.releaseAndDeleteAll();
m_metadataResult.clear();
+ m_updatesXmlResult.clear();
m_taskNumber = 0;
}
@@ -728,7 +768,6 @@ void MetadataJob::resetCompressedFetch()
{
setError(Job::NoError);
setErrorString(QString());
- m_unzipRepositoryitems.clear();
try {
foreach (QFutureWatcher<void> *const watcher, m_unzipTasks.keys()) {
@@ -762,7 +801,7 @@ MetadataJob::Status MetadataJob::parseUpdatesXml(const QList<FileTaskResult> &re
}
QFileInfo fileInfo(result.target());
- QScopedPointer<Metadata> metadata(new Metadata(fileInfo.absolutePath()));
+ std::unique_ptr<Metadata> metadata(new Metadata(fileInfo.absolutePath()));
QFile file(result.target());
if (!file.open(QIODevice::ReadOnly)) {
qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot open Updates.xml for reading:"
@@ -770,13 +809,21 @@ MetadataJob::Status MetadataJob::parseUpdatesXml(const QList<FileTaskResult> &re
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;
@@ -798,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;
@@ -855,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);
@@ -885,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.
@@ -973,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()) {
@@ -1015,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;
@@ -1122,4 +1169,14 @@ MetadataJob::Status MetadataJob::setAdditionalRepositories(QMultiHash<QString, Q
}
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 37d9367e5..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>
@@ -74,6 +74,7 @@ public:
bool resetCache(bool init = false);
bool clearCache();
+ bool isValidCache() const;
private slots:
void doStart() override;
@@ -86,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();
@@ -108,6 +109,7 @@ private:
const FileTaskResult &result, const Metadata &metadata);
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 ef2729dbb..837a7e9ae 100644
--- a/src/libs/installer/metadatajob_p.h
+++ b/src/libs/installer/metadatajob_p.h
@@ -62,11 +62,16 @@ class UnzipArchiveTask : public AbstractTask<void>
public:
UnzipArchiveTask(const QString &arcive, const QString &target)
- : m_archive(arcive), m_targetDir(target), m_removeArchive(false)
+ : 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
{
@@ -82,12 +87,43 @@ 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;
+ }
+
+ 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();
@@ -101,6 +137,7 @@ private:
QString m_archive;
QString m_targetDir;
bool m_removeArchive;
+ bool m_storeChecksums;
};
class CacheTaskException : public QException
@@ -126,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)
{}
@@ -140,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;
}
@@ -174,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 b7d7d2a26..0eae41ea3 100644
--- a/src/libs/installer/packagemanagercore.cpp
+++ b/src/libs/installer/packagemanagercore.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2023 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>
@@ -138,6 +146,8 @@ using namespace QInstaller;
Installation has to be updated.
\value EssentialUpdated
Installation essential components were updated.
+ \value NoPackagesFound
+ No packages found from remote.
*/
/*!
@@ -175,25 +185,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 +204,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.
@@ -600,6 +591,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>
@@ -612,6 +611,22 @@ 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}
@@ -639,6 +654,12 @@ void PackageManagerCore::componentsToInstallNeedsRecalculation()
*/
bool PackageManagerCore::recalculateAllComponents()
{
+ // 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())
@@ -792,12 +813,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;
}
@@ -833,7 +855,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);
@@ -1026,8 +1048,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()));
}
/*!
@@ -1516,7 +1537,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)) {
@@ -1542,7 +1563,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)
@@ -1587,10 +1608,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();
@@ -1663,11 +1685,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);
@@ -1695,8 +1748,15 @@ bool PackageManagerCore::fetchRemotePackagesTree()
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;
+
+ d->m_componentsToBeInstalled = components;
return fetchPackagesTree(packages, installedPackages);
}
@@ -1945,6 +2005,82 @@ void PackageManagerCore::setTemporaryRepositories(const QStringList &repositorie
settings().setTemporaryRepositories(repositorySet, replace);
}
+bool PackageManagerCore::addQBspRepositories(const QStringList &repositories)
+{
+ 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;
+}
+
+bool PackageManagerCore::validRepositoriesAvailable() const
+{
+ foreach (const Repository &repo, settings().repositories()) {
+ if (repo.isEnabled() && repo.isValid()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void PackageManagerCore::setAllowCompressedRepositoryInstall(bool allow)
+{
+ d->m_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.
@@ -2102,6 +2238,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
@@ -2127,6 +2272,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.
*/
@@ -2164,8 +2321,26 @@ QList<Component *> PackageManagerCore::componentsMarkedForInstallation() const
}
/*!
- Determines which components to install based on the current run mode, including dependencies
- and automatic dependencies. Returns \c true on success, \c false otherwise.
+ 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()
@@ -2179,15 +2354,13 @@ bool PackageManagerCore::calculateComponentsToInstall() const
emit aboutCalculateComponentsToInstall();
d->clearInstallerCalculator();
- const QList<Component*> selectedComponentsToInstall = componentsMarkedForInstallation();
- const bool componentsToInstallCalculated =
- d->installerCalculator()->solve(selectedComponentsToInstall);
+ const bool calculated = d->installerCalculator()->solve();
d->updateComponentInstallActions();
emit finishedCalculateComponentsToInstall();
- return componentsToInstallCalculated;
+ return calculated;
}
/*!
@@ -2455,12 +2628,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;
}
@@ -2473,44 +2647,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();
+ 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();
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)
@@ -2532,10 +2732,56 @@ 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;
+
+ if (!d->buildComponentAliases())
+ return false;
+
+ QRegularExpression re(regexp);
+ re.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
+
+ QList<ComponentAlias *> matchedAliases;
+ for (auto *alias : std::as_const(d->m_componentAliases)) {
+ if (!alias)
+ continue;
+
+ if (re.match(alias->name()).hasMatch()) {
+ 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)
@@ -2549,7 +2795,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()
@@ -2573,25 +2819,50 @@ 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 fallbackReposFetched)
{
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;
+ setCanceled();
+ return false;
+ } 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;
+ } else if (alias->missingOptionalComponents() && !fallbackReposFetched) {
+ unstableAliasFound = true;
+ setCanceled();
+ return false;
+ }
+
+ 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 "
@@ -2661,6 +2932,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.
@@ -2668,13 +2958,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();
@@ -2780,6 +3082,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.
@@ -2804,9 +3120,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;
@@ -2845,8 +3158,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())) {
@@ -2868,23 +3179,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);
}
/*!
@@ -2896,9 +3191,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
@@ -2911,24 +3203,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);
}
/*!
@@ -2994,6 +3269,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)
@@ -3002,12 +3292,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());
@@ -3063,21 +3363,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));
@@ -3086,22 +3386,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;
@@ -3160,6 +3463,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.
@@ -3171,6 +3478,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.
@@ -3686,6 +3997,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()
*/
@@ -3787,6 +4106,7 @@ bool PackageManagerCore::isPackageManager() const
void PackageManagerCore::setOfflineGenerator()
{
d->m_magicMarkerSupplement = BinaryContent::OfflineGenerator;
+ emit installerBinaryMarkerChanged(d->m_magicBinaryMarker);
}
/*!
@@ -3805,6 +4125,7 @@ bool PackageManagerCore::isOfflineGenerator() const
void PackageManagerCore::setPackageViewer()
{
d->m_magicMarkerSupplement = BinaryContent::PackageViewer;
+ emit installerBinaryMarkerChanged(d->m_magicBinaryMarker);
}
/*!
@@ -3818,6 +4139,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.
*/
@@ -3992,13 +4324,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()) {
@@ -4130,10 +4455,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.
@@ -4141,7 +4466,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
@@ -4164,7 +4489,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();
@@ -4219,7 +4544,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
@@ -4233,6 +4558,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());
@@ -4270,12 +4598,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)
diff --git a/src/libs/installer/packagemanagercore.h b/src/libs/installer/packagemanagercore.h
index e5236112e..d9da87d85 100644
--- a/src/libs/installer/packagemanagercore.h
+++ b/src/libs/installer/packagemanagercore.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2023 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,7 @@
#define PACKAGEMANAGERCORE_H
#include "binaryformat.h"
+#include "binarycontent.h"
#include "component.h"
#include "protocol.h"
#include "repository.h"
@@ -45,11 +46,14 @@
namespace QInstaller {
+struct AliasSource;
class ComponentModel;
+class ComponentAlias;
class ScriptEngine;
class PackageManagerCorePrivate;
class PackageManagerProxyFactory;
class Settings;
+class ComponentSortFilterProxyModel;
// -- PackageManagerCore
@@ -80,7 +84,8 @@ public:
Canceled = 3,
Unfinished = 4,
ForceUpdate = 5,
- EssentialUpdated = 6
+ EssentialUpdated = 6,
+ NoPackagesFound = 7
};
Status status() const;
QString error() const;
@@ -145,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();
@@ -202,9 +208,20 @@ public:
void setOfflineBinaryName(const QString &name);
QString offlineBinaryName() const;
+ 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);
@@ -241,7 +258,10 @@ 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;
@@ -252,6 +272,9 @@ public:
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;
@@ -263,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();
@@ -293,6 +321,8 @@ public:
void setPackageViewer();
Q_INVOKABLE bool isPackageViewer() const;
+ void resetBinaryMarkerSupplement();
+
void setUserSetBinaryMarker(qint64 magicMarker);
Q_INVOKABLE bool isUserSetBinaryMarker() const;
@@ -307,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;
@@ -353,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();
@@ -380,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();
@@ -447,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, bool fallbackReposFetched);
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 259aafeb6..0ed4bf6d8 100644
--- a/src/libs/installer/packagemanagercore_p.cpp
+++ b/src/libs/installer/packagemanagercore_p.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2023 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>
@@ -120,56 +123,67 @@ static QStringList checkRunningProcessesFromList(const QStringList &processList)
return stillRunningProcesses;
}
-static void deferredRename(const QString &oldName, const QString &newName, bool restart = false)
+static bool filterMissingAliasesToInstall(const QString& component, const QList<ComponentAlias *> packages)
{
-#ifdef Q_OS_WIN
- const QString currentExecutable = QCoreApplication::applicationFilePath();
- const QString tmpExecutable = generateTemporaryFileName(currentExecutable);
-
- QFile::rename(currentExecutable, tmpExecutable);
- QFile::rename(oldName, newName);
-
- QStringList arguments;
- if (restart) {
- // Restart with same command line arguments as first executable
- arguments = QCoreApplication::arguments();
- arguments.removeFirst(); // Remove program name
- arguments.prepend(tmpExecutable);
- arguments.prepend(QLatin1String("--")
- + CommandLineOptions::scCleanupUpdate);
- } else {
- arguments.append(QLatin1String("--")
- + CommandLineOptions::scCleanupUpdateOnly);
- arguments.append(tmpExecutable);
+ bool packageFound = false;
+ for (qsizetype i = 0; i < packages.size(); ++i) {
+ packageFound = (packages.at(i)->name() == component);
+ if (packageFound)
+ break;
}
- QProcessWrapper::startDetached2(newName, arguments);
+ return !packageFound;
+}
-#else
- QFile::remove(newName);
- QFile::rename(oldName, newName);
- SelfRestarter::setRestartOnQuit(restart);
-#endif
+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;
}
+static QString getAppBundlePath() {
+ QString appDirPath = QCoreApplication::applicationDirPath();
+ QDir dir(appDirPath);
+ while (!dir.isRoot()) {
+ if (dir.dirName().endsWith(QLatin1String(".app")))
+ return dir.absolutePath();
+ dir.cdUp();
+ }
+ return QString();
+}
// -- 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_foundEssentialUpdate(false)
, m_componentScriptEngine(nullptr)
, m_controlScriptEngine(nullptr)
, m_installerCalculator(nullptr)
@@ -177,37 +191,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_foundEssentialUpdate(false)
, m_componentScriptEngine(nullptr)
, m_controlScriptEngine(nullptr)
, m_installerCalculator(nullptr)
@@ -215,22 +238,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;
@@ -241,7 +263,7 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, q
<< operation.name;
continue;
}
- m_performedOperationsOld.append(op.take());
+ m_performedOperationsOld.append(op.release());
}
connect(this, &PackageManagerCorePrivate::installationStarted,
@@ -270,6 +292,7 @@ PackageManagerCorePrivate::~PackageManagerCorePrivate()
qDeleteAll(m_performedOperationsCurrentSession);
delete m_updateFinder;
+ delete m_aliasFinder;
delete m_proxyFactory;
delete m_defaultModel;
@@ -430,6 +453,124 @@ 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);
+ }
+ }
+
+ if (m_core->isPackageViewer())
+ return true;
+ // After aliases are loaded, perform sanity checks:
+
+ // 1. Component check state is changed by alias selection, so store the initial state
+ storeCheckState();
+
+ QStringList aliasNamesSelectedForInstall;
+
+ // 2. Get a list of alias names to be installed, dependency aliases needs to be listed first
+ // to get proper unstable state for parents
+ std::function<void(QStringList)> fetchAliases = [&](QStringList aliases) {
+ for (const QString &aliasName : aliases) {
+ ComponentAlias *alias = m_componentAliases.value(aliasName);
+ if (!alias || aliasNamesSelectedForInstall.contains(aliasName))
+ continue;
+ if (!aliasNamesSelectedForInstall.contains(aliasName))
+ aliasNamesSelectedForInstall.prepend(aliasName);
+ fetchAliases(QStringList() << QInstaller::splitStringWithComma(alias->value(scRequiredAliases))
+ << QInstaller::splitStringWithComma(alias->value(scOptionalAliases)));
+ }
+ };
+ for (const QString &installComponent : m_componentsToBeInstalled) {
+ ComponentAlias *alias = m_componentAliases.value(installComponent);
+ if (!alias)
+ continue;
+ if (!aliasNamesSelectedForInstall.contains(installComponent))
+ aliasNamesSelectedForInstall.prepend(installComponent);
+ fetchAliases(QStringList() << QInstaller::splitStringWithComma(alias->value(scRequiredAliases))
+ << QInstaller::splitStringWithComma(alias->value(scOptionalAliases)));
+ }
+
+ Graph<QString> aliasGraph;
+ QList<ComponentAlias *> aliasesSelectedForInstall;
+ for (auto &aliasName : std::as_const(aliasNamesSelectedForInstall)) {
+ ComponentAlias *alias = m_componentAliases.value(aliasName);
+ if (!alias)
+ continue;
+ 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()));
+ }
+ if (!aliasesSelectedForInstall.contains(alias))
+ aliasesSelectedForInstall.append(alias);
+ }
+
+ const QList<QString> sortedAliases = aliasGraph.sort();
+ // 3. 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;
+ }
+
+ // 4. 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();
+ // 5. Check for other errors preventing resolving components to install
+ if (!installerCalculator()->solve(aliasesSelectedForInstall)) {
+ setStatus(PackageManagerCore::Failure, installerCalculator()->error());
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("Error"),
+ tr("Unresolved component aliases"), installerCalculator()->error());
+
+ return false;
+ }
+
+ for (auto *alias : std::as_const(m_componentAliases))
+ alias->setSelected(false);
+
+ // 6. Restore original state
+ restoreCheckState();
+
+ return true;
+}
+
template <typename T>
bool PackageManagerCorePrivate::loadComponentScripts(const T &components, const bool postScript)
{
@@ -468,6 +609,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
@@ -486,6 +631,9 @@ ScriptEngine *PackageManagerCorePrivate::controlScriptEngine() const
void PackageManagerCorePrivate::clearAllComponentLists()
{
+ qDeleteAll(m_componentAliases);
+ m_componentAliases.clear();
+
QList<QInstaller::Component*> toDelete;
toDelete << m_rootComponents << m_deletedReplacedComponents;
@@ -645,6 +793,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);
@@ -773,7 +925,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")
);
@@ -864,7 +1016,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);
}
@@ -888,8 +1040,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();
@@ -922,6 +1074,8 @@ void PackageManagerCorePrivate::writeMaintenanceConfigFiles()
writer.writeEndElement();
writer.writeTextElement(QLatin1String("LocalCachePath"), m_data.settings().localCachePath());
writer.writeEndElement();
+
+ file.write(outputStr.toUtf8());
}
setDefaultFilePermissions(&file, DefaultFilePermissions::NonExecutable);
}
@@ -1070,8 +1224,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++;
}
}
}
@@ -1296,7 +1453,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"));
@@ -1390,6 +1547,7 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper
bool newBinaryWritten = false;
QString mtName = maintenanceToolName();
const QString installerBaseBinary = replaceVariables(m_installerBaseBinaryUnreplaced);
+ bool macOsMTBundleExtracted = false;
if (!installerBaseBinary.isEmpty() && QFileInfo::exists(installerBaseBinary)) {
qCDebug(QInstaller::lcInstallerInstallLog) << "Got a replacement installer base binary:"
<< installerBaseBinary;
@@ -1397,25 +1555,9 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper
// In macOS the installerbase is a whole app bundle. We do not modify the maintenancetool name in app bundle
// so that possible signing and notarization will remain. Therefore, the actual maintenance tool name might
// differ from the one defined in the settings.
- try {
- const QString maintenanceToolRenamedName = installerBaseBinary + QLatin1String(".new");
- qCDebug(QInstaller::lcInstallerInstallLog) << "Writing maintenance tool " << maintenanceToolRenamedName;
- QInstaller::copyDirectoryContents(installerBaseBinary, maintenanceToolRenamedName);
-
- newBinaryWritten = true;
- mtName = installerBaseBinary;
- } catch (const Error &error) {
- qCWarning(QInstaller::lcInstallerInstallLog) << error.message();
- }
- try {
- QInstaller::removeDirectory(installerBaseBinary);
- qCDebug(QInstaller::lcInstallerInstallLog) << "Removed installer base binary"
- << installerBaseBinary << "after updating the maintenance tool.";
- } catch (const Error &error) {
- qCDebug(QInstaller::lcInstallerInstallLog) << "Cannot remove installer base binary"
- << installerBaseBinary << "after updating the maintenance tool:"
- << error.message();
- }
+ newBinaryWritten = true;
+ mtName = installerBaseBinary;
+ macOsMTBundleExtracted = true;
} else {
writeMaintenanceToolAppBundle(performedOperations);
QFile replacementBinary(installerBaseBinary);
@@ -1557,7 +1699,9 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper
<< (restart ? "true." : "false.");
if (newBinaryWritten) {
- if (isInstaller())
+ if (macOsMTBundleExtracted)
+ deferredRename(mtName, targetDir() + QDir::separator() + fi.fileName(), restart);
+ else if (isInstaller())
QFile::rename(mtName + QLatin1String(".new"), mtName);
else
deferredRename(mtName + QLatin1String(".new"), mtName, restart);
@@ -1740,7 +1884,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;
@@ -1774,7 +1918,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"));
@@ -1972,7 +2116,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();
}
@@ -1993,7 +2137,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
@@ -2001,7 +2145,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"));
@@ -2070,7 +2214,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();
}
@@ -2078,7 +2222,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
@@ -2087,7 +2231,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.";
@@ -2249,7 +2393,7 @@ bool PackageManagerCorePrivate::runOfflineGenerator()
}
void PackageManagerCorePrivate::unpackComponents(const QList<Component *> &components,
- double progressOperationSize, bool adminRightsGained)
+ double progressOperationSize)
{
OperationList unpackOperations;
bool becameAdmin = false;
@@ -2273,7 +2417,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();
}
}
@@ -2321,7 +2465,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();
}
@@ -2390,8 +2534,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())
@@ -2412,7 +2555,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;
}
@@ -2501,23 +2644,67 @@ 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, fallbackReposFetched)) {
+ 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 {
+ for (const QString &possibleAliasName : components) {
+ if (ComponentAlias *alias = m_core->aliasByName(possibleAliasName)) {
+ if (alias->componentErrorMessage().isEmpty())
+ continue;
+ qCWarning(QInstaller::lcInstallerInstallLog).noquote().nospace() << alias->componentErrorMessage();
+ }
+ }
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote().nospace() << errorMessage
+ << "No components available with the current selection.";
+ }
+ }
return true;
+ };
+
+ if (!fetchComponents() && !fallbackReposFetched) {
+ fallbackReposFetched = true;
+ setStatus(PackageManagerCore::Running);
+ 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();
+ }
+ enableAllCategories();
+ fetchComponents();
}
- return false;
+
+ return m_core->status();
}
void PackageManagerCorePrivate::setComponentSelection(const QString &id, Qt::CheckState state)
@@ -2637,9 +2824,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
@@ -2676,8 +2866,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();
@@ -2688,7 +2878,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);
@@ -2796,6 +2986,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;
@@ -2858,9 +3067,9 @@ bool PackageManagerCorePrivate::addUpdateResourcesFromRepositories(bool compress
continue;
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"));
}
@@ -2913,6 +3122,125 @@ void PackageManagerCorePrivate::updateComponentInstallActions()
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(
+ componentsNotFoundForInstall,
+ [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::deferredRename(const QString &oldName, const QString &newName, bool restart)
+{
+
+#ifdef Q_OS_WINDOWS
+ const QString currentExecutable = QCoreApplication::applicationFilePath();
+ QString tmpExecutable = generateTemporaryFileName(currentExecutable);
+ QFile::rename(currentExecutable, tmpExecutable);
+ QFile::rename(oldName, newName);
+
+ QStringList arguments;
+ if (restart) {
+ // Restart with same command line arguments as first executable
+ arguments = QCoreApplication::arguments();
+ arguments.removeFirst(); // Remove program name
+ arguments.prepend(tmpExecutable);
+ arguments.prepend(QLatin1String("--")
+ + CommandLineOptions::scCleanupUpdate);
+ } else {
+ arguments.append(QLatin1String("--")
+ + CommandLineOptions::scCleanupUpdateOnly);
+ arguments.append(tmpExecutable);
+ }
+ QProcessWrapper::startDetached2(newName, arguments);
+#elif defined Q_OS_MACOS
+ // In macos oldName is the name of the maintenancetool we got from repository
+ // It might be extracted to a folder to avoid overlapping with running maintenancetool
+ // Here, ditto renames it to newName (and possibly moves from the subfolder).
+ if (oldName != newName) {
+ //1. Rename/move maintenancetool
+ QProcessWrapper process;
+ process.start(QLatin1String("ditto"), QStringList() << oldName << newName);
+ if (!process.waitForFinished()) {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Failed to rename maintenancetool from :" << oldName << " to "<<newName;
+ }
+ //2. Remove subfolder
+ QDir subDirectory(oldName);
+ subDirectory.cdUp();
+ QString subDirectoryPath = QDir::cleanPath(subDirectory.absolutePath());
+ QString targetDirectoryPath = QDir::cleanPath(targetDir());
+
+ //Make sure there is subdirectory in the targetdir so we don't delete the installation
+ if (subDirectoryPath.startsWith(targetDirectoryPath) && subDirectoryPath != targetDirectoryPath)
+ subDirectory.removeRecursively();
+ }
+
+ //3. If new maintenancetool name differs from original, remove the original maintenance tool
+ if (!isInstaller()) {
+ QString currentAppBundlePath = getAppBundlePath();
+ if (currentAppBundlePath != newName) {
+ QDir oldBundlePath(currentAppBundlePath);
+ oldBundlePath.removeRecursively();
+ }
+ }
+ SelfRestarter::setRestartOnQuit(restart);
+
+#elif defined Q_OS_LINUX
+ QFile::remove(newName);
+ QFile::rename(oldName, newName);
+ SelfRestarter::setRestartOnQuit(restart);
+#endif
+}
+
void PackageManagerCorePrivate::connectOperationCallMethodRequest(Operation *const operation)
{
QObject *const operationObject = dynamic_cast<QObject *> (operation);
@@ -2973,16 +3301,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.")
@@ -3008,24 +3336,6 @@ 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()
{
bool componentsOk = m_core->recalculateAllComponents();
@@ -3042,9 +3352,8 @@ bool PackageManagerCorePrivate::calculateComponentsAndRun()
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.";
@@ -3071,6 +3380,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();
@@ -3119,6 +3435,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 74cb0667c..c1b50e615 100644
--- a/src/libs/installer/packagemanagercore_p.h
+++ b/src/libs/installer/packagemanagercore_p.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2023 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);
@@ -169,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:
@@ -188,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;
@@ -217,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;
@@ -244,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();
@@ -257,18 +264,19 @@ 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 compressedRepository = false);
void processFilesForDelayedDeletion();
- void findExecutablesRecursive(const QString &path, const QStringList &excludeFiles, QStringList *result);
- QStringList runningInstallerProcesses(const QStringList &exludeFiles);
bool calculateComponentsAndRun();
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();
@@ -276,6 +284,13 @@ private:
void createLocalDependencyHash(const QString &componentName, const QString &dependencies);
void updateComponentInstallActions();
+ bool enableAllCategories();
+ void enableRepositoryCategory(const RepositoryCategory &repoCategory, const bool enable);
+
+ bool installablePackagesFound(const QStringList& components);
+
+ void deferredRename(const QString &oldName, const QString &newName, bool restart = false);
+
// remove once we deprecate isSelected, setSelected etc...
void restoreCheckState();
void storeCheckState();
@@ -286,6 +301,7 @@ private:
TempPathDeleter m_tmpPathDeleter;
bool m_updates;
+ bool m_aliases;
bool m_repoFetched;
bool m_updateSourcesAdded;
qint64 m_magicBinaryMarker;
@@ -308,6 +324,7 @@ private:
ComponentModel *m_defaultModel;
ComponentModel *m_updaterModel;
+ ComponentSortFilterProxyModel *m_componentSortFilterProxyModel;
QObject *m_guiObject;
QScopedPointer<RemoteFileEngineHandler> m_remoteFileEngineHandler;
@@ -325,6 +342,9 @@ private:
QHash<QString, QStringList > m_componentReplaces;
QString m_datFileName;
+ bool m_allowCompressedRepositoryInstall;
+ int m_connectedOperations;
+ QStringList m_componentsToBeInstalled;
};
} // namespace QInstaller
diff --git a/src/libs/installer/packagemanagercoredata.cpp b/src/libs/installer/packagemanagercoredata.cpp
index 1d5b9f713..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))
diff --git a/src/libs/installer/packagemanagergui.cpp b/src/libs/installer/packagemanagergui.cpp
index c3642e4ee..1f0462eea 100644
--- a/src/libs/installer/packagemanagergui.cpp
+++ b/src/libs/installer/packagemanagergui.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2023 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.
@@ -412,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);
@@ -689,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.
*/
@@ -1245,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();
@@ -1452,19 +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
-
- 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
@@ -1503,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);
@@ -1551,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"));
@@ -1607,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();
@@ -1654,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('&')));
@@ -1686,7 +1691,7 @@ bool IntroductionPage::validatePage()
}
}
- if (m_allPackagesFetched || localPackagesTreeFetched)
+ if (m_allPackagesFetched)
setComplete(true);
}
@@ -1833,22 +1838,6 @@ void IntroductionPage::setErrorMessage(const QString &error)
#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
@@ -1892,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()) {
@@ -1927,7 +1916,7 @@ void IntroductionPage::onCoreNetworkSettingsChanged()
PackageManagerCore *core = packageManagerCore();
if (core->isUninstaller() || core->isMaintainer()) {
- m_offlineMaintenanceTool = !validRepositoriesAvailable();
+ m_offlineMaintenanceTool = !core->validRepositoriesAvailable();
setMaintainerToolsEnabled(!m_offlineMaintenanceTool);
m_removeAllComponents->setChecked(m_offlineMaintenanceTool);
@@ -2117,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)
@@ -2210,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.")
};
@@ -2228,14 +2217,13 @@ void ComponentSelectionPage::entering()
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();
}
/*!
@@ -2245,6 +2233,7 @@ void ComponentSelectionPage::entering()
void ComponentSelectionPage::leaving()
{
d->hideCompressedRepositoryButton();
+ d->showCreateOfflineInstallerButton(false);
}
/*!
@@ -2328,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.
@@ -2357,6 +2336,11 @@ bool ComponentSelectionPage::addVirtualComponentToUninstall(const QString &name)
return false;
}
+void ComponentSelectionPage::setAllowCreateOfflineInstaller(bool allow)
+{
+ d->setAllowCreateOfflineInstaller(allow);
+}
+
void ComponentSelectionPage::setModified(bool modified)
{
setComplete(modified);
@@ -2370,18 +2354,7 @@ bool ComponentSelectionPage::isComplete() const
if (!d->componentsResolved())
return false;
- if (packageManagerCore()->isInstaller() || packageManagerCore()->isUpdater())
- return d->m_currentModel->checked().count();
-
- if (d->m_currentModel->checkedState().testFlag(ComponentModel::DefaultChecked) == false)
- return true;
-
- 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();
}
@@ -2719,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"));
@@ -2735,11 +2712,10 @@ void ReadyForInstallationPage::entering()
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);
}
}
@@ -2759,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"));
@@ -2817,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,
@@ -2886,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()));
@@ -2910,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"));
@@ -3014,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);
@@ -3039,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()));
@@ -3132,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();
}
/*!
@@ -3186,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 ba27c4af6..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,6 +82,7 @@ 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);
@@ -261,7 +262,6 @@ private:
void leaving() override;
void showWidgets(bool show);
- bool validRepositoriesAvailable() const;
private:
bool m_updatesFetched;
@@ -330,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/permissionsettings.cpp b/src/libs/installer/permissionsettings.cpp
index 68e4ab427..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,38 +37,6 @@ using namespace QInstaller;
\internal
*/
-PermissionSettings::PermissionSettings(const QString &organization, const QString &application, QObject *parent)
- : QSettings(organization, application, parent)
-{
-#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
- setIniCodec("UTF-8"); // to workaround QTBUG-102334
-#endif
-}
-
-PermissionSettings::PermissionSettings(Scope scope, const QString &organization, const QString &application, QObject *parent)
- : QSettings(scope, organization, application, parent)
-{
-#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
- setIniCodec("UTF-8"); // QTBUG-102334
-#endif
-}
-
-PermissionSettings::PermissionSettings(Format format, Scope scope, const QString &organization, const QString &application, QObject *parent)
- : QSettings(format, scope, organization, application, parent)
-{
-#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
- setIniCodec("UTF-8"); // QTBUG-102334
-#endif
-}
-
-PermissionSettings::PermissionSettings(const QString &fileName, Format format, QObject *parent)
- : QSettings(fileName, format, parent)
-{
-#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
- setIniCodec("UTF-8"); // QTBUG-102334
-#endif
-}
-
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/qprocesswrapper.cpp b/src/libs/installer/qprocesswrapper.cpp
index 6f34e36da..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.
@@ -54,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)));
@@ -88,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)) {
@@ -191,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));
@@ -216,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));
@@ -263,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();
@@ -296,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();
@@ -343,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);
@@ -354,7 +354,7 @@ 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);
@@ -376,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();
@@ -443,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);
@@ -455,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);
@@ -467,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/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/remotefileengine.cpp b/src/libs/installer/remotefileengine.cpp
index b600da618..2ead83861 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) 2024 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);
}
@@ -534,7 +554,11 @@ bool RemoteFileEngine::renameOverwrite(const QString &newName)
return m_fileEngine.renameOverwrite(newName);
}
+#if QT_VERSION < QT_VERSION_CHECK(6, 7, 0)
QDateTime RemoteFileEngine::fileTime(FileTime time) const
+#else
+QDateTime RemoteFileEngine::fileTime(QFile::FileTime time) const
+#endif
{
if ((const_cast<RemoteFileEngine *>(this))->connectToServer()) {
return callRemoteMethod<QDateTime>
diff --git a/src/libs/installer/remotefileengine.h b/src/libs/installer/remotefileengine.h
index 35ebf7742..c46e861ca 100644
--- a/src/libs/installer/remotefileengine.h
+++ b/src/libs/installer/remotefileengine.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.
@@ -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;
@@ -77,7 +87,11 @@ public:
QString fileName(FileName file = DefaultName) const override;
uint ownerId(FileOwner owner) const override;
QString owner(FileOwner owner) const override;
+#if QT_VERSION < QT_VERSION_CHECK(6, 7, 0)
QDateTime fileTime(FileTime time) const override;
+#else
+ QDateTime fileTime(QFile::FileTime time) const override;
+#endif
void setFileName(const QString &fileName) override;
int handle() const override;
bool atEnd() const;
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 c423de943..ddd512588 100644
--- a/src/libs/installer/remoteobject.h
+++ b/src/libs/installer/remoteobject.h
@@ -52,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)
+ template<typename... Args>
+ void callRemoteMethodDefaultReply(const QString &name, const Args&... args)
{
- const QString reply = sendReceivePacket<QString>(name, arg, arg2, dummy);
+ const QString reply = sendReceivePacket<QString>(name, args...);
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 T, typename... Args>
+ T callRemoteMethod(const QString &name, const Args&... args) const
{
- const QString reply = sendReceivePacket<QString>(name, arg, arg2, arg3);
- 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
- {
- 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 ed3d343fe..55c4f48d9 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) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -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;
@@ -570,7 +580,11 @@ void RemoteServerConnection::handleQFSFileEngine(RemoteServerReply *reply, const
} else if (command == QLatin1String(Protocol::QAbstractFileEngineFileTime)) {
qint32 filetime;
data >> filetime;
+#if QT_VERSION < QT_VERSION_CHECK(6, 7, 0)
reply->send(m_engine->fileTime(static_cast<QAbstractFileEngine::FileTime> (filetime)));
+#else
+ reply->send(m_engine->fileTime(static_cast<QFile::FileTime> (filetime)));
+#endif
} else if (!command.isEmpty()) {
qCDebug(QInstaller::lcServer) << "Unknown QAbstractFileEngine command:" << command;
}
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 4ef8f9f25..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,9 +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)
{
}
@@ -77,6 +79,7 @@ Repository::Repository(const QUrl &url, bool isDefault, bool compressed)
, m_default(isDefault)
, m_enabled(true)
, m_compressed(compressed)
+ , m_postLoadComponentScript(false)
{
}
@@ -252,6 +255,22 @@ 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!=()
*/
@@ -259,7 +278,8 @@ 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_xmlChecksum == other.m_xmlChecksum;
+ && m_displayname == other.m_displayname && m_xmlChecksum == other.m_xmlChecksum
+ && m_postLoadComponentScript == other.m_postLoadComponentScript;
}
/*!
@@ -288,6 +308,7 @@ const Repository &Repository::operator=(const Repository &other)
m_compressed = other.m_compressed;
m_categoryname = other.m_categoryname;
m_xmlChecksum = other.m_xmlChecksum;
+ m_postLoadComponentScript = other.m_postLoadComponentScript;
return *this;
}
@@ -307,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 >> repository.m_xmlChecksum;
+ >> 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)));
@@ -323,7 +344,7 @@ 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_xmlChecksum.toBase64();
+ << repository.m_xmlChecksum.toBase64() << repository.m_postLoadComponentScript;
}
}
diff --git a/src/libs/installer/repository.h b/src/libs/installer/repository.h
index 1edb83449..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.
@@ -71,10 +71,13 @@ public:
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);
@@ -90,9 +93,10 @@ private:
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 6be292330..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();
}
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/scriptengine.cpp b/src/libs/installer/scriptengine.cpp
index baf348868..7e3e69eb2 100644
--- a/src/libs/installer/scriptengine.cpp
+++ b/src/libs/installer/scriptengine.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2023 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.
@@ -56,18 +56,6 @@ namespace QInstaller {
/*!
\inmodule QtInstallerFramework
- \class QInstaller::ConsoleProxy
- \internal
-*/
-
-/*!
- \inmodule QtInstallerFramework
- \class QInstaller::InstallerProxy
- \internal
-*/
-
-/*!
- \inmodule QtInstallerFramework
\class QInstaller::QDesktopServicesProxy
\internal
*/
@@ -78,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;
@@ -223,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)
@@ -382,14 +354,10 @@ 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());
@@ -409,11 +377,6 @@ ScriptEngine::ScriptEngine(PackageManagerCore *core) : QObject(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")));
}
/*!
@@ -661,14 +624,19 @@ QJSValue ScriptEngine::generateDesktopServicesObject()
SETPROPERTY(desktopServices, PicturesLocation, QStandardPaths)
SETPROPERTY(desktopServices, TempLocation, QStandardPaths)
SETPROPERTY(desktopServices, HomeLocation, QStandardPaths)
- SETPROPERTY(desktopServices, AppDataLocation, 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_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 85ab0dece..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();
diff --git a/src/libs/installer/settings.h b/src/libs/installer/settings.h
index f98319110..85b59869c 100644
--- a/src/libs/installer/settings.h
+++ b/src/libs/installer/settings.h
@@ -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/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/utils.cpp b/src/libs/installer/utils.cpp
index 154a66553..9b64ade37 100644
--- a/src/libs/installer/utils.cpp
+++ b/src/libs/installer/utils.cpp
@@ -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
}