diff options
Diffstat (limited to 'src/libs/kdtools/updateoperation.cpp')
-rw-r--r-- | src/libs/kdtools/updateoperation.cpp | 179 |
1 files changed, 147 insertions, 32 deletions
diff --git a/src/libs/kdtools/updateoperation.cpp b/src/libs/kdtools/updateoperation.cpp index 897fecf1b..af89382a8 100644 --- a/src/libs/kdtools/updateoperation.cpp +++ b/src/libs/kdtools/updateoperation.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -80,6 +81,21 @@ using namespace KDUpdater; Undo operation. */ +/*! + \enum UpdateOperation::OperationGroup + This enum specifies the execution group of the operation. + + \value Unpack + Operation should be run in the unpacking phase. Operations in + this group are run concurrently between all selected components. + \value Install + Operation should be run in the installation phase. + \value All + All available operation groups. + \value Default + The default group for operations, synonym for Install. +*/ + /* \internal Returns a filename for a temporary file based on \a templateName. @@ -99,10 +115,13 @@ static QString backupFileName(const QString &templateName) \internal */ UpdateOperation::UpdateOperation(QInstaller::PackageManagerCore *core) - : m_error(0) + : m_group(OperationGroup::Default) + , m_error(0) , m_core(core) , m_requiresUnreplacedVariables(false) { + qRegisterMetaType<UpdateOperation *>(); + // Store the value for compatibility reasons. m_values[QLatin1String("installer")] = QVariant::fromValue(core); } @@ -139,6 +158,16 @@ QString UpdateOperation::operationCommand() const } /*! + Returns the execution group this operation belongs to. + + \sa setGroup() +*/ +UpdateOperation::OperationGroup UpdateOperation::group() const +{ + return m_group; +} + +/*! Returns \c true if a value called \a name exists, otherwise returns \c false. */ bool UpdateOperation::hasValue(const QString &name) const @@ -180,6 +209,17 @@ void UpdateOperation::setName(const QString &name) } /*! + Sets the execution group of the operation to \a group. Subclasses can change + the group to control which installation phase this operation should be run in. + + The default group is \c Install. +*/ +void UpdateOperation::setGroup(const OperationGroup &group) +{ + m_group = group; +} + +/*! Sets the arguments for the update operation to \a args. */ void UpdateOperation::setArguments(const QStringList &args) @@ -252,23 +292,18 @@ QStringList UpdateOperation::parsePerformOperationArguments() } /*! - Returns undo operation argument list. If the installation is - cancelled or failed, returns an empty list so that full undo - operation can be performed. + Returns \c true if operation undo should not be performed. + Returns \c false if the installation is cancelled or failed, or + \c UNDOOPERATION is not set in operation call. */ -QStringList UpdateOperation::parseUndoOperationArguments() +bool UpdateOperation::skipUndoOperation() { //Install has failed, allow a normal undo if (m_core && (m_core->status() == QInstaller::PackageManagerCore::Canceled || m_core->status() == QInstaller::PackageManagerCore::Failure)) { - return QStringList(); - } - int index = arguments().indexOf(QLatin1String("UNDOOPERATION")); - QStringList args; - if ((index != -1) && (arguments().length() > index + 1)) { - args = arguments().mid(index + 1); + return false; } - return args; + return arguments().contains(QLatin1String("UNDOOPERATION")); } /*! @@ -281,6 +316,41 @@ void UpdateOperation::setRequiresUnreplacedVariables(bool isRequired) m_requiresUnreplacedVariables = isRequired; } +/*! + Replaces installer \c value \a variableValue with predefined variable. + If \c key is found for the \a variableValue and the \c key ends with string _OLD, + the initial \a variableValue is replaced with the \c value having a key + without _OLD ending. This way we can replace the hard coded values defined for operations, + if the value has for some reason changed. For example if we set following variables + in install script: + \badcode + installer.setValue("MY_OWN_EXECUTABLE", "C:/Qt/NewLocation/Tools.exe") + installer.setValue("MY_OWN_EXECUTABLE_OLD", "C:/Qt/OldLocation/Tools.exe") + \endcode + and we have moved the Tools.exe from OldLocation to NewLocation, the operation + continues to work and use the Tools.exe from NewLocation although original + installation has been made with Tools.exe in OldLocation. + Returns \c true if \a variableValue is replaced. +*/ +bool UpdateOperation::variableReplacement(QString *variableValue) +{ + bool variableValueChanged = false; + const QString valueNormalized = QDir::cleanPath(*variableValue); + QString key = m_core->key(valueNormalized); + if (key.endsWith(QLatin1String("_OLD"))) { + key.chop(4); + if (m_core->containsValue(key)) { + key.prepend(QLatin1String("@")); + key.append(QLatin1String("@")); + *variableValue = m_core->replaceVariables(key); + qCDebug(QInstaller::lcInstallerInstallLog) << "Running above operation with replaced value: " << valueNormalized + << "has been replaced with" << *variableValue; + variableValueChanged = true; + } + } + return variableValueChanged; +} + struct StartsWith { explicit StartsWith(const QString &searchTerm) @@ -475,8 +545,15 @@ QDomDocument UpdateOperation::toXml() const const QString target = m_core ? m_core->value(QInstaller::scTargetDir) : QString(); Q_FOREACH (const QString &s, arguments()) { QDomElement arg = doc.createElement(QLatin1String("argument")); - arg.appendChild(doc.createTextNode(QInstaller::replacePath(s, target, - QLatin1String(QInstaller::scRelocatable)))); + // Do not call cleanPath to Execute operations paths. The operation might require the + // exact separators that are set in the operation call. + if (name() == QLatin1String("Execute")) { + arg.appendChild(doc.createTextNode(QInstaller::replacePath(s, target, + QLatin1String(QInstaller::scRelocatable), false))); + } else { + arg.appendChild(doc.createTextNode(QInstaller::replacePath(s, target, + QLatin1String(QInstaller::scRelocatable)))); + } args.appendChild(arg); } root.appendChild(args); @@ -495,14 +572,21 @@ QDomDocument UpdateOperation::toXml() const value.setAttribute(QLatin1String("name"), it.key()); value.setAttribute(QLatin1String("type"), QLatin1String(variant.typeName())); - if (variant.type() != QVariant::List && variant.type() != QVariant::StringList - && variant.canConvert(QVariant::String)) { - // it can convert to string? great! - value.appendChild(doc.createTextNode(QInstaller::replacePath(variant.toString(), - target, QLatin1String(QInstaller::scRelocatable)))); + int variantType; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + variantType = variant.typeId(); +#else + variantType = variant.type(); +#endif + + if (variantType != QMetaType::QStringList + && variant.canConvert<QString>()) { + // it can convert to string? great! + value.appendChild(doc.createTextNode(QInstaller::replacePath(variant.toString(), + target, QLatin1String(QInstaller::scRelocatable)))); } else { // no? then we have to go the hard way... - if (variant.type() == QVariant::StringList) { + if (variantType == QMetaType::QStringList) { QStringList list = variant.toStringList(); for (int i = 0; i < list.count(); ++i) { list[i] = QInstaller::replacePath(list.at(i), target, @@ -528,6 +612,7 @@ QDomDocument UpdateOperation::toXml() const bool UpdateOperation::fromXml(const QDomDocument &doc) { QString target = QCoreApplication::applicationDirPath(); + static const QLatin1String relocatable = QLatin1String(QInstaller::scRelocatable); // Does not change target on non macOS platforms. if (QInstaller::isInBundle(target, &target)) target = QDir::cleanPath(target + QLatin1String("/..")); @@ -539,8 +624,20 @@ bool UpdateOperation::fromXml(const QDomDocument &doc) for (QDomNode n = argsElem.firstChild(); ! n.isNull(); n = n.nextSibling()) { const QDomElement e = n.toElement(); if (!e.isNull() && e.tagName() == QLatin1String("argument")) { - args << QInstaller::replacePath(e.text(), QLatin1String(QInstaller::scRelocatable), - target); + // Sniff the Execute -operations file path separator. The operation might be + // strict with the used path separator + bool useCleanPath = true; + if (name() == QLatin1String("Execute")) { + if (e.text().startsWith(relocatable) && e.text().size() > relocatable.size()) { + const QChar separator = e.text().at(relocatable.size()); + if (separator == QLatin1Char('\\')) { + target = QDir::toNativeSeparators(target); + useCleanPath = false; + } + } + } + args << QInstaller::replacePath(e.text(), relocatable, + target, useCleanPath); } } setArguments(args); @@ -556,25 +653,31 @@ bool UpdateOperation::fromXml(const QDomDocument &doc) const QString type = v.attribute(QLatin1String("type")); const QString value = v.text(); + int variantType; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + const QMetaType t = QMetaType::fromName(type.toLatin1().data()); + variantType = t.id(); +#else const QVariant::Type t = QVariant::nameToType(type.toLatin1().data()); - QVariant var = qVariantFromValue(value); - if (t == QVariant::List || t == QVariant::StringList || !var.convert(t)) { - QDataStream stream(QByteArray::fromBase64( value.toLatin1())); + variantType = t; +#endif + QVariant var = QVariant::fromValue(value); + if (variantType == QMetaType::QStringList || !var.canConvert(t)) { + QDataStream stream(QByteArray::fromBase64(value.toLatin1())); stream >> var; - if (t == QVariant::StringList) { + if (variantType == QMetaType::QStringList) { QStringList list = var.toStringList(); for (int i = 0; i < list.count(); ++i) { list[i] = QInstaller::replacePath(list.at(i), - QLatin1String(QInstaller::scRelocatable), target); + relocatable, target); } var = QVariant::fromValue(list); } - } else if (t == QVariant::String) { - const QString str = QInstaller::replacePath(value, - QLatin1String(QInstaller::scRelocatable), target); - var = QVariant::fromValue(str); + } else if (variantType == QMetaType::QString) { + const QString str = QInstaller::replacePath(value, + relocatable, target); + var = QVariant::fromValue(str); } - m_values[name] = var; } @@ -582,6 +685,18 @@ bool UpdateOperation::fromXml(const QDomDocument &doc) } /*! + Returns a numerical representation of how this operation compares to + other operations in size, and in time it takes to perform the operation. + + The default returned value is \c 1. Subclasses may override this method to + implement custom size hints. +*/ +quint64 UpdateOperation::sizeHint() +{ + return 1; +} + +/*! \overload Restores operation arguments and values from the XML file at path \a xml. Returns \c true on |