From 0cdd2672608c1db7fb6595003d1acf7d0a04556c Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Mon, 9 Jan 2017 17:06:26 +0100 Subject: [PATCH] QtSupport: Use binary string from Qt's libraryLoader to refine ABI Use binary string from Qt's library loader class to refine ABI if that is not complete yet. This is necessary to reliably find the compiler used in static Qt builds on windows (using MSVC compilers). Before we were able to rely on the mkspec to pick a working compiler, that does no longer work with Qt 5.8. Task-number: QTCREATORBUG-17534 Change-Id: I50c428cbaa986a26ba165cff499f9e0be3937ea2 Reviewed-by: Tim Jenssen --- src/plugins/qtsupport/baseqtversion.cpp | 146 +++++++++++++++++++++++- 1 file changed, 141 insertions(+), 5 deletions(-) diff --git a/src/plugins/qtsupport/baseqtversion.cpp b/src/plugins/qtsupport/baseqtversion.cpp index 6644a8d1349..1c1021a08f9 100644 --- a/src/plugins/qtsupport/baseqtversion.cpp +++ b/src/plugins/qtsupport/baseqtversion.cpp @@ -47,7 +47,6 @@ #include #include #include -#include #include #include @@ -57,6 +56,8 @@ #include #include +#include + using namespace Core; using namespace QtSupport; using namespace QtSupport::Internal; @@ -1734,12 +1735,147 @@ FileNameList BaseQtVersion::qtCorePaths(const QHash &versionInf return dynamicLibs; } +static QByteArray scanQtBinaryForBuildString(const FileName &library) +{ + QFile lib(library.toString()); + QByteArray buildString; + + if (lib.open(QIODevice::ReadOnly)) { + const QByteArray startNeedle = "Qt "; + const QByteArray buildNeedle = " build; by "; + const size_t oneMiB = 1024 * 1024; + const size_t keepSpace = 4096; + const size_t bufferSize = oneMiB + keepSpace; + QByteArray buffer(bufferSize, '\0'); + + char *const readStart = buffer.data() + keepSpace; + auto readStartIt = buffer.begin() + keepSpace; + const auto copyStartIt = readStartIt + (oneMiB - keepSpace); + + while (!lib.atEnd()) { + const int read = lib.read(readStart, static_cast(oneMiB)); + const auto readEndIt = readStart + read; + auto currentIt = readStartIt; + + forever { + const auto qtFoundIt = std::search(currentIt, readEndIt, + startNeedle.begin(), startNeedle.end()); + if (qtFoundIt == readEndIt) + break; + + currentIt = qtFoundIt + 1; + + // Found "Qt ", now find the next '\0'. + const auto nullFoundIt = std::find(qtFoundIt, readEndIt, '\0'); + if (nullFoundIt == readEndIt) + break; + + // String much too long? + const size_t len = std::distance(qtFoundIt, nullFoundIt); + if (len > keepSpace) + continue; + + // Does it contain " build; by "? + const auto buildByFoundIt = std::search(qtFoundIt, nullFoundIt, + buildNeedle.begin(), buildNeedle.end()); + if (buildByFoundIt == nullFoundIt) + continue; + + buildString = QByteArray(qtFoundIt, len); + break; + } + + if (!buildString.isEmpty() || readEndIt != buffer.constEnd()) + break; + + std::move(copyStartIt, readEndIt, buffer.begin()); // Copy last section to front. + } + } + return buildString; +} + +static Abi refineAbiFromBuildString(const QByteArray &buildString, const Abi &probableAbi) +{ + if (buildString.isEmpty() + || buildString.count() > 4096) + return Abi(); + + const QRegularExpression buildStringMatcher("^Qt " + "([\\d\\.a-zA-Z]*) " // Qt version + "\\(" + "([a-z\\d_]*)-" // CPU + "(big|little)_endian-" + "([a-z]+(?:32|64))" // pointer information + "(?:-(qreal|))?" // extra information like -qreal + "(?:-([^-]+))? " // ABI information + "(shared|static) (?:\\(dynamic\\) )?" + "(debug|release)" + " build; by " + "(.*)" // compiler with extra info + "\\)$"); + + QTC_ASSERT(buildStringMatcher.isValid(), qWarning() << buildStringMatcher.errorString()); + const QRegularExpressionMatch match = buildStringMatcher.match(QString::fromUtf8(buildString)); + QTC_ASSERT(match.hasMatch(), return Abi()); + + // const QString qtVersion = match.captured(1); + // const QString cpu = match.captured(2); + // const bool littleEndian = (match.captured(3) == "little"); + // const QString pointer = match.captured(4); + // const QString extra = match.captured(5); + // const QString abiString = match.captured(6); + // const QString linkage = match.captured(7); + // const QString buildType = match.captured(8); + const QString compiler = match.captured(9); + + Abi::Architecture arch = probableAbi.architecture(); + Abi::OS os = probableAbi.os(); + Abi::OSFlavor flavor = probableAbi.osFlavor(); + Abi::BinaryFormat format = probableAbi.binaryFormat(); + unsigned char wordWidth = probableAbi.wordWidth(); + + if (compiler.startsWith("GCC ") && os == Abi::WindowsOS) { + flavor = Abi::WindowsMSysFlavor; + } else if (compiler.startsWith("MSVC 2005") && os == Abi::WindowsOS) { + flavor = Abi::WindowsMsvc2005Flavor; + } else if (compiler.startsWith("MSVC 2008") && os == Abi::WindowsOS) { + flavor = Abi::WindowsMsvc2008Flavor; + } else if (compiler.startsWith("MSVC 2010") && os == Abi::WindowsOS) { + flavor = Abi::WindowsMsvc2010Flavor; + } else if (compiler.startsWith("MSVC 2012") && os == Abi::WindowsOS) { + flavor = Abi::WindowsMsvc2012Flavor; + } else if (compiler.startsWith("MSVC 2015") && os == Abi::WindowsOS) { + flavor = Abi::WindowsMsvc2015Flavor; + } else if (compiler.startsWith("MSVC 2017") && os == Abi::WindowsOS) { + flavor = Abi::WindowsMsvc2017Flavor; + } + + return Abi(arch, os, flavor, format, wordWidth); +} + +static Abi scanQtBinaryForBuildStringAndRefineAbi(const FileName &library, + const Abi &probableAbi) +{ + static QHash results; + + if (!results.contains(library)) { + const QByteArray buildString = scanQtBinaryForBuildString(library); + results.insert(library, refineAbiFromBuildString(buildString, probableAbi)); + } + return results.value(library); +} + QList BaseQtVersion::qtAbisFromLibrary(const FileNameList &coreLibraries) { QList res; - foreach (const FileName &library, coreLibraries) - foreach (const Abi &abi, Abi::abisOfBinary(library)) - if (!res.contains(abi)) - res.append(abi); + foreach (const FileName &library, coreLibraries) { + for (Abi abi : Abi::abisOfBinary(library)) { + Abi tmp = abi; + if (abi.osFlavor() == Abi::UnknownFlavor) + tmp = scanQtBinaryForBuildStringAndRefineAbi(library, abi); + if (!res.contains(tmp)) + res.append(tmp); + } + } return res; }