diff options
Diffstat (limited to 'src/libs/ifwtools')
-rw-r--r-- | src/libs/ifwtools/binarycreator.cpp | 173 | ||||
-rw-r--r-- | src/libs/ifwtools/binarycreator.h | 6 | ||||
-rw-r--r-- | src/libs/ifwtools/rcc/rcc.cpp | 26 | ||||
-rw-r--r-- | src/libs/ifwtools/repositorygen.cpp | 197 | ||||
-rw-r--r-- | src/libs/ifwtools/repositorygen.h | 3 |
5 files changed, 260 insertions, 145 deletions
diff --git a/src/libs/ifwtools/binarycreator.cpp b/src/libs/ifwtools/binarycreator.cpp index cf04355de..341052650 100644 --- a/src/libs/ifwtools/binarycreator.cpp +++ b/src/libs/ifwtools/binarycreator.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. @@ -43,7 +43,7 @@ #include <QDirIterator> #include <QDomDocument> #include <QProcess> -#include <QRegExp> +#include <QRegularExpression> #include <QSettings> #include <QTemporaryFile> #include <QTemporaryDir> @@ -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)) { @@ -262,7 +262,7 @@ static int assemble(Input input, const QInstaller::Settings &settings, const QSt QFile pkgInfo(fi.filePath() + QLatin1String("/Contents/PkgInfo")); pkgInfo.open(QIODevice::WriteOnly); QTextStream pkgInfoStream(&pkgInfo); - pkgInfoStream << QLatin1String("APPL????") << endl; + pkgInfoStream << QLatin1String("APPL????") << Qt::endl; } QString iconFile; @@ -282,44 +282,44 @@ static int assemble(Input input, const QInstaller::Settings &settings, const QSt QFile infoPList(fi.filePath() + QLatin1String("/Contents/Info.plist")); infoPList.open(QIODevice::WriteOnly); QTextStream plistStream(&infoPList); - plistStream << QLatin1String("<?xml version=\"1.0\" encoding=\"UTF-8\"?>") << endl; + plistStream << QLatin1String("<?xml version=\"1.0\" encoding=\"UTF-8\"?>") << Qt::endl; plistStream << QLatin1String("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" " - "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">") << endl; - plistStream << QLatin1String("<plist version=\"1.0\">") << endl; - plistStream << QLatin1String("<dict>") << endl; - plistStream << QLatin1String("\t<key>CFBundleIconFile</key>") << endl; + "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">") << Qt::endl; + plistStream << QLatin1String("<plist version=\"1.0\">") << Qt::endl; + plistStream << QLatin1String("<dict>") << Qt::endl; + plistStream << QLatin1String("\t<key>CFBundleIconFile</key>") << Qt::endl; plistStream << QLatin1String("\t<string>") << iconTargetFile << QLatin1String("</string>") - << endl; - plistStream << QLatin1String("\t<key>CFBundlePackageType</key>") << endl; - plistStream << QLatin1String("\t<string>APPL</string>") << endl; + << Qt::endl; + plistStream << QLatin1String("\t<key>CFBundlePackageType</key>") << Qt::endl; + plistStream << QLatin1String("\t<string>APPL</string>") << Qt::endl; #define QUOTE_(x) #x #define QUOTE(x) QUOTE_(x) - plistStream << QLatin1String("\t<key>CFBundleShortVersionString</key>") << endl; + plistStream << QLatin1String("\t<key>CFBundleShortVersionString</key>") << Qt::endl; plistStream << QLatin1String("\t<string>") << QLatin1String(QUOTE(IFW_VERSION_STR)) << ("</string>") - << endl; - plistStream << QLatin1String("\t<key>CFBundleVersion</key>") << endl; + << Qt::endl; + plistStream << QLatin1String("\t<key>CFBundleVersion</key>") << Qt::endl; plistStream << QLatin1String("\t<string>") << QLatin1String(QUOTE(IFW_VERSION_STR)) << ("</string>") - << endl; + << Qt::endl; #undef QUOTE #undef QUOTE_ - plistStream << QLatin1String("\t<key>CFBundleSignature</key>") << endl; - plistStream << QLatin1String("\t<string>\?\?\?\?</string>") << endl; - plistStream << QLatin1String("\t<key>CFBundleExecutable</key>") << endl; + plistStream << QLatin1String("\t<key>CFBundleSignature</key>") << Qt::endl; + plistStream << QLatin1String("\t<string>\?\?\?\?</string>") << Qt::endl; + plistStream << QLatin1String("\t<key>CFBundleExecutable</key>") << Qt::endl; plistStream << QLatin1String("\t<string>") << fi.completeBaseName() << QLatin1String("</string>") - << endl; - plistStream << QLatin1String("\t<key>CFBundleIdentifier</key>") << endl; - plistStream << QLatin1String("\t<string>com.yourcompany.installerbase</string>") << endl; - plistStream << QLatin1String("\t<key>NOTE</key>") << endl; + << Qt::endl; + plistStream << QLatin1String("\t<key>CFBundleIdentifier</key>") << Qt::endl; + plistStream << QLatin1String("\t<string>com.yourcompany.installerbase</string>") << Qt::endl; + plistStream << QLatin1String("\t<key>NOTE</key>") << Qt::endl; plistStream << QLatin1String("\t<string>This file was generated by Qt Installer Framework.</string>") - << endl; - plistStream << QLatin1String("\t<key>NSPrincipalClass</key>") << endl; - plistStream << QLatin1String("\t<string>NSApplication</string>") << endl; + << Qt::endl; + plistStream << QLatin1String("\t<key>NSPrincipalClass</key>") << Qt::endl; + plistStream << QLatin1String("\t<string>NSApplication</string>") << Qt::endl; if (!minimumSystemVersion.isEmpty()) { - plistStream << QLatin1String("\t<key>LSMinimumSystemVersion</key>") << endl; - plistStream << QLatin1String("\t<string>") << minimumSystemVersion << QLatin1String("</string>") << endl; + plistStream << QLatin1String("\t<key>LSMinimumSystemVersion</key>") << Qt::endl; + plistStream << QLatin1String("\t<string>") << minimumSystemVersion << QLatin1String("</string>") << Qt::endl; } - plistStream << QLatin1String("</dict>") << endl; - plistStream << QLatin1String("</plist>") << endl; + plistStream << QLatin1String("</dict>") << Qt::endl; + plistStream << QLatin1String("</plist>") << Qt::endl; input.outputPath = QString::fromLatin1("%1/Contents/MacOS/%2").arg(input.outputPath) .arg(fi.completeBaseName()); @@ -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; } @@ -609,27 +611,38 @@ void QInstallerTools::copyConfigData(const QString &configFile, const QString &t qDebug().noquote() << QString::fromLatin1("Read dom element: <%1>%2</%1>.").arg(tagName, elementText); if (tagName == QLatin1String("ProductImages")) { - const QDomNodeList childNodes = domElement.childNodes(); - for (int index = 0; index < childNodes.count(); ++index) { - const QDomElement childElement = childNodes.at(index).toElement(); - const QString childName = childElement.tagName(); - if (childName != QLatin1String("Image")) + const QDomNodeList productImageNode = domElement.childNodes(); + for (int j = 0; j < productImageNode.count(); ++j) { + QDomElement productImagesElement = productImageNode.at(j).toElement(); + if (productImagesElement.isNull()) continue; + const QString childName = productImagesElement.tagName(); + if (childName != QLatin1String("ProductImage")) + continue; + const QDomNodeList imageNode = productImagesElement.childNodes(); + for (int k = 0; k < imageNode.count(); ++k) { + QDomElement productImageElement = imageNode.at(k).toElement(); + if (productImageElement.isNull()) + continue; + const QString imageChildName = productImageElement.tagName(); + if (imageChildName != QLatin1String("Image")) + continue; + const QString targetFile = targetDir + QLatin1Char('/') + productImageElement.text(); + const QFileInfo childFileInfo = QFileInfo(sourceConfigFilePath, productImageElement.text()); + QInstallerTools::copyWithException(childFileInfo.absoluteFilePath(), targetFile, imageChildName); + copyHighDPIImage(childFileInfo, imageChildName, targetFile); + } - const QString targetFile = targetDir + QLatin1Char('/') + childElement.text(); - const QFileInfo childFileInfo = QFileInfo(sourceConfigFilePath, childElement.text()); - QInstallerTools::copyWithException(childFileInfo.absoluteFilePath(), targetFile, childName); - copyHighDPIImage(childFileInfo, childName, targetFile); } continue; } - QString newName = domElement.text().replace(QRegExp(QLatin1String("\\\\|/|\\.|:")), - QLatin1String("_")); + static const QRegularExpression regex(QLatin1String("\\\\|/|\\.|:")); + QString newName = domElement.text().replace(regex, QLatin1String("_")); QString targetFile; QFileInfo elementFileInfo; - if (tagName == QLatin1String("Icon") || tagName == QLatin1String("InstallerApplicationIcon")) { + if (tagName == QLatin1String("InstallerApplicationIcon")) { #if defined(Q_OS_MACOS) const QString suffix = QLatin1String(".icns"); #elif defined(Q_OS_WIN) @@ -691,13 +704,13 @@ int QInstallerTools::createBinary(BinaryCreatorArgs args, QString &argumentError // Begin check arguments foreach (const QString &packageDir, args.packagesDirectories) { - if (!QFileInfo(packageDir).exists()) { + if (!QFileInfo::exists(packageDir)) { argumentError = QString::fromLatin1("Error: Package directory not found at the specified location."); return EXIT_FAILURE; } } foreach (const QString &repositoryDir, args.repositoryDirectories) { - if (!QFileInfo(repositoryDir).exists()) { + if (!QFileInfo::exists(repositoryDir)) { argumentError = QString::fromLatin1("Error: Only local filesystem repositories now supported."); return EXIT_FAILURE; } @@ -708,12 +721,12 @@ int QInstallerTools::createBinary(BinaryCreatorArgs args, QString &argumentError "contain any components apart from the root component."); return EXIT_FAILURE; } - if (!QFileInfo(args.templateBinary).exists()) { + if (!QFileInfo::exists(args.templateBinary)) { #ifdef Q_OS_WIN if (!args.templateBinary.endsWith(suffix)) args.templateBinary = args.templateBinary + suffix; // Try again with added executable suffix - if (!QFileInfo(args.templateBinary).exists()) { + if (!QFileInfo::exists(args.templateBinary)) { argumentError = QString::fromLatin1("Error: Template base binary not found at the specified location."); return EXIT_FAILURE; } @@ -743,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; } @@ -751,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()) { + 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; } @@ -827,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"); @@ -841,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; @@ -867,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..387195742 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 @@ -76,7 +77,7 @@ public: explicit BundleBackup(const QString &bundle = QString()) : bundle(bundle) { - if (!bundle.isEmpty() && QFileInfo(bundle).exists()) { + if (!bundle.isEmpty() && QFileInfo::exists(bundle)) { backup = QInstaller::generateTemporaryFileName(bundle); QFile::rename(bundle, backup); } @@ -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/ifwtools/rcc/rcc.cpp b/src/libs/ifwtools/rcc/rcc.cpp index 12f399937..caef84433 100644 --- a/src/libs/ifwtools/rcc/rcc.cpp +++ b/src/libs/ifwtools/rcc/rcc.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2017 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. @@ -36,7 +36,7 @@ #include <QtCore/QFile> #include <QtCore/QIODevice> #include <QtCore/QLocale> -#include <QtCore/QRegExp> +#include <QtCore/QRegularExpression> #include <QtCore/QStack> #include <QXmlStreamReader> @@ -108,7 +108,7 @@ public: QLocale::Country m_country; QFileInfo m_fileInfo; RCCFileInfo *m_parent; - QHash<QString, RCCFileInfo*> m_children; + QMultiHash<QString, RCCFileInfo*> m_children; int m_compressLevel; int m_compressThreshold; @@ -583,10 +583,10 @@ bool RCCResourceLibrary::addFile(const QString &alias, const RCCFileInfo &file) if (!parent->m_children.contains(node)) { RCCFileInfo *s = new RCCFileInfo(node, QFileInfo(), QLocale::C, QLocale::AnyCountry, RCCFileInfo::Directory); s->m_parent = parent; - parent->m_children.insert(node, s); + parent->m_children.replace(node, s); parent = s; } else { - parent = parent->m_children[node]; + parent = parent->m_children.value(node); } } @@ -598,7 +598,7 @@ bool RCCResourceLibrary::addFile(const QString &alias, const RCCFileInfo &file) qWarning("%s: Warning: potential duplicate alias detected: '%s'", qPrintable(fileName), qPrintable(filename)); } - parent->m_children.insertMulti(filename, s); + parent->m_children.insert(filename, s); return true; } @@ -664,7 +664,7 @@ QStringList RCCResourceLibrary::dataFiles() const pending.push(m_root); while (!pending.isEmpty()) { RCCFileInfo *file = pending.pop(); - for (QHash<QString, RCCFileInfo*>::iterator it = file->m_children.begin(); + for (QMultiHash<QString, RCCFileInfo*>::iterator it = file->m_children.begin(); it != file->m_children.end(); ++it) { RCCFileInfo *child = it.value(); if (child->m_flags & RCCFileInfo::Directory) @@ -678,7 +678,7 @@ QStringList RCCResourceLibrary::dataFiles() const // Determine map of resource identifier (':/newPrefix/images/p1.png') to file via recursion static void resourceDataFileMapRecursion(const RCCFileInfo *m_root, const QString &path, RCCResourceLibrary::ResourceDataFileMap &m) { - typedef QHash<QString, RCCFileInfo*>::const_iterator ChildConstIterator; + typedef QMultiHash<QString, RCCFileInfo*>::const_iterator ChildConstIterator; const QChar slash = QLatin1Char('/'); const ChildConstIterator cend = m_root->m_children.constEnd(); for (ChildConstIterator it = m_root->m_children.constBegin(); it != cend; ++it) { @@ -815,7 +815,7 @@ bool RCCResourceLibrary::writeDataBlobs() QString errorMessage; while (!pending.isEmpty()) { RCCFileInfo *file = pending.pop(); - for (QHash<QString, RCCFileInfo*>::iterator it = file->m_children.begin(); + for (QMultiHash<QString, RCCFileInfo*>::iterator it = file->m_children.begin(); it != file->m_children.end(); ++it) { RCCFileInfo *child = it.value(); if (child->m_flags & RCCFileInfo::Directory) @@ -851,7 +851,7 @@ bool RCCResourceLibrary::writeDataNames() qint64 offset = 0; while (!pending.isEmpty()) { RCCFileInfo *file = pending.pop(); - for (QHash<QString, RCCFileInfo*>::iterator it = file->m_children.begin(); + for (QMultiHash<QString, RCCFileInfo*>::iterator it = file->m_children.begin(); it != file->m_children.end(); ++it) { RCCFileInfo *child = it.value(); if (child->m_flags & RCCFileInfo::Directory) @@ -894,7 +894,7 @@ bool RCCResourceLibrary::writeDataStructure() //sort by hash value for binary lookup QList<RCCFileInfo*> m_children = file->m_children.values(); - qSort(m_children.begin(), m_children.end(), qt_rcc_compare_hash); + std::sort(m_children.begin(), m_children.end(), qt_rcc_compare_hash); //write out the actual data now for (int i = 0; i < m_children.size(); ++i) { @@ -913,7 +913,7 @@ bool RCCResourceLibrary::writeDataStructure() //sort by hash value for binary lookup QList<RCCFileInfo*> m_children = file->m_children.values(); - qSort(m_children.begin(), m_children.end(), qt_rcc_compare_hash); + std::sort(m_children.begin(), m_children.end(), qt_rcc_compare_hash); //write out the actual data now for (int i = 0; i < m_children.size(); ++i) { @@ -958,7 +958,7 @@ bool RCCResourceLibrary::writeInitializer() QString initName = m_initName; if (!initName.isEmpty()) { initName.prepend(QLatin1Char('_')); - initName.replace(QRegExp(QLatin1String("[^a-zA-Z0-9_]")), QLatin1String("_")); + initName.replace(QRegularExpression(QLatin1String("[^a-zA-Z0-9_]")), QLatin1String("_")); } //init diff --git a/src/libs/ifwtools/repositorygen.cpp b/src/libs/ifwtools/repositorygen.cpp index a045c5248..9232a02d4 100644 --- a/src/libs/ifwtools/repositorygen.cpp +++ b/src/libs/ifwtools/repositorygen.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -34,6 +34,7 @@ #include "errors.h" #include "globals.h" #include "archivefactory.h" +#include "metadata.h" #include "settings.h" #include "qinstallerglobal.h" #include "utils.h" @@ -42,7 +43,7 @@ #include "updater.h" #include <QtCore/QDirIterator> -#include <QtCore/QRegExp> +#include <QtCore/QRegularExpression> #include <QtXml/QDomDocument> #include <QTemporaryDir> @@ -128,6 +129,75 @@ static QStringList copyFilesFromNode(const QString &parentNode, const QString &c return copiedFiles; } +/* + Returns \c true if the \a file is an archive or an SHA1 checksum + file for an archive, /c false otherwise. +*/ +static bool isArchiveOrChecksum(const QString &file) +{ + if (file.endsWith(QLatin1String(".sha1"))) + return true; + + for (auto &supportedSuffix : ArchiveFactory::supportedTypes()) { + if (file.endsWith(supportedSuffix)) + return true; + } + return false; +} + +/* + Fills the package \a info with the name of the metadata archive when applicable. Returns + \c true if the component has metadata compressed in an archive or uncompressed to cache, or + if the metadata archive is redundant. Returns \c false if the component should have metadata + but none was found. +*/ +static bool findMetaFile(const QString &repositoryDir, const QDomElement &packageUpdate, PackageInfo &info) +{ + // Note: the order here is important, when updating from an existing + // repository we shouldn't drop the empty metadata archives. + + // 1. First, try with normal repository structure + QString metaFile = QString::fromLatin1("%1/%3%2").arg(info.directory, + QString::fromLatin1("meta.7z"), info.version); + + if (QFileInfo::exists(metaFile)) { + info.metaFile = metaFile; + return true; + } + + // 2. If that does not work, check for fetched temporary repository structure + metaFile = QString::fromLatin1("%1/%2-%3-%4").arg(repositoryDir, + info.name, info.version, QString::fromLatin1("meta.7z")); + + if (QFileInfo::exists(metaFile)) { + info.metaFile = metaFile; + return true; + } + + // 3. Try with the cached metadata directory structure + const QDir packageDir(info.directory); + const QStringList cachedMetaFiles = packageDir.entryList(QDir::Files); + for (auto &file : cachedMetaFiles) { + if (!isArchiveOrChecksum(file)) + return true; // Return for first non-archive file + } + + // 4. The meta archive may be redundant, skip in that case (cached item from a + // repository that has empty meta archive) + bool metaElementFound = false; + const QDomNodeList c1 = packageUpdate.childNodes(); + for (int i = 0; i < c1.count(); ++i) { + const QDomElement e1 = c1.at(i).toElement(); + for (const QString &meta : scMetaElements) { + if (e1.tagName() == meta) { + metaElementFound = true; + break; + } + } + } + return !metaElementFound; +} + void QInstallerTools::copyMetaData(const QString &_targetDir, const QString &metaDataDir, const PackageInfoVector &packages, const QString &appName, const QString &appVersion, const QStringList &uniteMetadatas) @@ -297,39 +367,8 @@ void QInstallerTools::copyMetaData(const QString &_targetDir, const QString &met root.appendChild(update); - // copy script file - const QString script = package.firstChildElement(QLatin1String("Script")).text(); - if (!script.isEmpty()) { - QFile scriptFile(QString::fromLatin1("%1/meta/%2").arg(info.directory, script)); - if (!scriptFile.open(QIODevice::ReadOnly | QIODevice::Text)) { - throw QInstaller::Error(QString::fromLatin1("Cannot open component script at \"%1\".") - .arg(QDir::toNativeSeparators(scriptFile.fileName()))); - } - - const QString scriptContent = QLatin1String("(function() {") - + QString::fromUtf8(scriptFile.readAll()) - + QLatin1String(";" - " if (typeof Component == \"undefined\")" - " throw \"Missing Component constructor. Please check your script.\";" - "})();"); - - // if the user isn't aware of the downloadable archives value we will add it automatically later - foundDownloadableArchives |= scriptContent.contains(QLatin1String("addDownloadableArchive")) - || scriptContent.contains(QLatin1String("removeDownloadableArchive")); - - static QInstaller::ScriptEngine testScriptEngine; - const QJSValue value = testScriptEngine.evaluate(scriptContent, scriptFile.fileName()); - if (value.isError()) { - throw QInstaller::Error(QString::fromLatin1("Exception while loading component " - "script at \"%1\": %2").arg(QDir::toNativeSeparators(scriptFile.fileName()), - value.toString().isEmpty() ? QString::fromLatin1("Unknown error.") : - value.toString() + QStringLiteral(" on line number: ") + - value.property(QStringLiteral("lineNumber")).toString())); - } - - const QString toLocation(QString::fromLatin1("%1/%2/%3").arg(targetDir, info.name, script)); - copyWithException(scriptFile.fileName(), toLocation, QInstaller::scScript); - } + // copy script files + copyScriptFiles(childNodes, info, foundDownloadableArchives, targetDir); // write DownloadableArchives tag if that is missed by the user if (!foundDownloadableArchives && !info.copiedFiles.isEmpty()) { @@ -338,7 +377,7 @@ void QInstallerTools::copyMetaData(const QString &_targetDir, const QString &met if (!filePath.endsWith(QLatin1String(".sha1"), Qt::CaseInsensitive)) { const QString fileName = QFileInfo(filePath).fileName(); // remove unnecessary version string from filename and add it to the list - realContentFiles.append(fileName.mid(info.version.count())); + realContentFiles.append(fileName.mid(info.version.size())); } } @@ -398,6 +437,19 @@ void QInstallerTools::copyMetaData(const QString &_targetDir, const QString &met throw Error(QString::fromLatin1("Could not extract archive \"%1\": %2").arg( QDir::toNativeSeparators(info.metaFile), metaFile->errorString())); } + } else { + // The metadata may have been already extracted, i.e. when reading from a + // local repository cache. + const QDir packageDir(info.directory); + const QStringList metaFiles = packageDir.entryList(QDir::Files); + for (auto &file : metaFiles) { + if (isArchiveOrChecksum(file)) + continue; // Skip data archives + + const QString source(QString::fromLatin1("%1/%2").arg(info.directory, file)); + const QString target(QString::fromLatin1("%1/%2/%3").arg(targetDir, info.name, file)); + copyWithException(source, target, QLatin1String("cached metadata")); + } } // Restore "PackageUpdate" node; @@ -517,15 +569,15 @@ PackageInfoVector QInstallerTools::createListOfPackages(const QStringList &packa info.version = packageElement.firstChildElement(QLatin1String("Version")).text(); // Version cannot start with comparison characters, be an empty string // or have whitespaces at the beginning or at the end - if (!QRegExp(QLatin1String("(?![<=>\\s]+)(.+)")).exactMatch(info.version) || - (info.version != info.version.trimmed())) { + static const QRegularExpression regex(QLatin1String("^(?![<=>\\s]+)(.+)$")); + if (!regex.match(info.version).hasMatch() || (info.version != info.version.trimmed())) { if (ignoreInvalidPackages) continue; throw QInstaller::Error(QString::fromLatin1("Component version for \"%1\" is invalid! <Version>%2</Version>") .arg(QDir::toNativeSeparators(fileInfo.absoluteFilePath()), info.version)); } info.dependencies = packageElement.firstChildElement(QLatin1String("Dependencies")).text() - .split(QInstaller::commaRegExp(), QString::SkipEmptyParts); + .split(QInstaller::commaRegExp(), Qt::SkipEmptyParts); info.directory = it->filePath(); if (packagesUpdatedWithSha.contains(info.name)) { info.createContentSha1Node = true; @@ -639,22 +691,9 @@ PackageInfoVector QInstallerTools::createListOfRepositoryPackages(const QStringL info.directory = QString::fromLatin1("%1/%2").arg(it->filePath(), info.name); if (!hasUnifiedMetaFile) { const QDomElement sha1 = el.firstChildElement(QInstaller::scSHA1); - if (!sha1.isNull()) { - // 1. First, try with normal repository structure - QString metaFile = QString::fromLatin1("%1/%3%2").arg(info.directory, - QString::fromLatin1("meta.7z"), info.version); - - if (!QFileInfo(metaFile).exists()) { - // 2. If that does not work, check for fetched temporary repository structure - metaFile = QString::fromLatin1("%1/%2-%3-%4").arg(it->filePath(), - info.name, info.version, QString::fromLatin1("meta.7z")); - - if (!QFileInfo(metaFile).exists()) { - throw QInstaller::Error(QString::fromLatin1("Could not find meta archive for component " - "%1 %2 in repository %3.").arg(info.name, info.version, it->filePath())); - } - } - info.metaFile = metaFile; + if (!sha1.isNull() && !findMetaFile(it->filePath(), el, info)) { + throw QInstaller::Error(QString::fromLatin1("Could not find metadata archive for component " + "%1 %2 in repository %3.").arg(info.name, info.version, it->filePath())); } } @@ -663,10 +702,10 @@ PackageInfoVector QInstallerTools::createListOfRepositoryPackages(const QStringL const QDomElement c2Element = c2.at(j).toElement(); if (c2Element.tagName() == QInstaller::scDependencies) info.dependencies = c2Element.text() - .split(QInstaller::commaRegExp(), QString::SkipEmptyParts); + .split(QInstaller::commaRegExp(), Qt::SkipEmptyParts); else if (c2Element.tagName() == QInstaller::scDownloadableArchives) { QStringList names = c2Element.text() - .split(QInstaller::commaRegExp(), QString::SkipEmptyParts); + .split(QInstaller::commaRegExp(), Qt::SkipEmptyParts); foreach (const QString &name, names) { info.copiedFiles.append(QString::fromLatin1("%1/%3%2").arg(info.directory, name, info.version)); @@ -907,6 +946,50 @@ void QInstallerTools::splitMetadata(const QStringList &entryList, const QString } } +void QInstallerTools::copyScriptFiles(const QDomNodeList &childNodes, const PackageInfo &info, bool &foundDownloadableArchives, const QString &targetDir) +{ + for (int i = 0; i < childNodes.count(); ++i) { + const QDomNode node = childNodes.at(i); + const QString key = node.nodeName(); + + if (key != QLatin1String("Script")) + continue; + const QString script = node.toElement().text(); + if (script.isEmpty()) + continue; + + QFile scriptFile(QString::fromLatin1("%1/meta/%2").arg(info.directory, script)); + if (!scriptFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + throw QInstaller::Error(QString::fromLatin1("Cannot open component script at \"%1\".") + .arg(QDir::toNativeSeparators(scriptFile.fileName()))); + } + + const QString scriptContent = QLatin1String("(function() {") + + QString::fromUtf8(scriptFile.readAll()) + + QLatin1String(";" + " if (typeof Component == \"undefined\")" + " throw \"Missing Component constructor. Please check your script.\";" + "})();"); + + // if the user isn't aware of the downloadable archives value we will add it automatically later + foundDownloadableArchives |= scriptContent.contains(QLatin1String("addDownloadableArchive")) + || scriptContent.contains(QLatin1String("removeDownloadableArchive")); + + static QInstaller::ScriptEngine testScriptEngine; + const QJSValue value = testScriptEngine.evaluate(scriptContent, scriptFile.fileName()); + if (value.isError()) { + throw QInstaller::Error(QString::fromLatin1("Exception while loading component " + "script at \"%1\": %2").arg(QDir::toNativeSeparators(scriptFile.fileName()), + value.toString().isEmpty() ? QString::fromLatin1("Unknown error.") : + value.toString() + QStringLiteral(" on line number: ") + + value.property(QStringLiteral("lineNumber")).toString())); + } + + const QString toLocation(QString::fromLatin1("%1/%2/%3").arg(targetDir, info.name, script)); + copyWithException(scriptFile.fileName(), toLocation, QInstaller::scScript); + } +} + void QInstallerTools::copyComponentData(const QStringList &packageDirs, const QString &repoDir, PackageInfoVector *const infos, const QString &archiveSuffix, Compression compression) { diff --git a/src/libs/ifwtools/repositorygen.h b/src/libs/ifwtools/repositorygen.h index 054c023f4..7ad3dd073 100644 --- a/src/libs/ifwtools/repositorygen.h +++ b/src/libs/ifwtools/repositorygen.h @@ -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. @@ -88,6 +88,7 @@ void IFWTOOLS_EXPORT compressMetaDirectories(const QString &repoDir, const QStri QStringList unifyMetadata(const QString &repoDir, const QString &existingRepoDir, QDomDocument doc); void splitMetadata(const QStringList &entryList, const QString &repoDir, QDomDocument doc, const QHash<QString, QString> &versionMapping); +void copyScriptFiles(const QDomNodeList &childNodes, const PackageInfo &info, bool &foundDownloadableArchives, const QString &targetDir); void IFWTOOLS_EXPORT copyMetaData(const QString &outDir, const QString &dataDir, const PackageInfoVector &packages, const QString &appName, const QString& appVersion, const QStringList &uniteMetadatas); |