diff options
-rw-r--r-- | doc/installerfw.qdoc | 43 | ||||
-rw-r--r-- | src/libs/ifwtools/binarycreator.cpp | 72 | ||||
-rw-r--r-- | src/libs/ifwtools/binarycreator.h | 4 | ||||
-rw-r--r-- | src/libs/installer/packagemanagercore.cpp | 3 | ||||
-rw-r--r-- | src/libs/installer/packagemanagercore.h | 1 | ||||
-rw-r--r-- | src/libs/installer/packagemanagercore_p.cpp | 146 | ||||
-rw-r--r-- | src/libs/installer/packagemanagercore_p.h | 8 | ||||
-rw-r--r-- | src/sdk/sdkapp.h | 45 | ||||
-rw-r--r-- | tests/auto/installer/createshortcutoperation/tst_createshortcutoperation.cpp | 2 | ||||
-rw-r--r-- | tests/auto/installer/messageboxhandler/tst_messageboxhandler.cpp | 2 | ||||
-rw-r--r-- | tests/auto/installer/packagemanagercore/tst_packagemanagercore.cpp | 4 | ||||
-rw-r--r-- | tests/auto/installer/treename/tst_treename.cpp | 4 | ||||
-rw-r--r-- | tools/binarycreator/main.cpp | 13 |
13 files changed, 236 insertions, 111 deletions
diff --git a/doc/installerfw.qdoc b/doc/installerfw.qdoc index 29399f0ba..e69a545b2 100644 --- a/doc/installerfw.qdoc +++ b/doc/installerfw.qdoc @@ -1861,13 +1861,30 @@ For full reference of elements supported by the configuration file, see \l{Configuration File}. + \section2 Getting the Maintenance Tool + + The maintenance tool for Linux and Windows is the same as the \c installerbase + executable located in your Qt Installer Framework's \c installation bin folder. + For macOS the maintenance tool app bundle can be created using the \c binarycreator + tool with the command line switch \c --mt or \c --create-maintenancetool. The + name of the macOS app bundle can be configured in config.xml using + the \c <MaintenanceToolName> element. The app bundle can be later signed and + notarized if needed. + + \code + binarycreator -c config/config.xml --mt + \endcode + \section2 Populating the Maintenance Tool Component - The \c installerbase executable from Qt Installer Framework's install folder and \c update.rcc - generated in the \l{Compiling the Update Resource} step should be copied to the component's data - directory. The meta directory should contain a \c package.xml file with the package information - elements of your choice. However it is a good idea to mark the component \c <Essential> so that - it gets automatically installed when running the updater. You may also want to mark the component + In Linux and in Windows the \c installerbase executable from Qt Installer + Framework's installation folder, or in macOS the maintenance tool app bundle, + should be copied to the component's data directory. If you generated the + \c update.rcc in the \l{Compiling the Update Resource} step, copy that to + the data directory as well. The meta directory should contain a \c package.xml + file with the package information elements of your choice. However, it is a + good idea to mark the component \c <Essential> so that it gets automatically + installed when running the updater. You may also want to mark the component \c <Virtual> to hide it from the component selection. For further information about the \c package.xml file, see @@ -1890,11 +1907,21 @@ Component.prototype.onInstallationStarted = function() { if (component.updateRequested() || component.installationRequested()) { - if (installer.value("os") == "win") + if (installer.value("os") == "win") { component.installerbaseBinaryPath = "@TargetDir@/installerbase.exe"; - else if (installer.value("os") == "x11" || installer.value("os") == "mac") + } else if (installer.value("os") == "x11") { component.installerbaseBinaryPath = "@TargetDir@/installerbase"; - + } else if (installer.value("os") == "mac") { + // In macOs maintenance tool can be either installerbase from Qt Installer + // Framework's install folder, or app bundle created by binarycreator + // with --create-maintenancetool switch. "MaintenanceTool.app" -name + // may differ depending on what has been defined in config.xml while + // creating the maintenance tool. + // Use either of the following (not both): + + // component.installerbaseBinaryPath = "@TargetDir@/installerbase"; + component.installerbaseBinaryPath = "@TargetDir@/MaintenanceTool.app"; + } installer.setInstallerBaseBinary(component.installerbaseBinaryPath); var updateResourceFilePath = installer.value("TargetDir") + "/update.rcc"; diff --git a/src/libs/ifwtools/binarycreator.cpp b/src/libs/ifwtools/binarycreator.cpp index 5bef651a8..d7adc2a52 100644 --- a/src/libs/ifwtools/binarycreator.cpp +++ b/src/libs/ifwtools/binarycreator.cpp @@ -223,7 +223,7 @@ static QVersionNumber readMachOMinimumSystemVersion(QIODevice *device) } #endif -static int assemble(Input input, const QInstaller::Settings &settings, const QString &signingIdentity) +static int assemble(Input input, const QInstaller::Settings &settings, const BinaryCreatorArgs &args) { #ifdef Q_OS_MACOS if (QInstaller::isInBundle(input.installerExePath)) { @@ -405,22 +405,26 @@ static int assemble(Input input, const QInstaller::Settings &settings, const QSt QInstaller::appendData(&out, &exe, exe.size()); #endif - foreach (const QInstallerTools::PackageInfo &info, input.packages) { - QInstaller::ResourceCollection collection; - collection.setName(info.name.toUtf8()); - - qDebug() << "Creating resource archive for" << info.name; - foreach (const QString &copiedFile, info.copiedFiles) { - const QSharedPointer<Resource> resource(new Resource(copiedFile)); - qDebug().nospace() << "Appending " << copiedFile << " (" << humanReadableSize(resource->size()) << ")"; - collection.appendResource(resource); + if (!args.createMaintenanceTool) { + foreach (const QInstallerTools::PackageInfo &info, input.packages) { + QInstaller::ResourceCollection collection; + collection.setName(info.name.toUtf8()); + qDebug() << "Creating resource archive for" << info.name; + foreach (const QString &copiedFile, info.copiedFiles) { + const QSharedPointer<Resource> resource(new Resource(copiedFile)); + qDebug().nospace() << "Appending " << copiedFile << " (" << humanReadableSize(resource->size()) << ")"; + collection.appendResource(resource); + } + input.manager.insertCollection(collection); } - input.manager.insertCollection(collection); + + const QList<QInstaller::OperationBlob> operations; + BinaryContent::writeBinaryContent(&out, operations, input.manager, + BinaryContent::MagicInstallerMarker, BinaryContent::MagicCookie); + } else { + createMTDatFile(out); } - const QList<QInstaller::OperationBlob> operations; - BinaryContent::writeBinaryContent(&out, operations, input.manager, - BinaryContent::MagicInstallerMarker, BinaryContent::MagicCookie); } catch (const Error &e) { qCritical("Error occurred while assembling the installer: %s", qPrintable(e.message())); QFile::remove(tempFile); @@ -445,14 +449,14 @@ static int assemble(Input input, const QInstaller::Settings &settings, const QSt QFile::remove(tempFile); #ifdef Q_OS_MACOS - if (isBundle && !signingIdentity.isEmpty()) { + if (isBundle && !args.signingIdentity.isEmpty()) { qDebug() << "Signing .app bundle..."; QProcess p; p.start(QLatin1String("codesign"), QStringList() << QLatin1String("--force") << QLatin1String("--deep") - << QLatin1String("--sign") << signingIdentity + << QLatin1String("--sign") << args.signingIdentity << bundle); if (!p.waitForFinished(-1)) { @@ -503,8 +507,6 @@ static int assemble(Input input, const QInstaller::Settings &settings, const QSt QDir(bundle).removeRecursively(); qDebug() << "done."; } -#else - Q_UNUSED(signingIdentity) #endif return EXIT_SUCCESS; } @@ -754,7 +756,7 @@ int QInstallerTools::createBinary(BinaryCreatorArgs args, QString &argumentError "--offline-only at the same time."); return EXIT_FAILURE; } - if (args.target.isEmpty() && !args.compileResource) { + if (args.target.isEmpty() && !args.compileResource && !args.createMaintenanceTool) { argumentError = QString::fromLatin1("Error: Target parameter missing."); return EXIT_FAILURE; } @@ -762,7 +764,9 @@ int QInstallerTools::createBinary(BinaryCreatorArgs args, QString &argumentError argumentError = QString::fromLatin1("Error: No configuration file selected."); return EXIT_FAILURE; } - if (args.packagesDirectories.isEmpty() && args.repositoryDirectories.isEmpty() && !args.compileResource) { + if (args.packagesDirectories.isEmpty() && args.repositoryDirectories.isEmpty() + && !args.compileResource + && !args.createMaintenanceTool) { argumentError = QString::fromLatin1("Error: Both Package directory and Repository parameters missing."); return EXIT_FAILURE; } @@ -838,11 +842,6 @@ int QInstallerTools::createBinary(BinaryCreatorArgs args, QString &argumentError confInternal.setValue(QLatin1String("offlineOnly"), args.offlineOnly); } -#ifdef Q_OS_MACOS - // on mac, we enforce building a bundle - if (!args.target.endsWith(QLatin1String(".app")) && !args.target.endsWith(QLatin1String(".dmg"))) - args.target += QLatin1String(".app"); -#endif if (!args.compileResource) { // 5; put the copied resources into a resource file ResourceCollection metaCollection("QResources"); @@ -852,11 +851,20 @@ int QInstallerTools::createBinary(BinaryCreatorArgs args, QString &argumentError input.manager.insertCollection(metaCollection); input.packages = packages; - input.outputPath = args.target; + if (args.createMaintenanceTool) + input.outputPath = settings.maintenanceToolName(); + else + input.outputPath = args.target; input.installerExePath = args.templateBinary; +#ifdef Q_OS_MACOS + // on mac, we enforce building a bundle + if (!input.outputPath.endsWith(QLatin1String(".app")) && !input.outputPath.endsWith(QLatin1String(".dmg"))) + input.outputPath += QLatin1String(".app"); +#endif + qDebug() << "Creating the binary"; - exitCode = assemble(input, settings, args.signingIdentity); + exitCode = assemble(input, settings, args); } else { createDefaultResourceFile(tmpMetaDir, QDir::currentPath() + QLatin1String("/update.rcc")); exitCode = EXIT_SUCCESS; @@ -878,3 +886,13 @@ int QInstallerTools::createBinary(BinaryCreatorArgs args, QString &argumentError return exitCode; } + +void QInstallerTools::createMTDatFile(QFile &datFile) +{ + QInstaller::appendInt64(&datFile, 0); // operations start + QInstaller::appendInt64(&datFile, 0); // operations end + QInstaller::appendInt64(&datFile, 0); // resource count + QInstaller::appendInt64(&datFile, 4 * sizeof(qint64)); // data block size + QInstaller::appendInt64(&datFile, BinaryContent::MagicUninstallerMarker); + QInstaller::appendInt64(&datFile, BinaryContent::MagicCookie); +} diff --git a/src/libs/ifwtools/binarycreator.h b/src/libs/ifwtools/binarycreator.h index 7c14ea039..779725660 100644 --- a/src/libs/ifwtools/binarycreator.h +++ b/src/libs/ifwtools/binarycreator.h @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2020 The Qt Company Ltd. +** Copyright (C) 2022 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -68,6 +68,7 @@ struct IFWTOOLS_EXPORT BinaryCreatorArgs FilterType ftype = QInstallerTools::Exclude; bool compileResource = false; QString signingIdentity; + bool createMaintenanceTool = false; }; class BundleBackup @@ -124,6 +125,7 @@ void copyConfigData(const QString &configFile, const QString &targetDir); void copyHighDPIImage(const QFileInfo &childFileInfo, const QString &childName, const QString &targetFile); int IFWTOOLS_EXPORT createBinary(BinaryCreatorArgs args, QString &argumentError); +void IFWTOOLS_EXPORT createMTDatFile(QFile &datFile); } // namespace QInstallerTools diff --git a/src/libs/installer/packagemanagercore.cpp b/src/libs/installer/packagemanagercore.cpp index 0c9533c8d..48d071321 100644 --- a/src/libs/installer/packagemanagercore.cpp +++ b/src/libs/installer/packagemanagercore.cpp @@ -1289,9 +1289,10 @@ PackageManagerCore::PackageManagerCore() and \a mode to set the server side authorization key. */ PackageManagerCore::PackageManagerCore(qint64 magicmaker, const QList<OperationBlob> &operations, + const QString &datFileName, const QString &socketName, const QString &key, Protocol::Mode mode, const QHash<QString, QString> ¶ms, const bool commandLineInstance) - : d(new PackageManagerCorePrivate(this, magicmaker, operations)) + : d(new PackageManagerCorePrivate(this, magicmaker, operations, datFileName)) { setCommandLineInstance(commandLineInstance); Repository::registerMetaType(); // register, cause we stream the type as QVariant diff --git a/src/libs/installer/packagemanagercore.h b/src/libs/installer/packagemanagercore.h index ebb590e35..db1c0765f 100644 --- a/src/libs/installer/packagemanagercore.h +++ b/src/libs/installer/packagemanagercore.h @@ -63,6 +63,7 @@ class INSTALLER_EXPORT PackageManagerCore : public QObject public: PackageManagerCore(); PackageManagerCore(qint64 magicmaker, const QList<OperationBlob> &ops, + const QString &datFilename = QString(), const QString &socketName = QString(), const QString &key = QLatin1String(Protocol::DefaultAuthorizationKey), Protocol::Mode mode = Protocol::Mode::Production, diff --git a/src/libs/installer/packagemanagercore_p.cpp b/src/libs/installer/packagemanagercore_p.cpp index 845c9cf02..c61c6bc99 100644 --- a/src/libs/installer/packagemanagercore_p.cpp +++ b/src/libs/installer/packagemanagercore_p.cpp @@ -188,11 +188,12 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core) , m_autoAcceptLicenses(false) , m_disableWriteMaintenanceTool(false) , m_autoConfirmCommand(false) + , m_datFileName(QString()) { } PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, qint64 magicInstallerMaker, - const QList<OperationBlob> &performedOperations) + const QList<OperationBlob> &performedOperations, const QString &datFileName) : m_updateFinder(nullptr) , m_localPackageHub(std::make_shared<LocalPackageHub>()) , m_status(PackageManagerCore::Unfinished) @@ -226,6 +227,7 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, q , m_autoAcceptLicenses(false) , m_disableWriteMaintenanceTool(false) , m_autoConfirmCommand(false) + , m_datFileName(datFileName) { foreach (const OperationBlob &operation, performedOperations) { QScopedPointer<QInstaller::Operation> op(KDUpdater::UpdateOperationFactory::instance() @@ -766,7 +768,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; @@ -809,6 +816,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); @@ -1153,12 +1167,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"))); @@ -1263,19 +1272,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...) @@ -1285,8 +1284,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)) { @@ -1357,6 +1354,20 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper performOperationThreaded(op); } #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 @@ -1392,45 +1403,70 @@ 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()) { 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."; + 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()) { 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)) { @@ -1526,7 +1562,9 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper writeMaintenanceConfigFiles(); QFile::remove(dataFile); - QFile::rename(dataFile + QLatin1String(".new"), 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:" @@ -1534,11 +1572,11 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper if (newBinaryWritten) { if (isInstaller()) - QFile::rename(maintenanceToolName() + QLatin1String(".new"), maintenanceToolName()); + QFile::rename(mtName + QLatin1String(".new"), mtName); else - deferredRename(maintenanceToolName() + QLatin1String(".new"), maintenanceToolName(), restart); - - writeMaintenanceToolAlias(); + deferredRename(mtName + QLatin1String(".new"), mtName, restart); + QFileInfo mtFileName(mtName); + writeMaintenanceToolAlias(mtFileName.fileName()); } else if (restart) { SelfRestarter::setRestartOnQuit(true); } @@ -1610,7 +1648,7 @@ void PackageManagerCorePrivate::writeOfflineBaseBinary() } } -void PackageManagerCorePrivate::writeMaintenanceToolAlias() +void PackageManagerCorePrivate::writeMaintenanceToolAlias(const QString &maintenanceToolName) { #ifdef Q_OS_MACOS const QString aliasPath = maintenanceToolAliasPath(); @@ -1618,7 +1656,7 @@ void PackageManagerCorePrivate::writeMaintenanceToolAlias() return; QString maintenanceToolBundle = QString::fromLatin1("%1/%2") - .arg(targetDir(), m_data.settings().maintenanceToolName()); + .arg(targetDir(), maintenanceToolName); if (!maintenanceToolBundle.endsWith(QLatin1String(".app"))) maintenanceToolBundle += QLatin1String(".app"); @@ -1627,6 +1665,8 @@ void PackageManagerCorePrivate::writeMaintenanceToolAlias() targetDir.mkpath(targetDir.absolutePath()); mkalias(maintenanceToolBundle, aliasPath); +#else + Q_UNUSED(maintenanceToolName) #endif } diff --git a/src/libs/installer/packagemanagercore_p.h b/src/libs/installer/packagemanagercore_p.h index d7c0b30d3..a42033ff6 100644 --- a/src/libs/installer/packagemanagercore_p.h +++ b/src/libs/installer/packagemanagercore_p.h @@ -69,7 +69,7 @@ class PackageManagerCorePrivate : public QObject public: explicit PackageManagerCorePrivate(PackageManagerCore *core); explicit PackageManagerCorePrivate(PackageManagerCore *core, qint64 magicInstallerMaker, - const QList<OperationBlob> &performedOperations); + const QList<OperationBlob> &performedOperations, const QString &datFileName); ~PackageManagerCorePrivate(); static bool isProcessRunning(const QString &name, const QList<ProcessInfo> &processes); @@ -92,6 +92,7 @@ public: QString maintenanceToolAliasPath() const; QString installerBinaryPath() const; QString offlineBinaryName() const; + QString datFileName(); void writeMaintenanceConfigFiles(); void readMaintenanceConfigFiles(const QString &targetDir); @@ -99,7 +100,7 @@ public: void writeMaintenanceTool(OperationList performedOperations); void writeOfflineBaseBinary(); - void writeMaintenanceToolAlias(); + void writeMaintenanceToolAlias(const QString &maintenanceToolName); QString componentsXmlPath() const; QString configurationFileName() const; @@ -254,6 +255,7 @@ private: void writeMaintenanceToolBinary(QFile *const input, qint64 size, bool writeBinaryLayout); void writeMaintenanceToolBinaryData(QFileDevice *output, QFile *const input, const OperationList &performed, const BinaryLayout &layout); + void writeMaintenanceToolAppBundle(OperationList &performedOperations); void runUndoOperations(const OperationList &undoOperations, double undoOperationProgressSize, bool adminRightsGained, bool deleteOperation); @@ -324,6 +326,8 @@ private: // < name (component replacing others), components to replace> QHash<QString, QStringList > m_componentReplaces; + + QString m_datFileName; }; } // namespace QInstaller diff --git a/src/sdk/sdkapp.h b/src/sdk/sdkapp.h index 2d6189960..d5d126f26 100644 --- a/src/sdk/sdkapp.h +++ b/src/sdk/sdkapp.h @@ -109,14 +109,9 @@ public: } binary.close(); #endif - QString fileName = datFile(binaryFile()); - quint64 cookie = QInstaller::BinaryContent::MagicCookieDat; - if (fileName.isEmpty()) { - fileName = binaryFile(); - cookie = QInstaller::BinaryContent::MagicCookie; - } - - binary.setFileName(fileName); + QString datFileName = datFile(binaryFile()); + quint64 cookie = datFileName.isEmpty() ? QInstaller::BinaryContent::MagicCookie : QInstaller::BinaryContent::MagicCookieDat; + binary.setFileName(!datFileName.isEmpty() ? datFileName : binaryFile()); QInstaller::openForRead(&binary); qint64 magicMarker; @@ -185,12 +180,12 @@ public: const QStringList arguments = m_parser.value(CommandLineOptions::scStartClientLong) .split(QLatin1Char(','), Qt::SkipEmptyParts); m_core = new QInstaller::PackageManagerCore( - magicMarker, oldOperations, + magicMarker, oldOperations, datFileName, arguments.value(0, QLatin1String(QInstaller::Protocol::DefaultSocket)), arguments.value(1, QLatin1String(QInstaller::Protocol::DefaultAuthorizationKey)), QInstaller::Protocol::Mode::Debug, userArgs, isCommandLineInterface); } else { - m_core = new QInstaller::PackageManagerCore(magicMarker, oldOperations, + m_core = new QInstaller::PackageManagerCore(magicMarker, oldOperations, datFileName, QUuid::createUuid().toString(), QUuid::createUuid().toString(), QInstaller::Protocol::Mode::Production, userArgs, isCommandLineInterface); } @@ -469,13 +464,39 @@ public: if (magicMarker == QInstaller::BinaryContent::MagicUninstallerMarker) { QFileInfo fi(binaryFile); QString bundlePath; + QString datFileName; if (QInstaller::isInBundle(fi.absoluteFilePath(), &bundlePath)) fi.setFile(bundlePath); #ifdef Q_OS_MACOS - return fi.absoluteDir().filePath(fi.baseName() + QLatin1String(".dat")); + datFileName = fi.absoluteDir().filePath(fi.baseName() + QLatin1String(".dat")); #else - return fi.absoluteDir().filePath(qApp->applicationName() + QLatin1String(".dat")); + datFileName = fi.absoluteDir().filePath(qApp->applicationName() + QLatin1String(".dat")); #endif + // When running maintenance tool, datFile name should be the same as the application name. + // In case we have updated maintenance tool in previous maintenance tool run, the datFile + // name may not match if the maintenance tool name has changed. In that case try to + // look for the dat file from the root folder of the install. + if (!QFileInfo::exists(datFileName)) { + QFileInfo fi(datFileName); + QDirIterator it(fi.absolutePath(), + QStringList() << QLatin1String("*.dat"), + QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot); + while (it.hasNext()) { + try { + QFile f(it.next()); + f.open(QIODevice::ReadOnly); + if (f.fileName().endsWith(QLatin1String("installer.dat"))) + continue; + QInstaller::BinaryContent::findMagicCookie(&f, magicMarker); + datFileName = f.fileName(); + break; + } catch (const QInstaller::Error &error) { + Q_UNUSED(error) + continue; + } + } + } + return datFileName; } return QString(); } diff --git a/tests/auto/installer/createshortcutoperation/tst_createshortcutoperation.cpp b/tests/auto/installer/createshortcutoperation/tst_createshortcutoperation.cpp index baf954a44..67856a74c 100644 --- a/tests/auto/installer/createshortcutoperation/tst_createshortcutoperation.cpp +++ b/tests/auto/installer/createshortcutoperation/tst_createshortcutoperation.cpp @@ -53,7 +53,7 @@ private: { QInstaller::init(); QScopedPointer<PackageManagerCore> core(new PackageManagerCore(BinaryContent::MagicInstallerMarker, - QList<OperationBlob> (), QString(), Protocol::DefaultAuthorizationKey, + QList<OperationBlob> (), QString(), QString(), Protocol::DefaultAuthorizationKey, Protocol::Mode::Production, QHash<QString, QString>(), true)); core->setAllowedRunningProcesses(QStringList() << QCoreApplication::applicationFilePath()); diff --git a/tests/auto/installer/messageboxhandler/tst_messageboxhandler.cpp b/tests/auto/installer/messageboxhandler/tst_messageboxhandler.cpp index 464e1eef7..d7ef0df90 100644 --- a/tests/auto/installer/messageboxhandler/tst_messageboxhandler.cpp +++ b/tests/auto/installer/messageboxhandler/tst_messageboxhandler.cpp @@ -93,7 +93,7 @@ private slots: QInstaller::init(); //This will eat debug output core = new PackageManagerCore(BinaryContent::MagicInstallerMarker, QList<OperationBlob> (), - QString(), Protocol::DefaultAuthorizationKey, Protocol::Mode::Production, + QString(), QString(), Protocol::DefaultAuthorizationKey, Protocol::Mode::Production, QHash<QString, QString>(), true); core->setAllowedRunningProcesses(QStringList() << QCoreApplication::applicationFilePath()); core->disableWriteMaintenanceTool(); diff --git a/tests/auto/installer/packagemanagercore/tst_packagemanagercore.cpp b/tests/auto/installer/packagemanagercore/tst_packagemanagercore.cpp index aa74d8dbe..44dc65960 100644 --- a/tests/auto/installer/packagemanagercore/tst_packagemanagercore.cpp +++ b/tests/auto/installer/packagemanagercore/tst_packagemanagercore.cpp @@ -339,7 +339,7 @@ private slots: QHash<QString, QString> userValues; PackageManagerCore *core = new PackageManagerCore(BinaryContent::MagicInstallerMarker, QList<OperationBlob> (), - QString(), Protocol::DefaultAuthorizationKey, Protocol::Mode::Production, + QString(), QString(), Protocol::DefaultAuthorizationKey, Protocol::Mode::Production, userValues, true); QCOMPARE(core->value("AllUsers"), QLatin1String("")); QCOMPARE(core->value("ProductName"), QLatin1String("Unit Test Application")); @@ -361,7 +361,7 @@ private slots: userValues.insert("RootDir", "Overwritten RootDir"); PackageManagerCore *core = new PackageManagerCore(BinaryContent::MagicInstallerMarker, QList<OperationBlob> (), - QString(), Protocol::DefaultAuthorizationKey, Protocol::Mode::Production, + QString(), QString(), Protocol::DefaultAuthorizationKey, Protocol::Mode::Production, userValues, true); QCOMPARE(core->value("AllUsers"), QLatin1String("true")); QCOMPARE(core->value("ProductName"), QLatin1String("Overwritten ProductName")); diff --git a/tests/auto/installer/treename/tst_treename.cpp b/tests/auto/installer/treename/tst_treename.cpp index 3d42126eb..2c8be6ddd 100644 --- a/tests/auto/installer/treename/tst_treename.cpp +++ b/tests/auto/installer/treename/tst_treename.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2022 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -394,7 +394,7 @@ void tst_TreeName::remotePackageConflictsLocal() QHash<QString, QString> params; params.insert(scTargetDir, qApp->applicationDirPath()); PackageManagerCore core(BinaryContent::MagicPackageManagerMarker, QList<OperationBlob>(), - QString(), Protocol::DefaultAuthorizationKey, Protocol::Mode::Production, params); + QString(), QString(), Protocol::DefaultAuthorizationKey, Protocol::Mode::Production, params); core.settings().setAllowUnstableComponents(true); core.settings().setDefaultRepositories(QSet<Repository>() diff --git a/tools/binarycreator/main.cpp b/tools/binarycreator/main.cpp index d74644670..1840d4c2a 100644 --- a/tools/binarycreator/main.cpp +++ b/tools/binarycreator/main.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2022 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -72,6 +72,9 @@ static void printUsage() << std::endl; std::cout << " 'update.rcc' in the current path." << std::endl; #ifdef Q_OS_MACOS + std::cout << " --mt|--create-maintenancetool " << std::endl; + std::cout << " Creates maintenance tool app bundle. Target option is omitted, bundle name "<<std::endl; + std::cout << " can be configured in the config.xml using element <MaintenanceToolName>." << std::endl; std::cout << " -s|--sign identity Sign generated app bundle using the given code " << std::endl; std::cout << " signing identity" << std::endl; #endif @@ -99,6 +102,11 @@ static void printUsage() std::cout << "Example update.rcc:" << std::endl; std::cout << " " << appName << " -c installer-config" << sep << "config.xml -p packages-directory " "-rcc" << std::endl; +#ifdef Q_OS_MACOS + std::cout << "Example (maintenance tool bundle):" << std::endl; + std::cout << " " << appName << " -c installer-config" << sep << "config.xml" << " --mt" << std::endl; + std::cout << std::endl; +#endif } static int printErrorAndUsageAndExit(const QString &err) @@ -206,6 +214,9 @@ int main(int argc, char **argv) } parsedArgs.compression = static_cast<AbstractArchive::CompressionLevel>(value); #ifdef Q_OS_MACOS + } else if (*it == QLatin1String("--mt") || *it == QLatin1String("--create-maintenancetool")) { + parsedArgs.createMaintenanceTool = true; + } else if (*it == QLatin1String("-s") || *it == QLatin1String("--sign")) { ++it; if (it == args.end() || it->startsWith(QLatin1String("-"))) |