summaryrefslogtreecommitdiffstats
path: root/src/libs/installer/packagemanagercore_p.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/installer/packagemanagercore_p.cpp')
-rw-r--r--src/libs/installer/packagemanagercore_p.cpp1275
1 files changed, 906 insertions, 369 deletions
diff --git a/src/libs/installer/packagemanagercore_p.cpp b/src/libs/installer/packagemanagercore_p.cpp
index d2f33c1b9..858698a7a 100644
--- a/src/libs/installer/packagemanagercore_p.cpp
+++ b/src/libs/installer/packagemanagercore_p.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -31,7 +31,6 @@
#include "binarycontent.h"
#include "binaryformatenginehandler.h"
#include "binarylayout.h"
-#include "component.h"
#include "scriptengine.h"
#include "componentmodel.h"
#include "errors.h"
@@ -46,19 +45,25 @@
#include "qsettingswrapper.h"
#include "installercalculator.h"
#include "uninstallercalculator.h"
+#include "componentalias.h"
#include "componentchecker.h"
#include "globals.h"
#include "binarycreator.h"
#include "loggingutils.h"
+#include "concurrentoperationrunner.h"
+#include "remoteclient.h"
+#include "operationtracer.h"
#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>
@@ -87,36 +92,6 @@ namespace QInstaller {
\internal
*/
-class OperationTracer
-{
-public:
- OperationTracer(Operation *operation) : m_operation(nullptr)
- {
- // don't create output for that hacky pseudo operation
- if (operation->name() != QLatin1String("MinimumProgress"))
- m_operation = operation;
- }
- void trace(const QString &state)
- {
- if (!m_operation)
- return;
- qCDebug(QInstaller::lcInstallerInstallLog).noquote() << QString::fromLatin1("%1 %2 operation: %3")
- .arg(state, m_operation->value(QLatin1String("component")).toString(), m_operation->name());
- QStringList args = m_operation->arguments();
- if (m_operation->requiresUnreplacedVariables())
- args = m_operation->packageManager()->replaceVariables(m_operation->arguments());
- qCDebug(QInstaller::lcInstallerInstallLog).noquote() << QString::fromLatin1("\t- arguments: %1")
- .arg(args.join(QLatin1String(", ")));
- }
- ~OperationTracer() {
- if (!m_operation)
- return;
- qCDebug(QInstaller::lcInstallerInstallLog) << "Done";
- }
-private:
- Operation *m_operation;
-};
-
static bool runOperation(Operation *operation, Operation::OperationType type)
{
OperationTracer tracer(operation);
@@ -151,54 +126,27 @@ static QStringList checkRunningProcessesFromList(const QStringList &processList)
static void deferredRename(const QString &oldName, const QString &newName, bool restart = false)
{
#ifdef Q_OS_WIN
- QStringList arguments;
-
- // Check if .vbs extension can be used for running renaming script. If not, create own extension
- QString extension = QLatin1String(".vbs");
- QSettingsWrapper settingRoot(QLatin1String("HKEY_CLASSES_ROOT\\.vbs"), QSettingsWrapper::NativeFormat);
- if (settingRoot.value(QLatin1String(".")).toString() != QLatin1String("VBSFile")) {
- extension = QLatin1String(".qtInstaller");
- QSettingsWrapper settingsUser(QLatin1String("HKEY_CURRENT_USER\\Software\\Classes"), QSettingsWrapper::NativeFormat);
- QString value = settingsUser.value(extension).toString();
- if (value != QLatin1String("VBSFile"))
- settingsUser.setValue(extension, QLatin1String("VBSFile"));
- }
- QTemporaryFile f(QDir::temp().absoluteFilePath(QLatin1String("deferredrenameXXXXXX%1")).arg(extension));
+ const QString currentExecutable = QCoreApplication::applicationFilePath();
+ const QString tmpExecutable = generateTemporaryFileName(currentExecutable);
- QInstaller::openForWrite(&f);
- f.setAutoRemove(false);
+ QFile::rename(currentExecutable, tmpExecutable);
+ QFile::rename(oldName, newName);
- arguments << QDir::toNativeSeparators(f.fileName()) << QDir::toNativeSeparators(oldName)
- << QDir::toNativeSeparators(QFileInfo(oldName).dir().absoluteFilePath(QFileInfo(newName)
- .fileName()));
-
- QTextStream batch(&f);
- batch.setCodec("UTF-16");
- batch << "Set fso = WScript.CreateObject(\"Scripting.FileSystemObject\")\n";
- batch << "Set tmp = WScript.CreateObject(\"WScript.Shell\")\n";
- batch << QString::fromLatin1("file = \"%1\"\n").arg(arguments[2]);
- batch << "on error resume next\n";
-
- batch << "while fso.FileExists(file)\n";
- batch << " fso.DeleteFile(file)\n";
- batch << " WScript.Sleep(1000)\n";
- batch << "wend\n";
- batch << QString::fromLatin1("fso.MoveFile \"%1\", file\n").arg(arguments[1]);
+ QStringList arguments;
if (restart) {
- //Restart with same command line arguments as first executable
- QStringList commandLineArguments = QCoreApplication::arguments();
- batch << QString::fromLatin1("tmp.exec \"%1 --%2")
- .arg(arguments[2]).arg(CommandLineOptions::scStartUpdaterLong);
- //Skip the first argument as that is executable itself
- for (int i = 1; i < commandLineArguments.count(); i++) {
- batch << QString::fromLatin1(" %1").arg(commandLineArguments.at(i));
- }
- batch << QString::fromLatin1("\"\n");
+ // 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);
}
- batch << "fso.DeleteFile(WScript.ScriptFullName)\n";
+ QProcessWrapper::startDetached2(newName, arguments);
- QProcessWrapper::startDetached(QLatin1String("cscript"), QStringList() << QLatin1String("//Nologo")
- << arguments[0]);
#else
QFile::remove(newName);
QFile::rename(oldName, newName);
@@ -206,27 +154,56 @@ static void deferredRename(const QString &oldName, const QString &newName, bool
#endif
}
+static bool filterMissingAliasesToInstall(const QString& component, const QList<ComponentAlias *> packages)
+{
+ bool packageFound = false;
+ for (qsizetype i = 0; i < packages.size(); ++i) {
+ packageFound = (packages.at(i)->name() == component);
+ if (packageFound)
+ break;
+ }
+ return !packageFound;
+}
+
+static bool filterMissingPackagesToInstall(const QString& component, const PackagesList& packages)
+{
+ bool packageFound = false;
+ for (qsizetype i = 0; i < packages.size(); ++i) {
+ packageFound = (packages.at(i)->data(scName).toString() == component);
+ if (packageFound)
+ break;
+ }
+ return !packageFound;
+}
// -- PackageManagerCorePrivate
PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core)
: m_updateFinder(nullptr)
- , m_compressedFinder(nullptr)
+ , m_aliasFinder(nullptr)
, m_localPackageHub(std::make_shared<LocalPackageHub>())
, m_status(PackageManagerCore::Unfinished)
, m_needsHardRestart(false)
, m_testChecksum(false)
, m_launchedAsRoot(AdminAuthorization::hasAdminRights())
+ , m_commandLineInstance(false)
+ , m_defaultInstall(false)
+ , m_userSetBinaryMarker(false)
+ , m_checkAvailableSpace(true)
, m_completeUninstall(false)
, m_needToWriteMaintenanceTool(false)
, m_dependsOnLocalInstallerBinary(false)
+ , m_autoAcceptLicenses(false)
+ , m_disableWriteMaintenanceTool(false)
+ , m_autoConfirmCommand(false)
, m_core(core)
, m_updates(false)
+ , m_aliases(false)
, m_repoFetched(false)
, m_updateSourcesAdded(false)
, m_magicBinaryMarker(0) // initialize with pseudo marker
, m_magicMarkerSupplement(BinaryContent::Default)
- , m_componentsToInstallCalculated(false)
+ , m_foundEssentialUpdate(false)
, m_componentScriptEngine(nullptr)
, m_controlScriptEngine(nullptr)
, m_installerCalculator(nullptr)
@@ -234,38 +211,46 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core)
, m_proxyFactory(nullptr)
, m_defaultModel(nullptr)
, m_updaterModel(nullptr)
+ , m_componentSortFilterProxyModel(nullptr)
, m_guiObject(nullptr)
, m_remoteFileEngineHandler(nullptr)
- , m_foundEssentialUpdate(false)
- , m_commandLineInstance(false)
- , m_defaultInstall(false)
- , m_userSetBinaryMarker(false)
- , m_checkAvailableSpace(true)
- , m_autoAcceptLicenses(false)
- , m_disableWriteMaintenanceTool(false)
- , m_autoConfirmCommand(false)
+ , m_datFileName(QString())
+#ifdef INSTALLCOMPRESSED
+ , m_allowCompressedRepositoryInstall(true)
+#else
+ , m_allowCompressedRepositoryInstall(false)
+#endif
+ , m_connectedOperations(0)
{
}
PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, qint64 magicInstallerMaker,
- const QList<OperationBlob> &performedOperations)
+ const QList<OperationBlob> &performedOperations, const QString &datFileName)
: m_updateFinder(nullptr)
- , m_compressedFinder(nullptr)
+ , m_aliasFinder(nullptr)
, m_localPackageHub(std::make_shared<LocalPackageHub>())
, m_status(PackageManagerCore::Unfinished)
, m_needsHardRestart(false)
, m_testChecksum(false)
, m_launchedAsRoot(AdminAuthorization::hasAdminRights())
+ , m_commandLineInstance(false)
+ , m_defaultInstall(false)
+ , m_userSetBinaryMarker(false)
+ , m_checkAvailableSpace(true)
, m_completeUninstall(false)
, m_needToWriteMaintenanceTool(false)
, m_dependsOnLocalInstallerBinary(false)
+ , m_autoAcceptLicenses(false)
+ , m_disableWriteMaintenanceTool(false)
+ , m_autoConfirmCommand(false)
, m_core(core)
, m_updates(false)
+ , m_aliases(false)
, m_repoFetched(false)
, m_updateSourcesAdded(false)
, m_magicBinaryMarker(magicInstallerMaker)
, m_magicMarkerSupplement(BinaryContent::Default)
- , m_componentsToInstallCalculated(false)
+ , m_foundEssentialUpdate(false)
, m_componentScriptEngine(nullptr)
, m_controlScriptEngine(nullptr)
, m_installerCalculator(nullptr)
@@ -273,21 +258,21 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, q
, m_proxyFactory(nullptr)
, m_defaultModel(nullptr)
, m_updaterModel(nullptr)
+ , m_componentSortFilterProxyModel(nullptr)
, m_guiObject(nullptr)
, m_remoteFileEngineHandler(new RemoteFileEngineHandler)
- , m_foundEssentialUpdate(false)
- , m_commandLineInstance(false)
- , m_defaultInstall(false)
- , m_userSetBinaryMarker(false)
- , m_checkAvailableSpace(true)
- , m_autoAcceptLicenses(false)
- , m_disableWriteMaintenanceTool(false)
- , m_autoConfirmCommand(false)
+ , m_datFileName(datFileName)
+#ifdef INSTALLCOMPRESSED
+ , m_allowCompressedRepositoryInstall(true)
+#else
+ , m_allowCompressedRepositoryInstall(false)
+#endif
+ , m_connectedOperations(0)
{
foreach (const OperationBlob &operation, performedOperations) {
- QScopedPointer<QInstaller::Operation> op(KDUpdater::UpdateOperationFactory::instance()
+ std::unique_ptr<QInstaller::Operation> op(KDUpdater::UpdateOperationFactory::instance()
.create(operation.name, core));
- if (op.isNull()) {
+ if (!op) {
qCWarning(QInstaller::lcInstallerInstallLog) << "Failed to load unknown operation"
<< operation.name;
continue;
@@ -298,7 +283,7 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, q
<< operation.name;
continue;
}
- m_performedOperationsOld.append(op.take());
+ m_performedOperationsOld.append(op.release());
}
connect(this, &PackageManagerCorePrivate::installationStarted,
@@ -327,6 +312,7 @@ PackageManagerCorePrivate::~PackageManagerCorePrivate()
qDeleteAll(m_performedOperationsCurrentSession);
delete m_updateFinder;
+ delete m_aliasFinder;
delete m_proxyFactory;
delete m_defaultModel;
@@ -368,7 +354,8 @@ bool PackageManagerCorePrivate::isProcessRunning(const QString &name,
}
/* static */
-bool PackageManagerCorePrivate::performOperationThreaded(Operation *operation, Operation::OperationType type)
+bool PackageManagerCorePrivate::performOperationThreaded(Operation *operation,
+ Operation::OperationType type)
{
QFutureWatcher<bool> futureWatcher;
const QFuture<bool> future = QtConcurrent::run(runOperation, operation, type);
@@ -429,11 +416,10 @@ bool PackageManagerCorePrivate::buildComponentTree(QHash<QString, Component*> &c
m_core->appendRootComponent(component);
}
- // after everything is set up, load the scripts if needed
- if (loadScript) {
- foreach (QInstaller::Component *component, components)
- component->loadComponentScript();
- }
+ // after everything is set up, load the scripts if needed and create helper hashes
+ // for autodependency and dependency components for quicker search later
+ if (loadScript && !loadComponentScripts(components))
+ return false;
// now we can preselect components in the tree
foreach (QInstaller::Component *component, components) {
@@ -458,10 +444,10 @@ bool PackageManagerCorePrivate::buildComponentTree(QHash<QString, Component*> &c
component->setCheckState(Qt::Checked);
clearInstallerCalculator();
- if (installerCalculator()->appendComponentsToInstall(components.values()) == false) {
- setStatus(PackageManagerCore::Failure, installerCalculator()->componentsToInstallError());
+ if (installerCalculator()->solve(components.values()) == false) {
+ setStatus(PackageManagerCore::Failure, installerCalculator()->error());
MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("Error"),
- tr("Unresolved dependencies"), installerCalculator()->componentsToInstallError());
+ tr("Unresolved dependencies"), installerCalculator()->error());
return false;
}
@@ -477,7 +463,6 @@ bool PackageManagerCorePrivate::buildComponentTree(QHash<QString, Component*> &c
} catch (const Error &error) {
clearAllComponentLists();
- emit m_core->finishAllComponentsReset(QList<QInstaller::Component*>());
setStatus(PackageManagerCore::Failure, error.message());
// TODO: make sure we remove all message boxes inside the library at some point.
@@ -488,8 +473,120 @@ bool PackageManagerCorePrivate::buildComponentTree(QHash<QString, Component*> &c
return true;
}
+bool PackageManagerCorePrivate::buildComponentAliases()
+{
+ // For now, aliases are only used for command line runs
+ if (!m_core->isCommandLineInstance())
+ return true;
+
+ {
+ const QList<ComponentAlias *> aliasList = componentAliases();
+ if (aliasList.isEmpty())
+ return true;
+
+ for (const auto *alias : aliasList) {
+ // Create a new alias object for package manager core to take ownership of
+ ComponentAlias *newAlias = new ComponentAlias(m_core);
+ for (const QString &key : alias->keys())
+ newAlias->setValue(key, alias->value(key));
+
+ m_componentAliases.insert(alias->name(), newAlias);
+ }
+ }
+ // After aliases are loaded, perform sanity checks:
+
+ // 1. Component check state is changed by alias selection, so store the initial state
+ storeCheckState();
+
+ Graph<QString> aliasGraph;
+ for (auto *alias : qAsConst(m_componentAliases)) {
+ aliasGraph.addNode(alias->name());
+ aliasGraph.addEdges(alias->name(),
+ QInstaller::splitStringWithComma(alias->value(scRequiredAliases)) <<
+ QInstaller::splitStringWithComma(alias->value(scOptionalAliases)));
+
+ if (!m_core->componentByName(alias->name())) {
+ // Name ok, select for sanity check calculation
+ alias->setSelected(true);
+ } else {
+ alias->setUnstable(ComponentAlias::ComponentNameConfict,
+ tr("Alias declares name that conflicts with an existing component \"%1\"")
+ .arg(alias->name()));
+ }
+ }
+
+ const QList<QString> sortedAliases = aliasGraph.sort();
+ // 2. Check for cyclic dependency errors
+ if (aliasGraph.hasCycle()) {
+ setStatus(PackageManagerCore::Failure, installerCalculator()->error());
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("Error"),
+ tr("Unresolved component aliases"),
+ tr("Cyclic dependency between aliases \"%1\" and \"%2\" detected.")
+ .arg(aliasGraph.cycle().first, aliasGraph.cycle().second));
+
+ return false;
+ }
+
+ // 3. Test for required aliases and components, this triggers setting the
+ // alias unstable in case of a broken reference.
+ for (const auto &aliasName : sortedAliases) {
+ ComponentAlias *alias = m_componentAliases.value(aliasName);
+ if (!alias) // sortedAliases may contain dependencies that don't exist, we don't know it yet
+ continue;
+
+ alias->components();
+ alias->aliases();
+ }
+
+ clearInstallerCalculator();
+ // 4. Check for other errors preventing resolving components to install
+ if (!installerCalculator()->solve(m_componentAliases.values())) {
+ setStatus(PackageManagerCore::Failure, installerCalculator()->error());
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("Error"),
+ tr("Unresolved component aliases"), installerCalculator()->error());
+
+ return false;
+ }
+
+ for (auto *alias : qAsConst(m_componentAliases))
+ alias->setSelected(false);
+
+ // 5. Restore original state
+ restoreCheckState();
+
+ return true;
+}
+
+template <typename T>
+bool PackageManagerCorePrivate::loadComponentScripts(const T &components, const bool postScript)
+{
+ infoMessage(nullptr, tr("Loading component scripts..."));
+
+ quint64 loadedComponents = 0;
+ for (auto *component : components) {
+ if (statusCanceledOrFailed())
+ return false;
+
+ component->loadComponentScript(postScript);
+ ++loadedComponents;
+
+ const int currentProgress = qRound(double(loadedComponents) / components.count() * 100);
+ infoProgress(nullptr, currentProgress, 100);
+ qApp->processEvents();
+ }
+ return true;
+}
+
+template bool PackageManagerCorePrivate::loadComponentScripts<QList<Component *>>(const QList<Component *> &, const bool);
+template bool PackageManagerCorePrivate::loadComponentScripts<QHash<QString, Component *>>(const QHash<QString, Component *> &, const bool);
+
void PackageManagerCorePrivate::cleanUpComponentEnvironment()
{
+ m_componentReplaces.clear();
+ m_autoDependencyComponentHash.clear();
+ m_localDependencyComponentHash.clear();
+ m_localVirtualComponents.clear();
+ m_componentByNameHash.clear();
// clean up registered (downloaded) data
if (m_core->isMaintainer())
BinaryFormatEngineHandler::instance()->clear();
@@ -498,6 +595,10 @@ void PackageManagerCorePrivate::cleanUpComponentEnvironment()
// so we need to remove the current component script engine
delete m_componentScriptEngine;
m_componentScriptEngine = nullptr;
+
+ // Calculators become invalid after clearing components
+ clearInstallerCalculator();
+ clearUninstallerCalculator();
}
ScriptEngine *PackageManagerCorePrivate::componentScriptEngine() const
@@ -516,18 +617,18 @@ ScriptEngine *PackageManagerCorePrivate::controlScriptEngine() const
void PackageManagerCorePrivate::clearAllComponentLists()
{
+ qDeleteAll(m_componentAliases);
+ m_componentAliases.clear();
+
QList<QInstaller::Component*> toDelete;
- toDelete << m_rootComponents;
+ toDelete << m_rootComponents << m_deletedReplacedComponents;
m_rootComponents.clear();
-
m_rootDependencyReplacements.clear();
+ m_deletedReplacedComponents.clear();
- const QList<QPair<Component*, Component*> > list = m_componentsToReplaceAllMode.values();
- for (int i = 0; i < list.count(); ++i)
- toDelete << list.at(i).second;
m_componentsToReplaceAllMode.clear();
- m_componentsToInstallCalculated = false;
+ m_foundEssentialUpdate = false;
qDeleteAll(toDelete);
cleanUpComponentEnvironment();
@@ -535,8 +636,10 @@ void PackageManagerCorePrivate::clearAllComponentLists()
void PackageManagerCorePrivate::clearUpdaterComponentLists()
{
- QSet<Component*> usedComponents =
- QSet<Component*>::fromList(m_updaterComponents + m_updaterComponentsDeps);
+
+ QSet<Component*> usedComponents(m_updaterComponents.begin(), m_updaterComponents.end());
+ usedComponents.unite(QSet<Component*>(m_updaterComponentsDeps.begin(),
+ m_updaterComponentsDeps.end()));
const QList<QPair<Component*, Component*> > list = m_componentsToReplaceUpdaterMode.values();
for (int i = 0; i < list.count(); ++i) {
@@ -552,7 +655,7 @@ void PackageManagerCorePrivate::clearUpdaterComponentLists()
m_updaterDependencyReplacements.clear();
m_componentsToReplaceUpdaterMode.clear();
- m_componentsToInstallCalculated = false;
+ m_foundEssentialUpdate = false;
qDeleteAll(usedComponents);
cleanUpComponentEnvironment();
@@ -568,6 +671,11 @@ QHash<QString, QPair<Component*, Component*> > &PackageManagerCorePrivate::compo
return (!isUpdater()) ? m_componentsToReplaceAllMode : m_componentsToReplaceUpdaterMode;
}
+QHash<QString, QStringList> &PackageManagerCorePrivate::componentReplaces()
+{
+ return m_componentReplaces;
+}
+
void PackageManagerCorePrivate::clearInstallerCalculator()
{
delete m_installerCalculator;
@@ -578,8 +686,7 @@ InstallerCalculator *PackageManagerCorePrivate::installerCalculator() const
{
if (!m_installerCalculator) {
PackageManagerCorePrivate *const pmcp = const_cast<PackageManagerCorePrivate *> (this);
- pmcp->m_installerCalculator = new InstallerCalculator(
- m_core->components(PackageManagerCore::ComponentType::AllNoReplacements));
+ pmcp->m_installerCalculator = new InstallerCalculator(m_core, pmcp->m_autoDependencyComponentHash);
}
return m_installerCalculator;
}
@@ -603,7 +710,8 @@ UninstallerCalculator *PackageManagerCorePrivate::uninstallerCalculator() const
}
}
- pmcp->m_uninstallerCalculator = new UninstallerCalculator(installedComponents);
+ pmcp->m_uninstallerCalculator = new UninstallerCalculator(m_core,
+ pmcp->m_autoDependencyComponentHash, pmcp->m_localDependencyComponentHash, pmcp->m_localVirtualComponents);
}
return m_uninstallerCalculator;
}
@@ -612,10 +720,9 @@ void PackageManagerCorePrivate::initialize(const QHash<QString, QString> &params
{
m_coreCheckedHash.clear();
m_data = PackageManagerCoreData(params, isInstaller());
- m_componentsToInstallCalculated = false;
#ifdef Q_OS_LINUX
- if (m_launchedAsRoot)
+ if (m_launchedAsRoot && isInstaller())
m_data.setValue(scTargetDir, replaceVariables(m_data.settings().adminTargetDir()));
#endif
@@ -670,11 +777,16 @@ void PackageManagerCorePrivate::initialize(const QHash<QString, QString> &params
m_localPackageHub->setApplicationVersion(QLatin1String(QUOTE(IFW_REPOSITORY_FORMAT_VERSION)));
if (isInstaller())
- m_packageSources.insert(PackageSource(QUrl(QLatin1String("resource://metadata/")), 0));
+ 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);
+
connect(&m_metadataJob, &Job::infoMessage, this, &PackageManagerCorePrivate::infoMessage);
connect(&m_metadataJob, &Job::progress, this, &PackageManagerCorePrivate::infoProgress);
connect(&m_metadataJob, &Job::totalProgress, this, &PackageManagerCorePrivate::totalProgress);
@@ -777,7 +889,12 @@ Operation *PackageManagerCorePrivate::takeOwnedOperation(Operation *operation)
QString PackageManagerCorePrivate::maintenanceToolName() const
{
- QString filename = m_data.settings().maintenanceToolName();
+ QString filename;
+ if (isInstaller())
+ filename = m_data.settings().maintenanceToolName();
+ else
+ filename = QCoreApplication::applicationName();
+
#if defined(Q_OS_MACOS)
if (QInstaller::isInBundle(QCoreApplication::applicationDirPath()))
filename += QLatin1String(".app/Contents/MacOS/") + filename;
@@ -787,6 +904,27 @@ QString PackageManagerCorePrivate::maintenanceToolName() const
return QString::fromLatin1("%1/%2").arg(targetDir()).arg(filename);
}
+QString PackageManagerCorePrivate::maintenanceToolAliasPath() const
+{
+#ifdef Q_OS_MACOS
+ const QString aliasName = m_data.settings().maintenanceToolAlias();
+ if (aliasName.isEmpty())
+ return QString();
+
+ const bool isRoot = m_core->hasAdminRights();
+ const QString applicationsDir = m_core->value(
+ isRoot ? QLatin1String("ApplicationsDir") : QLatin1String("ApplicationsDirUser")
+ );
+ QString maintenanceToolAlias = QString::fromLatin1("%1/%2")
+ .arg(applicationsDir, aliasName);
+ if (!maintenanceToolAlias.endsWith(QLatin1String(".app")))
+ maintenanceToolAlias += QLatin1String(".app");
+
+ return maintenanceToolAlias;
+#endif
+ return QString();
+}
+
QString PackageManagerCorePrivate::offlineBinaryName() const
{
QString filename = m_core->value(scOfflineBinaryName, qApp->applicationName()
@@ -799,6 +937,13 @@ QString PackageManagerCorePrivate::offlineBinaryName() const
return QString::fromLatin1("%1/%2").arg(targetDir()).arg(filename);
}
+QString PackageManagerCorePrivate::datFileName()
+{
+ if (m_datFileName.isEmpty())
+ m_datFileName = targetDir() + QLatin1Char('/') + m_data.settings().maintenanceToolName() + QLatin1String(".dat");
+ return m_datFileName;
+}
+
static QNetworkProxy readProxy(QXmlStreamReader &reader)
{
QNetworkProxy proxy(QNetworkProxy::HttpProxy);
@@ -852,12 +997,12 @@ void PackageManagerCorePrivate::writeMaintenanceConfigFiles()
QVariantHash variables; // Do not change to QVariantMap! Breaks existing .ini files,
// cause the variant types do not match while restoring the variables from the file.
- QSettingsWrapper cfg(iniPath, QSettingsWrapper::IniFormat);
+ QSettingsWrapper cfg(iniPath, QSettings::IniFormat);
foreach (const QString &key, m_data.keys()) {
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);
}
@@ -881,8 +1026,8 @@ void PackageManagerCorePrivate::writeMaintenanceConfigFiles()
QFile file(targetDir() + QLatin1Char('/') + QLatin1String("network.xml"));
if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
- QXmlStreamWriter writer(&file);
- writer.setCodec("UTF-8");
+ QString outputStr;
+ QXmlStreamWriter writer(&outputStr);
writer.setAutoFormatting(true);
writer.writeStartDocument();
@@ -913,7 +1058,10 @@ void PackageManagerCorePrivate::writeMaintenanceConfigFiles()
writer.writeEndElement();
}
writer.writeEndElement();
+ writer.writeTextElement(QLatin1String("LocalCachePath"), m_data.settings().localCachePath());
writer.writeEndElement();
+
+ file.write(outputStr.toUtf8());
}
setDefaultFilePermissions(&file, DefaultFilePermissions::NonExecutable);
}
@@ -921,12 +1069,17 @@ void PackageManagerCorePrivate::writeMaintenanceConfigFiles()
void PackageManagerCorePrivate::readMaintenanceConfigFiles(const QString &targetDir)
{
QSettingsWrapper cfg(targetDir + QLatin1Char('/') + m_data.settings().maintenanceToolIniFile(),
- QSettingsWrapper::IniFormat);
+ QSettings::IniFormat);
const QVariantHash v = cfg.value(QLatin1String("Variables")).toHash(); // Do not change to
// QVariantMap! Breaks reading from existing .ini files, cause the variant types do not match.
for (QVariantHash::const_iterator it = v.constBegin(); it != v.constEnd(); ++it) {
- if (!m_data.contains(it.key()) || m_data.value(it.key()).isNull())
- m_data.setValue(it.key(), replacePath(it.value().toString(), QLatin1String(scRelocatable), targetDir));
+ if (m_data.contains(it.key()) && !m_data.value(it.key()).isNull()) {
+ // Exception: StartMenuDir should be permanent after initial installation
+ // and must be read from maintenancetool.ini
+ if (it.key() != scStartMenuDir)
+ continue;
+ }
+ m_data.setValue(it.key(), replacePath(it.value().toString(), QLatin1String(scRelocatable), targetDir));
}
QSet<Repository> repos;
const QVariantList variants = cfg.value(QLatin1String("DefaultRepositories"))
@@ -948,7 +1101,7 @@ void PackageManagerCorePrivate::readMaintenanceConfigFiles(const QString &target
case QXmlStreamReader::StartElement: {
if (reader.name() == QLatin1String("Network")) {
while (reader.readNextStartElement()) {
- const QStringRef name = reader.name();
+ const QStringView name = reader.name();
if (name == QLatin1String("Ftp")) {
m_data.settings().setFtpProxy(readProxy(reader));
} else if (name == QLatin1String("Http")) {
@@ -957,6 +1110,8 @@ void PackageManagerCorePrivate::readMaintenanceConfigFiles(const QString &target
m_data.settings().addUserRepositories(readRepositories(reader, false));
} else if (name == QLatin1String("ProxyType")) {
m_data.settings().setProxyType(Settings::ProxyType(reader.readElementText().toInt()));
+ } else if (name == QLatin1String("LocalCachePath")) {
+ m_data.settings().setLocalCachePath(reader.readElementText());
} else {
reader.skipCurrentElement();
}
@@ -1055,8 +1210,11 @@ void PackageManagerCorePrivate::connectOperationToInstaller(Operation *const ope
connect(m_core, SIGNAL(installationInterrupted()), operationObject, SLOT(cancelOperation()));
if (mo->indexOfSignal(QMetaObject::normalizedSignature("progressChanged(double)")) > -1) {
+ // create unique object names for progress information track
+ operationObject->setObjectName(QLatin1String("operation_%1").arg(QString::number(m_connectedOperations)));
ProgressCoordinator::instance()->registerPartProgress(operationObject,
SIGNAL(progressChanged(double)), operationPartSize);
+ m_connectedOperations++;
}
}
}
@@ -1135,12 +1293,7 @@ void PackageManagerCorePrivate::writeMaintenanceToolBinary(QFile *const input, q
// other code a lot (since installers don't have any appended data either)
QFile dataOut(generateTemporaryFileName());
QInstaller::openForWrite(&dataOut);
- QInstaller::appendInt64(&dataOut, 0); // operations start
- QInstaller::appendInt64(&dataOut, 0); // operations end
- QInstaller::appendInt64(&dataOut, 0); // resource count
- QInstaller::appendInt64(&dataOut, 4 * sizeof(qint64)); // data block size
- QInstaller::appendInt64(&dataOut, BinaryContent::MagicUninstallerMarker);
- QInstaller::appendInt64(&dataOut, BinaryContent::MagicCookie);
+ QInstallerTools::createMTDatFile(dataOut);
{
QFile dummy(resourcePath.filePath(QLatin1String("installer.dat")));
@@ -1245,19 +1398,9 @@ void PackageManagerCorePrivate::writeMaintenanceToolBinaryData(QFileDevice *outp
QInstaller::appendInt64(output, BinaryContent::MagicUninstallerMarker);
}
-void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOperations)
+void PackageManagerCorePrivate::writeMaintenanceToolAppBundle(OperationList &performedOperations)
{
- if (m_disableWriteMaintenanceTool) {
- qCDebug(QInstaller::lcInstallerInstallLog()) << "Maintenance tool writing disabled.";
- return;
- }
-
- bool gainedAdminRights = false;
- if (!directoryWritable(targetDir())) {
- m_core->gainAdminRights();
- gainedAdminRights = true;
- }
-
+#ifdef Q_OS_MACOS
const QString targetAppDirPath = QFileInfo(maintenanceToolName()).path();
if (!QDir().exists(targetAppDirPath)) {
// create the directory containing the maintenance tool (like a bundle structure on macOS...)
@@ -1267,8 +1410,6 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper
performOperationThreaded(op);
performedOperations.append(takeOwnedOperation(op));
}
-
-#ifdef Q_OS_MACOS
// if it is a bundle, we need some stuff in it...
const QString sourceAppDirPath = QCoreApplication::applicationDirPath();
if (isInstaller() && QInstaller::isInBundle(sourceAppDirPath)) {
@@ -1298,7 +1439,7 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper
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"));
@@ -1338,7 +1479,23 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper
<< (targetAppDirPath + QLatin1String("/../plugins")));
performOperationThreaded(op);
}
+#else
+ Q_UNUSED(performedOperations);
#endif
+}
+
+void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOperations)
+{
+ if (m_disableWriteMaintenanceTool) {
+ qCDebug(QInstaller::lcInstallerInstallLog()) << "Maintenance tool writing disabled.";
+ return;
+ }
+
+ bool gainedAdminRights = false;
+ if (!directoryWritable(targetDir())) {
+ m_core->gainAdminRights();
+ gainedAdminRights = true;
+ }
try {
// 1 - check if we have a installer base replacement
@@ -1374,45 +1531,71 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper
// 5.1 - this will only happen -if- we wrote out a new binary
bool newBinaryWritten = false;
- bool replacementExists = false;
+ QString mtName = maintenanceToolName();
const QString installerBaseBinary = replaceVariables(m_installerBaseBinaryUnreplaced);
- if (!installerBaseBinary.isEmpty() && QFileInfo(installerBaseBinary).exists()) {
+ if (!installerBaseBinary.isEmpty() && QFileInfo::exists(installerBaseBinary)) {
qCDebug(QInstaller::lcInstallerInstallLog) << "Got a replacement installer base binary:"
<< installerBaseBinary;
-
- QFile replacementBinary(installerBaseBinary);
- try {
- QInstaller::openForRead(&replacementBinary);
- writeMaintenanceToolBinary(&replacementBinary, replacementBinary.size(), true);
- qCDebug(QInstaller::lcInstallerInstallLog) << "Wrote the binary with the new replacement.";
-
- newBinaryWritten = true;
- replacementExists = true;
- } catch (const Error &error) {
- qCWarning(QInstaller::lcInstallerInstallLog) << error.message();
- }
-
- if (!replacementBinary.remove()) {
- // Is there anything more sensible we can do with this error? I think not. It's not serious
- // enough for throwing / aborting the process.
- qCDebug(QInstaller::lcInstallerInstallLog) << "Cannot remove installer base binary"
- << installerBaseBinary << "after updating the maintenance tool:"
- << replacementBinary.errorString();
+ if (QInstaller::isInBundle(installerBaseBinary)) {
+ // 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();
+ }
} else {
- qCDebug(QInstaller::lcInstallerInstallLog) << "Removed installer base binary"
- << installerBaseBinary << "after updating the maintenance tool.";
+ writeMaintenanceToolAppBundle(performedOperations);
+ QFile replacementBinary(installerBaseBinary);
+ try {
+ QInstaller::openForRead(&replacementBinary);
+ writeMaintenanceToolBinary(&replacementBinary, replacementBinary.size(), true);
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Wrote the binary with the new replacement.";
+
+ newBinaryWritten = true;
+ } catch (const Error &error) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << error.message();
+ }
+
+ if (!replacementBinary.remove()) {
+ // Is there anything more sensible we can do with this error? I think not. It's not serious
+ // enough for throwing / aborting the process.
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Cannot remove installer base binary"
+ << installerBaseBinary << "after updating the maintenance tool:"
+ << replacementBinary.errorString();
+ } else {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Removed installer base binary"
+ << installerBaseBinary << "after updating the maintenance tool.";
+ }
}
m_installerBaseBinaryUnreplaced.clear();
- } else if (!installerBaseBinary.isEmpty() && !QFileInfo(installerBaseBinary).exists()) {
+ } else if (!installerBaseBinary.isEmpty() && !QFileInfo::exists(installerBaseBinary)) {
qCWarning(QInstaller::lcInstallerInstallLog) << "The current maintenance tool could not be updated."
<< installerBaseBinary << "does not exist. Please fix the \"setInstallerBaseBinary"
"(<temp_installer_base_binary_path>)\" call in your script.";
+ writeMaintenanceToolAppBundle(performedOperations);
+ } else {
+ writeMaintenanceToolAppBundle(performedOperations);
}
QFile input;
BinaryLayout layout;
- const QString dataFile = targetDir() + QLatin1Char('/') + m_data.settings().maintenanceToolName()
- + QLatin1String(".dat");
+ const QString dataFile = datFileName();
try {
if (isInstaller()) {
if (QFile::exists(dataFile)) {
@@ -1493,14 +1676,38 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper
input.close();
if (m_core->isInstaller())
registerMaintenanceTool();
+#ifdef Q_OS_MACOS
+ if (newBinaryWritten) {
+ // Remove old alias as the name may have changed.
+ deleteMaintenanceToolAlias();
+ const QString aliasPath = maintenanceToolAliasPath();
+ if (!aliasPath.isEmpty()) {
+ // The new alias file is created after the maintenance too binary is renamed,
+ // but we need to set the value before the variables get written to disk.
+ m_core->setValue(QLatin1String("CreatedMaintenanceToolAlias"), aliasPath);
+ }
+ }
+#endif
writeMaintenanceConfigFiles();
- deferredRename(dataFile + QLatin1String(".new"), dataFile, false);
+
+ QFile::remove(dataFile);
+ QFileInfo fi(mtName);
+ //Rename the dat file according to maintenancetool name
+ QFile::rename(dataFile + QLatin1String(".new"), targetDir() + QLatin1Char('/') + fi.baseName() + QLatin1String(".dat"));
+
+ const bool restart = !statusCanceledOrFailed() && m_needsHardRestart;
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Maintenance tool hard restart:"
+ << (restart ? "true." : "false.");
if (newBinaryWritten) {
- const bool restart = replacementExists && isUpdater() && (!statusCanceledOrFailed()) && m_needsHardRestart;
- deferredRename(maintenanceToolName() + QLatin1String(".new"), maintenanceToolName(), restart);
- qCDebug(QInstaller::lcInstallerInstallLog) << "Maintenance tool restart:"
- << (restart ? "true." : "false.");
+ if (isInstaller())
+ QFile::rename(mtName + QLatin1String(".new"), mtName);
+ else
+ deferredRename(mtName + QLatin1String(".new"), mtName, restart);
+ QFileInfo mtFileName(mtName);
+ writeMaintenanceToolAlias(mtFileName.fileName());
+ } else if (restart) {
+ SelfRestarter::setRestartOnQuit(true);
}
} catch (const Error &err) {
setStatus(PackageManagerCore::Failure);
@@ -1570,6 +1777,28 @@ void PackageManagerCorePrivate::writeOfflineBaseBinary()
}
}
+void PackageManagerCorePrivate::writeMaintenanceToolAlias(const QString &maintenanceToolName)
+{
+#ifdef Q_OS_MACOS
+ const QString aliasPath = maintenanceToolAliasPath();
+ if (aliasPath.isEmpty())
+ return;
+
+ QString maintenanceToolBundle = QString::fromLatin1("%1/%2")
+ .arg(targetDir(), maintenanceToolName);
+ if (!maintenanceToolBundle.endsWith(QLatin1String(".app")))
+ maintenanceToolBundle += QLatin1String(".app");
+
+ const QDir targetDir(QFileInfo(aliasPath).absolutePath());
+ if (!targetDir.exists())
+ targetDir.mkpath(targetDir.absolutePath());
+
+ mkalias(maintenanceToolBundle, aliasPath);
+#else
+ Q_UNUSED(maintenanceToolName)
+#endif
+}
+
QString PackageManagerCorePrivate::registerPath()
{
#ifdef Q_OS_WIN
@@ -1654,7 +1883,7 @@ bool PackageManagerCorePrivate::runInstaller()
throw Error(tr("It is not possible to install from network location"));
}
- if (!adminRightsGained) {
+ if (!m_core->hasAdminRights()) {
foreach (Component *component, componentsToInstall) {
if (component->value(scRequiresAdminRights, scFalse) == scFalse)
continue;
@@ -1687,8 +1916,8 @@ bool PackageManagerCorePrivate::runInstaller()
+ (PackageManagerCore::createLocalRepositoryFromBinary() ? 1 : 0);
double progressOperationSize = componentsInstallPartProgressSize / progressOperationCount;
- foreach (Component *component, componentsToInstall)
- installComponent(component, progressOperationSize, adminRightsGained);
+ // Now install the requested components
+ unpackAndInstallComponents(componentsToInstall, progressOperationSize);
if (m_core->isOfflineOnly() && PackageManagerCore::createLocalRepositoryFromBinary()) {
emit m_core->titleMessageChanged(tr("Creating local repository"));
@@ -1747,7 +1976,8 @@ bool PackageManagerCorePrivate::runInstaller()
if (progress < 100)
ProgressCoordinator::instance()->addManualPercentagePoints(100 - progress);
- ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("\nInstallation finished!"));
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(QLatin1Char('\n')
+ + tr("Installation finished!"));
if (adminRightsGained)
m_core->dropAdminRights();
@@ -1765,7 +1995,9 @@ bool PackageManagerCorePrivate::runInstaller()
m_core->rollBackInstallation();
- ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("\nInstallation aborted!"));
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(QLatin1Char('\n')
+ + tr("Installation aborted!"));
+
if (adminRightsGained)
m_core->dropAdminRights();
emit installationFinished();
@@ -1843,7 +2075,7 @@ bool PackageManagerCorePrivate::runPackageUpdater()
// There is a replacement, but the replacement is not scheduled for update, keep it as well.
if (m_componentsToReplaceUpdaterMode.contains(name)
- && !m_componentsToReplaceUpdaterMode.value(name).first->updateRequested()) {
+ && !m_installerCalculator->resolvedComponents().contains(m_componentsToReplaceUpdaterMode.value(name).first)) {
nonRevertedOperations.append(operation);
continue;
}
@@ -1883,7 +2115,7 @@ bool PackageManagerCorePrivate::runPackageUpdater()
}
// we did not request admin rights till we found out that a component/ undo needs admin rights
- if (updateAdminRights && !adminRightsGained) {
+ if (updateAdminRights && !m_core->hasAdminRights()) {
m_core->gainAdminRights();
m_core->dropAdminRights();
}
@@ -1904,15 +2136,15 @@ 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
const double progressOperationCount = countProgressOperations(componentsToInstall);
const double progressOperationSize = componentsInstallPartProgressSize / progressOperationCount;
- foreach (Component *component, componentsToInstall)
- installComponent(component, progressOperationSize, adminRightsGained);
+ // Now install the requested new components
+ unpackAndInstallComponents(componentsToInstall, progressOperationSize);
emit m_core->titleMessageChanged(tr("Creating Maintenance Tool"));
@@ -1924,7 +2156,8 @@ bool PackageManagerCorePrivate::runPackageUpdater()
// usually this should be only the reserved one from the beginning
if (progress < 100)
ProgressCoordinator::instance()->addManualPercentagePoints(100 - progress);
- ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("\nUpdate finished!"));
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(QLatin1Char('\n')
+ + tr("Update finished!"));
if (adminRightsGained)
m_core->dropAdminRights();
@@ -1944,7 +2177,9 @@ bool PackageManagerCorePrivate::runPackageUpdater()
m_core->rollBackInstallation();
- ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("\nUpdate aborted!"));
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(QLatin1Char('\n')
+ + tr("Update aborted!"));
+
if (adminRightsGained)
m_core->dropAdminRights();
emit installationFinished();
@@ -1978,7 +2213,7 @@ bool PackageManagerCorePrivate::runUninstaller()
}
// We did not yet request elevated permissions but they are required.
- if (updateAdminRights && !adminRightsGained) {
+ if (updateAdminRights && !m_core->hasAdminRights()) {
m_core->gainAdminRights();
m_core->dropAdminRights();
}
@@ -1986,15 +2221,16 @@ 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
+ deleteMaintenanceToolAlias();
// 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.";
@@ -2070,7 +2306,7 @@ bool PackageManagerCorePrivate::runOfflineGenerator()
m_core->downloadNeededArchives(double(1));
const QString installerBaseReplacement = replaceVariables(m_offlineBaseBinaryUnreplaced);
- if (!installerBaseReplacement.isEmpty() && QFileInfo(installerBaseReplacement).exists()) {
+ if (!installerBaseReplacement.isEmpty() && QFileInfo::exists(installerBaseReplacement)) {
qCDebug(QInstaller::lcInstallerInstallLog) << "Got a replacement installer base binary:"
<< offlineBinaryTempName;
@@ -2155,10 +2391,151 @@ bool PackageManagerCorePrivate::runOfflineGenerator()
return success;
}
-void PackageManagerCorePrivate::installComponent(Component *component, double progressOperationSize,
- bool adminRightsGained)
+void PackageManagerCorePrivate::unpackComponents(const QList<Component *> &components,
+ double progressOperationSize)
+{
+ OperationList unpackOperations;
+ bool becameAdmin = false;
+
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(QLatin1Char('\n')
+ + tr("Preparing to unpack components..."));
+
+ // 1. Collect operations
+ quint64 totalOperationsSizeHint = 0;
+ for (auto *component : components) {
+ const OperationList operations = component->operations(Operation::Unpack);
+ if (!component->operationsCreatedSuccessfully())
+ m_core->setCanceled();
+
+ for (auto &op : operations) {
+ if (statusCanceledOrFailed())
+ throw Error(tr("Installation canceled by user"));
+
+ unpackOperations.append(op);
+ totalOperationsSizeHint += op->sizeHint();
+
+ // 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 (!m_core->hasAdminRights() && op->value(QLatin1String("admin")).toBool())
+ becameAdmin = m_core->gainAdminRights();
+ }
+ }
+
+ // 2. Calculate proportional progress sizes
+ const double totalOperationsSize = progressOperationSize * unpackOperations.size();
+ for (auto *op : unpackOperations) {
+ const double ratio = static_cast<double>(op->sizeHint()) / totalOperationsSizeHint;
+ const double proportionalProgressOperationSize = totalOperationsSize * ratio;
+
+ connectOperationToInstaller(op, proportionalProgressOperationSize);
+ connectOperationCallMethodRequest(op);
+ }
+
+ // 3. Backup operations
+ ConcurrentOperationRunner runner(&unpackOperations, Operation::Backup);
+ runner.setMaxThreadCount(m_core->maxConcurrentOperations());
+
+ connect(m_core, &PackageManagerCore::installationInterrupted,
+ &runner, &ConcurrentOperationRunner::cancel);
+
+ connect(&runner, &ConcurrentOperationRunner::progressChanged, [](const int completed, const int total) {
+ const QString statusText = tr("%1 of %2 operations completed.")
+ .arg(QString::number(completed), QString::number(total));
+ ProgressCoordinator::instance()->emitAdditionalProgressStatus(statusText);
+ });
+ connect(&runner, &ConcurrentOperationRunner::finished, [] {
+ // Clear the status text to not cause confusion when installations begin.
+ ProgressCoordinator::instance()->emitAdditionalProgressStatus(QLatin1String(""));
+ });
+
+ const QHash<Operation *, bool> backupResults = runner.run();
+ const OperationList backupOperations = backupResults.keys();
+
+ for (auto &operation : backupOperations) {
+ if (m_core->status() == PackageManagerCore::Canceled)
+ break; // User canceled, no need to print warnings
+
+ if (!backupResults.value(operation) || operation->error() != Operation::NoError) {
+ // For Extract, backup stops only on read errors. That means the perform step will
+ // also fail later on, which handles the user selection on what to do with the error.
+ qCWarning(QInstaller::lcInstallerInstallLog) << QString::fromLatin1("Backup of operation "
+ "\"%1\" with arguments \"%2\" failed: %3").arg(operation->name(), operation->arguments()
+ .join(QLatin1String("; ")), operation->errorString());
+ continue;
+ }
+ // Backup may request performing operation as admin
+ if (!m_core->hasAdminRights() && operation->value(QLatin1String("admin")).toBool())
+ becameAdmin = m_core->gainAdminRights();
+ }
+
+ // TODO: Do we need to rollback backups too? For Extract op this works without,
+ // as the backup files are not used in Undo step.
+ if (statusCanceledOrFailed())
+ throw Error(tr("Installation canceled by user"));
+
+ // 4. Perform operations
+ std::sort(unpackOperations.begin(), unpackOperations.end(), [](Operation *lhs, Operation *rhs) {
+ // We want to run the longest taking operations first
+ return lhs->sizeHint() > rhs->sizeHint();
+ });
+
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(QLatin1Char('\n')
+ + tr("Unpacking components..."));
+
+ runner.setType(Operation::Perform);
+ const QHash<Operation *, bool> results = runner.run();
+ const OperationList performedOperations = results.keys();
+
+ QString error;
+ for (auto &operation : performedOperations) {
+ const QString component = operation->value(QLatin1String("component")).toString();
+
+ bool ignoreError = false;
+ bool ok = results.value(operation);
+
+ while (!ok && !ignoreError && m_core->status() != PackageManagerCore::Canceled) {
+ qCDebug(QInstaller::lcInstallerInstallLog) << QString::fromLatin1("Operation \"%1\" with arguments "
+ "\"%2\" failed: %3").arg(operation->name(), operation->arguments()
+ .join(QLatin1String("; ")), operation->errorString());
+
+ const QMessageBox::StandardButton button =
+ MessageBoxHandler::warning(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("installationErrorWithCancel"), tr("Installer Error"),
+ tr("Error during installation process (%1):\n%2").arg(component, operation->errorString()),
+ QMessageBox::Retry | QMessageBox::Ignore | QMessageBox::Cancel, QMessageBox::Cancel);
+
+ if (button == QMessageBox::Retry)
+ ok = performOperationThreaded(operation);
+ else if (button == QMessageBox::Ignore)
+ ignoreError = true;
+ else if (button == QMessageBox::Cancel)
+ m_core->interrupt();
+ }
+
+ if (ok || operation->error() > Operation::InvalidArguments) {
+ // Remember that the operation was performed, that allows us to undo it
+ // if this operation failed but still needs an undo call to cleanup.
+ addPerformed(operation);
+ }
+
+ // Catch the error message from first failure, but throw only after all
+ // operations requiring undo step are marked as performed.
+ if (!ok && !ignoreError && error.isEmpty())
+ error = operation->errorString();
+ }
+
+ if (becameAdmin)
+ m_core->dropAdminRights();
+
+ if (!error.isEmpty())
+ throw Error(error);
+
+ ProgressCoordinator::instance()->emitDetailTextChanged(tr("Done"));
+}
+
+void PackageManagerCorePrivate::installComponent(Component *component, double progressOperationSize)
{
- const OperationList operations = component->operations();
+ OperationList operations = component->operations(Operation::Install);
if (!component->operationsCreatedSuccessfully())
m_core->setCanceled();
@@ -2166,8 +2543,8 @@ void PackageManagerCorePrivate::installComponent(Component *component, double pr
// show only components which do something, MinimumProgress is only for progress calculation safeness
bool showDetailsLog = false;
if (opCount > 1 || (opCount == 1 && operations.at(0)->name() != QLatin1String("MinimumProgress"))) {
- ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("\nInstalling component %1")
- .arg(component->displayName()));
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(QLatin1Char('\n')
+ + tr("Installing component %1").arg(component->displayName()));
showDetailsLog = true;
}
@@ -2177,7 +2554,7 @@ void PackageManagerCorePrivate::installComponent(Component *component, double pr
// maybe this operations wants us to be admin...
bool becameAdmin = false;
- if (!adminRightsGained && operation->value(QLatin1String("admin")).toBool()) {
+ if (!m_core->hasAdminRights() && operation->value(QLatin1String("admin")).toBool()) {
becameAdmin = m_core->gainAdminRights();
qCDebug(QInstaller::lcInstallerInstallLog) << operation->name() << "as admin:" << becameAdmin;
}
@@ -2220,11 +2597,13 @@ void PackageManagerCorePrivate::installComponent(Component *component, double pr
if (!ok && !ignoreError)
throw Error(operation->errorString());
+ }
- if (((component->value(scEssential, scFalse) == scTrue) || (component->value(scForcedUpdate, scFalse) == scTrue))
- && !m_core->isCommandLineInstance()) {
+ if (!m_core->isCommandLineInstance()) {
+ if ((component->value(scEssential, scFalse) == scTrue) && !isInstaller())
+ m_needsHardRestart = true;
+ else if ((component->value(scForcedUpdate, scFalse) == scTrue) && isUpdater())
m_needsHardRestart = true;
- }
}
registerPathsForUninstallation(component->pathsForUninstallation(), component->name());
@@ -2242,8 +2621,10 @@ void PackageManagerCorePrivate::installComponent(Component *component, double pr
m_localPackageHub->addPackage(component->name(),
component->value(scVersion),
component->value(scDisplayName),
- component->value(scTreeName),
+ QPair<QString, bool>(component->value(scTreeName),
+ component->treeNameMoveChildren()),
component->value(scDescription),
+ component->value(scSortingPriority).toInt(),
component->dependencies(),
component->autoDependencies(),
component->forcedInstallation(),
@@ -2262,23 +2643,54 @@ void PackageManagerCorePrivate::installComponent(Component *component, double pr
ProgressCoordinator::instance()->emitDetailTextChanged(tr("Done"));
}
-bool PackageManagerCorePrivate::runningProcessesFound()
+PackageManagerCore::Status PackageManagerCorePrivate::fetchComponentsAndInstall(const QStringList& components)
{
- //Check if there are processes running in the install
- QStringList excludeFiles = m_allowedRunningProcesses;
- excludeFiles.append(maintenanceToolName());
+ // init default model before fetching remote packages tree
+ ComponentModel *model = m_core->defaultComponentModel();
+ Q_UNUSED(model);
- const QString performModeWarning = m_completeUninstall
- ? QLatin1String("Unable to remove components.")
- : QLatin1String("Unable to update components.");
+ bool fallbackReposFetched = false;
+ auto fetchComponents = [&]() {
+ bool packagesFound = m_core->fetchPackagesWithFallbackRepositories(components, fallbackReposFetched);
- QStringList runningProcesses = runningInstallerProcesses(excludeFiles);
- if (!runningProcesses.isEmpty()) {
- qCWarning(QInstaller::lcInstallerInstallLog).noquote().nospace() << performModeWarning
- << " Please stop these processes: " << runningProcesses << " and try again.";
+ if (!packagesFound) {
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote().nospace()
+ << "No components available with the current selection.";
+ setStatus(PackageManagerCore::Canceled);
+ return false;
+ }
+ QString errorMessage;
+ bool unstableAliasFound = false;
+ if (m_core->checkComponentsForInstallation(components, errorMessage, unstableAliasFound)) {
+ if (!errorMessage.isEmpty())
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote().nospace() << errorMessage;
+ if (calculateComponentsAndRun()) {
+ if (m_core->isOfflineGenerator())
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Created installer to:" << offlineBinaryName();
+ else
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Components installed successfully";
+ }
+ } else {
+ // We found unstable alias and all repos were not fetched. Alias might have dependency to component
+ // which exists in non-default repository. Fetch all repositories now.
+ if (unstableAliasFound && !fallbackReposFetched) {
+ return false;
+ }
+ else {
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote().nospace() << errorMessage
+ << "No components available with the current selection.";
+ }
+ }
return true;
+ };
+
+ if (!fetchComponents() && !fallbackReposFetched) {
+ setStatus(PackageManagerCore::Running);
+ enableAllCategories();
+ fetchComponents();
}
- return false;
+
+ return m_core->status();
}
void PackageManagerCorePrivate::setComponentSelection(const QString &id, Qt::CheckState state)
@@ -2299,6 +2711,14 @@ void PackageManagerCorePrivate::setComponentSelection(const QString &id, Qt::Che
void PackageManagerCorePrivate::deleteMaintenanceTool()
{
+ QDir resourcePath(QFileInfo(maintenanceToolName()).dir());
+ resourcePath.remove(QLatin1String("installer.dat"));
+ QDir installDir(targetDir());
+ installDir.remove(m_data.settings().maintenanceToolName() + QLatin1String(".dat"));
+ installDir.remove(QLatin1String("network.xml"));
+ installDir.remove(m_data.settings().maintenanceToolIniFile());
+ QInstaller::VerboseWriter::instance()->setFileName(QString());
+ installDir.remove(m_core->value(QLatin1String("LogFileName"), QLatin1String("InstallationLog.txt")));
#ifdef Q_OS_WIN
// Since Windows does not support that the maintenance tool deletes itself we have to go with a rather dirty
// hack. What we do is to create a batchfile that will try to remove the maintenance tool once per second. Then
@@ -2311,6 +2731,7 @@ void PackageManagerCorePrivate::deleteMaintenanceTool()
if (!f.open(QIODevice::WriteOnly | QIODevice::Text))
throw Error(tr("Cannot prepare removal"));
+ const bool removeTargetDir = QVariant(m_core->value(scRemoveTargetDir)).toBool();
QTextStream batch(&f);
batch << "Set fso = WScript.CreateObject(\"Scripting.FileSystemObject\")\n";
batch << "file = WScript.Arguments.Item(0)\n";
@@ -2322,10 +2743,12 @@ void PackageManagerCorePrivate::deleteMaintenanceTool()
batch << " fso.DeleteFile(file)\n";
batch << " WScript.Sleep(1000)\n";
batch << "wend\n";
-// batch << "if folder.SubFolders.Count = 0 and folder.Files.Count = 0 then\n";
+ if (!removeTargetDir)
+ batch << "if folder.SubFolders.Count = 0 and folder.Files.Count = 0 then\n";
batch << " Set folder = Nothing\n";
batch << " fso.DeleteFolder folderpath, true\n";
-// batch << "end if\n";
+ if (!removeTargetDir)
+ batch << "end if\n";
batch << "fso.DeleteFile(WScript.ScriptFullName)\n";
f.close();
@@ -2333,11 +2756,7 @@ void PackageManagerCorePrivate::deleteMaintenanceTool()
QStringList arguments;
arguments << QLatin1String("//Nologo") << batchfile; // execute the batchfile
arguments << QDir::toNativeSeparators(QFileInfo(installerBinaryPath()).absoluteFilePath());
- if (!m_performedOperationsOld.isEmpty()) {
- const Operation *const op = m_performedOperationsOld.first();
- if (op->name() == QLatin1String("Mkdir")) // the target directory name
- arguments << QDir::toNativeSeparators(QFileInfo(op->arguments().first()).absoluteFilePath());
- }
+ arguments << targetDir();
if (!QProcessWrapper::startDetached(QLatin1String("cscript"), arguments, QDir::rootPath()))
throw Error(tr("Cannot start removal"));
@@ -2361,10 +2780,27 @@ void PackageManagerCorePrivate::deleteMaintenanceTool()
}
}
+void PackageManagerCorePrivate::deleteMaintenanceToolAlias()
+{
+#ifdef Q_OS_MACOS
+ const QString maintenanceToolAlias = m_core->value(QLatin1String("CreatedMaintenanceToolAlias"));
+ QFile aliasFile(maintenanceToolAlias);
+ if (!maintenanceToolAlias.isEmpty() && !aliasFile.remove()) {
+ // Not fatal
+ qWarning(lcInstallerInstallLog) << "Cannot remove alias file"
+ << maintenanceToolAlias << "for maintenance tool:" << aliasFile.errorString();
+ }
+#endif
+}
+
void PackageManagerCorePrivate::registerMaintenanceTool()
{
#ifdef Q_OS_WIN
- QSettingsWrapper settings(registerPath(), QSettingsWrapper::NativeFormat);
+ auto quoted = [](const QString &s) {
+ return QString::fromLatin1("\"%1\"").arg(s);
+ };
+
+ QSettingsWrapper settings(registerPath(), QSettings::NativeFormat);
settings.setValue(scDisplayName, m_data.value(QLatin1String("ProductName")));
settings.setValue(QLatin1String("DisplayVersion"), m_data.value(QLatin1String("ProductVersion")));
const QString maintenanceTool = QDir::toNativeSeparators(maintenanceToolName());
@@ -2374,9 +2810,12 @@ void PackageManagerCorePrivate::registerMaintenanceTool()
settings.setValue(QLatin1String("Comments"), m_data.value(scTitle));
settings.setValue(QLatin1String("InstallDate"), QDateTime::currentDateTime().toString());
settings.setValue(QLatin1String("InstallLocation"), QDir::toNativeSeparators(targetDir()));
- settings.setValue(QLatin1String("UninstallString"), maintenanceTool);
- settings.setValue(QLatin1String("ModifyPath"), QString(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
@@ -2408,21 +2847,24 @@ void PackageManagerCorePrivate::registerMaintenanceTool()
void PackageManagerCorePrivate::unregisterMaintenanceTool()
{
#ifdef Q_OS_WIN
- QSettingsWrapper settings(registerPath(), QSettingsWrapper::NativeFormat);
+ QSettingsWrapper settings(registerPath(), QSettings::NativeFormat);
settings.remove(QString());
#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();
+ int rolledBackOperations = 0;
+
foreach (Operation *undoOperation, undoOperations) {
if (statusCanceledOrFailed())
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);
@@ -2455,6 +2897,10 @@ void PackageManagerCorePrivate::runUndoOperations(const OperationList &undoOpera
}
}
+ ++rolledBackOperations;
+ ProgressCoordinator::instance()->emitAdditionalProgressStatus(tr("%1 of %2 operations rolled back.")
+ .arg(QString::number(rolledBackOperations), QString::number(operationsCount)));
+
if (becameAdmin)
m_core->dropAdminRights();
@@ -2469,6 +2915,7 @@ void PackageManagerCorePrivate::runUndoOperations(const OperationList &undoOpera
throw Error(tr("Unknown error"));
}
m_localPackageHub->writeToDisk();
+ ProgressCoordinator::instance()->emitAdditionalProgressStatus(tr("Rollbacks complete."));
}
PackagesList PackageManagerCorePrivate::remotePackages()
@@ -2481,7 +2928,7 @@ PackagesList PackageManagerCorePrivate::remotePackages()
m_updateFinder = new KDUpdater::UpdateFinder;
m_updateFinder->setAutoDelete(false);
- m_updateFinder->setPackageSources(m_packageSources);
+ m_updateFinder->setPackageSources(m_packageSources + m_compressedPackageSources);
m_updateFinder->setLocalPackageHub(m_localPackageHub);
m_updateFinder->run();
@@ -2495,40 +2942,17 @@ PackagesList PackageManagerCorePrivate::remotePackages()
return m_updateFinder->updates();
}
-PackagesList PackageManagerCorePrivate::compressedPackages()
-{
- if (m_compressedUpdates && m_compressedFinder)
- return m_compressedFinder->updates();
- m_compressedUpdates = false;
- delete m_compressedFinder;
-
- m_compressedFinder = new KDUpdater::UpdateFinder;
- m_compressedFinder->setAutoDelete(false);
- m_compressedFinder->addCompressedPackage(true);
- m_compressedFinder->setPackageSources(m_compressedPackageSources);
-
- m_compressedFinder->setLocalPackageHub(m_localPackageHub);
- m_compressedFinder->run();
- if (m_compressedFinder->updates().isEmpty()) {
- setStatus(PackageManagerCore::Failure, tr("Cannot retrieve remote tree %1.")
- .arg(m_compressedFinder->errorString()));
- return PackagesList();
- }
- m_compressedUpdates = true;
- return m_compressedFinder->updates();
-}
-
/*!
Returns a hash containing the installed package name and it's associated package information. If
the application is running in installer mode or the local components file could not be parsed, the
hash is empty.
*/
-LocalPackagesHash PackageManagerCorePrivate::localInstalledPackages()
+LocalPackagesMap PackageManagerCorePrivate::localInstalledPackages()
{
if (isInstaller())
- return LocalPackagesHash();
+ return LocalPackagesMap();
+
- LocalPackagesHash installedPackages;
if (m_localPackageHub->error() != LocalPackageHub::NoError) {
if (m_localPackageHub->fileName().isEmpty())
m_localPackageHub->setFileName(componentsXmlPath());
@@ -2545,14 +2969,26 @@ LocalPackagesHash PackageManagerCorePrivate::localInstalledPackages()
setStatus(PackageManagerCore::Failure, tr("Failure to read packages from %1.")
.arg(componentsXmlPath()));
}
+ return m_localPackageHub->localPackages();
+}
- foreach (const LocalPackage &package, m_localPackageHub->packageInfos()) {
- if (statusCanceledOrFailed())
- break;
- installedPackages.insert(package.name, package);
+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 *>();
}
- return installedPackages;
+ m_aliases = true;
+ return m_aliasFinder->aliases();
}
bool PackageManagerCorePrivate::fetchMetaInformationFromRepositories(DownloadType type)
@@ -2585,48 +3021,12 @@ bool PackageManagerCorePrivate::fetchMetaInformationFromRepositories(DownloadTyp
return m_repoFetched;
}
-bool PackageManagerCorePrivate::fetchMetaInformationFromCompressedRepositories()
-{
- bool compressedRepoFetched = false;
-
- m_compressedUpdates = false;
- m_updateSourcesAdded = false;
-
- try {
- //Tell MetadataJob that only compressed packages needed to be fetched and not all.
- //We cannot do this in general fetch meta method as the compressed packages might be
- //installed after components tree is generated
- m_metadataJob.addDownloadType(DownloadType::CompressedPackage);
- m_metadataJob.start();
- m_metadataJob.waitForFinished();
- } catch (Error &error) {
- setStatus(PackageManagerCore::Failure, tr("Cannot retrieve meta information: %1")
- .arg(error.message()));
- return compressedRepoFetched;
- }
-
- if (m_metadataJob.error() != Job::NoError) {
- switch (m_metadataJob.error()) {
- case QInstaller::UserIgnoreError:
- break; // we can simply ignore this error, the user knows about it
- default:
- //Do not change core status here, we can recover if there is invalid
- //compressed repository
- setStatus(m_core->status(), m_metadataJob.errorString());
- return compressedRepoFetched;
- }
- }
-
- compressedRepoFetched = true;
- return compressedRepoFetched;
-}
-
-bool PackageManagerCorePrivate::addUpdateResourcesFromRepositories(bool parseChecksum, bool compressedRepository)
+bool PackageManagerCorePrivate::addUpdateResourcesFromRepositories(bool compressedRepository)
{
if (!compressedRepository && m_updateSourcesAdded)
return m_updateSourcesAdded;
- const QList<Metadata> metadata = m_metadataJob.metadata();
+ const QList<Metadata *> metadata = m_metadataJob.metadata();
if (metadata.isEmpty()) {
m_updateSourcesAdded = true;
return m_updateSourcesAdded;
@@ -2639,53 +3039,25 @@ bool PackageManagerCorePrivate::addUpdateResourcesFromRepositories(bool parseChe
m_updates = false;
m_updateSourcesAdded = false;
if (isInstaller())
- m_packageSources.insert(PackageSource(QUrl(QLatin1String("resource://metadata/")), 0));
+ m_packageSources.insert(PackageSource(QUrl(QLatin1String("resource://metadata/")), 1));
}
- foreach (const Metadata &data, metadata) {
- if (compressedRepository && !data.repository.isCompressed()) {
+ foreach (const Metadata *data, metadata) {
+ if (compressedRepository && !data->repository().isCompressed()) {
continue;
}
if (statusCanceledOrFailed())
return false;
- if (data.directory.isEmpty())
+ if (data->path().isEmpty())
continue;
- if (parseChecksum) {
- const QString updatesXmlPath = data.directory + QLatin1String("/Updates.xml");
- QFile updatesFile(updatesXmlPath);
- try {
- QInstaller::openForRead(&updatesFile);
- } catch(const Error &e) {
- qCWarning(QInstaller::lcInstallerInstallLog) << "Error opening Updates.xml:"
- << e.message();
- setStatus(PackageManagerCore::Failure, tr("Cannot add temporary update source information."));
- return false;
- }
-
- int line = 0;
- int column = 0;
- QString error;
- QDomDocument doc;
- if (!doc.setContent(&updatesFile, &error, &line, &column)) {
- qCWarning(QInstaller::lcInstallerInstallLog).nospace() << "Parse error in file "
- << updatesFile.fileName() << ": " << error << " at line " << line
- << " col " << column;
- setStatus(PackageManagerCore::Failure, tr("Cannot add temporary update source information."));
- return false;
- }
-
- const QDomNode checksum = doc.documentElement().firstChildElement(QLatin1String("Checksum"));
- if (!checksum.isNull())
- m_core->setTestChecksum(checksum.toElement().text().toLower() == scTrue);
- }
- if (compressedRepository)
- m_compressedPackageSources.insert(PackageSource(QUrl::fromLocalFile(data.directory), 1));
+ if (data->repository().isCompressed())
+ m_compressedPackageSources.insert(PackageSource(QUrl::fromLocalFile(data->path()), 2, data->repository().postLoadComponentScript()));
else
- m_packageSources.insert(PackageSource(QUrl::fromLocalFile(data.directory), 1));
+ m_packageSources.insert(PackageSource(QUrl::fromLocalFile(data->path()), 0, data->repository().postLoadComponentScript()));
- ProductKeyCheck::instance()->addPackagesFromXml(data.directory + QLatin1String("/Updates.xml"));
+ ProductKeyCheck::instance()->addPackagesFromXml(data->path() + QLatin1String("/Updates.xml"));
}
if ((compressedRepository && m_compressedPackageSources.count() == 0 ) ||
(!compressedRepository && m_packageSources.count() == 0)) {
@@ -2711,7 +3083,6 @@ void PackageManagerCorePrivate::restoreCheckState()
}
m_coreCheckedHash.clear();
- m_componentsToInstallCalculated = false;
}
void PackageManagerCorePrivate::storeCheckState()
@@ -2724,6 +3095,76 @@ void PackageManagerCorePrivate::storeCheckState()
m_coreCheckedHash.insert(component, component->checkState());
}
+void PackageManagerCorePrivate::updateComponentInstallActions()
+{
+ for (Component *component : m_core->components(PackageManagerCore::ComponentType::All)) {
+ component->setInstallAction(component->isInstalled()
+ ? ComponentModelHelper::KeepInstalled
+ : ComponentModelHelper::KeepUninstalled);
+ }
+ for (Component *component : uninstallerCalculator()->resolvedComponents())
+ component->setInstallAction(ComponentModelHelper::Uninstall);
+ for (Component *component : installerCalculator()->resolvedComponents())
+ component->setInstallAction(ComponentModelHelper::Install);
+}
+
+bool PackageManagerCorePrivate::enableAllCategories()
+{
+ QSet<RepositoryCategory> repoCategories = m_data.settings().repositoryCategories();
+ bool additionalRepositoriesEnabled = false;
+ for (const auto &category : repoCategories) {
+ if (!category.isEnabled()) {
+ additionalRepositoriesEnabled = true;
+ enableRepositoryCategory(category, true);
+ }
+ }
+ return additionalRepositoriesEnabled;
+}
+
+void PackageManagerCorePrivate::enableRepositoryCategory(const RepositoryCategory &repoCategory, const bool enable)
+{
+ RepositoryCategory replacement = repoCategory;
+ replacement.setEnabled(enable);
+ QSet<RepositoryCategory> tmpRepoCategories = m_data.settings().repositoryCategories();
+ if (tmpRepoCategories.contains(repoCategory)) {
+ tmpRepoCategories.remove(repoCategory);
+ tmpRepoCategories.insert(replacement);
+ m_data.settings().addRepositoryCategories(tmpRepoCategories);
+ }
+}
+
+bool PackageManagerCorePrivate::installablePackagesFound(const QStringList& components)
+{
+ if (components.isEmpty())
+ return true;
+
+ PackagesList remotes = remotePackages();
+
+ auto componentsNotFoundForInstall = QtConcurrent::blockingFiltered(
+ components,
+ [remotes](const QString& installerPackage) {
+ return filterMissingPackagesToInstall(installerPackage, remotes);
+ }
+ );
+
+ if (componentsNotFoundForInstall.count() > 0) {
+ QList<ComponentAlias *> aliasList = componentAliases();
+ auto aliasesNotFoundForInstall = QtConcurrent::blockingFiltered(
+ components,
+ [aliasList](const QString& installerPackage) {
+ return filterMissingAliasesToInstall(installerPackage, aliasList);
+ }
+ );
+
+ if (aliasesNotFoundForInstall.count() > 0) {
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote().nospace() << "Cannot install " << aliasesNotFoundForInstall.join(QLatin1String(", ")) << ". Component(s) not found.";
+ setStatus(PackageManagerCore::NoPackagesFound);
+ return false;
+ }
+ }
+ return true;
+}
+
void PackageManagerCorePrivate::connectOperationCallMethodRequest(Operation *const operation)
{
QObject *const operationObject = dynamic_cast<QObject *> (operation);
@@ -2774,6 +3215,34 @@ void PackageManagerCorePrivate::handleMethodInvocationRequest(const QString &inv
QMetaObject::invokeMethod(obj, qPrintable(invokableMethodName));
}
+/*
+ Adds the \a path for deletetion. Unlike files for delayed deletion, which are deleted
+ on the start of next installer run, these paths are deleted on exit.
+*/
+void PackageManagerCorePrivate::addPathForDeletion(const QString &path)
+{
+ m_tmpPathDeleter.add(path);
+}
+
+void PackageManagerCorePrivate::unpackAndInstallComponents(const QList<Component *> &components,
+ const double progressOperationSize)
+{
+ // Perform extract operations
+ 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);
+
+ ++installedComponents;
+ ProgressCoordinator::instance()->emitAdditionalProgressStatus(tr("%1 of %2 components installed.")
+ .arg(QString::number(installedComponents), QString::number(componentsToInstallCount)));
+ }
+ ProgressCoordinator::instance()->emitAdditionalProgressStatus(tr("All components installed."));
+}
+
void PackageManagerCorePrivate::processFilesForDelayedDeletion()
{
if (m_filesForDelayedDeletion.isEmpty())
@@ -2791,36 +3260,24 @@ void PackageManagerCorePrivate::processFilesForDelayedDeletion()
}
}
-void PackageManagerCorePrivate::findExecutablesRecursive(const QString &path, const QStringList &excludeFiles, QStringList *result)
-{
- QDirIterator it(path, QDir::NoDotAndDotDot | QDir::Executable | QDir::Files | QDir::System, QDirIterator::Subdirectories );
-
- while (it.hasNext())
- result->append(QDir::toNativeSeparators(it.next().toLower()));
-
- foreach (const QString &process, excludeFiles)
- result->removeAll(QDir::toNativeSeparators(process.toLower()));
-}
-
-QStringList PackageManagerCorePrivate::runningInstallerProcesses(const QStringList &excludeFiles)
-{
- QStringList resultFiles;
- findExecutablesRecursive(QCoreApplication::applicationDirPath(), excludeFiles, &resultFiles);
- return checkRunningProcessesFromList(resultFiles);
-}
-
bool PackageManagerCorePrivate::calculateComponentsAndRun()
{
- QString htmlOutput;
- bool componentsOk = m_core->calculateComponents(&htmlOutput);
+ bool componentsOk = m_core->recalculateAllComponents();
+
if (statusCanceledOrFailed()) {
qCDebug(QInstaller::lcInstallerInstallLog) << "Installation canceled.";
} else if (componentsOk && acceptLicenseAgreements()) {
- qCDebug(QInstaller::lcInstallerInstallLog).noquote() << htmlToString(htmlOutput);
+ try {
+ loadComponentScripts(installerCalculator()->resolvedComponents(), true);
+ } catch (const Error &error) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << error.message();
+ return false;
+ }
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote()
+ << htmlToString(m_core->componentResolveReasons());
- QString spaceInfo;
- const bool spaceOk = m_core->checkAvailableSpace(spaceInfo);
- qCDebug(QInstaller::lcInstallerInstallLog) << spaceInfo;
+ const bool spaceOk = m_core->checkAvailableSpace();
+ qCDebug(QInstaller::lcInstallerInstallLog) << m_core->availableSpaceMessage();
if (!spaceOk || !(m_autoConfirmCommand || askUserConfirmCommand())) {
qCDebug(QInstaller::lcInstallerInstallLog) << "Installation aborted.";
@@ -2847,6 +3304,13 @@ bool PackageManagerCorePrivate::acceptLicenseAgreements() const
m_core->addLicenseItem(component->licenses());
}
+ const QString acceptanceText = ProductKeyCheck::instance()->licenseAcceptanceText();
+ if (!acceptanceText.isEmpty()) {
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote() << acceptanceText;
+ if (!m_autoAcceptLicenses && !acceptRejectCliQuery())
+ return false;
+ }
+
QHash<QString, QMap<QString, QString>> priorityHash = m_core->sortedLicenses();
QStringList priorities = priorityHash.keys();
priorities.sort();
@@ -2895,6 +3359,23 @@ bool PackageManagerCorePrivate::askUserAcceptLicense(const QString &name, const
}
}
+bool PackageManagerCorePrivate::acceptRejectCliQuery() const
+{
+ forever {
+ const QString input = m_core->readConsoleLine(QLatin1String("Accept|Reject"));
+
+ if (QString::compare(input, QLatin1String("Accept"), Qt::CaseInsensitive) == 0
+ || QString::compare(input, QLatin1String("A"), Qt::CaseInsensitive) == 0) {
+ return true;
+ } else if (QString::compare(input, QLatin1String("Reject"), Qt::CaseInsensitive) == 0
+ || QString::compare(input, QLatin1String("R"), Qt::CaseInsensitive) == 0) {
+ return false;
+ } else {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Unknown answer:" << input;
+ }
+ }
+}
+
bool PackageManagerCorePrivate::askUserConfirmCommand() const
{
qCDebug(QInstaller::lcInstallerInstallLog) << "Do you want to continue?";
@@ -2929,4 +3410,60 @@ bool PackageManagerCorePrivate::packageNeedsUpdate(const LocalPackage &localPack
return updateNeeded;
}
+void PackageManagerCorePrivate::commitPendingUnstableComponents()
+{
+ if (m_pendingUnstableComponents.isEmpty())
+ return;
+
+ for (auto &componentName : m_pendingUnstableComponents.keys()) {
+ Component *const component = m_core->componentByName(componentName);
+ if (!component) {
+ qCWarning(lcInstallerInstallLog) << "Failure while marking component "
+ "unstable. No such component exists:" << componentName;
+ continue;
+ }
+
+ const QPair<Component::UnstableError, QString> unstableError
+ = m_pendingUnstableComponents.value(componentName);
+
+ component->setUnstable(unstableError.first, unstableError.second);
+ }
+ m_pendingUnstableComponents.clear();
+}
+
+void PackageManagerCorePrivate::createAutoDependencyHash(const QString &component, const QString &oldDependencies, const QString &newDependencies)
+{
+ // User might have changed autodependencies with setValue. Remove the old values.
+ const QStringList oldDependencyList = oldDependencies.split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
+ for (const QString &removedDependency : oldDependencyList) {
+ QStringList value = m_autoDependencyComponentHash.value(removedDependency);
+ value.removeAll(component);
+ if (value.isEmpty())
+ m_autoDependencyComponentHash.remove(removedDependency);
+ else
+ m_autoDependencyComponentHash.insert(removedDependency, value);
+ }
+
+ const QStringList newDependencyList = newDependencies.split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
+ for (const QString &autodepend : newDependencyList) {
+ QStringList value = m_autoDependencyComponentHash.value(autodepend);
+ if (!value.contains(component)) {
+ value.append(component);
+ m_autoDependencyComponentHash.insert(autodepend, value);
+ }
+ }
+}
+
+void PackageManagerCorePrivate::createLocalDependencyHash(const QString &component, const QString &dependencies)
+{
+ const QStringList localDependencies = dependencies.split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
+ for (const QString &depend : localDependencies) {
+ QStringList value = m_localDependencyComponentHash.value(depend);
+ if (!value.contains(component)) {
+ value.append(component);
+ m_localDependencyComponentHash.insert(depend, value);
+ }
+ }
+}
+
} // namespace QInstaller