diff --git a/src/libs/utils/datafromprocess.h b/src/libs/utils/datafromprocess.h index 5acb220ba4e..e8b996b29aa 100644 --- a/src/libs/utils/datafromprocess.h +++ b/src/libs/utils/datafromprocess.h @@ -31,7 +31,8 @@ public: class Parameters { public: - using OutputParser = std::function(const QString &)>; + using OutputParser = std::function< + std::optional(const QString & /* stdOut */, const QString & /* stdErr */)>; using ErrorHandler = std::function; using Callback = std::function &)>; @@ -133,7 +134,7 @@ inline std::optional DataFromProcess::handleProcessFinished( std::optional data; if (params.allowedResults.contains(process->result())) - data = params.parser(process->cleanedStdOut()); + data = params.parser(process->cleanedStdOut(), process->cleanedStdErr()); else if (params.errorHandler) params.errorHandler(*process); QMutexLocker cacheLocker(&m_cacheMutex); diff --git a/src/libs/utils/pathchooser.cpp b/src/libs/utils/pathchooser.cpp index 26c190730ce..f268d4de2ca 100644 --- a/src/libs/utils/pathchooser.cpp +++ b/src/libs/utils/pathchooser.cpp @@ -107,9 +107,9 @@ bool BinaryVersionToolTipEventFilter::eventFilter(QObject *o, QEvent *e) QTC_ASSERT(le, return false); const QString binary = le->text(); - DataFromProcess::Parameters params(CommandLine(FilePath::fromUserInput(binary), - m_arguments), - [](const QString &output) { return output; }); + DataFromProcess::Parameters params( + CommandLine(FilePath::fromUserInput(binary), m_arguments), + [](const QString &output, const QString &) { return output; }); params.callback = [binary, self = QPointer(this), le = QPointer(le)](const std::optional &version) { if (!self || !le) diff --git a/src/plugins/clangtools/executableinfo.cpp b/src/plugins/clangtools/executableinfo.cpp index 23dc807367d..b3d4f9eb790 100644 --- a/src/plugins/clangtools/executableinfo.cpp +++ b/src/plugins/clangtools/executableinfo.cpp @@ -41,7 +41,8 @@ static QStringList queryClangTidyChecks(const FilePath &executable, // abseil-duration-division // abseil-duration-factory-float // ... - static const auto parser = [](const QString &stdOut) -> std::optional { + static const auto parser = [](const QString &stdOut, + const QString &) -> std::optional { QString output = stdOut; QTextStream stream(&output); QString line = stream.readLine(); @@ -85,7 +86,8 @@ static ClazyChecks querySupportedClazyChecks(const FilePath &executablePath) // ... // ] // } - static const auto parser = [](const QString &jsonOutput) -> std::optional { + static const auto parser = [](const QString &jsonOutput, + const QString &) -> std::optional { const QJsonDocument document = QJsonDocument::fromJson(jsonOutput.toUtf8()); if (document.isNull()) return {}; @@ -130,7 +132,8 @@ ClazyStandaloneInfo::ClazyStandaloneInfo(const FilePath &executablePath) : defaultChecks(queryClangTidyChecks(executablePath, {})) // Yup, behaves as clang-tidy. , supportedChecks(querySupportedClazyChecks(executablePath)) { - static const auto parser = [](const QString &stdOut) -> std::optional { + static const auto parser = [](const QString &stdOut, + const QString &) -> std::optional { QString output = stdOut; QTextStream stream(&output); while (!stream.atEnd()) { @@ -157,7 +160,8 @@ static FilePath queryResourceDir(const FilePath &clangToolPath) // lib/clang/10.0.1 // Error while trying to load a compilation database: // ... - const auto parser = [&clangToolPath](const QString &stdOut) -> std::optional { + const auto parser = + [&clangToolPath](const QString &stdOut, const QString &) -> std::optional { QString output = stdOut; QTextStream stream(&output); const QString path = clangToolPath.parentDir().parentDir() @@ -180,7 +184,7 @@ static FilePath queryResourceDir(const FilePath &clangToolPath) QString queryVersion(const FilePath &clangToolPath, QueryFailMode failMode) { - static const auto parser = [](const QString &stdOut) -> std::optional { + static const auto parser = [](const QString &stdOut, const QString &) -> std::optional { QString output = stdOut; QTextStream stream(&output); while (!stream.atEnd()) { diff --git a/src/plugins/git/gerrit/gerritparameters.cpp b/src/plugins/git/gerrit/gerritparameters.cpp index d8f69878fac..d2649b9e3d4 100644 --- a/src/plugins/git/gerrit/gerritparameters.cpp +++ b/src/plugins/git/gerrit/gerritparameters.cpp @@ -74,8 +74,8 @@ void GerritParameters::setPortFlagBySshType() { bool isPlink = false; if (!ssh.isEmpty()) { - DataFromProcess::Parameters params({ssh, {"-V"}}, - [](const QString &output) { return output; }); + DataFromProcess::Parameters + params({ssh, {"-V"}}, [](const QString &output, const QString &) { return output; }); using namespace std::chrono_literals; params.timeout = 1s; if (const auto version = DataFromProcess::getData(params)) diff --git a/src/plugins/projectexplorer/gcctoolchain.cpp b/src/plugins/projectexplorer/gcctoolchain.cpp index a290c0cf508..0d19c0a8968 100644 --- a/src/plugins/projectexplorer/gcctoolchain.cpp +++ b/src/plugins/projectexplorer/gcctoolchain.cpp @@ -390,6 +390,10 @@ GccToolchain::GccToolchain(Id typeId, SubType subType) setTypeDisplayName(Tr::tr("Clang")); syncAutodetectedWithParentToolchains(); } + + setVersionFlagsAndParser({"-dumpversion"}, [](const QString &output, const QString &) { + return QVersionNumber::fromString(output.trimmed()); + }); } GccToolchain::~GccToolchain() diff --git a/src/plugins/projectexplorer/msvctoolchain.cpp b/src/plugins/projectexplorer/msvctoolchain.cpp index 715505381c9..083bb619243 100644 --- a/src/plugins/projectexplorer/msvctoolchain.cpp +++ b/src/plugins/projectexplorer/msvctoolchain.cpp @@ -852,6 +852,17 @@ MsvcToolchain::MsvcToolchain(Utils::Id typeId) setTypeDisplayName(Tr::tr("MSVC")); addToAvailableMsvcToolchains(this); setTargetAbiKey(KEY_ROOT "SupportedAbi"); + setVersionFlagsAndParser({}, [](const QString &, const QString &stdErr) -> QVersionNumber { + const QString markerString = " Version "; + const int markerIndex = stdErr.indexOf(markerString); + if (markerIndex == -1) + return {}; + const int versionOffset = markerIndex + markerString.size(); + const int spaceIndex = stdErr.indexOf(' ', versionOffset); + if (spaceIndex == -1) + return {}; + return QVersionNumber::fromString(stdErr.mid(versionOffset, spaceIndex - versionOffset)); + }); } void MsvcToolchain::inferWarningsForLevel(int warningLevel, WarningFlags &flags) @@ -1677,6 +1688,19 @@ ClangClToolchain::ClangClToolchain() { setDisplayName("clang-cl"); setTypeDisplayName(Tr::tr("Clang")); + setVersionFlagsAndParser( + {"--version"}, [](const QString &output, const QString &) -> QVersionNumber { + const QString marker = "clang version "; + const int markerIndex = output.indexOf(marker); + if (markerIndex == -1) + return {}; + const int versionOffset = markerIndex + marker.size(); + const int newlineIndex = output.indexOf('\n', versionOffset); + if (newlineIndex == -1) + return {}; + return QVersionNumber::fromString( + output.mid(versionOffset, newlineIndex - versionOffset).trimmed()); + }); } bool ClangClToolchain::isValid() const @@ -2251,7 +2275,7 @@ ClangClInfo ClangClInfo::getInfo(const FilePath &filePath) { QTC_ASSERT(!filePath.isEmpty(), return {}); - static const auto parser = [](const QString &stdOut) { + static const auto parser = [](const QString &stdOut, const QString &) { ClangClInfo info; const QRegularExpressionMatch versionMatch = QRegularExpression("clang version (\\d+(\\.\\d+)+)").match(stdOut); diff --git a/src/plugins/projectexplorer/msvctoolchain.h b/src/plugins/projectexplorer/msvctoolchain.h index 1ac2594fd1a..8e7f1083d80 100644 --- a/src/plugins/projectexplorer/msvctoolchain.h +++ b/src/plugins/projectexplorer/msvctoolchain.h @@ -144,7 +144,7 @@ public: const QList &msvcToolchains() const; Utils::FilePath clangPath() const { return m_clangPath; } - void setClangPath(const Utils::FilePath &path) { m_clangPath = path; } + void setClangPath(const Utils::FilePath &path) { m_clangPath = path; clearVersion(); } Macros msvcPredefinedMacros(const QStringList &cxxflags, const Utils::Environment &env) const override; diff --git a/src/plugins/projectexplorer/toolchain.cpp b/src/plugins/projectexplorer/toolchain.cpp index 681f61fd480..e625c143d59 100644 --- a/src/plugins/projectexplorer/toolchain.cpp +++ b/src/plugins/projectexplorer/toolchain.cpp @@ -12,6 +12,7 @@ #include "task.h" #include +#include #include #include @@ -77,6 +78,11 @@ public: Toolchain::MacrosCache m_predefinedMacrosCache; Toolchain::HeaderPathsCache m_headerPathsCache; + + QStringList m_versionFlags; + Toolchain::VersionParser m_versionParser; + std::optional m_version; + std::optional m_isValid; bool m_hasError = false; }; @@ -353,6 +359,24 @@ void Toolchain::setTargetAbi(const Abi &abi) toolChainUpdated(); } +QVersionNumber Toolchain::version() const +{ + if (d->m_version) + return *d->m_version; + + if (!d->m_versionParser || compilerCommand().isEmpty()) + return {}; + + using DFP = DataFromProcess; + DFP::Parameters params({compilerCommand(), d->m_versionFlags}, d->m_versionParser); + params.environment.setupEnglishOutput(); + params.environment.set("VSLANG", "1033"); + d->m_version = DFP::getData(params); + if (!d->m_version) + d->m_version.emplace(); + return *d->m_version; +} + void Toolchain::setTargetAbiNoSignal(const Abi &abi) { d->m_targetAbi = abi; @@ -375,6 +399,7 @@ void Toolchain::setCompilerCommand(const FilePath &command) if (command == d->m_compilerCommand) return; d->m_compilerCommand = command; + clearVersion(); toolChainUpdated(); } @@ -393,6 +418,17 @@ void Toolchain::setTypeDisplayName(const QString &typeName) d->m_typeDisplayName = typeName; } +void Toolchain::setVersionFlagsAndParser(const QStringList &flags, const VersionParser &parser) +{ + d->m_versionFlags = flags; + d->m_versionParser = parser; +} + +void Toolchain::clearVersion() +{ + d->m_version.reset(); +} + /*! Used by the tool chain manager to load user-generated tool chains. diff --git a/src/plugins/projectexplorer/toolchain.h b/src/plugins/projectexplorer/toolchain.h index 40a384c0110..bb08d04dcf5 100644 --- a/src/plugins/projectexplorer/toolchain.h +++ b/src/plugins/projectexplorer/toolchain.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -99,6 +100,8 @@ public: Abi targetAbi() const; void setTargetAbi(const Abi &abi); + QVersionNumber version() const; + virtual ProjectExplorer::Abis supportedAbis() const; virtual QString originalTargetTriple() const { return {}; } virtual QStringList extraCodeModelFlags() const { return {}; } @@ -179,6 +182,11 @@ protected: void setTypeDisplayName(const QString &typeName); + using VersionParser + = std::function; + void setVersionFlagsAndParser(const QStringList &flags, const VersionParser &parser); + void clearVersion(); + void setTargetAbiNoSignal(const Abi &abi); void setTargetAbiKey(const Utils::Key &abiKey); @@ -211,6 +219,7 @@ private: friend class Internal::ToolchainSettingsAccessor; friend class ToolchainFactory; + friend class Internal::ToolchainPrivate; }; using Toolchains = QList; diff --git a/src/plugins/projectexplorer/toolchainmanager.cpp b/src/plugins/projectexplorer/toolchainmanager.cpp index 89d952a4f9f..91379fffa95 100644 --- a/src/plugins/projectexplorer/toolchainmanager.cpp +++ b/src/plugins/projectexplorer/toolchainmanager.cpp @@ -334,8 +334,8 @@ void ToolchainManager::addBadToolchain(const Utils::FilePath &toolchain) // Use as a tie-breaker for toolchains that match the strong requirements like toolchain type // and ABI. -// For toolchains with the same priority, gives precedence to icecc and ccache -// and otherwise simply chooses the one with the shortest path. +// For toolchains with the same priority, gives precedence to icecc and ccache, +// prefers the higher version and otherwise simply chooses the one with the shortest path. bool ToolchainManager::isBetterToolchain( const ToolchainBundle &bundle1, const ToolchainBundle &bundle2) { @@ -346,18 +346,20 @@ bool ToolchainManager::isBetterToolchain( if (priority1 < priority2) return false; - const QString path1 = bundle1.get(&Toolchain::compilerCommand).path(); - const QString path2 = bundle2.get(&Toolchain::compilerCommand).path(); + const FilePath path1 = bundle1.get(&Toolchain::compilerCommand); + const FilePath path2 = bundle2.get(&Toolchain::compilerCommand); + const QString pathString1 = path1.path(); + const QString pathString2 = path2.path(); - const bool b1IsIcecc = path1.contains("icecc"); - const bool b2IsIcecc = path2.contains("icecc"); + const bool b1IsIcecc = pathString1.contains("icecc"); + const bool b2IsIcecc = pathString2.contains("icecc"); if (b1IsIcecc) return !b2IsIcecc; if (b2IsIcecc) return false; - const bool b1IsCCache = path1.contains("ccache"); - const bool b2IsCcache = path2.contains("ccache"); + const bool b1IsCCache = pathString1.contains("ccache"); + const bool b2IsCcache = pathString2.contains("ccache"); if (b1IsCCache) return !b2IsCcache; if (b2IsCcache) @@ -383,7 +385,18 @@ bool ToolchainManager::isBetterToolchain( } } - return path1.size() < path2.size(); + if (!path1.needsDevice() && !path2.needsDevice()) { + const QVersionNumber v1 = bundle1.get(&Toolchain::version); + const QVersionNumber v2 = bundle2.get(&Toolchain::version); + if (!v1.isNull() && !v2.isNull()) { + if (v1 > v2) + return true; + if (v1 < v2) + return false; + } + } + + return pathString1.size() < pathString2.size(); } } // namespace ProjectExplorer