diff options
Diffstat (limited to 'src/libs/installer/packagemanagercore_p.cpp')
-rw-r--r-- | src/libs/installer/packagemanagercore_p.cpp | 469 |
1 files changed, 349 insertions, 120 deletions
diff --git a/src/libs/installer/packagemanagercore_p.cpp b/src/libs/installer/packagemanagercore_p.cpp index 7313a2761..10ca27d00 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> @@ -151,25 +154,56 @@ static void deferredRename(const QString &oldName, const QString &newName, bool #endif } +static bool filterMissingAliasesToInstall(const QString& component, const QList<ComponentAlias *> packages) +{ + bool packageFound = false; + for (qsizetype i = 0; i < packages.size(); ++i) { + packageFound = (packages.at(i)->name() == component); + if (packageFound) + break; + } + return !packageFound; +} + +static bool filterMissingPackagesToInstall(const QString& component, const PackagesList& packages) +{ + bool packageFound = false; + for (qsizetype i = 0; i < packages.size(); ++i) { + packageFound = (packages.at(i)->data(scName).toString() == component); + if (packageFound) + break; + } + return !packageFound; +} // -- PackageManagerCorePrivate PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core) : m_updateFinder(nullptr) + , m_aliasFinder(nullptr) , m_localPackageHub(std::make_shared<LocalPackageHub>()) , m_status(PackageManagerCore::Unfinished) , m_needsHardRestart(false) , m_testChecksum(false) , m_launchedAsRoot(AdminAuthorization::hasAdminRights()) + , m_commandLineInstance(false) + , m_defaultInstall(false) + , m_userSetBinaryMarker(false) + , m_checkAvailableSpace(true) , m_completeUninstall(false) , m_needToWriteMaintenanceTool(false) , m_dependsOnLocalInstallerBinary(false) + , m_autoAcceptLicenses(false) + , m_disableWriteMaintenanceTool(false) + , m_autoConfirmCommand(false) , m_core(core) , m_updates(false) + , m_aliases(false) , m_repoFetched(false) , m_updateSourcesAdded(false) , m_magicBinaryMarker(0) // initialize with pseudo marker , m_magicMarkerSupplement(BinaryContent::Default) + , m_foundEssentialUpdate(false) , m_componentScriptEngine(nullptr) , m_controlScriptEngine(nullptr) , m_installerCalculator(nullptr) @@ -177,37 +211,46 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core) , m_proxyFactory(nullptr) , m_defaultModel(nullptr) , m_updaterModel(nullptr) + , m_componentSortFilterProxyModel(nullptr) , m_guiObject(nullptr) , m_remoteFileEngineHandler(nullptr) - , m_foundEssentialUpdate(false) - , m_commandLineInstance(false) - , m_defaultInstall(false) - , m_userSetBinaryMarker(false) - , m_checkAvailableSpace(true) - , m_autoAcceptLicenses(false) - , m_disableWriteMaintenanceTool(false) - , m_autoConfirmCommand(false) , m_datFileName(QString()) +#ifdef INSTALLCOMPRESSED + , m_allowCompressedRepositoryInstall(true) +#else + , m_allowCompressedRepositoryInstall(false) +#endif + , m_connectedOperations(0) { } PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, qint64 magicInstallerMaker, const QList<OperationBlob> &performedOperations, const QString &datFileName) : m_updateFinder(nullptr) + , m_aliasFinder(nullptr) , m_localPackageHub(std::make_shared<LocalPackageHub>()) , m_status(PackageManagerCore::Unfinished) , m_needsHardRestart(false) , m_testChecksum(false) , m_launchedAsRoot(AdminAuthorization::hasAdminRights()) + , m_commandLineInstance(false) + , m_defaultInstall(false) + , m_userSetBinaryMarker(false) + , m_checkAvailableSpace(true) , m_completeUninstall(false) , m_needToWriteMaintenanceTool(false) , m_dependsOnLocalInstallerBinary(false) + , m_autoAcceptLicenses(false) + , m_disableWriteMaintenanceTool(false) + , m_autoConfirmCommand(false) , m_core(core) , m_updates(false) + , m_aliases(false) , m_repoFetched(false) , m_updateSourcesAdded(false) , m_magicBinaryMarker(magicInstallerMaker) , m_magicMarkerSupplement(BinaryContent::Default) + , m_foundEssentialUpdate(false) , m_componentScriptEngine(nullptr) , m_controlScriptEngine(nullptr) , m_installerCalculator(nullptr) @@ -215,22 +258,21 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, q , m_proxyFactory(nullptr) , m_defaultModel(nullptr) , m_updaterModel(nullptr) + , m_componentSortFilterProxyModel(nullptr) , m_guiObject(nullptr) , m_remoteFileEngineHandler(new RemoteFileEngineHandler) - , m_foundEssentialUpdate(false) - , m_commandLineInstance(false) - , m_defaultInstall(false) - , m_userSetBinaryMarker(false) - , m_checkAvailableSpace(true) - , m_autoAcceptLicenses(false) - , m_disableWriteMaintenanceTool(false) - , m_autoConfirmCommand(false) , m_datFileName(datFileName) +#ifdef INSTALLCOMPRESSED + , m_allowCompressedRepositoryInstall(true) +#else + , m_allowCompressedRepositoryInstall(false) +#endif + , m_connectedOperations(0) { foreach (const OperationBlob &operation, performedOperations) { - QScopedPointer<QInstaller::Operation> op(KDUpdater::UpdateOperationFactory::instance() + std::unique_ptr<QInstaller::Operation> op(KDUpdater::UpdateOperationFactory::instance() .create(operation.name, core)); - if (op.isNull()) { + if (!op) { qCWarning(QInstaller::lcInstallerInstallLog) << "Failed to load unknown operation" << operation.name; continue; @@ -241,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, @@ -270,6 +312,7 @@ PackageManagerCorePrivate::~PackageManagerCorePrivate() qDeleteAll(m_performedOperationsCurrentSession); delete m_updateFinder; + delete m_aliasFinder; delete m_proxyFactory; delete m_defaultModel; @@ -401,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; } @@ -430,6 +473,90 @@ bool PackageManagerCorePrivate::buildComponentTree(QHash<QString, Component*> &c return true; } +bool PackageManagerCorePrivate::buildComponentAliases() +{ + // For now, aliases are only used for command line runs + if (!m_core->isCommandLineInstance()) + return true; + + { + const QList<ComponentAlias *> aliasList = componentAliases(); + if (aliasList.isEmpty()) + return true; + + for (const auto *alias : aliasList) { + // Create a new alias object for package manager core to take ownership of + ComponentAlias *newAlias = new ComponentAlias(m_core); + for (const QString &key : alias->keys()) + newAlias->setValue(key, alias->value(key)); + + m_componentAliases.insert(alias->name(), newAlias); + } + } + // After aliases are loaded, perform sanity checks: + + // 1. Component check state is changed by alias selection, so store the initial state + storeCheckState(); + + Graph<QString> aliasGraph; + for (auto *alias : qAsConst(m_componentAliases)) { + aliasGraph.addNode(alias->name()); + aliasGraph.addEdges(alias->name(), + QInstaller::splitStringWithComma(alias->value(scRequiredAliases)) << + QInstaller::splitStringWithComma(alias->value(scOptionalAliases))); + + if (!m_core->componentByName(alias->name())) { + // Name ok, select for sanity check calculation + alias->setSelected(true); + } else { + alias->setUnstable(ComponentAlias::ComponentNameConfict, + tr("Alias declares name that conflicts with an existing component \"%1\"") + .arg(alias->name())); + } + } + + const QList<QString> sortedAliases = aliasGraph.sort(); + // 2. Check for cyclic dependency errors + if (aliasGraph.hasCycle()) { + setStatus(PackageManagerCore::Failure, installerCalculator()->error()); + MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("Error"), + tr("Unresolved component aliases"), + tr("Cyclic dependency between aliases \"%1\" and \"%2\" detected.") + .arg(aliasGraph.cycle().first, aliasGraph.cycle().second)); + + return false; + } + + // 3. Test for required aliases and components, this triggers setting the + // alias unstable in case of a broken reference. + for (const auto &aliasName : sortedAliases) { + ComponentAlias *alias = m_componentAliases.value(aliasName); + if (!alias) // sortedAliases may contain dependencies that don't exist, we don't know it yet + continue; + + alias->components(); + alias->aliases(); + } + + clearInstallerCalculator(); + // 4. Check for other errors preventing resolving components to install + if (!installerCalculator()->solve(m_componentAliases.values())) { + setStatus(PackageManagerCore::Failure, installerCalculator()->error()); + MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("Error"), + tr("Unresolved component aliases"), installerCalculator()->error()); + + return false; + } + + for (auto *alias : qAsConst(m_componentAliases)) + alias->setSelected(false); + + // 5. Restore original state + restoreCheckState(); + + return true; +} + template <typename T> bool PackageManagerCorePrivate::loadComponentScripts(const T &components, const bool postScript) { @@ -468,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 @@ -486,6 +617,9 @@ ScriptEngine *PackageManagerCorePrivate::controlScriptEngine() const void PackageManagerCorePrivate::clearAllComponentLists() { + qDeleteAll(m_componentAliases); + m_componentAliases.clear(); + QList<QInstaller::Component*> toDelete; toDelete << m_rootComponents << m_deletedReplacedComponents; @@ -645,6 +779,10 @@ void PackageManagerCorePrivate::initialize(const QHash<QString, QString> ¶ms 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 +911,7 @@ QString PackageManagerCorePrivate::maintenanceToolAliasPath() const if (aliasName.isEmpty()) return QString(); - const bool isRoot = (AdminAuthorization::hasAdminRights() || RemoteClient::instance().isActive()); + const bool isRoot = m_core->hasAdminRights(); const QString applicationsDir = m_core->value( isRoot ? QLatin1String("ApplicationsDir") : QLatin1String("ApplicationsDirUser") ); @@ -864,7 +1002,7 @@ void PackageManagerCorePrivate::writeMaintenanceConfigFiles() if (key == scRunProgramDescription || key == scRunProgram || key == scRunProgramArguments) continue; QVariant value = m_data.value(key); - if (value.canConvert(QVariant::String)) + if (value.canConvert<QString>()) value = replacePath(value.toString(), targetDir(), QLatin1String(scRelocatable)); variables.insert(key, value); } @@ -888,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(); @@ -922,6 +1060,8 @@ void PackageManagerCorePrivate::writeMaintenanceConfigFiles() writer.writeEndElement(); writer.writeTextElement(QLatin1String("LocalCachePath"), m_data.settings().localCachePath()); writer.writeEndElement(); + + file.write(outputStr.toUtf8()); } setDefaultFilePermissions(&file, DefaultFilePermissions::NonExecutable); } @@ -1070,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++; } } } @@ -1296,7 +1439,7 @@ void PackageManagerCorePrivate::writeMaintenanceToolAppBundle(OperationList &per const QString after = QLatin1String("<string>") + QFileInfo(maintenanceToolName()).baseName() + QLatin1String("</string>"); while (!in.atEnd()) - out << in.readLine().replace(before, after) << endl; + out << in.readLine().replace(before, after) << Qt::endl; // copy qt_menu.nib if it exists op = createOwnedOperation(QLatin1String("Mkdir")); @@ -1740,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; @@ -1774,7 +1917,7 @@ bool PackageManagerCorePrivate::runInstaller() double progressOperationSize = componentsInstallPartProgressSize / progressOperationCount; // Now install the requested components - unpackAndInstallComponents(componentsToInstall, progressOperationSize, adminRightsGained); + unpackAndInstallComponents(componentsToInstall, progressOperationSize); if (m_core->isOfflineOnly() && PackageManagerCore::createLocalRepositoryFromBinary()) { emit m_core->titleMessageChanged(tr("Creating local repository")); @@ -1932,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_installerCalculator->orderedComponentsToInstall().contains(m_componentsToReplaceUpdaterMode.value(name).first)) { + && !m_installerCalculator->resolvedComponents().contains(m_componentsToReplaceUpdaterMode.value(name).first)) { nonRevertedOperations.append(operation); continue; } @@ -1972,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(); } @@ -1993,7 +2136,7 @@ bool PackageManagerCorePrivate::runPackageUpdater() if (undoOperations.count() > 0) { ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("Removing deselected components...")); - runUndoOperations(undoOperations, undoOperationProgressSize, adminRightsGained, true); + runUndoOperations(undoOperations, undoOperationProgressSize, true); } m_performedOperationsOld = nonRevertedOperations; // these are all operations left: those not reverted @@ -2001,7 +2144,7 @@ bool PackageManagerCorePrivate::runPackageUpdater() const double progressOperationSize = componentsInstallPartProgressSize / progressOperationCount; // Now install the requested new components - unpackAndInstallComponents(componentsToInstall, progressOperationSize, adminRightsGained); + unpackAndInstallComponents(componentsToInstall, progressOperationSize); emit m_core->titleMessageChanged(tr("Creating Maintenance Tool")); @@ -2070,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(); } @@ -2078,7 +2221,7 @@ bool PackageManagerCorePrivate::runUninstaller() const int uninstallOperationCount = countProgressOperations(undoOperations); const double undoOperationProgressSize = double(1) / double(uninstallOperationCount); - runUndoOperations(undoOperations, undoOperationProgressSize, adminRightsGained, false); + runUndoOperations(undoOperations, undoOperationProgressSize, false); // No operation delete here, as all old undo operations are deleted in the destructor. deleteMaintenanceTool(); // this will also delete the TargetDir on Windows @@ -2087,7 +2230,7 @@ bool PackageManagerCorePrivate::runUninstaller() // If not on Windows, we need to remove TargetDir manually. #ifndef Q_OS_WIN if (QVariant(m_core->value(scRemoveTargetDir)).toBool() && !targetDir().isEmpty()) { - if (updateAdminRights && !adminRightsGained) + if (updateAdminRights && !m_core->hasAdminRights()) adminRightsGained = m_core->gainAdminRights(); removeDirectoryThreaded(targetDir(), true); qCDebug(QInstaller::lcInstallerInstallLog) << "Complete uninstallation was chosen."; @@ -2249,7 +2392,7 @@ bool PackageManagerCorePrivate::runOfflineGenerator() } void PackageManagerCorePrivate::unpackComponents(const QList<Component *> &components, - double progressOperationSize, bool adminRightsGained) + double progressOperationSize) { OperationList unpackOperations; bool becameAdmin = false; @@ -2273,7 +2416,7 @@ void PackageManagerCorePrivate::unpackComponents(const QList<Component *> &compo // There's currently no way to control this on a per-operation basis, so // any op requesting execution as admin means all extracts are done as admin. - if (!adminRightsGained && !becameAdmin && op->value(QLatin1String("admin")).toBool()) + if (!m_core->hasAdminRights() && op->value(QLatin1String("admin")).toBool()) becameAdmin = m_core->gainAdminRights(); } } @@ -2321,7 +2464,7 @@ void PackageManagerCorePrivate::unpackComponents(const QList<Component *> &compo continue; } // Backup may request performing operation as admin - if (!adminRightsGained && !becameAdmin && operation->value(QLatin1String("admin")).toBool()) + if (!m_core->hasAdminRights() && operation->value(QLatin1String("admin")).toBool()) becameAdmin = m_core->gainAdminRights(); } @@ -2390,8 +2533,7 @@ void PackageManagerCorePrivate::unpackComponents(const QList<Component *> &compo ProgressCoordinator::instance()->emitDetailTextChanged(tr("Done")); } -void PackageManagerCorePrivate::installComponent(Component *component, double progressOperationSize, - bool adminRightsGained) +void PackageManagerCorePrivate::installComponent(Component *component, double progressOperationSize) { OperationList operations = component->operations(Operation::Install); if (!component->operationsCreatedSuccessfully()) @@ -2412,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; } @@ -2501,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) @@ -2637,9 +2810,12 @@ void PackageManagerCorePrivate::registerMaintenanceTool() settings.setValue(QLatin1String("Comments"), m_data.value(scTitle)); settings.setValue(QLatin1String("InstallDate"), QDateTime::currentDateTime().toString()); settings.setValue(QLatin1String("InstallLocation"), QDir::toNativeSeparators(targetDir())); - settings.setValue(QLatin1String("UninstallString"), quoted(maintenanceTool)); - settings.setValue(QLatin1String("ModifyPath"), QString(quoted(maintenanceTool) - + QLatin1String(" --manage-packages"))); + settings.setValue(QLatin1String("UninstallString"), QString(quoted(maintenanceTool) + + QLatin1String(" --") + CommandLineOptions::scStartUninstallerLong)); + if (!isOfflineOnly()) { + settings.setValue(QLatin1String("ModifyPath"), QString(quoted(maintenanceTool) + + QLatin1String(" --") + CommandLineOptions::scStartPackageManagerLong)); + } // required disk space of the installed components quint64 estimatedSizeKB = m_core->requiredDiskSpace() / 1024; // add required space for the maintenance tool @@ -2676,8 +2852,8 @@ void PackageManagerCorePrivate::unregisterMaintenanceTool() #endif } -void PackageManagerCorePrivate::runUndoOperations(const OperationList &undoOperations, double progressSize, - bool adminRightsGained, bool deleteOperation) +void PackageManagerCorePrivate::runUndoOperations(const OperationList &undoOperations, + double progressSize, bool deleteOperation) { try { const int operationsCount = undoOperations.size(); @@ -2688,7 +2864,7 @@ void PackageManagerCorePrivate::runUndoOperations(const OperationList &undoOpera throw Error(tr("Installation canceled by user")); bool becameAdmin = false; - if (!adminRightsGained && undoOperation->value(QLatin1String("admin")).toBool()) + if (!m_core->hasAdminRights() && undoOperation->value(QLatin1String("admin")).toBool()) becameAdmin = m_core->gainAdminRights(); connectOperationToInstaller(undoOperation, progressSize); @@ -2796,6 +2972,25 @@ LocalPackagesMap PackageManagerCorePrivate::localInstalledPackages() return m_localPackageHub->localPackages(); } +QList<ComponentAlias *> PackageManagerCorePrivate::componentAliases() +{ + if (m_aliases && m_aliasFinder) + return m_aliasFinder->aliases(); + + m_aliases = false; + delete m_aliasFinder; + + m_aliasFinder = new AliasFinder(m_core); + m_aliasFinder->setAliasSources(m_aliasSources); + if (!m_aliasFinder->run()) { + qCDebug(lcDeveloperBuild) << "No component aliases found." << Qt::endl; + return QList<ComponentAlias *>(); + } + + m_aliases = true; + return m_aliasFinder->aliases(); +} + bool PackageManagerCorePrivate::fetchMetaInformationFromRepositories(DownloadType type) { m_updates = false; @@ -2826,7 +3021,7 @@ bool PackageManagerCorePrivate::fetchMetaInformationFromRepositories(DownloadTyp return m_repoFetched; } -bool PackageManagerCorePrivate::addUpdateResourcesFromRepositories(bool parseChecksum, bool compressedRepository) +bool PackageManagerCorePrivate::addUpdateResourcesFromRepositories(bool compressedRepository) { if (!compressedRepository && m_updateSourcesAdded) return m_updateSourcesAdded; @@ -2857,38 +3052,10 @@ bool PackageManagerCorePrivate::addUpdateResourcesFromRepositories(bool parseChe if (data->path().isEmpty()) continue; - if (parseChecksum) { - const QString updatesXmlPath = data->path() + QLatin1String("/Updates.xml"); - QFile updatesFile(updatesXmlPath); - try { - QInstaller::openForRead(&updatesFile); - } catch(const Error &e) { - qCWarning(QInstaller::lcInstallerInstallLog) << "Error opening Updates.xml:" - << e.message(); - setStatus(PackageManagerCore::Failure, tr("Cannot add temporary update source information.")); - return false; - } - - int line = 0; - int column = 0; - QString error; - QDomDocument doc; - if (!doc.setContent(&updatesFile, &error, &line, &column)) { - qCWarning(QInstaller::lcInstallerInstallLog).nospace() << "Parse error in file " - << updatesFile.fileName() << ": " << error << " at line " << line - << " col " << column; - setStatus(PackageManagerCore::Failure, tr("Cannot add temporary update source information.")); - return false; - } - - const QDomNode checksum = doc.documentElement().firstChildElement(QLatin1String("Checksum")); - if (!checksum.isNull()) - m_core->setTestChecksum(checksum.toElement().text().toLower() == scTrue); - } if (data->repository().isCompressed()) - m_compressedPackageSources.insert(PackageSource(QUrl::fromLocalFile(data->path()), 2)); + m_compressedPackageSources.insert(PackageSource(QUrl::fromLocalFile(data->path()), 2, data->repository().postLoadComponentScript())); else - m_packageSources.insert(PackageSource(QUrl::fromLocalFile(data->path()), 0)); + m_packageSources.insert(PackageSource(QUrl::fromLocalFile(data->path()), 0, data->repository().postLoadComponentScript())); ProductKeyCheck::instance()->addPackagesFromXml(data->path() + QLatin1String("/Updates.xml")); } @@ -2928,19 +3095,76 @@ void PackageManagerCorePrivate::storeCheckState() m_coreCheckedHash.insert(component, component->checkState()); } -void PackageManagerCorePrivate::updateComponentCheckedState() +void PackageManagerCorePrivate::updateComponentInstallActions() { for (Component *component : m_core->components(PackageManagerCore::ComponentType::All)) { component->setInstallAction(component->isInstalled() ? ComponentModelHelper::KeepInstalled : ComponentModelHelper::KeepUninstalled); } - for (Component *component : uninstallerCalculator()->componentsToUninstall()) + for (Component *component : uninstallerCalculator()->resolvedComponents()) component->setInstallAction(ComponentModelHelper::Uninstall); - for (Component *component : installerCalculator()->orderedComponentsToInstall()) + for (Component *component : installerCalculator()->resolvedComponents()) component->setInstallAction(ComponentModelHelper::Install); } +bool PackageManagerCorePrivate::enableAllCategories() +{ + QSet<RepositoryCategory> repoCategories = m_data.settings().repositoryCategories(); + bool additionalRepositoriesEnabled = false; + for (const auto &category : repoCategories) { + if (!category.isEnabled()) { + additionalRepositoriesEnabled = true; + enableRepositoryCategory(category, true); + } + } + return additionalRepositoriesEnabled; +} + +void PackageManagerCorePrivate::enableRepositoryCategory(const RepositoryCategory &repoCategory, const bool enable) +{ + RepositoryCategory replacement = repoCategory; + replacement.setEnabled(enable); + QSet<RepositoryCategory> tmpRepoCategories = m_data.settings().repositoryCategories(); + if (tmpRepoCategories.contains(repoCategory)) { + tmpRepoCategories.remove(repoCategory); + tmpRepoCategories.insert(replacement); + m_data.settings().addRepositoryCategories(tmpRepoCategories); + } +} + +bool PackageManagerCorePrivate::installablePackagesFound(const QStringList& components) +{ + if (components.isEmpty()) + return true; + + PackagesList remotes = remotePackages(); + + auto componentsNotFoundForInstall = QtConcurrent::blockingFiltered( + components, + [remotes](const QString& installerPackage) { + return filterMissingPackagesToInstall(installerPackage, remotes); + } + ); + + if (componentsNotFoundForInstall.count() > 0) { + QList<ComponentAlias *> aliasList = componentAliases(); + auto aliasesNotFoundForInstall = QtConcurrent::blockingFiltered( + components, + [aliasList](const QString& installerPackage) { + return filterMissingAliasesToInstall(installerPackage, aliasList); + } + ); + + if (aliasesNotFoundForInstall.count() > 0) { + qCDebug(QInstaller::lcInstallerInstallLog).noquote().nospace() << "Cannot select " << aliasesNotFoundForInstall.join(QLatin1String(", ")) << ". Component(s) not found."; + setStatus(PackageManagerCore::NoPackagesFound); + return false; + } + } + return true; +} + void PackageManagerCorePrivate::connectOperationCallMethodRequest(Operation *const operation) { QObject *const operationObject = dynamic_cast<QObject *> (operation); @@ -3001,16 +3225,16 @@ void PackageManagerCorePrivate::addPathForDeletion(const QString &path) } void PackageManagerCorePrivate::unpackAndInstallComponents(const QList<Component *> &components, - const double progressOperationSize, const bool adminRightsGained) + const double progressOperationSize) { // Perform extract operations - unpackComponents(components, progressOperationSize, adminRightsGained); + unpackComponents(components, progressOperationSize); // Perform rest of the operations and mark component as installed const int componentsToInstallCount = components.size(); int installedComponents = 0; foreach (Component *component, components) { - installComponent(component, progressOperationSize, adminRightsGained); + installComponent(component, progressOperationSize); ++installedComponents; ProgressCoordinator::instance()->emitAdditionalProgressStatus(tr("%1 of %2 components installed.") @@ -3036,24 +3260,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(); @@ -3062,7 +3268,7 @@ bool PackageManagerCorePrivate::calculateComponentsAndRun() qCDebug(QInstaller::lcInstallerInstallLog) << "Installation canceled."; } else if (componentsOk && acceptLicenseAgreements()) { try { - loadComponentScripts(installerCalculator()->orderedComponentsToInstall(), true); + loadComponentScripts(installerCalculator()->resolvedComponents(), true); } catch (const Error &error) { qCWarning(QInstaller::lcInstallerInstallLog) << error.message(); return false; @@ -3070,9 +3276,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."; @@ -3099,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(); @@ -3147,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?"; |