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
|
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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
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 "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
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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);
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
QTextStream stream(&output);
|
DataFromProcess<QStringList>::Parameters params({executable, arguments}, parser);
|
||||||
QString line = stream.readLine();
|
params.environment.setupEnglishOutput();
|
||||||
if (!line.startsWith("Enabled checks:"))
|
params.errorHandler = handleProcessError;
|
||||||
return {};
|
if (const auto checks = DataFromProcess<QStringList>::getData(params))
|
||||||
|
return *checks;
|
||||||
QStringList checks;
|
return {};
|
||||||
while (!stream.atEnd()) {
|
|
||||||
const QString candidate = stream.readLine().trimmed();
|
|
||||||
if (!candidate.isEmpty())
|
|
||||||
checks << candidate;
|
|
||||||
}
|
|
||||||
|
|
||||||
return checks;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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,29 +85,41 @@ static ClazyChecks querySupportedClazyChecks(const FilePath &executablePath)
|
|||||||
// ...
|
// ...
|
||||||
// ]
|
// ]
|
||||||
// }
|
// }
|
||||||
|
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())
|
||||||
|
continue;
|
||||||
|
info.level = checkObject["level"].toInt();
|
||||||
|
for (const QJsonValue &item : checkObject["categories"].toArray())
|
||||||
|
info.topics.append(item.toString().trimmed());
|
||||||
|
infos << info;
|
||||||
|
}
|
||||||
|
return infos;
|
||||||
|
};
|
||||||
|
|
||||||
ClazyChecks infos;
|
static const QString queryFlag = "-supported-checks-json";
|
||||||
|
DataFromProcess<ClazyChecks>::Parameters params(CommandLine(executablePath, {queryFlag}),
|
||||||
const QJsonDocument document = QJsonDocument::fromJson(jsonOutput.toUtf8());
|
parser);
|
||||||
if (document.isNull())
|
params.environment.setupEnglishOutput();
|
||||||
return {};
|
params.errorHandler = handleProcessError;
|
||||||
const QJsonArray checksArray = document.object()["checks"].toArray();
|
auto checks = DataFromProcess<ClazyChecks>::getData(params);
|
||||||
|
if (!checks) {
|
||||||
for (const QJsonValue &item: checksArray) {
|
// Some clazy 1.6.x versions have a bug where they expect an argument after the
|
||||||
const QJsonObject checkObject = item.toObject();
|
// option.
|
||||||
|
params.commandLine = CommandLine(executablePath, {queryFlag, "dummy"});
|
||||||
ClazyCheck info;
|
checks = DataFromProcess<ClazyChecks>::getData(params);
|
||||||
info.name = checkObject["name"].toString().trimmed();
|
|
||||||
if (info.name.isEmpty())
|
|
||||||
continue;
|
|
||||||
info.level = checkObject["level"].toInt();
|
|
||||||
for (const QJsonValue &item : checkObject["categories"].toArray())
|
|
||||||
info.topics.append(item.toString().trimmed());
|
|
||||||
|
|
||||||
infos << info;
|
|
||||||
}
|
}
|
||||||
|
if (checks)
|
||||||
return infos;
|
return *checks;
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ClangTidyInfo::ClangTidyInfo(const FilePath &executablePath)
|
ClangTidyInfo::ClangTidyInfo(const FilePath &executablePath)
|
||||||
@@ -141,76 +127,80 @@ 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> {
|
||||||
QTextStream stream(&output);
|
QString output = stdOut;
|
||||||
while (!stream.atEnd()) {
|
QTextStream stream(&output);
|
||||||
// It's just "clazy version " right now, but let's be prepared for someone adding a colon
|
while (!stream.atEnd()) {
|
||||||
// later on.
|
// It's just "clazy version " right now, but let's be prepared for someone
|
||||||
static const QStringList versionPrefixes{"clazy version ", "clazy version: "};
|
// adding a colon later on.
|
||||||
const QString line = stream.readLine().simplified();
|
static const QStringList versionPrefixes{"clazy version ", "clazy version: "};
|
||||||
for (const QString &prefix : versionPrefixes) {
|
const QString line = stream.readLine().simplified();
|
||||||
if (line.startsWith(prefix)) {
|
for (const QString &prefix : versionPrefixes) {
|
||||||
version = QVersionNumber::fromString(line.mid(prefix.length()));
|
if (line.startsWith(prefix))
|
||||||
break;
|
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)
|
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> {
|
||||||
|
QString output = stdOut;
|
||||||
|
QTextStream stream(&output);
|
||||||
|
const QString path = clangToolPath.parentDir().parentDir()
|
||||||
|
.pathAppended(stream.readLine()).toString();
|
||||||
|
const auto filePath = FilePath::fromUserInput(QDir::cleanPath(path));
|
||||||
|
if (filePath.exists())
|
||||||
|
return filePath;
|
||||||
|
return {};
|
||||||
|
};
|
||||||
|
|
||||||
// Parse
|
DataFromProcess<FilePath>::Parameters params({clangToolPath,
|
||||||
QTextStream stream(&output);
|
{"someFilePath", "--", "-print-resource-dir"}},
|
||||||
const QString path = clangToolPath.parentDir().parentDir()
|
parser);
|
||||||
.pathAppended(stream.readLine()).toString();
|
params.environment.setupEnglishOutput();
|
||||||
const auto filePath = FilePath::fromUserInput(QDir::cleanPath(path));
|
params.allowedResults << ProcessResult::FinishedWithError;
|
||||||
if (filePath.exists())
|
if (const auto filePath = DataFromProcess<FilePath>::getData(params))
|
||||||
return filePath;
|
return *filePath;
|
||||||
return {};
|
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> {
|
||||||
QTextStream stream(&output);
|
QString output = stdOut;
|
||||||
while (!stream.atEnd()) {
|
QTextStream stream(&output);
|
||||||
static const QStringList versionPrefixes{"LLVM version ", "clang version: "};
|
while (!stream.atEnd()) {
|
||||||
const QString line = stream.readLine().simplified();
|
static const QStringList versionPrefixes{"LLVM version ", "clang version: "};
|
||||||
for (const QString &prefix : versionPrefixes) {
|
const QString line = stream.readLine().simplified();
|
||||||
auto idx = line.indexOf(prefix);
|
for (const QString &prefix : versionPrefixes) {
|
||||||
if (idx >= 0)
|
auto idx = line.indexOf(prefix);
|
||||||
return line.mid(idx + prefix.length());
|
if (idx >= 0)
|
||||||
|
return line.mid(idx + prefix.length());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
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 {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return info;
|
||||||
m_cache.insert(filePath, entry);
|
};
|
||||||
return info;
|
const auto info = DataFromProcess<ClangClInfo>::getData({{filePath, {"--version"}}, parser});
|
||||||
|
return info ? *info : ClangClInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ProjectExplorer::Internal
|
} // namespace ProjectExplorer::Internal
|
||||||
|
|||||||
Reference in New Issue
Block a user