diff options
author | Jarek Kobus <[email protected]> | 2023-01-11 23:48:53 +0100 |
---|---|---|
committer | Jarek Kobus <[email protected]> | 2023-01-12 10:11:44 +0000 |
commit | e7781e2a99bce1981ab4efab97e5df14b142238c (patch) | |
tree | 6d6f6e917c5522714e6b21e6a984d9fe374a0d6f /src/plugins/clangtools | |
parent | 1dad90ea451b763f8cf90ceb1d355206252c7255 (diff) |
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 <[email protected]>
Diffstat (limited to 'src/plugins/clangtools')
-rw-r--r-- | src/plugins/clangtools/clangtoolruncontrol.cpp | 142 | ||||
-rw-r--r-- | src/plugins/clangtools/clangtoolruncontrol.h | 24 | ||||
-rw-r--r-- | src/plugins/clangtools/clangtoolrunner.cpp | 164 | ||||
-rw-r--r-- | src/plugins/clangtools/clangtoolrunner.h | 36 | ||||
-rw-r--r-- | src/plugins/clangtools/clangtoolsutils.cpp | 6 | ||||
-rw-r--r-- | src/plugins/clangtools/clangtoolsutils.h | 1 | ||||
-rw-r--r-- | src/plugins/clangtools/documentclangtoolrunner.cpp | 167 | ||||
-rw-r--r-- | src/plugins/clangtools/documentclangtoolrunner.h | 9 |
8 files changed, 207 insertions, 342 deletions
diff --git a/src/plugins/clangtools/clangtoolruncontrol.cpp b/src/plugins/clangtools/clangtoolruncontrol.cpp index 7482868b3f3..ce9d1028d1c 100644 --- a/src/plugins/clangtools/clangtoolruncontrol.cpp +++ b/src/plugins/clangtools/clangtoolruncontrol.cpp @@ -5,40 +5,25 @@ #include "clangtool.h" #include "clangtoolrunner.h" -#include "clangtoolssettings.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/cppprojectfile.h> -#include <cppeditor/projectinfo.h> -#include <projectexplorer/abi.h> #include <projectexplorer/buildconfiguration.h> #include <projectexplorer/buildmanager.h> #include <projectexplorer/kitinformation.h> #include <projectexplorer/project.h> #include <projectexplorer/projectexplorer.h> -#include <projectexplorer/projectexplorericons.h> -#include <projectexplorer/runconfiguration.h> #include <projectexplorer/target.h> #include <projectexplorer/toolchain.h> #include <utils/algorithm.h> -#include <utils/environment.h> -#include <utils/hostosinfo.h> #include <utils/qtcprocess.h> #include <utils/stringutils.h> +#include <utils/tasktree.h> -#include <QAction> #include <QLoggingCategory> using namespace CppEditor; @@ -98,7 +83,6 @@ static QDebug operator<<(QDebug debug, const AnalyzeUnits &analyzeUnits) return debug; } - ClangToolRunWorker::ClangToolRunWorker(ClangTool *tool, RunControl *runControl, const RunSettings &runSettings, const CppEditor::ClangDiagnosticConfig &diagnosticConfig, @@ -134,12 +118,7 @@ ClangToolRunWorker::ClangToolRunWorker(ClangTool *tool, RunControl *runControl, m_toolChainType = toolChain->typeId(); } -QList<RunnerCreator> ClangToolRunWorker::runnerCreators(const AnalyzeUnit &unit) -{ - if (m_tool == ClangTidyTool::instance()) - return {[=] { return createRunner(ClangToolType::Tidy, unit); }}; - return {[=] { return createRunner(ClangToolType::Clazy, unit); }}; -} +ClangToolRunWorker::~ClangToolRunWorker() = default; void ClangToolRunWorker::start() { @@ -196,55 +175,42 @@ void ClangToolRunWorker::start() qCDebug(LOG) << Q_FUNC_INFO << runControl()->commandLine().executable() << includeDir << clangVersion; 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_filesNotAnalyzed.clear(); - // Set up progress information - using namespace Core; - m_progress = QFutureInterface<void>(); - FutureProgress *futureProgress - = ProgressManager::addTask(m_progress.future(), tr("Analyzing"), - toolName.toStdString().c_str()); - connect(futureProgress, &FutureProgress::canceled, - this, &ClangToolRunWorker::onProgressCanceled); - m_progress.setProgressRange(0, m_initialQueueSize); - m_progress.reportStarted(); - - // Start process(es) - qCDebug(LOG) << "Environment:" << m_environment; - m_runners.clear(); - const int parallelRuns = m_runSettings.parallelJobs(); - QTC_ASSERT(parallelRuns >= 1, reportFailure(); return); - - if (m_runnerCreators.isEmpty()) { - finalize(); - return; + const ClangToolType tool = m_tool == ClangTidyTool::instance() ? ClangToolType::Tidy + : ClangToolType::Clazy; + using namespace Tasking; + QList<TaskItem> tasks{ParallelLimit(qMax(1, m_runSettings.parallelJobs()))}; + for (const AnalyzeUnit &unit : std::as_const(unitsToProcess)) { + const AnalyzeInputData input{tool, m_diagnosticConfig, m_temporaryDir.path(), + m_environment, unit}; + const auto setupHandler = [this, unit, tool] { + const QString filePath = FilePath::fromString(unit.file).toUserOutput(); + appendMessage(tr("Analyzing \"%1\" [%2].").arg(filePath, clangToolName(tool)), + Utils::StdOutFormat); + return true; + }; + const auto outputHandler = [this](const AnalyzeOutputData &output) { onDone(output); }; + tasks.append(clangToolTask(input, setupHandler, outputHandler)); } + 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(); m_elapsed.start(); - - while (m_runners.size() < parallelRuns && !m_runnerCreators.isEmpty()) - analyzeNextFile(); + m_taskTree->start(); } void ClangToolRunWorker::stop() { - for (ClangToolRunner *runner : std::as_const(m_runners)) { - QObject::disconnect(runner, nullptr, this, nullptr); - delete runner; - } + m_taskTree.reset(); m_projectFiles.clear(); - m_runners.clear(); - m_runnerCreators.clear(); - m_progress.reportFinished(); reportStopped(); @@ -253,26 +219,6 @@ void ClangToolRunWorker::stop() 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) { 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() { + if (m_taskTree) + m_taskTree.release()->deleteLater(); const QString toolName = m_tool->name(); if (m_filesNotAnalyzed.size() != 0) { appendMessage(tr("Error: Failed to analyze %n files.", nullptr, m_filesNotAnalyzed.size()), @@ -356,23 +285,8 @@ void ClangToolRunWorker::finalize() .arg(m_filesNotAnalyzed.size()), Utils::NormalMessageFormat); - m_progress.reportFinished(); 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 ClangTools diff --git a/src/plugins/clangtools/clangtoolruncontrol.h b/src/plugins/clangtools/clangtoolruncontrol.h index 08ba1806021..63ffdf99c05 100644 --- a/src/plugins/clangtools/clangtoolruncontrol.h +++ b/src/plugins/clangtools/clangtoolruncontrol.h @@ -13,21 +13,17 @@ #include <utils/temporarydirectory.h> #include <QElapsedTimer> -#include <QFutureInterface> #include <QSet> -#include <QStringList> + +namespace Utils { class TaskTree; } namespace ClangTools { namespace Internal { class AnalyzeOutputData; -class AnalyzeUnit; class ClangTool; -class ClangToolRunner; class ProjectBuilder; -using RunnerCreator = std::function<ClangToolRunner*()>; - class ClangToolRunWorker : public ProjectExplorer::RunWorker { Q_OBJECT @@ -39,6 +35,7 @@ public: const CppEditor::ClangDiagnosticConfig &diagnosticConfig, const FileInfos &fileInfos, bool buildBeforeAnalysis); + ~ClangToolRunWorker(); int filesAnalyzed() const { return m_filesAnalyzed.size(); } int filesNotAnalyzed() const { return m_filesNotAnalyzed.size(); } @@ -53,16 +50,6 @@ private: void start() final; void stop() final; 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(); private: @@ -80,11 +67,8 @@ private: QString m_targetTriple; Utils::Id m_toolChainType; - QFutureInterface<void> m_progress; - QList<RunnerCreator> m_runnerCreators; + std::unique_ptr<Utils::TaskTree> m_taskTree; QSet<Utils::FilePath> m_projectFiles; - QSet<ClangToolRunner *> m_runners; - int m_initialQueueSize = 0; QSet<QString> m_filesAnalyzed; QSet<QString> m_filesNotAnalyzed; diff --git a/src/plugins/clangtools/clangtoolrunner.cpp b/src/plugins/clangtools/clangtoolrunner.cpp index fcb310c07df..8adc889bec1 100644 --- a/src/plugins/clangtools/clangtoolrunner.cpp +++ b/src/plugins/clangtools/clangtoolrunner.cpp @@ -3,15 +3,14 @@ #include "clangtoolrunner.h" +#include "clangtoolstr.h" #include "clangtoolsutils.h" #include <coreplugin/icore.h> #include <cppeditor/clangdiagnosticconfigsmodel.h> -#include <cppeditor/compileroptionsbuilder.h> #include <cppeditor/cpptoolsreuse.h> -#include <utils/environment.h> #include <utils/qtcassert.h> #include <utils/qtcprocess.h> #include <utils/temporaryfile.h> @@ -25,6 +24,7 @@ static Q_LOGGING_CATEGORY(LOG, "qtc.clangtools.runner", QtWarningMsg) using namespace CppEditor; using namespace Utils; +using namespace Tasking; namespace ClangTools { namespace Internal { @@ -85,30 +85,6 @@ static QStringList clangArguments(const ClangDiagnosticConfig &diagnosticConfig, 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) { const QString fileName = QFileInfo(fileToAnalyze).fileName(); @@ -124,52 +100,98 @@ static QString createOutputFilePath(const FilePath &dirPath, const QString &file return {}; } -bool ClangToolRunner::run() +TaskItem clangToolTask(const AnalyzeInputData &input, + const AnalyzeSetupHandler &setupHandler, + const AnalyzeOutputHandler &outputHandler) { - QTC_ASSERT(m_executable.isExecutableFile(), - qWarning() << "Can't start:" << m_executable << "as" << m_name; return false); - QTC_CHECK(!m_input.unit.arguments.contains(QLatin1String("-o"))); - QTC_CHECK(!m_input.unit.arguments.contains(m_input.unit.file)); - QTC_ASSERT(FilePath::fromString(m_input.unit.file).exists(), return false); - - m_outputFilePath = createOutputFilePath(m_input.outputDirPath, m_input.unit.file); - QTC_ASSERT(!m_outputFilePath.isEmpty(), return false); - - const QStringList args = checksArguments(m_input.tool, m_input.config) - + mainToolArguments() - + QStringList{"--"} - + clangArguments(m_input.config, m_input.unit.arguments); - const CommandLine commandLine = {m_executable, args}; - - qCDebug(LOG).noquote() << "Starting" << commandLine.toUserOutput(); - m_process.setCommand(commandLine); - m_process.start(); - return true; -} - -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" - "Process Error: %2\n" - "Output:\n%3") - .arg(m_process.commandLine().toUserOutput()) - .arg(m_process.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}); + struct ClangToolStorage { + QString name; + Utils::FilePath executable; + QString outputFilePath; + }; + const TreeStorage<ClangToolStorage> storage; + + const auto mainToolArguments = [=](const ClangToolStorage *data) + { + 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 auto onGroupSetup = [=] { + const GroupConfig error = GroupConfig{GroupAction::StopWithError}; + if (setupHandler && !setupHandler()) + return error; + + ClangToolStorage *data = storage.activeStorage(); + data->name = clangToolName(input.tool); + data->executable = toolExecutable(input.tool); + if (!data->executable.isExecutableFile()) { + qWarning() << "Can't start:" << data->executable << "as" << data->name; + return error; + } + + QTC_CHECK(!input.unit.arguments.contains(QLatin1String("-o"))); + QTC_CHECK(!input.unit.arguments.contains(input.unit.file)); + QTC_ASSERT(FilePath::fromString(input.unit.file).exists(), return error); + data->outputFilePath = createOutputFilePath(input.outputDirPath, input.unit.file); + QTC_ASSERT(!data->outputFilePath.isEmpty(), return error); + + 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 diff --git a/src/plugins/clangtools/clangtoolrunner.h b/src/plugins/clangtools/clangtoolrunner.h index 37f6bdf915a..b9afb812127 100644 --- a/src/plugins/clangtools/clangtoolrunner.h +++ b/src/plugins/clangtools/clangtoolrunner.h @@ -8,9 +8,8 @@ #include <cppeditor/clangdiagnosticconfig.h> #include <utils/environment.h> -#include <utils/qtcprocess.h> -#include <memory> +namespace Utils::Tasking { class TaskItem; } namespace ClangTools { namespace Internal { @@ -45,35 +44,12 @@ struct AnalyzeOutputData QString errorDetails = {}; }; -class ClangToolRunner : public QObject -{ - Q_OBJECT - -public: - ClangToolRunner(const AnalyzeInputData &input, QObject *parent = nullptr); - - 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); +using AnalyzeSetupHandler = std::function<bool()>; +using AnalyzeOutputHandler = std::function<void(const AnalyzeOutputData &)>; -private: - void onProcessDone(); - - QStringList mainToolArguments() const; - - const AnalyzeInputData m_input; - Utils::QtcProcess m_process; - - QString m_name; - Utils::FilePath m_executable; - QString m_outputFilePath; -}; +Utils::Tasking::TaskItem clangToolTask(const AnalyzeInputData &input, + const AnalyzeSetupHandler &setupHandler, + const AnalyzeOutputHandler &outputHandler); } // namespace Internal } // namespace ClangTools diff --git a/src/plugins/clangtools/clangtoolsutils.cpp b/src/plugins/clangtools/clangtoolsutils.cpp index 8ec086d6c1a..eaebc68b6a7 100644 --- a/src/plugins/clangtools/clangtoolsutils.cpp +++ b/src/plugins/clangtools/clangtoolsutils.cpp @@ -7,6 +7,7 @@ #include "clangtoolsconstants.h" #include "clangtoolsdiagnostic.h" #include "clangtoolssettings.h" +#include "clangtoolstr.h" #include <coreplugin/icore.h> #include <cppeditor/cppeditorconstants.h> @@ -200,6 +201,11 @@ FilePath toolFallbackExecutable(ClangToolType tool) 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) { static QMap<FilePath, bool> vfsCapabilities; diff --git a/src/plugins/clangtools/clangtoolsutils.h b/src/plugins/clangtools/clangtoolsutils.h index 26421f0e8eb..6f4f2871a02 100644 --- a/src/plugins/clangtools/clangtoolsutils.h +++ b/src/plugins/clangtools/clangtoolsutils.h @@ -46,6 +46,7 @@ void showHintAboutBuildBeforeAnalysis(); Utils::FilePath toolShippedExecutable(CppEditor::ClangToolType tool); Utils::FilePath toolExecutable(CppEditor::ClangToolType tool); Utils::FilePath toolFallbackExecutable(CppEditor::ClangToolType tool); +QString clangToolName(CppEditor::ClangToolType tool); bool isVFSOverlaySupported(const Utils::FilePath &executable); diff --git a/src/plugins/clangtools/documentclangtoolrunner.cpp b/src/plugins/clangtools/documentclangtoolrunner.cpp index 488770c38be..7409ea3bea5 100644 --- a/src/plugins/clangtools/documentclangtoolrunner.cpp +++ b/src/plugins/clangtools/documentclangtoolrunner.cpp @@ -3,18 +3,14 @@ #include "documentclangtoolrunner.h" -#include "clangfileinfo.h" -#include "clangtoolruncontrol.h" #include "clangtoolsconstants.h" #include "clangtoolslogfilereader.h" -#include "clangtoolsprojectsettings.h" #include "clangtoolrunner.h" #include "clangtoolsutils.h" #include "diagnosticmark.h" #include "executableinfo.h" #include "virtualfilesystemoverlay.h" -#include <coreplugin/documentmanager.h> #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/ieditor.h> @@ -27,12 +23,12 @@ #include <texteditor/textdocument.h> #include <texteditor/texteditor.h> -#include <texteditor/textmark.h> #include <utils/qtcassert.h> -#include <utils/utilsicons.h> +#include <utils/tasktree.h> #include <QLoggingCategory> +#include <QScopeGuard> static Q_LOGGING_CATEGORY(LOG, "qtc.clangtools.cftr", QtWarningMsg) @@ -49,31 +45,20 @@ DocumentClangToolRunner::DocumentClangToolRunner(IDocument *document) , m_document(document) , m_temporaryDir("clangtools-single-XXXXXX") { - m_runTimer.setInterval(500); m_runTimer.setSingleShot(true); - connect(m_document, - &IDocument::contentsChanged, - this, - &DocumentClangToolRunner::scheduleRun); - connect(CppModelManager::instance(), - &CppModelManager::projectPartsUpdated, - this, - &DocumentClangToolRunner::scheduleRun); - connect(ClangToolsSettings::instance(), - &ClangToolsSettings::changed, - this, - &DocumentClangToolRunner::scheduleRun); + connect(m_document, &IDocument::contentsChanged, + this, &DocumentClangToolRunner::scheduleRun); + connect(CppModelManager::instance(), &CppModelManager::projectPartsUpdated, + this, &DocumentClangToolRunner::scheduleRun); + connect(ClangToolsSettings::instance(), &ClangToolsSettings::changed, + this, &DocumentClangToolRunner::scheduleRun); connect(&m_runTimer, &QTimer::timeout, this, &DocumentClangToolRunner::run); run(); } -DocumentClangToolRunner::~DocumentClangToolRunner() -{ - cancel(); - qDeleteAll(m_marks); -} +DocumentClangToolRunner::~DocumentClangToolRunner() = default; FilePath DocumentClangToolRunner::filePath() const { @@ -168,78 +153,69 @@ static Environment projectBuildEnvironment(Project *project) void DocumentClangToolRunner::run() { - cancel(); + if (m_projectSettingsUpdate) + disconnect(m_projectSettingsUpdate); + m_taskTree.reset(); + QScopeGuard guard([this] { finalize(); }); + auto isEditorForCurrentDocument = [this](const IEditor *editor) { return editor->document() == m_document; }; - if (Utils::anyOf(EditorManager::visibleEditors(), isEditorForCurrentDocument)) { - const FilePath filePath = m_document->filePath(); - if (Project *project = findProject(filePath)) { - m_fileInfo = getFileInfo(filePath, project); - if (m_fileInfo.file.exists()) { - const auto projectSettings = ClangToolsProjectSettings::getSettings(project); - - const RunSettings &runSettings = projectSettings->useGlobalSettings() - ? ClangToolsSettings::instance()->runSettings() - : projectSettings->runSettings(); - - m_suppressed = projectSettings->suppressedDiagnostics(); - m_lastProjectDirectory = project->projectDirectory(); - m_projectSettingsUpdate = connect(projectSettings.data(), - &ClangToolsProjectSettings::changed, - this, - &DocumentClangToolRunner::run); - - if (runSettings.analyzeOpenFiles()) { - vfso().update(); - - const ClangDiagnosticConfig config - = diagnosticConfig(runSettings.diagnosticConfigId()); - - const Environment env = projectBuildEnvironment(project); - const auto addClangTool = [this, config, env](ClangToolType tool) { - if (!config.isEnabled(tool)) - return; - const FilePath executable = toolExecutable(tool); - const auto [includeDir, clangVersion] - = getClangIncludeDirAndVersion(executable); - if (!executable.isExecutableFile() || includeDir.isEmpty() || clangVersion.isEmpty()) - return; - const AnalyzeUnit unit(m_fileInfo, includeDir, clangVersion); - m_runnerCreators << [=]() -> ClangToolRunner * { - if (m_document->isModified() && !isVFSOverlaySupported(executable)) - return nullptr; - auto runner = new ClangToolRunner({tool, config, m_temporaryDir.path(), - env, unit, vfso().overlayFilePath().toString()}, this); - connect(runner, &ClangToolRunner::done, - this, &DocumentClangToolRunner::onDone); - return runner; - }; - }; - addClangTool(ClangToolType::Tidy); - addClangTool(ClangToolType::Clazy); - } - } - } - } else { + if (!Utils::anyOf(EditorManager::visibleEditors(), isEditorForCurrentDocument)) { deleteLater(); + return; } + const FilePath filePath = m_document->filePath(); + Project *project = findProject(filePath); + if (!project) + return; - runNext(); -} + m_fileInfo = getFileInfo(filePath, project); + if (!m_fileInfo.file.exists()) + return; -void DocumentClangToolRunner::runNext() -{ - if (m_currentRunner) - m_currentRunner.release()->deleteLater(); + const auto projectSettings = ClangToolsProjectSettings::getSettings(project); + const RunSettings &runSettings = projectSettings->useGlobalSettings() + ? ClangToolsSettings::instance()->runSettings() + : projectSettings->runSettings(); + m_suppressed = projectSettings->suppressedDiagnostics(); + m_lastProjectDirectory = project->projectDirectory(); + m_projectSettingsUpdate = connect(projectSettings.data(), &ClangToolsProjectSettings::changed, + this, &DocumentClangToolRunner::run); + if (!runSettings.analyzeOpenFiles()) + return; - if (m_runnerCreators.isEmpty()) { - finalize(); + vfso().update(); + const ClangDiagnosticConfig config = diagnosticConfig(runSettings.diagnosticConfigId()); + const Environment env = projectBuildEnvironment(project); + using namespace Tasking; + QList<TaskItem> tasks{parallel}; + const auto addClangTool = [this, &config, &env, &tasks](ClangToolType tool) { + if (!config.isEnabled(tool)) + return; + const FilePath executable = toolExecutable(tool); + const auto [includeDir, clangVersion] = getClangIncludeDirAndVersion(executable); + if (!executable.isExecutableFile() || includeDir.isEmpty() || clangVersion.isEmpty()) + return; + const AnalyzeUnit unit(m_fileInfo, includeDir, clangVersion); + const AnalyzeInputData input{tool, config, m_temporaryDir.path(), env, unit, + vfso().overlayFilePath().toString()}; + const auto setupHandler = [this, executable] { + return !m_document->isModified() || isVFSOverlaySupported(executable); + }; + const auto outputHandler = [this](const AnalyzeOutputData &output) { onDone(output); }; + tasks.append(Group{optional, clangToolTask(input, setupHandler, outputHandler)}); + }; + addClangTool(ClangToolType::Tidy); + addClangTool(ClangToolType::Clazy); + if (tasks.isEmpty()) return; - } - m_currentRunner.reset(m_runnerCreators.takeFirst()()); - if (!m_currentRunner || !m_currentRunner->run()) - runNext(); + + guard.dismiss(); + m_taskTree.reset(new TaskTree(tasks)); + 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) @@ -252,7 +228,6 @@ void DocumentClangToolRunner::onDone(const AnalyzeOutputData &output) if (!output.success) { qCDebug(LOG) << "Failed to analyze " << m_fileInfo.file << ":" << output.errorMessage << output.errorDetails; - runNext(); return; } @@ -316,26 +291,18 @@ void DocumentClangToolRunner::onDone(const AnalyzeOutputData &output) m_editorsWithMarkers << widget; } } - - runNext(); } void DocumentClangToolRunner::finalize() { - // remove all disabled textMarks - auto [newMarks, toDelete] = Utils::partition(m_marks, &DiagnosticMark::enabled); + if (m_taskTree) + m_taskTree.release()->deleteLater(); + // remove all disabled marks + const auto [newMarks, toDelete] = Utils::partition(m_marks, &DiagnosticMark::enabled); m_marks = newMarks; 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 { auto equalsSuppressed = [this, &diagnostic](const SuppressedDiagnostic &suppressed) { diff --git a/src/plugins/clangtools/documentclangtoolrunner.h b/src/plugins/clangtools/documentclangtoolrunner.h index 2bf555e65a8..e3c274ce9cd 100644 --- a/src/plugins/clangtools/documentclangtoolrunner.h +++ b/src/plugins/clangtools/documentclangtoolrunner.h @@ -15,12 +15,12 @@ namespace Core { class IDocument; } namespace TextEditor { class TextEditorWidget; } +namespace Utils { class TaskTree; } namespace ClangTools { namespace Internal { class AnalyzeOutputData; -class ClangToolRunner; class DiagnosticMark; class DocumentClangToolRunner : public QObject @@ -36,27 +36,22 @@ public: private: void scheduleRun(); void run(); - void runNext(); void onDone(const AnalyzeOutputData &output); - void finalize(); - void cancel(); - bool isSuppressed(const Diagnostic &diagnostic) const; QTimer m_runTimer; Core::IDocument *m_document = nullptr; Utils::TemporaryDirectory m_temporaryDir; - std::unique_ptr<ClangToolRunner> m_currentRunner; - QList<std::function<ClangToolRunner *()>> m_runnerCreators; QList<DiagnosticMark *> m_marks; FileInfo m_fileInfo; QMetaObject::Connection m_projectSettingsUpdate; QList<QPointer<TextEditor::TextEditorWidget>> m_editorsWithMarkers; SuppressedDiagnosticsList m_suppressed; Utils::FilePath m_lastProjectDirectory; + std::unique_ptr<Utils::TaskTree> m_taskTree; }; } // namespace Internal |