forked from qt-creator/qt-creator
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:
@@ -31,6 +31,7 @@ add_qtc_library(Utils
|
||||
completingtextedit.cpp completingtextedit.h
|
||||
cpplanguage_details.h
|
||||
crumblepath.cpp crumblepath.h
|
||||
datafromprocess.h
|
||||
delegates.cpp delegates.h
|
||||
detailsbutton.cpp detailsbutton.h
|
||||
detailswidget.cpp detailswidget.h
|
||||
|
||||
@@ -22,7 +22,9 @@
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace Utils
|
||||
{
|
||||
@@ -1532,6 +1534,17 @@ void addToHash(QHash<Key, T> *result, const QHash<Key, T> &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()
|
||||
// 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)
|
||||
|
||||
95
src/libs/utils/datafromprocess.h
Normal file
95
src/libs/utils/datafromprocess.h
Normal 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 ¶ms);
|
||||
|
||||
// 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 ¶ms)
|
||||
{
|
||||
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
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "async.h"
|
||||
#include "commandline.h"
|
||||
#include "datafromprocess.h"
|
||||
#include "environment.h"
|
||||
#include "fileutils.h"
|
||||
#include "guard.h"
|
||||
@@ -130,15 +131,12 @@ bool BinaryVersionToolTipEventFilter::eventFilter(QObject *o, QEvent *e)
|
||||
|
||||
QString BinaryVersionToolTipEventFilter::toolVersion(const CommandLine &cmd)
|
||||
{
|
||||
if (cmd.executable().isEmpty())
|
||||
return QString();
|
||||
Process proc;
|
||||
proc.setCommand(cmd);
|
||||
DataFromProcess<QString>::Parameters params(cmd, [](const QString &output) { return output; });
|
||||
using namespace std::chrono_literals;
|
||||
proc.runBlocking(1s);
|
||||
if (proc.result() != ProcessResult::FinishedWithSuccess)
|
||||
return QString();
|
||||
return proc.allOutput();
|
||||
params.timeout = 1s;
|
||||
if (const auto version = DataFromProcess<QString>::getData(params))
|
||||
return *version;
|
||||
return {};
|
||||
}
|
||||
|
||||
// Extends BinaryVersionToolTipEventFilter to prepend the existing pathchooser
|
||||
|
||||
@@ -77,6 +77,7 @@ QtcLibrary {
|
||||
"cpplanguage_details.h",
|
||||
"crumblepath.cpp",
|
||||
"crumblepath.h",
|
||||
"datafromprocess.h",
|
||||
"delegates.cpp",
|
||||
"delegates.h",
|
||||
"detailsbutton.cpp",
|
||||
|
||||
@@ -230,7 +230,7 @@ VersionAndSuffix ClangToolsSettings::clangTidyVersion()
|
||||
|
||||
QVersionNumber ClangToolsSettings::clazyVersion()
|
||||
{
|
||||
return ClazyStandaloneInfo::getInfo(Internal::toolExecutable(ClangToolType::Clazy)).version;
|
||||
return ClazyStandaloneInfo(Internal::toolExecutable(ClangToolType::Clazy)).version;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
@@ -1263,7 +1263,7 @@ QString removeClangTidyCheck(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);
|
||||
model.enableChecks(checks.split(',', Qt::SkipEmptyParts));
|
||||
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) {
|
||||
config.setClazyMode(ClangDiagnosticConfig::ClazyMode::UseCustomChecks);
|
||||
const ClazyStandaloneInfo clazyInfo
|
||||
= ClazyStandaloneInfo::getInfo(toolExecutable(ClangToolType::Clazy));
|
||||
= ClazyStandaloneInfo(toolExecutable(ClangToolType::Clazy));
|
||||
config.setChecks(ClangToolType::Clazy, clazyInfo.defaultChecks.join(','));
|
||||
}
|
||||
config.setChecks(ClangToolType::Clazy,
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/messagemanager.h>
|
||||
|
||||
#include <utils/datafromprocess.h>
|
||||
#include <utils/environment.h>
|
||||
#include <utils/process.h>
|
||||
|
||||
@@ -21,27 +22,10 @@ using namespace Utils;
|
||||
namespace ClangTools {
|
||||
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())
|
||||
return {};
|
||||
|
||||
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();
|
||||
Core::MessageManager::writeFlashing(p.exitMessage());
|
||||
Core::MessageManager::writeFlashing(QString::fromUtf8(p.allRawOutput()));
|
||||
}
|
||||
|
||||
static QStringList queryClangTidyChecks(const FilePath &executable,
|
||||
@@ -51,47 +35,37 @@ static QStringList queryClangTidyChecks(const FilePath &executable,
|
||||
if (!checksArgument.isEmpty())
|
||||
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):
|
||||
// Enabled checks:
|
||||
// abseil-duration-comparison
|
||||
// abseil-duration-division
|
||||
// abseil-duration-factory-float
|
||||
// ...
|
||||
|
||||
static const auto parser = [](const QString &stdOut) -> std::optional<QStringList> {
|
||||
QString output = stdOut;
|
||||
QTextStream stream(&output);
|
||||
QString line = stream.readLine();
|
||||
if (!line.startsWith("Enabled checks:"))
|
||||
return {};
|
||||
|
||||
QStringList checks;
|
||||
while (!stream.atEnd()) {
|
||||
const QString candidate = stream.readLine().trimmed();
|
||||
if (!candidate.isEmpty())
|
||||
checks << candidate;
|
||||
}
|
||||
|
||||
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 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):
|
||||
// {
|
||||
// "available_categories" : ["readability", "qt4", "containers", ... ],
|
||||
@@ -111,17 +85,14 @@ static ClazyChecks querySupportedClazyChecks(const FilePath &executablePath)
|
||||
// ...
|
||||
// ]
|
||||
// }
|
||||
|
||||
ClazyChecks infos;
|
||||
|
||||
static const auto parser = [](const QString &jsonOutput) -> std::optional<ClazyChecks> {
|
||||
const QJsonDocument document = QJsonDocument::fromJson(jsonOutput.toUtf8());
|
||||
if (document.isNull())
|
||||
return {};
|
||||
const QJsonArray checksArray = document.object()["checks"].toArray();
|
||||
|
||||
ClazyChecks infos;
|
||||
for (const QJsonValue &item: checksArray) {
|
||||
const QJsonObject checkObject = item.toObject();
|
||||
|
||||
ClazyCheck info;
|
||||
info.name = checkObject["name"].toString().trimmed();
|
||||
if (info.name.isEmpty())
|
||||
@@ -129,11 +100,26 @@ static ClazyChecks querySupportedClazyChecks(const FilePath &executablePath)
|
||||
info.level = checkObject["level"].toInt();
|
||||
for (const QJsonValue &item : checkObject["categories"].toArray())
|
||||
info.topics.append(item.toString().trimmed());
|
||||
|
||||
infos << info;
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -141,54 +127,39 @@ ClangTidyInfo::ClangTidyInfo(const FilePath &executablePath)
|
||||
, 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)
|
||||
: defaultChecks(queryClangTidyChecks(executablePath, {})) // Yup, behaves as clang-tidy.
|
||||
, 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);
|
||||
while (!stream.atEnd()) {
|
||||
// It's just "clazy version " right now, but let's be prepared for someone adding a colon
|
||||
// later on.
|
||||
// It's just "clazy version " right now, but let's be prepared for someone
|
||||
// adding a colon later on.
|
||||
static const QStringList versionPrefixes{"clazy version ", "clazy version: "};
|
||||
const QString line = stream.readLine().simplified();
|
||||
for (const QString &prefix : versionPrefixes) {
|
||||
if (line.startsWith(prefix)) {
|
||||
version = QVersionNumber::fromString(line.mid(prefix.length()));
|
||||
break;
|
||||
}
|
||||
if (line.startsWith(prefix))
|
||||
return QVersionNumber::fromString(line.mid(prefix.length()));
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
QString output = runExecutable(CommandLine(clangToolPath, {"someFilePath", "--",
|
||||
"-print-resource-dir"}),
|
||||
QueryFailMode::Silent);
|
||||
|
||||
// Expected output is (clang-tidy 10):
|
||||
// lib/clang/10.0.1
|
||||
// Error while trying to load a compilation database:
|
||||
// ...
|
||||
|
||||
// Parse
|
||||
const auto parser = [&clangToolPath](const QString &stdOut) -> std::optional<FilePath> {
|
||||
QString output = stdOut;
|
||||
QTextStream stream(&output);
|
||||
const QString path = clangToolPath.parentDir().parentDir()
|
||||
.pathAppended(stream.readLine()).toString();
|
||||
@@ -196,11 +167,22 @@ static FilePath queryResourceDir(const FilePath &clangToolPath)
|
||||
if (filePath.exists())
|
||||
return filePath;
|
||||
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 output = runExecutable(CommandLine(clangToolPath, {"--version"}), failMode);
|
||||
static const auto parser = [](const QString &stdOut) -> std::optional<QString> {
|
||||
QString output = stdOut;
|
||||
QTextStream stream(&output);
|
||||
while (!stream.atEnd()) {
|
||||
static const QStringList versionPrefixes{"LLVM version ", "clang version: "};
|
||||
@@ -212,6 +194,14 @@ QString queryVersion(const FilePath &clangToolPath, QueryFailMode failMode)
|
||||
}
|
||||
}
|
||||
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)
|
||||
@@ -233,7 +223,5 @@ QPair<FilePath, QString> getClangIncludeDirAndVersion(const FilePath &clangToolP
|
||||
return it.value();
|
||||
}
|
||||
|
||||
QHash<Utils::FilePath, QPair<QDateTime, ClazyStandaloneInfo>> ClazyStandaloneInfo::cache;
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ClangTools
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
|
||||
#include <utils/filepath.h>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QHash>
|
||||
#include <QPair>
|
||||
#include <QStringList>
|
||||
#include <QVector>
|
||||
@@ -40,16 +38,11 @@ using ClazyChecks = QVector<ClazyCheck>;
|
||||
class ClazyStandaloneInfo
|
||||
{
|
||||
public:
|
||||
static ClazyStandaloneInfo getInfo(const Utils::FilePath &executablePath);
|
||||
ClazyStandaloneInfo(const Utils::FilePath &executablePath);
|
||||
|
||||
QVersionNumber version;
|
||||
QStringList defaultChecks;
|
||||
ClazyChecks supportedChecks;
|
||||
|
||||
private:
|
||||
ClazyStandaloneInfo(const Utils::FilePath &executablePath);
|
||||
|
||||
static QHash<Utils::FilePath, QPair<QDateTime, ClazyStandaloneInfo>> cache;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
@@ -85,7 +85,7 @@ static ClangDiagnosticConfigsWidget *createEditWidget(const ClangDiagnosticConfi
|
||||
return new DiagnosticConfigsWidget(configs,
|
||||
configToSelect,
|
||||
ClangTidyInfo(clangTidyPath),
|
||||
ClazyStandaloneInfo::getInfo(clazyStandalonePath));
|
||||
ClazyStandaloneInfo(clazyStandalonePath));
|
||||
}
|
||||
|
||||
void RunSettingsWidget::fromSettings(const RunSettings &s)
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/async.h>
|
||||
#include <utils/datafromprocess.h>
|
||||
#include <utils/environment.h>
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/pathchooser.h>
|
||||
@@ -42,8 +43,6 @@
|
||||
#include <QFormLayout>
|
||||
#include <QLabel>
|
||||
|
||||
#include <utility>
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
@@ -1580,7 +1579,6 @@ private:
|
||||
FilePath m_filePath;
|
||||
QVersionNumber m_version;
|
||||
Abi m_defaultAbi;
|
||||
static inline QHash<FilePath, std::pair<ClangClInfo, QDateTime>> m_cache;
|
||||
};
|
||||
|
||||
static const MsvcToolchain *selectMsvcToolChain(const QString &displayedVarsBat,
|
||||
@@ -2280,18 +2278,8 @@ ClangClInfo ClangClInfo::getInfo(const FilePath &filePath)
|
||||
{
|
||||
QTC_ASSERT(!filePath.isEmpty(), return {});
|
||||
|
||||
auto &entry = m_cache[filePath];
|
||||
ClangClInfo &info = entry.first;
|
||||
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();
|
||||
static const auto parser = [](const QString &stdOut) {
|
||||
ClangClInfo info;
|
||||
const QRegularExpressionMatch versionMatch
|
||||
= QRegularExpression("clang version (\\d+(\\.\\d+)+)").match(stdOut);
|
||||
if (versionMatch.hasMatch())
|
||||
@@ -2314,9 +2302,10 @@ ClangClInfo ClangClInfo::getInfo(const FilePath &filePath)
|
||||
detectedAbi.wordWidth());
|
||||
}
|
||||
}
|
||||
}
|
||||
m_cache.insert(filePath, entry);
|
||||
return info;
|
||||
};
|
||||
const auto info = DataFromProcess<ClangClInfo>::getData({{filePath, {"--version"}}, parser});
|
||||
return info ? *info : ClangClInfo();
|
||||
}
|
||||
|
||||
} // namespace ProjectExplorer::Internal
|
||||
|
||||
Reference in New Issue
Block a user