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; }