Utils: Add convenience class for parsing and caching process output

... and base the existing uses of this pattern on it.

Change-Id: I0eaf7535b68ff8c3e8e1c923ce08e63896cc83f7
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Christian Kandeler
2024-02-16 16:47:43 +01:00
parent 7c5a0e6bb2
commit 737bf48a0c
11 changed files with 238 additions and 160 deletions

View File

@@ -31,6 +31,7 @@ add_qtc_library(Utils
completingtextedit.cpp completingtextedit.h completingtextedit.cpp completingtextedit.h
cpplanguage_details.h cpplanguage_details.h
crumblepath.cpp crumblepath.h crumblepath.cpp crumblepath.h
datafromprocess.h
delegates.cpp delegates.h delegates.cpp delegates.h
detailsbutton.cpp detailsbutton.h detailsbutton.cpp detailsbutton.h
detailswidget.cpp detailswidget.h detailswidget.cpp detailswidget.h

View File

@@ -22,7 +22,9 @@
#include <memory> #include <memory>
#include <optional> #include <optional>
#include <tuple>
#include <type_traits> #include <type_traits>
#include <utility>
namespace Utils namespace Utils
{ {
@@ -1532,6 +1534,17 @@ void addToHash(QHash<Key, T> *result, const QHash<Key, T> &additionalContents)
result->insert(additionalContents); result->insert(additionalContents);
} }
template <typename Tuple, std::size_t... I>
static std::size_t tupleHashHelper(uint seed, const Tuple &tuple, std::index_sequence<I...>)
{
return qHashMulti(seed, (std::get<I>(tuple), ...));
}
template<typename... T> std::size_t qHash(const std::tuple<T...> &tuple, uint seed = 0)
{
return tupleHashHelper(seed, tuple, std::make_index_sequence<sizeof...(T)>());
}
// Workaround for missing information from QSet::insert() // Workaround for missing information from QSet::insert()
// Return type could be a pair like for std::set, but we never use the iterator anyway. // Return type could be a pair like for std::set, but we never use the iterator anyway.
template<typename T, typename U> [[nodiscard]] bool insert(QSet<T> &s, const U &v) template<typename T, typename U> [[nodiscard]] bool insert(QSet<T> &s, const U &v)

View File

@@ -0,0 +1,95 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include "algorithm.h"
#include "commandline.h"
#include "environment.h"
#include "filepath.h"
#include "process.h"
#include <QDateTime>
#include <QHash>
#include <QMutex>
#include <QMutexLocker>
#include <chrono>
#include <functional>
#include <optional>
#include <utility>
namespace Utils {
// Use this facility for cached retrieval of data from a tool that always returns the same
// output for the same parameters and is side effect free.
// A prime example is version info via a --version switch.
template<typename Data> class DataFromProcess
{
public:
class Parameters
{
public:
using OutputParser = std::function<std::optional<Data>(const QString &)>;
using ErrorHandler = std::function<void(const Process &)>;
Parameters(const CommandLine &cmdLine, const OutputParser &parser)
: commandLine(cmdLine)
, parser(parser)
{}
CommandLine commandLine;
Environment environment = Environment::systemEnvironment();
std::chrono::seconds timeout = std::chrono::seconds(10);
OutputParser parser;
ErrorHandler errorHandler;
QList<ProcessResult> allowedResults{ProcessResult::FinishedWithSuccess};
};
static std::optional<Data> getData(const Parameters &params);
// TODO: async variant.
private:
using Key = std::tuple<FilePath, QStringList, QString>;
using Value = std::pair<std::optional<Data>, QDateTime>;
static inline QHash<Key, Value> m_cache;
static inline QMutex m_cacheMutex;
};
template<typename Data>
inline std::optional<Data> DataFromProcess<Data>::getData(const Parameters &params)
{
if (params.commandLine.executable().isEmpty())
return {};
const auto key = std::make_tuple(params.commandLine.executable(),
params.environment.toStringList(),
params.commandLine.arguments());
const QDateTime exeTimestamp = params.commandLine.executable().lastModified();
{
QMutexLocker<QMutex> cacheLocker(&m_cacheMutex);
const auto it = m_cache.constFind(key);
if (it != m_cache.constEnd() && it.value().second == exeTimestamp)
return it.value().first;
}
Process outputRetriever;
outputRetriever.setCommand(params.commandLine);
outputRetriever.runBlocking(params.timeout);
// Do not store into cache: The next call might succeed.
if (outputRetriever.result() == ProcessResult::Canceled)
return {};
std::optional<Data> data;
if (params.allowedResults.contains(outputRetriever.result()))
data = params.parser(outputRetriever.cleanedStdOut());
else if (params.errorHandler)
params.errorHandler(outputRetriever);
QMutexLocker<QMutex> cacheLocker(&m_cacheMutex);
m_cache.insert(key, std::make_pair(data, exeTimestamp));
return data;
}
} // namespace Utils

View File

@@ -5,6 +5,7 @@
#include "async.h" #include "async.h"
#include "commandline.h" #include "commandline.h"
#include "datafromprocess.h"
#include "environment.h" #include "environment.h"
#include "fileutils.h" #include "fileutils.h"
#include "guard.h" #include "guard.h"
@@ -130,15 +131,12 @@ bool BinaryVersionToolTipEventFilter::eventFilter(QObject *o, QEvent *e)
QString BinaryVersionToolTipEventFilter::toolVersion(const CommandLine &cmd) QString BinaryVersionToolTipEventFilter::toolVersion(const CommandLine &cmd)
{ {
if (cmd.executable().isEmpty()) DataFromProcess<QString>::Parameters params(cmd, [](const QString &output) { return output; });
return QString();
Process proc;
proc.setCommand(cmd);
using namespace std::chrono_literals; using namespace std::chrono_literals;
proc.runBlocking(1s); params.timeout = 1s;
if (proc.result() != ProcessResult::FinishedWithSuccess) if (const auto version = DataFromProcess<QString>::getData(params))
return QString(); return *version;
return proc.allOutput(); return {};
} }
// Extends BinaryVersionToolTipEventFilter to prepend the existing pathchooser // Extends BinaryVersionToolTipEventFilter to prepend the existing pathchooser

View File

@@ -77,6 +77,7 @@ QtcLibrary {
"cpplanguage_details.h", "cpplanguage_details.h",
"crumblepath.cpp", "crumblepath.cpp",
"crumblepath.h", "crumblepath.h",
"datafromprocess.h",
"delegates.cpp", "delegates.cpp",
"delegates.h", "delegates.h",
"detailsbutton.cpp", "detailsbutton.cpp",

View File

@@ -230,7 +230,7 @@ VersionAndSuffix ClangToolsSettings::clangTidyVersion()
QVersionNumber ClangToolsSettings::clazyVersion() QVersionNumber ClangToolsSettings::clazyVersion()
{ {
return ClazyStandaloneInfo::getInfo(Internal::toolExecutable(ClangToolType::Clazy)).version; return ClazyStandaloneInfo(Internal::toolExecutable(ClangToolType::Clazy)).version;
} }
} // namespace Internal } // namespace Internal

View File

@@ -1263,7 +1263,7 @@ QString removeClangTidyCheck(const QString &checks, const QString &check)
QString removeClazyCheck(const QString &checks, const QString &check) QString removeClazyCheck(const QString &checks, const QString &check)
{ {
const ClazyStandaloneInfo clazyInfo = ClazyStandaloneInfo::getInfo(toolExecutable(ClangToolType::Clazy)); const ClazyStandaloneInfo clazyInfo = ClazyStandaloneInfo(toolExecutable(ClangToolType::Clazy));
ClazyChecksTreeModel model(clazyInfo.supportedChecks); ClazyChecksTreeModel model(clazyInfo.supportedChecks);
model.enableChecks(checks.split(',', Qt::SkipEmptyParts)); model.enableChecks(checks.split(',', Qt::SkipEmptyParts));
const QModelIndex index = model.indexForName(check.mid(QString("clazy-").length())); const QModelIndex index = model.indexForName(check.mid(QString("clazy-").length()));
@@ -1314,7 +1314,7 @@ void disableChecks(const QList<Diagnostic> &diagnostics)
if (config.clazyMode() == ClangDiagnosticConfig::ClazyMode::UseDefaultChecks) { if (config.clazyMode() == ClangDiagnosticConfig::ClazyMode::UseDefaultChecks) {
config.setClazyMode(ClangDiagnosticConfig::ClazyMode::UseCustomChecks); config.setClazyMode(ClangDiagnosticConfig::ClazyMode::UseCustomChecks);
const ClazyStandaloneInfo clazyInfo const ClazyStandaloneInfo clazyInfo
= ClazyStandaloneInfo::getInfo(toolExecutable(ClangToolType::Clazy)); = ClazyStandaloneInfo(toolExecutable(ClangToolType::Clazy));
config.setChecks(ClangToolType::Clazy, clazyInfo.defaultChecks.join(',')); config.setChecks(ClangToolType::Clazy, clazyInfo.defaultChecks.join(','));
} }
config.setChecks(ClangToolType::Clazy, config.setChecks(ClangToolType::Clazy,

View File

@@ -6,6 +6,7 @@
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h> #include <coreplugin/messagemanager.h>
#include <utils/datafromprocess.h>
#include <utils/environment.h> #include <utils/environment.h>
#include <utils/process.h> #include <utils/process.h>
@@ -21,27 +22,10 @@ using namespace Utils;
namespace ClangTools { namespace ClangTools {
namespace Internal { namespace Internal {
static QString runExecutable(const Utils::CommandLine &commandLine, QueryFailMode queryFailMode) static void handleProcessError(const Process &p)
{ {
if (commandLine.executable().isEmpty() || !commandLine.executable().toFileInfo().isExecutable()) Core::MessageManager::writeFlashing(p.exitMessage());
return {}; Core::MessageManager::writeFlashing(QString::fromUtf8(p.allRawOutput()));
Process cpp;
Environment env = Environment::systemEnvironment();
env.setupEnglishOutput();
cpp.setEnvironment(env);
cpp.setCommand(commandLine);
cpp.runBlocking();
if (cpp.result() != ProcessResult::FinishedWithSuccess
&& (queryFailMode == QueryFailMode::Noisy
|| cpp.result() != ProcessResult::FinishedWithError)) {
Core::MessageManager::writeFlashing(cpp.exitMessage());
Core::MessageManager::writeFlashing(QString::fromUtf8(cpp.allRawOutput()));
return {};
}
return cpp.cleanedStdOut();
} }
static QStringList queryClangTidyChecks(const FilePath &executable, static QStringList queryClangTidyChecks(const FilePath &executable,
@@ -51,47 +35,37 @@ static QStringList queryClangTidyChecks(const FilePath &executable,
if (!checksArgument.isEmpty()) if (!checksArgument.isEmpty())
arguments.prepend(checksArgument); arguments.prepend(checksArgument);
const CommandLine commandLine(executable, arguments);
QString output = runExecutable(commandLine, QueryFailMode::Noisy);
if (output.isEmpty())
return {};
// Expected output is (clang-tidy 8.0): // Expected output is (clang-tidy 8.0):
// Enabled checks: // Enabled checks:
// abseil-duration-comparison // abseil-duration-comparison
// abseil-duration-division // abseil-duration-division
// abseil-duration-factory-float // abseil-duration-factory-float
// ... // ...
static const auto parser = [](const QString &stdOut) -> std::optional<QStringList> {
QString output = stdOut;
QTextStream stream(&output); QTextStream stream(&output);
QString line = stream.readLine(); QString line = stream.readLine();
if (!line.startsWith("Enabled checks:")) if (!line.startsWith("Enabled checks:"))
return {}; return {};
QStringList checks; QStringList checks;
while (!stream.atEnd()) { while (!stream.atEnd()) {
const QString candidate = stream.readLine().trimmed(); const QString candidate = stream.readLine().trimmed();
if (!candidate.isEmpty()) if (!candidate.isEmpty())
checks << candidate; checks << candidate;
} }
return checks; return checks;
};
DataFromProcess<QStringList>::Parameters params({executable, arguments}, parser);
params.environment.setupEnglishOutput();
params.errorHandler = handleProcessError;
if (const auto checks = DataFromProcess<QStringList>::getData(params))
return *checks;
return {};
} }
static ClazyChecks querySupportedClazyChecks(const FilePath &executablePath) static ClazyChecks querySupportedClazyChecks(const FilePath &executablePath)
{ {
static const QString queryFlag = "-supported-checks-json";
QString jsonOutput = runExecutable(CommandLine(executablePath, {queryFlag}),
QueryFailMode::Noisy);
// Some clazy 1.6.x versions have a bug where they expect an argument after the
// option.
if (jsonOutput.isEmpty())
jsonOutput = runExecutable(CommandLine(executablePath, {queryFlag, "dummy"}),
QueryFailMode::Noisy);
if (jsonOutput.isEmpty())
return {};
// Expected output is (clazy-standalone 1.6): // Expected output is (clazy-standalone 1.6):
// { // {
// "available_categories" : ["readability", "qt4", "containers", ... ], // "available_categories" : ["readability", "qt4", "containers", ... ],
@@ -111,17 +85,14 @@ static ClazyChecks querySupportedClazyChecks(const FilePath &executablePath)
// ... // ...
// ] // ]
// } // }
static const auto parser = [](const QString &jsonOutput) -> std::optional<ClazyChecks> {
ClazyChecks infos;
const QJsonDocument document = QJsonDocument::fromJson(jsonOutput.toUtf8()); const QJsonDocument document = QJsonDocument::fromJson(jsonOutput.toUtf8());
if (document.isNull()) if (document.isNull())
return {}; return {};
const QJsonArray checksArray = document.object()["checks"].toArray(); const QJsonArray checksArray = document.object()["checks"].toArray();
ClazyChecks infos;
for (const QJsonValue &item: checksArray) { for (const QJsonValue &item: checksArray) {
const QJsonObject checkObject = item.toObject(); const QJsonObject checkObject = item.toObject();
ClazyCheck info; ClazyCheck info;
info.name = checkObject["name"].toString().trimmed(); info.name = checkObject["name"].toString().trimmed();
if (info.name.isEmpty()) if (info.name.isEmpty())
@@ -129,11 +100,26 @@ static ClazyChecks querySupportedClazyChecks(const FilePath &executablePath)
info.level = checkObject["level"].toInt(); info.level = checkObject["level"].toInt();
for (const QJsonValue &item : checkObject["categories"].toArray()) for (const QJsonValue &item : checkObject["categories"].toArray())
info.topics.append(item.toString().trimmed()); info.topics.append(item.toString().trimmed());
infos << info; infos << info;
} }
return infos; return infos;
};
static const QString queryFlag = "-supported-checks-json";
DataFromProcess<ClazyChecks>::Parameters params(CommandLine(executablePath, {queryFlag}),
parser);
params.environment.setupEnglishOutput();
params.errorHandler = handleProcessError;
auto checks = DataFromProcess<ClazyChecks>::getData(params);
if (!checks) {
// Some clazy 1.6.x versions have a bug where they expect an argument after the
// option.
params.commandLine = CommandLine(executablePath, {queryFlag, "dummy"});
checks = DataFromProcess<ClazyChecks>::getData(params);
}
if (checks)
return *checks;
return {};
} }
ClangTidyInfo::ClangTidyInfo(const FilePath &executablePath) ClangTidyInfo::ClangTidyInfo(const FilePath &executablePath)
@@ -141,54 +127,39 @@ ClangTidyInfo::ClangTidyInfo(const FilePath &executablePath)
, supportedChecks(queryClangTidyChecks(executablePath, "-checks=*")) , supportedChecks(queryClangTidyChecks(executablePath, "-checks=*"))
{} {}
ClazyStandaloneInfo ClazyStandaloneInfo::getInfo(const FilePath &executablePath)
{
const QDateTime timeStamp = executablePath.lastModified();
const auto it = cache.find(executablePath);
if (it == cache.end()) {
const ClazyStandaloneInfo info(executablePath);
cache.insert(executablePath, {timeStamp, info});
return info;
}
if (it->first != timeStamp) {
it->first = timeStamp;
it->second = ClazyStandaloneInfo::getInfo(executablePath);
}
return it->second;
}
ClazyStandaloneInfo::ClazyStandaloneInfo(const FilePath &executablePath) 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))
{ {
QString output = runExecutable({executablePath, {"--version"}}, QueryFailMode::Silent); static const auto parser = [](const QString &stdOut) -> std::optional<QVersionNumber> {
QString output = stdOut;
QTextStream stream(&output); QTextStream stream(&output);
while (!stream.atEnd()) { while (!stream.atEnd()) {
// It's just "clazy version " right now, but let's be prepared for someone adding a colon // It's just "clazy version " right now, but let's be prepared for someone
// later on. // adding a colon later on.
static const QStringList versionPrefixes{"clazy version ", "clazy version: "}; static const QStringList versionPrefixes{"clazy version ", "clazy version: "};
const QString line = stream.readLine().simplified(); const QString line = stream.readLine().simplified();
for (const QString &prefix : versionPrefixes) { for (const QString &prefix : versionPrefixes) {
if (line.startsWith(prefix)) { if (line.startsWith(prefix))
version = QVersionNumber::fromString(line.mid(prefix.length())); return QVersionNumber::fromString(line.mid(prefix.length()));
break;
}
} }
} }
return {};
};
DataFromProcess<QVersionNumber>::Parameters params({{executablePath, {"--version"}}, parser});
params.environment.setupEnglishOutput();
if (const auto v = DataFromProcess<QVersionNumber>::getData(params))
version = *v;
} }
static FilePath queryResourceDir(const FilePath &clangToolPath) static FilePath queryResourceDir(const FilePath &clangToolPath)
{ {
QString output = runExecutable(CommandLine(clangToolPath, {"someFilePath", "--",
"-print-resource-dir"}),
QueryFailMode::Silent);
// Expected output is (clang-tidy 10): // Expected output is (clang-tidy 10):
// 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> {
// Parse QString output = stdOut;
QTextStream stream(&output); QTextStream stream(&output);
const QString path = clangToolPath.parentDir().parentDir() const QString path = clangToolPath.parentDir().parentDir()
.pathAppended(stream.readLine()).toString(); .pathAppended(stream.readLine()).toString();
@@ -196,11 +167,22 @@ static FilePath queryResourceDir(const FilePath &clangToolPath)
if (filePath.exists()) if (filePath.exists())
return filePath; return filePath;
return {}; return {};
};
DataFromProcess<FilePath>::Parameters params({clangToolPath,
{"someFilePath", "--", "-print-resource-dir"}},
parser);
params.environment.setupEnglishOutput();
params.allowedResults << ProcessResult::FinishedWithError;
if (const auto filePath = DataFromProcess<FilePath>::getData(params))
return *filePath;
return {};
} }
QString queryVersion(const FilePath &clangToolPath, QueryFailMode failMode) QString queryVersion(const FilePath &clangToolPath, QueryFailMode failMode)
{ {
QString output = runExecutable(CommandLine(clangToolPath, {"--version"}), failMode); static const auto parser = [](const QString &stdOut) -> std::optional<QString> {
QString output = stdOut;
QTextStream stream(&output); QTextStream stream(&output);
while (!stream.atEnd()) { while (!stream.atEnd()) {
static const QStringList versionPrefixes{"LLVM version ", "clang version: "}; static const QStringList versionPrefixes{"LLVM version ", "clang version: "};
@@ -212,6 +194,14 @@ QString queryVersion(const FilePath &clangToolPath, QueryFailMode failMode)
} }
} }
return {}; return {};
};
DataFromProcess<QString>::Parameters params({clangToolPath, {"--version"}}, parser);
params.environment.setupEnglishOutput();
if (failMode == QueryFailMode::Noisy)
params.errorHandler = handleProcessError;
if (const auto version = DataFromProcess<QString>::getData(params))
return *version;
return {};
} }
static QPair<FilePath, QString> clangIncludeDirAndVersion(const FilePath &clangToolPath) static QPair<FilePath, QString> clangIncludeDirAndVersion(const FilePath &clangToolPath)
@@ -233,7 +223,5 @@ QPair<FilePath, QString> getClangIncludeDirAndVersion(const FilePath &clangToolP
return it.value(); return it.value();
} }
QHash<Utils::FilePath, QPair<QDateTime, ClazyStandaloneInfo>> ClazyStandaloneInfo::cache;
} // namespace Internal } // namespace Internal
} // namespace ClangTools } // namespace ClangTools

View File

@@ -5,8 +5,6 @@
#include <utils/filepath.h> #include <utils/filepath.h>
#include <QDateTime>
#include <QHash>
#include <QPair> #include <QPair>
#include <QStringList> #include <QStringList>
#include <QVector> #include <QVector>
@@ -40,16 +38,11 @@ using ClazyChecks = QVector<ClazyCheck>;
class ClazyStandaloneInfo class ClazyStandaloneInfo
{ {
public: public:
static ClazyStandaloneInfo getInfo(const Utils::FilePath &executablePath); ClazyStandaloneInfo(const Utils::FilePath &executablePath);
QVersionNumber version; QVersionNumber version;
QStringList defaultChecks; QStringList defaultChecks;
ClazyChecks supportedChecks; ClazyChecks supportedChecks;
private:
ClazyStandaloneInfo(const Utils::FilePath &executablePath);
static QHash<Utils::FilePath, QPair<QDateTime, ClazyStandaloneInfo>> cache;
}; };
} // namespace Internal } // namespace Internal

View File

@@ -85,7 +85,7 @@ static ClangDiagnosticConfigsWidget *createEditWidget(const ClangDiagnosticConfi
return new DiagnosticConfigsWidget(configs, return new DiagnosticConfigsWidget(configs,
configToSelect, configToSelect,
ClangTidyInfo(clangTidyPath), ClangTidyInfo(clangTidyPath),
ClazyStandaloneInfo::getInfo(clazyStandalonePath)); ClazyStandaloneInfo(clazyStandalonePath));
} }
void RunSettingsWidget::fromSettings(const RunSettings &s) void RunSettingsWidget::fromSettings(const RunSettings &s)

View File

@@ -17,6 +17,7 @@
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/async.h> #include <utils/async.h>
#include <utils/datafromprocess.h>
#include <utils/environment.h> #include <utils/environment.h>
#include <utils/hostosinfo.h> #include <utils/hostosinfo.h>
#include <utils/pathchooser.h> #include <utils/pathchooser.h>
@@ -42,8 +43,6 @@
#include <QFormLayout> #include <QFormLayout>
#include <QLabel> #include <QLabel>
#include <utility>
using namespace Utils; using namespace Utils;
using namespace std::chrono_literals; using namespace std::chrono_literals;
@@ -1580,7 +1579,6 @@ private:
FilePath m_filePath; FilePath m_filePath;
QVersionNumber m_version; QVersionNumber m_version;
Abi m_defaultAbi; Abi m_defaultAbi;
static inline QHash<FilePath, std::pair<ClangClInfo, QDateTime>> m_cache;
}; };
static const MsvcToolchain *selectMsvcToolChain(const QString &displayedVarsBat, static const MsvcToolchain *selectMsvcToolChain(const QString &displayedVarsBat,
@@ -2280,18 +2278,8 @@ ClangClInfo ClangClInfo::getInfo(const FilePath &filePath)
{ {
QTC_ASSERT(!filePath.isEmpty(), return {}); QTC_ASSERT(!filePath.isEmpty(), return {});
auto &entry = m_cache[filePath]; static const auto parser = [](const QString &stdOut) {
ClangClInfo &info = entry.first; ClangClInfo info;
const QDateTime lastModified = filePath.lastModified();
if (entry.second == lastModified)
return info;
entry.second = lastModified;
Process clangClProcess;
clangClProcess.setCommand({filePath, {"--version"}});
clangClProcess.runBlocking();
if (clangClProcess.result() == ProcessResult::FinishedWithSuccess) {
const QString stdOut = clangClProcess.cleanedStdOut();
const QRegularExpressionMatch versionMatch const QRegularExpressionMatch versionMatch
= QRegularExpression("clang version (\\d+(\\.\\d+)+)").match(stdOut); = QRegularExpression("clang version (\\d+(\\.\\d+)+)").match(stdOut);
if (versionMatch.hasMatch()) if (versionMatch.hasMatch())
@@ -2314,9 +2302,10 @@ ClangClInfo ClangClInfo::getInfo(const FilePath &filePath)
detectedAbi.wordWidth()); detectedAbi.wordWidth());
} }
} }
}
m_cache.insert(filePath, entry);
return info; return info;
};
const auto info = DataFromProcess<ClangClInfo>::getData({{filePath, {"--version"}}, parser});
return info ? *info : ClangClInfo();
} }
} // namespace ProjectExplorer::Internal } // namespace ProjectExplorer::Internal