ProjectExplorer: Expose toolchain versions

... and use them as a possible criterion when comparing toolchains.

Change-Id: I6053cc5915edd1e146405410ff82ee660fd84c53
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Christian Kandeler
2024-09-04 17:31:01 +02:00
parent 7887f9435c
commit 2338325df1
10 changed files with 114 additions and 23 deletions

View File

@@ -31,7 +31,8 @@ public:
class Parameters class Parameters
{ {
public: public:
using OutputParser = std::function<std::optional<Data>(const QString &)>; using OutputParser = std::function<
std::optional<Data>(const QString & /* stdOut */, const QString & /* stdErr */)>;
using ErrorHandler = std::function<void(const Process &)>; using ErrorHandler = std::function<void(const Process &)>;
using Callback = std::function<void(const std::optional<Data> &)>; using Callback = std::function<void(const std::optional<Data> &)>;
@@ -133,7 +134,7 @@ inline std::optional<Data> DataFromProcess<Data>::handleProcessFinished(
std::optional<Data> data; std::optional<Data> data;
if (params.allowedResults.contains(process->result())) if (params.allowedResults.contains(process->result()))
data = params.parser(process->cleanedStdOut()); data = params.parser(process->cleanedStdOut(), process->cleanedStdErr());
else if (params.errorHandler) else if (params.errorHandler)
params.errorHandler(*process); params.errorHandler(*process);
QMutexLocker<QMutex> cacheLocker(&m_cacheMutex); QMutexLocker<QMutex> cacheLocker(&m_cacheMutex);

View File

@@ -107,9 +107,9 @@ bool BinaryVersionToolTipEventFilter::eventFilter(QObject *o, QEvent *e)
QTC_ASSERT(le, return false); QTC_ASSERT(le, return false);
const QString binary = le->text(); const QString binary = le->text();
DataFromProcess<QString>::Parameters params(CommandLine(FilePath::fromUserInput(binary), DataFromProcess<QString>::Parameters params(
m_arguments), CommandLine(FilePath::fromUserInput(binary), m_arguments),
[](const QString &output) { return output; }); [](const QString &output, const QString &) { return output; });
params.callback = [binary, self = QPointer(this), params.callback = [binary, self = QPointer(this),
le = QPointer(le)](const std::optional<QString> &version) { le = QPointer(le)](const std::optional<QString> &version) {
if (!self || !le) if (!self || !le)

View File

@@ -41,7 +41,8 @@ static QStringList queryClangTidyChecks(const FilePath &executable,
// abseil-duration-division // abseil-duration-division
// abseil-duration-factory-float // abseil-duration-factory-float
// ... // ...
static const auto parser = [](const QString &stdOut) -> std::optional<QStringList> { static const auto parser = [](const QString &stdOut,
const QString &) -> std::optional<QStringList> {
QString output = stdOut; QString output = stdOut;
QTextStream stream(&output); QTextStream stream(&output);
QString line = stream.readLine(); QString line = stream.readLine();
@@ -85,7 +86,8 @@ static ClazyChecks querySupportedClazyChecks(const FilePath &executablePath)
// ... // ...
// ] // ]
// } // }
static const auto parser = [](const QString &jsonOutput) -> std::optional<ClazyChecks> { static const auto parser = [](const QString &jsonOutput,
const QString &) -> std::optional<ClazyChecks> {
const QJsonDocument document = QJsonDocument::fromJson(jsonOutput.toUtf8()); const QJsonDocument document = QJsonDocument::fromJson(jsonOutput.toUtf8());
if (document.isNull()) if (document.isNull())
return {}; return {};
@@ -130,7 +132,8 @@ ClazyStandaloneInfo::ClazyStandaloneInfo(const FilePath &executablePath)
: defaultChecks(queryClangTidyChecks(executablePath, {})) // Yup, behaves as clang-tidy. : defaultChecks(queryClangTidyChecks(executablePath, {})) // Yup, behaves as clang-tidy.
, supportedChecks(querySupportedClazyChecks(executablePath)) , supportedChecks(querySupportedClazyChecks(executablePath))
{ {
static const auto parser = [](const QString &stdOut) -> std::optional<QVersionNumber> { static const auto parser = [](const QString &stdOut,
const QString &) -> std::optional<QVersionNumber> {
QString output = stdOut; QString output = stdOut;
QTextStream stream(&output); QTextStream stream(&output);
while (!stream.atEnd()) { while (!stream.atEnd()) {
@@ -157,7 +160,8 @@ static FilePath queryResourceDir(const FilePath &clangToolPath)
// lib/clang/10.0.1 // lib/clang/10.0.1
// Error while trying to load a compilation database: // Error while trying to load a compilation database:
// ... // ...
const auto parser = [&clangToolPath](const QString &stdOut) -> std::optional<FilePath> { const auto parser =
[&clangToolPath](const QString &stdOut, const QString &) -> std::optional<FilePath> {
QString output = stdOut; QString output = stdOut;
QTextStream stream(&output); QTextStream stream(&output);
const QString path = clangToolPath.parentDir().parentDir() const QString path = clangToolPath.parentDir().parentDir()
@@ -180,7 +184,7 @@ static FilePath queryResourceDir(const FilePath &clangToolPath)
QString queryVersion(const FilePath &clangToolPath, QueryFailMode failMode) QString queryVersion(const FilePath &clangToolPath, QueryFailMode failMode)
{ {
static const auto parser = [](const QString &stdOut) -> std::optional<QString> { static const auto parser = [](const QString &stdOut, const QString &) -> std::optional<QString> {
QString output = stdOut; QString output = stdOut;
QTextStream stream(&output); QTextStream stream(&output);
while (!stream.atEnd()) { while (!stream.atEnd()) {

View File

@@ -74,8 +74,8 @@ void GerritParameters::setPortFlagBySshType()
{ {
bool isPlink = false; bool isPlink = false;
if (!ssh.isEmpty()) { if (!ssh.isEmpty()) {
DataFromProcess<QString>::Parameters params({ssh, {"-V"}}, DataFromProcess<QString>::Parameters
[](const QString &output) { return output; }); params({ssh, {"-V"}}, [](const QString &output, const QString &) { return output; });
using namespace std::chrono_literals; using namespace std::chrono_literals;
params.timeout = 1s; params.timeout = 1s;
if (const auto version = DataFromProcess<QString>::getData(params)) if (const auto version = DataFromProcess<QString>::getData(params))

View File

@@ -390,6 +390,10 @@ GccToolchain::GccToolchain(Id typeId, SubType subType)
setTypeDisplayName(Tr::tr("Clang")); setTypeDisplayName(Tr::tr("Clang"));
syncAutodetectedWithParentToolchains(); syncAutodetectedWithParentToolchains();
} }
setVersionFlagsAndParser({"-dumpversion"}, [](const QString &output, const QString &) {
return QVersionNumber::fromString(output.trimmed());
});
} }
GccToolchain::~GccToolchain() GccToolchain::~GccToolchain()

View File

@@ -852,6 +852,17 @@ MsvcToolchain::MsvcToolchain(Utils::Id typeId)
setTypeDisplayName(Tr::tr("MSVC")); setTypeDisplayName(Tr::tr("MSVC"));
addToAvailableMsvcToolchains(this); addToAvailableMsvcToolchains(this);
setTargetAbiKey(KEY_ROOT "SupportedAbi"); 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) void MsvcToolchain::inferWarningsForLevel(int warningLevel, WarningFlags &flags)
@@ -1677,6 +1688,19 @@ ClangClToolchain::ClangClToolchain()
{ {
setDisplayName("clang-cl"); setDisplayName("clang-cl");
setTypeDisplayName(Tr::tr("Clang")); 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 bool ClangClToolchain::isValid() const
@@ -2251,7 +2275,7 @@ ClangClInfo ClangClInfo::getInfo(const FilePath &filePath)
{ {
QTC_ASSERT(!filePath.isEmpty(), return {}); QTC_ASSERT(!filePath.isEmpty(), return {});
static const auto parser = [](const QString &stdOut) { static const auto parser = [](const QString &stdOut, const QString &) {
ClangClInfo info; ClangClInfo info;
const QRegularExpressionMatch versionMatch const QRegularExpressionMatch versionMatch
= QRegularExpression("clang version (\\d+(\\.\\d+)+)").match(stdOut); = QRegularExpression("clang version (\\d+(\\.\\d+)+)").match(stdOut);

View File

@@ -144,7 +144,7 @@ public:
const QList<MsvcToolchain *> &msvcToolchains() const; const QList<MsvcToolchain *> &msvcToolchains() const;
Utils::FilePath clangPath() const { return m_clangPath; } 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, Macros msvcPredefinedMacros(const QStringList &cxxflags,
const Utils::Environment &env) const override; const Utils::Environment &env) const override;

View File

@@ -12,6 +12,7 @@
#include "task.h" #include "task.h"
#include <utils/async.h> #include <utils/async.h>
#include <utils/datafromprocess.h>
#include <utils/pathchooser.h> #include <utils/pathchooser.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
@@ -77,6 +78,11 @@ public:
Toolchain::MacrosCache m_predefinedMacrosCache; Toolchain::MacrosCache m_predefinedMacrosCache;
Toolchain::HeaderPathsCache m_headerPathsCache; Toolchain::HeaderPathsCache m_headerPathsCache;
QStringList m_versionFlags;
Toolchain::VersionParser m_versionParser;
std::optional<QVersionNumber> m_version;
std::optional<bool> m_isValid; std::optional<bool> m_isValid;
bool m_hasError = false; bool m_hasError = false;
}; };
@@ -353,6 +359,24 @@ void Toolchain::setTargetAbi(const Abi &abi)
toolChainUpdated(); toolChainUpdated();
} }
QVersionNumber Toolchain::version() const
{
if (d->m_version)
return *d->m_version;
if (!d->m_versionParser || compilerCommand().isEmpty())
return {};
using DFP = DataFromProcess<QVersionNumber>;
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) void Toolchain::setTargetAbiNoSignal(const Abi &abi)
{ {
d->m_targetAbi = abi; d->m_targetAbi = abi;
@@ -375,6 +399,7 @@ void Toolchain::setCompilerCommand(const FilePath &command)
if (command == d->m_compilerCommand) if (command == d->m_compilerCommand)
return; return;
d->m_compilerCommand = command; d->m_compilerCommand = command;
clearVersion();
toolChainUpdated(); toolChainUpdated();
} }
@@ -393,6 +418,17 @@ void Toolchain::setTypeDisplayName(const QString &typeName)
d->m_typeDisplayName = 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. Used by the tool chain manager to load user-generated tool chains.

View File

@@ -19,6 +19,7 @@
#include <QDateTime> #include <QDateTime>
#include <QSet> #include <QSet>
#include <QVersionNumber>
#include <functional> #include <functional>
#include <memory> #include <memory>
@@ -99,6 +100,8 @@ public:
Abi targetAbi() const; Abi targetAbi() const;
void setTargetAbi(const Abi &abi); void setTargetAbi(const Abi &abi);
QVersionNumber version() const;
virtual ProjectExplorer::Abis supportedAbis() const; virtual ProjectExplorer::Abis supportedAbis() const;
virtual QString originalTargetTriple() const { return {}; } virtual QString originalTargetTriple() const { return {}; }
virtual QStringList extraCodeModelFlags() const { return {}; } virtual QStringList extraCodeModelFlags() const { return {}; }
@@ -179,6 +182,11 @@ protected:
void setTypeDisplayName(const QString &typeName); void setTypeDisplayName(const QString &typeName);
using VersionParser
= std::function<QVersionNumber(const QString & /* stdOut */, const QString & /* stdErr */)>;
void setVersionFlagsAndParser(const QStringList &flags, const VersionParser &parser);
void clearVersion();
void setTargetAbiNoSignal(const Abi &abi); void setTargetAbiNoSignal(const Abi &abi);
void setTargetAbiKey(const Utils::Key &abiKey); void setTargetAbiKey(const Utils::Key &abiKey);
@@ -211,6 +219,7 @@ private:
friend class Internal::ToolchainSettingsAccessor; friend class Internal::ToolchainSettingsAccessor;
friend class ToolchainFactory; friend class ToolchainFactory;
friend class Internal::ToolchainPrivate;
}; };
using Toolchains = QList<Toolchain *>; using Toolchains = QList<Toolchain *>;

View File

@@ -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 // Use as a tie-breaker for toolchains that match the strong requirements like toolchain type
// and ABI. // and ABI.
// For toolchains with the same priority, gives precedence to icecc and ccache // For toolchains with the same priority, gives precedence to icecc and ccache,
// and otherwise simply chooses the one with the shortest path. // prefers the higher version and otherwise simply chooses the one with the shortest path.
bool ToolchainManager::isBetterToolchain( bool ToolchainManager::isBetterToolchain(
const ToolchainBundle &bundle1, const ToolchainBundle &bundle2) const ToolchainBundle &bundle1, const ToolchainBundle &bundle2)
{ {
@@ -346,18 +346,20 @@ bool ToolchainManager::isBetterToolchain(
if (priority1 < priority2) if (priority1 < priority2)
return false; return false;
const QString path1 = bundle1.get(&Toolchain::compilerCommand).path(); const FilePath path1 = bundle1.get(&Toolchain::compilerCommand);
const QString path2 = bundle2.get(&Toolchain::compilerCommand).path(); 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 b1IsIcecc = pathString1.contains("icecc");
const bool b2IsIcecc = path2.contains("icecc"); const bool b2IsIcecc = pathString2.contains("icecc");
if (b1IsIcecc) if (b1IsIcecc)
return !b2IsIcecc; return !b2IsIcecc;
if (b2IsIcecc) if (b2IsIcecc)
return false; return false;
const bool b1IsCCache = path1.contains("ccache"); const bool b1IsCCache = pathString1.contains("ccache");
const bool b2IsCcache = path2.contains("ccache"); const bool b2IsCcache = pathString2.contains("ccache");
if (b1IsCCache) if (b1IsCCache)
return !b2IsCcache; return !b2IsCcache;
if (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 } // namespace ProjectExplorer