diff options
Diffstat (limited to 'src/tools/windeployqt/utils.cpp')
-rw-r--r-- | src/tools/windeployqt/utils.cpp | 217 |
1 files changed, 129 insertions, 88 deletions
diff --git a/src/tools/windeployqt/utils.cpp b/src/tools/windeployqt/utils.cpp index 41b2d2f063..5141119254 100644 --- a/src/tools/windeployqt/utils.cpp +++ b/src/tools/windeployqt/utils.cpp @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "utils.h" -#include "elfreader.h" #include <QtCore/QString> #include <QtCore/QDebug> @@ -15,6 +14,7 @@ #include <QtCore/QStandardPaths> #if defined(Q_OS_WIN) # include <QtCore/qt_windows.h> +# include <QtCore/private/qsystemerror_p.h> # include <shlwapi.h> # include <delayimp.h> #else // Q_OS_WIN @@ -61,7 +61,7 @@ bool createSymbolicLink(const QFileInfo &source, const QString &target, QString return true; } -bool createDirectory(const QString &directory, QString *errorMessage) +bool createDirectory(const QString &directory, QString *errorMessage, bool dryRun) { const QFileInfo fi(directory); if (fi.isDir()) @@ -73,11 +73,13 @@ bool createDirectory(const QString &directory, QString *errorMessage) } if (optVerboseLevel) std::wcout << "Creating " << QDir::toNativeSeparators(directory) << "...\n"; - QDir dir; - if (!dir.mkpath(directory)) { - *errorMessage = QString::fromLatin1("Could not create directory %1."). - arg(QDir::toNativeSeparators(directory)); - return false; + if (!dryRun) { + QDir dir; + if (!dir.mkpath(directory)) { + *errorMessage = QString::fromLatin1("Could not create directory %1.") + .arg(QDir::toNativeSeparators(directory)); + return false; + } } return true; } @@ -92,7 +94,7 @@ QStringList findSharedLibraries(const QDir &directory, Platform platform, nameFilter += u'*'; if (debugMatchMode == MatchDebug && platformHasDebugSuffix(platform)) nameFilter += u'd'; - nameFilter += sharedLibrarySuffix(platform); + nameFilter += sharedLibrarySuffix(); QStringList result; QString errorMessage; const QFileInfoList &dlls = directory.entryInfoList(QStringList(nameFilter), QDir::Files); @@ -116,22 +118,6 @@ QStringList findSharedLibraries(const QDir &directory, Platform platform, } #ifdef Q_OS_WIN -QString winErrorMessage(unsigned long error) -{ - QString rc = QString::fromLatin1("#%1: ").arg(error); - char16_t *lpMsgBuf; - - const DWORD len = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, error, 0, reinterpret_cast<LPTSTR>(&lpMsgBuf), 0, NULL); - if (len) { - rc = QString::fromUtf16(lpMsgBuf, int(len)); - LocalFree(lpMsgBuf); - } else { - rc += QString::fromLatin1("<unknown error>"); - } - return rc; -} // Case-Normalize file name via GetShortPathNameW()/GetLongPathNameW() QString normalizeFileName(const QString &name) @@ -263,8 +249,10 @@ bool runProcess(const QString &binary, const QStringList &args, CloseHandle(si.hStdOutput); if (stdErr) CloseHandle(si.hStdError); - if (errorMessage) - *errorMessage = QStringLiteral("CreateProcessW failed: ") + winErrorMessage(GetLastError()); + if (errorMessage) { + *errorMessage = QStringLiteral("CreateProcessW failed: ") + + QSystemError::windowsString(); + } return false; } @@ -373,7 +361,7 @@ bool runProcess(const QString &binary, const QStringList &args, char **argv = new char *[args.size() + 2]; // Create argv. char **ap = argv; *ap++ = encodeFileName(binary); - for (const QString &a : qAsConst(args)) + for (const QString &a : std::as_const(args)) *ap++ = encodeFileName(a); *ap = 0; @@ -435,14 +423,18 @@ const char *qmakeInfixKey = "QT_INFIX"; QMap<QString, QString> queryQtPaths(const QString &qtpathsBinary, QString *errorMessage) { const QString binary = !qtpathsBinary.isEmpty() ? qtpathsBinary : QStringLiteral("qtpaths"); + const QString colonSpace = QStringLiteral(": "); QByteArray stdOut; QByteArray stdErr; unsigned long exitCode = 0; - if (!runProcess(binary, QStringList(QStringLiteral("-query")), QString(), &exitCode, &stdOut, &stdErr, errorMessage)) + if (!runProcess(binary, QStringList(QStringLiteral("-query")), QString(), &exitCode, &stdOut, + &stdErr, errorMessage)) { + *errorMessage = QStringLiteral("Error running binary ") + binary + colonSpace + *errorMessage; return QMap<QString, QString>(); + } if (exitCode) { *errorMessage = binary + QStringLiteral(" returns ") + QString::number(exitCode) - + QStringLiteral(": ") + QString::fromLocal8Bit(stdErr); + + colonSpace + QString::fromLocal8Bit(stdErr); return QMap<QString, QString>(); } const QString output = QString::fromLocal8Bit(stdOut).trimmed().remove(u'\r'); @@ -478,7 +470,7 @@ QMap<QString, QString> queryQtPaths(const QString &qtpathsBinary, QString *error } } else { std::wcerr << "Warning: Unable to read " << QDir::toNativeSeparators(qconfigPriFile.fileName()) - << ": " << qconfigPriFile.errorString()<< '\n'; + << colonSpace << qconfigPriFile.errorString()<< '\n'; } return result; } @@ -564,37 +556,6 @@ bool updateFile(const QString &sourceFileName, const QStringList &nameFilters, return true; } -bool readElfExecutable(const QString &elfExecutableFileName, QString *errorMessage, - QStringList *dependentLibraries, unsigned *wordSize, - bool *isDebug) -{ - ElfReader elfReader(elfExecutableFileName); - const ElfData data = elfReader.readHeaders(); - if (data.sectionHeaders.isEmpty()) { - *errorMessage = QStringLiteral("Unable to read ELF binary \"") - + QDir::toNativeSeparators(elfExecutableFileName) + QStringLiteral("\": ") - + elfReader.errorString(); - return false; - } - if (wordSize) - *wordSize = data.elfclass == Elf_ELFCLASS64 ? 64 : 32; - if (dependentLibraries) { - dependentLibraries->clear(); - const QList<QByteArray> libs = elfReader.dependencies(); - if (libs.isEmpty()) { - *errorMessage = QStringLiteral("Unable to read dependenices of ELF binary \"") - + QDir::toNativeSeparators(elfExecutableFileName) + QStringLiteral("\": ") - + elfReader.errorString(); - return false; - } - for (const QByteArray &l : libs) - dependentLibraries->push_back(QString::fromLocal8Bit(l)); - } - if (isDebug) - *isDebug = data.symbolsType != UnknownSymbols && data.symbolsType != NoSymbols; - return true; -} - #ifdef Q_OS_WIN static inline QString stringFromRvaPtr(const void *rvaPtr) @@ -716,13 +677,23 @@ static inline MsvcDebugRuntimeResult checkMsvcDebugRuntime(const QStringList &de qsizetype pos = 0; if (lib.startsWith("MSVCR"_L1, Qt::CaseInsensitive) || lib.startsWith("MSVCP"_L1, Qt::CaseInsensitive) - || lib.startsWith("VCRUNTIME"_L1, Qt::CaseInsensitive)) { + || lib.startsWith("VCRUNTIME"_L1, Qt::CaseInsensitive) + || lib.startsWith("VCCORLIB"_L1, Qt::CaseInsensitive) + || lib.startsWith("CONCRT"_L1, Qt::CaseInsensitive) + || lib.startsWith("UCRTBASE"_L1, Qt::CaseInsensitive)) { qsizetype lastDotPos = lib.lastIndexOf(u'.'); pos = -1 == lastDotPos ? 0 : lastDotPos - 1; } - if (pos > 0 && lib.contains("_app"_L1, Qt::CaseInsensitive)) - pos -= 4; + if (pos > 0) { + const auto removeExtraSuffix = [&lib, &pos](const QString &suffix) -> void { + if (lib.contains(suffix, Qt::CaseInsensitive)) + pos -= suffix.size(); + }; + removeExtraSuffix("_app"_L1); + removeExtraSuffix("_atomic_wait"_L1); + removeExtraSuffix("_codecvt_ids"_L1); + } if (pos) return lib.at(pos).toLower() == u'd' ? MsvcDebugRuntime : MsvcReleaseRuntime; @@ -731,28 +702,43 @@ static inline MsvcDebugRuntimeResult checkMsvcDebugRuntime(const QStringList &de } template <class ImageNtHeader> +inline QStringList determineDependentLibs(const ImageNtHeader *nth, const void *fileMemory, + QString *errorMessage) +{ + return readImportSections(nth, fileMemory, errorMessage); +} + +template <class ImageNtHeader> +inline bool determineDebug(const ImageNtHeader *nth, const void *fileMemory, + QStringList *dependentLibrariesIn, QString *errorMessage) +{ + if (nth->FileHeader.Characteristics & IMAGE_FILE_DEBUG_STRIPPED) + return false; + + const QStringList dependentLibraries = dependentLibrariesIn != nullptr ? + *dependentLibrariesIn : + determineDependentLibs(nth, fileMemory, errorMessage); + + const bool hasDebugEntry = nth->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size; + // When an MSVC debug entry is present, check whether the debug runtime + // is actually used to detect -release / -force-debug-info builds. + const MsvcDebugRuntimeResult msvcrt = checkMsvcDebugRuntime(dependentLibraries); + if (msvcrt == NoMsvcRuntime) + return hasDebugEntry; + else + return hasDebugEntry && msvcrt == MsvcDebugRuntime; +} + +template <class ImageNtHeader> inline void determineDebugAndDependentLibs(const ImageNtHeader *nth, const void *fileMemory, - bool isMinGW, QStringList *dependentLibrariesIn, bool *isDebugIn, QString *errorMessage) { - const bool hasDebugEntry = nth->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size; - QStringList dependentLibraries; - if (dependentLibrariesIn || (isDebugIn != nullptr && hasDebugEntry && !isMinGW)) - dependentLibraries = readImportSections(nth, fileMemory, errorMessage); - if (dependentLibrariesIn) - *dependentLibrariesIn = dependentLibraries; - if (isDebugIn != nullptr) { - if (isMinGW) { - // Use logic that's used e.g. in objdump / pfd library - *isDebugIn = !(nth->FileHeader.Characteristics & IMAGE_FILE_DEBUG_STRIPPED); - } else { - // When an MSVC debug entry is present, check whether the debug runtime - // is actually used to detect -release / -force-debug-info builds. - *isDebugIn = hasDebugEntry && checkMsvcDebugRuntime(dependentLibraries) != MsvcReleaseRuntime; - } - } + *dependentLibrariesIn = determineDependentLibs(nth, fileMemory, errorMessage); + + if (isDebugIn) + *isDebugIn = determineDebug(nth, fileMemory, dependentLibrariesIn, errorMessage); } // Read a PE executable and determine dependent libraries, word size @@ -778,19 +764,22 @@ bool readPeExecutable(const QString &peExecutableFileName, QString *errorMessage hFile = CreateFile(reinterpret_cast<const WCHAR*>(peExecutableFileName.utf16()), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE || hFile == NULL) { - *errorMessage = QString::fromLatin1("Cannot open '%1': %2").arg(peExecutableFileName, winErrorMessage(GetLastError())); + *errorMessage = QString::fromLatin1("Cannot open '%1': %2") + .arg(peExecutableFileName, QSystemError::windowsString()); break; } hFileMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); if (hFileMap == NULL) { - *errorMessage = QString::fromLatin1("Cannot create file mapping of '%1': %2").arg(peExecutableFileName, winErrorMessage(GetLastError())); + *errorMessage = QString::fromLatin1("Cannot create file mapping of '%1': %2") + .arg(peExecutableFileName, QSystemError::windowsString()); break; } fileMemory = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0); if (!fileMemory) { - *errorMessage = QString::fromLatin1("Cannot map '%1': %2").arg(peExecutableFileName, winErrorMessage(GetLastError())); + *errorMessage = QString::fromLatin1("Cannot map '%1': %2") + .arg(peExecutableFileName, QSystemError::windowsString()); break; } @@ -803,10 +792,10 @@ bool readPeExecutable(const QString &peExecutableFileName, QString *errorMessage *wordSizeIn = wordSize; if (wordSize == 32) { determineDebugAndDependentLibs(reinterpret_cast<const IMAGE_NT_HEADERS32 *>(ntHeaders), - fileMemory, isMinGW, dependentLibrariesIn, isDebugIn, errorMessage); + fileMemory, dependentLibrariesIn, isDebugIn, errorMessage); } else { determineDebugAndDependentLibs(reinterpret_cast<const IMAGE_NT_HEADERS64 *>(ntHeaders), - fileMemory, isMinGW, dependentLibrariesIn, isDebugIn, errorMessage); + fileMemory, dependentLibrariesIn, isDebugIn, errorMessage); } if (machineArchIn) @@ -868,7 +857,7 @@ QString findD3dCompiler(Platform platform, const QString &qtBinDir, unsigned wor candidateVersions.append(prefix + QString::number(i) + suffix); // Check the bin directory of the Qt SDK (in case it is shadowed by the // Windows system directory in PATH). - for (const QString &candidate : qAsConst(candidateVersions)) { + for (const QString &candidate : std::as_const(candidateVersions)) { const QFileInfo fi(qtBinDir + u'/' + candidate); if (fi.isFile()) return fi.absoluteFilePath(); @@ -877,7 +866,7 @@ QString findD3dCompiler(Platform platform, const QString &qtBinDir, unsigned wor if (platform.testFlag(IntelBased)) { QString errorMessage; unsigned detectedWordSize; - for (const QString &candidate : qAsConst(candidateVersions)) { + for (const QString &candidate : std::as_const(candidateVersions)) { const QString dll = findInPath(candidate); if (!dll.isEmpty() && readPeExecutable(dll, &errorMessage, 0, &detectedWordSize, 0) @@ -889,6 +878,53 @@ QString findD3dCompiler(Platform platform, const QString &qtBinDir, unsigned wor return QString(); } +QStringList findDxc(Platform platform, const QString &qtBinDir, unsigned wordSize) +{ + QStringList results; + const QString kitDir = QString::fromLocal8Bit(qgetenv("WindowsSdkDir")); + const QString suffix = QLatin1StringView(windowsSharedLibrarySuffix); + for (QString prefix : { QStringLiteral("dxcompiler"), QStringLiteral("dxil") }) { + QString name = prefix + suffix; + if (!kitDir.isEmpty()) { + QString redistDirPath = QDir::cleanPath(kitDir) + QStringLiteral("/Redist/D3D/"); + if (platform.testFlag(ArmBased)) { + redistDirPath += wordSize == 32 ? QStringLiteral("arm") : QStringLiteral("arm64"); + } else { + redistDirPath += wordSize == 32 ? QStringLiteral("x86") : QStringLiteral("x64"); + } + QDir redistDir(redistDirPath); + if (redistDir.exists()) { + const QFileInfoList files = redistDir.entryInfoList(QStringList(prefix + u'*' + suffix), QDir::Files); + if (!files.isEmpty()) { + results.append(files.front().absoluteFilePath()); + continue; + } + } + } + // Check the bin directory of the Qt SDK (in case it is shadowed by the + // Windows system directory in PATH). + const QFileInfo fi(qtBinDir + u'/' + name); + if (fi.isFile()) { + results.append(fi.absoluteFilePath()); + continue; + } + // Try to find it in the PATH (e.g. the Vulkan SDK ships these, even if Windows itself doesn't). + if (platform.testFlag(IntelBased)) { + QString errorMessage; + unsigned detectedWordSize; + const QString dll = findInPath(name); + if (!dll.isEmpty() + && readPeExecutable(dll, &errorMessage, 0, &detectedWordSize, 0) + && detectedWordSize == wordSize) + { + results.append(dll); + continue; + } + } + } + return results; +} + #else // Q_OS_WIN bool readPeExecutable(const QString &, QString *errorMessage, @@ -903,6 +939,11 @@ QString findD3dCompiler(Platform, const QString &, unsigned) return QString(); } +QStringList findDxc(Platform, const QString &, unsigned) +{ + return QStringList(); +} + #endif // !Q_OS_WIN // Search for "qt_prfxpath=xxxx" in \a path, and replace it with "qt_prfxpath=." |