diff options
Diffstat (limited to 'src/tools/windeployqt/utils.cpp')
-rw-r--r-- | src/tools/windeployqt/utils.cpp | 308 |
1 files changed, 162 insertions, 146 deletions
diff --git a/src/tools/windeployqt/utils.cpp b/src/tools/windeployqt/utils.cpp index bc52de924e..5141119254 100644 --- a/src/tools/windeployqt/utils.cpp +++ b/src/tools/windeployqt/utils.cpp @@ -1,33 +1,7 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// 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> @@ -40,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 @@ -55,12 +30,14 @@ QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + int optVerboseLevel = 1; bool isBuildDirectory(Platform platform, const QString &dirName) { return (platform.testFlag(Msvc) || platform.testFlag(ClangMsvc)) - && (dirName == QLatin1String("debug") || dirName == QLatin1String("release")); + && (dirName == "debug"_L1 || dirName == "release"_L1); } // Create a symbolic link by changing to the source directory to make sure the @@ -84,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()) @@ -96,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; } @@ -112,10 +91,10 @@ QStringList findSharedLibraries(const QDir &directory, Platform platform, { QString nameFilter = prefix; if (nameFilter.isEmpty()) - nameFilter += QLatin1Char('*'); + nameFilter += u'*'; if (debugMatchMode == MatchDebug && platformHasDebugSuffix(platform)) - nameFilter += QLatin1Char('d'); - nameFilter += sharedLibrarySuffix(platform); + nameFilter += u'd'; + nameFilter += sharedLibrarySuffix(); QStringList result; QString errorMessage; const QFileInfoList &dlls = directory.entryInfoList(QStringList(nameFilter), QDir::Files); @@ -139,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) @@ -172,10 +135,10 @@ QString normalizeFileName(const QString &name) // Find a tool binary in the Windows SDK 8 QString findSdkTool(const QString &tool) { - QStringList paths = QString::fromLocal8Bit(qgetenv("PATH")).split(QLatin1Char(';')); + QStringList paths = QString::fromLocal8Bit(qgetenv("PATH")).split(u';'); const QByteArray sdkDir = qgetenv("WindowsSdkDir"); if (!sdkDir.isEmpty()) - paths.prepend(QDir::cleanPath(QString::fromLocal8Bit(sdkDir)) + QLatin1String("/Tools/x64")); + paths.prepend(QDir::cleanPath(QString::fromLocal8Bit(sdkDir)) + "/Tools/x64"_L1); return QStandardPaths::findExecutable(tool, paths); } @@ -212,14 +175,14 @@ static inline void readTemporaryProcessFile(HANDLE handle, QByteArray *result) static inline void appendToCommandLine(const QString &argument, QString *commandLine) { - const bool needsQuote = argument.contains(QLatin1Char(' ')); + const bool needsQuote = argument.contains(u' '); if (!commandLine->isEmpty()) - commandLine->append(QLatin1Char(' ')); + commandLine->append(u' '); if (needsQuote) - commandLine->append(QLatin1Char('"')); + commandLine->append(u'"'); commandLine->append(argument); if (needsQuote) - commandLine->append(QLatin1Char('"')); + commandLine->append(u'"'); } // runProcess: Run a command line process (replacement for QProcess which @@ -244,7 +207,7 @@ bool runProcess(const QString &binary, const QStringList &args, PROCESS_INFORMATION pi; ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); - const QChar backSlash = QLatin1Char('\\'); + const QChar backSlash = u'\\'; QString nativeWorkingDir = QDir::toNativeSeparators(workingDirectory.isEmpty() ? QDir::currentPath() : workingDirectory); if (!nativeWorkingDir.endsWith(backSlash)) nativeWorkingDir += backSlash; @@ -286,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; } @@ -317,8 +282,8 @@ static inline char *encodeFileName(const QString &f) static inline char *tempFilePattern() { QString path = QDir::tempPath(); - if (!path.endsWith(QLatin1Char('/'))) - path += QLatin1Char('/'); + if (!path.endsWith(u'/')) + path += u'/'; path += QStringLiteral("tmpXXXXXX"); return encodeFileName(path); } @@ -396,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; @@ -458,24 +423,28 @@ 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(QLatin1Char('\r')); + const QString output = QString::fromLocal8Bit(stdOut).trimmed().remove(u'\r'); QMap<QString, QString> result; - const int size = output.size(); - for (int pos = 0; pos < size; ) { - const int colonPos = output.indexOf(QLatin1Char(':'), pos); + const qsizetype size = output.size(); + for (qsizetype pos = 0; pos < size; ) { + const qsizetype colonPos = output.indexOf(u':', pos); if (colonPos < 0) break; - int endPos = output.indexOf(QLatin1Char('\n'), colonPos + 1); + qsizetype endPos = output.indexOf(u'\n', colonPos + 1); if (endPos < 0) endPos = size; const QString key = output.mid(pos, colonPos - pos); @@ -494,14 +463,14 @@ QMap<QString, QString> queryQtPaths(const QString &qtpathsBinary, QString *error if (pos >= 0) { const QString infix = QString::fromUtf8(line.right(line.size() - pos - 1).trimmed()); if (!infix.isEmpty()) - result.insert(QLatin1String(qmakeInfixKey), infix); + result.insert(QLatin1StringView(qmakeInfixKey), infix); } break; } } } else { std::wcerr << "Warning: Unable to read " << QDir::toNativeSeparators(qconfigPriFile.fileName()) - << ": " << qconfigPriFile.errorString()<< '\n'; + << colonSpace << qconfigPriFile.errorString()<< '\n'; } return result; } @@ -511,7 +480,7 @@ bool updateFile(const QString &sourceFileName, const QStringList &nameFilters, const QString &targetDirectory, unsigned flags, JsonOutput *json, QString *errorMessage) { const QFileInfo sourceFileInfo(sourceFileName); - const QString targetFileName = targetDirectory + QLatin1Char('/') + sourceFileInfo.fileName(); + const QString targetFileName = targetDirectory + u'/' + sourceFileInfo.fileName(); if (optVerboseLevel > 1) std::wcout << "Checking " << sourceFileName << ", " << targetFileName<< '\n'; @@ -587,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) @@ -736,48 +674,71 @@ enum MsvcDebugRuntimeResult { MsvcDebugRuntime, MsvcReleaseRuntime, NoMsvcRuntim static inline MsvcDebugRuntimeResult checkMsvcDebugRuntime(const QStringList &dependentLibraries) { for (const QString &lib : dependentLibraries) { - int pos = 0; - if (lib.startsWith(QLatin1String("MSVCR"), Qt::CaseInsensitive) - || lib.startsWith(QLatin1String("MSVCP"), Qt::CaseInsensitive) - || lib.startsWith(QLatin1String("VCRUNTIME"), Qt::CaseInsensitive)) { - int lastDotPos = lib.lastIndexOf(QLatin1Char('.')); + qsizetype pos = 0; + if (lib.startsWith("MSVCR"_L1, Qt::CaseInsensitive) + || lib.startsWith("MSVCP"_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(QLatin1String("_app"), Qt::CaseInsensitive)) - pos -= 4; - - if (pos) { - return lib.at(pos).toLower() == QLatin1Char('d') - ? MsvcDebugRuntime : MsvcReleaseRuntime; + 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; } return NoMsvcRuntime; } 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 @@ -803,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; } @@ -828,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) @@ -846,7 +810,7 @@ bool readPeExecutable(const QString &peExecutableFileName, QString *errorMessage if (dependentLibrariesIn) { std::wcout << ", dependent libraries: "; if (optVerboseLevel > 2) - std::wcout << dependentLibrariesIn->join(QLatin1Char(' ')); + std::wcout << dependentLibrariesIn->join(u' '); else std::wcout << dependentLibrariesIn->size(); } @@ -871,7 +835,7 @@ bool readPeExecutable(const QString &peExecutableFileName, QString *errorMessage QString findD3dCompiler(Platform platform, const QString &qtBinDir, unsigned wordSize) { const QString prefix = QStringLiteral("D3Dcompiler_"); - const QString suffix = QLatin1String(windowsSharedLibrarySuffix); + const QString suffix = QLatin1StringView(windowsSharedLibrarySuffix); // Get the DLL from Kit 8.0 onwards const QString kitDir = QString::fromLocal8Bit(qgetenv("WindowsSdkDir")); if (!kitDir.isEmpty()) { @@ -883,7 +847,7 @@ QString findD3dCompiler(Platform platform, const QString &qtBinDir, unsigned wor } QDir redistDir(redistDirPath); if (redistDir.exists()) { - const QFileInfoList files = redistDir.entryInfoList(QStringList(prefix + QLatin1Char('*') + suffix), QDir::Files); + const QFileInfoList files = redistDir.entryInfoList(QStringList(prefix + u'*' + suffix), QDir::Files); if (!files.isEmpty()) return files.front().absoluteFilePath(); } @@ -893,8 +857,8 @@ 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)) { - const QFileInfo fi(qtBinDir + QLatin1Char('/') + candidate); + for (const QString &candidate : std::as_const(candidateVersions)) { + const QFileInfo fi(qtBinDir + u'/' + candidate); if (fi.isFile()) return fi.absoluteFilePath(); } @@ -902,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) @@ -914,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, @@ -928,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=." |