summaryrefslogtreecommitdiffstats
path: root/src/tools/windeployqt/utils.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/windeployqt/utils.h')
-rw-r--r--src/tools/windeployqt/utils.h364
1 files changed, 364 insertions, 0 deletions
diff --git a/src/tools/windeployqt/utils.h b/src/tools/windeployqt/utils.h
new file mode 100644
index 0000000000..2c7cd79bf0
--- /dev/null
+++ b/src/tools/windeployqt/utils.h
@@ -0,0 +1,364 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <QStringList>
+#include <QMap>
+#include <QtCore/QFile>
+#include <QtCore/QDir>
+#include <QtCore/QDateTime>
+#include <QtCore/QJsonArray>
+#include <QtCore/QJsonObject>
+#include <QtCore/QJsonDocument>
+
+#include <iostream>
+
+QT_BEGIN_NAMESPACE
+
+enum PlatformFlag {
+ // OS
+ WindowsBased = 0x00001,
+ // CPU
+ IntelBased = 0x00010,
+ ArmBased = 0x00020,
+ // Compiler
+ Msvc = 0x00100,
+ MinGW = 0x00200,
+ ClangMsvc = 0x00400,
+ ClangMinGW = 0x00800,
+ // Platforms
+ WindowsDesktopMsvc = WindowsBased + IntelBased + Msvc,
+ WindowsDesktopMinGW = WindowsBased + IntelBased + MinGW,
+ WindowsDesktopClangMsvc = WindowsBased + IntelBased + ClangMsvc,
+ WindowsDesktopClangMinGW = WindowsBased + IntelBased + ClangMinGW,
+ UnknownPlatform
+};
+
+Q_DECLARE_FLAGS(Platform, PlatformFlag)
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(Platform)
+
+inline bool platformHasDebugSuffix(Platform p) // Uses 'd' debug suffix
+{
+ return p.testFlag(Msvc) || p.testFlag(ClangMsvc);
+}
+
+enum ListOption {
+ ListNone = 0,
+ ListSource,
+ ListTarget,
+ ListRelative,
+ ListMapping
+};
+
+inline std::wostream &operator<<(std::wostream &str, const QString &s)
+{
+#ifdef Q_OS_WIN
+ str << reinterpret_cast<const wchar_t *>(s.utf16());
+#else
+ str << s.toStdWString();
+#endif
+ return str;
+}
+
+// Container class for JSON output
+class JsonOutput
+{
+ using SourceTargetMapping = std::pair<QString, QString>;
+ using SourceTargetMappings = QList<SourceTargetMapping>;
+
+public:
+ void addFile(const QString &source, const QString &target)
+ {
+ m_files.append(SourceTargetMapping(source, target));
+ }
+
+ void removeTargetDirectory(const QString &targetDirectory)
+ {
+ for (int i = m_files.size() - 1; i >= 0; --i) {
+ if (m_files.at(i).second == targetDirectory)
+ m_files.removeAt(i);
+ }
+ }
+
+ QByteArray toJson() const
+ {
+ QJsonObject document;
+ QJsonArray files;
+ for (const SourceTargetMapping &mapping : m_files) {
+ QJsonObject object;
+ object.insert(QStringLiteral("source"), QDir::toNativeSeparators(mapping.first));
+ object.insert(QStringLiteral("target"), QDir::toNativeSeparators(mapping.second));
+ files.append(object);
+ }
+ document.insert(QStringLiteral("files"), files);
+ return QJsonDocument(document).toJson();
+ }
+ QByteArray toList(ListOption option, const QDir &base) const
+ {
+ QByteArray list;
+ for (const SourceTargetMapping &mapping : m_files) {
+ const QString source = QDir::toNativeSeparators(mapping.first);
+ const QString fileName = QFileInfo(mapping.first).fileName();
+ const QString target = QDir::toNativeSeparators(mapping.second) + QDir::separator() + fileName;
+ switch (option) {
+ case ListNone:
+ break;
+ case ListSource:
+ list += source.toUtf8() + '\n';
+ break;
+ case ListTarget:
+ list += target.toUtf8() + '\n';
+ break;
+ case ListRelative:
+ list += QDir::toNativeSeparators(base.relativeFilePath(target)).toUtf8() + '\n';
+ break;
+ case ListMapping:
+ list += '"' + source.toUtf8() + "\" \"" + QDir::toNativeSeparators(base.relativeFilePath(target)).toUtf8() + "\"\n";
+ break;
+ }
+ }
+ return list;
+ }
+private:
+ SourceTargetMappings m_files;
+};
+
+#ifdef Q_OS_WIN
+QString normalizeFileName(const QString &name);
+QString winErrorMessage(unsigned long error);
+QString findSdkTool(const QString &tool);
+#else // !Q_OS_WIN
+inline QString normalizeFileName(const QString &name) { return name; }
+#endif // !Q_OS_WIN
+
+static const char windowsSharedLibrarySuffix[] = ".dll";
+
+inline QString sharedLibrarySuffix() { return QLatin1StringView(windowsSharedLibrarySuffix); }
+bool isBuildDirectory(Platform platform, const QString &dirName);
+
+bool createSymbolicLink(const QFileInfo &source, const QString &target, QString *errorMessage);
+bool createDirectory(const QString &directory, QString *errorMessage, bool dryRun);
+QString findInPath(const QString &file);
+
+extern const char *qmakeInfixKey; // Fake key containing the libinfix
+
+QMap<QString, QString> queryQtPaths(const QString &qmakeBinary, QString *errorMessage);
+
+enum DebugMatchMode {
+ MatchDebug,
+ MatchRelease,
+ MatchDebugOrRelease
+};
+
+QStringList findSharedLibraries(const QDir &directory, Platform platform,
+ DebugMatchMode debugMatchMode,
+ const QString &prefix = QString());
+
+bool updateFile(const QString &sourceFileName, const QStringList &nameFilters,
+ const QString &targetDirectory, unsigned flags, JsonOutput *json, QString *errorMessage);
+bool runProcess(const QString &binary, const QStringList &args,
+ const QString &workingDirectory = QString(),
+ unsigned long *exitCode = 0, QByteArray *stdOut = 0, QByteArray *stdErr = 0,
+ QString *errorMessage = 0);
+
+bool readPeExecutable(const QString &peExecutableFileName, QString *errorMessage,
+ QStringList *dependentLibraries = 0, unsigned *wordSize = 0,
+ bool *isDebug = 0, bool isMinGW = false, unsigned short *machineArch = nullptr);
+
+#ifdef Q_OS_WIN
+# if !defined(IMAGE_FILE_MACHINE_ARM64)
+# define IMAGE_FILE_MACHINE_ARM64 0xAA64
+# endif
+QString getArchString (unsigned short machineArch);
+#endif // Q_OS_WIN
+
+// Return dependent modules of executable files.
+
+inline QStringList findDependentLibraries(const QString &executableFileName, QString *errorMessage)
+{
+ QStringList result;
+ readPeExecutable(executableFileName, errorMessage, &result);
+ return result;
+}
+
+QString findD3dCompiler(Platform platform, const QString &qtBinDir, unsigned wordSize);
+QStringList findDxc(Platform platform, const QString &qtBinDir, unsigned wordSize);
+
+bool patchQtCore(const QString &path, QString *errorMessage);
+
+extern int optVerboseLevel;
+
+// Recursively update a file or directory, matching DirectoryFileEntryFunction against the QDir
+// to obtain the files.
+enum UpdateFileFlag {
+ ForceUpdateFile = 0x1,
+ SkipUpdateFile = 0x2,
+ RemoveEmptyQmlDirectories = 0x4,
+ SkipQmlDesignerSpecificsDirectories = 0x8
+};
+
+template <class DirectoryFileEntryFunction>
+bool updateFile(const QString &sourceFileName,
+ DirectoryFileEntryFunction directoryFileEntryFunction,
+ const QString &targetDirectory,
+ unsigned flags,
+ JsonOutput *json,
+ QString *errorMessage)
+{
+ const QFileInfo sourceFileInfo(sourceFileName);
+ const QString targetFileName = targetDirectory + u'/' + sourceFileInfo.fileName();
+ if (optVerboseLevel > 1)
+ std::wcout << "Checking " << sourceFileName << ", " << targetFileName << '\n';
+
+ if (!sourceFileInfo.exists()) {
+ *errorMessage = QString::fromLatin1("%1 does not exist.").arg(QDir::toNativeSeparators(sourceFileName));
+ return false;
+ }
+
+ const QFileInfo targetFileInfo(targetFileName);
+
+ if (sourceFileInfo.isSymLink()) {
+ const QString sourcePath = sourceFileInfo.symLinkTarget();
+ const QString relativeSource = QDir(sourceFileInfo.absolutePath()).relativeFilePath(sourcePath);
+ if (relativeSource.contains(u'/')) {
+ *errorMessage = QString::fromLatin1("Symbolic links across directories are not supported (%1).")
+ .arg(QDir::toNativeSeparators(sourceFileName));
+ return false;
+ }
+
+ // Update the linked-to file
+ if (!updateFile(sourcePath, directoryFileEntryFunction, targetDirectory, flags, json, errorMessage))
+ return false;
+
+ if (targetFileInfo.exists()) {
+ if (!targetFileInfo.isSymLink()) {
+ *errorMessage = QString::fromLatin1("%1 already exists and is not a symbolic link.")
+ .arg(QDir::toNativeSeparators(targetFileName));
+ return false;
+ } // Not a symlink
+ const QString relativeTarget = QDir(targetFileInfo.absolutePath()).relativeFilePath(targetFileInfo.symLinkTarget());
+ if (relativeSource == relativeTarget) // Exists and points to same entry: happy.
+ return true;
+ QFile existingTargetFile(targetFileName);
+ if (!(flags & SkipUpdateFile) && !existingTargetFile.remove()) {
+ *errorMessage = QString::fromLatin1("Cannot remove existing symbolic link %1: %2")
+ .arg(QDir::toNativeSeparators(targetFileName), existingTargetFile.errorString());
+ return false;
+ }
+ } // target symbolic link exists
+ return createSymbolicLink(QFileInfo(targetDirectory + u'/' + relativeSource), sourceFileInfo.fileName(), errorMessage);
+ } // Source is symbolic link
+
+ if (sourceFileInfo.isDir()) {
+ if ((flags & SkipQmlDesignerSpecificsDirectories) && sourceFileInfo.fileName() == QLatin1StringView("designer")) {
+ if (optVerboseLevel)
+ std::wcout << "Skipping " << QDir::toNativeSeparators(sourceFileName) << ".\n";
+ return true;
+ }
+ bool created = false;
+ if (targetFileInfo.exists()) {
+ if (!targetFileInfo.isDir()) {
+ *errorMessage = QString::fromLatin1("%1 already exists and is not a directory.")
+ .arg(QDir::toNativeSeparators(targetFileName));
+ return false;
+ } // Not a directory.
+ } else { // exists.
+ QDir d(targetDirectory);
+ if (optVerboseLevel)
+ std::wcout << "Creating " << targetFileName << ".\n";
+ if (!(flags & SkipUpdateFile)) {
+ created = d.mkdir(sourceFileInfo.fileName());
+ if (!created) {
+ *errorMessage = QString::fromLatin1("Cannot create directory %1 under %2.")
+ .arg(sourceFileInfo.fileName(), QDir::toNativeSeparators(targetDirectory));
+ return false;
+ }
+ }
+ }
+ // Recurse into directory
+ QDir dir(sourceFileName);
+
+ const QStringList allEntries = directoryFileEntryFunction(dir) + dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
+ for (const QString &entry : allEntries)
+ if (!updateFile(sourceFileName + u'/' + entry, directoryFileEntryFunction, targetFileName, flags, json, errorMessage))
+ return false;
+ // Remove empty directories, for example QML import folders for which the filter did not match.
+ if (created && (flags & RemoveEmptyQmlDirectories)) {
+ QDir d(targetFileName);
+ const QStringList entries = d.entryList(QStringList(), QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
+ if (entries.isEmpty() || (entries.size() == 1 && entries.first() == QLatin1StringView("qmldir"))) {
+ if (!d.removeRecursively()) {
+ *errorMessage = QString::fromLatin1("Cannot remove empty directory %1.")
+ .arg(QDir::toNativeSeparators(targetFileName));
+ return false;
+ }
+ if (json)
+ json->removeTargetDirectory(targetFileName);
+ }
+ }
+ return true;
+ } // Source is directory.
+
+ if (targetFileInfo.exists()) {
+ if (!(flags & ForceUpdateFile)
+ && targetFileInfo.lastModified() >= sourceFileInfo.lastModified()) {
+ if (optVerboseLevel)
+ std::wcout << sourceFileInfo.fileName() << " is up to date.\n";
+ if (json)
+ json->addFile(sourceFileName, targetDirectory);
+ return true;
+ }
+ QFile targetFile(targetFileName);
+ if (!(flags & SkipUpdateFile) && !targetFile.remove()) {
+ *errorMessage = QString::fromLatin1("Cannot remove existing file %1: %2")
+ .arg(QDir::toNativeSeparators(targetFileName), targetFile.errorString());
+ return false;
+ }
+ } // target exists
+ QFile file(sourceFileName);
+ if (optVerboseLevel)
+ std::wcout << "Updating " << sourceFileInfo.fileName() << ".\n";
+ if (!(flags & SkipUpdateFile)) {
+ if (!file.copy(targetFileName)) {
+ *errorMessage = QString::fromLatin1("Cannot copy %1 to %2: %3")
+ .arg(QDir::toNativeSeparators(sourceFileName),
+ QDir::toNativeSeparators(targetFileName),
+ file.errorString());
+ return false;
+ }
+ if (!(file.permissions() & QFile::WriteUser)) { // QTBUG-40152, clear inherited read-only attribute
+ QFile targetFile(targetFileName);
+ if (!targetFile.setPermissions(targetFile.permissions() | QFile::WriteUser)) {
+ *errorMessage = QString::fromLatin1("Cannot set write permission on %1: %2")
+ .arg(QDir::toNativeSeparators(targetFileName), file.errorString());
+ return false;
+ }
+ } // Check permissions
+ } // !SkipUpdateFile
+ if (json)
+ json->addFile(sourceFileName, targetDirectory);
+ return true;
+}
+
+// Base class to filter files by name filters functions to be passed to updateFile().
+class NameFilterFileEntryFunction {
+public:
+ explicit NameFilterFileEntryFunction(const QStringList &nameFilters) : m_nameFilters(nameFilters) {}
+ QStringList operator()(const QDir &dir) const { return dir.entryList(m_nameFilters, QDir::Files); }
+
+private:
+ const QStringList m_nameFilters;
+};
+
+// Convenience for all files.
+inline bool updateFile(const QString &sourceFileName, const QString &targetDirectory, unsigned flags, JsonOutput *json, QString *errorMessage)
+{
+ return updateFile(sourceFileName, NameFilterFileEntryFunction(QStringList()), targetDirectory, flags, json, errorMessage);
+}
+
+QT_END_NAMESPACE
+
+#endif // UTILS_H