summaryrefslogtreecommitdiffstats
path: root/src/shared/winutils/utils.cpp
diff options
context:
space:
mode:
authorJoerg Bornemann <joerg.bornemann@qt.io>2021-11-18 14:58:02 +0100
committerJoerg Bornemann <joerg.bornemann@qt.io>2021-11-26 18:10:44 +0100
commit4d8ca0d6c3c15ab0b92cbc4b8913a0f092fd8bdd (patch)
treeedaee42b4dfdd13d345fa6b64c6a19bb726c2769 /src/shared/winutils/utils.cpp
parentb6c369549425a660a51a3dc06c19f55f9a72076f (diff)
Remove macdeployqt and windeployqt
These tools have been moved to qtbase. Change-Id: Idaf799d44be399040f9058252675c0ee3eecc39b Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io> Reviewed-by: Mitch Curtis <mitch.curtis@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Iikka Eklund <iikka.eklund@qt.io> Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
Diffstat (limited to 'src/shared/winutils/utils.cpp')
-rw-r--r--src/shared/winutils/utils.cpp1006
1 files changed, 0 insertions, 1006 deletions
diff --git a/src/shared/winutils/utils.cpp b/src/shared/winutils/utils.cpp
deleted file mode 100644
index bc52de924..000000000
--- a/src/shared/winutils/utils.cpp
+++ /dev/null
@@ -1,1006 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include "utils.h"
-#include "elfreader.h"
-
-#include <QtCore/QString>
-#include <QtCore/QDebug>
-#include <QtCore/QDir>
-#include <QtCore/QFile>
-#include <QtCore/QFileInfo>
-#include <QtCore/QTemporaryFile>
-#include <QtCore/QScopedPointer>
-#include <QtCore/QScopedArrayPointer>
-#include <QtCore/QStandardPaths>
-#if defined(Q_OS_WIN)
-# include <QtCore/qt_windows.h>
-# include <shlwapi.h>
-# include <delayimp.h>
-#else // Q_OS_WIN
-# include <sys/wait.h>
-# include <sys/types.h>
-# include <sys/stat.h>
-# include <unistd.h>
-# include <stdlib.h>
-# include <string.h>
-# include <errno.h>
-# include <fcntl.h>
-#endif // !Q_OS_WIN
-
-QT_BEGIN_NAMESPACE
-
-int optVerboseLevel = 1;
-
-bool isBuildDirectory(Platform platform, const QString &dirName)
-{
- return (platform.testFlag(Msvc) || platform.testFlag(ClangMsvc))
- && (dirName == QLatin1String("debug") || dirName == QLatin1String("release"));
-}
-
-// Create a symbolic link by changing to the source directory to make sure the
-// link uses relative paths only (QFile::link() otherwise uses the absolute path).
-bool createSymbolicLink(const QFileInfo &source, const QString &target, QString *errorMessage)
-{
- const QString oldDirectory = QDir::currentPath();
- if (!QDir::setCurrent(source.absolutePath())) {
- *errorMessage = QStringLiteral("Unable to change to directory %1.").arg(QDir::toNativeSeparators(source.absolutePath()));
- return false;
- }
- QFile file(source.fileName());
- const bool success = file.link(target);
- QDir::setCurrent(oldDirectory);
- if (!success) {
- *errorMessage = QString::fromLatin1("Failed to create symbolic link %1 -> %2: %3")
- .arg(QDir::toNativeSeparators(source.absoluteFilePath()),
- QDir::toNativeSeparators(target), file.errorString());
- return false;
- }
- return true;
-}
-
-bool createDirectory(const QString &directory, QString *errorMessage)
-{
- const QFileInfo fi(directory);
- if (fi.isDir())
- return true;
- if (fi.exists()) {
- *errorMessage = QString::fromLatin1("%1 already exists and is not a directory.").
- arg(QDir::toNativeSeparators(directory));
- return false;
- }
- 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;
- }
- return true;
-}
-
-// Find shared libraries matching debug/Platform in a directory, return relative names.
-QStringList findSharedLibraries(const QDir &directory, Platform platform,
- DebugMatchMode debugMatchMode,
- const QString &prefix)
-{
- QString nameFilter = prefix;
- if (nameFilter.isEmpty())
- nameFilter += QLatin1Char('*');
- if (debugMatchMode == MatchDebug && platformHasDebugSuffix(platform))
- nameFilter += QLatin1Char('d');
- nameFilter += sharedLibrarySuffix(platform);
- QStringList result;
- QString errorMessage;
- const QFileInfoList &dlls = directory.entryInfoList(QStringList(nameFilter), QDir::Files);
- for (const QFileInfo &dllFi : dlls) {
- const QString dllPath = dllFi.absoluteFilePath();
- bool matches = true;
- if (debugMatchMode != MatchDebugOrRelease && (platform & WindowsBased)) {
- bool debugDll;
- if (readPeExecutable(dllPath, &errorMessage, 0, 0, &debugDll,
- (platform == WindowsDesktopMinGW))) {
- matches = debugDll == (debugMatchMode == MatchDebug);
- } else {
- std::wcerr << "Warning: Unable to read " << QDir::toNativeSeparators(dllPath)
- << ": " << errorMessage;
- }
- } // Windows
- if (matches)
- result += dllFi.fileName();
- } // for
- return result;
-}
-
-#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)
-{
- wchar_t shortBuffer[MAX_PATH];
- const QString nativeFileName = QDir::toNativeSeparators(name);
- if (!GetShortPathNameW(reinterpret_cast<LPCWSTR>(nativeFileName.utf16()), shortBuffer, MAX_PATH))
- return name;
- wchar_t result[MAX_PATH];
- if (!GetLongPathNameW(shortBuffer, result, MAX_PATH))
- return name;
- return QDir::fromNativeSeparators(QString::fromWCharArray(result));
-}
-
-// Find a tool binary in the Windows SDK 8
-QString findSdkTool(const QString &tool)
-{
- QStringList paths = QString::fromLocal8Bit(qgetenv("PATH")).split(QLatin1Char(';'));
- const QByteArray sdkDir = qgetenv("WindowsSdkDir");
- if (!sdkDir.isEmpty())
- paths.prepend(QDir::cleanPath(QString::fromLocal8Bit(sdkDir)) + QLatin1String("/Tools/x64"));
- return QStandardPaths::findExecutable(tool, paths);
-}
-
-// runProcess helper: Create a temporary file for stdout/stderr redirection.
-static HANDLE createInheritableTemporaryFile()
-{
- wchar_t path[MAX_PATH];
- if (!GetTempPath(MAX_PATH, path))
- return INVALID_HANDLE_VALUE;
- wchar_t name[MAX_PATH];
- if (!GetTempFileName(path, L"temp", 0, name)) // Creates file.
- return INVALID_HANDLE_VALUE;
- SECURITY_ATTRIBUTES securityAttributes;
- ZeroMemory(&securityAttributes, sizeof(securityAttributes));
- securityAttributes.nLength = sizeof(securityAttributes);
- securityAttributes.bInheritHandle = TRUE;
- return CreateFile(name, GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_READ | FILE_SHARE_WRITE, &securityAttributes,
- TRUNCATE_EXISTING,
- FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL);
-}
-
-// runProcess helper: Rewind and read out a temporary file for stdout/stderr.
-static inline void readTemporaryProcessFile(HANDLE handle, QByteArray *result)
-{
- if (SetFilePointer(handle, 0, 0, FILE_BEGIN) == 0xFFFFFFFF)
- return;
- char buf[1024];
- DWORD bytesRead;
- while (ReadFile(handle, buf, sizeof(buf), &bytesRead, NULL) && bytesRead)
- result->append(buf, int(bytesRead));
- CloseHandle(handle);
-}
-
-static inline void appendToCommandLine(const QString &argument, QString *commandLine)
-{
- const bool needsQuote = argument.contains(QLatin1Char(' '));
- if (!commandLine->isEmpty())
- commandLine->append(QLatin1Char(' '));
- if (needsQuote)
- commandLine->append(QLatin1Char('"'));
- commandLine->append(argument);
- if (needsQuote)
- commandLine->append(QLatin1Char('"'));
-}
-
-// runProcess: Run a command line process (replacement for QProcess which
-// does not exist in the bootstrap library).
-bool runProcess(const QString &binary, const QStringList &args,
- const QString &workingDirectory,
- unsigned long *exitCode, QByteArray *stdOut, QByteArray *stdErr,
- QString *errorMessage)
-{
- if (exitCode)
- *exitCode = 0;
-
- STARTUPINFO si;
- ZeroMemory(&si, sizeof(si));
- si.cb = sizeof(si);
-
- STARTUPINFO myInfo;
- GetStartupInfo(&myInfo);
- si.hStdInput = myInfo.hStdInput;
- si.hStdOutput = myInfo.hStdOutput;
- si.hStdError = myInfo.hStdError;
-
- PROCESS_INFORMATION pi;
- ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
- const QChar backSlash = QLatin1Char('\\');
- QString nativeWorkingDir = QDir::toNativeSeparators(workingDirectory.isEmpty() ? QDir::currentPath() : workingDirectory);
- if (!nativeWorkingDir.endsWith(backSlash))
- nativeWorkingDir += backSlash;
-
- if (stdOut) {
- si.hStdOutput = createInheritableTemporaryFile();
- if (si.hStdOutput == INVALID_HANDLE_VALUE) {
- if (errorMessage)
- *errorMessage = QStringLiteral("Error creating stdout temporary file");
- return false;
- }
- si.dwFlags |= STARTF_USESTDHANDLES;
- }
-
- if (stdErr) {
- si.hStdError = createInheritableTemporaryFile();
- if (si.hStdError == INVALID_HANDLE_VALUE) {
- if (errorMessage)
- *errorMessage = QStringLiteral("Error creating stderr temporary file");
- return false;
- }
- si.dwFlags |= STARTF_USESTDHANDLES;
- }
-
- // Create a copy of the command line which CreateProcessW can modify.
- QString commandLine;
- appendToCommandLine(binary, &commandLine);
- for (const QString &a : args)
- appendToCommandLine(a, &commandLine);
- if (optVerboseLevel > 1)
- std::wcout << "Running: " << commandLine << '\n';
-
- QScopedArrayPointer<wchar_t> commandLineW(new wchar_t[commandLine.size() + 1]);
- commandLine.toWCharArray(commandLineW.data());
- commandLineW[commandLine.size()] = 0;
- if (!CreateProcessW(0, commandLineW.data(), 0, 0, /* InheritHandles */ TRUE, 0, 0,
- reinterpret_cast<LPCWSTR>(nativeWorkingDir.utf16()), &si, &pi)) {
- if (stdOut)
- CloseHandle(si.hStdOutput);
- if (stdErr)
- CloseHandle(si.hStdError);
- if (errorMessage)
- *errorMessage = QStringLiteral("CreateProcessW failed: ") + winErrorMessage(GetLastError());
- return false;
- }
-
- WaitForSingleObject(pi.hProcess, INFINITE);
- CloseHandle(pi.hThread);
- if (exitCode)
- GetExitCodeProcess(pi.hProcess, exitCode);
- CloseHandle(pi.hProcess);
-
- if (stdOut)
- readTemporaryProcessFile(si.hStdOutput, stdOut);
- if (stdErr)
- readTemporaryProcessFile(si.hStdError, stdErr);
- return true;
-}
-
-#else // Q_OS_WIN
-
-static inline char *encodeFileName(const QString &f)
-{
- const QByteArray encoded = QFile::encodeName(f);
- char *result = new char[encoded.size() + 1];
- strcpy(result, encoded.constData());
- return result;
-}
-
-static inline char *tempFilePattern()
-{
- QString path = QDir::tempPath();
- if (!path.endsWith(QLatin1Char('/')))
- path += QLatin1Char('/');
- path += QStringLiteral("tmpXXXXXX");
- return encodeFileName(path);
-}
-
-static inline QByteArray readOutRedirectFile(int fd)
-{
- enum { bufSize = 256 };
-
- QByteArray result;
- if (!lseek(fd, 0, 0)) {
- char buf[bufSize];
- while (true) {
- const ssize_t rs = read(fd, buf, bufSize);
- if (rs <= 0)
- break;
- result.append(buf, int(rs));
- }
- }
- close(fd);
- return result;
-}
-
-// runProcess: Run a command line process (replacement for QProcess which
-// does not exist in the bootstrap library).
-bool runProcess(const QString &binary, const QStringList &args,
- const QString &workingDirectory,
- unsigned long *exitCode, QByteArray *stdOut, QByteArray *stdErr,
- QString *errorMessage)
-{
- QScopedArrayPointer<char> stdOutFileName;
- QScopedArrayPointer<char> stdErrFileName;
-
- int stdOutFile = 0;
- if (stdOut) {
- stdOutFileName.reset(tempFilePattern());
- stdOutFile = mkstemp(stdOutFileName.data());
- if (stdOutFile < 0) {
- *errorMessage = QStringLiteral("mkstemp() failed: ") + QString::fromLocal8Bit(strerror(errno));
- return false;
- }
- }
-
- int stdErrFile = 0;
- if (stdErr) {
- stdErrFileName.reset(tempFilePattern());
- stdErrFile = mkstemp(stdErrFileName.data());
- if (stdErrFile < 0) {
- *errorMessage = QStringLiteral("mkstemp() failed: ") + QString::fromLocal8Bit(strerror(errno));
- return false;
- }
- }
-
- const pid_t pID = fork();
-
- if (pID < 0) {
- *errorMessage = QStringLiteral("Fork failed: ") + QString::fromLocal8Bit(strerror(errno));
- return false;
- }
-
- if (!pID) { // Child
- if (stdOut) {
- dup2(stdOutFile, STDOUT_FILENO);
- close(stdOutFile);
- }
- if (stdErr) {
- dup2(stdErrFile, STDERR_FILENO);
- close(stdErrFile);
- }
-
- if (!workingDirectory.isEmpty() && !QDir::setCurrent(workingDirectory)) {
- std::wcerr << "Failed to change working directory to " << workingDirectory << ".\n";
- ::_exit(-1);
- }
-
- char **argv = new char *[args.size() + 2]; // Create argv.
- char **ap = argv;
- *ap++ = encodeFileName(binary);
- for (const QString &a : qAsConst(args))
- *ap++ = encodeFileName(a);
- *ap = 0;
-
- execvp(argv[0], argv);
- ::_exit(-1);
- }
-
- int status;
- pid_t waitResult;
-
- do {
- waitResult = waitpid(pID, &status, 0);
- } while (waitResult == -1 && errno == EINTR);
-
- if (stdOut) {
- *stdOut = readOutRedirectFile(stdOutFile);
- unlink(stdOutFileName.data());
- }
- if (stdErr) {
- *stdErr = readOutRedirectFile(stdErrFile);
- unlink(stdErrFileName.data());
- }
-
- if (waitResult < 0) {
- *errorMessage = QStringLiteral("Wait failed: ") + QString::fromLocal8Bit(strerror(errno));
- return false;
- }
- if (!WIFEXITED(status)) {
- *errorMessage = binary + QStringLiteral(" did not exit cleanly.");
- return false;
- }
- if (exitCode)
- *exitCode = WEXITSTATUS(status);
- return true;
-}
-
-#endif // !Q_OS_WIN
-
-// Find a file in the path using ShellAPI. This can be used to locate DLLs which
-// QStandardPaths cannot do.
-QString findInPath(const QString &file)
-{
-#if defined(Q_OS_WIN)
- if (file.size() < MAX_PATH - 1) {
- wchar_t buffer[MAX_PATH];
- file.toWCharArray(buffer);
- buffer[file.size()] = 0;
- if (PathFindOnPath(buffer, NULL))
- return QDir::cleanPath(QString::fromWCharArray(buffer));
- }
- return QString();
-#else // Q_OS_WIN
- return QStandardPaths::findExecutable(file);
-#endif // !Q_OS_WIN
-}
-
-const char *qmakeInfixKey = "QT_INFIX";
-
-QMap<QString, QString> queryQtPaths(const QString &qtpathsBinary, QString *errorMessage)
-{
- const QString binary = !qtpathsBinary.isEmpty() ? qtpathsBinary : QStringLiteral("qtpaths");
- QByteArray stdOut;
- QByteArray stdErr;
- unsigned long exitCode = 0;
- if (!runProcess(binary, QStringList(QStringLiteral("-query")), QString(), &exitCode, &stdOut, &stdErr, errorMessage))
- return QMap<QString, QString>();
- if (exitCode) {
- *errorMessage = binary + QStringLiteral(" returns ") + QString::number(exitCode)
- + QStringLiteral(": ") + QString::fromLocal8Bit(stdErr);
- return QMap<QString, QString>();
- }
- const QString output = QString::fromLocal8Bit(stdOut).trimmed().remove(QLatin1Char('\r'));
- QMap<QString, QString> result;
- const int size = output.size();
- for (int pos = 0; pos < size; ) {
- const int colonPos = output.indexOf(QLatin1Char(':'), pos);
- if (colonPos < 0)
- break;
- int endPos = output.indexOf(QLatin1Char('\n'), colonPos + 1);
- if (endPos < 0)
- endPos = size;
- const QString key = output.mid(pos, colonPos - pos);
- const QString value = output.mid(colonPos + 1, endPos - colonPos - 1);
- result.insert(key, value);
- pos = endPos + 1;
- }
- QFile qconfigPriFile(result.value(QStringLiteral("QT_HOST_DATA")) + QStringLiteral("/mkspecs/qconfig.pri"));
- if (qconfigPriFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
- while (true) {
- const QByteArray line = qconfigPriFile.readLine();
- if (line.isEmpty())
- break;
- if (line.startsWith("QT_LIBINFIX")) {
- const int pos = line.indexOf('=');
- if (pos >= 0) {
- const QString infix = QString::fromUtf8(line.right(line.size() - pos - 1).trimmed());
- if (!infix.isEmpty())
- result.insert(QLatin1String(qmakeInfixKey), infix);
- }
- break;
- }
- }
- } else {
- std::wcerr << "Warning: Unable to read " << QDir::toNativeSeparators(qconfigPriFile.fileName())
- << ": " << qconfigPriFile.errorString()<< '\n';
- }
- return result;
-}
-
-// Update a file or directory.
-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();
- 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;
- }
-
- if (sourceFileInfo.isSymLink()) {
- *errorMessage = QString::fromLatin1("Symbolic links are not supported (%1).")
- .arg(QDir::toNativeSeparators(sourceFileName));
- return false;
- }
-
- const QFileInfo targetFileInfo(targetFileName);
-
- if (sourceFileInfo.isDir()) {
- 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 " << QDir::toNativeSeparators(targetFileName) << ".\n";
- if (!(flags & SkipUpdateFile) && !d.mkdir(sourceFileInfo.fileName())) {
- *errorMessage = QString::fromLatin1("Cannot create directory %1 under %2.")
- .arg(sourceFileInfo.fileName(), QDir::toNativeSeparators(targetDirectory));
- return false;
- }
- }
- // Recurse into directory
- QDir dir(sourceFileName);
- const QFileInfoList allEntries = dir.entryInfoList(nameFilters, QDir::Files)
- + dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
- for (const QFileInfo &entryFi : allEntries) {
- if (!updateFile(entryFi.absoluteFilePath(), nameFilters, targetFileName, flags, json, errorMessage))
- return false;
- }
- 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) && !file.copy(targetFileName)) {
- *errorMessage = QString::fromLatin1("Cannot copy %1 to %2: %3")
- .arg(QDir::toNativeSeparators(sourceFileName),
- QDir::toNativeSeparators(targetFileName),
- file.errorString());
- return false;
- }
- if (json)
- json->addFile(sourceFileName, targetDirectory);
- 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)
-{
- return QString::fromLocal8Bit(static_cast<const char *>(rvaPtr));
-}
-
-// Helper for reading out PE executable files: Find a section header for an RVA
-// (IMAGE_NT_HEADERS64, IMAGE_NT_HEADERS32).
-template <class ImageNtHeader>
-const IMAGE_SECTION_HEADER *findSectionHeader(DWORD rva, const ImageNtHeader *nTHeader)
-{
- const IMAGE_SECTION_HEADER *section = IMAGE_FIRST_SECTION(nTHeader);
- const IMAGE_SECTION_HEADER *sectionEnd = section + nTHeader->FileHeader.NumberOfSections;
- for ( ; section < sectionEnd; ++section)
- if (rva >= section->VirtualAddress && rva < (section->VirtualAddress + section->Misc.VirtualSize))
- return section;
- return 0;
-}
-
-// Helper for reading out PE executable files: convert RVA to pointer (IMAGE_NT_HEADERS64, IMAGE_NT_HEADERS32).
-template <class ImageNtHeader>
-inline const void *rvaToPtr(DWORD rva, const ImageNtHeader *nTHeader, const void *imageBase)
-{
- const IMAGE_SECTION_HEADER *sectionHdr = findSectionHeader(rva, nTHeader);
- if (!sectionHdr)
- return 0;
- const DWORD delta = sectionHdr->VirtualAddress - sectionHdr->PointerToRawData;
- return static_cast<const char *>(imageBase) + rva - delta;
-}
-
-// Helper for reading out PE executable files: return word size of a IMAGE_NT_HEADERS64, IMAGE_NT_HEADERS32
-template <class ImageNtHeader>
-inline unsigned ntHeaderWordSize(const ImageNtHeader *header)
-{
- // defines IMAGE_NT_OPTIONAL_HDR32_MAGIC, IMAGE_NT_OPTIONAL_HDR64_MAGIC
- enum { imageNtOptionlHeader32Magic = 0x10b, imageNtOptionlHeader64Magic = 0x20b };
- if (header->OptionalHeader.Magic == imageNtOptionlHeader32Magic)
- return 32;
- if (header->OptionalHeader.Magic == imageNtOptionlHeader64Magic)
- return 64;
- return 0;
-}
-
-// Helper for reading out PE executable files: Retrieve the NT image header of an
-// executable via the legacy DOS header.
-static IMAGE_NT_HEADERS *getNtHeader(void *fileMemory, QString *errorMessage)
-{
- IMAGE_DOS_HEADER *dosHeader = static_cast<PIMAGE_DOS_HEADER>(fileMemory);
- // Check DOS header consistency
- if (IsBadReadPtr(dosHeader, sizeof(IMAGE_DOS_HEADER))
- || dosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
- *errorMessage = QString::fromLatin1("DOS header check failed.");
- return 0;
- }
- // Retrieve NT header
- char *ntHeaderC = static_cast<char *>(fileMemory) + dosHeader->e_lfanew;
- IMAGE_NT_HEADERS *ntHeaders = reinterpret_cast<IMAGE_NT_HEADERS *>(ntHeaderC);
- // check NT header consistency
- if (IsBadReadPtr(ntHeaders, sizeof(ntHeaders->Signature))
- || ntHeaders->Signature != IMAGE_NT_SIGNATURE
- || IsBadReadPtr(&ntHeaders->FileHeader, sizeof(IMAGE_FILE_HEADER))) {
- *errorMessage = QString::fromLatin1("NT header check failed.");
- return 0;
- }
- // Check magic
- if (!ntHeaderWordSize(ntHeaders)) {
- *errorMessage = QString::fromLatin1("NT header check failed; magic %1 is invalid.").
- arg(ntHeaders->OptionalHeader.Magic);
- return 0;
- }
- // Check section headers
- IMAGE_SECTION_HEADER *sectionHeaders = IMAGE_FIRST_SECTION(ntHeaders);
- if (IsBadReadPtr(sectionHeaders, ntHeaders->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER))) {
- *errorMessage = QString::fromLatin1("NT header section header check failed.");
- return 0;
- }
- return ntHeaders;
-}
-
-// Helper for reading out PE executable files: Read out import sections from
-// IMAGE_NT_HEADERS64, IMAGE_NT_HEADERS32.
-template <class ImageNtHeader>
-inline QStringList readImportSections(const ImageNtHeader *ntHeaders, const void *base, QString *errorMessage)
-{
- // Get import directory entry RVA and read out
- const DWORD importsStartRVA = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
- if (!importsStartRVA) {
- *errorMessage = QString::fromLatin1("Failed to find IMAGE_DIRECTORY_ENTRY_IMPORT entry.");
- return QStringList();
- }
- const IMAGE_IMPORT_DESCRIPTOR *importDesc = static_cast<const IMAGE_IMPORT_DESCRIPTOR *>(rvaToPtr(importsStartRVA, ntHeaders, base));
- if (!importDesc) {
- *errorMessage = QString::fromLatin1("Failed to find IMAGE_IMPORT_DESCRIPTOR entry.");
- return QStringList();
- }
- QStringList result;
- for ( ; importDesc->Name; ++importDesc)
- result.push_back(stringFromRvaPtr(rvaToPtr(importDesc->Name, ntHeaders, base)));
-
- // Read delay-loaded DLLs, see http://msdn.microsoft.com/en-us/magazine/cc301808.aspx .
- // Check on grAttr bit 1 whether this is the format using RVA's > VS 6
- if (const DWORD delayedImportsStartRVA = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].VirtualAddress) {
- const ImgDelayDescr *delayedImportDesc = static_cast<const ImgDelayDescr *>(rvaToPtr(delayedImportsStartRVA, ntHeaders, base));
- for ( ; delayedImportDesc->rvaDLLName && (delayedImportDesc->grAttrs & 1); ++delayedImportDesc)
- result.push_back(stringFromRvaPtr(rvaToPtr(delayedImportDesc->rvaDLLName, ntHeaders, base)));
- }
-
- return result;
-}
-
-// Check for MSCV runtime (MSVCP90D.dll/MSVCP90.dll, MSVCP120D.dll/MSVCP120.dll,
-// VCRUNTIME140D.DLL/VCRUNTIME140.DLL (VS2015) or msvcp120d_app.dll/msvcp120_app.dll).
-enum MsvcDebugRuntimeResult { MsvcDebugRuntime, MsvcReleaseRuntime, NoMsvcRuntime };
-
-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('.'));
- 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;
- }
- }
- return NoMsvcRuntime;
-}
-
-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;
- }
- }
-}
-
-// Read a PE executable and determine dependent libraries, word size
-// and debug flags.
-bool readPeExecutable(const QString &peExecutableFileName, QString *errorMessage,
- QStringList *dependentLibrariesIn, unsigned *wordSizeIn,
- bool *isDebugIn, bool isMinGW, unsigned short *machineArchIn)
-{
- bool result = false;
- HANDLE hFile = NULL;
- HANDLE hFileMap = NULL;
- void *fileMemory = 0;
-
- if (dependentLibrariesIn)
- dependentLibrariesIn->clear();
- if (wordSizeIn)
- *wordSizeIn = 0;
- if (isDebugIn)
- *isDebugIn = false;
-
- do {
- // Create a memory mapping of the file
- 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()));
- 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()));
- break;
- }
-
- fileMemory = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0);
- if (!fileMemory) {
- *errorMessage = QString::fromLatin1("Cannot map '%1': %2").arg(peExecutableFileName, winErrorMessage(GetLastError()));
- break;
- }
-
- const IMAGE_NT_HEADERS *ntHeaders = getNtHeader(fileMemory, errorMessage);
- if (!ntHeaders)
- break;
-
- const unsigned wordSize = ntHeaderWordSize(ntHeaders);
- if (wordSizeIn)
- *wordSizeIn = wordSize;
- if (wordSize == 32) {
- determineDebugAndDependentLibs(reinterpret_cast<const IMAGE_NT_HEADERS32 *>(ntHeaders),
- fileMemory, isMinGW, dependentLibrariesIn, isDebugIn, errorMessage);
- } else {
- determineDebugAndDependentLibs(reinterpret_cast<const IMAGE_NT_HEADERS64 *>(ntHeaders),
- fileMemory, isMinGW, dependentLibrariesIn, isDebugIn, errorMessage);
- }
-
- if (machineArchIn)
- *machineArchIn = ntHeaders->FileHeader.Machine;
-
- result = true;
- if (optVerboseLevel > 1) {
- std::wcout << __FUNCTION__ << ": " << QDir::toNativeSeparators(peExecutableFileName)
- << ' ' << wordSize << " bit";
- if (isMinGW)
- std::wcout << ", MinGW";
- if (dependentLibrariesIn) {
- std::wcout << ", dependent libraries: ";
- if (optVerboseLevel > 2)
- std::wcout << dependentLibrariesIn->join(QLatin1Char(' '));
- else
- std::wcout << dependentLibrariesIn->size();
- }
- if (isDebugIn)
- std::wcout << (*isDebugIn ? ", debug" : ", release");
- std::wcout << '\n';
- }
- } while (false);
-
- if (fileMemory)
- UnmapViewOfFile(fileMemory);
-
- if (hFileMap != NULL)
- CloseHandle(hFileMap);
-
- if (hFile != NULL && hFile != INVALID_HANDLE_VALUE)
- CloseHandle(hFile);
-
- return result;
-}
-
-QString findD3dCompiler(Platform platform, const QString &qtBinDir, unsigned wordSize)
-{
- const QString prefix = QStringLiteral("D3Dcompiler_");
- const QString suffix = QLatin1String(windowsSharedLibrarySuffix);
- // Get the DLL from Kit 8.0 onwards
- const QString kitDir = QString::fromLocal8Bit(qgetenv("WindowsSdkDir"));
- if (!kitDir.isEmpty()) {
- QString redistDirPath = QDir::cleanPath(kitDir) + QStringLiteral("/Redist/D3D/");
- if (platform.testFlag(ArmBased)) {
- redistDirPath += QStringLiteral("arm");
- } else {
- redistDirPath += wordSize == 32 ? QStringLiteral("x86") : QStringLiteral("x64");
- }
- QDir redistDir(redistDirPath);
- if (redistDir.exists()) {
- const QFileInfoList files = redistDir.entryInfoList(QStringList(prefix + QLatin1Char('*') + suffix), QDir::Files);
- if (!files.isEmpty())
- return files.front().absoluteFilePath();
- }
- }
- QStringList candidateVersions;
- for (int i = 47 ; i >= 40 ; --i)
- 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);
- if (fi.isFile())
- return fi.absoluteFilePath();
- }
- // Find the latest D3D compiler DLL in path (Windows 8.1 has d3dcompiler_47).
- if (platform.testFlag(IntelBased)) {
- QString errorMessage;
- unsigned detectedWordSize;
- for (const QString &candidate : qAsConst(candidateVersions)) {
- const QString dll = findInPath(candidate);
- if (!dll.isEmpty()
- && readPeExecutable(dll, &errorMessage, 0, &detectedWordSize, 0)
- && detectedWordSize == wordSize) {
- return dll;
- }
- }
- }
- return QString();
-}
-
-#else // Q_OS_WIN
-
-bool readPeExecutable(const QString &, QString *errorMessage,
- QStringList *, unsigned *, bool *, bool, unsigned short *)
-{
- *errorMessage = QStringLiteral("Not implemented.");
- return false;
-}
-
-QString findD3dCompiler(Platform, const QString &, unsigned)
-{
- return QString();
-}
-
-#endif // !Q_OS_WIN
-
-// Search for "qt_prfxpath=xxxx" in \a path, and replace it with "qt_prfxpath=."
-bool patchQtCore(const QString &path, QString *errorMessage)
-{
- if (optVerboseLevel)
- std::wcout << "Patching " << QFileInfo(path).fileName() << "...\n";
-
- QFile file(path);
- if (!file.open(QIODevice::ReadOnly)) {
- *errorMessage = QString::fromLatin1("Unable to patch %1: %2").arg(
- QDir::toNativeSeparators(path), file.errorString());
- return false;
- }
- const QByteArray oldContent = file.readAll();
-
- if (oldContent.isEmpty()) {
- *errorMessage = QString::fromLatin1("Unable to patch %1: Could not read file content").arg(
- QDir::toNativeSeparators(path));
- return false;
- }
- file.close();
-
- QByteArray content = oldContent;
-
- QByteArray prfxpath("qt_prfxpath=");
- int startPos = content.indexOf(prfxpath);
- if (startPos == -1) {
- *errorMessage = QString::fromLatin1(
- "Unable to patch %1: Could not locate pattern \"qt_prfxpath=\"").arg(
- QDir::toNativeSeparators(path));
- return false;
- }
- startPos += prfxpath.length();
- int endPos = content.indexOf(char(0), startPos);
- if (endPos == -1) {
- *errorMessage = QString::fromLatin1("Unable to patch %1: Internal error").arg(
- QDir::toNativeSeparators(path));
- return false;
- }
-
- QByteArray replacement = QByteArray(endPos - startPos, char(0));
- replacement[0] = '.';
- content.replace(startPos, endPos - startPos, replacement);
- if (content == oldContent)
- return true;
-
- if (!file.open(QIODevice::WriteOnly)
- || (file.write(content) != content.size())) {
- *errorMessage = QString::fromLatin1("Unable to patch %1: Could not write to file: %2").arg(
- QDir::toNativeSeparators(path), file.errorString());
- return false;
- }
- return true;
-}
-
-#ifdef Q_OS_WIN
-QString getArchString(unsigned short machineArch)
-{
- switch (machineArch) {
- case IMAGE_FILE_MACHINE_I386:
- return QStringLiteral("x86");
- case IMAGE_FILE_MACHINE_ARM:
- return QStringLiteral("arm");
- case IMAGE_FILE_MACHINE_AMD64:
- return QStringLiteral("x64");
- case IMAGE_FILE_MACHINE_ARM64:
- return QStringLiteral("arm64");
- default:
- break;
- }
- return QString();
-}
-#endif // Q_OS_WIN
-
-QT_END_NAMESPACE