ClangTools: Reuse TaskTree

Reuse it in ClangToolRunControl and DocumentClangToolRunner.
Get rid of ClangToolRunner and provide clangToolTask() method
instead.

Change-Id: I677940b325850849c5f5a60f2d320c031a4f0da0
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
Jarek Kobus
2023-01-11 23:48:53 +01:00
parent 1dad90ea45
commit e7781e2a99
8 changed files with 204 additions and 339 deletions

View File

@@ -5,40 +5,25 @@
#include "clangtool.h" #include "clangtool.h"
#include "clangtoolrunner.h" #include "clangtoolrunner.h"
#include "clangtoolssettings.h"
#include "executableinfo.h" #include "executableinfo.h"
#include <debugger/analyzer/analyzerconstants.h> #include <coreplugin/progressmanager/taskprogress.h>
#include <clangcodemodel/clangutils.h>
#include <coreplugin/progressmanager/futureprogress.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <cppeditor/clangdiagnosticconfigsmodel.h>
#include <cppeditor/compileroptionsbuilder.h>
#include <cppeditor/cppmodelmanager.h> #include <cppeditor/cppmodelmanager.h>
#include <cppeditor/cppprojectfile.h>
#include <cppeditor/projectinfo.h>
#include <projectexplorer/abi.h>
#include <projectexplorer/buildconfiguration.h> #include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/buildmanager.h> #include <projectexplorer/buildmanager.h>
#include <projectexplorer/kitinformation.h> #include <projectexplorer/kitinformation.h>
#include <projectexplorer/project.h> #include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorericons.h>
#include <projectexplorer/runconfiguration.h>
#include <projectexplorer/target.h> #include <projectexplorer/target.h>
#include <projectexplorer/toolchain.h> #include <projectexplorer/toolchain.h>
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/environment.h>
#include <utils/hostosinfo.h>
#include <utils/qtcprocess.h> #include <utils/qtcprocess.h>
#include <utils/stringutils.h> #include <utils/stringutils.h>
#include <utils/tasktree.h>
#include <QAction>
#include <QLoggingCategory> #include <QLoggingCategory>
using namespace CppEditor; using namespace CppEditor;
@@ -98,7 +83,6 @@ static QDebug operator<<(QDebug debug, const AnalyzeUnits &analyzeUnits)
return debug; return debug;
} }
ClangToolRunWorker::ClangToolRunWorker(ClangTool *tool, RunControl *runControl, ClangToolRunWorker::ClangToolRunWorker(ClangTool *tool, RunControl *runControl,
const RunSettings &runSettings, const RunSettings &runSettings,
const CppEditor::ClangDiagnosticConfig &diagnosticConfig, const CppEditor::ClangDiagnosticConfig &diagnosticConfig,
@@ -134,12 +118,7 @@ ClangToolRunWorker::ClangToolRunWorker(ClangTool *tool, RunControl *runControl,
m_toolChainType = toolChain->typeId(); m_toolChainType = toolChain->typeId();
} }
QList<RunnerCreator> ClangToolRunWorker::runnerCreators(const AnalyzeUnit &unit) ClangToolRunWorker::~ClangToolRunWorker() = default;
{
if (m_tool == ClangTidyTool::instance())
return {[=] { return createRunner(ClangToolType::Tidy, unit); }};
return {[=] { return createRunner(ClangToolType::Clazy, unit); }};
}
void ClangToolRunWorker::start() void ClangToolRunWorker::start()
{ {
@@ -196,55 +175,42 @@ void ClangToolRunWorker::start()
qCDebug(LOG) << Q_FUNC_INFO << runControl()->commandLine().executable() qCDebug(LOG) << Q_FUNC_INFO << runControl()->commandLine().executable()
<< includeDir << clangVersion; << includeDir << clangVersion;
qCDebug(LOG) << "Files to process:" << unitsToProcess; qCDebug(LOG) << "Files to process:" << unitsToProcess;
qCDebug(LOG) << "Environment:" << m_environment;
m_runnerCreators.clear();
for (const AnalyzeUnit &unit : std::as_const(unitsToProcess)) {
for (const RunnerCreator &creator : runnerCreators(unit))
m_runnerCreators << creator;
}
m_initialQueueSize = m_runnerCreators.count();
m_filesAnalyzed.clear(); m_filesAnalyzed.clear();
m_filesNotAnalyzed.clear(); m_filesNotAnalyzed.clear();
// Set up progress information const ClangToolType tool = m_tool == ClangTidyTool::instance() ? ClangToolType::Tidy
using namespace Core; : ClangToolType::Clazy;
m_progress = QFutureInterface<void>(); using namespace Tasking;
FutureProgress *futureProgress QList<TaskItem> tasks{ParallelLimit(qMax(1, m_runSettings.parallelJobs()))};
= ProgressManager::addTask(m_progress.future(), tr("Analyzing"), for (const AnalyzeUnit &unit : std::as_const(unitsToProcess)) {
toolName.toStdString().c_str()); const AnalyzeInputData input{tool, m_diagnosticConfig, m_temporaryDir.path(),
connect(futureProgress, &FutureProgress::canceled, m_environment, unit};
this, &ClangToolRunWorker::onProgressCanceled); const auto setupHandler = [this, unit, tool] {
m_progress.setProgressRange(0, m_initialQueueSize); const QString filePath = FilePath::fromString(unit.file).toUserOutput();
m_progress.reportStarted(); appendMessage(tr("Analyzing \"%1\" [%2].").arg(filePath, clangToolName(tool)),
Utils::StdOutFormat);
// Start process(es) return true;
qCDebug(LOG) << "Environment:" << m_environment; };
m_runners.clear(); const auto outputHandler = [this](const AnalyzeOutputData &output) { onDone(output); };
const int parallelRuns = m_runSettings.parallelJobs(); tasks.append(clangToolTask(input, setupHandler, outputHandler));
QTC_ASSERT(parallelRuns >= 1, reportFailure(); return);
if (m_runnerCreators.isEmpty()) {
finalize();
return;
} }
m_taskTree.reset(new TaskTree(tasks));
connect(m_taskTree.get(), &TaskTree::done, this, &ClangToolRunWorker::finalize);
connect(m_taskTree.get(), &TaskTree::errorOccurred, this, &ClangToolRunWorker::finalize);
auto progress = new Core::TaskProgress(m_taskTree.get());
progress->setDisplayName(tr("Analyzing"));
reportStarted(); reportStarted();
m_elapsed.start(); m_elapsed.start();
m_taskTree->start();
while (m_runners.size() < parallelRuns && !m_runnerCreators.isEmpty())
analyzeNextFile();
} }
void ClangToolRunWorker::stop() void ClangToolRunWorker::stop()
{ {
for (ClangToolRunner *runner : std::as_const(m_runners)) { m_taskTree.reset();
QObject::disconnect(runner, nullptr, this, nullptr);
delete runner;
}
m_projectFiles.clear(); m_projectFiles.clear();
m_runners.clear();
m_runnerCreators.clear();
m_progress.reportFinished();
reportStopped(); reportStopped();
@@ -253,26 +219,6 @@ void ClangToolRunWorker::stop()
appendMessage(elapsedTime, NormalMessageFormat); appendMessage(elapsedTime, NormalMessageFormat);
} }
void ClangToolRunWorker::analyzeNextFile()
{
if (m_progress.isFinished())
return; // The previous call already reported that we are finished.
if (m_runnerCreators.isEmpty()) {
if (m_runners.isEmpty())
finalize();
return;
}
const RunnerCreator runnerCreator = m_runnerCreators.takeFirst();
ClangToolRunner *runner = runnerCreator();
m_runners.insert(runner);
if (!runner->run()) {
reportFailure(tr("Failed to start runner \"%1\".").arg(runner->name()));
stop();
}
}
void ClangToolRunWorker::onDone(const AnalyzeOutputData &output) void ClangToolRunWorker::onDone(const AnalyzeOutputData &output)
{ {
emit runnerFinished(); emit runnerFinished();
@@ -314,27 +260,10 @@ void ClangToolRunWorker::onDone(const AnalyzeOutputData &output)
} }
} }
void ClangToolRunWorker::handleFinished(ClangToolRunner *runner)
{
m_runners.remove(runner);
updateProgressValue();
runner->deleteLater();
analyzeNextFile();
}
void ClangToolRunWorker::onProgressCanceled()
{
m_progress.reportCanceled();
runControl()->initiateStop();
}
void ClangToolRunWorker::updateProgressValue()
{
m_progress.setProgressValue(m_initialQueueSize - m_runnerCreators.size());
}
void ClangToolRunWorker::finalize() void ClangToolRunWorker::finalize()
{ {
if (m_taskTree)
m_taskTree.release()->deleteLater();
const QString toolName = m_tool->name(); const QString toolName = m_tool->name();
if (m_filesNotAnalyzed.size() != 0) { if (m_filesNotAnalyzed.size() != 0) {
appendMessage(tr("Error: Failed to analyze %n files.", nullptr, m_filesNotAnalyzed.size()), appendMessage(tr("Error: Failed to analyze %n files.", nullptr, m_filesNotAnalyzed.size()),
@@ -356,23 +285,8 @@ void ClangToolRunWorker::finalize()
.arg(m_filesNotAnalyzed.size()), .arg(m_filesNotAnalyzed.size()),
Utils::NormalMessageFormat); Utils::NormalMessageFormat);
m_progress.reportFinished();
runControl()->initiateStop(); runControl()->initiateStop();
} }
ClangToolRunner *ClangToolRunWorker::createRunner(ClangToolType tool, const AnalyzeUnit &unit)
{
auto runner = new ClangToolRunner(
{tool, m_diagnosticConfig, m_temporaryDir.path(), m_environment, unit}, this);
connect(runner, &ClangToolRunner::done, this, [this, runner](const AnalyzeOutputData &output) {
onDone(output);
handleFinished(runner);
});
const QString filePath = FilePath::fromString(unit.file).toUserOutput();
appendMessage(tr("Analyzing \"%1\" [%2].").arg(filePath, runner->name()),
Utils::StdOutFormat);
return runner;
}
} // namespace Internal } // namespace Internal
} // namespace ClangTools } // namespace ClangTools

View File

@@ -13,21 +13,17 @@
#include <utils/temporarydirectory.h> #include <utils/temporarydirectory.h>
#include <QElapsedTimer> #include <QElapsedTimer>
#include <QFutureInterface>
#include <QSet> #include <QSet>
#include <QStringList>
namespace Utils { class TaskTree; }
namespace ClangTools { namespace ClangTools {
namespace Internal { namespace Internal {
class AnalyzeOutputData; class AnalyzeOutputData;
class AnalyzeUnit;
class ClangTool; class ClangTool;
class ClangToolRunner;
class ProjectBuilder; class ProjectBuilder;
using RunnerCreator = std::function<ClangToolRunner*()>;
class ClangToolRunWorker : public ProjectExplorer::RunWorker class ClangToolRunWorker : public ProjectExplorer::RunWorker
{ {
Q_OBJECT Q_OBJECT
@@ -39,6 +35,7 @@ public:
const CppEditor::ClangDiagnosticConfig &diagnosticConfig, const CppEditor::ClangDiagnosticConfig &diagnosticConfig,
const FileInfos &fileInfos, const FileInfos &fileInfos,
bool buildBeforeAnalysis); bool buildBeforeAnalysis);
~ClangToolRunWorker();
int filesAnalyzed() const { return m_filesAnalyzed.size(); } int filesAnalyzed() const { return m_filesAnalyzed.size(); }
int filesNotAnalyzed() const { return m_filesNotAnalyzed.size(); } int filesNotAnalyzed() const { return m_filesNotAnalyzed.size(); }
@@ -53,16 +50,6 @@ private:
void start() final; void start() final;
void stop() final; void stop() final;
void onDone(const AnalyzeOutputData &output); void onDone(const AnalyzeOutputData &output);
QList<RunnerCreator> runnerCreators(const AnalyzeUnit &unit);
ClangToolRunner *createRunner(CppEditor::ClangToolType tool, const AnalyzeUnit &unit);
void analyzeNextFile();
void handleFinished(ClangToolRunner *runner);
void onProgressCanceled();
void updateProgressValue();
void finalize(); void finalize();
private: private:
@@ -80,11 +67,8 @@ private:
QString m_targetTriple; QString m_targetTriple;
Utils::Id m_toolChainType; Utils::Id m_toolChainType;
QFutureInterface<void> m_progress; std::unique_ptr<Utils::TaskTree> m_taskTree;
QList<RunnerCreator> m_runnerCreators;
QSet<Utils::FilePath> m_projectFiles; QSet<Utils::FilePath> m_projectFiles;
QSet<ClangToolRunner *> m_runners;
int m_initialQueueSize = 0;
QSet<QString> m_filesAnalyzed; QSet<QString> m_filesAnalyzed;
QSet<QString> m_filesNotAnalyzed; QSet<QString> m_filesNotAnalyzed;

View File

@@ -3,15 +3,14 @@
#include "clangtoolrunner.h" #include "clangtoolrunner.h"
#include "clangtoolstr.h"
#include "clangtoolsutils.h" #include "clangtoolsutils.h"
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <cppeditor/clangdiagnosticconfigsmodel.h> #include <cppeditor/clangdiagnosticconfigsmodel.h>
#include <cppeditor/compileroptionsbuilder.h>
#include <cppeditor/cpptoolsreuse.h> #include <cppeditor/cpptoolsreuse.h>
#include <utils/environment.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <utils/qtcprocess.h> #include <utils/qtcprocess.h>
#include <utils/temporaryfile.h> #include <utils/temporaryfile.h>
@@ -25,6 +24,7 @@ static Q_LOGGING_CATEGORY(LOG, "qtc.clangtools.runner", QtWarningMsg)
using namespace CppEditor; using namespace CppEditor;
using namespace Utils; using namespace Utils;
using namespace Tasking;
namespace ClangTools { namespace ClangTools {
namespace Internal { namespace Internal {
@@ -85,30 +85,6 @@ static QStringList clangArguments(const ClangDiagnosticConfig &diagnosticConfig,
return arguments; return arguments;
} }
ClangToolRunner::ClangToolRunner(const AnalyzeInputData &input, QObject *parent)
: QObject(parent)
, m_input(input)
{
m_name = input.tool == ClangToolType::Tidy ? tr("Clang-Tidy") : tr("Clazy");
m_executable = toolExecutable(input.tool);
QTC_CHECK(!m_input.outputDirPath.isEmpty());
m_process.setEnvironment(input.environment);
m_process.setUseCtrlCStub(true);
m_process.setWorkingDirectory(m_input.outputDirPath); // Current clang-cl puts log file into working dir.
connect(&m_process, &QtcProcess::done, this, &ClangToolRunner::onProcessDone);
}
QStringList ClangToolRunner::mainToolArguments() const
{
QStringList result;
result << "-export-fixes=" + m_outputFilePath;
if (!m_input.overlayFilePath.isEmpty() && isVFSOverlaySupported(m_executable))
result << "--vfsoverlay=" + m_input.overlayFilePath;
result << QDir::toNativeSeparators(m_input.unit.file);
return result;
}
static QString createOutputFilePath(const FilePath &dirPath, const QString &fileToAnalyze) static QString createOutputFilePath(const FilePath &dirPath, const QString &fileToAnalyze)
{ {
const QString fileName = QFileInfo(fileToAnalyze).fileName(); const QString fileName = QFileInfo(fileToAnalyze).fileName();
@@ -124,52 +100,98 @@ static QString createOutputFilePath(const FilePath &dirPath, const QString &file
return {}; return {};
} }
bool ClangToolRunner::run() TaskItem clangToolTask(const AnalyzeInputData &input,
const AnalyzeSetupHandler &setupHandler,
const AnalyzeOutputHandler &outputHandler)
{ {
QTC_ASSERT(m_executable.isExecutableFile(), struct ClangToolStorage {
qWarning() << "Can't start:" << m_executable << "as" << m_name; return false); QString name;
QTC_CHECK(!m_input.unit.arguments.contains(QLatin1String("-o"))); Utils::FilePath executable;
QTC_CHECK(!m_input.unit.arguments.contains(m_input.unit.file)); QString outputFilePath;
QTC_ASSERT(FilePath::fromString(m_input.unit.file).exists(), return false); };
const TreeStorage<ClangToolStorage> storage;
m_outputFilePath = createOutputFilePath(m_input.outputDirPath, m_input.unit.file); const auto mainToolArguments = [=](const ClangToolStorage *data)
QTC_ASSERT(!m_outputFilePath.isEmpty(), return false); {
QStringList result;
result << "-export-fixes=" + data->outputFilePath;
if (!input.overlayFilePath.isEmpty() && isVFSOverlaySupported(data->executable))
result << "--vfsoverlay=" + input.overlayFilePath;
result << QDir::toNativeSeparators(input.unit.file);
return result;
};
const QStringList args = checksArguments(m_input.tool, m_input.config) const auto onGroupSetup = [=] {
+ mainToolArguments() const GroupConfig error = GroupConfig{GroupAction::StopWithError};
+ QStringList{"--"} if (setupHandler && !setupHandler())
+ clangArguments(m_input.config, m_input.unit.arguments); return error;
const CommandLine commandLine = {m_executable, args};
qCDebug(LOG).noquote() << "Starting" << commandLine.toUserOutput(); ClangToolStorage *data = storage.activeStorage();
m_process.setCommand(commandLine); data->name = clangToolName(input.tool);
m_process.start(); data->executable = toolExecutable(input.tool);
return true; if (!data->executable.isExecutableFile()) {
} qWarning() << "Can't start:" << data->executable << "as" << data->name;
return error;
void ClangToolRunner::onProcessDone()
{
if (m_process.result() == ProcessResult::FinishedWithSuccess) {
qCDebug(LOG).noquote() << "Output:\n" << m_process.cleanedStdOut();
emit done({true, m_input.unit.file, m_outputFilePath, m_name});
return;
} }
const QString details = tr("Command line: %1\n" QTC_CHECK(!input.unit.arguments.contains(QLatin1String("-o")));
"Process Error: %2\n" QTC_CHECK(!input.unit.arguments.contains(input.unit.file));
"Output:\n%3") QTC_ASSERT(FilePath::fromString(input.unit.file).exists(), return error);
.arg(m_process.commandLine().toUserOutput()) data->outputFilePath = createOutputFilePath(input.outputDirPath, input.unit.file);
.arg(m_process.error()) QTC_ASSERT(!data->outputFilePath.isEmpty(), return error);
.arg(m_process.cleanedStdOut());
QString message;
if (m_process.result() == ProcessResult::StartFailed)
message = tr("An error occurred with the %1 process.").arg(m_name);
else if (m_process.result() == ProcessResult::FinishedWithError)
message = tr("%1 finished with exit code: %2.").arg(m_name).arg(m_process.exitCode());
else
message = tr("%1 crashed.").arg(m_name);
emit done({false, m_input.unit.file, m_outputFilePath, m_name, message, details}); return GroupConfig{GroupAction::ContinueAll};
};
const auto onProcessSetup = [=](QtcProcess &process) {
process.setEnvironment(input.environment);
process.setUseCtrlCStub(true);
process.setWorkingDirectory(input.outputDirPath); // Current clang-cl puts log file into working dir.
const ClangToolStorage *data = storage.activeStorage();
const QStringList args = checksArguments(input.tool, input.config)
+ mainToolArguments(data)
+ QStringList{"--"}
+ clangArguments(input.config, input.unit.arguments);
const CommandLine commandLine = {data->executable, args};
qCDebug(LOG).noquote() << "Starting" << commandLine.toUserOutput();
process.setCommand(commandLine);
};
const auto onProcessDone = [=](const QtcProcess &process) {
qCDebug(LOG).noquote() << "Output:\n" << process.cleanedStdOut();
if (!outputHandler)
return;
const ClangToolStorage *data = storage.activeStorage();
outputHandler({true, input.unit.file, data->outputFilePath, data->name});
};
const auto onProcessError = [=](const QtcProcess &process) {
if (!outputHandler)
return;
const QString details = Tr::tr("Command line: %1\nProcess Error: %2\nOutput:\n%3")
.arg(process.commandLine().toUserOutput())
.arg(process.error())
.arg(process.cleanedStdOut());
const ClangToolStorage *data = storage.activeStorage();
QString message;
if (process.result() == ProcessResult::StartFailed)
message = Tr::tr("An error occurred with the %1 process.").arg(data->name);
else if (process.result() == ProcessResult::FinishedWithError)
message = Tr::tr("%1 finished with exit code: %2.").arg(data->name).arg(process.exitCode());
else
message = Tr::tr("%1 crashed.").arg(data->name);
outputHandler({false, input.unit.file, data->outputFilePath, data->name, message, details});
};
const Group group {
Storage(storage),
DynamicSetup(onGroupSetup),
Group {
optional,
Process(onProcessSetup, onProcessDone, onProcessError)
}
};
return group;
} }
} // namespace Internal } // namespace Internal

View File

@@ -8,9 +8,8 @@
#include <cppeditor/clangdiagnosticconfig.h> #include <cppeditor/clangdiagnosticconfig.h>
#include <utils/environment.h> #include <utils/environment.h>
#include <utils/qtcprocess.h>
#include <memory> namespace Utils::Tasking { class TaskItem; }
namespace ClangTools { namespace ClangTools {
namespace Internal { namespace Internal {
@@ -45,35 +44,12 @@ struct AnalyzeOutputData
QString errorDetails = {}; QString errorDetails = {};
}; };
class ClangToolRunner : public QObject using AnalyzeSetupHandler = std::function<bool()>;
{ using AnalyzeOutputHandler = std::function<void(const AnalyzeOutputData &)>;
Q_OBJECT
public: Utils::Tasking::TaskItem clangToolTask(const AnalyzeInputData &input,
ClangToolRunner(const AnalyzeInputData &input, QObject *parent = nullptr); const AnalyzeSetupHandler &setupHandler,
const AnalyzeOutputHandler &outputHandler);
QString name() const { return m_name; }
// compilerOptions is expected to contain everything except:
// (1) file to analyze
// (2) -o output-file
bool run();
signals:
void done(const AnalyzeOutputData &output);
private:
void onProcessDone();
QStringList mainToolArguments() const;
const AnalyzeInputData m_input;
Utils::QtcProcess m_process;
QString m_name;
Utils::FilePath m_executable;
QString m_outputFilePath;
};
} // namespace Internal } // namespace Internal
} // namespace ClangTools } // namespace ClangTools

View File

@@ -7,6 +7,7 @@
#include "clangtoolsconstants.h" #include "clangtoolsconstants.h"
#include "clangtoolsdiagnostic.h" #include "clangtoolsdiagnostic.h"
#include "clangtoolssettings.h" #include "clangtoolssettings.h"
#include "clangtoolstr.h"
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <cppeditor/cppeditorconstants.h> #include <cppeditor/cppeditorconstants.h>
@@ -200,6 +201,11 @@ FilePath toolFallbackExecutable(ClangToolType tool)
return findValidExecutable({toolShippedExecutable(tool), fallback}); return findValidExecutable({toolShippedExecutable(tool), fallback});
} }
QString clangToolName(CppEditor::ClangToolType tool)
{
return tool == ClangToolType::Tidy ? Tr::tr("Clang-Tidy") : Tr::tr("Clazy");
}
bool isVFSOverlaySupported(const FilePath &executable) bool isVFSOverlaySupported(const FilePath &executable)
{ {
static QMap<FilePath, bool> vfsCapabilities; static QMap<FilePath, bool> vfsCapabilities;

View File

@@ -46,6 +46,7 @@ void showHintAboutBuildBeforeAnalysis();
Utils::FilePath toolShippedExecutable(CppEditor::ClangToolType tool); Utils::FilePath toolShippedExecutable(CppEditor::ClangToolType tool);
Utils::FilePath toolExecutable(CppEditor::ClangToolType tool); Utils::FilePath toolExecutable(CppEditor::ClangToolType tool);
Utils::FilePath toolFallbackExecutable(CppEditor::ClangToolType tool); Utils::FilePath toolFallbackExecutable(CppEditor::ClangToolType tool);
QString clangToolName(CppEditor::ClangToolType tool);
bool isVFSOverlaySupported(const Utils::FilePath &executable); bool isVFSOverlaySupported(const Utils::FilePath &executable);

View File

@@ -3,18 +3,14 @@
#include "documentclangtoolrunner.h" #include "documentclangtoolrunner.h"
#include "clangfileinfo.h"
#include "clangtoolruncontrol.h"
#include "clangtoolsconstants.h" #include "clangtoolsconstants.h"
#include "clangtoolslogfilereader.h" #include "clangtoolslogfilereader.h"
#include "clangtoolsprojectsettings.h"
#include "clangtoolrunner.h" #include "clangtoolrunner.h"
#include "clangtoolsutils.h" #include "clangtoolsutils.h"
#include "diagnosticmark.h" #include "diagnosticmark.h"
#include "executableinfo.h" #include "executableinfo.h"
#include "virtualfilesystemoverlay.h" #include "virtualfilesystemoverlay.h"
#include <coreplugin/documentmanager.h>
#include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/editormanager/ieditor.h> #include <coreplugin/editormanager/ieditor.h>
@@ -27,12 +23,12 @@
#include <texteditor/textdocument.h> #include <texteditor/textdocument.h>
#include <texteditor/texteditor.h> #include <texteditor/texteditor.h>
#include <texteditor/textmark.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <utils/utilsicons.h> #include <utils/tasktree.h>
#include <QLoggingCategory> #include <QLoggingCategory>
#include <QScopeGuard>
static Q_LOGGING_CATEGORY(LOG, "qtc.clangtools.cftr", QtWarningMsg) static Q_LOGGING_CATEGORY(LOG, "qtc.clangtools.cftr", QtWarningMsg)
@@ -49,31 +45,20 @@ DocumentClangToolRunner::DocumentClangToolRunner(IDocument *document)
, m_document(document) , m_document(document)
, m_temporaryDir("clangtools-single-XXXXXX") , m_temporaryDir("clangtools-single-XXXXXX")
{ {
m_runTimer.setInterval(500); m_runTimer.setInterval(500);
m_runTimer.setSingleShot(true); m_runTimer.setSingleShot(true);
connect(m_document, connect(m_document, &IDocument::contentsChanged,
&IDocument::contentsChanged, this, &DocumentClangToolRunner::scheduleRun);
this, connect(CppModelManager::instance(), &CppModelManager::projectPartsUpdated,
&DocumentClangToolRunner::scheduleRun); this, &DocumentClangToolRunner::scheduleRun);
connect(CppModelManager::instance(), connect(ClangToolsSettings::instance(), &ClangToolsSettings::changed,
&CppModelManager::projectPartsUpdated, this, &DocumentClangToolRunner::scheduleRun);
this,
&DocumentClangToolRunner::scheduleRun);
connect(ClangToolsSettings::instance(),
&ClangToolsSettings::changed,
this,
&DocumentClangToolRunner::scheduleRun);
connect(&m_runTimer, &QTimer::timeout, this, &DocumentClangToolRunner::run); connect(&m_runTimer, &QTimer::timeout, this, &DocumentClangToolRunner::run);
run(); run();
} }
DocumentClangToolRunner::~DocumentClangToolRunner() DocumentClangToolRunner::~DocumentClangToolRunner() = default;
{
cancel();
qDeleteAll(m_marks);
}
FilePath DocumentClangToolRunner::filePath() const FilePath DocumentClangToolRunner::filePath() const
{ {
@@ -168,78 +153,69 @@ static Environment projectBuildEnvironment(Project *project)
void DocumentClangToolRunner::run() void DocumentClangToolRunner::run()
{ {
cancel(); if (m_projectSettingsUpdate)
disconnect(m_projectSettingsUpdate);
m_taskTree.reset();
QScopeGuard guard([this] { finalize(); });
auto isEditorForCurrentDocument = [this](const IEditor *editor) { auto isEditorForCurrentDocument = [this](const IEditor *editor) {
return editor->document() == m_document; return editor->document() == m_document;
}; };
if (Utils::anyOf(EditorManager::visibleEditors(), isEditorForCurrentDocument)) { if (!Utils::anyOf(EditorManager::visibleEditors(), isEditorForCurrentDocument)) {
deleteLater();
return;
}
const FilePath filePath = m_document->filePath(); const FilePath filePath = m_document->filePath();
if (Project *project = findProject(filePath)) { Project *project = findProject(filePath);
m_fileInfo = getFileInfo(filePath, project); if (!project)
if (m_fileInfo.file.exists()) { return;
const auto projectSettings = ClangToolsProjectSettings::getSettings(project);
m_fileInfo = getFileInfo(filePath, project);
if (!m_fileInfo.file.exists())
return;
const auto projectSettings = ClangToolsProjectSettings::getSettings(project);
const RunSettings &runSettings = projectSettings->useGlobalSettings() const RunSettings &runSettings = projectSettings->useGlobalSettings()
? ClangToolsSettings::instance()->runSettings() ? ClangToolsSettings::instance()->runSettings()
: projectSettings->runSettings(); : projectSettings->runSettings();
m_suppressed = projectSettings->suppressedDiagnostics(); m_suppressed = projectSettings->suppressedDiagnostics();
m_lastProjectDirectory = project->projectDirectory(); m_lastProjectDirectory = project->projectDirectory();
m_projectSettingsUpdate = connect(projectSettings.data(), m_projectSettingsUpdate = connect(projectSettings.data(), &ClangToolsProjectSettings::changed,
&ClangToolsProjectSettings::changed, this, &DocumentClangToolRunner::run);
this, if (!runSettings.analyzeOpenFiles())
&DocumentClangToolRunner::run); return;
if (runSettings.analyzeOpenFiles()) {
vfso().update(); vfso().update();
const ClangDiagnosticConfig config = diagnosticConfig(runSettings.diagnosticConfigId());
const ClangDiagnosticConfig config
= diagnosticConfig(runSettings.diagnosticConfigId());
const Environment env = projectBuildEnvironment(project); const Environment env = projectBuildEnvironment(project);
const auto addClangTool = [this, config, env](ClangToolType tool) { using namespace Tasking;
QList<TaskItem> tasks{parallel};
const auto addClangTool = [this, &config, &env, &tasks](ClangToolType tool) {
if (!config.isEnabled(tool)) if (!config.isEnabled(tool))
return; return;
const FilePath executable = toolExecutable(tool); const FilePath executable = toolExecutable(tool);
const auto [includeDir, clangVersion] const auto [includeDir, clangVersion] = getClangIncludeDirAndVersion(executable);
= getClangIncludeDirAndVersion(executable);
if (!executable.isExecutableFile() || includeDir.isEmpty() || clangVersion.isEmpty()) if (!executable.isExecutableFile() || includeDir.isEmpty() || clangVersion.isEmpty())
return; return;
const AnalyzeUnit unit(m_fileInfo, includeDir, clangVersion); const AnalyzeUnit unit(m_fileInfo, includeDir, clangVersion);
m_runnerCreators << [=]() -> ClangToolRunner * { const AnalyzeInputData input{tool, config, m_temporaryDir.path(), env, unit,
if (m_document->isModified() && !isVFSOverlaySupported(executable)) vfso().overlayFilePath().toString()};
return nullptr; const auto setupHandler = [this, executable] {
auto runner = new ClangToolRunner({tool, config, m_temporaryDir.path(), return !m_document->isModified() || isVFSOverlaySupported(executable);
env, unit, vfso().overlayFilePath().toString()}, this);
connect(runner, &ClangToolRunner::done,
this, &DocumentClangToolRunner::onDone);
return runner;
}; };
const auto outputHandler = [this](const AnalyzeOutputData &output) { onDone(output); };
tasks.append(Group{optional, clangToolTask(input, setupHandler, outputHandler)});
}; };
addClangTool(ClangToolType::Tidy); addClangTool(ClangToolType::Tidy);
addClangTool(ClangToolType::Clazy); addClangTool(ClangToolType::Clazy);
} if (tasks.isEmpty())
}
}
} else {
deleteLater();
}
runNext();
}
void DocumentClangToolRunner::runNext()
{
if (m_currentRunner)
m_currentRunner.release()->deleteLater();
if (m_runnerCreators.isEmpty()) {
finalize();
return; return;
}
m_currentRunner.reset(m_runnerCreators.takeFirst()()); guard.dismiss();
if (!m_currentRunner || !m_currentRunner->run()) m_taskTree.reset(new TaskTree(tasks));
runNext(); connect(m_taskTree.get(), &TaskTree::done, this, &DocumentClangToolRunner::finalize);
connect(m_taskTree.get(), &TaskTree::errorOccurred, this, &DocumentClangToolRunner::finalize);
m_taskTree->start();
} }
static void updateLocation(Debugger::DiagnosticLocation &location) static void updateLocation(Debugger::DiagnosticLocation &location)
@@ -252,7 +228,6 @@ void DocumentClangToolRunner::onDone(const AnalyzeOutputData &output)
if (!output.success) { if (!output.success) {
qCDebug(LOG) << "Failed to analyze " << m_fileInfo.file qCDebug(LOG) << "Failed to analyze " << m_fileInfo.file
<< ":" << output.errorMessage << output.errorDetails; << ":" << output.errorMessage << output.errorDetails;
runNext();
return; return;
} }
@@ -316,26 +291,18 @@ void DocumentClangToolRunner::onDone(const AnalyzeOutputData &output)
m_editorsWithMarkers << widget; m_editorsWithMarkers << widget;
} }
} }
runNext();
} }
void DocumentClangToolRunner::finalize() void DocumentClangToolRunner::finalize()
{ {
// remove all disabled textMarks if (m_taskTree)
auto [newMarks, toDelete] = Utils::partition(m_marks, &DiagnosticMark::enabled); m_taskTree.release()->deleteLater();
// remove all disabled marks
const auto [newMarks, toDelete] = Utils::partition(m_marks, &DiagnosticMark::enabled);
m_marks = newMarks; m_marks = newMarks;
qDeleteAll(toDelete); qDeleteAll(toDelete);
} }
void DocumentClangToolRunner::cancel()
{
if (m_projectSettingsUpdate)
disconnect(m_projectSettingsUpdate);
m_runnerCreators.clear();
m_currentRunner.reset(nullptr);
}
bool DocumentClangToolRunner::isSuppressed(const Diagnostic &diagnostic) const bool DocumentClangToolRunner::isSuppressed(const Diagnostic &diagnostic) const
{ {
auto equalsSuppressed = [this, &diagnostic](const SuppressedDiagnostic &suppressed) { auto equalsSuppressed = [this, &diagnostic](const SuppressedDiagnostic &suppressed) {

View File

@@ -15,12 +15,12 @@
namespace Core { class IDocument; } namespace Core { class IDocument; }
namespace TextEditor { class TextEditorWidget; } namespace TextEditor { class TextEditorWidget; }
namespace Utils { class TaskTree; }
namespace ClangTools { namespace ClangTools {
namespace Internal { namespace Internal {
class AnalyzeOutputData; class AnalyzeOutputData;
class ClangToolRunner;
class DiagnosticMark; class DiagnosticMark;
class DocumentClangToolRunner : public QObject class DocumentClangToolRunner : public QObject
@@ -36,27 +36,22 @@ public:
private: private:
void scheduleRun(); void scheduleRun();
void run(); void run();
void runNext();
void onDone(const AnalyzeOutputData &output); void onDone(const AnalyzeOutputData &output);
void finalize(); void finalize();
void cancel();
bool isSuppressed(const Diagnostic &diagnostic) const; bool isSuppressed(const Diagnostic &diagnostic) const;
QTimer m_runTimer; QTimer m_runTimer;
Core::IDocument *m_document = nullptr; Core::IDocument *m_document = nullptr;
Utils::TemporaryDirectory m_temporaryDir; Utils::TemporaryDirectory m_temporaryDir;
std::unique_ptr<ClangToolRunner> m_currentRunner;
QList<std::function<ClangToolRunner *()>> m_runnerCreators;
QList<DiagnosticMark *> m_marks; QList<DiagnosticMark *> m_marks;
FileInfo m_fileInfo; FileInfo m_fileInfo;
QMetaObject::Connection m_projectSettingsUpdate; QMetaObject::Connection m_projectSettingsUpdate;
QList<QPointer<TextEditor::TextEditorWidget>> m_editorsWithMarkers; QList<QPointer<TextEditor::TextEditorWidget>> m_editorsWithMarkers;
SuppressedDiagnosticsList m_suppressed; SuppressedDiagnosticsList m_suppressed;
Utils::FilePath m_lastProjectDirectory; Utils::FilePath m_lastProjectDirectory;
std::unique_ptr<Utils::TaskTree> m_taskTree;
}; };
} // namespace Internal } // namespace Internal